Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / cli / cmclihandlers.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from __future__ import print_function
15 import sys
16 import inspect
17 import socket
18 import pprint
19 import prettytable
20
21 from cmframework.apis import cmmanage
22 from cmframework.apis import cmerror
23
24
25 class VerboseLogger(object):
26     def __call__(self, msg):
27         print(msg)
28
29
30 class CMCLIHandler(object):
31     def __init__(self):
32         self.api = None
33         self.verbose_logger = VerboseLogger()
34
35     def _init_api(self, ip, port, client_lib, verbose):
36         logger = None
37         if verbose:
38             logger = self.verbose_logger
39
40         try:
41             serverip = socket.gethostbyname(ip)
42         except Exception:  # pylint: disable=broad-except
43             # use localhost in-case we cannot resolve the provided hostname
44             serverip = '127.0.0.1'
45
46         self.api = cmmanage.CMManage(serverip, port, client_lib, logger)
47
48     def set_handler(self, subparser):
49         subparser.set_defaults(handler=self)
50
51     # pylint: disable=no-self-use, unused-argument
52     def init_subparser(self, subparsers):
53         raise cmerror.CMError('Not implemented')
54
55     # pylint: disable=no-self-use, unused-argument
56     def __call__(self, args):
57         raise cmerror.CMError('Not implemented')
58
59
60 class CMCLIGetPropertyHandler(CMCLIHandler):
61     def init_subparser(self, subparsers):
62         subparser = subparsers.add_parser('get-property', help='Get a property value')
63         subparser.add_argument('--property',
64                                required=True,
65                                dest='name',
66                                metavar='NAME',
67                                action='store')
68         subparser.add_argument('--snapshot',
69                                required=False,
70                                dest='snapshot',
71                                metavar='SNAPSHOT-NAME',
72                                action='store')
73         self.set_handler(subparser)
74
75     def __call__(self, args):
76         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
77         name = args.name
78         snapshot = args.snapshot
79         value = self.api.get_property(name, snapshot)
80         print(value)
81
82
83 class CMCLIGetPropertiesHandler(CMCLIHandler):
84     def init_subparser(self, subparsers):
85         subparser = subparsers.add_parser('get-properties',
86                                           help='Get the properties matching a filter')
87         subparser.add_argument('--matching-filter',
88                                required=True,
89                                dest='matching_filter',
90                                metavar='MATCHING-FILTER',
91                                action='store')
92         subparser.add_argument('--snapshot',
93                                required=False,
94                                dest='snapshot',
95                                metavar='SNAPSHOT-NAME',
96                                action='store')
97         self.set_handler(subparser)
98
99     def __call__(self, args):
100         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
101         matching_filter = args.matching_filter
102         snapshot = args.snapshot
103         props = self.api.get_properties(matching_filter, snapshot)
104         for name, value in props.iteritems():
105             print('%s=%s' % (name, value))
106
107
108 class CMCLISetPropertyHandler(CMCLIHandler):
109     def init_subparser(self, subparsers):
110         subparser = subparsers.add_parser('set-property', help='Set a property')
111         subparser.add_argument('--property',
112                                required=True,
113                                dest='prop',
114                                metavar='NAME VALUE',
115                                action='store',
116                                nargs=2)
117         self.set_handler(subparser)
118
119     def __call__(self, args):
120         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
121         prop = args.prop
122         change_uuid = self.api.set_property(prop[0], prop[1])
123         print("change-uuid:%s" % change_uuid)
124
125
126 class CMCLISetPropertiesHandler(CMCLIHandler):
127     def init_subparser(self, subparsers):
128         subparser = subparsers.add_parser('set-properties', help='Set a group of properties')
129         subparser.add_argument('--property',
130                                required=True,
131                                dest='props',
132                                metavar='NAME VALUE',
133                                action='append',
134                                nargs=2)
135         self.set_handler(subparser)
136
137     def __call__(self, args):
138         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
139         props = args.props
140         dic = {}
141         for prop in props:
142             name = prop[0]
143             value = prop[1]
144             dic[name] = value
145         change_uuid = self.api.set_properties(dic)
146         print("change-uuid:%s" % change_uuid)
147
148
149 class CMCLIDeletePropertyHandler(CMCLIHandler):
150     def init_subparser(self, subparsers):
151         subparser = subparsers.add_parser('delete-property', help='Delete a property')
152         subparser.add_argument('--property',
153                                required=True,
154                                dest='name',
155                                metavar='NAME',
156                                action='store')
157         self.set_handler(subparser)
158
159     def __call__(self, args):
160         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
161         name = args.name
162         change_uuid = self.api.delete_property(name)
163         print("change-uuid:%s" % change_uuid)
164
165
166 class CMCLIDeletePropertiesWithFilterHandler(CMCLIHandler):
167     def init_subparser(self, subparsers):
168         subparser = subparsers.add_parser('delete-properties-with-filter',
169                                           help='Delete properties matching a filter')
170         subparser.add_argument('--matching-filter',
171                                required=True,
172                                dest='matching_filter',
173                                metavar='MATCHING-FILTER',
174                                action='store')
175         self.set_handler(subparser)
176
177     def __call__(self, args):
178         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
179         matching_filter = args.matching_filter
180         change_uuid = self.api.delete_properties(matching_filter)
181         print("change-uuid:%s" % change_uuid)
182
183
184 class CMCLIDeletePropertiesHandler(CMCLIHandler):
185     def init_subparser(self, subparsers):
186         subparser = subparsers.add_parser('delete-properties', help='Delete a group of properties')
187         subparser.add_argument('--property',
188                                required=True,
189                                dest='props',
190                                metavar='PROPERY-NAME',
191                                action='append')
192         self.set_handler(subparser)
193
194     def __call__(self, args):
195         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
196         props = args.props
197         change_uuid = self.api.delete_properties(props)
198         print("change-uuid:%s" % change_uuid)
199
200
201 class CMCLICreateSnapshotHandler(CMCLIHandler):
202     def init_subparser(self, subparsers):
203         subparser = subparsers.add_parser('create-snapshot',
204                                           help='Take a snapshot of the configuration data')
205         subparser.add_argument('--name',
206                                required=True,
207                                dest='snapshot_full_name',
208                                metavar='SNAPSHOT-FULL-NAME',
209                                action='store')
210         self.set_handler(subparser)
211
212     def __call__(self, args):
213         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
214         snapshot = args.snapshot_full_name
215         self.api.create_snapshot(snapshot)
216
217
218 class CMCLIRestoreSnapshotHandler(CMCLIHandler):
219     def init_subparser(self, subparsers):
220         subparser = subparsers.add_parser('restore-snapshot',
221                                           help='Restore a configuration snapshot')
222         subparser.add_argument('--name',
223                                required=True,
224                                dest='snapshot_full_name',
225                                metavar='SNAPSHOT-FULL-NAME',
226                                action='store')
227         self.set_handler(subparser)
228
229     def __call__(self, args):
230         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
231         snapshot = args.snapshot_full_name
232         self.api.restore_snapshot(snapshot)
233
234
235 class CMCLIDeleteSnapshotHandler(CMCLIHandler):
236     def init_subparser(self, subparsers):
237         subparser = subparsers.add_parser('delete-snapshot', help='Delete a configuration snapshot')
238         subparser.add_argument('--name',
239                                required=True,
240                                dest='snapshot_full_name',
241                                metavar='SNAPSHOT-FULL-NAME',
242                                action='store')
243         self.set_handler(subparser)
244
245     def __call__(self, args):
246         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
247         snapshot = args.snapshot_full_name
248         self.api.delete_snapshot(snapshot)
249
250
251 class CMCLIListSnapshotHandler(CMCLIHandler):
252     def init_subparser(self, subparsers):
253         subparser = subparsers.add_parser('list-snapshots', help='List all configuration snapshots')
254         self.set_handler(subparser)
255
256     def __call__(self, args):
257         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
258         snapshots = self.api.list_snapshots()
259         sorted_snapshots = sorted(snapshots, key=lambda k: k['creation_date'])
260
261         t = prettytable.PrettyTable(['Name', 'Date', 'Release', 'Build'])
262         t.set_style(prettytable.PLAIN_COLUMNS)
263         t.align = 'l'
264         for snapshot in sorted_snapshots:
265             t.add_row([snapshot['name'],
266                        snapshot['creation_date'],
267                        snapshot['release'],
268                        snapshot['build']])
269         print(t)
270
271
272 class CMCLIActivateHandler(CMCLIHandler):
273     def init_subparser(self, subparsers):
274         subparser = subparsers.add_parser(
275             'activate',
276             help='Activate the configuration in all or specified node')
277         subparser.add_argument('--node-name',
278                                required=False,
279                                dest='node_name',
280                                metavar='NODE-NAME',
281                                action='store')
282         self.set_handler(subparser)
283
284     def __call__(self, args):
285         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
286         node_name = args.node_name
287         self.api.activate(node_name)
288
289
290 class CMCLIRebootHandler(CMCLIHandler):
291     def init_subparser(self, subparsers):
292         subparser = subparsers.add_parser('reboot-request',
293                                           help=('Request reboot of a specified node '
294                                                 'during activation'))
295         subparser.add_argument('--node-name',
296                                required=True,
297                                dest='node_name',
298                                metavar='NODE-NAME',
299                                action='store')
300         self.set_handler(subparser)
301
302     def __call__(self, args):
303         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
304         node_name = args.node_name
305         self.api.reboot_node(node_name)
306
307
308 class CMCLIBootstrapHandler(CMCLIHandler):
309     def init_subparser(self, subparsers):
310         subparser = subparsers.add_parser('bootstrap',
311                                           help='Bootsrap the backend with the user config data')
312         subparser.add_argument('--config',
313                                required=True,
314                                dest='config',
315                                metavar='INITIAL-USER-CONFIG',
316                                action='store')
317         subparser.add_argument('--plugin_path',
318                                required=True,
319                                dest='plugin_path',
320                                metavar='BOOTSTRAP-PLUGIN-PATH',
321                                action='store')
322         self.set_handler(subparser)
323
324     def __call__(self, args):
325         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
326         from cmframework.utils.cmuserconfig import UserConfig
327         uc = UserConfig(args.config, args.plugin_path)
328         flat_config = uc.get_flat_config()
329         self.api.set_properties(flat_config)
330
331
332 class CMCLIAnsibleInventoryHandler(CMCLIHandler):
333     def init_subparser(self, subparsers):
334         subparser = subparsers.add_parser('ansible-inventory',
335                                           help='Prints the ansible inventory json to output')
336         subparser.add_argument('--plugin_path',
337                                required=False,
338                                default='/opt/cmframework/inventoryhandlers',
339                                dest='plugin_path',
340                                metavar='INVENTORY-HANDLERS-PLUGIN-PATH',
341                                action='store')
342         self.set_handler(subparser)
343
344     def __call__(self, args):
345         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
346         from cmframework.utils.cmansibleinventory import AnsibleInventory
347         import json
348
349         properties = self.api.get_properties('.*')
350
351         inventory = AnsibleInventory(properties, args.plugin_path)
352         inv = inventory.generate_inventory()
353
354         print (json.dumps(inv, indent=4, sort_keys=True))
355
356
357 class CMAnsiblePlaybookHandler(CMCLIHandler):
358     def init_subparser(self, subparsers):
359         subparser = subparsers.add_parser('ansible-playbooks-generate',
360                                           help=('Generate the ansible playbooks for the '
361                                                 'bootstrapping, provisioning, '
362                                                 'postconfig and finalizing phases'))
363         subparser.add_argument('--bootstrapping-playbooks-path',
364                                required=False,
365                                default='/etc/lcm/playbooks/installation/bootstrapping',
366                                dest='bootstrapping_path',
367                                metavar='BOOTSTRAPPING-PLAYBOOKS-PATH',
368                                action='store')
369         subparser.add_argument('--provisioning-playbooks-path',
370                                required=False,
371                                default='/etc/lcm/playbooks/installation/provisioning',
372                                dest='provisioning_path',
373                                metavar='PROVISIONING-PLAYBOOKS-PATH',
374                                action='store')
375         subparser.add_argument('--postconfig-playbooks-path',
376                                required=False,
377                                default='/etc/lcm/playbooks/installation/postconfig',
378                                dest='postconfig_path',
379                                metavar='POSTCONFIG-PLAYBOOKS-PATH',
380                                action='store')
381         subparser.add_argument('--finalize-playbooks-path',
382                                required=False,
383                                default='/etc/lcm/playbooks/installation/finalize',
384                                dest='finalize_path',
385                                metavar='FINALIZE-PLAYBOOKS-PATH',
386                                action='store')
387         subparser.add_argument('--destination-path',
388                                required=False,
389                                default='/opt/openstack-ansible/playbooks/',
390                                dest='destination_path',
391                                metavar='DESTINATION-PATH',
392                                action='store')
393         self.set_handler(subparser)
394
395     def __call__(self, args):
396         from cmframework.utils.cmansibleplaybooks import AnsiblePlaybooks
397
398         playbooks = AnsiblePlaybooks(args.destination_path, args.bootstrapping_path,
399                                      args.provisioning_path, args.postconfig_path,
400                                      args.finalize_path)
401         playbooks.generate_playbooks()
402
403
404 class CMCLIDisableAutomaticActivationHandler(CMCLIHandler):
405     def init_subparser(self, subparsers):
406         subparser = subparsers.add_parser('disable-automatic-activation',
407                                           help='Disable automatic activation')
408         self.set_handler(subparser)
409
410     def __call__(self, args):
411         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
412         self.api.disable_automatic_activation()
413
414
415 class CMCLIEnableAutomaticActivationHandler(CMCLIHandler):
416     def init_subparser(self, subparsers):
417         subparser = subparsers.add_parser('enable-automatic-activation',
418                                           help='Enable automatic activation')
419         self.set_handler(subparser)
420
421     def __call__(self, args):
422         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
423         self.api.enable_automatic_activation()
424
425
426 class CMCLIGetChangesStatesHandler(CMCLIHandler):
427     def init_subparser(self, subparsers):
428         subparser = subparsers.add_parser('get-changes-states',
429                                           help='Get the configuration changes states')
430         subparser.add_argument('--uuid',
431                                required=False,
432                                dest='change_uuid',
433                                metavar='CHANGE_UUID',
434                                action='store')
435         self.set_handler(subparser)
436
437     def __call__(self, args):
438         self._init_api(args.ip, args.port, args.client_lib, args.verbose)
439         change_uuid = None
440         if args.change_uuid:
441             change_uuid = args.change_uuid
442
443         result = self.api.get_changes_states(change_uuid)
444         pp = pprint.PrettyPrinter(indent=4)
445         pp.pprint(result)
446
447
448 def get_handlers_list():
449     handlers = []
450     for name, obj in inspect.getmembers(sys.modules[__name__]):
451         if inspect.isclass(obj):
452             if name != 'CMCLIHandler':
453                 if issubclass(obj, CMCLIHandler):
454                     handlers.append(obj())
455     return handlers
456
457
458 def main():
459     handlers = get_handlers_list()
460     for handler in handlers:
461         print('handler is ', handler)
462
463
464 if __name__ == '__main__':
465     main()