示例#1
0
    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)
示例#2
0
    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
示例#5
0
    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
示例#7
0
    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__()
示例#10
0
    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
示例#11
0
    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