21 # *ctl module should limit the number of import to be imported as quickly as |
21 # *ctl module should limit the number of import to be imported as quickly as |
22 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash |
22 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash |
23 # completion). So import locally in command helpers. |
23 # completion). So import locally in command helpers. |
24 import sys |
24 import sys |
25 from warnings import filterwarnings |
25 from warnings import filterwarnings |
26 from os import listdir, system |
26 from os import listdir |
27 from os.path import exists, join, isdir |
27 from os.path import exists, join, isdir |
28 |
28 |
29 try: |
29 try: |
30 from os import kill, getpgid |
30 from os import kill, getpgid |
31 except ImportError: |
31 except ImportError: |
96 |
96 |
97 return [drop_prefix(cube) for cube in cwcfg.available_cubes()] |
97 return [drop_prefix(cube) for cube in cwcfg.available_cubes()] |
98 |
98 |
99 |
99 |
100 class InstanceCommand(Command): |
100 class InstanceCommand(Command): |
101 """base class for command taking 0 to n instance id as arguments |
101 """base class for command taking one instance id as arguments""" |
102 (0 meaning all registered instances) |
102 arguments = '<instance>' |
103 """ |
103 |
104 arguments = '[<instance>...]' |
104 # enforce having one instance |
|
105 min_args = max_args = 1 |
|
106 |
105 options = ( |
107 options = ( |
106 ("force", |
108 ("force", |
107 {'short': 'f', 'action': 'store_true', |
109 {'short': 'f', 'action': 'store_true', |
108 'default': False, |
110 'default': False, |
109 'help': 'force command without asking confirmation', |
111 'help': 'force command without asking confirmation', |
114 |
116 |
115 def run(self, args): |
117 def run(self, args): |
116 """run the <command>_method on each argument (a list of instance |
118 """run the <command>_method on each argument (a list of instance |
117 identifiers) |
119 identifiers) |
118 """ |
120 """ |
119 if not args: |
121 appid = args[0] |
120 args = list_instances(cwcfg.instances_dir()) |
|
121 try: |
|
122 askconfirm = not self.config.force |
|
123 except AttributeError: |
|
124 # no force option |
|
125 askconfirm = False |
|
126 else: |
|
127 askconfirm = False |
|
128 self.run_args(args, askconfirm) |
|
129 |
|
130 def run_args(self, args, askconfirm): |
|
131 status = 0 |
|
132 for appid in args: |
|
133 if askconfirm: |
|
134 print('*' * 72) |
|
135 if not ASK.confirm('%s instance %r ?' % (self.name, appid)): |
|
136 continue |
|
137 try: |
|
138 status = max(status, self.run_arg(appid)) |
|
139 except (KeyboardInterrupt, SystemExit): |
|
140 sys.stderr.write('%s aborted\n' % self.name) |
|
141 return 2 # specific error code |
|
142 sys.exit(status) |
|
143 |
|
144 def run_arg(self, appid): |
|
145 cmdmeth = getattr(self, '%s_instance' % self.name) |
122 cmdmeth = getattr(self, '%s_instance' % self.name) |
|
123 |
146 try: |
124 try: |
147 status = cmdmeth(appid) or 0 |
125 status = cmdmeth(appid) or 0 |
148 except (ExecutionError, ConfigurationError) as ex: |
126 except (ExecutionError, ConfigurationError) as ex: |
149 sys.stderr.write('instance %s not %s: %s\n' % ( |
127 sys.stderr.write('instance %s not %s: %s\n' % ( |
150 appid, self.actionverb, ex)) |
128 appid, self.actionverb, ex)) |
153 import traceback |
131 import traceback |
154 traceback.print_exc() |
132 traceback.print_exc() |
155 sys.stderr.write('instance %s not %s: %s\n' % ( |
133 sys.stderr.write('instance %s not %s: %s\n' % ( |
156 appid, self.actionverb, ex)) |
134 appid, self.actionverb, ex)) |
157 status = 8 |
135 status = 8 |
158 return status |
136 |
159 |
137 except (KeyboardInterrupt, SystemExit): |
160 |
138 sys.stderr.write('%s aborted\n' % self.name) |
161 class InstanceCommandFork(InstanceCommand): |
139 status = 2 # specific error code |
162 """Same as `InstanceCommand`, but command is forked in a new environment |
140 |
163 for each argument |
141 sys.exit(status) |
164 """ |
|
165 |
|
166 def run_args(self, args, askconfirm): |
|
167 if len(args) > 1: |
|
168 forkcmd = ' '.join(w for w in sys.argv if w not in args) |
|
169 else: |
|
170 forkcmd = None |
|
171 for appid in args: |
|
172 if askconfirm: |
|
173 print('*' * 72) |
|
174 if not ASK.confirm('%s instance %r ?' % (self.name, appid)): |
|
175 continue |
|
176 if forkcmd: |
|
177 status = system('%s %s' % (forkcmd, appid)) |
|
178 if status: |
|
179 print('%s exited with status %s' % (forkcmd, status)) |
|
180 else: |
|
181 self.run_arg(appid) |
|
182 |
142 |
183 |
143 |
184 # base commands ############################################################### |
144 # base commands ############################################################### |
185 |
145 |
186 class ListCommand(Command): |
146 class ListCommand(Command): |
463 if loglevel is not None: |
423 if loglevel is not None: |
464 config.global_set_option('log-threshold', loglevel.upper()) |
424 config.global_set_option('log-threshold', loglevel.upper()) |
465 config.init_log(config['log-threshold'], force=True) |
425 config.init_log(config['log-threshold'], force=True) |
466 |
426 |
467 |
427 |
468 class UpgradeInstanceCommand(InstanceCommandFork): |
428 class UpgradeInstanceCommand(InstanceCommand): |
469 """Upgrade an instance after cubicweb and/or component(s) upgrade. |
429 """Upgrade an instance after cubicweb and/or component(s) upgrade. |
470 |
430 |
471 For repository update, you will be prompted for a login / password to use |
431 For repository update, you will be prompted for a login / password to use |
472 to connect to the system database. For some upgrades, the given user |
432 to connect to the system database. For some upgrades, the given user |
473 should have create or alter table permissions. |
433 should have create or alter table permissions. |
474 |
434 |
475 <instance>... |
435 <instance> |
476 identifiers of the instances to upgrade. If no instance is |
436 identifier of the instance to upgrade. |
477 given, upgrade them all. |
|
478 """ |
437 """ |
479 name = 'upgrade' |
438 name = 'upgrade' |
480 actionverb = 'upgraded' |
439 actionverb = 'upgraded' |
481 options = InstanceCommand.options + ( |
440 options = InstanceCommand.options + ( |
482 ('force-cube-version', |
441 ('force-cube-version', |
636 the identifier of the instance to connect. |
595 the identifier of the instance to connect. |
637 """ |
596 """ |
638 name = 'shell' |
597 name = 'shell' |
639 arguments = '<instance> [batch command file(s)] [-- <script arguments>]' |
598 arguments = '<instance> [batch command file(s)] [-- <script arguments>]' |
640 min_args = 1 |
599 min_args = 1 |
|
600 max_args = None |
641 options = ( |
601 options = ( |
642 ('system-only', |
602 ('system-only', |
643 {'short': 'S', 'action': 'store_true', |
603 {'short': 'S', 'action': 'store_true', |
644 'help': 'only connect to the system source when the instance is ' |
604 'help': 'only connect to the system source when the instance is ' |
645 'using multiple sources. You can\'t use this option and the ' |
605 'using multiple sources. You can\'t use this option and the ' |
699 |
659 |
700 |
660 |
701 class RecompileInstanceCatalogsCommand(InstanceCommand): |
661 class RecompileInstanceCatalogsCommand(InstanceCommand): |
702 """Recompile i18n catalogs for instances. |
662 """Recompile i18n catalogs for instances. |
703 |
663 |
704 <instance>... |
664 <instance> |
705 identifiers of the instances to consider. If no instance is |
665 identifier of the instance to consider. |
706 given, recompile for all registered instances. |
|
707 """ |
666 """ |
708 name = 'i18ninstance' |
667 name = 'i18ninstance' |
709 |
668 |
710 @staticmethod |
669 @staticmethod |
711 def i18ninstance_instance(appid): |
670 def i18ninstance_instance(appid): |