def check_nodes_with_cc(self, job_ctx, ipmi_nodes_detail):
        try:
            # TODO: returning all nodes as of now, it must be the diff of nodes
            # from CC and ipmi_nodes_details
            final_ipmi_details = []
            job_input = FilterModule._validate_job_ctx(job_ctx)
            cc_host = job_input.get('contrail_command_host')
            cc_auth_token = job_ctx.get('auth_token')
            cc_node = CreateCCNode(cc_host, cc_auth_token)
            cc_nodes = cc_node.get_cc_nodes()
            cc_node_data = []
            for node in cc_nodes['nodes']:
                cc_node = {}
                if node.get('bms_info') \
                        and node['bms_info'].get('driver_info') \
                        and node['bms_info']['driver_info'].get('ipmi_address'):

                    print node['bms_info']['driver_info']['ipmi_address']
                    cc_node['ipmi_address'] = node['bms_info']['driver_info'][
                        'ipmi_address']
                    if node['bms_info']['driver_info'].get('ipmi_port'):
                        print node['bms_info']['driver_info']['ipmi_port']
                        cc_node['ipmi_port'] = node['bms_info']['driver_info'][
                            'ipmi_port']
                    else:
                        cc_node['ipmi_port'] = '623'

                    cc_node_data.append(cc_node)

            for ipmi_node in ipmi_nodes_detail:
                node_found_in_cc = False
                for cc_node in cc_node_data:
                    if ipmi_node['address'] == cc_node['ipmi_address'] \
                            and int(ipmi_node['port']) == int(
                                cc_node['ipmi_port']):
                        # node is already there in contrail-command, skip for
                        # introspection
                        node_found_in_cc = True
                        break

                if node_found_in_cc == False:
                    final_ipmi_details.append(ipmi_node)
        except Exception as e:
            errmsg = "Unexpected error: %s\n%s" % (
                str(e), traceback.format_exc()
            )
            self._logger.error(errmsg)
            return {
                'status': 'failure',
                'error_msg': errmsg,
                'discovery_log': DiscoveryLog.instance().dump()
            }

        return {
            'status': 'success',
            'final_ipmi_details': final_ipmi_details,
            'discover_log': DiscoveryLog.instance().dump()
        }
Beispiel #2
0
    def __init__(self,
                 auth_args,
                 cluster_id=None,
                 cluster_token=None,
                 cc_host=None,
                 cc_username=None,
                 cc_password=None,
                 added_nodes_list=None):
        for key, val in list(auth_args.items()):
            if key in self.auth_args:
                self.auth_args[key] = val
            elif key in self.ironic_client_kwargs:
                self.ironic_client_kwargs[key] = val
            if 'os_' + str(key) in self.ironic_client_kwargs:
                self.ironic_client_kwargs['os_' + str(key)] = val

        if not self.auth_args['auth_url']:
            self.auth_url = '%s://%s:%s%s' % (
                self.auth_args['auth_protocol'], self.auth_args['auth_host'],
                self.auth_args['auth_port'],
                self.auth_args['auth_url_version'])
        else:
            self.auth_url = self.auth_args['auth_url']
            parsed_auth_url = urlparse(self.auth_url)
            self.auth_args['auth_protocol'] = parsed_auth_url.scheme
            self.auth_args['auth_host'] = parsed_auth_url.hostname
            self.auth_args['auth_port'] = parsed_auth_url.port
            self.auth_args['auth_url_version'] = parsed_auth_url.path

        if added_nodes_list:
            self.added_nodes_dict = {
                node['uuid']: node
                for node in added_nodes_list
            }

        # Set authentication and client auth objects
        self.set_ks_auth_sess()
        self.set_ironic_clients()
        self.auth_token = self.keystone_session.get_token()
        self.auth_headers['X-Auth-Token'] = self.auth_token
        self.cc_node = CreateCCNode(cc_host, cluster_id, cluster_token,
                                    cc_username, cc_password)
Beispiel #3
0
    def import_nodes_from_file(self, job_ctx):
        """
        :param job_ctx: Dictionary
            example:
            {
                "auth_token": "EB9ABC546F98",
                "job_input": {
                    "encoded_nodes": "....",
                    "contrail_command_host": "....",
                    "encoded_file": "...."
                }
            }
        :return: Dictionary
            if success, returns
            [
                <list: imported nodes>
            ]
            if failure, returns
            {
                'status': 'failure',
                'error_msg': <string: error message>,
                'import_log': <string: import_log>
            }
            """
        try:
            job_input = FilterModule._validate_job_ctx(job_ctx)
            self._logger.info("Job INPUT:\n" + str(job_input))

            encoded_file = job_input.get("encoded_file")
            file_format = job_input.get("file_format")
            decoded = base64.decodestring(encoded_file)

            cluster_id = job_ctx.get('contrail_cluster_id')
            cluster_token = job_ctx.get('auth_token')

            cc_host = job_input.get('contrail_command_host')
            cc_username = job_input.get('cc_username')
            cc_password = job_input.get('cc_password')

            self._logger.warn("Starting Server Import")
            cc_node_obj = CreateCCNode(cc_host, cluster_id, cluster_token,
                                       cc_username, cc_password)

            if file_format.lower() == "yaml":
                data = yaml.load(decoded)

            elif file_format.lower() == "json":
                data = self.convert(json.loads(decoded))
            else:
                raise ValueError('File format not recognized. Only yaml or '
                                 'json supported')
            added_nodes = self.import_nodes(data, cc_node_obj)
        except Exception as e:
            errmsg = "Unexpected error: %s\n%s" % (str(e),
                                                   traceback.format_exc())
            self._logger.error(errmsg)
            return {
                'status': 'failure',
                'error_msg': errmsg,
                'import_log': ImportLog.instance().dump()
            }

        return {
            'status': 'success',
            'added_nodes': added_nodes,
            'import_log': ImportLog.instance().dump()
        }
Beispiel #4
0
class ImportIronicNodes(object):

    added_nodes_list = None
    auth_token = None
    auth_url = None
    ironic_client = None
    ironic_inspector_client = None
    keystone_auth = None
    keystone_session = None
    keystone_client = None
    cc_node = None
    introspection_timeout_secs = 1200
    introspection_check_secs = 30
    node_info_list = []
    uuid_node_map = {}

    # Ironic Default Auth Args
    auth_args = {
        'auth_url': None,
        'auth_protocol': 'http',
        'auth_host': "",
        'username': "******",
        'password': "",
        'auth_port': '5000',
        'user_domain_name': 'default',
        'project_domain_name': 'default',
        'project_name': 'admin',
        'auth_url_version': "/v2.0",
        'aaa_mode': None
    }

    ironic_client_kwargs = {
        'os_auth_url': '',
        'os_username': '',
        'os_password': '',
        'os_project_name': 'admin',
        'os_user_domain_name': 'default',
        'os_project_domain_name': 'default',
        'os_endpoint_type': 'internalURL',
        'os_ironic_api_version': "1.19"
    }

    auth_headers = {'Content-Type': 'application/json'}

    def __init__(self,
                 auth_args,
                 cluster_id=None,
                 cluster_token=None,
                 cc_host=None,
                 cc_username=None,
                 cc_password=None,
                 added_nodes_list=None):
        for key, val in list(auth_args.items()):
            if key in self.auth_args:
                self.auth_args[key] = val
            elif key in self.ironic_client_kwargs:
                self.ironic_client_kwargs[key] = val
            if 'os_' + str(key) in self.ironic_client_kwargs:
                self.ironic_client_kwargs['os_' + str(key)] = val

        if not self.auth_args['auth_url']:
            self.auth_url = '%s://%s:%s%s' % (
                self.auth_args['auth_protocol'], self.auth_args['auth_host'],
                self.auth_args['auth_port'],
                self.auth_args['auth_url_version'])
        else:
            self.auth_url = self.auth_args['auth_url']
            parsed_auth_url = urlparse(self.auth_url)
            self.auth_args['auth_protocol'] = parsed_auth_url.scheme
            self.auth_args['auth_host'] = parsed_auth_url.hostname
            self.auth_args['auth_port'] = parsed_auth_url.port
            self.auth_args['auth_url_version'] = parsed_auth_url.path

        if added_nodes_list:
            self.added_nodes_dict = {
                node['uuid']: node
                for node in added_nodes_list
            }

        # Set authentication and client auth objects
        self.set_ks_auth_sess()
        self.set_ironic_clients()
        self.auth_token = self.keystone_session.get_token()
        self.auth_headers['X-Auth-Token'] = self.auth_token
        self.cc_node = CreateCCNode(cc_host, cluster_id, cluster_token,
                                    cc_username, cc_password)

    def set_ks_auth_sess(self):
        self.keystone_auth = v3.Password(
            auth_url=self.auth_url,
            username=self.auth_args['username'],
            password=self.auth_args['password'],
            project_name=self.auth_args['project_name'],
            user_domain_name=self.auth_args['user_domain_name'],
            project_domain_name=self.auth_args['project_domain_name'])
        self.keystone_session = session.Session(auth=self.keystone_auth,
                                                verify=False)
        self.keystone_client = client.Client(session=self.keystone_session)

    def set_ironic_clients(self):
        self.ironic_client_kwargs['os_auth_url'] = self.auth_url
        self.ironic_client = \
            ironicclient.get_client(1, **self.ironic_client_kwargs)
        self.ironic_inspector_client = ironic_inspector_client.ClientV1(
            session=self.keystone_session)

    def read_nodes_from_db(self):
        all_ironic_nodes = self.ironic_client.node.list(detail=True)

        for node_dict in all_ironic_nodes:
            node_dict = node_dict.to_dict()

            if self.added_nodes_dict:
                if node_dict.get('uuid',
                                 None) in list(self.added_nodes_dict.keys()):
                    self.node_info_list.append(node_dict)
                    self.uuid_node_map[str(node_dict['uuid'])] = node_dict
            else:
                self.node_info_list.append(node_dict)
                self.uuid_node_map[str(node_dict['uuid'])] = node_dict

    def check_introspection(self):
        all_introspected_nodes = self.ironic_inspector_client.list_statuses()
        nodes_to_check_introspected = []

        uuid_list = []
        failed_nodes = []
        success_nodes = []

        for node in self.node_info_list:
            uuid_list.append(node['uuid'])

        for node in all_introspected_nodes:
            if node['uuid'] in uuid_list:
                nodes_to_check_introspected.append(
                    self.uuid_node_map[node['uuid']])

        if not len(nodes_to_check_introspected):
            return [], []
        else:
            success_nodes, failed_nodes = self.wait_for_introspection(
                nodes_to_check_introspected)
        return success_nodes, failed_nodes

    def wait_for_introspection(self, node_list):

        timeout = 0
        failed_nodes = []
        success_nodes = []
        while len(node_list) and timeout < self.introspection_timeout_secs:
            new_node_list = []
            for node in node_list:
                intro_status = self.check_introspection_status(node)
                if intro_status == "finished":
                    print("CREATING CC NODE", node)
                    # Get latest Node Info from Ironic POST Introspection
                    node_info = self.ironic_client.node.get(node['uuid'])
                    new_node_info = node_info.to_dict()

                    # Merge with known IPMI Info if available
                    if self.added_nodes_dict and node['uuid'] in \
                            list(self.added_nodes_dict.keys()):
                        node_ipmi_info = self.added_nodes_dict[node['uuid']]
                        new_node_info['driver_info'] = {
                            str("ipmi_" + k): node_ipmi_info[k]
                            for k in
                            ['username', 'password', 'address', 'port']
                        }

                    self.create_cc_node(new_node_info)
                    success_nodes.append(new_node_info['uuid'])
                elif intro_status == "error":
                    self.log_introspection_error([node])
                    failed_nodes.append(node['uuid'])
                else:
                    new_node_list.append(node)

            node_list = new_node_list
            if len(node_list):
                print("SLEEPING FOR NEXT INSPECT", timeout)
                time.sleep(self.introspection_check_secs)
                timeout += self.introspection_check_secs

        if len(node_list):
            self.log_introspection_error(node_list)
            for node in node_list:
                failed_nodes.append(node['uuid'])
        return success_nodes, failed_nodes

    def check_introspection_status(self, node):
        status = self.ironic_inspector_client.get_status(node['uuid'])
        if status['finished']:
            return "finished"
        elif status['error']:
            return "error"
        else:
            return "running"

    def log_introspection_error(self, node_list):
        print("LOG INTROSPECTION ERROR")
        for node in node_list:
            print("FAILED NODES", node['uuid'])
        return

    def get_cc_port_payload(self, port_dict, local_link_dict):
        cc_port = {
            "kind": "port",
            "data": {
                "parent_type": "node",
                "parent_uuid": port_dict['node_uuid'],
                "name": port_dict['ifname'],
                "uuid": port_dict['uuid'],
                "bms_port_info": {
                    "pxe_enabled": port_dict['pxe_enabled'],
                    "address": port_dict['address'],
                    "node_uuid": port_dict['node_uuid'],
                    "local_link_connection": local_link_dict
                }
            }
        }
        return cc_port

    def get_cc_node_payload(self, node_dict):
        cc_node = {
            "resources": [{
                "kind": "node",
                "data": {
                    "uuid": node_dict['uuid'],
                    "node_type": "baremetal",
                    "name": node_dict['hostname'],
                    "display_name": node_dict['hostname'],
                    "hostname": node_dict['hostname'],
                    "parent_type": "global-system-config",
                    "fq_name":
                    ["default-global-system-config", node_dict['uuid']],
                    "bms_info": {
                        "name": node_dict['hostname'],
                        "network_interface": "neutron",
                        "properties": node_dict['properties'],
                        "driver": "pxe_ipmitool",
                        "driver_info": node_dict["driver_info"]
                    }
                }
            }]
        }
        cc_node['resources'][0]['data']['bms_info']['driver_info'][
            'ipmi_port'] = str(cc_node['resources'][0]['data']['bms_info']
                               ['driver_info']['ipmi_port'])

        return cc_node

    def get_processed_if_data(self, introspection_data):
        processed_data = {}
        interface_data_dict = introspection_data['all_interfaces']

        for if_name, if_data in list(interface_data_dict.items()):
            mac_address = if_data['mac']
            processed_data[mac_address] = {'ifname': if_name}
            if 'lldp_processed' in if_data:
                processed_data[mac_address]['switch_name'] = \
                    if_data['lldp_processed']['switch_system_name']
                processed_data[mac_address]['port_name'] = \
                    if_data['lldp_processed']['switch_port_description']
        return processed_data

    def convert_port_format(self, port_list, introspection_data, uuid):
        cc_port_list = []
        generated_hostname = ""
        processed_interface_dict = self.get_processed_if_data(
            introspection_data)

        for port in port_list:
            port_dict = port.to_dict()
            local_link_dict = port_dict['local_link_connection']
            mac = port_dict['address']
            ifname = "p-" + mac.replace(":", "")[6:]
            port_dict['ifname'] = ifname
            if port_dict['pxe_enabled']:
                generated_hostname = "auto-" + mac.replace(":", "")[6:]
            if mac in processed_interface_dict:
                local_link_dict['switch_info'] = \
                    processed_interface_dict[mac].get('switch_name',"")
                local_link_dict['port_id'] = \
                    processed_interface_dict[mac].get('port_name', "")

            cc_port = self.get_cc_port_payload(port_dict, local_link_dict)
            cc_port_list.append(cc_port)
        return generated_hostname, cc_port_list

    def create_cc_node(self, node_dict):
        port_list = self.ironic_client.port.list(node=node_dict['uuid'],
                                                 detail=True)
        introspection_data = self.ironic_inspector_client.get_data(
            node_dict['uuid'])
        hostname, port_list = self.convert_port_format(port_list,
                                                       introspection_data,
                                                       node_dict['uuid'])
        node_dict['hostname'] = hostname
        for k in ['cpus', 'local_gb', 'memory_mb']:
            node_dict['properties'][k] = int(node_dict['properties'][k])
        cc_node_payload = self.get_cc_node_payload(node_dict)
        cc_node_payload['resources'].extend(port_list)

        self.cc_node.create_cc_node(cc_node_payload)

    def register_nodes(self, node_ipmi_details):
        registered_nodes_list = []
        for node in node_ipmi_details:
            ironic_node = {}
            ironic_node['driver'] = 'pxe_ipmitool'
            ironic_node['driver_info'] = {
                str("ipmi_" + k): v
                for (k, v) in list(node.items())
            }
            try:
                resp = self.ironic_client.node.create(**ironic_node)
                print(resp.uuid)
                node['uuid'] = resp.uuid
                registered_nodes_list.append(node)
            except Exception as error:
                print("ERROR: ", error)
                raise error

        return registered_nodes_list

    def trigger_introspection(self, registered_nodes):
        for node in registered_nodes:
            print(node['uuid'])
            try:
                self.ironic_inspector_client.introspect(node['uuid'])
                node['inspect'] = True
            except Exception as error:
                node['inspect'] = False
                print("ERROR: ", error)
                raise error
        return registered_nodes
    def create_os_node_filter(self, job_ctx, devices_command_outputs):
        """
        Param (devices_command_outputs) is a result from "show lldp neighbors detail" command.

        This param was gathered automatically in previous task, when above command was run on all
        leaf devices in fabric.

        :param devices_command_outputs: Dictionary
            example:
            {
                'msg': u'All items completed',
                'changed': False,
                'results': [
                    {
                        "parsed_output": {
                            "lldp-neighbors-information": {
                                "lldp-neighbor-information": [
                                    {
                                        (...)
                                        "lldp-local-interface": "xe-0/0/0",
                                        (...)
                                        "lldp-remote-management-address": "10.5.5.5",
                                       (...)
                                        "lldp-remote-port-description": "ens256",
                                        "lldp-remote-port-id": "00:0c:29:13:37:c5"
                                    }
                                    ]
                                }
                            }
                        }
                    }
                ]
            }
        :param job_ctx: Dictionary
            example:
            {
                'job_transaction_descr': 'Discover OS Computes',
                'fabric_uuid': '123412341234-123412341234',
                'contrail_command_host': '10.10.10.10:9091',
                'cc_username': '******',
                'cc_password': "******"
            }
        :return: Dictionary
            if success, returns
            {
                'status': 'success'
                'os_compute_nodes':
                    {
                        'nodes':
                        [
                            {
                                'name': 'node-1'
                                'node_type': 'ovs-compute',
                                'ports': [{
                                    'address': '00:0c:29:13:37:c5',
                                    'port_name': 'xe-0/0/0',
                                    'switch_name': 'VM283DF6BA00',
                                    'name': 'ens256'
                                }]
                            }
                        ]
                    }
            }
            if failure, returns
            {
                'status': 'failure',
                'error_msg': <string: error message>
            }
        """
        try:
            FilterModule._validate_job_ctx(job_ctx)
            job_input = job_ctx.get('input')
            vnc_api = JobVncApi.vnc_init(job_ctx)
            fabric_uuid = job_input['fabric_uuid']
            cluster_id = job_ctx.get('contrail_cluster_id')
            cluster_token = job_ctx.get('auth_token')
            cc_host = job_input['contrail_command_host']
            cc_username = job_input['cc_username']
            cc_password = job_input['cc_password']
            cc_node_obj = CreateCCNode(cc_host, cluster_id, cluster_token,
                                       cc_username, cc_password)
            os_compute_nodes = self.create_os_node(vnc_api,
                                                   devices_command_outputs,
                                                   fabric_uuid, cc_node_obj)
        except Exception as e:
            errmsg = "Unexpected error: %s\n%s" % (str(e),
                                                   traceback.format_exc())
            return {
                STATUS: FAILURE,
                ERRMSG: errmsg,
            }

        return {
            STATUS: SUCCESS,
            OS_COMPUTE_NODES: os_compute_nodes,
        }