Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / cli / cmclihandlers.py
diff --git a/cmframework/src/cmframework/cli/cmclihandlers.py b/cmframework/src/cmframework/cli/cmclihandlers.py
new file mode 100644 (file)
index 0000000..bc30886
--- /dev/null
@@ -0,0 +1,465 @@
+# Copyright 2019 Nokia
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import print_function
+import sys
+import inspect
+import socket
+import pprint
+import prettytable
+
+from cmframework.apis import cmmanage
+from cmframework.apis import cmerror
+
+
+class VerboseLogger(object):
+    def __call__(self, msg):
+        print(msg)
+
+
+class CMCLIHandler(object):
+    def __init__(self):
+        self.api = None
+        self.verbose_logger = VerboseLogger()
+
+    def _init_api(self, ip, port, client_lib, verbose):
+        logger = None
+        if verbose:
+            logger = self.verbose_logger
+
+        try:
+            serverip = socket.gethostbyname(ip)
+        except Exception:  # pylint: disable=broad-except
+            # use localhost in-case we cannot resolve the provided hostname
+            serverip = '127.0.0.1'
+
+        self.api = cmmanage.CMManage(serverip, port, client_lib, logger)
+
+    def set_handler(self, subparser):
+        subparser.set_defaults(handler=self)
+
+    # pylint: disable=no-self-use, unused-argument
+    def init_subparser(self, subparsers):
+        raise cmerror.CMError('Not implemented')
+
+    # pylint: disable=no-self-use, unused-argument
+    def __call__(self, args):
+        raise cmerror.CMError('Not implemented')
+
+
+class CMCLIGetPropertyHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('get-property', help='Get a property value')
+        subparser.add_argument('--property',
+                               required=True,
+                               dest='name',
+                               metavar='NAME',
+                               action='store')
+        subparser.add_argument('--snapshot',
+                               required=False,
+                               dest='snapshot',
+                               metavar='SNAPSHOT-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        name = args.name
+        snapshot = args.snapshot
+        value = self.api.get_property(name, snapshot)
+        print(value)
+
+
+class CMCLIGetPropertiesHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('get-properties',
+                                          help='Get the properties matching a filter')
+        subparser.add_argument('--matching-filter',
+                               required=True,
+                               dest='matching_filter',
+                               metavar='MATCHING-FILTER',
+                               action='store')
+        subparser.add_argument('--snapshot',
+                               required=False,
+                               dest='snapshot',
+                               metavar='SNAPSHOT-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        matching_filter = args.matching_filter
+        snapshot = args.snapshot
+        props = self.api.get_properties(matching_filter, snapshot)
+        for name, value in props.iteritems():
+            print('%s=%s' % (name, value))
+
+
+class CMCLISetPropertyHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('set-property', help='Set a property')
+        subparser.add_argument('--property',
+                               required=True,
+                               dest='prop',
+                               metavar='NAME VALUE',
+                               action='store',
+                               nargs=2)
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        prop = args.prop
+        change_uuid = self.api.set_property(prop[0], prop[1])
+        print("change-uuid:%s" % change_uuid)
+
+
+class CMCLISetPropertiesHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('set-properties', help='Set a group of properties')
+        subparser.add_argument('--property',
+                               required=True,
+                               dest='props',
+                               metavar='NAME VALUE',
+                               action='append',
+                               nargs=2)
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        props = args.props
+        dic = {}
+        for prop in props:
+            name = prop[0]
+            value = prop[1]
+            dic[name] = value
+        change_uuid = self.api.set_properties(dic)
+        print("change-uuid:%s" % change_uuid)
+
+
+class CMCLIDeletePropertyHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('delete-property', help='Delete a property')
+        subparser.add_argument('--property',
+                               required=True,
+                               dest='name',
+                               metavar='NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        name = args.name
+        change_uuid = self.api.delete_property(name)
+        print("change-uuid:%s" % change_uuid)
+
+
+class CMCLIDeletePropertiesWithFilterHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('delete-properties-with-filter',
+                                          help='Delete properties matching a filter')
+        subparser.add_argument('--matching-filter',
+                               required=True,
+                               dest='matching_filter',
+                               metavar='MATCHING-FILTER',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        matching_filter = args.matching_filter
+        change_uuid = self.api.delete_properties(matching_filter)
+        print("change-uuid:%s" % change_uuid)
+
+
+class CMCLIDeletePropertiesHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('delete-properties', help='Delete a group of properties')
+        subparser.add_argument('--property',
+                               required=True,
+                               dest='props',
+                               metavar='PROPERY-NAME',
+                               action='append')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        props = args.props
+        change_uuid = self.api.delete_properties(props)
+        print("change-uuid:%s" % change_uuid)
+
+
+class CMCLICreateSnapshotHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('create-snapshot',
+                                          help='Take a snapshot of the configuration data')
+        subparser.add_argument('--name',
+                               required=True,
+                               dest='snapshot_full_name',
+                               metavar='SNAPSHOT-FULL-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        snapshot = args.snapshot_full_name
+        self.api.create_snapshot(snapshot)
+
+
+class CMCLIRestoreSnapshotHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('restore-snapshot',
+                                          help='Restore a configuration snapshot')
+        subparser.add_argument('--name',
+                               required=True,
+                               dest='snapshot_full_name',
+                               metavar='SNAPSHOT-FULL-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        snapshot = args.snapshot_full_name
+        self.api.restore_snapshot(snapshot)
+
+
+class CMCLIDeleteSnapshotHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('delete-snapshot', help='Delete a configuration snapshot')
+        subparser.add_argument('--name',
+                               required=True,
+                               dest='snapshot_full_name',
+                               metavar='SNAPSHOT-FULL-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        snapshot = args.snapshot_full_name
+        self.api.delete_snapshot(snapshot)
+
+
+class CMCLIListSnapshotHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('list-snapshots', help='List all configuration snapshots')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        snapshots = self.api.list_snapshots()
+        sorted_snapshots = sorted(snapshots, key=lambda k: k['creation_date'])
+
+        t = prettytable.PrettyTable(['Name', 'Date', 'Release', 'Build'])
+        t.set_style(prettytable.PLAIN_COLUMNS)
+        t.align = 'l'
+        for snapshot in sorted_snapshots:
+            t.add_row([snapshot['name'],
+                       snapshot['creation_date'],
+                       snapshot['release'],
+                       snapshot['build']])
+        print(t)
+
+
+class CMCLIActivateHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser(
+            'activate',
+            help='Activate the configuration in all or specified node')
+        subparser.add_argument('--node-name',
+                               required=False,
+                               dest='node_name',
+                               metavar='NODE-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        node_name = args.node_name
+        self.api.activate(node_name)
+
+
+class CMCLIRebootHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('reboot-request',
+                                          help=('Request reboot of a specified node '
+                                                'during activation'))
+        subparser.add_argument('--node-name',
+                               required=True,
+                               dest='node_name',
+                               metavar='NODE-NAME',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        node_name = args.node_name
+        self.api.reboot_node(node_name)
+
+
+class CMCLIBootstrapHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('bootstrap',
+                                          help='Bootsrap the backend with the user config data')
+        subparser.add_argument('--config',
+                               required=True,
+                               dest='config',
+                               metavar='INITIAL-USER-CONFIG',
+                               action='store')
+        subparser.add_argument('--plugin_path',
+                               required=True,
+                               dest='plugin_path',
+                               metavar='BOOTSTRAP-PLUGIN-PATH',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        from cmframework.utils.cmuserconfig import UserConfig
+        uc = UserConfig(args.config, args.plugin_path)
+        flat_config = uc.get_flat_config()
+        self.api.set_properties(flat_config)
+
+
+class CMCLIAnsibleInventoryHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('ansible-inventory',
+                                          help='Prints the ansible inventory json to output')
+        subparser.add_argument('--plugin_path',
+                               required=False,
+                               default='/opt/cmframework/inventoryhandlers',
+                               dest='plugin_path',
+                               metavar='INVENTORY-HANDLERS-PLUGIN-PATH',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        from cmframework.utils.cmansibleinventory import AnsibleInventory
+        import json
+
+        properties = self.api.get_properties('.*')
+
+        inventory = AnsibleInventory(properties, args.plugin_path)
+        inv = inventory.generate_inventory()
+
+        print (json.dumps(inv, indent=4, sort_keys=True))
+
+
+class CMAnsiblePlaybookHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('ansible-playbooks-generate',
+                                          help=('Generate the ansible playbooks for the '
+                                                'bootstrapping, provisioning, '
+                                                'postconfig and finalizing phases'))
+        subparser.add_argument('--bootstrapping-playbooks-path',
+                               required=False,
+                               default='/etc/lcm/playbooks/installation/bootstrapping',
+                               dest='bootstrapping_path',
+                               metavar='BOOTSTRAPPING-PLAYBOOKS-PATH',
+                               action='store')
+        subparser.add_argument('--provisioning-playbooks-path',
+                               required=False,
+                               default='/etc/lcm/playbooks/installation/provisioning',
+                               dest='provisioning_path',
+                               metavar='PROVISIONING-PLAYBOOKS-PATH',
+                               action='store')
+        subparser.add_argument('--postconfig-playbooks-path',
+                               required=False,
+                               default='/etc/lcm/playbooks/installation/postconfig',
+                               dest='postconfig_path',
+                               metavar='POSTCONFIG-PLAYBOOKS-PATH',
+                               action='store')
+        subparser.add_argument('--finalize-playbooks-path',
+                               required=False,
+                               default='/etc/lcm/playbooks/installation/finalize',
+                               dest='finalize_path',
+                               metavar='FINALIZE-PLAYBOOKS-PATH',
+                               action='store')
+        subparser.add_argument('--destination-path',
+                               required=False,
+                               default='/opt/openstack-ansible/playbooks/',
+                               dest='destination_path',
+                               metavar='DESTINATION-PATH',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        from cmframework.utils.cmansibleplaybooks import AnsiblePlaybooks
+
+        playbooks = AnsiblePlaybooks(args.destination_path, args.bootstrapping_path,
+                                     args.provisioning_path, args.postconfig_path,
+                                     args.finalize_path)
+        playbooks.generate_playbooks()
+
+
+class CMCLIDisableAutomaticActivationHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('disable-automatic-activation',
+                                          help='Disable automatic activation')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        self.api.disable_automatic_activation()
+
+
+class CMCLIEnableAutomaticActivationHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('enable-automatic-activation',
+                                          help='Enable automatic activation')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        self.api.enable_automatic_activation()
+
+
+class CMCLIGetChangesStatesHandler(CMCLIHandler):
+    def init_subparser(self, subparsers):
+        subparser = subparsers.add_parser('get-changes-states',
+                                          help='Get the configuration changes states')
+        subparser.add_argument('--uuid',
+                               required=False,
+                               dest='change_uuid',
+                               metavar='CHANGE_UUID',
+                               action='store')
+        self.set_handler(subparser)
+
+    def __call__(self, args):
+        self._init_api(args.ip, args.port, args.client_lib, args.verbose)
+        change_uuid = None
+        if args.change_uuid:
+            change_uuid = args.change_uuid
+
+        result = self.api.get_changes_states(change_uuid)
+        pp = pprint.PrettyPrinter(indent=4)
+        pp.pprint(result)
+
+
+def get_handlers_list():
+    handlers = []
+    for name, obj in inspect.getmembers(sys.modules[__name__]):
+        if inspect.isclass(obj):
+            if name != 'CMCLIHandler':
+                if issubclass(obj, CMCLIHandler):
+                    handlers.append(obj())
+    return handlers
+
+
+def main():
+    handlers = get_handlers_list()
+    for handler in handlers:
+        print('handler is ', handler)
+
+
+if __name__ == '__main__':
+    main()