Example #1
0
    def _get_orphan_pairs(self):
        """
        Uses the DOM topology to get a list of all VLANs/VSwitches which are
        orphaned.

        :return: List of VSwitch/VLAN ID pairs.  The list will look like:
                 [ 'ETHERNET0:12', 'ETHERNET1:43']
        """
        # Read the VIOS adapters from the database
        session = db_session.get_session()
        orphan_pairs = []
        with session.begin():
            vios_list = db_api.vio_server_find_all(context.get_admin_context(),
                                                   CONF.host, session)
            host = dom_model.Host(CONF.host, vios_list)
            for vios in vios_list:
                orphan_veas = vios.get_orphan_virtual_ethernet_adapters()
                for vea in orphan_veas:
                    pair_string = '%(vs)s:%(vlan)d' % {'vs': vea.vswitch_name,
                                                       'vlan': vea.pvid}
                    if not host.is_vlan_vswitch_on_sea(vea.pvid,
                                                       vea.vswitch_name):
                        orphan_pairs.append(pair_string)

                    for addl_vlan in vea.addl_vlan_ids:
                        pair_string = ('%(vs)s:%(vlan)d' %
                                       {'vs': vea.vswitch_name,
                                        'vlan': addl_vlan})
                        if not host.is_vlan_vswitch_on_sea(addl_vlan,
                                                           vea.vswitch_name):
                            orphan_pairs.append(pair_string)

        return orphan_pairs
Example #2
0
def get_host_dom(context, host_id, session=None):
    """
    Will query the system for a given host and will return its corresponding
    DOM object.

    :param context: The context for the query
    :param host_id: The host identifier
    :param session: Optional session to keep the objects within a single
                    transaction
    """
    vio_servers = dom_api.vio_server_find_all(context, host_id, session)
    return dom_model.Host(host_id, vio_servers)
Example #3
0
    def _get_host_dom(self, context, sea):
        """
        Maintains a cache of the host dom information.  Is cleared each time
        a filter method is called.  Keeps this information in cache so that
        multiple lookups to the same host are not done.

        :param context: The request to perform DB actions against
        :param sea: The shared ethernet adapter to look up the host dom for
        """
        host_name = sea.vio_server.get_host_name()
        host_dom_cache = self.host_dom_map.get(host_name, None)
        if host_dom_cache is not None:
            return host_dom_cache
        vio_servers = dom_api.vio_server_find_all(context, host_name)
        host_dom = dom_model.Host(host_name, vio_servers)
        self.host_dom_map[host_name] = host_dom
        return host_dom
Example #4
0
def _log_before_and_after(context, host_data, db_session):
    """
    Logs the DOM info before and after the inventory job to help debug.

    :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 db_session: The database session.  Should be started and finalized
                       outside this class.
    """
    try:
        # Read the new VIOS data, and the old VIOS data from the database
        new_vioses = []
        old_vioses = []
        server_dom = dom.parse_to_host(host_data, dom.No_DB_DOM_Factory())
        for vios in server_dom.vio_servers:
            new_vioses.append(pprint.pformat(vios.to_dictionary()))
        for vios in db.vio_server_find_all(context, server_dom.host_name,
                                           db_session):
            old_vioses.append(pprint.pformat(vios.to_dictionary()))

        # Log the VIOS data.  Use LOG_NO_NAME to improve readability.
        LOG_NO_NAME.error(_('New VIOS data:'))
        LOG_NO_NAME.error(_('host_name:'))
        LOG_NO_NAME.error(server_dom.host_name)
        for vios_string in new_vioses:
            for line in vios_string.split('\n'):
                LOG_NO_NAME.error(line)
        LOG_NO_NAME.error('Old VIOS data:')
        for vios_string in old_vioses:
            for line in vios_string.split('\n'):
                LOG_NO_NAME.error(line)

    except Exception:
        # This method is only invoked to log data during error flows.  If an
        # error occurs during the error flow, that's not much we can do.
        pass
Example #5
0
def _reconcile_host(context, host_data, dom_factory=dom.DOM_Factory(),
                    db_session=None):
    """
    Performs the actual reconciliation at the host level

    :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()

    # Parse the inventory data into a DOM object.  Use the no_db DOM factory
    # as we want to parse into non-DB backed elements to start...
    non_db_fact = dom.No_DB_DOM_Factory()
    server_dom = dom.parse_to_host(host_data, non_db_fact)

    msg = (ras.vif_get_msg('info', 'RECONCILE_HOST_START') %
           {'host': server_dom.host_name})
    ras.trace(LOG, __name__, ras.TRACE_DEBUG, msg)

    # Get the inventory data from the database.
    db_vio_servers = db.vio_server_find_all(context, server_dom.host_name,
                                            db_session)

    # If there are no VIO Servers, the system may be turned off.  It is very
    # unlikely that they are actually removed (all of them at least).
    # Therefore, we flip the SEAs in each VioServer to a state of unavailable
    # and they do not show up in the UI...but are not deleted.
    if len(server_dom.vio_servers) == 0:
        LOG.info(_("Flipping host %s to unavailable due to lack of VioServers"
                   % server_dom.host_name))
        _make_system_unavailable(db_vio_servers, context, db_session)
        return

    # The first step is to find VIO Servers do add/remove/modify.  Those are
    # the three passes that need to be made.
    #
    # We start with the idea that all of the data base items should be removed.
    # From there, we parse down which are still on the system (therefore need
    # to be modified) and then the new adds.
    db_vios_to_del = dom.shallow_copy_as_ordinary_list(db_vio_servers)
    srv_vios_to_add = []
    srv_vios_to_modify = []

    for vio_server in server_dom.vio_servers:
        db_vios = _find_vios(db_vio_servers, vio_server.lpar_id)
        if db_vios:
            srv_vios_to_modify.append(vio_server)
            db_vios_to_del.remove(db_vios)
        else:
            srv_vios_to_add.append(vio_server)

    # Now that we know what to modify/create/delete...loop through each and
    # execute the commands to reconcile
    db_host_dom = dom.Host(server_dom.host_name, db_vio_servers)

    # Save off the network associations first so we can recreate any that
    # need to be later on.
    net_assns = _build_net_assn_dict(
        db.network_association_find_all(context,
                                        db_host_dom.host_name,
                                        db_session))

    for db_vios in db_vios_to_del:
        _remove_vios(db_vios, db_host_dom, context, db_session)
    for server_vios in srv_vios_to_modify:
        _reconcile_vios(_find_vios(db_vio_servers, server_vios.lpar_id),
                        server_vios, context, db_session, dom_factory)
    for server_vios in srv_vios_to_add:
        _add_vios(server_vios, db_host_dom, context, db_session, dom_factory)

    msg = (ras.vif_get_msg('info', 'RECONCILE_HOST_END') %
           {'host': server_dom.host_name})
    ras.trace(LOG, __name__, ras.TRACE_DEBUG, msg)

    # Cleanup NetworkAssociations in case any VIOSes went away or came back.
    _cleanup_network_associations(db_host_dom, net_assns, context, db_session)
Example #6
0
    def _get_viable_hosts(self, context, instance_uuid, hosts_from_db,
                          network_uuids, source_host=None):
        """
        Find the viable hosts that match the network requirements.

        :param context: A context object used to authorize the DB access.
        :param instance_uuid: The instance UUID that will be used to get the
                              network requirements of an instance.
        :param hosts_from_db: A list of compute nodes
        :param network_uuids: An list of requested networks for the deployment.
        :param source_host: When this API is invoked for LPM, the host that
                            is being migrated from.  This is needed to check
                            vswitch config on the source host.
        :returns: A dictionary of hosts with the hostid and the hostname
        """
        candidate_hosts = copy.copy(hosts_from_db)

        # Determine which vswitches are needed for each network on source_host
        required_vswitches = {}
        if source_host:
            netasc_list = dom_api.network_association_find_all(context,
                                                               source_host)
            for netasc in netasc_list:
                for net_uuid in network_uuids:
                    if netasc.neutron_net_id == net_uuid and\
                            netasc.sea is not None:
                        required_vswitches[net_uuid] = \
                            netasc.sea.get_primary_vea().vswitch_name

        dom_factory = dom_model.DOM_Factory()
        # Check if each host contains a valid network
        for host in hosts_from_db:
            # Keep track of the networks that remaining to be processed.
            networks_to_process = copy.copy(network_uuids)
            if not networks_to_process:
                continue

            # Get the DOM so that we can verify the state of the chains.
            host_dom = dom_model.Host(host,
                                      dom_api.vio_server_find_all(context,
                                                                  host))

            # Walk through each of the network associations that exist for
            # the host and see if it was set to be used.  A 'use this host'
            # indication is if the network associations SEA is not None
            netasc_list = dom_api.network_association_find_all(context, host)
            for net_assn in netasc_list:
                if net_assn.neutron_net_id in network_uuids:
                    # No longer a network to process
                    networks_to_process.remove(net_assn.neutron_net_id)

                    # Check the SEA chain.  At least one SEA in the chain
                    # should be available.  If there are no SEAs, then remove
                    # it as a candidate
                    at_least_one_available = False
                    if net_assn.sea:
                        s_nm = net_assn.sea.name
                        s_lp = net_assn.sea.vio_server.lpar_id
                        sea_chain = host_dom.find_sea_chain_for_sea_info(s_nm,
                                                                         s_lp)
                        for sea in sea_chain:
                            if sea and sea.is_available():
                                at_least_one_available = True
                                break

                    # Check the vswitches.  The same vswitch name must be used
                    # on the source and target host.
                    vswitch_matches = True
                    if net_assn.neutron_net_id in required_vswitches and\
                            net_assn.sea is not None:
                        if(net_assn.sea.get_primary_vea().vswitch_name !=
                           required_vswitches[net_assn.neutron_net_id]):
                            vswitch_matches = False

                    # Check the conditions.  If there isn't one available,
                    # yet the host is still here...remove that host as a
                    # candidate.
                    if((not vswitch_matches or not at_least_one_available) and
                       host in candidate_hosts):
                        candidate_hosts.remove(host)

            # Skip the next, computationally expensive, step if there are no
            # more networks to process.
            if len(networks_to_process) == 0:
                continue

            # There may be some networks that haven't been processed yet.
            # This is typically for when a host was added after the network
            # was created.  In this scenario, we should call down into
            # the adapter mapping task and find the default.  The default
            # may be do not use.
            #
            # We don't do this by default because it's far more expensive
            # than a database operation, and we want the standard flow to be
            # as fast as possible.
            temp_context = context.elevated()
            vios_list = dom_api.vio_server_find_all(temp_context, host)
            host_dom = dom_factory.create_host(host, vios_list)
            for net_id in networks_to_process:
                net_info = self.get(temp_context, net_id)
                if not net_info or\
                        net_info.get('provider:segmentation_id') is None:
                    continue
                vlanid = int(net_info.get('provider:segmentation_id'))
                sea = self.build_default_network_assn(temp_context,
                                                      host_dom,
                                                      vlanid,
                                                      net_id)
                # If the SEA that came back from this is None, we need to
                # remove it as a candidate.  We can be confident at this
                # point though that the network association was created,
                # thus speeding up our flow through next time.
                if sea is None or not sea.is_available():
                    candidate_hosts.remove(host)

        return candidate_hosts
Example #7
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
        }