16 |
16 |
17 import os |
17 import os |
18 import fnmatch |
18 import fnmatch |
19 import logging |
19 import logging |
20 from optparse import OptionParser, OptionGroup |
20 from optparse import OptionParser, OptionGroup |
21 |
|
22 import cone_common |
21 import cone_common |
23 from cone.public import api, plugin, utils, exceptions |
22 from cone.public import api, plugin, utils, exceptions |
24 |
23 |
25 |
24 |
26 VERSION = '1.0' |
25 VERSION = '1.0' |
27 DEFAULT_EXT = '.cpf' |
26 DEFAULT_EXT = '.cpf' |
|
27 CONE_SCRIPT_PATTERN = 'conesub_*.py' |
|
28 ROOT_PATH = os.path.abspath(os.path.dirname(__file__)) |
28 |
29 |
29 logger = logging.getLogger('cone') |
30 logger = logging.getLogger('cone') |
30 DATA_NAME = 'confml/data.confml' |
31 DATA_NAME = 'confml/data.confml' |
31 |
32 |
|
33 class ExportFailedException(Exception): |
|
34 pass |
|
35 |
32 def main(): |
36 def main(): |
|
37 """ Export configurations. """ |
33 parser = OptionParser(version="%%prog %s" % VERSION) |
38 parser = OptionParser(version="%%prog %s" % VERSION) |
34 |
39 |
35 parser.add_options(cone_common.COMMON_OPTIONS) |
40 parser.add_options(cone_common.COMMON_OPTIONS) |
36 |
41 |
37 parser.add_option("-c", "--configuration", |
42 parser.add_option("-c", "--configuration", |
106 "can create even an empty layer. "\ |
111 "can create even an empty layer. "\ |
107 "Example --add foo/root.confml --add bar/root-confml.", |
112 "Example --add foo/root.confml --add bar/root-confml.", |
108 metavar="CONFIG", |
113 metavar="CONFIG", |
109 default=None) |
114 default=None) |
110 |
115 |
|
116 group.add_option("--run-action", |
|
117 dest="action", |
|
118 action="append", |
|
119 type="string", |
|
120 help="Adds a execution of extra step that can manipulate the configuration before it is exported to external storage. "\ |
|
121 "The --run-action operation can be used several times in a single command and it "\ |
|
122 "will execute the actions in given order."\ |
|
123 "Example --run-action=fix, which would execute fix action during export.", |
|
124 metavar="PLUGIN", |
|
125 default=None) |
|
126 |
111 group.add_option("--exclude-folders", |
127 group.add_option("--exclude-folders", |
112 dest="exclude_empty_folders", |
128 dest="exclude_empty_folders", |
113 action="store_true", |
129 action="store_true", |
114 help="Excludes empty folders from export", |
130 help="Excludes empty folders from export", |
115 default=False) |
131 default=False) |
|
132 |
|
133 group.add_option("--exclude-content-filter", |
|
134 dest="exclude_content_filter", |
|
135 help="Filters out files and folders from content folder which matches with "\ |
|
136 "the given regular expression. Matching is case-insensitive. "\ |
|
137 "Example --exclude-content-filter=\".*\.sisx|.*\.sis|.*\.wgz|.*wgt\" "\ |
|
138 "Filters out all files with extension sis, sisx, wgz or wgt.", |
|
139 default=None) |
|
140 |
116 |
141 |
117 parser.add_option_group(group) |
142 parser.add_option_group(group) |
118 (options, args) = parser.parse_args() |
143 (options, args) = parser.parse_args() |
119 |
144 |
120 cone_common.handle_common_options(options) |
145 cone_common.handle_common_options(options) |
129 if options.export_dir and not (options.configs or options.config_wildcards or options.config_regexes): |
154 if options.export_dir and not (options.configs or options.config_wildcards or options.config_regexes): |
130 parser.error("Use of --export-dir requires at least one configuration to be specified") |
155 parser.error("Use of --export-dir requires at least one configuration to be specified") |
131 if options.export_dir and os.path.isfile(options.export_dir): |
156 if options.export_dir and os.path.isfile(options.export_dir): |
132 parser.error("Given export directory '%s' is a file") |
157 parser.error("Given export directory '%s' is a file") |
133 |
158 |
|
159 try: |
|
160 run_export(options) |
|
161 except ExportFailedException, e: |
|
162 parser.error(str(e)) |
|
163 |
|
164 |
|
165 |
|
166 def run_export(options): |
134 # Open the project and find out the active configuration |
167 # Open the project and find out the active configuration |
135 project = api.Project(api.Storage.open(options.project, "r")) |
168 storage = api.Storage.open(options.project, "r", username=options.username, password=options.password) |
|
169 project = api.Project(storage) |
136 try: |
170 try: |
137 active_root = project.get_storage().get_active_configuration() |
171 active_root = project.get_storage().get_active_configuration() |
138 except AttributeError: |
172 except AttributeError: |
139 active_root = None |
173 active_root = None |
140 |
174 |
146 project = project, |
180 project = project, |
147 configs = options.configs, |
181 configs = options.configs, |
148 config_wildcards = options.config_wildcards, |
182 config_wildcards = options.config_wildcards, |
149 config_regexes = options.config_regexes) |
183 config_regexes = options.config_regexes) |
150 except cone_common.ConfigurationNotFoundError, e: |
184 except cone_common.ConfigurationNotFoundError, e: |
151 parser.error(str(e)) |
185 raise ExportFailedException(str(e)) |
152 |
186 |
153 # Use the active configuration if no configurations are specifically given |
187 # Use the active configuration if no configurations are specifically given |
154 if len(config_list) == 0: |
188 if len(config_list) == 0: |
155 if active_root is None: |
189 if active_root is None: |
156 parser.error("No configurations given and the project does not have an active root") |
190 raise ExportFailedException("No configurations given and the project does not have an active root") |
157 else: |
191 else: |
158 logger.info('No configurations given! Using active root configuration %s' % active_root) |
192 logger.info('No configurations given! Using active root configuration %s' % active_root) |
159 config_list = [active_root] |
193 config_list = [active_root] |
160 |
194 |
161 # Perform the export |
195 # Perform the export |
163 _export_to_dir(project = project, |
197 _export_to_dir(project = project, |
164 export_dir = options.export_dir, |
198 export_dir = options.export_dir, |
165 export_format = options.export_format or 'cpf', |
199 export_format = options.export_format or 'cpf', |
166 configs = config_list, |
200 configs = config_list, |
167 added_layers = options.added, |
201 added_layers = options.added, |
168 empty_folders = not options.exclude_empty_folders) |
202 empty_folders = not options.exclude_empty_folders, |
|
203 exclude_filters = {"content": options.exclude_content_filter}, |
|
204 options = options) |
169 else: |
205 else: |
170 _export_to_storage(project = project, |
206 _export_to_storage(project = project, |
171 remote_project_location = options.remote, |
207 remote_project_location = options.remote, |
172 configs = config_list, |
208 configs = config_list, |
173 added_layers = options.added, |
209 added_layers = options.added, |
174 empty_folders = not options.exclude_empty_folders) |
210 empty_folders = not options.exclude_empty_folders, |
175 |
211 exclude_filters = {"content": options.exclude_content_filter}, |
176 |
212 options = options) |
177 def _export_to_storage(project, remote_project_location, configs, added_layers, empty_folders): |
213 |
|
214 |
|
215 def _export_to_storage(project, remote_project_location, configs, added_layers, empty_folders, exclude_filters, options): |
178 assert len(configs) > 0 |
216 assert len(configs) > 0 |
179 |
217 |
180 # If the remote storage is not given, determine it automatically based |
218 # If the remote storage is not given, determine it automatically based |
181 # on the first specified configuration name |
219 # on the first specified configuration name |
182 if not remote_project_location: |
220 if not remote_project_location: |
183 remotename, ext = os.path.splitext(os.path.basename(configs[0])) |
221 remotename, ext = os.path.splitext(os.path.basename(configs[0])) |
184 remotename += DEFAULT_EXT |
222 remotename += DEFAULT_EXT |
185 logger.info('No remote storage given! Using source configuration name %s' % remotename) |
223 logger.info('No remote storage given! Using source configuration name %s' % remotename) |
186 remote_project_location = remotename |
224 remote_project_location = remotename |
187 |
225 |
188 remote_project = api.Project(api.Storage.open(remote_project_location, "w")) |
226 remote_project = api.Project(api.Storage.open(remote_project_location, "w", username=options.username, password=options.password)) |
189 for config_path in configs: |
227 for config_path in configs: |
190 config = project.get_configuration(config_path) |
228 config = project.get_configuration(config_path) |
|
229 # immediate solution to run fixes during export |
|
230 if options.action: |
|
231 from cone.action import loader |
|
232 for action_name in options.action: |
|
233 try: |
|
234 action_class = loader.get_class(action_name) |
|
235 action = action_class(configuration=config, |
|
236 project=project) |
|
237 action.run() |
|
238 except Exception,e: |
|
239 logger.warning('Running plugin %s threw an exception: %s' % (action_name, e)) |
191 project.export_configuration(config, |
240 project.export_configuration(config, |
192 remote_project.storage, |
241 remote_project.storage, |
193 empty_folders = empty_folders) |
242 empty_folders = empty_folders, |
|
243 exclude_filters = exclude_filters) |
194 print "Export %s to %s done!" % (config_path, remote_project_location) |
244 print "Export %s to %s done!" % (config_path, remote_project_location) |
195 |
245 |
196 # Setting first as active configuration if there are more than one configuration defined. |
246 # Setting first as active configuration if there are more than one configuration defined. |
197 configs = remote_project.list_configurations() |
247 configs = remote_project.list_configurations() |
198 if len(configs): |
248 if len(configs): |
202 pass |
252 pass |
203 |
253 |
204 remote_project.save() |
254 remote_project.save() |
205 remote_project.close() |
255 remote_project.close() |
206 |
256 |
207 _add_layers(project, remote_project_location, added_layers, empty_folders) |
257 _add_layers(project, remote_project_location, added_layers, empty_folders, exclude_filters, options) |
208 |
258 |
209 def _add_layers(source_project, remote_project_location, added_configs, empty_folders): |
259 def _add_layers(source_project, remote_project_location, added_configs, empty_folders, exclude_filters, options): |
210 """ |
260 """ |
211 Add new configuration layers from source_project into |
261 Add new configuration layers from source_project into |
212 """ |
262 """ |
213 if not added_configs: |
263 if not added_configs: |
214 return |
264 return |
225 if source_project.storage.is_resource(added_config_name): |
275 if source_project.storage.is_resource(added_config_name): |
226 # The configuration exists in the source project, export it from there |
276 # The configuration exists in the source project, export it from there |
227 existing_config = source_project.get_configuration(added_config_name) |
277 existing_config = source_project.get_configuration(added_config_name) |
228 source_project.export_configuration(existing_config, |
278 source_project.export_configuration(existing_config, |
229 target_project.storage, |
279 target_project.storage, |
230 empty_folders = empty_folders) |
280 empty_folders = empty_folders, |
|
281 exclude_filters = exclude_filters) |
231 else: |
282 else: |
232 # The given configuration does not exist in the source project, |
283 # The given configuration does not exist in the source project, |
233 # create a new empty layer |
284 # create a new empty layer |
234 logger.info("Creating new layer %s." % added_config_name) |
285 logger.info("Creating new layer %s." % added_config_name) |
235 new_config = target_project.create_configuration(added_config_name) |
286 new_config = target_project.create_configuration(added_config_name) |
239 target_config.include_configuration(utils.resourceref.norm(added_config_name)) |
290 target_config.include_configuration(utils.resourceref.norm(added_config_name)) |
240 |
291 |
241 target_project.save() |
292 target_project.save() |
242 target_project.close() |
293 target_project.close() |
243 |
294 |
244 def _export_to_dir(project, export_dir, export_format, configs, added_layers, empty_folders): |
295 def _export_to_dir(project, export_dir, export_format, configs, added_layers, empty_folders, exclude_filters, options): |
245 if not os.path.exists(export_dir): |
296 if not os.path.exists(export_dir): |
246 os.makedirs(export_dir) |
297 os.makedirs(export_dir) |
247 |
298 |
248 for config in configs: |
299 for config in configs: |
249 remote_name, _ = os.path.splitext(os.path.basename(config)) |
300 remote_name, _ = os.path.splitext(os.path.basename(config)) |