Exemple #1
0
    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
Exemple #2
0
    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__()
Exemple #3
0
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)
Exemple #4
0
    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)
Exemple #5
0
    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
Exemple #6
0
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'))
Exemple #7
0
    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)
Exemple #8
0
    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
Exemple #9
0
    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
Exemple #10
0
    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)
Exemple #11
0
    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
Exemple #12
0
    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,
            )
        ]
Exemple #13
0
    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
Exemple #14
0
    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
Exemple #15
0
    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)
Exemple #16
0
    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
        }
Exemple #17
0
    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
Exemple #18
0
    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
Exemple #19
0
    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
Exemple #20
0
    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
Exemple #21
0
    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
        }