Cisco NSO300 Lab 3: Nano Services

Nano Services

In LAB 3: Nano Services, participants delve into the intricacies of Nano Services within the Cisco Network Services Orchestrator (NSO) environment. Nano Services represent a microservices-based architecture, allowing for the modular decomposition and composition of network services. This lab involves tasks such as creating, configuring, and orchestrating Nano Services to achieve specific network functionalities. Participants gain hands-on experience in harnessing the flexibility and scalability advantages offered by Nano Services, showcasing their ability to streamline and modularize network service design and deployment. Successful completion of NSO300 LAB 3 equips participants with practical skills in working with Nano Services, a pivotal aspect of modern network automation architectures, fostering agility and efficiency in service orchestration.

Lab:

Task 1: Modify Service Model

In this task, you will modify the existing service model in two steps. First, you will create a separate list for the vpn-id allocations for the network operator. Next, you will delete the existing vpn-id leaf.

The final solution for this lab is located in the ~/solutions/lab3/l3mplsvpn/nano directory. You can use it for copying and pasting longer pieces of code and as a reference point for troubleshooting your packages.

Step 1: Connect to the VM server by clicking the NSO icon

Step 2: Open the terminal window using the Terminal icon on the bottom bar.

Step 3: Change directory to /home/rst/NSO/nso300

rst@rst:~$ **cd NSO/nso300**
rst@rst:~/NSO/nso300$

Step 4: From the Terminal window, copy the existing l3mplsvpn package.

rst@rst:~/NSO/nso300$ **cp -r ../packages/l3mplsvpn/ packages/l3mplsvpn**
rst@rst:~/NSO/nso300$ 

Step 5: List the contents of the ../packages/l3mplsvpn directory.

rst@rst:~/NSO/nso300$ **ls packages/l3mplsvpn/**
package-meta-data.xml  python  README  src  templates
rst@rst:~/NSO/nso300$  

Step 6: Open the service model file using a text editor of your choice

rst@rst:~/NSO/nso300$ code packages/l3mplsvpn/src/yang/l3mplsvpn.yang 

Step 7: Delete the existing vpn-id leaf.

leaf vpn-id {
    type uint16;
    tailf:info "Unique VPN ID";
}

Step 8: Create a list vpn-allocations with two leaves — vpn-name and vpn-id. Declare vpn-name to be used as a key leaf and use a unique constraint for the vpn-id. Make sure that vpn-name refers to the name leaf located in the l3mplsvpn list.

\### OUTPUT OMITTED ###

  revision 2020-06-04 {
    description
      "Initial revision.";
  }

  **list vpn-allocations {**
  **  key "vpn-name";**
  **  unique "vpn-id";**

  **  leaf vpn-name {**
  **    type leafref {**
  **      path /l3mplsvpn/name;**
  **    }**
  **  }**
  **  leaf vpn-id {**
  **    type uint16;**
  **    tailf:info "Unique VPN ID";**
  **  }**
  **}**

  list l3mplsvpn {
    uses ncs:service-data;
    ncs:servicepoint l3mplsvpn-servicepoint;
    key name;
    
### OUTPUT OMITTED ###     

Step 9: Save the file and exit the text editor.

Task 2: Change to Nano Services

In this task, you will change the service model from the previous task and implement nano service. This change will consist of three key steps.

First, the service must include the ncs:nano-plan-data grouping which will change the execution model of your service.

Second, the service must define a nano plan declared with the ncs:plan-outline statement. Nano plan definitions consist of plan components and plan state definitions.

Finally, you must define a service behavior tree declared with the ncs:service-behavior-tree statement. The behavior tree is responsible for creating and removing plan components from instances of your service plan.

Step 1: Open the service model file using a text editor of your choice and study the service model.

rst@rst:~/NSO/nso300$ **code packages/l3mplsvpn/src/yang/l3mplsvpn.yang **    

Step 2: Find the l3mplsvpn list definition and declare the use of nano-plan-data.

\### OUTPUT OMITTED ###

 list l3mplsvpn {
    uses ncs:service-data;
    **uses ncs:nano-plan-data;**
    ncs:servicepoint l3mplsvpn-servicepoint;
    key name;

    leaf name {
      tailf:info "Service Instance Name";
      type string;
    }

### OUTPUT OMITTED ###

Step 3: Declare the plan component and plan state for later use in the plan outline.

\### OUTPUT OMITTED ###

revision 2020-06-04 {
     description
       "Initial revision.";
   }
 
   **identity l3mplsvpn {**
   **  base ncs:plan-component-type;**
   **}**
 
   **identity l3mplsvpn-configured {**
   **  base ncs:plan-state;**
   **}**
 
   list vpn-allocations {
     key "vpn-name";
     unique "vpn-id";

### OUTPUT OMITTED ###

Step 4: Define your service plan outline and add a mandatory self component with two mandatory states — init and readyinit and ready states are primarily used as placeholders for timestamps.

\### OUTPUT OMITTED ###

  identity l3mplsvpn-configured {
    base ncs:plan-state;
  }

  **ncs:plan-outline l3mplsvpn-plan {**
  **  description "L3 MPLS VPN Plan";**

  **  ncs:component-type "ncs:self" {**
  **    ncs:state "ncs:init";**
  **    ncs:state "ncs:ready";**
  **  }**
  **}**

  list vpn-allocations {
    key "vpn-name";
    unique "vpn-id";

### OUTPUT OMITTED ###

Step 5: Define the l3mplsvpn component type and define two possible states. However, this time, use the l3mplsvpn:l3mplsvpn-configured for the second state declaration. You have formally declared these identities previously.

\### OUTPUT OMITTED ###

  identity l3mplsvpn-configured {
    base ncs:plan-state;
  }

  ncs:plan-outline l3mplsvpn-plan {
    description "L3 MPLS VPN Plan";

    ncs:component-type "ncs:self" {
      ncs:state "ncs:init";
      ncs:state "ncs:ready";
    }

    **ncs:component-type "l3mplsvpn:l3mplsvpn" {**
    **  ncs:state "ncs:init";**
    **  ncs:state "l3mplsvpn:l3mplsvpn-configured" {**
    
    **  }**
    **}**
  }

  list vpn-allocations {
    key "vpn-name";
    unique "vpn-id";

### OUTPUT OMITTED ###

Step 6: Finally, add a create nano callback and define a pre-condition inside the block. Write an XPath expression and pass it as an argument to the monitor statement. This will make sure that l3mplsvpn-configured state can be reached only if the monitor condition is satisfied. Make sure you add a nano-callback declaration right after the pre-condition block.

\### OUTPUT OMITTED ###

  identity l3mplsvpn-configured {
    base ncs:plan-state;
  }

  ncs:plan-outline l3mplsvpn-plan {
    description "L3 MPLS VPN Plan";

    ncs:component-type "ncs:self" {
      ncs:state "ncs:init";
      ncs:state "ncs:ready";
    }

    ncs:component-type "l3mplsvpn:l3mplsvpn" {
      ncs:state "ncs:init";
      ncs:state "l3mplsvpn:l3mplsvpn-configured" {
        **ncs:create {**
        **  ncs:pre-condition {**
        **    ncs:monitor "/vpn-allocations\[vpn-name=$SERVICE/name\]/vpn-id";**
        **  }**
        **  ncs:nano-callback;**
        **}**
      }
    }
  }

  list vpn-allocations {
    key "vpn-name";
    unique "vpn-id";

### OUTPUT OMITTED ###

Step 7: Write a behavior tree for your service by providing the name of your service point and the name of your plan outline. A behavior tree is responsible for creating components instantiated from your service plan.

\### OUTPUT OMITTED ###

  identity l3mplsvpn {
    base ncs:plan-component-type;
  }

  identity l3mplsvpn-configured {
    base ncs:plan-state;
  }

  **ncs:service-behavior-tree l3mplsvpn-servicepoint {**
  **  description "L3 MPLS VPN behavior tree";**
  **  ncs:plan-outline-red "l3mplsvpn:l3mplsvpn-plan";**
  **  ncs:selector {**
  **    ncs:create-component "'self'" {**
  **      ncs:component-type-ref "ncs:self";**
  **    }**
  **    ncs:create-component "'l3mplsvpn'" {**
  **      ncs:component-type-ref "l3mplsvpn:l3mplsvpn";**
  **    }**
  **  }**
  **}**

  ncs:plan-outline l3mplsvpn-plan {
    description "L3 MPLS VPN Plan";

### OUTPUT OMITTED ###

Step 8: Save changes and close the file.

Step 9: Open the l3mplsvpn.py file using a text editor of your choice.

rst@rst:~/NSO/nso300$ `code packages/l3mplsvpn/python/l3mplsvpn/l3mplsvpn.py` 

Step 10: Delete the highlighted ServiceCallbacks class declaration, @Service.create decorator, and cb_create method declaration along with the call to the self.log.debug method.

import ncs
import math

class ServiceCallbacks(ncs.application.Service):

    @Service.create
    def cb\_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service.\_path, ')')

### OUTPUT OMITTED ###

Step 11: Declare a class L3MPLSNanoService, which inherits from the ncs.application NanoService class. Use the @ncs.application.NanoService.create decorator and define the cb_nano_create method with a call to the self.log.debug method.

Note: cb_nano_create accepts additional parameters—plan, component, state, and component_proplist.

import ncs
import math

**class L3MPLSNanoService(ncs.application.NanoService):**
    **@ncs.application.NanoService.create**
    **def cb\_nano\_create(**
        **self, tctx, root, service, plan, component, state, proplist, component\_proplist**
    **):**
        **self.log.debug("NanoService create ", state)**

### OUTPUT OMITTED ###

Step 12: Delete the line with the vpn_id declaration. Remember that your service model no longer provides the vpn-id.

\### OUTPUT OMITTED ###

vpn\_id = service.vpn\_id

### OUTPUT OMITTED ###

Step 13: Add lines to obtain vpn-id from vpn-allocations list, and a call to self.log.info method. Make sure that you use underscores and not hyphen when declaring variables.

\### OUTPUT OMITTED ###
     self.log.debug("NanoService create ", state)

     **vpn\_id = root.vpn\_allocations\[service.name\].vpn\_id**
     **self.log.info(f"Vpn ID read: {vpn\_id}")**

### OUTPUT OMITTED ###

Step 14: Scroll down to near the end of the file and add a return proplist statement. Also, add a call to self.register_nano_service with your service parameters.

\### OUTPUT OMITTED ###
          template.apply("l3mplsvpn-template", tvars)
          
     **return proplist**

# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
class L3MplsVpn(ncs.application.Application):
    def setup(self):
        self.log.info("L3MplsVpn RUNNING")
        **self.register\_nano\_service(**
        **    "l3mplsvpn-servicepoint",**
        **    "l3mplsvpn:l3mplsvpn",**
        **    "l3mplsvpn:l3mplsvpn-configured",**
        **    L3MPLSNanoService,**
        **)**

    def teardown(self):
        self.log.info("L3MplsVpn FINISHED")

### OUTPUT OMITTED ###

Step 15: Save changes and exit the text editor.

Step 16: Compile and deploy your package.

rst@rst:~/NSO/nso300$ **make testenv-build** 
### OUTPUT OMITTED ###
>>> System upgrade has completed successfully.
reload-result {
    package cisco-asa-cli-6.7
    result true
}
reload-result {
    package cisco-ios-cli-6.42
    result true
}
reload-result {
    package cisco-iosxr-cli-7.18
    result true
}
reload-result {
    package cisco-nx-cli-5.13
    result true
}
reload-result {
    **package l3mplsvpn**
    **result true**
}
rst@rst:~/NSO/nso300$ 

Task 3: Deploy Nano Services

In this task, you will deploy the newly created nano service.

Step 1: Enter the NSO CLI and switch to the Cisco mode.

rst@rst:~/NSO/nso300$ **make testenv-cli**
docker exec -it testenv-nso300-5.3-rst-nso bash -lc 'ncs\_cli -u admin'

admin connected from 127.0.0.1 using console on 83dc28b9733d
admin@ncs> **switch cli**
admin@ncs# 

Step 2: Enter the configuration mode and create a new customer entry called ACME.

admin@ncs# **config**
Entering configuration mode terminal
admin@ncs(config)# **customers customer ACME**
admin@ncs(config-customer-ACME)#

Step 3: Commit the configuration and return to the top.

admin@ncs(config-customer-ACME)# **top**  
admin@ncs(config)# 

Step 4: Configure a new VPN service instance for the new customer and name it vpn512.

admin@ncs(config)# **l3mplsvpn vpn512 customer ACME**
admin@ncs(config-l3mplsvpn-vpn512)# 

Step 5: Configure two links, each on a different device.

admin@ncs(config-l3mplsvpn-vpn512)# **link 1 device PE11 interface 0/1**
admin@ncs(config-link-1)# **exit**
admin@ncs(config-l3mplsvpn-vpn512)# **link 2 device PE21 interface 0/2**
admin@ncs(config-link-2)# **exit**

Step 6: Commit the configuration and exit configuration mode.

admin@ncs(config-l3mplsvpn-vpn512)# **commit**
Commit complete.
admin@ncs(config-l3mplsvpn-vpn512)# **end**
admin@ncs# 

Step 7: Inspect the status of the newly configured service instance.

Plan component l3mplsvpn has not reached the l3mplsvpn-configured state because a pre-condition exists, which requires the presence of a vpn-id value in the vpn-allocations list.

The NSO CLI adapts the output to the width of your terminal window. In case your window is too narrow, you can use the <command> | tab format, to force the NSO CLI to display the tabulated output.

admin@ncs# **show l3mplsvpn vpn512** 
                                                                                                POST    
                      BACK                                                                      ACTION  
TYPE       NAME       TRACK  GOAL  STATE                 STATUS       WHEN                 ref  STATUS  
--------------------------------------------------------------------------------------------------------
self       self       false  -     init                  reached      2021-07-29T08:04:42  -    -       
                                   ready                 reached      2021-07-29T08:04:42  -    -       
l3mplsvpn  l3mplsvpn  false  -     init                  reached      2021-07-29T08:04:42  -    -       
                                   l3mplsvpn-configured  not-reached  -                    -    -       

admin@ncs# 

Step 8: Enter the configuration mode.

admin@ncs# **config**
Entering configuration mode terminal

Step 9: Use the vpn-allocations command to allocate a new vpn-id to the service instance. You are taking the role of a network operator that is acting upon a new VPN service request.

admin@ncs(config)# **vpn-allocations ?**
Possible completions:
  vpn512
admin@ncs(config)# **vpn-allocations vpn512 vpn-id 512**

Step 10: Commit the configuration and exit the configuration mode.

admin@ncs(config-vpn-allocations-vpn512)# **commit**
Commit complete.
admin@ncs(config-vpn-allocations-vpn512)# **end**
admin@ncs# 

Step 11: Display the status of the service instance vpn512 one more time. Notice that the plan component l3mplsvpn has now reached the l3mplsvpn-configured state after the pre-condition was satisfied.

admin@ncs# **show l3mplsvpn vpn512** 
l3mplsvpn vpn512
 modified devices \[ PE11 PE21 \]
 directly-modified devices \[ PE11 PE21 \]
 device-list \[ PE11 PE21 \]
                                                                                            POST    
                      BACK                                                                  ACTION  
TYPE       NAME       TRACK  GOAL  STATE                 STATUS   WHEN                 ref  STATUS  
----------------------------------------------------------------------------------------------------
self       self       false  -     init                  reached  2021-07-29T08:04:42  -    -       
                                   ready                 reached  2021-07-29T08:04:42  -    -       
l3mplsvpn  l3mplsvpn  false  -     init                  reached  2021-07-29T08:04:42  -    -       
                                   l3mplsvpn-configured  reached  2021-07-29T08:05:38  -    -       

admin@ncs# 

You have successful configured service instance. Your plan component l3mplsvpn has reached l3mplsvpn-configured state.