cwctl.py
branchstable
changeset 9013 b4bcabf55e77
parent 8994 df64cca71c36
parent 9002 f98d1c46ed9f
child 9251 e4d753c8b1c4
child 9315 5298cfb132e6
equal deleted inserted replaced
9012:2cf127d4f5fd 9013:b4bcabf55e77
    36     def kill(*args):
    36     def kill(*args):
    37         """win32 kill implementation"""
    37         """win32 kill implementation"""
    38     def getpgid():
    38     def getpgid():
    39         """win32 getpgid implementation"""
    39         """win32 getpgid implementation"""
    40 
    40 
    41 
       
    42 
       
    43 from logilab.common.clcommands import CommandLine
    41 from logilab.common.clcommands import CommandLine
    44 from logilab.common.shellutils import ASK
    42 from logilab.common.shellutils import ASK
       
    43 from logilab.common.configuration import merge_options
    45 
    44 
    46 from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage
    45 from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage
    47 from cubicweb.utils import support_args
    46 from cubicweb.utils import support_args
    48 from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CWDEV, CONFIGURATIONS
    47 from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CWDEV, CONFIGURATIONS
    49 from cubicweb.toolsutils import Command, rm, create_dir, underline_title
    48 from cubicweb.toolsutils import Command, rm, create_dir, underline_title
   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')
   344           'default': 'all-in-one',
   361           'default': 'all-in-one',
   345           'help': 'installation type, telling which part of an instance '
   362           'help': 'installation type, telling which part of an instance '
   346           'should be installed. You can list available configurations using the'
   363           'should be installed. You can list available configurations using the'
   347           ' "list" command. Default to "all-in-one", e.g. an installation '
   364           ' "list" command. Default to "all-in-one", e.g. an installation '
   348           'embedding both the RQL repository and the web server.',
   365           'embedding both the RQL repository and the web server.',
       
   366           }),
       
   367         ('no-db-create',
       
   368          {'short': 'S',
       
   369           'action': 'store_true',
       
   370           'default': False,
       
   371           'help': 'stop after creation and do not continue with db-create',
   349           }),
   372           }),
   350         )
   373         )
   351 
   374 
   352     def run(self, args):
   375     def run(self, args):
   353         """run the command with its specific arguments"""
   376         """run the command with its specific arguments"""
   413             from logilab.common.shellutils import chown
   436             from logilab.common.shellutils import chown
   414             # this directory should be owned by the uid of the server process
   437             # this directory should be owned by the uid of the server process
   415             print 'set %s as owner of the data directory' % config['uid']
   438             print 'set %s as owner of the data directory' % config['uid']
   416             chown(config.appdatahome, config['uid'])
   439             chown(config.appdatahome, config['uid'])
   417         print '\n-> creation done for %s\n' % repr(config.apphome)[1:-1]
   440         print '\n-> creation done for %s\n' % repr(config.apphome)[1:-1]
   418         helper.postcreate(self.config.automatic, self.config.config_level)
   441         if not self.config.no_db_create:
       
   442             helper.postcreate(self.config.automatic, self.config.config_level)
   419 
   443 
   420     def _handle_win32(self, config, appid):
   444     def _handle_win32(self, config, appid):
   421         if sys.platform != 'win32':
   445         if sys.platform != 'win32':
   422             return
   446             return
   423         service_template = """
   447         service_template = """
   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)