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() }
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 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() }
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, }