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