fabric-testbed / InformationModel

FABRIC Information Model library
MIT License
7 stars 1 forks source link

L3VPN interface PeerLabels #151

Closed ibaldin closed 1 year ago

ibaldin commented 1 year ago

Example code for ASMs - it creates two nodes, links them with L3VPN service, then creates AL2S service linked to a FacilityPort, finally AL2S and L3VPN services are 'peered' with each other and an example shows how Labels from one service's interfaces can be copied onto PeerLabels of the opposite service's interface.

        n1 = t.add_node(name='n1', site='MASS')
        n1.add_component(name='nic1', model_type=ComponentModelType.SharedNIC_ConnectX_6)
        n2 = t.add_node(name='n2', site='RENC')
        n2.add_component(name='nic1', model_type=ComponentModelType.SharedNIC_ConnectX_6)

        ns1 = t.add_network_service(name='ns1', nstype=ServiceType.L3VPN,
                              interfaces=[n1.interface_list[0], n2.interface_list[0]],
                              # you can also specify ipv4/ipv6 addresses, subnets as usual
                              labels=Labels(asn='654342', ipv4_subnet='192.168.1.1/24'))

        # add facility
        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 (in this case AL2S stand-in)
        al2s = self.topo.add_network_service(name='al2s', nstype=f.ServiceType.L3VPN,
                                             interfaces=[fac1.interface_list[0]])

        # 'peer' AL2S with our own L3VPN
        al2s.peer(ns1, labels=Labels(asn='12345', bgp_key='secret', ipv4_subnet='192.168.1.1/24'))

        # normally called by orchestrator, but generally idempotent (so can be called by the user without harm)
        # copies from AL2S peering interface Labels onto the corresponding ns1 interface's PeerLabels
        ns1.copy_to_peer_labels() 

        # check values set on copy
        self.assertEqual(ns1.interfaces[ns1.name + '-' + al2s.name].peer_labels.bgp_key, 'secret')

Will provide NetworkService sliver example code in AMHandlers repo shortly.

ibaldin commented 1 year ago

This is now fabric_fim-1.4.0rc2

ibaldin commented 1 year ago

See AMHandlers example here: fabric-testbed/AMHandlers#74

xi-yang commented 1 year ago

I am still having problem with adding labels (w/ IP + ASN) to NetworkService instead of interfaces.

If we do the current way, we can only have 1 peering in the L3VPN.

I would prefer something like this.

        # peering over FABRIC-AL2S facility port
        fac0 = self.topo.add_facility(name='Internet2-Starlight', site='STAR', capacities=f.Capacities(bw=10),
                                      labels=f.Labels(vlan='3737', asn='55038',  ipv4_subnet='192.168.0.1/24'))
        # peering over AL2S-AWS facility port
        fac1 = self.topo.add_facility(name='AL2S-AWS-East', site='WASH', capacities=f.Capacities(bw=10),
                                      labels=f.Labels(vlan='100', asn='55038',  ipv4_subnet='192.168.1.1/24'))
        # additional / optional cloud peering
        fac2 = self.topo.add_facility(name='AL2S-GCP-West', site='SEAT', capacities=f.Capacities(bw=10),
                                      labels=f.Labels(vlan='200', asn='55038', ipv4_subnet='192.168.2.1/24'))

        al2s = self.topo.add_network_service(name='al2s', nstype=f.ServiceType.L3VPN,
                                             interfaces=[fac0.interface_list[0], fac1.interface_list[0], fac2.interface_list[0]])

        # then peering from the facility port using fac.interface_list[0].peer() or fac.peer() 
        fac0.interface_list[0].peer(al2s, labels=Labels(asn='398900' bgp_key='secret', ipv4_subnet='192.168.0.2/24'))
        fac1.interface_list[0].peer(al2s, labels=Labels(asn='65123, bgp_key='secret', ipv4_subnet='192.168.1.2/24'))
        # additional / optional peering
        fac2.interface_list[0].peer(al2s, labels=Labels(asn='65456', bgp_key='secret', ipv4_subnet='192.168.2.2/24'))

Note that the above only covers the L3VPN in AL2S. Similar struct should be composed for L3VPN in FABRIC.

ibaldin commented 1 year ago

Let me try to add a version of your code above as a test case. I'll let you know.

xi-yang commented 1 year ago

As ASN for FABRIC and AL2S are the same for all, we could have Label asn='55038' or asn='398900' be a NS label. But VLAN, IP, peering IP and acount_id etc. still have to be on interface / facilityPort.

zlion commented 1 year ago

Tested the fabric_fim-1.4.0rc2 and it does the job.

ibaldin commented 1 year ago

@xi-yang regarding ASN labels - it is in fact the intent to have them as NetworkService labels in the advertisement/ARM (in slice model/ASM the ASN label has to hang off of ConnectionPoint of the Facility because only those labels are copied to PeerLabels of opposite service interface).

You should be able to set this when you create the substrate model for the L3VPN network service attached to AL2S node. WRT FABRIC it is a bit trickier, since we have multiple switch nodes, each with its own L3VPN service (that you need to add), but I see no reason why you can't set the same ASN on all of them.

ibaldin commented 1 year ago

@xi-yang something like this for FABRIC Network Model (just the last few lines are new where L3VPN service is added)

        # DP switch
        switch_model = 'NCS 55A1-36H' # new switches have  NCS-57B1-6D24-SYS
        switch_ip = "192.168.11.3"
        switch = self.topo.add_node(name=dp_switch_name_id(site, switch_ip)[0], model=switch_model,
                                    node_id=dp_switch_name_id(site, switch_ip)[1], site=site,
                                    ntype=f.NodeType.Switch)

        dp_ns = switch.add_network_service(name=switch.name+'-ns',
                                           node_id=switch.node_id + '-ns',
                                           nstype=f.ServiceType.MPLS,
                                           labels=f.Labels(vlan_range='1-100'))

        dp_l3ns = switch.add_network_service(name=switch.name + '-l3ns',
                                             node_id=switch.node_id + '-l3ns',
                                             nstype=f.ServiceType.FABNetv4,
                                             labels=f.Labels(ipv4_range='192.168.1.1-192.168.1.255',
                                                             vlan_range='100-200'))

        dp_l3vpn = switch.add_network_service(name=switch.name + '-l3vpn',
                                             node_id=switch.node_id + '-l3vpn',
                                             nstype=f.ServiceType.L3VPN,
                                             labels=f.Labels(asn='12345'))
ibaldin commented 1 year ago

The ipv4 management flag can be specified thusly:

        n1.flags = f.Flags(auto_config=True, ipv4_management=True)

and queried thusly

        self.assertTrue(n1.flags.ipv4_management)
ibaldin commented 1 year ago

The basic usage for user_data and layout_data, is just like for mf_data:

        # user data on model elements (nodes, links, components, interfaces, network services)
        # can be set simply as json string (string length not to exceed 1M)
        n1.user_data = json.dumps({'k1': ['some', 'user', 'configuration']})
        # you are guaranteed that whatever is on user_data is JSON parsable and can be reconstituted into
        # an object
        user_object1 = n1.user_data
        self.assertTrue(user_object1['k1'] == ['some', 'user', 'configuration'])
        # when nothing is set, it is None
        self.assertEqual(n2.user_data, None)

        # you can also set it as UserData object
        my_user_data_object = {'key1': {'user2': ['v1', 2]}}
        n1.user_data = f.UserData(json.dumps(my_user_data_object))

        # you can also just pass a JSON serializable object to UserData constructor:
        n1.user_data = f.UserData(my_user_data_object)
        # or even an serializable object itself.
        n1.user_data = my_user_data_object

        # for most uses, just set the object
        my_user_data_object = {'key1': {'key2': ['some', 'user', 'info']}}
        n1.user_data = my_user_data_object

        # you get back your object (in this case a dict)
        self.assertTrue(isinstance(n1.user_data, dict))
        user_object2 = n1.user_data
        self.assertTrue(user_object2['key1'] == {'key2': ['some', 'user', 'info']})