def test_L2Bridge_Hairpin(self): # create a NetworkService sliver for L2Bridge prop = { AmConstants.CONFIG_PROPERTIES_FILE: '../config/net_handler_config.yml' } handler = NetHandler(logger=self.logger, properties=prop) # # create a network sliver for L2Bridge and its interfaces # sliver = NetworkServiceSliver() # service name (set by user) - only guaranteed unique within a slice sliver.set_name('L2-UKY-Hairpin') # if service name global uniqueness is a requirement use Labels.local_name for that (optional) # e.g. concatenate name + res id (or another unique id) # sliver.set_labels(Labels().set_fields(local_name='test-l2bridge-shortname')) # per @xiyang he uses unit id for service name so this is not needed. sliver.set_type(ServiceType.L2Bridge) sliver.set_layer(NSLayer.L2) # Interface properties # # The service definitions make a distinction between interface which requires # type = parse(InterfaceSliver.Labels.local_name) # id = parse(InterfaceSliver.Labels.local_name) # outervlan = InterfaceSliver.Labels.vlan # innervlan = InterfaceSliver.Labels.inner_vlan # bw = InterfaceSliver.Capacities.bw (0 - best-effort) # burst size = InterfaceSliver.Capacities.burst_size # # and STP which in addition also requires NSO device name. # In deep network sliver NSO Device name goes on *each* interface, then handler.create can parse # out the interfaces and figure out which STP each interface goes with based on that. # NSO device name = InterfaceSliver.Labels.device_name # # The properties of InterfaceSlivers noted above must be copied by Orchestrator from various places # a) the switch TrunkPort port the ASM ServicePort maps to in CBM # b) the Shared or Dedicated ASM port on the card the ServicePort peers with in ASM # c) the Shared or Dedicated CBM port the peer ASM port maps to # Below for each property comments indicate where they come from by a, b, c # Orchestrator determines peer ports in ASM (between ServicePort and corresponding Shared/Dedicated card port) # and sets nodemaps to point from ASM ServicePort to corresponding CBM TrunkPort # as well as between Shared/Dedicated ASM port on the NIC and the corresponding CBM Shared/Dedicated port # # create a small number of Interface slivers, set their properties and link to service # isl1 = InterfaceSliver() # the name is set by FIM as '-' concatenation of service name isl1.set_name('Interface1') # this will be a ServicePort in the network service sliver. It is created by FIM automatically when # the user adds a NetworkService to the ASM. The name is set by the FIM as '-' concatenation of service # name and peer interface sliver name. isl1.set_type(InterfaceType.ServicePort) sliver_labels = Labels() sliver_capacities = Capacities() # inner_vlan - not used for now - user would fill it in directly on the sliver Labels - # need to discuss. # sl1labs.set_fields(inner_vlan='3') # vlan - source: (c) sliver_labels.set_fields(vlan='101') # local_name source: (a) sliver_labels.set_fields(local_name='HundredGigE0/0/0/5') # NSO device name source: (a) - need to find the owner switch of the network service in CBM # and take its .name or labels.local_name sliver_labels.set_fields(device_name='uky-data-sw') # capacities (bw in Gbps, burst size is in Mbytes) source: (b) sliver_capacities.set_fields(bw=1) # assign labels and capacities isl1.set_labels(sliver_labels) isl1.set_capacities(sliver_capacities) # # Second interface (comments for field info origin omitted below) # isl2 = InterfaceSliver() isl2.set_name('Interface2') isl2.set_type(InterfaceType.ServicePort) sliver_labels = Labels() sliver_capacities = Capacities() sliver_labels.set_fields(vlan='102') sliver_labels.set_fields(inner_vlan='200') sliver_labels.set_fields(local_name='HundredGigE0/0/0/5') sliver_labels.set_fields(device_name='uky-data-sw') sliver_capacities.set_fields(bw=1) isl2.set_labels(sliver_labels) isl2.set_capacities(sliver_capacities) # create interface info object, add populated interfaces to it ifi = InterfaceInfo() ifi.add_interface(isl1) ifi.add_interface(isl2) # add interface info object to sliver. All of this happens automagically normally sliver.interface_info = ifi # set a fake unit reservation uid = uuid.uuid3(uuid.NAMESPACE_DNS, 'test_L2Bridge') self.unit = Unit(rid=ID(uid=str(uid))) self.unit.set_sliver(sliver=sliver) # # create a service (create needs to parse out sliver information # into exact parameters the service ansible script needs) # r, updated_unit = handler.create(unit=self.unit) self.assertEqual(r[Constants.PROPERTY_TARGET_NAME], Constants.TARGET_CREATE) self.assertEqual(r[Constants.PROPERTY_ACTION_SEQUENCE_NUMBER], 0) self.assertEqual(r[Constants.PROPERTY_TARGET_RESULT_CODE], Constants.RESULT_CODE_OK) time.sleep(30) # # delete - need to make sure the updated unit has the right info to delete the service # r, updated_unit = handler.delete(updated_unit) self.assertEqual(r[Constants.PROPERTY_TARGET_NAME], Constants.TARGET_DELETE) self.assertEqual(r[Constants.PROPERTY_ACTION_SEQUENCE_NUMBER], 0) self.assertEqual(r[Constants.PROPERTY_TARGET_RESULT_CODE], Constants.RESULT_CODE_OK)
def test_L2PTP(self): # create a NetworkService sliver for L2PTP prop = { AmConstants.CONFIG_PROPERTIES_FILE: '../config/net_handler_config.yml' } handler = NetHandler(logger=self.logger, properties=prop) # # create a network sliver for L2Bridge and its interfaces # sliver = NetworkServiceSliver() # service name - can we use the sliver name - only guaranteed unique in the slice sliver.set_name('L2PTPServiceTest') sliver.set_type(ServiceType.L2PTP) sliver.set_layer(NSLayer.L2) # ERO # first declare a path. Each path is a list of somethings. a2z and z2a maintained separately within Path ero_path = Path() ero_path.set_symmetric(["10.1.1.1", "10.1.1.2"]) # default is loose ERO, set strict=True if want otherwise ero = ERO(strict=False) ero.set(ero_path) sliver.set_ero(ero) # # STP_A interface # stp_a = InterfaceSliver() stp_a.set_name('Interface1') stp_a.set_type(InterfaceType.ServicePort) sliver_labels = Labels() sliver_capacities = Capacities() sliver_labels.set_fields(vlan='235') sliver_labels.set_fields(local_name='HundredGigE0/0/0/17') sliver_labels.set_fields(device_name='renc-data-sw') sliver_capacities.set_fields(bw=1000) stp_a.set_labels(sliver_labels) stp_a.set_capacities(sliver_capacities) # # STP_Z interface # stp_z = InterfaceSliver() stp_z.set_name('Interface2') stp_z.set_type(InterfaceType.ServicePort) sliver_labels = Labels() sliver_capacities = Capacities() sliver_labels.set_fields(vlan='235') sliver_labels.set_fields(local_name='HundredGigE0/0/0/13') sliver_labels.set_fields(device_name='uky-data-sw') sliver_capacities.set_fields(bw=1000) stp_z.set_labels(sliver_labels) stp_z.set_capacities(sliver_capacities) # create interface info object, add interfaces to it ifi = InterfaceInfo() ifi.add_interface(stp_a) ifi.add_interface(stp_z) # All of this happens automagically in FIM sliver.interface_info = ifi uid = uuid.uuid3(uuid.NAMESPACE_DNS, 'test_L2PTP') self.unit = Unit(rid=ID(uid=str(uid))) self.unit.set_sliver(sliver=sliver) # # create a service # r, updated_unit = handler.create(unit=self.unit) self.assertEqual(r[Constants.PROPERTY_TARGET_NAME], Constants.TARGET_CREATE) self.assertEqual(r[Constants.PROPERTY_ACTION_SEQUENCE_NUMBER], 0) self.assertEqual(r[Constants.PROPERTY_TARGET_RESULT_CODE], Constants.RESULT_CODE_OK) time.sleep(30) # # delete - need to make sure the updated unit has the right info to delete the service # r, updated_unit = handler.delete(updated_unit) self.assertEqual(r[Constants.PROPERTY_TARGET_NAME], Constants.TARGET_DELETE) self.assertEqual(r[Constants.PROPERTY_ACTION_SEQUENCE_NUMBER], 0) self.assertEqual(r[Constants.PROPERTY_TARGET_RESULT_CODE], Constants.RESULT_CODE_OK)
def allocate( self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, graph_node: BaseSliver, existing_reservations: List[ABCReservationMixin] ) -> Tuple[str, BaseSliver]: """ Allocate an extending or ticketing reservation :param rid: reservation id of the reservation to be allocated :param requested_sliver: requested sliver :param graph_id: BQM graph id :param graph_node: BQM graph node identified to serve the reservation :param existing_reservations: Existing Reservations served by the same BQM node :return: Tuple of Delegation Id and the Requested Sliver annotated with BQM Node Id and other properties :raises: BrokerException in case the request cannot be satisfied """ if graph_node.get_capacity_delegations() is None or rid is None: raise BrokerException( error_code=Constants.INVALID_ARGUMENT, msg=f"capacity_delegations is missing or reservation is None") if not isinstance(requested_sliver, NodeSliver): raise BrokerException( error_code=Constants.INVALID_ARGUMENT, msg=f"resource type: {requested_sliver.get_type()}") if not isinstance(graph_node, NodeSliver): raise BrokerException( error_code=Constants.INVALID_ARGUMENT, msg=f"resource type: {graph_node.get_type()}") # Always use requested capacities to be mapped from flavor i.e. capacity hints requested_capacity_hints = requested_sliver.get_capacity_hints() catalog = InstanceCatalog() requested_capacities = catalog.get_instance_capacities( instance_type=requested_capacity_hints.instance_type) # Check if Capacities can be satisfied delegation_id = self.__check_capacities( rid=rid, requested_capacities=requested_capacities, delegated_capacities=graph_node.get_capacity_delegations(), existing_reservations=existing_reservations) # Check if Components can be allocated if requested_sliver.attached_components_info is not None: requested_sliver.attached_components_info = self.__check_components( rid=rid, requested_components=requested_sliver.attached_components_info, graph_id=graph_id, graph_node=graph_node, existing_reservations=existing_reservations) requested_sliver.capacity_allocations = Capacities() requested_sliver.capacity_allocations = Labels.update( lab=requested_capacities) requested_sliver.label_allocations = Labels( instance_parent=graph_node.get_name()) requested_sliver.set_node_map(node_map=(graph_id, graph_node.node_id)) self.logger.info( f"Reservation# {rid} is being served by delegation# {delegation_id} " f"node# [{graph_id}/{graph_node.node_id}]") return delegation_id, requested_sliver
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 testNodesAndComponents(self): n1 = self.topo.add_node(name='Node1', site='RENC') n2 = self.topo.add_node(name='Node2', site='RENC', ntype=f.NodeType.Server) n3 = self.topo.add_node(name='Node3', site='RENC', management_ip='123.45.67.98', image_ref='http://some.image', image_type='image type') n3.rename('node3') assert (self.topo.nodes['Node1'] is not None) assert (self.topo.nodes['Node2'] is not None) assert (self.topo.nodes['node3'] is not None) with self.assertRaises(KeyError): n = self.topo.nodes['Node3'] # properties checks n3.set_properties(details='some details') self.assertEqual(n3.get_property('details'), 'some details') self.assertEqual(n3.details, 'some details') n3.details = 'other details' self.assertEqual(n3.details, 'other details') self.assertTrue('capacities' in n3.list_properties()) self.assertEqual(n3.get_property('image_ref'), 'http://some.image') self.assertEqual(n3.image_ref, 'http://some.image') n3.image_ref = 'http://other.image' self.assertEqual(n3.image_ref, 'http://other.image') # component checks n1.add_component(ctype=f.ComponentType.GPU, model='RTX6000', name='gpu1') n1.add_component(ctype=f.ComponentType.SharedNIC, model='ConnectX-6', name='nic1') n2.add_component(ctype=f.ComponentType.SmartNIC, model='ConnectX-6', name='nic2') n3.add_component(ctype=f.ComponentType.SharedNIC, model='ConnectX-6', name='nic3') self.assertEqual(len(n1.components), 2) self.assertEqual(len(n2.components), 1) gpu1 = n1.components['gpu1'] nic1 = n1.components['nic1'] nic2 = n2.components['nic2'] p1 = nic1.interfaces['nic1-p1'] p1lab = p1.get_property('labels') self.assertEqual(p1lab.local_name, "p1") p1 = nic2.interfaces['nic2-p1'] p1lab = p1.get_property('labels') self.assertEqual(p1lab.local_name, "p1") p1lab = p1.labels self.assertEqual(p1lab.local_name, "p1") p2 = nic2.interfaces['nic2-p2'] p2lab = p2.get_property('labels') self.assertEqual(p2lab.local_name, "p2") p2lab = p2.labels self.assertEqual(p2lab.local_name, "p2") # check parent code assert (self.topo.get_parent_element(e=gpu1) == n1) assert (self.topo.get_parent_element( e=p1) == self.topo.get_parent_element(e=p2)) # name uniqueness enforcement with self.assertRaises(TopologyException) as e: self.topo.add_node(name='Node1', site='UKY') # storage n1.add_storage(name='mystorage', labels=Labels(local_name='volume_name')) with self.assertRaises(Exception) as e: n1.remove_storage(name='wrong_name') n1.remove_storage(name='mystorage') gpu1_1 = n2.add_component(ctype=f.ComponentType.GPU, model='RTX6000', name='gpu1') n2.remove_component(name='gpu1') nic1_1 = n2.add_component(ctype=f.ComponentType.SmartNIC, model='ConnectX-6', name='nic1') n2.remove_component(name='nic1') # check various modes of access assert (self.topo.nodes['Node1'] == n1) assert (self.topo.nodes['Node1'].components['gpu1'] == gpu1) assert (len(self.topo.interface_list) == 4) assert (len(nic1.interface_list) == 1) assert (len(nic2.interface_list) == 2) assert (len(list( nic1.network_services.values())[0].interface_list) == 1) self.assertTrue(len(gpu1.interfaces) == 0) self.assertTrue(len(nic1.interfaces) == 1) nic1.interface_list[0].update_capacities(bw=50, unit=1) nic1.interface_list[0].update_labels(ipv4="192.168.1.12") self.assertEqual(nic1.interface_list[0].get_property('capacities').bw, 50) self.assertEqual(nic1.interface_list[0].capacities.unit, 1) self.assertEqual(nic1.interface_list[0].capacities.disk, 0) self.assertEqual(nic1.interface_list[0].labels.local_name, 'p1') self.assertEqual( n1.components['nic1'].interface_list[0].get_property( 'labels').ipv4, "192.168.1.12") self.assertEqual(n1.components['nic1'].interface_list[0].labels.ipv4, "192.168.1.12") # comparisons nic11 = n1.components['nic1'] self.assertEqual(nic1, nic11) # interfaces and links self.assertEqual(len(self.topo.interface_list), 4) # no peers yet service_port = self.topo.interface_list[0].get_peers() self.assertEqual(service_port, None) self.topo.add_network_service(name='s1', nstype=f.ServiceType.L2Bridge, interfaces=self.topo.interface_list) print(self.topo.network_services['s1']) assert ( self.topo.network_services['s1'].get_property('site') == 'RENC') # test peer code service_port = self.topo.interface_list[0].get_peers()[0] print(f'This is a service port {service_port}') self.assertEqual(service_port.get_property('type'), f.InterfaceType.ServicePort) # back to self self_port = service_port.get_peers()[0] self.assertEqual(self_port, self.topo.interface_list[0]) # nodemap and unset n1.set_properties(node_map=('dead-beef-graph', 'dead-beef-node')) assert n1.get_property('node_map') == ('dead-beef-graph', 'dead-beef-node') n1.unset_property('node_map') assert n1.get_property('node_map') is None n1.node_map = ('dead-beef-graph', 'dead-beef-node') assert n1.node_map == ('dead-beef-graph', 'dead-beef-node') n1.unset_property('node_map') assert n1.get_property('node_map') is None self.assertEqual(len(self.topo.links), 4) # 4 services - 3 in NICs, one global self.assertEqual(len(self.topo.network_services), 4) # removal checks self.topo.remove_node(name='Node2') self.assertTrue(len(self.topo.links), 1) self.assertTrue(len(self.topo.nodes), 2) n1.remove_component(name='nic1') self.assertEqual(len(self.topo.links), 1) n3.remove_component(name='nic3') self.assertEqual(len(self.topo.links), 0) # GPU left self.assertTrue(len(n1.components), 1) # remove remaining nodes self.topo.remove_node(name='node3') self.topo.remove_node(name='Node1') self.assertEqual(len(self.topo.nodes), 0) self.assertEqual(len(self.topo.interface_list), 0) self.assertEqual(len(self.topo.links), 0) self.assertEqual(len(self.topo.network_services), 1) self.topo.remove_network_service(name='s1') self.assertEqual(len(self.topo.network_services), 0)
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 testNetworkServices(self): n1 = self.topo.add_node(name='Node1', site='RENC') n2 = self.topo.add_node(name='Node2', site='UKY', ntype=f.NodeType.Server) n3 = self.topo.add_node(name='Node3', site='RENC', management_ip='123.45.67.98', image_ref='http://some.image', image_type='image type') # component checks n1.add_component(ctype=f.ComponentType.GPU, model='RTX6000', name='gpu1') n1.add_component(model_type=f.ComponentModelType.SharedNIC_ConnectX_6, name='nic1') n1.add_component(model_type=f.ComponentModelType.SmartNIC_ConnectX_6, name='nic4') n2.add_component(ctype=f.ComponentType.SmartNIC, model='ConnectX-6', name='nic2') nc3 = n3.add_component(ctype=f.ComponentType.SharedNIC, model='ConnectX-6', name='nic3', boot_script='#!/bin/bash echo *') #tags on model elements (nodes, links, components, interfaces, network services) n1.tags = f.Tags('blue', 'heavy') self.assertTrue('blue' in n1.tags) # unset the tags n1.tags = None self.assertEqual(n1.tags, None) # flags on model elements n1.flags = f.Flags(auto_config=True) self.assertTrue(n1.flags.auto_config) n1.flags = None self.assertEqual(n1.flags, None) self.assertEqual(nc3.boot_script, '#!/bin/bash echo *') #boot script on nodes only n1.boot_script = """ #!/bin/bash echo * """ self.assertTrue("bash" in n1.boot_script) n1.boot_script = None self.assertIsNone(n1.boot_script) # measurement data on model elements (nodes, links, components, interfaces, network services) # can be set simply as json string (string length not to exceed 1M) n1.mf_data = json.dumps( {'k1': ['some', 'measurement', 'configuration']}) # you are guaranteed that whatever is on mf_data is JSON parsable and can be reconstituted into # an object mf_object1 = n1.mf_data self.assertTrue( mf_object1['k1'] == ['some', 'measurement', 'configuration']) # when nothing is set, it is None self.assertEqual(n2.mf_data, None) # you can also set it as MeasurementData object my_meas_data_object = {'key1': {'key2': ['v1', 2]}} n1.mf_data = f.MeasurementData(json.dumps(my_meas_data_object)) # you can also just pass a JSON serializable object to MeasurementData constructor: n1.mf_data = f.MeasurementData(my_meas_data_object) # or even an serializable object itself. Either way the limit of 1M on the JSON string # length is enforced n1.mf_data = my_meas_data_object # for most uses, just set the object my_meas_data_object = {'key1': {'key2': ['some', 'config', 'info']}} n1.mf_data = my_meas_data_object # you get back your object (in this case a dict) self.assertTrue(isinstance(n1.mf_data, dict)) mf_object2 = n1.mf_data self.assertTrue( mf_object2['key1'] == {'key2': ['some', 'config', 'info']}) class MyClass: def __init__(self, val): self.val = val # this is not a valid object - json.dumps() will fail on it bad_meas_data_object = {'key1': MyClass(3)} with self.assertRaises(MeasurementDataError): n1.mf_data = bad_meas_data_object # also cannot use bad strings with self.assertRaises(MeasurementDataError): # you cannot assign non-json string to measurement data either as MeasurementData object n1.mf_data = f.MeasurementData("not parsable json") with self.assertRaises(MeasurementDataError): # or directly as string n1.mf_data = 'random string' # most settable properties can be unset by setting them to None (there are exceptions, like e.g. name) n1.mf_data = None self.assertIsNone(n1.mf_data) gpu1 = n1.components['gpu1'] nic1 = n1.components['nic1'] nic2 = n2.components['nic2'] p1 = nic2.interfaces['nic2-p1'] p2 = nic2.interfaces['nic2-p2'] cap = f.Capacities(bw=50, unit=1) nic1.capacities = cap lab = f.Labels(ipv4="192.168.1.12") nic1.labels = lab caphints = f.CapacityHints(instance_type='blah') n1.capacity_hints = caphints # check capacities, hints labels on the graph n1p = self.topo.nodes['Node1'] caphints1 = n1p.get_property('capacity_hints') assert (caphints.instance_type == caphints1.instance_type) caphints1 = n1p.capacity_hints assert (caphints.instance_type == caphints1.instance_type) #s1 = self.topo.add_network_service(name='s1', nstype=f.ServiceType.L2Bridge, interfaces=self.topo.interface_list) s1 = self.topo.add_network_service(name='s1', nstype=f.ServiceType.L2STS, interfaces=[ n1.interface_list[0], n2.interface_list[0], n3.interface_list[0] ]) # facilities fac1 = self.topo.add_facility(name='RENCI-DTN', site='RENC', capacities=f.Capacities(bw=10), labels=f.Labels(vlan='100')) # facility needs to be connected via a service to something else sfac = self.topo.add_network_service( name='s-fac', nstype=f.ServiceType.L2STS, interfaces=[fac1.interface_list[0], n1.interface_list[2]]) self.assertEqual(s1.layer, f.Layer.L2) print(fac1.network_services['RENCI-DTN-ns'].labels) self.assertEqual(fac1.network_services['RENCI-DTN-ns'].layer, f.Layer.L2) self.assertEqual(fac1.interface_list[0].labels.vlan, '100') # this is typically done by orchestrator s1.gateway = Gateway( Labels(ipv4_subnet="192.168.1.0/24", ipv4="192.168.1.1", mac="00:11:22:33:44:55")) self.assertEqual(s1.gateway.gateway, "192.168.1.1") self.assertEqual(s1.gateway.subnet, "192.168.1.0/24") print(f'S1 has these interfaces: {s1.interface_list}') self.assertEqual(len(s1.interface_list), 3) self.topo.validate() # Import it in the neo4j as ASM slice_graph = self.topo.serialize() generic_graph = self.n4j_imp.import_graph_from_string( graph_string=slice_graph) asm_graph = Neo4jASMFactory.create(generic_graph) asm_graph.validate_graph() self.n4j_imp.delete_all_graphs() s1p = self.topo.network_services['s1'] print(f'S1 has these interfaces: {s1p.interface_list}') s1.disconnect_interface(interface=p1) print(f'S1 has these interfaces: {s1.interface_list}') self.assertEqual(len(s1.interface_list), 2) # validate the topology self.topo.validate() # Import it in the neo4j as ASM generic_graph = self.n4j_imp.import_graph_from_string( graph_string=slice_graph) asm_graph = Neo4jASMFactory.create(generic_graph) asm_graph.validate_graph() self.n4j_imp.delete_all_graphs() self.topo.remove_network_service('s1') with self.assertRaises(TopologyException): # connection conflict because we have an interface connecting to s-fac s2 = self.topo.add_network_service( name='s2', nstype=f.ServiceType.L2PTP, interfaces=self.topo.interface_list) s2 = self.topo.add_network_service( name='s2', nstype=f.ServiceType.L2PTP, interfaces=[n1.interface_list[1], n2.interface_list[1]]) self.assertEqual(len(s2.interface_list), 2) print(f'S2 has these interfaces: {s2.interface_list}') print(f'There are {self.topo.links} links left in topology') self.assertEqual(len(self.topo.links), 4) print( f'There are {self.topo.network_services} Network services in topology' ) self.assertEqual(len(self.topo.network_services), 7) self.topo.validate() self.topo.remove_network_service('s2') self.topo.validate() self.assertEqual(len(self.topo.network_services), 6) n1.remove_component('nic1') self.assertEqual(len(self.topo.network_services), 5)
def from_json(cls, json_string: str): return Gateway(Labels.from_json(json_string))
class Gateway: def __init__(self, lab: Labels or None): """ Labels specify IPv4 or IPv6 subnet, IPv4 or IPv6 gateway address and an optional MAC. Specify the following fields on Labels object: - ipv4_subnet and ipv4 or - ipv6_subnet and ipv6 and an optional mac :param lab: """ if lab is None: self.lab = None return # labels must only specify IPv4 or v6 subnet and optional mac if lab.ipv4_subnet is not None and lab.ipv4 is not None: self.lab = Labels(ipv4_subnet=lab.ipv4_subnet, ipv4=lab.ipv4) elif lab.ipv6_subnet is not None and lab.ipv6 is not None: self.lab = Labels(ipv6_subnet=lab.ipv6_subnet, ipv6=lab.ipv6) else: raise GatewayException('Gateway must specify IPv4 or IPv6 subnet, gateway address and an optional MAC') if lab.mac is not None: self.lab.mac = lab.mac @property def gateway(self) -> str: if self.lab is not None: return self.lab.ipv4 if self.lab.ipv4 is not None else self.lab.ipv6 return None @property def subnet(self) -> str: if self.lab is not None: return self.lab.ipv4_subnet if self.lab.ipv4_subnet is not None else self.lab.ipv6_subnet return None @property def mac(self) -> str: if self.lab is not None: return self.lab.mac return None def to_json(self) -> str: if self.lab is not None: return self.lab.to_json() return None @classmethod def from_json(cls, json_string: str): return Gateway(Labels.from_json(json_string)) def __str__(self): ar = list() if self.lab is None: return "" if self.lab.ipv4_subnet is not None: ar.append("IPv4 subnet: " + self.lab.ipv4_subnet + " GW: " + self.lab.ipv4) else: ar.append("IPv6: " + self.lab.ipv6_subnet + " GW: " + self.lab.ipv6) if self.lab.mac is not None: ar.append("(MAC: " + self.lab.mac + ")") return " ".join(ar) def __repr__(self): return self.__str__()
def allocate_ifs( self, *, requested_ns: NetworkServiceSliver, requested_ifs: InterfaceSliver, owner_switch: NodeSliver, mpls_ns: NetworkServiceSliver, bqm_ifs_id: str, existing_reservations: List[ABCReservationMixin] ) -> InterfaceSliver: """ Allocate Interface Sliver - For L2 services, validate the VLAN tag specified is within the allowed range - For L3 services, - grab the VLAN from BQM Site specific NetworkService - exclude the VLAN already assigned to other Interface Sliver on the same port - allocate the first available VLAN to the Interface Sliver :param requested_ns: Requested NetworkService :param requested_ifs: Requested Interface Sliver :param owner_switch: BQM Owner site switch identified to serve the InterfaceSliver :param mpls_ns: BQM MPLS NetworkService identified to serve the InterfaceSliver :param bqm_ifs_id: BQM InterfaceSliver identified to serve the InterfaceSliver :param existing_reservations: Existing Reservations which also are served by the owner switch :return Interface Sliver updated with the allocated VLAN tag for FABNetv4 and FABNetv6 services :raises Exception if vlan tag range is not in the valid range for L2 services Return the sliver updated with the VLAN """ if requested_ns.get_layer() == NSLayer.L2: delegation_id, delegated_label = self._get_delegations( lab_cap_delegations=mpls_ns.get_label_delegations()) vlans = None if delegated_label.vlan_range is not None: vlans = delegated_label.vlan_range.split("-") if vlans is not None and requested_ifs.labels.vlan is not None and \ int(vlans[0]) > int(requested_ifs.labels.vlan) > int(vlans[1]): raise BrokerException( error_code=ExceptionErrorCode.FAILURE, msg=f"Vlan for L2 service is outside the allowed range " f"{mpls_ns.label_delegations.vlan_range}") else: for ns in owner_switch.network_service_info.network_services.values( ): if requested_ns.get_type() == ns.get_type(): # Grab Label Delegations delegation_id, delegated_label = self._get_delegations( lab_cap_delegations=ns.get_label_delegations()) # Get the VLAN range vlans = None vlan_range = None if delegated_label.vlan_range is not None: vlans = delegated_label.vlan_range.split("-") vlan_range = list(range(int(vlans[0]), int(vlans[1]))) # Exclude the already allocated VLANs and subnets if existing_reservations is not None: for reservation in existing_reservations: # For Active or Ticketed or Ticketing reservations; reduce the counts from available allocated_sliver = None if reservation.is_ticketing( ) and reservation.get_approved_resources( ) is not None: allocated_sliver = reservation.get_approved_resources( ).get_sliver() if (reservation.is_active() or reservation.is_ticketed()) and \ reservation.get_resources() is not None: allocated_sliver = reservation.get_resources( ).get_sliver() self.logger.debug( f"Existing res# {reservation.get_reservation_id()} allocated: {allocated_sliver}" ) if allocated_sliver is None: continue # Ignore reservations for L2 services if allocated_sliver.get_type() != ServiceType.FABNetv4 or \ allocated_sliver.get_type() != ServiceType.FABNetv6: continue if allocated_sliver.interface_info is None or allocated_sliver.interface_info.interfaces is None: continue for allocated_ifs in allocated_sliver.interface_info.interfaces.values( ): # Ignore the Interface Slivers not on the same port if allocated_ifs.get_node_map( )[0] != bqm_ifs_id: continue self.logger.debug( f"Excluding already allocated VLAN: " f"{allocated_ifs.label_allocations.vlan} to " f"res# {reservation.get_reservation_id()}") # Exclude VLANs on the allocated on the same port if vlan_range is not None and allocated_ifs.label_allocations.vlan in vlan_range: vlan_range.remove( int(allocated_sliver.label_allocations. vlan)) # Allocate the first available VLAN if vlan_range is not None: requested_ifs.labels.vlan = str(vlan_range[0]) requested_ifs.label_allocations = Labels( vlan=str(vlan_range[0])) break return requested_ifs
def allocate( self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_switch: NodeSliver, existing_reservations: List[ABCReservationMixin] ) -> NetworkServiceSliver: """ Allocate Network Service Sliver (Only for L3 Service) - grab the /17 or /48 from BQM Site specific NetworkService - divide it into /24 or /64 subnets - exclude the 1st subnet (reserved for control plane) - exclude the subnets already assigned to other V3/V4 NetworkService on the same owner switch - allocate the first available subnet to the NetworkService :param requested_ns: Requested NetworkService :param owner_switch: BQM Owner site switch identified to serve the NetworkService :param existing_reservations: Existing Reservations which also are served by the owner switch :return NetworkService updated with the allocated subnet for FABNetv4 and FABNetv6 services Return the sliver updated with the subnet """ if requested_ns.get_type( ) != ServiceType.FABNetv4 and requested_ns.get_type( ) != ServiceType.FABNetv6: return requested_ns for ns in owner_switch.network_service_info.network_services.values(): if requested_ns.get_type() == ns.get_type(): # Grab Label Delegations delegation_id, delegated_label = self._get_delegations( lab_cap_delegations=ns.get_label_delegations()) subnet_list = None # Get Subnet if ns.get_type() == ServiceType.FABNetv6: ip_network = IPv6Network(delegated_label.ipv6_subnet) subnet_list = list(ip_network.subnets(new_prefix=64)) elif ns.get_type() == ServiceType.FABNetv4: ip_network = IPv4Network(delegated_label.ipv4_subnet) subnet_list = list(ip_network.subnets(new_prefix=24)) # Exclude the 1st subnet as it is reserved for control plane subnet_list.pop(0) # Exclude the already allocated VLANs and subnets for reservation in existing_reservations: if rid == reservation.get_reservation_id(): continue # For Active or Ticketed or Ticketing reservations; reduce the counts from available allocated_sliver = None if reservation.is_ticketing( ) and reservation.get_approved_resources() is not None: allocated_sliver = reservation.get_approved_resources( ).get_sliver() if (reservation.is_active() or reservation.is_ticketed()) and \ reservation.get_resources() is not None: allocated_sliver = reservation.get_resources( ).get_sliver() self.logger.debug( f"Existing res# {reservation.get_reservation_id()} allocated: {allocated_sliver}" ) if allocated_sliver is None: continue if allocated_sliver.get_type() != requested_ns.get_type(): continue if allocated_sliver.get_type() == ServiceType.FABNetv4: subnet_to_remove = IPv4Network( allocated_sliver.get_gateway().lab.ipv4_subnet) subnet_list.remove(subnet_to_remove) self.logger.debug( f"Excluding already allocated IP4Subnet: " f"{allocated_sliver.get_gateway().lab.ipv4_subnet}" f" to res# {reservation.get_reservation_id()}") elif allocated_sliver.get_gateway( ).lab.ipv6_subnet is not None: subnet_to_remove = IPv6Network( allocated_sliver.get_gateway().lab.ipv6_subnet) subnet_list.remove(subnet_to_remove) self.logger.debug( f"Excluding already allocated IPv6Subnet: " f"{allocated_sliver.get_gateway().lab.ipv6_subnet}" f" to res# {reservation.get_reservation_id()}") gateway_labels = Labels() if requested_ns.get_type() == ServiceType.FABNetv4: gateway_labels.ipv4_subnet = subnet_list[0].with_prefixlen gateway_labels.ipv4 = str(next(subnet_list[0].hosts())) elif requested_ns.get_type() == ServiceType.FABNetv6: gateway_labels.ipv6_subnet = subnet_list[0].with_prefixlen gateway_labels.ipv6 = str(next(subnet_list[0].hosts())) requested_ns.gateway = Gateway(lab=gateway_labels) break return requested_ns