203 # base commands ############################################################### |
202 # base commands ############################################################### |
204 |
203 |
205 class ListCommand(Command): |
204 class ListCommand(Command): |
206 """List configurations, cubes and instances. |
205 """List configurations, cubes and instances. |
207 |
206 |
208 list available configurations, installed cubes, and registered instances |
207 List available configurations, installed cubes, and registered instances. |
|
208 |
|
209 If given, the optional argument allows to restrict listing only a category of items. |
209 """ |
210 """ |
210 name = 'list' |
211 name = 'list' |
|
212 arguments = '[all|cubes|configurations|instances]' |
211 options = ( |
213 options = ( |
212 ('verbose', |
214 ('verbose', |
213 {'short': 'v', 'action' : 'store_true', |
215 {'short': 'v', 'action' : 'store_true', |
214 'help': "display more information."}), |
216 'help': "display more information."}), |
215 ) |
217 ) |
216 |
218 |
217 def run(self, args): |
219 def run(self, args): |
218 """run the command with its specific arguments""" |
220 """run the command with its specific arguments""" |
219 if args: |
221 if not args: |
|
222 mode = 'all' |
|
223 elif len(args) == 1: |
|
224 mode = args[0] |
|
225 else: |
220 raise BadCommandUsage('Too many arguments') |
226 raise BadCommandUsage('Too many arguments') |
|
227 |
221 from cubicweb.migration import ConfigurationProblem |
228 from cubicweb.migration import ConfigurationProblem |
222 print 'CubicWeb %s (%s mode)' % (cwcfg.cubicweb_version(), cwcfg.mode) |
229 |
223 print |
230 if mode == 'all': |
224 print 'Available configurations:' |
231 print 'CubicWeb %s (%s mode)' % (cwcfg.cubicweb_version(), cwcfg.mode) |
225 for config in CONFIGURATIONS: |
232 print |
226 print '*', config.name |
233 |
227 for line in config.__doc__.splitlines(): |
234 if mode in ('all', 'config', 'configurations'): |
228 line = line.strip() |
235 print 'Available configurations:' |
229 if not line: |
236 for config in CONFIGURATIONS: |
230 continue |
237 print '*', config.name |
231 print ' ', line |
238 for line in config.__doc__.splitlines(): |
232 print |
239 line = line.strip() |
233 cfgpb = ConfigurationProblem(cwcfg) |
240 if not line: |
234 try: |
241 continue |
235 cubesdir = pathsep.join(cwcfg.cubes_search_path()) |
242 print ' ', line |
236 namesize = max(len(x) for x in cwcfg.available_cubes()) |
243 print |
237 except ConfigurationError as ex: |
244 |
238 print 'No cubes available:', ex |
245 if mode in ('all', 'cubes'): |
239 except ValueError: |
246 cfgpb = ConfigurationProblem(cwcfg) |
240 print 'No cubes available in %s' % cubesdir |
247 try: |
241 else: |
248 cubesdir = pathsep.join(cwcfg.cubes_search_path()) |
242 print 'Available cubes (%s):' % cubesdir |
249 namesize = max(len(x) for x in cwcfg.available_cubes()) |
243 for cube in cwcfg.available_cubes(): |
250 except ConfigurationError as ex: |
244 try: |
251 print 'No cubes available:', ex |
245 tinfo = cwcfg.cube_pkginfo(cube) |
252 except ValueError: |
246 tversion = tinfo.version |
253 print 'No cubes available in %s' % cubesdir |
247 cfgpb.add_cube(cube, tversion) |
254 else: |
248 except (ConfigurationError, AttributeError) as ex: |
255 print 'Available cubes (%s):' % cubesdir |
249 tinfo = None |
256 for cube in cwcfg.available_cubes(): |
250 tversion = '[missing cube information: %s]' % ex |
257 try: |
251 print '* %s %s' % (cube.ljust(namesize), tversion) |
258 tinfo = cwcfg.cube_pkginfo(cube) |
252 if self.config.verbose: |
259 tversion = tinfo.version |
253 if tinfo: |
260 cfgpb.add_cube(cube, tversion) |
254 descr = getattr(tinfo, 'description', '') |
261 except (ConfigurationError, AttributeError) as ex: |
255 if not descr: |
262 tinfo = None |
256 descr = getattr(tinfo, 'short_desc', '') |
263 tversion = '[missing cube information: %s]' % ex |
|
264 print '* %s %s' % (cube.ljust(namesize), tversion) |
|
265 if self.config.verbose: |
|
266 if tinfo: |
|
267 descr = getattr(tinfo, 'description', '') |
|
268 if not descr: |
|
269 descr = getattr(tinfo, 'short_desc', '') |
|
270 if descr: |
|
271 warn('[3.8] short_desc is deprecated, update %s' |
|
272 ' pkginfo' % cube, DeprecationWarning) |
|
273 else: |
|
274 descr = tinfo.__doc__ |
257 if descr: |
275 if descr: |
258 warn('[3.8] short_desc is deprecated, update %s' |
276 print ' '+ ' \n'.join(descr.splitlines()) |
259 ' pkginfo' % cube, DeprecationWarning) |
277 modes = detect_available_modes(cwcfg.cube_dir(cube)) |
260 else: |
278 print ' available modes: %s' % ', '.join(modes) |
261 descr = tinfo.__doc__ |
|
262 if descr: |
|
263 print ' '+ ' \n'.join(descr.splitlines()) |
|
264 modes = detect_available_modes(cwcfg.cube_dir(cube)) |
|
265 print ' available modes: %s' % ', '.join(modes) |
|
266 print |
|
267 try: |
|
268 regdir = cwcfg.instances_dir() |
|
269 except ConfigurationError as ex: |
|
270 print 'No instance available:', ex |
|
271 print |
279 print |
272 return |
280 |
273 instances = list_instances(regdir) |
281 if mode in ('all', 'instances'): |
274 if instances: |
282 try: |
275 print 'Available instances (%s):' % regdir |
283 regdir = cwcfg.instances_dir() |
276 for appid in instances: |
284 except ConfigurationError as ex: |
277 modes = cwcfg.possible_configurations(appid) |
285 print 'No instance available:', ex |
278 if not modes: |
286 print |
279 print '* %s (BROKEN instance, no configuration found)' % appid |
287 return |
280 continue |
288 instances = list_instances(regdir) |
281 print '* %s (%s)' % (appid, ', '.join(modes)) |
289 if instances: |
282 try: |
290 print 'Available instances (%s):' % regdir |
283 config = cwcfg.config_for(appid, modes[0]) |
291 for appid in instances: |
284 except Exception as exc: |
292 modes = cwcfg.possible_configurations(appid) |
285 print ' (BROKEN instance, %s)' % exc |
293 if not modes: |
286 continue |
294 print '* %s (BROKEN instance, no configuration found)' % appid |
287 else: |
295 continue |
288 print 'No instance available in %s' % regdir |
296 print '* %s (%s)' % (appid, ', '.join(modes)) |
289 print |
297 try: |
290 # configuration management problem solving |
298 config = cwcfg.config_for(appid, modes[0]) |
291 cfgpb.solve() |
299 except Exception as exc: |
292 if cfgpb.warnings: |
300 print ' (BROKEN instance, %s)' % exc |
293 print 'Warnings:\n', '\n'.join('* '+txt for txt in cfgpb.warnings) |
301 continue |
294 if cfgpb.errors: |
302 else: |
295 print 'Errors:' |
303 print 'No instance available in %s' % regdir |
296 for op, cube, version, src in cfgpb.errors: |
304 print |
297 if op == 'add': |
305 |
298 print '* cube', cube, |
306 if mode == 'all': |
299 if version: |
307 # configuration management problem solving |
300 print ' version', version, |
308 cfgpb.solve() |
301 print 'is not installed, but required by %s' % src |
309 if cfgpb.warnings: |
302 else: |
310 print 'Warnings:\n', '\n'.join('* '+txt for txt in cfgpb.warnings) |
303 print '* cube %s version %s is installed, but version %s is required by %s' % ( |
311 if cfgpb.errors: |
304 cube, cfgpb.cubes[cube], version, src) |
312 print 'Errors:' |
|
313 for op, cube, version, src in cfgpb.errors: |
|
314 if op == 'add': |
|
315 print '* cube', cube, |
|
316 if version: |
|
317 print ' version', version, |
|
318 print 'is not installed, but required by %s' % src |
|
319 else: |
|
320 print '* cube %s version %s is installed, but version %s is required by %s' % ( |
|
321 cube, cfgpb.cubes[cube], version, src) |
305 |
322 |
306 def check_options_consistency(config): |
323 def check_options_consistency(config): |
307 if config.automatic and config.config_level > 0: |
324 if config.automatic and config.config_level > 0: |
308 raise BadCommandUsage('--automatic and --config-level should not be ' |
325 raise BadCommandUsage('--automatic and --config-level should not be ' |
309 'used together') |
326 'used together') |
809 identifiers of the instances to list versions for. |
833 identifiers of the instances to list versions for. |
810 """ |
834 """ |
811 name = 'versions' |
835 name = 'versions' |
812 |
836 |
813 def versions_instance(self, appid): |
837 def versions_instance(self, appid): |
814 from logilab.common.changelog import Version |
|
815 config = cwcfg.config_for(appid) |
838 config = cwcfg.config_for(appid) |
816 # should not raise error if db versions don't match fs versions |
839 # should not raise error if db versions don't match fs versions |
817 config.repairing = True |
840 config.repairing = True |
818 if hasattr(config, 'set_sources_mode'): |
841 if hasattr(config, 'set_sources_mode'): |
819 config.set_sources_mode(('migration',)) |
842 config.set_sources_mode(('migration',)) |
820 repo = config.migration_handler().repo_connect() |
843 repo = config.migration_handler().repo_connect() |
821 vcconf = repo.get_versions() |
844 vcconf = repo.get_versions() |
822 for key in sorted(vcconf): |
845 for key in sorted(vcconf): |
823 print key+': %s.%s.%s' % vcconf[key] |
846 print key+': %s.%s.%s' % vcconf[key] |
824 |
|
825 |
847 |
826 class ShellCommand(Command): |
848 class ShellCommand(Command): |
827 """Run an interactive migration shell on an instance. This is a python shell |
849 """Run an interactive migration shell on an instance. This is a python shell |
828 with enhanced migration commands predefined in the namespace. An additional |
850 with enhanced migration commands predefined in the namespace. An additional |
829 argument may be given corresponding to a file containing commands to execute |
851 argument may be given corresponding to a file containing commands to execute |
987 def run(self, args): |
1009 def run(self, args): |
988 """run the command with its specific arguments""" |
1010 """run the command with its specific arguments""" |
989 for cube in cwcfg.available_cubes(): |
1011 for cube in cwcfg.available_cubes(): |
990 print cube |
1012 print cube |
991 |
1013 |
|
1014 class ConfigureInstanceCommand(InstanceCommand): |
|
1015 """Configure instance. |
|
1016 |
|
1017 <instance>... |
|
1018 identifier of the instance to configure. |
|
1019 """ |
|
1020 name = 'configure' |
|
1021 actionverb = 'configured' |
|
1022 |
|
1023 options = merge_options(InstanceCommand.options + |
|
1024 (('param', |
|
1025 {'short': 'p', 'type' : 'named', 'metavar' : 'key1:value1,key2:value2', |
|
1026 'default': None, |
|
1027 'help': 'set <key> to <value> in configuration file.', |
|
1028 }), |
|
1029 )) |
|
1030 |
|
1031 def configure_instance(self, appid): |
|
1032 if self.config.param is not None: |
|
1033 appcfg = cwcfg.config_for(appid) |
|
1034 for key, value in self.config.param.iteritems(): |
|
1035 try: |
|
1036 appcfg.global_set_option(key, value) |
|
1037 except KeyError: |
|
1038 raise ConfigurationError('unknown configuration key "%s" for mode %s' % (key, appcfg.name)) |
|
1039 appcfg.save() |
|
1040 |
992 for cmdcls in (ListCommand, |
1041 for cmdcls in (ListCommand, |
993 CreateInstanceCommand, DeleteInstanceCommand, |
1042 CreateInstanceCommand, DeleteInstanceCommand, |
994 StartInstanceCommand, StopInstanceCommand, RestartInstanceCommand, |
1043 StartInstanceCommand, StopInstanceCommand, RestartInstanceCommand, |
995 ReloadConfigurationCommand, StatusCommand, |
1044 ReloadConfigurationCommand, StatusCommand, |
996 UpgradeInstanceCommand, |
1045 UpgradeInstanceCommand, |
997 ListVersionsInstanceCommand, |
1046 ListVersionsInstanceCommand, |
998 ShellCommand, |
1047 ShellCommand, |
999 RecompileInstanceCatalogsCommand, |
1048 RecompileInstanceCatalogsCommand, |
1000 ListInstancesCommand, ListCubesCommand, |
1049 ListInstancesCommand, ListCubesCommand, |
|
1050 ConfigureInstanceCommand, |
1001 ): |
1051 ): |
1002 CWCTL.register(cmdcls) |
1052 CWCTL.register(cmdcls) |
1003 |
|
1004 |
1053 |
1005 def run(args): |
1054 def run(args): |
1006 """command line tool""" |
1055 """command line tool""" |
1007 import os |
1056 import os |
1008 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) |
1057 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) |