modules.opnfv: fuel adapter: Switch to MCP
[releng.git] / modules / opnfv / deployment / fuel / adapter.py
index d53966e..a57168d 100644 (file)
@@ -1,5 +1,5 @@
 ##############################################################################
-# Copyright (c) 2017 Ericsson AB and others.
+# Copyright (c) 2018 Ericsson AB and others.
 # Author: Jose Lausuch (jose.lausuch@ericsson.com)
 #         George Paraskevopoulos (geopar@intracom-telecom.com)
 # All rights reserved. This program and the accompanying materials
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
+'''
+    This modules implements the Fuel@OPNFV adapter
 
+    - host executing this module needs network connectivity to a cluster via:
+      * mcpcontrol network (usually 10.20.0.0/24, created by installer);
+      * PXE/admin network;
+      The above are always true for an OPNFV Pharos jumpserver.
+    - key-based SSH auth is used throughout the cluster, without proxy-ing
+      cluster node access via Salt master (old Fuel@OPNFV used to);
+'''
+
+from yaml import safe_load, YAMLError
 
 from opnfv.deployment import manager
 from opnfv.utils import opnfv_logger as logger
 from opnfv.utils import ssh_utils
 
-logger = logger.Logger("FuelAdapter").getLogger()
+LOGGER = logger.Logger(__name__).getLogger()
 
 
 class FuelAdapter(manager.DeploymentHandler):
+    '''
+        This class extends the generic handler with Fuel@OPNFV specifics
+    '''
 
-    def __init__(self, installer_ip, installer_user, installer_pwd):
+    def __init__(self, installer_ip, installer_user, pkey_file):
         super(FuelAdapter, self).__init__(installer='fuel',
                                           installer_ip=installer_ip,
                                           installer_user=installer_user,
-                                          installer_pwd=installer_pwd,
-                                          pkey_file=None)
-
-    def _get_clusters(self):
-        environments = []
-        output = self.runcmd_fuel_env()
-        lines = output.rsplit('\n')
-        if len(lines) < 2:
-            logger.info("No environments found in the deployment.")
-            return None
-        else:
-            fields = lines[0].rsplit(' | ')
-
-            index_id = -1
-            index_status = -1
-            index_name = -1
-            index_release_id = -1
-
-            for i in range(len(fields) - 1):
-                if "id" in fields[i]:
-                    index_id = i
-                elif "status" in fields[i]:
-                    index_status = i
-                elif "name" in fields[i]:
-                    index_name = i
-                elif "release_id" in fields[i]:
-                    index_release_id = i
-
-            # order env info
-            for i in range(2, len(lines) - 1):
-                fields = lines[i].rsplit(' | ')
-                dict = {"id": fields[index_id].strip(),
-                        "status": fields[index_status].strip(),
-                        "name": fields[index_name].strip(),
-                        "release_id": fields[index_release_id].strip()}
-                environments.append(dict)
-
-        return environments
-
-    def nodes(self, options=None):
+                                          installer_pwd=None,
+                                          pkey_file=pkey_file)
+
+    def get_nodes(self, options=None):
+        '''
+            Generates a list of all the nodes in the deployment
+        '''
+        # Unlike old Fuel@Openstack, we don't keep track of different clusters
+        # explicitly, but through domain names.
+        # For simplicity, we will assume a single cluster per Salt master node.
+        try:
+            # if we have retrieved previously all the nodes, don't do it again
+            # This fails the first time when the constructor calls this method
+            # therefore the try/except
+            if len(self.nodes) > 0:
+                return self.nodes
+        # pylint: disable=bare-except
+        except:
+            pass
+
+        # Manager roles to reclass properties mapping
+        _map = {
+            'salt:master:enabled': manager.Role.INSTALLER,
+            'maas:region:enabled': manager.Role.INSTALLER,
+            'nova:controller:enabled': manager.Role.CONTROLLER,
+            'nova:compute:enabled': manager.Role.COMPUTE,
+            'opendaylight:server:enabled': manager.Role.ODL,
+        }
         nodes = []
-        cmd = 'fuel node'
+        cmd = ("sudo salt '*' pillar.item {} --out yaml --static 2>/dev/null"
+               .format(' '.join(_map.keys() + ['_param:pxe_admin_address'])))
+        # Sample output (for one node):
+        #   cmp001.mcp-ovs-noha.local:
+        #     _param:pxe_admin_address: 192.168.11.34
+        #     maas:region:enabled: ''
+        #     nova:compute:enabled: true
+        #     nova:controller:enabled: ''
+        #     opendaylight:server:enabled: ''
+        #     retcode: 0
+        #     salt:master:enabled: ''
         output = self.installer_node.run_cmd(cmd)
-        lines = output.rsplit('\n')
-        if len(lines) < 2:
-            logger.info("No nodes found in the deployment.")
-            return None
-        else:
-            # get fields indexes
-            fields = lines[0].rsplit(' | ')
-
-            index_id = -1
-            index_status = -1
-            index_name = -1
-            index_cluster = -1
-            index_ip = -1
-            index_mac = -1
-            index_roles = -1
-            index_online = -1
-
-            for i in range(0, len(fields) - 1):
-                if "id" in fields[i]:
-                    index_id = i
-                elif "status" in fields[i]:
-                    index_status = i
-                elif "name" in fields[i]:
-                    index_name = i
-                elif "cluster" in fields[i]:
-                    index_cluster = i
-                elif "ip" in fields[i]:
-                    index_ip = i
-                elif "mac" in fields[i]:
-                    index_mac = i
-                elif "roles " in fields[i]:
-                    index_roles = i
-                elif "online" in fields[i]:
-                    index_online = i
-
-            # order nodes info
-            for i in range(2, len(lines) - 1):
-                fields = lines[i].rsplit(' | ')
-
-                id = fields[index_id].strip(),
-                ip = fields[index_ip].strip()
-                status_node = fields[index_status].strip()
-                name = fields[index_name].strip()
-                roles = fields[index_roles].strip()
-
-                dict = {"cluster": fields[index_cluster].strip(),
-                        "mac": fields[index_mac].strip(),
-                        "online": fields[index_online].strip()}
-
-                if status_node == 'ready':
-                    status = manager.Node.STATUS_OK
-                    proxy = {'ip': self.installer_ip,
-                             'username': self.installer_user,
-                             'password': self.installer_pwd}
-                    ssh_client = ssh_utils.get_ssh_client(hostname=ip,
-                                                          username='root',
-                                                          proxy=proxy)
-                else:
-                    status = manager.Node.STATUS_INACTIVE
-                    ssh_client = None
-
-                node = manager.Node(
-                    id, ip, name, status, roles, ssh_client, dict)
-                nodes.append(node)
-
-                # TODO: Add support for Fuel cluster selection
-                '''
-                if options and options['cluster']:
-                    if fields[index_cluster].strip() == options['cluster']:
-                '''
+        if output.startswith('No minions matched the target'):
+            LOGGER.info('No nodes found in the deployment.')
+            return nodes
+
+        try:
+            yaml_output = safe_load(output)
+        except YAMLError as exc:
+            LOGGER.error(exc)
+        for node_name in yaml_output.keys():
+            ip_addr = yaml_output[node_name]['_param:pxe_admin_address']
+            ssh_client = ssh_utils.get_ssh_client(hostname=ip_addr,
+                                                  username='ubuntu',
+                                                  pkey_file=self.pkey_file)
+            node = manager.Node(
+                id=node_name,
+                ip=ip_addr,
+                name=node_name,
+                status=manager.NodeStatus.STATUS_OK,
+                roles=[_map[x] for x in _map if yaml_output[node_name][x]],
+                ssh_client=ssh_client)
+            nodes.append(node)
 
         return nodes
 
     def get_openstack_version(self):
-        cmd = 'source openrc;nova-manage version 2>/dev/null'
-        version = None
-        for node in self.nodes:
-            if 'controller' in node.get_attribute('roles'):
-                version = node.run_cmd(cmd)
-                break
-        return version
+        '''
+        Returns a string of the openstack version (nova-compute)
+        '''
+        cmd = ("sudo salt -C 'I@nova:controller and *01*' "
+               "cmd.run 'nova-manage version 2>/dev/null' --out yaml --static")
+        nova_version = self.installer_node.run_cmd(cmd)
+        if nova_version:
+            return nova_version.split(' ')[-1]
+        return None
 
     def get_sdn_version(self):
-        cmd = "apt-cache show opendaylight|grep Version|sed 's/^.*\: //'"
+        '''
+        Returns a string of the sdn controller and its version, if exists
+        '''
+        cmd = ("sudo salt -C 'I@opendaylight:server and *01*'"
+               "pkg.version opendaylight --out yaml --static")
         version = None
         for node in self.nodes:
-            if 'controller' in node.get_attribute('roles'):
-                odl_version = node.run_cmd(cmd)
+            if manager.Role.ODL in node.roles and node.is_active():
+                odl_version = self.installer_node.run_cmd(cmd)
                 if odl_version:
-                    version = 'OpenDaylight ' + odl_version
-                break
+                    version = 'OpenDaylight ' + odl_version.split(' ')[-1]
+                    break
         return version
 
     def get_deployment_status(self):
-        cmd = 'fuel env|grep operational'
-        result = self.installer_node.run_cmd(cmd)
-        if result is None or len(result) == 0:
-            return 'failed'
-        else:
-            return 'active'
+        '''
+        Returns a string of the status of the deployment
+        '''
+        # NOTE: Requires Fuel-side signaling of deployment status, stub it
+        return 'active'