cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Announcements

Community Helping Community

Porting an existing service to use a NETCONF NED, part 2

727
Views
11
Helpful
0
Comments
Cisco Employee

This is the second part of the story porting an existing service to use a NETCONF NED. In the previous post, we prepared the environment and made sure we have both a CLI NED and a NETCONF NED ready to go for our IOS-XE device. You don't have to go back to read the previous post if you don't want to, but if you'd like to, it's here Porting an existing service to use a NETCONF NED, part 1

 

As noted in the root post Porting an existing service to use a NETCONF NED, the next step would be:

 

2) Extend service with (additional) support for the NETCONF variant of the device

- Switch NSO to talk to the device over CLI

- Configure a service instance, commit

- Switch NSO to talk to the device over NETCONF

- View the configuration changes in XML

- Take this XML as input to new sections in the service templates

- Fill in variables as given by the CLI version of the template

 

Ok, so now that I know I have a working NETCONF NED for the device, I actually want to switch back to the CLI NED for a while. After all, that's all my service code can relate to at this point. It is mapping the service intent to the CLI NED model right now. I'll be switching ce0 back to NETCONF later.

 

JLINDBLA-M-W0J2# con

Entering configuration mode terminal

JLINDBLA-M-W0J2(config)# devices device ce0

JLINDBLA-M-W0J2(config-device-ce0)# device-type cli ned-id cisco-ios

JLINDBLA-M-W0J2(config-device-ce0)# no port

JLINDBLA-M-W0J2(config-device-ce0)# comm

Commit complete.

 

It will be useful to also have a NETCONF interface view of the ce0 at all times, so I will play the little party trick to add the same ce0 device once more into the NSO device list, but this time as a NETCONF device. I'll call this view of the device ce0-nc. The XE device will hence be listed _twice_ in NSO. This is absolutely not normal and would lead to out of sync situations in normal operations, but today that's exactly what we're after. You'll see in a minute.

 

JLINDBLA-M-W0J2(config)# devices device ce0-nc address 192.168.50.50 port 830 authgroup vagrant device-type netconf  

JLINDBLA-M-W0J2(config-device-ce0-nc)# state admin-state unlocked

JLINDBLA-M-W0J2(config-device-ce0-nc)# comm

Commit complete.

JLINDBLA-M-W0J2(config-device-ce0-nc)# ssh fetch-host-keys

result updated

fingerprint {

    algorithm ssh-rsa

    value b0:d4:d4:f8:01:b6:c9:07:6f:ad:a1:82:bc:b3:d0:3d

}

JLINDBLA-M-W0J2(config-device-ce0-nc)# top

 

And then I'd like to ensure we have an up to date config from all devices.

 

JLINDBLA-M-W0J2(config)# devices sync-f

sync-result {

    device ce0

    result true

}

sync-result {

    device ce0-nc

    result true

}

sync-result {

    device ce1

    result true

}

 

Now, I'll configure a service instance and push to all the devices. I made a file that I can load (over and over), but you could of course type this in manually just as well.

 

JLINDBLA-M-W0J2(config)# load merge vpn_skoda.xml

Loading.

923 bytes parsed in 0.00 sec (185.00 KiB/sec)

JLINDBLA-M-W0J2(config)# show c

vpn l3vpn skoda

as-number 61000

endpoint newyork-office

  ce-device    ce3

  ce-interface GigabitEthernet0/2

  ip-network   10.1.0.0/24

  bandwidth    2000000

!

endpoint riodejaneiro-office

  ce-device    ce6

  ce-interface GigabitEthernet0/2

  ip-network   10.0.0.0/24

  bandwidth    10000000

!

endpoint tokyo-office

  ce-device    ce0

  ce-interface GigabitEthernet2

  ip-network   10.0.0.0/24

  bandwidth    10000000

!

!

 

The important piece here is that we configured some basic service that _involves the ce0 device_. See how the tokyo-office is using ce0. Commit dry-run to see what's about to happen. I just pasted the part that relates to ce0 here, there's much more.

 

JLINDBLA-M-W0J2(config)# commit dry-run

cli {

    local-node {

        data  devices {

                  device ce0 {

                      config {

                          ios:policy-map skoda {

             +                # first

             +                class class-default {

             +                    shape {

             +                        average {

             +                            bit-rate 10000000;

             +                        }

             +                    }

             +                }

                          }

                          ios:interface {

                              GigabitEthernet 2 {

                                  ip {

                                      no-address {

             -                            address false;

                                      }

                                      address {

                                          primary {

             +                                address 10.0.0.1;

             +                                mask 255.255.255.0;

                                          }

                                      }

                                  }

                              }

                              GigabitEthernet 3.88 {

                              }

                          }

                          ios:router {

                              bgp 61000 {

             +                    network 10.0.0.0;

                                  neighbor 192.168.1.2 {

             +                        activate;

                                  }

                              }

                          }

                      }

                  }

                  device ce3 {

                      config {

             +            ios:policy-map skoda {

             +                class class-default {

 

Let's commit this. The commit updates the NSO view of the ce0 device, it also updates the actual device. It does not, however, update NSO's view of the ce0-nc device, which is now out of sync. If we ask the ce0-nc device for the configuration (get-config) over NETCONF, we will get a translation of the service we just sent over CLI into NETCONF. The only problem is that there will be loads of other configuration on the device that has nothing to do with our service.

 

This is where the ce0-nc device comes in. Before we added the service to the device, we did a sync-from towards all devices, which included ce0-nc. So if we compare the configuration of the actual ce0-nc device over NETCONF with the saved state in NSO of ce0-nc, we can get a pretty good diff. We can get this diff in a variety of formats, but for my purposes, I will want it in XML.

 

JLINDBLA-M-W0J2(config)# commit

Commit complete.

JLINDBLA-M-W0J2(config)# devices device ce0-nc compare-config outformat xml

diff

<devices xmlns="http://tail-f.com/ns/ncs">

  <device>

    <name>ce0-nc</name>

    <config>

      <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">

        <policy>

          <policy-map xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-policy">

            <name>skoda</name>

            <class>

              <name>class-default</name>

              <action-list>

                <action-type>shape</action-type>

                <shape>

                  <average>

                    <bit-rate>10000000</bit-rate>

                  </average>

                </shape>

              </action-list>

            </class>

          </policy-map>

        </policy>

        <interface>

          <GigabitEthernet>

            <name>2</name>

            <ip>

              <address>

                <primary>

                  <address>10.0.0.1</address>

                  <mask>255.255.255.0</mask>

                </primary>

              </address>

            </ip>

          </GigabitEthernet>

        </interface>

        <router>

          <bgp xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-bgp">

            <id>61000</id>

            <neighbor>

              <id>192.168.1.2</id>

              <activate/>

            </neighbor>

            <network>

              <number>10.0.0.0</number>

            </network>

          </bgp>

        </router>

      </native>

    </config>

  </device>

</devices>

 

This XML is essentially what the service needs to push to the device over NETCONF. Since the service is based on an XML template towards the CLI NED, all I need to do is add this XML to that template, and parametrize it using the same variables that the CLI template is using. Let's do it.

 

If we look in the L3VPN template directory (packages/l3vpn/templates/) you'll see a number of template files.

 

$ ls ~/nso/4.4.2/examples.ncs/service-provider/vvpn-xe/packages/l3vpn/templates

l3vpn-acl.xml l3vpn-pe.xml l3vpn-qos-pe-class.xml l3vpn-qos-pe.xml l3vpn-qos.xml

l3vpn-ce.xml l3vpn-qos-class.xml l3vpn-qos-pe-prio.xml l3vpn-qos-prio.xml l3vpn-vm-manager.xml

 

Only some of these are configure XE devices, though. To quickly figure out which ones would touch an XE device, we can grep for the XE CLI NED namespace ID. You can find this in the XE CLI NED YANG files, but I can tell you it's "urn:ios". So let's grep for that.

 

$ grep -rils urn:ios packages/l3vpn/templates

packages/l3vpn/templates/l3vpn-acl.xml

packages/l3vpn/templates/l3vpn-ce.xml

packages/l3vpn/templates/l3vpn-pe.xml

packages/l3vpn/templates/l3vpn-qos-class.xml

packages/l3vpn/templates/l3vpn-qos-prio.xml

packages/l3vpn/templates/l3vpn-qos.xml

 

Looking at those files, I quickly realize that the only one I need to care about for the simple service I defined (with no QoS settings etc) is the l3vpn-ce.xml . And the l3vpn-pe.xml would not be relevant for an XE device in the CE role.

 

Looking at the l3vpn-ce.xml, this is what it contains right now:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0">

  <devices xmlns="http://tail-f.com/ns/ncs">

    <device tags="nocreate">

      <name>{$CE}</name>

      <config>

        <interface xmlns="urn:ios" tags="merge">

          <GigabitEthernet

              when="{starts-with($CE_INT_NAME,'GigabitEthernet')}">

            <name>{substring($CE_INT_NAME,16)}.{$VLAN_ID}</name>

            <description>Link to PE / {$PE} - {$PE_INT_NAME}</description>

            <encapsulation>

              <dot1Q>

                <vlan-id>{$VLAN_ID}</vlan-id>

              </dot1Q>

            </encapsulation>

            <ip>

              <address>

                <primary>

                  <address>{$LINK_CE_ADR}</address>

                  <mask>{$LINK_MASK}</mask>

                </primary>

              </address>

            </ip>

             <service-policy>

              <output>{/name}</output>

            </service-policy>

          </GigabitEthernet>

          <GigabitEthernet

               when="{starts-with($CE_LOCAL_INT_NAME,'GigabitEthernet')}">

            <name>{substring($CE_LOCAL_INT_NAME,16)}</name>

            <description>{/name} local network</description>

            <ip>

              <address>

                <primary>

                  <address>{$LOCAL_CE_ADR}</address>

                  <mask>{$CE_MASK}</mask>

                </primary>

              </address>

            </ip>

          </GigabitEthernet>

        </interface>

        <policy-map xmlns="urn:ios" tags="merge">

          <name>{/name}</name>

          <class>

            <name>class-default</name>

            <shape>

              <average>

                <bit-rate>{$BW}</bit-rate>

              </average>

            </shape>

          </class>

        </policy-map>

        <router xmlns="urn:ios" tags="merge">

          <bgp>

            <as-no>{/as-number}</as-no>

            <neighbor>

              <id>{$LINK_PE_ADR}</id>

              <remote-as>100</remote-as>

              <activate/>

            </neighbor>

            <network>

              <number>{$LOCAL_CE_NET}</number>

            </network>

          </bgp>

        </router>

      </config>

    </device>

  </devices>

</config-template>

 

That's somewhat similar to the XML diff we saw earlier. The { ... } are XPATH expressions evaluated by the NSO template applier, and the $VAR expressions are variables set by the L3VPN service when it applies the template.

 

The next step now is to simply paste the XML diff we got earlier into the template file inside the <config></config> tags, but not outside the previous content in between there. So in effect, insert the diff right after the top <config> tag. We don't want every line of the diff, only the part that's inside the diff's <config>...</config> tags. Also, remember to add tags="merge" on each top level node.

 

The resulting template becomes something like this:

 

<config-template xmlns="http://tail-f.com/ns/config/1.0">

  <devices xmlns="http://tail-f.com/ns/ncs">

    <device tags="nocreate">

      <name>{$CE}</name>

      <config>

        <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native" tags="merge">

          <policy>

            <policy-map xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-policy">

              <name>skoda</name>

              <class>

   ...

        </native>

        <interface xmlns="urn:ios" tags="merge">

          <GigabitEthernet

              when="{starts-with($CE_INT_NAME,'GigabitEthernet')}">

            <name>{substring($CE_INT_NAME,16)}.{$VLAN_ID}</name>

   ...

 

The pasted template contains a lot of hard coded names, e.g. "skoda", that we need to replace by one of the variables computed by the service. We can look at the CLI template part for inspiration.

 

Looking at the pasted template, I find <name>skoda</name>. What could that be? It sits on policy/policy-map. Looking at the CLI side of the model, under policy-map I find <name>{/name}</name>. I.e. the template is picking up the service name and inserts here. Since I named my service instance "skoda", this makes a lot of sense. So let's change the pasted template line to the same: <name>{/name}</name>

 

Next in the pasted template, I find <name>class-default</name>. Looking for something similar in the CLI model, I find <name>class-default</name>. A hard-coded constant. Fine, I'll leave it alone then.

 

Next, <action-type>shape</action-type>. I don't find any value that corresponds to this in the CLI model, but I do find a tag called <shape>, so this is presumably just a small modeling difference. I'll leave it alone like this.

 

<bit-rate>10000000</bit-rate> seems to correspond with <bit-rate>{$BW}</bit-rate>, so I'll replace the number by {$BW}.

 

Going through the entire pasted XML diff like this, the template I come up with looks like this (plus the unmodified CLI variant, which I'm not showing).

 

<config-template xmlns="http://tail-f.com/ns/config/1.0">

  <devices xmlns="http://tail-f.com/ns/ncs">

    <device tags="nocreate">

      <name>{$CE}</name>

      <config>

 

 

        <!-- XE over NETCONF -->

        <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native" tags="merge">

          <policy>

            <policy-map xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-policy">

              <name>{/name}</name>

              <class>

                <name>class-default</name>

                <action-list>

                  <action-type>shape</action-type>

                  <shape>

                    <average>

                      <bit-rate>{$BW}</bit-rate>

                    </average>

                  </shape>

                </action-list>

              </class>

            </policy-map>

          </policy>

          <interface>

            <GigabitEthernet when="{starts-with($CE_INT_NAME,'GigabitEthernet')}">

              <name>{substring($CE_LOCAL_INT_NAME,16)}</name>

              <description>{/name} local network</description>

              <ip>

                <address>

                  <primary>

                    <address>{$LOCAL_CE_ADR}</address>

                    <mask>{$CE_MASK}</mask>

                  </primary>

                </address>

              </ip>

            </GigabitEthernet>

          </interface>

          <interface>

            <GigabitEthernet

                when="{starts-with($CE_INT_NAME,'GigabitEthernet')}">

              <name>{substring($CE_INT_NAME,16)}.{$VLAN_ID}</name>

              <description>Link to PE / {$PE} - {$PE_INT_NAME}</description>

              <encapsulation>

                <dot1Q>

                  <vlan-id>{$VLAN_ID}</vlan-id>

                </dot1Q>

              </encapsulation>

              <ip>

                <address>

                  <primary>

                    <address>{$LINK_CE_ADR}</address>

                    <mask>{$LINK_MASK}</mask>

                  </primary>

                </address>

              </ip>

               <service-policy>

                <output>{/name}</output>

              </service-policy>

            </GigabitEthernet>

          </interface>

          <router>

            <bgp xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-bgp">

              <id>{/as-number}</id>

              <neighbor>

                <id>{$LINK_PE_ADR}</id>

                <activate/>

              </neighbor>

              <network>

                <number>{$LOCAL_CE_NET}</number>

              </network>

            </bgp>

          </router>

        </native>

        <!-- End of XE over NETCONF -->

 

 

        <!-- XE over CLI -->

        <interface xmlns="urn:ios" tags="merge">

          <GigabitEthernet

              when="{starts-with($CE_INT_NAME,'GigabitEthernet')}">

            <name>{substring($CE_INT_NAME,16)}.{$VLAN_ID}</name>

 

If I typed everything in correctly, I should now be able to reload packages to get the changes in. If I messed up the template, the package loader will typically notice and tell me about it.

 

JLINDBLA-M-W0J2(config)# exit

JLINDBLA-M-W0J2# packages reload

reload-result {

    package ce0

    result true

}

...

reload-result {

    package l3vpn

    result false

    info [l3vpn-ce.xml:57 Unknown element: 'service-policy']

}

 

Oh well. Seems I forgot the xmlns namespace on the service-policy element. Correcting that and reloading packages again works fine. I have the habit of checking that everything is fine with a show packages package oper-status

 

JLINDBLA-M-W0J2# show packages package oper-status

                                                                                        PACKAGE               

                         PROGRAM                                                        META     FILE         

                         CODE     JAVA           BAD NCS  PACKAGE  PACKAGE  CIRCULAR    DATA     LOAD   ERROR 

NAME                 UP  ERROR    UNINITIALIZED  VERSION  NAME     VERSION  DEPENDENCY  ERROR    ERROR  INFO  

---------------------------------------------------------------------------------------------------------------

ce0                  X   -        -              -        -        -        -           -        -      -     

cisco-ios            X   -        -              -        -        -        -           -        -      -     

cisco-iosxr          X   -        -              -        -        -        -           -        -      -     

esc                  X   -        -              -        -        -        -           -        -      -     

id-allocator         X   -        -              -        -        -        -           -        -      -     

ipaddress-allocator  X   -        -              -        -        -        -           -        -      -     

juniper-junos        X   -        -              -        -        -        -           -        -      -     

l3vpn                X   -        -              -        -        -        -           -        -      -     

l3vpnui              X   -        -              -        -        -        -           -        -      -     

pioneer              X   -        -              -        -        -        -           -        -      -     

resource-manager     X   -        -              -        -        -        -           -        -      -     

vm-manager           X   -        -              -        -        -        -           -        -      -     

vm-manager-esc       X   -        -              -        -        -        -           -        -      -     

weblog               X   -        -              -        -        -        -           -        -      -     

 

Now it's time to switch the ce0 device back to NETCONF, so that we can verify that the service translation was ok.

 

JLINDBLA-M-W0J2# con

Entering configuration mode terminal

JLINDBLA-M-W0J2(config)# devices device ce0

Possible completions:

  ce0  ce0-nc

JLINDBLA-M-W0J2(config)# devices device ce0 device-type netconf

JLINDBLA-M-W0J2(config-device-ce0)# no port

JLINDBLA-M-W0J2(config-device-ce0)# comm

Commit complete.

JLINDBLA-M-W0J2(config)# sync-from

result true

 

If we did the template work correctly, we should be able to re-deploy the service without anything being sent to the device.

 

JLINDBLA-M-W0J2(config-device-ce0)# top

JLINDBLA-M-W0J2(config)# vpn l3vpn skoda re-deploy dry-run

cli {

}

 

Let's also see if we can un-deploy and re-deploy for real.

 

JLINDBLA-M-W0J2(config)# vpn l3vpn skoda un-deploy            

JLINDBLA-M-W0J2(config)# vpn l3vpn skoda re-deploy

JLINDBLA-M-W0J2(config)# devices device ce0 compare-config

JLINDBLA-M-W0J2(config)# vpn l3vpn skoda get-modifications                                

cli {

    local-node {

        data  devices {

                   device ce0 {

                       config {

                           ios-native:native {

                               policy {

              +                    policy-map skoda {

              +                        class class-default {

              +                            action-list shape {

              +                                shape {

              +                                    average {

              +                                        bit-rate 10000000;

              +                                    }

              +                                }

              +                            }

              +                        }

              +                    }

                               }

                               interface {

              +                    GigabitEthernet 2 {

              +                        description "skoda local network";

              +                        ip {

              +                            address {

              +                                primary {

              +                                    address 10.0.0.1;

              +                                    mask 255.255.255.0;

              +                                }

              +                            }

              +                        }

              +                    }

              +                    GigabitEthernet 3.88 {

              +                        description "Link to PE / pe0 - GigabitEthernet0/0/0/3";

              +                        encapsulation {

              +                            dot1Q {

              +                                vlan-id 88;

              +                            }

              +                        }

              +                        ip {

              +                            address {

              +                                primary {

              +                                    address 192.168.1.1;

              +                                    mask 255.255.255.252;

              +                                }

              +                            }

              +                        }

              +                        service-policy {

              +                            output skoda;

              +                        }

              +                    }

                               }

                               router {

              +                    bgp 61000 {

              +                        neighbor 192.168.1.2 {

              +                            activate;

              +                        }

              +                        network 10.0.0.0;

              +                    }

                               }

                           }

                       }

                   }

                   device ce3 {

                       config {

              +            ios:policy-map skoda {

 

Cool!

 

That was the first step of porting the service. As noted earlier, there are a few more templates to look at. The next post will complete that work, and also show how to do some testing of the result.

 

Continue to part 3.