Deploying multi vendor L2VPN services using Crosswork Network Controller

8 minutes read

Authors

Bradley Riapolov and Tahsin Chowdhury

Introduction

Service Provider networks inherently span vast and diverse landscapes. These networks often involve intricate blends of hardware and software solutions, often sourced from various vendors. While these ecosystems offer advantages, the task of aligning one vendor’s tools to effectively manage changes on another vendor’s hardware continues to present a formidable challenge.

Cisco’s Crosswork Controller serves as one of these network orchestration tools. It’s essential to understand the range of supported use cases to fully appreciate its capabilities and limitations.

In the realm of multi-vendor support, exercising caution is essential—an imperative that applies to any tool claiming such capabilities. Anticipating a tool to accommodate the myriad variations and deployment configurations, all subjected to rigorous testing through release processes, is not a practical expectation. Nonetheless, this shouldn’t deter exploration; rather, it’s an invitation to embark on uncharted paths and discover if orchestrating and visualizing a multi-vendor setup is attainable.

A Multi-Vendor Objective

In this particular scenario, the task was to establish and visualize a L2VPN point-to-point configuration between Cisco’s IOS XR and Juniper’s Junos using the Crosswork Network Controller GUI. Our objective is to lead you through the steps we took in this process, pushing the boundaries of what is presently recognized as officially supported (we hope to inspire others to explore the realm of possibilities with just a little added effort). For our setup, we used a network with Cisco IOS XRv9000 virtual routers and Juniper vMX virtual routers along with CNC, CDG, SR-PCE, NSO (with CNC core function packs).

A Step-by-Step Solution

We built a working configuration between the two devices:

Cisco IOS XRv9000:

interface GigabitEthernet0/0/0/1.101 l2transport
 description T-SDN Interface
 encapsulation dot1q 512
!
l2vpn
 ignore-mtu-mismatch
 pw-class TEST
  encapsulation mpls
   control-word
   transport-mode vlan
  !
 !
 xconnect group TEST
  p2p TEST
   interface GigabitEthernet0/0/0/1.101
   neighbor ipv4 198.19.1.99 pw-id 101
    pw-class TEST
   !
  !
 !
!

Juniper vMX:

set interfaces ge-0/0/1 vlan-tagging
set interfaces ge-0/0/1 encapsulation vlan-ccc
set interfaces ge-0/0/1 unit 101 encapsulation vlan-ccc
set interfaces ge-0/0/1 unit 101 vlan-id 512
set interfaces ge-0/0/1 unit 101 family ccc
set protocols l2circuit neighbor 198.19.1.4 interface ge-0/0/1.101 virtual-circuit-id 101
set protocols l2circuit neighbor 198.19.1.4 interface ge-0/0/1.101 ignore-encapsulation-mismatch
set protocols l2circuit neighbor 198.19.1.4 interface ge-0/0/1.101 ignore-mtu-mismatch
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface lo0.0
set protocols mpls label-switched-path lsp_to_pe2 to 198.19.1.4
set protocols mpls interface ge-0/0/0

Note: We configured our virtual environment to ignore the MTU setting so the l2circuit (Juniper)/ xconnect(Cisco) could come up (notice the L2circuit is UP on both ends). This is specific to this virtual environment. It’s strongly advised to use similar MTU in production networks.

Juniper ouput:


lab@PE1> show l2circuit connections 
Layer-2 Circuit Connections:

Legend for connection status (St)   
EI -- encapsulation invalid      NP -- interface h/w not present   
MM -- mtu mismatch               Dn -- down                       
EM -- encapsulation mismatch     VC-Dn -- Virtual circuit Down    
CM -- control-word mismatch      Up -- operational                
VM -- vlan id mismatch		 CF -- Call admission control failure
OL -- no outgoing label          IB -- TDM incompatible bitrate 
NC -- intf encaps not CCC/TCC    TM -- TDM misconfiguration 
BK -- Backup Connection          ST -- Standby Connection
CB -- rcvd cell-bundle size bad  SP -- Static Pseudowire
LD -- local site signaled down   RS -- remote site standby
RD -- remote site signaled down  HS -- Hot-standby Connection
XX -- unknown

Legend for interface status  
Up -- operational            
Dn -- down                   
Neighbor: 198.19.1.99 
    Interface                 Type  St     Time last up          # Up trans
    ge-0/0/0.101(vc 101)      rmt   Up     Jul 13 11:41:51 2023           1
      Remote PE: 198.19.1.99, Negotiated control-word: Yes (Null)
      Incoming label: 299840, Outgoing label: 299840
      Negotiated PW status TLV: No
      Local interface: ge-0/0/0.101, Status: Up, Encapsulation: VLAN
      Flow Label Transmit: No, Flow Label Receive: No

Cisco output:


Node-4# show l2vpn xconnect

Legend: ST = State, UP = Up, DN = Down, AD = Admin Down, UR = Unresolved,
        SB = Standby, SR = Standby Ready, (PP) = Partially Programmed,
        LU = Local Up, RU = Remote Up, CO = Connected, (SI) = Seamless Inactive

XConnect                   Segment 1                       Segment 2                
Group      Name       ST   Description            ST       Description            ST    
------------------------   -----------------------------   -----------------------------
TEST       TEST       UP   Gi0/0/0/1.512          UP       198.19.1.99     101    UP    

With the multi-vendor configuration working, our next goal was to provide a functional setup (along with the visualization) through Cisco Network Controller only. This involves using NSO (Network Services Orchestrator) to deliver these network changes. Let us adapt the NSO files accordingly. We “git-cloned” the repository (tsdn-juniper) that Cisco Automation engineers had posted on github into our NSO VM.

We extended the l2vpn service package in that repository to make our example configuration work. We copied the “flat-l2vpn-juniper” folder into the package folder in our NSO environment. We followed the Installation section of that repository to install the “flat-l2vpn-juniper” package.

Using the Juniper config syntax stated in the above example, we used NSO CLI to configure the commands and use commit dry-run outformat xml to get the configuration template and replace the required parameters with input variable (seen in the XML section below, but did not apply the commit).

cisco@nso-cnc5-brown-nss-j:~$ ncs_cli -u admin -C
admin connected from 10.16.27.1 using ssh on nso-cnc5-brown-nss-j
admin@ncs#
admin@ncs# config
Entering configuration mode terminal
admin@ncs(config)# devices device vmx99 config configuration interfaces interface ge-0/0/1 vlan-tagging
admin@ncs(config-interface-ge-0/0/1)# encapsulation vlan-ccc
admin@ncs(config-interface-ge-0/0/1)# unit 101 encapsulation vlan-ccc
admin@ncs(config-unit-101)# vlan-id 512
admin@ncs(config-unit-101)# family ccc
admin@ncs(config-unit-101)# top
admin@ncs(config)# devices device vmx99 config configuration protocols l2circuit neighbor 198.19.1.4 interface ge-0/0/1.101 virtual-circuit-id 101 
admin@ncs(config-interface-ge-0/0/1.101)# ignore-encapsulation-mismatch 
admin@ncs(config-interface-ge-0/0/1.101)# ignore-mtu-mismatch 
admin@ncs(config-interface-ge-0/0/1.101)# top
admin@ncs(config)# devices device vmx99 config configuration protocols mpls label-switched-path lsp_to_pe2 to 198.19.1.4                          
admin@ncs(config-label-switched-path-lsp_to_pe2)# 
admin@ncs(config-label-switched-path-lsp_to_pe2)# 
admin@ncs(config-label-switched-path-lsp_to_pe2)# 
admin@ncs(config-label-switched-path-lsp_to_pe2)# top
admin@ncs(config)# 
admin@ncs(config)# commit dry-run outformat xml
result-xml {
    local-node {
        data <devices xmlns="http://tail-f.com/ns/ncs">
               <device>
                 <name>vmx99</name>
                 <config>
                   <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm">
                     <interfaces>
                       <interface>
                         <name>ge-0/0/1</name>
                         <unit>
                           <name>101</name>
                           <encapsulation>vlan-ccc</encapsulation>
                           <vlan-id>512</vlan-id>
                           <family>
                             <ccc/>
                           </family>
                         </unit>
                       </interface>
                     </interfaces>
                     <protocols>
                       <mpls>
                         <label-switched-path>
                           <name>lsp_to_pe2</name>
                           <to>198.19.1.4</to>
                         </label-switched-path>
                       </mpls>
                       <l2circuit>
                         <neighbor>
                           <name>198.19.1.4</name>
                           <interface>
                             <name>ge-0/0/1.101</name>
                             <virtual-circuit-id>101</virtual-circuit-id>
                             <ignore-encapsulation-mismatch/>
                             <ignore-mtu-mismatch/>
                           </interface>
                         </neighbor>
                       </l2circuit>
                     </protocols>
                   </configuration>
                 </config>
               </device>
             </devices>
    }
}
admin@ncs(config)#
admin@ncs(config)# end
Uncommitted changes found, commit them? [yes/no/CANCEL] no
admin@ncs#

We saved the config template inside ~/ncs-run/packages/flat-l2vpn-juniper/templates folder as cisco-flat-L2vpn-fp-junos-p2p-l2circuit-template.xml. This file has been added with this repository.

cisco@nso-cnc5-brown-nss-j:~$ cd ncs-run/packages/flat-l2vpn-juniper/
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper$ 
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper$ 
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper$ cd templates/
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper/templates$ 
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper/templates$ 
cisco@nso-cnc5-brown-nss-j:~/ncs-run/packages/flat-l2vpn-juniper/templates$ ls
cisco-flat-L2vpn-fp-junos-p2p-l2circuit-template.xml  cisco-flat-L2vpn-fp-junos-template.xml

We went to the folder ~/ncs-run/packages/flat-l2vpn-juniper/python/flat_l2vpn_juniper and edited the Junos.py to add the p2p section inside the def conf_l2vpn(self, site, local): function.

class Junos:
    def conf_l2vpn(self, site, local):
        self.log.info(f"Configuring Flat L2VPN/SR on Junos for service {site.pe}")

        if self.service.service_type == "evpn-vpws":
            l2vpn_vars = ncs.template.Variables()
            l2vpn_vars.add("LOCAL_NODE", "true" if local is True else "false")
            l2vpn_template = ncs.template.Template(site)
            l2vpn_template.apply("cisco-flat-L2vpn-fp-junos-template", l2vpn_vars)
        
        """ Add this section""" 
        elif self.service.service_type == "p2p":
            l2vpn_vars = ncs.template.Variables()
            l2vpn_vars.add("LOCAL_NODE", "true" if local is True else "false")
            l2vpn_template = ncs.template.Template(site)
            l2vpn_template.apply("cisco-flat-L2vpn-fp-junos-p2p-l2circuit-template", l2vpn_vars)
       """ End of section """

This file has been added in our repository.

To make this configuration work, we had to modify the template for NSO Core Function Package for L2vpn (cisco-flat-L2vpn-fp-internal).

  • Went to the folder ~/ncs-run/packages/cisco-flat-L2vpn-fp-internal/templates.
  • Edited both cisco-flat-L2vpn-fp-cli-evpn-vpws-template.xml and cisco-flat-L2vpn-fp-cli-p2p-template.xml with the content below:
<l2vpn xmlns="http://tail-f.com/ned/cisco-ios-xr">
      <ignore-mtu-mismatch/>
      <pw-class>
        ......< skipping some sections>.....
        <encapsulation>
          <mpls>
             ......< skipping some sections>.....
            <transport-mode>
               <vlan/>
            </transport-mode>
           </mpls>
        </encapsulation>
      </pw-class>
      ......< skipping some sections>.....
    </l2vpn>

Reloaded the packages in NSO (packages reload).

cisco@nso-cnc5-brown-nss-j:~/ncs-run$ ncs_cli -u admin -C
admin connected from 10.16.27.1 using ssh on nso-cnc5-brown-nss-j
admin@ncs# 
admin@ncs# packages reload

…..............< skipping some output >..................

reload-result {
    package cisco-flat-L2vpn-fp-internal
    result true
}

…..............< skipping some output >..................

reload-result {
    package cisco-iosxr-cli-7.46
    result true
}
…..............< skipping some output >..................

reload-result {
    package flat-l2vpn-juniper
    result true
}
reload-result {
    package flat-l2vpn-multi-vendors
    result true
}
…..............< skipping some output >..................

Now we were ready to deploy and visualize this in Cisco Crosswork Network Controller. (We documented these steps in the Github Repo)

  • Select L2VPN under Services Section.

image

  • Either use GUI hitting “+” sign or import a configuration file in JSON format.

image

  • We will import a JSON configuration file for the service. The JSON file has been added in this repository.

image

  • Check the imported parameters and hit “commit changes” (optional, also can do a dry-run before commit).

image

  • And finaly - visualize the service in CNC (remember, we never touched the command line on the routers, everything was done via Crosswork Network Controller):

image

Let us now examine the CLI on the routers to verify the output.

Cisco IOS XRv9000 (show l2vpn xconnect):

Legend: ST = State, UP = Up, DN = Down, AD = Admin Down, UR = Unresolved,
        SB = Standby, SR = Standby Ready, (PP) = Partially Programmed,
        LU = Local Up, RU = Remote Up, CO = Connected, (SI) = Seamless Inactive

XConnect                   Segment 1                       Segment 2                
Group      Name       ST   Description            ST       Description            ST    
------------------------   -----------------------------   -----------------------------
TEST       TEST       UP   Gi0/0/0/1.512          UP       198.19.1.99     101    UP    
----------------------------------------------------------------------------------------

Juniper vMX (show l2circuit connections):

Layer-2 Circuit Connections:

Legend for connection status (St)   
EI -- encapsulation invalid      NP -- interface h/w not present   
MM -- mtu mismatch               Dn -- down                       
EM -- encapsulation mismatch     VC-Dn -- Virtual circuit Down    
CM -- control-word mismatch      Up -- operational                
VM -- vlan id mismatch		 CF -- Call admission control failure
OL -- no outgoing label          IB -- TDM incompatible bitrate 
NC -- intf encaps not CCC/TCC    TM -- TDM misconfiguration 
BK -- Backup Connection          ST -- Standby Connection
CB -- rcvd cell-bundle size bad  SP -- Static Pseudowire
LD -- local site signaled down   RS -- remote site standby
RD -- remote site signaled down  HS -- Hot-standby Connection
XX -- unknown

Legend for interface status  
Up -- operational            
Dn -- down                   
Neighbor: 198.19.1.4 
    Interface                 Type  St     Time last up          # Up trans
    ge-0/0/1.512(vc 101)      rmt   Up     Aug 23 18:06:20 2023           1
      Remote PE: 198.19.1.4, Negotiated control-word: Yes (Null)
      Incoming label: 80002, Outgoing label: 80007
      Negotiated PW status TLV: No
      Local interface: ge-0/0/1.512, Status: Up, Encapsulation: VLAN
      Flow Label Transmit: No, Flow Label Receive: No

Conclusion

Establishing operational multi-vendor setups might appear daunting, yet there’s always a pathway when you’re willing to explore how these products operate at their core.

Leave a Comment