def _find_default_with_no_vlan(self, primary_seas): """ This method finds the default adapter when there's no vlanid specified. The default SEA is the one that has the lowest pvid in all the primary seas. :param: primary_seas: This is a list of all primary_seas for a given host :return: A default adapter. Note there can be only one in this case? """ lowest_sea = None # A None should never be returned however low = 4096 for i in range(0, len(primary_seas)): # if the sea is not available - we need to find the next available if primary_seas[i].pvid < low and primary_seas[i].is_available(): low = primary_seas[i].pvid lowest_sea = primary_seas[i] msg = (ras.vif_get_msg ('info', 'LOWEST_PVID') % {'lowest_sea': lowest_sea.name}) ras.function_tracepoint(LOG, __name__, ras.TRACE_INFO, msg) # Let's say that none of the seas are available, in this case we pick # anyone and return if lowest_sea is None and len(primary_seas) >= 1: lowest_sea = primary_seas[0] LOG.info(_('None of the seas are in available state, picking %s' 'as default' % lowest_sea.name)) return lowest_sea
def __init__(self, host_name, pvm_op=None): """ IBMPowerVMVlanVIFDriver's constructor. :param host_name: The name of the host machine for nova, not to be confused with the networking concept of hostname. :param pvm_op: It is the PowerVMOperator._operator initialized by __init__() of PowerVMOperator. """ # IVMOperator for IVM ssh command execution.. if pvm_op: self._pvmops = pvm_op else: pvm_conn = pvmcom.Connection(CONF.powervm_mgr, CONF.powervm_mgr_user, CONF.powervm_mgr_passwd, CONF.powervm_mgr_port) msg = (ras.vif_get_msg ('info', 'CONNECTION_IVM')) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) self._pvmops = operator.IVMOperator(pvm_conn) # Centralized topology service for IVM self._topo = topo_ivm.IBMPowerVMNetworkTopoIVM(host_name, self._pvmops) # Call parent constructor super(IBMPowerVMVlanVIFDriver, self).__init__()
def reconcile(context, host_data, dom_factory=dom.DOM_Factory(), db_session=None): """ This is the entry method to reconcile the host data from the system with that stored in the database. :param context: The database context. :param host_data: A dictionary of data that represents the latest inventory information on the server. The data should be in the network DOM format. :param dom_factory: Optional factory used to create the DOM objects. Not required to be set. :param db_session: The database session. Should be started and finalized outside this class. """ if not db_session: db_session = session.get_session() try: with db_session.begin(): _reconcile_host(context, host_data, dom_factory, db_session) except Exception as e: _log_before_and_after(context, host_data, db_session) msg = ras.vif_get_msg('error', 'HOST_RECONCILE_ERROR') ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, msg) ras.function_tracepoint( LOG, __name__, ras.TRACE_EXCEPTION, e.message) raise # We only want to run the used port clean up rarely, as it is expensive global USED_PORT_COUNT USED_PORT_COUNT = USED_PORT_COUNT + 1 if USED_PORT_COUNT >= USED_PORT_THRESHOLD: USED_PORT_COUNT = 0 _neutron_unused_port_cleanup(context)
def _discover_vios_config(self): """ This function will discover the SEA configuration on the managed VIOSes. If it detects any faulty configuration, an exception will be thrown. The exception should include data on what the issue was. """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") try: # Get all the VIOS under this host, and verify we have at least one vio_servers = self._get_all_vios() if not vio_servers: ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg('error', 'VIOS_NONE') % self.host_name) raise excp.IBMPowerVMInvalidHostConfig(attr='vios') # Loop through every VIOS on the host. for vios in vio_servers: # See if we find some adapters if not self._populate_adapters_into_vios(vios): # Found no adapters... this could be fine, but log it. ras.function_tracepoint(LOG, __name__, ras.TRACE_WARNING, vios.lpar_name + ': ' + ras.vif_get_msg('error', 'VIOS_NOADAPTERS')) # If we get here, we've found all adapters, added them to their # respective VioServer, and verified every VioServer has at least # one SharedEthernetAdapter. Create the Host object with those # VioServers and we're done! self.host = dom.Host(self.host_name, vio_servers) # Update the available pool of VLAN IDs self.host.maintain_available_vid_pool() # except K2Error as e: # Bug0002104,NameError: global name 'K2Error' is not defined # # If this was a K2Error, we want to reraise it so we don't put # # out a message about an invalid configuration, which is misleading # if e.k2response is not None: # LOG.error(_('Request headers:')) # LOG.error(e.k2response.reqheaders) # LOG.error(_('Request body:')) # LOG.error(e.k2response.reqbody) # LOG.error(_('Response headers:')) # LOG.error(e.k2response.headers) # LOG.error(_('Response body:')) # LOG.error(e.k2response.body) # raise except Exception as e: msg = (ras.vif_get_msg('error', 'VIOS_UNKNOWN') + " (" + (_('%s') % e) + ")") ras.function_tracepoint(LOG, __name__, ras.TRACE_EXCEPTION, msg)
def __init__(self, host_name, operator=None): if operator: self._pvmops = operator else: self._pvmops = ivm_local_oper.IVMLocalOperator() self.host = None if not host_name: ras.function_tracepoint( LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg("error", "DEVNAME_INVALID") % {"devname": host_name} ) raise excp.IBMPowerVMInvalidHostConfig(attr="host_name") self.host_name = host_name self.operator = operator
def parse_slot_num(virt_eths, adapter_name): """ Parse the adapter slot data from the IVM output. :param virt_eths: A dictionary with adapter data from IVM :param adapter_name: The name of a SEA or VEA :returns: An integer of the slot the adapter occupies """ try: physloc = virt_eths[adapter_name][1].strip() except KeyError: msg = (ras.vif_get_msg('error', 'VIOS_SEAINVALIDLOCS') % {'devname': adapter_name}) ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, msg) return None return int(physloc.split('-')[2].lstrip('C'))
def __init__(self, host_name, operator=None): """ IBMPowerVMNetworkTopoIVM constructor :param host_name: Host_name for this compute process's node :param operator: PowerVMOperator to interface with PowerVM host """ if operator: self._pvmops = operator else: msg = ras.vif_get_msg("info", "CONNECTION_IVM_TOPO") ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) pvm_conn = pvmcom.Connection( CONF.powervm_mgr, CONF.powervm_mgr_user, CONF.powervm_mgr_passwd, CONF.powervm_mgr_port ) self._pvmops = ivm_oper.IVMOperator(pvm_conn) topo.IBMPowerVMNetworkTopo.__init__(self, host_name=host_name, operator=self._pvmops)
def get_current_config(self, must_discover_sea=True): """ This method will retrieve the current configuration of the SEAs on the PowerVM host and return information about them in a Host object. :param must_discover_sea: If True, we will raise an exception if no SEAs are discovered. :returns host: A Host object (from the DOM) containing info on the VIOS(es) and their adapters. """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") # Refresh the topology. Force the discovery so that any errors # may be thrown upwards. self.refresh(must_discover_sea) # Return the Host created return self.host
def _find_first_avail_slot(self, vios): """ This method will return the first available virtual slot on the VIOS. To be used when creating a VEA. :param vios: VioServer DOM object representing VIOS we're working with. :returns slotnum: Virtual slot number of the first available slot """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") # find out all the available virtual adapters and their physloc cmds = utils.get_virt_adapters_with_physloc_cmd(vios.lpar_id) physloc_list = self._pvmops.run_vios_command(cmds) msg = (ras.vif_get_msg('info', 'PHYSC_LOC_LIST') % {'output': physloc_list}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) # Output example: #['U9117.MMC.0604C17-V100-C2-T1', # .....] cmd = utils.get_curr_max_virtual_slots_cmd(vios.lpar_id) maxslots = int(self._pvmops.run_vios_command(cmd)[0]) msg = (ras.vif_get_msg('info', 'MAX_SLOTS') % {'output': maxslots}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) if not physloc_list: return used_slots = [] #Need to delte column descriptions that command returns physloc_list.reverse() physloc_list.pop() #loop through all slots used for slot in physloc_list: if len(slot.strip()) == 0: continue # get the first column from output , adapter details slot = slot.split(' ')[0] snum = int(slot.split('-')[2].lstrip('C')) if snum < maxslots: used_slots.append(snum) used_slots.sort() # vslot 0-9 are reserved on IVM. for snum in range(10, maxslots): if snum not in used_slots: break if snum == (maxslots - 1): # The - 1 is because 0 is a valid slot return else: msg = (ras.vif_get_msg('info', 'SLOT_NUM') % {'snum': snum}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) return snum
def _build_network_info_model(self, context, instance, networks=None, port_ids=None): LOG.debug('Enter _build_network_info_model with instance %s and ' 'networks %s' % (instance['project_id'], networks)) # Get all the nova networks associated with the neutron network passed # in. If no neutron network was passed in, this will return all nova # networks. net_infos = [] if networks is None and port_ids is None: # We need to check to see if the cache thinks we're none as well network_info = compute_utils.get_nw_info_for_instance(instance) # If the network info is an empty list...it may be valid. However, # it is improbable. We should go query against the master list, # which is neutron. if network_info is None or len(network_info) == 0: net_infos = self._update_instance_nw_cache(instance, context) # We didn't get the network infos - this could mean that the cache data # on the instance is proper. So let's call neutron's parent API and # get network details. if net_infos == []: net_infos = super(PowerVMAPI, self)._build_network_info_model(context, instance, networks, port_ids) LOG.debug('Found net_infos %s' % net_infos) # Get the neutron networks related to our instance if the invoker did # not pass them in. if not networks: networks = self._get_available_networks(context, instance['project_id']) msg = (ras.vif_get_msg ('info', 'NO_NET_FOUND') % {'host': instance['host']}) ras.function_tracepoint(LOG, __name__, ras.TRACE_INFO, msg) # Pare down the nova networks list to only include those that map to # the neutron network we're interested in. This will also add in the # vlan id to the nova network object. return self._match_network_models(networks, net_infos, context)
def __init__(self, host_name, operator=None): """ Initialize the DOM objects to None and set the operator we'll use to interface with the system. NOTE: Subclasses' __init__ should call this __init__() and then set up their specific operator. For IVM, this will be an IVMOperator object. For HMC, this will be a K2 operator. :param host_name: Host_name for this compute process's node :param operator: Operator used to interface with the system. """ self.host = None if not host_name: ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg('error', 'DEVNAME_INVALID') % {'devname': host_name}) raise excp.IBMPowerVMInvalidHostConfig(attr='host_name') self.host_name = host_name self.operator = operator
def _get_all_vios(self, dom_factory=model.No_DB_DOM_Factory()): """ Overridden method from powervc_nova.virt.ibmpowervm.vif.topo.IBMPowerVMNetworkTopo IVM systems only have a single VIOS by definition, so this returns a list with a single element. :param dom_factory: Factory used to create the DOM objects, optional. :returns vioses: A list of VioServer objects """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") # Find the LPAR name and ID cmd = utils.get_vios_lpar_id_name() output = self._pvmops.run_vios_command(cmd) # Split the LPAR name and ID apart output_array = output[0].split() vio_lpar_id = output_array.pop(0) vio_lpar_name = " ".join(output_array) msg = ras.vif_get_msg("info", "LPAR_DETAILS") % {"lpar_id": vio_lpar_id, "lpar_name": vio_lpar_name} ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) # Get the RMC status cmd = utils.get_rmc_status() output = self._pvmops.run_vios_command(cmd) rmc_state = utils.parse_rmc_status(output, vio_lpar_id) # Use the factory to build the VIOS DOM object # NOTE: By virtue of the fact that we're running VIOS commands over # SSH, the VIOS state is 'running'... so just hard code it below. return [ dom_factory.create_vios( lpar_name=vio_lpar_name, lpar_id=vio_lpar_id, host_name=self.host_name, state="running", rmc_state=rmc_state, ) ]
def _find_veths_by_vlan(self, vlan_id): """ Will run the command to list the virtual eth's on the system and return all of the lines that match this vlan. This may include the veth that bridges the vlan and the client veth that using the vlan. need to check both veth pvid and addl_vlan_ids. :param vlan_id: The additional VLAN id to search for :return: Will return a comma delimited string. The output may look like the following: ['1,13,4093,1,1,"201,207"', '1,19,4000,1,1,"300,301,302"'] The order is... 0 - lpar_id 1 - slot_num 2 - port_vlan_id 3 - is_trunk 4 - ieee_virtual_eth 5..n - addl_vlan_ids - Notice the quotes around this! """ cmds = utils.get_veth_list_by_vlan_id_cmd() output = self._pvmops.run_vios_command(cmds) msg = (ras.vif_get_msg('info', 'LIST_VETH') % {'cmd': output}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) results = [] for output_line in output: spots = output_line.split(',') # check port_vlan_id first if int(spots[2]) == vlan_id: results.append(output_line) msg = (ras.vif_get_msg('info', 'PVID_FOUND') % {'pvid': output_line}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) # no need to check addl_vlan_ids then , continue continue # Based on the first four...and the fact that we only need to check # additional vlans, pop off 0 through 4 and then check to see if # this VLAN is in the list spots[0:5] = [] # Loop through all the additional VLANs (which may need to be # trimmed) and if one matches, add it to results for addl_vlan_str in spots: if (addl_vlan_str == 'none'): continue addl_vlan_int = vif.parse_to_int(addl_vlan_str) if addl_vlan_int == vlan_id: msg = (ras.vif_get_msg('info', 'VLANID_IN_ADDL') % {'vlanid': vlan_id}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) results.append(output_line) break return results
def _find_vlan_users(self, vios, vlanid, vswitch): """ This method will search for the given vlanid on the system and return two things. First, the pvid of the SEA on the VIOS containing the vlanid. Second, the number of users of that vlanid total (ie, client LPARs in addition to VIOS). :param vios: Not needed for IvM. Only here because superclass has it. :param vlanid: VLANID to search for :param vswitch: Not needed for IVM since there's only one vswitch. :returns pvid: port vlanid of the SEA on the VIOS containing the vlanid. Will be 0 if not found. :returns num_users: Total number of adapters found with this vlanid. """ pvid = 0 # Get the VEA slot that bridges this vlanid # output looks like the following: # lparid, slot,port_vlan_id, is_trunk, # ieee_virtual_eth,addl_vlan_ids # [1,13,4093,1,1,"201,207"], output = self._find_veths_by_vlan(vlanid) num_users = len(output) msg = (ras.vif_get_msg('info', 'VLAN_USERS') % {'vlanid': vlanid, 'num': num_users}) ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, msg) if output: # Find the veth that represents the bridging veth for veth in output: # There can only be one is_trunk adapter (ie, adapter on the # VIOS) elements = veth.split(',') if int(elements[3]): pvid = int(elements[2]) return pvid, num_users
def _determine_vlan(self, vlan, net_id, host, context): """ Determines the VLAN that should be used. :param vlan: The passed in VLAN from the user. May be None. :param net_id: The neutron network identifier. May be None. :param host: The host being queried. String value :param context: The context for neturon queries :return: The VLAN identifier to use. If both parameters are none, then None will be returned, indicating 'all seas' should be returned """ # This is the scenario where the user wants all SEAs. if vlan is None and net_id is None: return None # If a network id is passed in, we will override any VLAN info # with the info from the network ID. if net_id: msg = (ras.vif_get_msg('info', 'NET_ID_SUPPLIED') % {'vlan_id': vlan, 'host_name': host, 'net_id': net_id}) ras.function_tracepoint(LOG, __name__, ras.TRACE_INFO, msg) try: net_api = self._build_network_api() neutron_net = net_api.get(context, net_id) except neutronv2.exceptions.NeutronClientException as e: ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, e.message) # We need to stop execution here. Since we didn't get # the client raise if neutron_net and neutron_net.\ get('provider:segmentation_id'): vlan = neutron_net.get('provider:segmentation_id', 1) elif neutron_net and neutron_net.get('vlan'): vlan = neutron_net.get('vlan', 1) else: msg = _("Couldn't retrieve VLAN associated with Net_id" "setting default to 1") ras.function_tracepoint(LOG, __name__, ras.TRACE_WARNING, msg) vlan = 1 # Return the passed in value, but make sure its an int return int(vlan)
def get_host_seas(self, context, host_name=None, vswitch=None, vlan=None, net_id=None, session=None): """ This method will return a dictionary of data that represents the Shared Ethernet Adapter information for a given host or a set of hosts. If vlan or net_id are passed in, then only SEAs that are valid for the given vlan or net_id will be returned. :param context: The context for the request. :param host_name: The identifying name of the host to request the data If None is passed in, a dictionary representing all of the managed hosts will be found. :param vswitch: The vswitch that should be used to help identify the default adapter. If set to None (the default value), only the VLAN ID will be used. ie, all vswitches will be candidates. :param vlan: The vlan that should be used to help identify the default and candidate adapters. If set to None (the default value), a VLAN ID of 1 will be used. This parameter will be ignored if net_id is passed in. :param net_id: The network UUID for an existing neutron network. If this is passed in, then vlan will be ignored and the vlan to use will be obtained from the neutron network. :param session: session to be used for db access :return: A dictionary of host level Shared Ethernet Adapter data. Ex: { "host-seas": [ { "host_name": "host1", "adapters": [ { "default": false, "sea_name": "ent11", "vswitch": "ETHERNET0", "lpar_id": 1, "ha_lpar_id": 2, "ha_mode": "enabled", "pvid": 1, "state": "Available", "ha_state": "Available", "lpar_name": "10-23C2P", "ha_lpar_name": "10-24C2P", "ha_sea": "ent21" }, { "default": false, "sea_name": "ent12", "vswitch": "ETHERNET0", "lpar_id": 1, "ha_lpar_id": 2, "ha_mode": "enabled", "pvid": 2, "state": "Available", "ha_state": "Available", "lpar_name": "10-23C2P", "ha_lpar_name": "10-24C2P", "ha_sea": "ent22" } ] }, { "host_name": "host2", "adapters": [ { "default": true, "sea_name": "ent5", "vswitch": "ETHERNET0", "lpar_id": 1, "ha_lpar_id": null, "ha_mode": "disabled", "pvid": 1, "state": "Available", "ha_state": null, "lpar_name": "15-34B9Z", "ha_lpar_name": null, "ha_sea": null } ] } ] } """ ras.function_tracepoint(LOG, __name__, ras.TRACE_INFO, ras.vif_get_msg('info', 'GET_HOST_SEAS') % {'host': host_name, 'vlan': vlan, 'vswitch': vswitch, 'net_id': net_id}) # This class should only be used in PowerVM environments self.raise_if_not_powervm() if session is None: session = db_session.get_session() hosts = self._find_all_host_names(context) if host_name: if host_name in hosts: # Should make it empty before we add for below for loop # We want to be specific that now it has only one host hosts = [host_name] else: msg = (ras.vif_get_msg ('info', 'HOST_NOT_FOUND') % {'hostid': host_name}) ras.function_tracepoint( LOG, __name__, ras.TRACE_INFO, msg) raise exception.ComputeHostNotFound(host=host_name) # Validate that network exists if net_id or net_id == '': try: network.API().get(context, net_id) except: raise exception.InvalidID(id=net_id) host_dict_list = [] with session.begin(): ports = None if net_id: # Performance optimization -- read network ports before loop # because this operation is expensive search_opts = {'network_id': net_id} network_data = network.API().list_ports(context, **search_opts) ports = network_data.get('ports', []) for host in hosts: resp = self._get_specific_host_seas(context, host, vswitch, vlan, net_id, session, ports) host_dict_list.append(resp) return { 'host-seas': host_dict_list }
def _populate_adapters_into_vios(self, vios, dom_factory=model.No_DB_DOM_Factory()): """ Overridden method from powervc_nova.virt.ibmpowervm.vif.topo.IBMPowerVMNetworkTopo This method returns all the adapters, both SEA and VEA, for the VIOS this IVM runs on. Each VEA will be attached to it's owning SEA, if it has one. :param vios: VioServer to fetch adapters from :param dom_factory: Factory used to create the DOM objects, optional. :returns boolean: True if at least one adapter was found, False otherwise. """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") try: # If the VIOS RMC state is down, we can't use adapters on it. if vios.rmc_state.lower() != "active": ras.trace(LOG, __name__, ras.TRACE_INFO, _("RMC state is not active on VIOS lpar %s") % vios.lpar_name) return False # Find all of the adapters on the VIOS cmd = utils.get_all_sea_with_physloc_cmd() output = self._pvmops.run_vios_command(cmd) virt_eths = utils.parse_physloc_adapter_output(output) # Find all of the SEAs on this IVM seas_found = {} for vea in virt_eths: if virt_eths[vea][0].strip() != "Shared Ethernet Adapter": # this is not a SEA device. continue seas_found[vea] = virt_eths[vea] # Go through the SEAs and get their current configuration from IVM veas_attached_to_seas = [] for sea_devname in seas_found: # Connect to the IVM endpoint and collect the SEA info sea_dev = self._get_sea_from_ivm(vios, sea_devname, virt_eths, dom_factory) # Keep track of which VEAs are owned by an SEA so we can later # detect orphan VEAs veas_attached_to_seas.append(sea_dev.primary_vea.name) for vea in sea_dev.additional_veas: veas_attached_to_seas.append(vea.name) # Any VEA not on an SEA is considered an orphan self._find_orphaned_veas(vios, virt_eths, veas_attached_to_seas, dom_factory) # Return True if adapters were found if len(seas_found.values()) > 0: ras.trace(LOG, __name__, ras.TRACE_DEBUG, "SEA discover finished successfully.") return True ras.trace(LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg("error", "VIOS_NOSEA")) raise excp.IBMPowerVMValidSEANotFound() except pvmexcp.IBMPowerVMCommandFailed: ras.trace(LOG, __name__, ras.TRACE_EXCEPTION, ras.vif_get_msg("error", "VIOS_CMDFAIL") % {"cmd": cmd}) return False except Exception as e: ras.trace( LOG, __name__, ras.TRACE_EXCEPTION, ras.vif_get_msg("error", "VIOS_UNKNOWN") + " (" + _("%s") % e + ")" ) return False
def _get_sea_from_ivm(self, vios, sea_devname, virt_eths, dom_factory=model.No_DB_DOM_Factory()): """ Connect to an IVM endpoint and retrieve all of the configuration for a SEA and the VEAs associated with it. :param vios: The VioServer DOM object that represents this IVM :param sea_devname: The device name for the SEA to query :param virt_eths: Dictionary of physical adapter info from IVM :param dom_factory: Factory used to create the DOM objects, optional. :returns SharedEthernetAdapter: An SEA DOM object """ # Got the SEA logical device name on VIOS. # Need to find out its pvid_adapter and virt_adapters and pvid cmd = utils.get_sea_attribute_cmd(sea_devname) output = self._pvmops.run_vios_command(cmd) # Output example: # ['value', '', '100', 'ent4', 'ent4,ent13,ent14', # 'ent24 Available Shared Ethernet Adapter'] if len(output) == 0 or output[0] != "value": ras.trace(LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg("error", "VIOS_CMDFAIL") % {"cmd": cmd}) return None # Get rid of 'value','' from the list output[0:2] = [] # Find out pvid adapter and all the virt_adapters for # the give SEA and create SeaDevices object based # on the information retrieved from HMC/IVM. sea_pvid = int(output[0].strip()) sea_pvid_adpt = output[1].strip() sea_virt_adpts = output[2].strip().split(",") sea_state = output[3].split()[1] # virt_adpts has at least pvid_adpt if sea_pvid_adpt not in sea_virt_adpts: msg = ras.vif_get_msg("error", "SEA_INVALIDSTATE") ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, msg) return None # Get the full virt_adapters list without the pvid_adapter sea_virt_adpts.remove(sea_pvid_adpt) # Get the slot number for this adapter vea_slot_num = utils.parse_slot_num(virt_eths, sea_pvid_adpt) # Create the VEA and SEA dom objects vea_dev = self._get_vea_for_slot(vios, sea_pvid_adpt, vea_slot_num, sea_pvid, dom_factory) sea_dev = dom_factory.create_sea( name=sea_devname, vio_server=vios, slot=vea_slot_num, state=sea_state, primary_vea=vea_dev, control_channel=None, additional_veas=None, ) ras.trace(LOG, __name__, ras.TRACE_DEBUG, ("SEA %(devname)s is discovered" % {"devname": sea_devname})) # Find all the virt_adapters if there are any in sea_virt_adpts # list for devname in sea_virt_adpts: # Get the slot number for this adapter vea_slot_num = utils.parse_slot_num(virt_eths, devname) vea_dev = self._get_vea_for_slot(vios, devname, vea_slot_num, sea_pvid, dom_factory) try: sea_dev.add_vea_to_sea(vea_dev) except excp.IBMPowerVMInvalidSEAConfig: msg = ras.vif_get_msg("error", "SEA_FAILTOADDVEA") % {"veaname": vea_dev.name, "seaname": sea_dev.name} ras.trace(LOG, __name__, ras.TRACE_EXCEPTION, msg) return None # End of for loop to discovery all the virtual adapters # for the SEA. It is the end of the inner for loop return sea_dev
def _create_vea_on_vios(self, vios, sea, slotnum, port_vlan_id, addl_vlan_ids): """ This method will create the 802.1Q VirtualEthernetAdapter on the VIOS and return the device name of the newly created adapter. A IBMPowerVMFailToAddDataVlan should be raised if unable to create the new VEA. :param vios: VioServer DOM object representing VIOS this is being created on. :param sea: SEA that owns the VEA. Not needed on IVM as the VEA is attached to the SEA in a separate step. :param slotnum: Virtual slot number to create the new VEA in. :param port_vlan_id: pvid to set on the new VEA :param addl_vlan_ids: Additional vlan ids to set on the new VEA :returns vea_devname: Device name of the newly created VEA :returns slot_number: Always returns None """ ras.function_tracepoint(LOG, __name__, ras.TRACE_DEBUG, "Enter") try: cmd = utils.create_8021Q_vea_cmd(lpar_id=vios.lpar_id, slotnum=slotnum, port_vlan_id=port_vlan_id, addl_vlan_ids=addl_vlan_ids) self._pvmops.run_vios_command(cmd) # find out the new slot's devname. Even though DR slot add # operation will call cfgmgr on the VIOS, just invoke cfgdev # again on vio0 to make sure the device is discovered by VIOS. cmd = utils.get_newly_added_slot_name_cmd(lpar_id=vios.lpar_id, mts=self._get_mts(), slotnum=slotnum) vea_devname = self._pvmops.run_vios_command(cmd)[0] msg = (ras.vif_get_msg('info', 'VEA_DEV_NAME') % {'vea_dev': vea_devname, 'cmd': cmd}) except Exception as e: # catch the hmc/vios command exception ras.trace(LOG, __name__, ras.TRACE_EXCEPTION, ras.msg('error', 'VETH_NOTCFG') % {'slotnum': slotnum} + "(" + (_('%s') % e) + ")") raise if not vea_devname: # failed to find the newly created veth slot on VIOS. clean it up. ras.trace(LOG, __name__, ras.TRACE_ERROR, ras.msg('error', 'VETH_NOTCFG') % {'slotnum': slotnum}) try: cmds = utils.remove_virtual_slot_cmd(lpar_id=vios.lpar_id, slot_num=slotnum) self._pvmops.run_vios_command(cmds) ras.trace(LOG, __name__, ras.TRACE_DEBUG, 'Clean up: slot %(slotnum)d has been removed' % {'slotnum': slotnum}) except Exception: ras.trace(LOG, __name__, ras.TRACE_EXCEPTION, ras.msg('error', 'VETH_FAILTOREMOVE') % {'slotnum': slotnum} + "(" + _('%s') % e + ")") raise excp.IBMPowerVMFailToAddDataVlan(data_vlan_id=addl_vlan_ids) # If we got here, we succeeded! return vea_devname, None
def refresh(self, must_discover_sea=True): """ This method will refresh the topology such that it will reset its internal data model and then run a discovery on the system. This ensures that any changes done on the system itself will be picked up and added to the data model. """ # Reset the data model self.host = None # If the CEC is not running, do not discover the host. if self._is_cec_running(): # Discover the VIOSes and remember if discovery succeeds self._discover_vios_config() # If we were told we must discover at least one SEA, verify we did. if must_discover_sea: if self.host is None: msg = ras.vif_get_msg('error', 'NET_INVALIDCFG') ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, msg) raise excp.IBMPowerVMInvalidMgmtNetworkCfg(msg=msg) elif not self.host.find_all_primary_seas(): # If any of the VIOSes have RMC down, they could have been the # one with an SEA on it. Raise an exception pointing to RMC # as the problem. at_least_one_rmc_active = False at_least_one_rmc_inactive = False for vios in self.host.vio_servers: if vios.rmc_state.lower() != 'active': ras.function_tracepoint(LOG, __name__, ras.TRACE_ERROR, ras.vif_get_msg('error', 'VIOS_NORMC') % {'lpar': vios.lpar_name, 'state': vios.rmc_state}) at_least_one_rmc_inactive = True else: at_least_one_rmc_active = True if at_least_one_rmc_active and at_least_one_rmc_inactive: # We found a mixture of both active and inactive RMC # connections. We can't definitively point to RMC as the # problem, so put out a message that it COULD be the # reason we found no SEAs. raise excp.IBMPowerVMRMCDownNoSEA(host=self.host_name) elif at_least_one_rmc_active: # We found an active RMC connection but no inactive ones, # so we really have a situation where no SEAs were found. raise excp.IBMPowerVMValidSEANotFound() elif at_least_one_rmc_inactive: # We must've found NO active RMC connections. Put out # the very specific message about this. raise excp.IBMPowerVMAllRMCDown(host=self.host_name) else: # This can only be reached if neither active nor inactive # RMC connections were found. In other words, if no # VIOSes were found. I doubt this will ever happen, but # better safe than sorry. msg = ras.vif_get_msg('error', 'NET_INVALIDCFG') raise excp.IBMPowerVMInvalidHostConfig(attr='No VIOS') else: return
def _get_specific_host_seas(self, context, host, vswitch=None, vlan=None, net_id=None, session=None, ports=None): """ This method will return the SEA candidates for a given host, and only that host. The format of the response will be: { "host_name": "host2", "adapters": [ { "default": true, "sea_name": "ent5", "vswitch": "ETHERNET0", "lpar_id": 1, "ha_lpar_id": null, "ha_mode": "disabled", "pvid": 1, "state": "Available", "ha_state": null, "lpar_name": "15-34B9Z", "ha_lpar_name": null, "ha_sea": null } ] } :param context: The context for the request. :param host: The host name (as a string) :param vswitch: The vswitch that should be used to help identify the default adapter. If set to None all of the vSwitches will be utilized. :param vlan: The vlan that should be used to help identify the default adapter. If set to None (the default value), a VLAN ID of 1 will be used. :param net_id: The network UUID of a neutron Network. This is optional :param ports: An optional list of ports for the specified network. """ # Build basic data to determine targets vio_servers = dom_api.vio_server_find_all(context, host, session) host_dom = dom_model.Host(host, vio_servers) vswitches = self._get_vswitches(host_dom.find_all_primary_seas()) # If the network id was set, then we should always use that networks # vlan id instead of the passed in value. vlan = self._determine_vlan(vlan, net_id, host, context) # We need to determine if this network has any VMs on the host. If so, # then we can't be set to Do not Use. We also can't allow them to # change vSwitches. allow_do_not_use_option = False if net_id: # We only allow the do not use option if the VM count for this # network is 0. Otherwise a VM is using it, and we can't flip # to do not use until all the VMs are done. vm_list = dom_api.instances_find(context, host, net_id, ports) allow_do_not_use_option = (len(vm_list) == 0) # As noted above...if the network association has VMs, then we # can't let the user change the vSwitch that the networks are on. # Therefore, set the specific_vswitch so that the candidate_sea # loop won't take any other vSwitches into account. if len(vm_list) > 0: net_assn = dom_api.network_association_find(context, host_dom.host_name, net_id, session) # If there is a network association, just override the vSwitch # list with the network associations vSwitch. This limits it # to a single vSwitch search scope. if net_assn and net_assn.sea: vswitches = [net_assn.sea.primary_vea.vswitch_name] else: # If there was not a network id, then we assume that this is a # new network and therefore do not use should always be returned. allow_do_not_use_option = True # Variable to store all candidate SEAs candidate_seas = [] # Walk through the vswitches on this host and determine the valid # candidates (broken into pools defined by the vswitches). for vswitch_name in vswitches: # If the user passed in a vswitch, and we don't match, continue if vswitch is not None and vswitch != vswitch_name: continue # Extend the candidates candidate_seas.extend(self._get_candidate_seas_for_vswitch( host_dom, vswitch_name, vlan)) # Now we need to find the default adapter...may be None, which # indicates that it is a do not use. default_sea = self._find_default_adapter(host_dom, candidate_seas, net_id, vlan, context, session) # If the default sea is not selected, and there's only one vswitch # we need to determine a default adapter for this VLAN and create # that relationship. if(default_sea is None and not allow_do_not_use_option and net_id): vswitch_with_vlan = [] for vswitch in vswitches: sea = host_dom.find_sea_for_vlan_vswitch(vlan, vswitch) if sea: vswitch_with_vlan.append(sea) # We would like to set this as the default since in this # present call to host-seas - we need to report a default if len(vswitch_with_vlan) == 1: default_sea = vswitch_with_vlan[0] dom_api.network_association_put_sea(context, host, net_id, vswitch_with_vlan[0], session) # Now, build the adapter list to return adapter_list = [] if allow_do_not_use_option or len(vswitches) > 1: adapter_list.append(self._format_sea_to_dict_response (host_dom, None, default_sea is None)) if default_sea and default_sea not in candidate_seas: candidate_seas.append(default_sea) for sea in candidate_seas: adapter_list.append(self._format_sea_to_dict_response (host_dom, sea, default_sea == sea)) msg = (ras.vif_get_msg('info', 'HOST_SEAS_RETURN') % {'vlan': vlan, 'host_name': host, 'list': adapter_list}) ras.function_tracepoint( LOG, __name__, ras.TRACE_INFO, msg) return { 'host_name': host_dom.host_name, 'adapters': adapter_list }