def create_sliver(self, name: str, pci_address: str, gpu_name: str) -> Unit:
        u = Unit(rid=ID(uid=name))
        sliver = NodeSliver()
        cap = Capacities(core=2, ram=6, disk=10)
        sliver.set_properties(type=NodeType.VM, site="UKY", capacity_allocations=cap)
        sliver.label_allocations = Labels(instance_parent="uky-w2.fabric-testbed.net")
        catalog = InstanceCatalog()
        instance_type = catalog.map_capacities_to_instance(cap=cap)
        cap_hints = CapacityHints(instance_type=instance_type)
        sliver.set_properties(capacity_hints=cap_hints,
                              capacity_allocations=catalog.get_instance_capacities(instance_type=instance_type))

        sliver.set_properties(name=name)

        sliver.set_properties(image_type='qcow2', image_ref='default_ubuntu_20')

        component = ComponentSliver()
        labels = Labels(bdf=pci_address)
        component.set_properties(type=ComponentType.GPU, model='Tesla T4', name=gpu_name, label_allocations=labels)
        sliver.attached_components_info = AttachedComponentsInfo()
        sliver.attached_components_info.add_device(device_info=component)

        u.set_sliver(sliver=sliver)
        u.set_resource_type(rtype=ResourceType(resource_type=NodeType.VM.name))
        return u
Example #2
0
    def configure_nic(self, *, component: ComponentSliver, mgmt_ip: str,
                      user: str):
        """
        Configure Interfaces associated with SharedNIC or SmartNIC
        :param component Component Sliver
        :param mgmt_ip Management IP
        :param user Default Linux user for the VM
        """
        # Only do this for SharedNIC and SmartNIC
        if component.get_type(
        ) != ComponentType.SharedNIC and component.get_type(
        ) != ComponentType.SmartNIC:
            return

        if component.network_service_info is None or component.network_service_info.network_services is None:
            return

        for ns in component.network_service_info.network_services.values():
            if ns.interface_info is None or ns.interface_info.interfaces is None:
                continue

            for ifs in ns.interface_info.interfaces.values():
                if ifs.flags is None or not ifs.flags.auto_config:
                    continue
                self.get_logger().info(f"Configuring Interface  {ifs}")
                self.configure_network_interface(
                    mgmt_ip=mgmt_ip,
                    user=user,
                    resource_type=component.get_type().name,
                    ipv4_address=ifs.label_allocations.ipv4,
                    ipv6_address=ifs.label_allocations.ipv6,
                    mac_address=ifs.label_allocations.mac,
                    vlan=ifs.label_allocations.vlan)
Example #3
0
    def create_unit(include_pci: bool = True,
                    include_image: bool = True,
                    include_name: bool = True,
                    include_instance_name: bool = False) -> Unit:
        """
        Create a unit
        :param include_pci:
        :param include_image:
        :param include_name:
        :param include_instance_name:
        :return:
        """
        u = Unit(rid=ID(uid='rid-1'))
        sliver = NodeSliver()
        cap = Capacities()
        cap.set_fields(core=2, ram=8, disk=10)
        sliver.set_properties(type=NodeType.VM,
                              site="RENC",
                              capacity_allocations=cap)
        sliver.label_allocations = Labels().set_fields(
            instance_parent="renc-w3")
        catalog = InstanceCatalog()
        instance_type = catalog.map_capacities_to_instance(cap=cap)
        cap_hints = CapacityHints().set_fields(instance_type=instance_type)
        sliver.set_properties(
            capacity_hints=cap_hints,
            capacity_allocations=catalog.get_instance_capacities(
                instance_type=instance_type))

        if include_name:
            sliver.set_properties(name="n2")

        if include_image:
            sliver.set_properties(image_type='qcow2',
                                  image_ref='default_fedora')

        if include_pci:
            component = ComponentSliver()
            labels = Labels()
            labels.set_fields(bdf=["0000:41:00.0", "0000:41:00.1"])
            component.set_properties(type=ComponentType.SmartNIC,
                                     model='ConnectX-5',
                                     name='nic1',
                                     label_allocations=labels)
            #labels.set_fields(bdf="0000:81:00.0")
            #component.set_properties(type=ComponentType.GPU, model='Tesla T4', name='nic12', label_allocations=labels)
            sliver.attached_components_info = AttachedComponentsInfo()
            sliver.attached_components_info.add_device(device_info=component)

        if include_instance_name:
            sliver.label_allocations.set_fields(instance="instance-001")

        u.set_sliver(sliver=sliver)
        return u
    def __exclude_allocated_component(self, *, graph_node: NodeSliver,
                                      available_component: ComponentSliver,
                                      allocated_component: ComponentSliver):
        """
        Remove the allocated component from the candidate Node. For dedicated components, the whole component is removed,
        for Shared NIC, only the allocated PCI address is removed and the number of units is reduced by 1.
        If all the PCIs are allocated for a Shared NIC, the complete Shared NIC is removed

        @param graph_node candidate node identified to satisfy the reservation
        @param available_component available component
        @param allocated_component allocated component
        """
        exclude = True
        if allocated_component.get_type() == ComponentType.SharedNIC:
            available_component, exclude = self.__exclude_allocated_pci_device_from_shared_nic(
                shared_nic=available_component,
                allocated_nic=allocated_component)
        if exclude:
            graph_node.attached_components_info.remove_device(
                name=available_component.get_name())
    def build_sliver_with_components(self) -> NodeSliver:
        node_sliver = NodeSliver()
        node_sliver.resource_type = NodeType.VM
        node_sliver.node_id = "test-slice-node-1"
        cap = Capacities(core=4, ram=64, disk=500)
        catalog = InstanceCatalog()
        instance_type = catalog.map_capacities_to_instance(cap=cap)
        cap_hints = CapacityHints(instance_type=instance_type)
        node_sliver.set_properties(name="node-1",
                                   type=NodeType.VM,
                                   site="RENC",
                                   capacities=cap,
                                   image_type='qcow2',
                                   image_ref='default_centos_8',
                                   capacity_hints=cap_hints)
        component_sliver = ComponentSliver()
        component_sliver.set_properties(type=ComponentType.SmartNIC,
                                        model='ConnectX-6',
                                        name='nic1')
        node_sliver.attached_components_info = AttachedComponentsInfo()
        node_sliver.attached_components_info.add_device(
            device_info=component_sliver)

        return node_sliver
    def __exclude_allocated_pci_device_from_shared_nic(
            self, shared_nic: ComponentSliver,
            allocated_nic: ComponentSliver) -> Tuple[ComponentSliver, bool]:
        """
        For Shared NIC cards, exclude the already assigned PCI addresses from the available PCI addresses in
        BQM Component Sliver for the NIC Card
        @param shared_nic: Available Shared NIC
        @param allocated_nic: Allocated NIC
        @return Available NIC updated to exclude the Allocated PCI addresses and True/False indicating if Available
        Shared NIC has any available PCI addresses
        """

        if shared_nic.get_type(
        ) != ComponentType.SharedNIC and allocated_nic.get_type(
        ) != ComponentType.SharedNIC:
            raise BrokerException(
                error_code=ExceptionErrorCode.INVALID_ARGUMENT,
                msg=f"shared_nic: {shared_nic} allocated_nic: {allocated_nic}")

        # Reduce capacity for component
        delegation_id, delegated_capacity = self._get_delegations(
            lab_cap_delegations=shared_nic.get_capacity_delegations())

        self.logger.debug(
            f"Allocated NIC: {allocated_nic} labels: {allocated_nic.get_label_allocations()}"
        )

        # Get the Allocated PCI address
        allocated_labels = allocated_nic.get_label_allocations()

        delegation_id, delegated_label = self._get_delegations(
            lab_cap_delegations=shared_nic.get_label_delegations())

        # Remove allocated PCI address from delegations
        excluded_labels = []

        if isinstance(allocated_labels.bdf, list):
            excluded_labels = allocated_labels.bdf
        else:
            excluded_labels = [allocated_labels.bdf]

        exists = False
        for e in excluded_labels:
            if e in delegated_label.bdf:
                self.logger.debug(f"Excluding PCI device {e}")
                delegated_label.bdf.remove(e)
                exists = True

        # Exclude already allocated Shared NIC cards
        if exists:
            delegated_capacity -= allocated_nic.get_capacity_allocations()

        return shared_nic, (delegated_capacity.unit < 1)
    def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph,
                         **kwargs) -> ABCBQMPropertyGraph:
        """
        Take a CBM, sort nodes by site, aggregate servers, components and interfaces to
        create a site-based advertisement. Use a NetworkX-based implementation.
        :param cbm:
        :param kwargs:
        :return:
        """
        if kwargs.get('query_level',
                      None) is None or kwargs['query_level'] != 1:
            return cbm.clone_graph(new_graph_id=str(uuid.uuid4()))

        # do a one-pass aggregation of servers, their components and interfaces
        # this includes facilities
        nnodes = cbm.get_all_nodes_by_class(
            label=ABCPropertyGraph.CLASS_NetworkNode)
        slivers_by_site = defaultdict(list)
        for n in nnodes:
            # build deep slivers for each advertised server, aggregate by site
            node_sliver = cbm.build_deep_node_sliver(node_id=n)
            slivers_by_site[node_sliver.site].append(node_sliver)

        # create a new blank Aggregated BQM NetworkX graph
        abqm = NetworkXAggregateBQM(
            graph_id=str(uuid.uuid4()),
            importer=NetworkXGraphImporter(logger=self.logger),
            logger=self.logger)

        site_to_composite_node_id = dict()
        site_to_ns_node_id = dict()
        facilities_by_site = defaultdict(list)
        for s, ls in slivers_by_site.items():
            # add up capacities and delegated capacities, skip labels for now
            # count up components and figure out links between site

            site_sliver = CompositeNodeSliver()
            # count what is taken
            site_sliver.capacity_allocations = Capacities()
            # count what is available
            site_sliver.capacities = Capacities()
            site_sliver.resource_name = s
            site_sliver.resource_type = NodeType.Server
            site_sliver.node_id = str(uuid.uuid4())
            # available components organized by [type][model]
            site_comps_by_type = defaultdict(dict)
            # occupied component capacities organized by [type][model] into lists (by server)
            site_allocated_comps_caps_by_type = defaultdict(dict)

            loc = None
            for sliver in ls:
                if sliver.get_type() != NodeType.Server:
                    # skipping NAS, Facility and dataplane switches
                    if sliver.get_type() == NodeType.Facility:
                        # keep track of facilities for each site
                        facilities_by_site[s].append(sliver)
                    continue
                if self.DEBUG_FLAG:
                    # for debugging and running in a test environment
                    allocated_comp_caps = dict()
                else:
                    # query database for everything taken on this node
                    allocated_caps, allocated_comp_caps = self.__occupied_node_capacity(
                        node_id=sliver.node_id)
                    site_sliver.capacity_allocations = site_sliver.capacity_allocations + allocated_caps

                # get the location if available
                if loc is None:
                    loc = sliver.get_location()

                # calculate available node capacities based on delegations
                if sliver.get_capacity_delegations() is not None:
                    # CBM only has one delegation if it has one
                    _, delegation = sliver.get_capacity_delegations(
                    ).get_sole_delegation()
                    # FIXME: skip pool definitions and references for now
                    if delegation.get_format() == DelegationFormat.SinglePool:
                        site_sliver.capacities = site_sliver.capacities + \
                            delegation.get_details()

                # merge allocated component capacities
                for kt, v in allocated_comp_caps.items():
                    for km, vcap in v.items():
                        if site_allocated_comps_caps_by_type[kt].get(
                                km) is None:
                            site_allocated_comps_caps_by_type[kt][
                                km] = Capacities()
                        site_allocated_comps_caps_by_type[kt][km] = site_allocated_comps_caps_by_type[kt][km] + \
                                                                    vcap

                # collect available components in lists by type and model for the site (for later aggregation)
                if sliver.attached_components_info is None:
                    continue
                for comp in sliver.attached_components_info.list_devices():
                    rt = comp.resource_type
                    rm = comp.resource_model
                    if site_comps_by_type[rt].get(rm) is None:
                        site_comps_by_type[rt][rm] = list()
                    site_comps_by_type[rt][rm].append(comp)

            # set location to whatever is available
            site_sliver.set_location(loc)
            site_sliver.set_site(s)

            # create a Composite node for every site
            site_to_composite_node_id[s] = site_sliver.node_id
            site_props = abqm.node_sliver_to_graph_properties_dict(site_sliver)
            abqm.add_node(node_id=site_sliver.node_id,
                          label=ABCPropertyGraph.CLASS_CompositeNode,
                          props=site_props)
            # add a network service
            ns_id = str(uuid.uuid4())
            site_to_ns_node_id[s] = ns_id
            ns_props = {
                ABCPropertyGraph.PROP_NAME: s + '_ns',
                ABCPropertyGraph.PROP_TYPE: str(ServiceType.MPLS)
            }
            abqm.add_node(node_id=ns_id,
                          label=ABCPropertyGraph.CLASS_NetworkService,
                          props=ns_props)
            abqm.add_link(node_a=site_sliver.node_id,
                          rel=ABCPropertyGraph.REL_HAS,
                          node_b=ns_id)

            # create a component sliver for every component type/model pairing
            # and add a node for it linking back to site node
            for ctype, cdict in site_comps_by_type.items():
                for cmodel, comp_list in cdict.items():
                    comp_sliver = ComponentSliver()
                    # count what is available
                    comp_sliver.capacities = Capacities()
                    # count what is taken (ignore those type/model pairings that were unused)
                    comp_sliver.capacity_allocations = site_allocated_comps_caps_by_type[ctype].get(cmodel) or \
                                                       Capacities()
                    comp_sliver.set_type(ctype)
                    comp_sliver.set_model(cmodel)
                    comp_sliver.set_name(str(ctype) + '-' + cmodel)
                    for comp in comp_list:
                        comp_sliver.capacities = comp_sliver.capacities + comp.capacities
                    comp_node_id = str(uuid.uuid4())
                    comp_props = abqm.component_sliver_to_graph_properties_dict(
                        comp_sliver)
                    abqm.add_node(node_id=comp_node_id,
                                  label=ABCPropertyGraph.CLASS_Component,
                                  props=comp_props)
                    abqm.add_link(node_a=site_sliver.node_id,
                                  rel=ABCPropertyGraph.REL_HAS,
                                  node_b=comp_node_id)

        # get all intersite links - add them to the aggregated BQM graph
        intersite_links = cbm.get_intersite_links()
        for l in intersite_links:
            source_switch = l[0]
            sink_switch = l[2]
            link = l[1]
            source_site = l[3]
            sink_site = l[4]
            source_cp = l[5]
            sink_cp = l[6]
            _, cbm_source_cp_props = cbm.get_node_properties(node_id=source_cp)
            _, cbm_sink_cp_props = cbm.get_node_properties(node_id=sink_cp)
            _, cbm_link_props = cbm.get_node_properties(node_id=link)
            # add connection point, link, connection point between two NetworkServices
            assert (site_to_ns_node_id.get(source_site) is not None
                    and site_to_ns_node_id.get(sink_site) is not None)
            source_cp_id = str(uuid.uuid4())
            sink_cp_id = str(uuid.uuid4())
            source_cp_props = {
                ABCPropertyGraph.PROP_NAME:
                "_".join([source_site, sink_site]),
                ABCPropertyGraph.PROP_TYPE:
                str(InterfaceType.TrunkPort),
                ABCPropertyGraph.PROP_CLASS:
                ABCPropertyGraph.CLASS_ConnectionPoint,
                ABCPropertyGraph.PROP_LABELS:
                cbm_source_cp_props.get(ABCPropertyGraph.PROP_LABELS),
                ABCPropertyGraph.PROP_CAPACITIES:
                cbm_source_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES)
            }
            source_cp_props = {k: v for (k, v) in source_cp_props.items() if v}

            abqm.add_node(node_id=source_cp_id,
                          label=ABCPropertyGraph.CLASS_ConnectionPoint,
                          props=source_cp_props)
            # FIXME: CP names may not be unique if we are dealing with a multigraph
            sink_cp_props = {
                ABCPropertyGraph.PROP_NAME:
                "_".join([sink_site, source_site]),
                ABCPropertyGraph.PROP_TYPE:
                str(InterfaceType.TrunkPort),
                ABCPropertyGraph.PROP_CLASS:
                ABCPropertyGraph.CLASS_ConnectionPoint,
                ABCPropertyGraph.PROP_LABELS:
                cbm_sink_cp_props.get(ABCPropertyGraph.PROP_LABELS),
                ABCPropertyGraph.PROP_CAPACITIES:
                cbm_sink_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES)
            }
            sink_cp_props = {k: v for (k, v) in sink_cp_props.items() if v}
            abqm.add_node(node_id=sink_cp_id,
                          label=ABCPropertyGraph.CLASS_ConnectionPoint,
                          props=sink_cp_props)
            # selectively replicate link node and its properties from CBM
            new_link_props = {
                ABCPropertyGraph.PROP_NAME:
                cbm_link_props[ABCPropertyGraph.PROP_NAME],
                ABCPropertyGraph.PROP_TYPE:
                cbm_link_props[ABCPropertyGraph.PROP_TYPE],
                ABCPropertyGraph.PROP_CLASS:
                cbm_link_props[ABCPropertyGraph.PROP_CLASS],
                ABCPropertyGraph.PROP_LAYER:
                cbm_link_props[ABCPropertyGraph.PROP_LAYER]
            }
            abqm.add_node(node_id=link,
                          label=ABCPropertyGraph.CLASS_Link,
                          props=new_link_props)
            # connect them together
            abqm.add_link(node_a=site_to_ns_node_id[source_site],
                          rel=ABCPropertyGraph.REL_CONNECTS,
                          node_b=source_cp_id)
            abqm.add_link(node_a=source_cp_id,
                          rel=ABCPropertyGraph.REL_CONNECTS,
                          node_b=link)
            abqm.add_link(node_a=link,
                          rel=ABCPropertyGraph.REL_CONNECTS,
                          node_b=sink_cp_id)
            abqm.add_link(node_a=sink_cp_id,
                          rel=ABCPropertyGraph.REL_CONNECTS,
                          node_b=site_to_ns_node_id[sink_site])

        # link facilities to their sites
        for s, lf in facilities_by_site.items():
            # multiple facilities per site possible
            for fac_sliver in lf:
                fac_nbs = cbm.get_first_and_second_neighbor(
                    node_id=fac_sliver.node_id,
                    rel1=ABCPropertyGraph.REL_HAS,
                    node1_label=ABCPropertyGraph.CLASS_NetworkService,
                    rel2=ABCPropertyGraph.REL_CONNECTS,
                    node2_label=ABCPropertyGraph.CLASS_ConnectionPoint)
                try:
                    fac_ns_node_id = fac_nbs[0][0]
                    fac_cp_node_id = fac_nbs[0][1]
                except KeyError:
                    if self.logger:
                        self.logger.warning(
                            f'Unable to trace facility ConnectionPoint for '
                            f'facility {fac_sliver.resource_name}, continuing')
                    else:
                        print(
                            f'Unable to trace facility ConnectionPoint for '
                            f'facility {fac_sliver.resource_name}, continuing')
                    continue
                _, fac_props = cbm.get_node_properties(
                    node_id=fac_sliver.node_id)
                _, fac_ns_props = cbm.get_node_properties(
                    node_id=fac_ns_node_id)
                _, fac_cp_props = cbm.get_node_properties(
                    node_id=fac_cp_node_id)

                # filter down only the needed properties then recreate the structure of facility in ABQM
                new_fac_props = {
                    ABCPropertyGraph.PROP_NAME:
                    fac_props[ABCPropertyGraph.PROP_NAME],
                    ABCPropertyGraph.PROP_TYPE:
                    fac_props[ABCPropertyGraph.PROP_TYPE]
                }
                abqm.add_node(node_id=fac_sliver.node_id,
                              label=ABCPropertyGraph.CLASS_NetworkNode,
                              props=new_fac_props)
                new_ns_props = {
                    ABCPropertyGraph.PROP_NAME:
                    fac_ns_props[ABCPropertyGraph.PROP_NAME],
                    ABCPropertyGraph.PROP_TYPE:
                    fac_ns_props[ABCPropertyGraph.PROP_TYPE]
                }
                abqm.add_node(node_id=fac_ns_node_id,
                              label=ABCPropertyGraph.CLASS_NetworkService,
                              props=new_ns_props)
                new_cp_props = {
                    ABCPropertyGraph.PROP_NAME:
                    fac_cp_props[ABCPropertyGraph.PROP_NAME],
                    ABCPropertyGraph.PROP_TYPE:
                    fac_cp_props[ABCPropertyGraph.PROP_TYPE],
                    ABCPropertyGraph.PROP_LABELS:
                    fac_cp_props.get(ABCPropertyGraph.PROP_LABELS),
                    ABCPropertyGraph.PROP_CAPACITIES:
                    fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES)
                }
                new_cp_props = {k: v for (k, v) in new_cp_props.items() if v}
                abqm.add_node(node_id=fac_cp_node_id,
                              label=ABCPropertyGraph.CLASS_ConnectionPoint,
                              props=new_cp_props)
                abqm.add_link(node_a=fac_sliver.node_id,
                              rel=ABCPropertyGraph.REL_HAS,
                              node_b=fac_ns_node_id)
                abqm.add_link(node_a=fac_ns_node_id,
                              rel=ABCPropertyGraph.REL_CONNECTS,
                              node_b=fac_cp_node_id)

                # trace the link to a switch port/ConnectionPoint and replicate them for simplicity
                fac_cp_nbs = cbm.get_first_and_second_neighbor(
                    node_id=fac_cp_node_id,
                    rel1=ABCPropertyGraph.REL_CONNECTS,
                    node1_label=ABCPropertyGraph.CLASS_Link,
                    rel2=ABCPropertyGraph.REL_CONNECTS,
                    node2_label=ABCPropertyGraph.CLASS_ConnectionPoint)
                if len(fac_cp_nbs) == 0 or len(fac_cp_nbs) > 1:
                    if self.logger:
                        self.logger.warning(
                            f'Unable to trace switch port from Facility port '
                            f'for facility {fac_sliver.resource_name} {fac_cp_nbs}'
                        )
                    else:
                        print(
                            f'Unable to trace switch port from Facility port '
                            f'for facility {fac_sliver.resource_name} {fac_cp_nbs}'
                        )
                    continue

                fac_link_id = fac_cp_nbs[0][0]
                fac_sp_id = fac_cp_nbs[0][1]

                _, fac_link_props = cbm.get_node_properties(
                    node_id=fac_link_id)
                # selectively replicate link properties
                new_link_props = {
                    ABCPropertyGraph.PROP_NAME:
                    fac_link_props[ABCPropertyGraph.PROP_NAME],
                    ABCPropertyGraph.PROP_TYPE:
                    fac_link_props[ABCPropertyGraph.PROP_TYPE],
                    ABCPropertyGraph.PROP_LAYER:
                    fac_link_props[ABCPropertyGraph.PROP_LAYER]
                }
                abqm.add_node(node_id=fac_link_id,
                              label=ABCPropertyGraph.CLASS_Link,
                              props=new_link_props)
                try:
                    abqm.get_node_properties(node_id=fac_sp_id)
                except PropertyGraphQueryException:
                    # if the node doesn't exist we need to create it (it could have been created in the first pass)
                    _, fac_sp_props = cbm.get_node_properties(
                        node_id=fac_sp_id)
                    new_sp_props = {
                        ABCPropertyGraph.PROP_NAME:
                        fac_sp_props[ABCPropertyGraph.PROP_NAME],
                        ABCPropertyGraph.PROP_TYPE:
                        fac_sp_props[ABCPropertyGraph.PROP_TYPE],
                        ABCPropertyGraph.PROP_CAPACITIES:
                        fac_sp_props.get(ABCPropertyGraph.PROP_CAPACITIES),
                        ABCPropertyGraph.PROP_LABELS:
                        fac_sp_props.get(ABCPropertyGraph.PROP_LABELS)
                    }
                    new_sp_props = {
                        k: v
                        for (k, v) in new_sp_props.items() if v
                    }
                    abqm.add_node(node_id=fac_sp_id,
                                  label=ABCPropertyGraph.CLASS_ConnectionPoint,
                                  props=new_sp_props)

                # link these together
                abqm.add_link(node_a=fac_cp_node_id,
                              rel=ABCPropertyGraph.REL_CONNECTS,
                              node_b=fac_link_id)
                abqm.add_link(node_a=fac_link_id,
                              rel=ABCPropertyGraph.REL_CONNECTS,
                              node_b=fac_sp_id)
                abqm.add_link(node_a=fac_sp_id,
                              rel=ABCPropertyGraph.REL_CONNECTS,
                              node_b=site_to_ns_node_id[s])

        return abqm
    def testComponentSliver(self):
        cs = ComponentSliver()
        cs.set_property('details', 'these are details')
        cs.set_properties(type=ComponentType.GPU, name='name')

        self.assertEqual(cs.get_details(), 'these are details')
        self.assertEqual(cs.get_type(), ComponentType.GPU)
        self.assertEqual(cs.get_name(), 'name')

        cs1 = ComponentSliver()
        cs1.set_property('details', 'these are other details')
        cs1.set_properties(type=ComponentType.GPU, name='name1')

        cs2 = ComponentSliver()
        cs2.set_property('details', 'these are some details')
        cs2.set_properties(type=ComponentType.SmartNIC, name='name2')

        acs = AttachedComponentsInfo()
        acs.add_device(cs)
        acs.add_device(cs1)
        acs.add_device(cs2)

        self.assertEqual(len(acs.by_type[ComponentType.GPU]), 2)
        self.assertEqual(len(acs.by_type[ComponentType.SmartNIC]), 1)
        self.assertEqual(len(acs.get_devices_by_type(ComponentType.SharedNIC)), 0)

        acs.remove_device(name='name')
        acs.remove_device(name='name1')
        self.assertEqual(len(acs.get_devices_by_type(ComponentType.GPU)), 0)
        self.assertEqual(len(acs.get_devices_by_type(ComponentType.SmartNIC)), 1)
    def __update_shared_nic_labels_and_capacities(
            self, *, available_component: ComponentSliver,
            requested_component: ComponentSliver) -> ComponentSliver:
        """
        Update the shared NIC Labels and Capacities. Assign the 1st available PCI address/bdf to the requested component
        Traverse the available component's labels to find the index for bdf assigned
        Using the found labels, assign BDF, MAC and VLAN address to the IFS on the Requested component
        In case of L2 service, also copy the requested IP address so it can be used by the AMHandler to configure the
        interface post VM creation
        :param available_component: Available Component
        :param requested_component: Requested Component
        :return updated requested component with VLAN, MAC and IP information
        """
        # Check labels
        delegation_id, delegated_label = self._get_delegations(
            lab_cap_delegations=available_component.get_label_delegations())

        if delegated_label.bdf is None or len(delegated_label.bdf) < 1:
            message = "No PCI devices available in the delegation"
            self.logger.error(message)
            raise BrokerException(
                error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES,
                msg=f"{message}")

        # Assign the first PCI Id from the list of available PCI slots
        requested_component.label_allocations = Labels(
            bdf=delegated_label.bdf[0])

        # Find the VLAN from the BQM Component
        if available_component.network_service_info is None or \
                len(available_component.network_service_info.network_services) != 1:
            message = "Shared NIC Card must have one Network Service"
            self.logger.error(message)
            raise BrokerException(error_code=ExceptionErrorCode.FAILURE,
                                  msg=f"{message}")

        ns_name = next(
            iter(available_component.network_service_info.network_services))
        ns = available_component.network_service_info.network_services[ns_name]

        if ns.interface_info is None or len(ns.interface_info.interfaces) != 1:
            message = "Shared NIC Card must have one Connection Point"
            self.logger.error(message)
            raise BrokerException(error_code=ExceptionErrorCode.FAILURE,
                                  msg=f"{message}")

        ifs_name = next(iter(ns.interface_info.interfaces))
        ifs = ns.interface_info.interfaces[ifs_name]

        delegation_id, ifs_delegated_labels = self._get_delegations(
            lab_cap_delegations=ifs.get_label_delegations())

        # Determine the index which points to the same PCI id as assigned above
        # This index points to the other relevant information such as MAC Address,
        # VLAN tag for that PCI device
        i = 0
        for pci_id in ifs_delegated_labels.bdf:
            if pci_id == delegated_label.bdf[0]:
                break
            i += 1

        # Updated the Requested component with VLAN, BDF, MAC
        req_ns_name = next(
            iter(requested_component.network_service_info.network_services))
        req_ns = requested_component.network_service_info.network_services[
            req_ns_name]
        req_ifs_name = next(iter(req_ns.interface_info.interfaces))
        req_ifs = req_ns.interface_info.interfaces[req_ifs_name]

        lab = Labels(bdf=ifs_delegated_labels.bdf[i],
                     mac=ifs_delegated_labels.mac[i],
                     vlan=ifs_delegated_labels.vlan[i],
                     local_name=ifs_delegated_labels.local_name[i])

        # For the Layer 2 copying the IP address to the label allocations
        # This is to be used by AM Handler to configure Network Interface
        if req_ns.layer == NSLayer.L2:
            if req_ifs.labels is not None and req_ifs.labels.ipv4 is not None:
                lab.ipv4 = req_ifs.labels.ipv4
            if req_ifs.labels is not None and req_ifs.labels.ipv6 is not None:
                lab.ipv6 = req_ifs.labels.ipv6

        req_ifs.set_label_allocations(lab=lab)

        self.logger.info(f"Assigned Interface Sliver: {req_ifs}")
        return requested_component
    def __check_component_labels_and_capacities(
            self, *, available_component: ComponentSliver, graph_id: str,
            requested_component: ComponentSliver) -> ComponentSliver:
        """
        Check if available component capacities, labels to match requested component
        :param available_component: available component
        :param graph_id: BQM graph id
        :param requested_component: requested component
        :return: requested component annotated with properties in case of success, None otherwise
        """
        if requested_component.get_model() is not None and \
                requested_component.get_model() != available_component.get_model():
            return requested_component

        # Checking capacity for component
        delegation_id, delegated_capacity = self._get_delegations(
            lab_cap_delegations=available_component.get_capacity_delegations())

        # Delegated capacity would have been decremented already to exclude allocated shared NICs
        if delegated_capacity.unit < 1:
            message = f"Insufficient Capacities for component: {requested_component}"
            self.logger.error(message)
            raise BrokerException(
                error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES,
                msg=f"{message}")

        requested_component.capacity_allocations = Capacities(unit=1)

        # Check labels
        delegation_id, delegated_label = self._get_delegations(
            lab_cap_delegations=available_component.get_label_delegations())

        if requested_component.get_type() == ComponentType.SharedNIC:
            requested_component = self.__update_shared_nic_labels_and_capacities(
                available_component=available_component,
                requested_component=requested_component)
        else:
            requested_component.label_allocations = delegated_label
            if requested_component.get_type() == ComponentType.SmartNIC:
                requested_component = self.__update_smart_nic_labels_and_capacities(
                    available_component=available_component,
                    requested_component=requested_component)

        node_map = tuple([graph_id, available_component.node_id])
        requested_component.set_node_map(node_map=node_map)

        return requested_component
    def create_unit(include_pci: bool = True,
                    include_image: bool = True,
                    include_name: bool = True,
                    include_instance_name: bool = False,
                    include_ns: bool = False) -> Unit:
        """
        Create a unit
        :param include_pci:
        :param include_image:
        :param include_name:
        :param include_instance_name:
        :param include_ns:
        :return:
        """
        u = Unit(rid=ID(uid="rid-1"))
        sliver = NodeSliver()
        cap = Capacities()
        cap.set_fields(core=4, ram=64, disk=500)
        catalog = InstanceCatalog()
        instance_type = catalog.map_capacities_to_instance(cap=cap)
        cap_hints = CapacityHints().set_fields(instance_type=instance_type)
        sliver.set_properties(
            type=NodeType.VM,
            site="RENC",
            capacity_hints=cap_hints,
            capacity_allocations=catalog.get_instance_capacities(
                instance_type=instance_type))
        sliver.label_allocations = Labels().set_fields(
            instance_parent="renc-w1.fabric-testbed.net")

        if include_name:
            sliver.set_properties(name="n1")

        if include_image:
            sliver.set_properties(image_type='qcow2',
                                  image_ref='default_centos_8')

        if include_pci:
            component = ComponentSliver()
            labels = Labels()
            labels.set_fields(bdf=["0000:41:00.0", "0000:41:00.1"])
            component.set_properties(type=ComponentType.SmartNIC,
                                     model='ConnectX-6',
                                     name='nic1',
                                     label_allocations=labels)
            sliver.attached_components_info = AttachedComponentsInfo()
            sliver.attached_components_info.add_device(device_info=component)

        if include_instance_name:
            sliver.label_allocations.set_fields(instance="instance-001")

        if include_ns:
            sliver.network_service_info = NetworkServiceInfo()
            ns = NetworkServiceSliver()
            ns.interface_info = InterfaceInfo()
            ifs1 = InterfaceSliver()
            c = Capacities()
            c.bw = 100
            c.unit = 1
            l1 = Labels()
            l1.ipv4 = '192.168.11.3'
            l1.vlan = '200'
            l1.local_name = 'p1'
            la_1 = Labels()
            la_1.mac = '0C:42:A1:EA:C7:51'
            la_1.vlan = '200'
            ifs1.capacities = c
            ifs1.labels = l1
            ifs1.label_allocations = la_1

            ifs2 = InterfaceSliver()
            ifs2.capacities = c
            l2 = Labels()
            l2.ipv4 = '192.168.11.2'
            l2.local_name = 'p2'
            la_2 = Labels()
            la_2.mac = '0C:42:A1:EA:C7:52'

            ifs2.labels = l2
            ifs2.label_allocations = la_1

            ns.interface_info.interfaces = {'ifs1': ifs1, 'ifs2': ifs2}
            sliver.network_service_info.network_services = {'ns1': ns}

        u.set_sliver(sliver=sliver)
        return u
Example #12
0
    def test_arm_load(self):
        """
        Load an ARM, transform to ADM, then merge into BQM
        :return:
        """

        plain_neo4j = self.n4j_imp.import_graph_from_file_direct(
            graph_file='RENCI-ad.graphml')

        print("Validating ARM graph")
        plain_neo4j.validate_graph()

        cbm = Neo4jCBMGraph(importer=self.n4j_imp)

        site_arm = Neo4jARMGraph(graph=Neo4jPropertyGraph(
            graph_id=plain_neo4j.graph_id, importer=self.n4j_imp))
        # generate a dict of ADMs from site graph ARM
        site_adms = site_arm.generate_adms()
        print('ADMS' + str(site_adms.keys()))

        # desired ADM is under 'primary'
        site_adm = site_adms['primary']
        cbm.merge_adm(adm=site_adm)

        cbm.validate_graph()
        print('CBM ID is ' + cbm.graph_id)

        # test delegation format
        list_of_nodes = cbm.get_matching_nodes_with_components(
            label=ABCPropertyGraphConstants.CLASS_NetworkNode,
            props={'Name': 'renc-w3'})

        print('Deleting ADM and ARM graphs')
        for adm in site_adms.values():
            adm.delete_graph()
        site_arm.delete_graph()

        #print("Printing component models")
        #for n in cbm.get_all_nodes_by_class(label=ABCPropertyGraphConstants.CLASS_Component):
        #    _, prop = cbm.get_node_properties(node_id=n)
        #    if prop.get(ABCPropertyGraphConstants.PROP_MODEL, None) is not None:
        #        print(prop[ABCPropertyGraphConstants.PROP_MODEL])

        # test CBM querying
        node_props = {'Site': 'RENC', 'Type': 'Server'}
        list_of_nodes = cbm.get_matching_nodes_with_components(
            label=ABCPropertyGraphConstants.CLASS_NetworkNode,
            props=node_props)
        self.assertEqual(len(list_of_nodes), 3)

        # construct some components
        c1 = ComponentSliver()
        c1.resource_name = 'c1'
        c1.resource_type = ComponentType.GPU
        c1.resource_model = 'Tesla T4'
        c2 = ComponentSliver()
        c2.resource_name = 'c2'
        c2.resource_type = ComponentType.SharedNIC
        c3 = ComponentSliver()
        c3.resource_name = 'c3'
        c3.resource_type = ComponentType.SmartNIC
        c3.resource_model = 'ConnectX-5'
        c4 = ComponentSliver()
        c4.resource_name = 'c4'
        c4.resource_type = ComponentType.SmartNIC
        c4.resource_model = 'ConnectX-5'

        ci = AttachedComponentsInfo()
        ci.add_device(c1)
        ci.add_device(c2)
        ci.add_device(c3)
        ci.add_device(c4)
        list_of_nodes = cbm.get_matching_nodes_with_components(
            label=ABCPropertyGraphConstants.CLASS_NetworkNode,
            props=node_props,
            comps=ci)
        print(f'Testing a mix of components #1 {list_of_nodes=}')
        self.assertEqual(len(list_of_nodes), 1)

        c5 = ComponentSliver()
        c5.resource_name = 'c5'
        c5.resource_type = ComponentType.SmartNIC
        c5.resource_model = 'ConnectX-5'
        ci.add_device(c5)
        list_of_nodes = cbm.get_matching_nodes_with_components(
            label=ABCPropertyGraphConstants.CLASS_NetworkNode,
            props=node_props,
            comps=ci)
        print(
            f'Testing a mix of components #2 {list_of_nodes=} (expected empty)'
        )
        self.assertEqual(len(list_of_nodes), 0)

        # test for SR-IOV shared cards
        ci = AttachedComponentsInfo()
        c6 = ComponentSliver()
        c6.resource_name = 'c6'
        c6.resource_type = ComponentType.SharedNIC
        c6.resource_model = 'ConnectX-6'
        ci.add_device(c6)

        c7 = ComponentSliver()
        c7.resource_name = 'c7'
        c7.resource_type = ComponentType.SharedNIC
        c7.resource_model = 'ConnectX-6'
        ci.add_device(c7)

        list_of_nodes = cbm.get_matching_nodes_with_components(
            label=ABCPropertyGraphConstants.CLASS_NetworkNode,
            props=node_props,
            comps=ci)
        print(f'Testing a mix of components #3 {list_of_nodes=}')
        self.assertEqual(len(list_of_nodes), 3)

        self.n4j_imp.delete_all_graphs()
Example #13
0
    def __attach_detach_pci(self,
                            *,
                            playbook_path: str,
                            inventory_path: str,
                            host: str,
                            instance_name: str,
                            device_name: str,
                            component: ComponentSliver,
                            user: str = None,
                            mgmt_ip: str = None,
                            attach: bool = True):
        """
        Invoke ansible playbook to attach/detach a PCI device to a provisioned VM
        :param playbook_path: playbook location
        :param inventory_path: inventory location
        :param host: host
        :param instance_name: Instance Name
        :param device_name: Device Name
        :param component: Component Sliver
        :param mgmt_ip: Management IP of the VM to which the component is attached
        :param attach: True for attach and False for detach
        :return:
        """
        self.get_logger().debug("__attach_detach_pci IN")
        try:
            pci_device_list = None
            mac = None
            if component.get_type() == ComponentType.SharedNIC:
                for ns in component.network_service_info.network_services.values(
                ):
                    if ns.interface_info is None or ns.interface_info.interfaces is None:
                        continue

                    for ifs in ns.interface_info.interfaces.values():
                        mac = ifs.label_allocations.mac
            if isinstance(component.label_allocations.bdf, str):
                pci_device_list = [component.label_allocations.bdf]
            else:
                pci_device_list = component.label_allocations.bdf

            worker_node = host

            extra_vars = {
                AmConstants.WORKER_NODE_NAME: worker_node,
                AmConstants.PCI_PROV_DEVICE: device_name
            }
            if attach:
                extra_vars[
                    AmConstants.PCI_OPERATION] = AmConstants.PCI_PROV_ATTACH
            else:
                extra_vars[
                    AmConstants.PCI_OPERATION] = AmConstants.PCI_PROV_DETACH

            self.get_logger().info(
                f"Device List Size: {len(pci_device_list)} List: {pci_device_list}"
            )
            index = 0
            for device in pci_device_list:
                ansible_helper = AnsibleHelper(inventory_path=inventory_path,
                                               logger=self.get_logger())
                ansible_helper.set_extra_vars(extra_vars=extra_vars)

                device_char_arr = self.__extract_device_addr_octets(
                    device_address=device)
                ansible_helper.add_vars(host=worker_node,
                                        var_name=AmConstants.KVM_GUEST_NAME,
                                        value=instance_name)
                ansible_helper.add_vars(host=worker_node,
                                        var_name=AmConstants.PCI_DOMAIN,
                                        value=device_char_arr[0])
                ansible_helper.add_vars(host=worker_node,
                                        var_name=AmConstants.PCI_BUS,
                                        value=device_char_arr[1])
                ansible_helper.add_vars(host=worker_node,
                                        var_name=AmConstants.PCI_SLOT,
                                        value=device_char_arr[2])
                ansible_helper.add_vars(host=worker_node,
                                        var_name=AmConstants.PCI_FUNCTION,
                                        value=device_char_arr[3])

                if mac is not None:
                    ansible_helper.add_vars(host=worker_node,
                                            var_name=AmConstants.MAC,
                                            value=mac)

                self.get_logger().info(
                    f"Executing playbook {playbook_path} to attach({attach})/detach({not attach}) "
                    f"PCI device ({device}) extra_vars: {extra_vars}")

                ansible_helper.run_playbook(playbook_path=playbook_path)

            # Configure the Network Interface card
            if attach:
                self.configure_nic(component=component,
                                   mgmt_ip=mgmt_ip,
                                   user=user)
        finally:
            self.get_logger().debug("__attach_detach_pci OUT")