cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
594
Views
2
Helpful
9
Replies

Attempting to build a layered, or stacked, service

Hi,

I'm trying to understand how the stacked services work and if it is applicable to my use-case. So, what I am trying to achieve is to break a l3vpn service into smaller parts due to the lifecycle of the individual parts.

* At the top level I have the general identity of the l3vpn (vrf) with basic customer-related leaf variables.
* At the next level I want the individual l3 segments/subnets to exist as a child of the parent vrf service. The reason for this, is that there are several API calls related to the creation and deletion of this service in addition to the device level configuration itself which are not called if the 'subnets' are list elements of the parent service.

So, as I am reading the few posts I can find on this the services are created at two individual service packages. But I am finding it difficult to find how they tie together and the "subnet" service is called as a child to the "vrf" service. And as the "subnet" service is the only config related service, how to I access the variabels from the parent-service from its python scripts and/or templates?

If it is possible to get this to work, how would you make sure that a child instance (subnet) from one parent service instance isn't bound to another parent service?

I hope that the above makes sense and is very excited to hear if it's possible

- Johnny

 

1 Accepted Solution

Accepted Solutions

hazad
Cisco Employee
Cisco Employee

The concept of stacked services is to let a service create another service. So in your case a VRF service would create a subnet service, by applying a template containing the service input data, which is also how the VRF service passes data to the subnet service. But I wonder if stacked services are the solution for this. You might also want to look into nano-services and post-actions. That could potentially be another approach, if you're looking for options. But if you're dedicated to stacking services, then I'd recommend that you do so in such a way where the lowest services that are configuring the devices, configures not more than one device per service.

Any API call necessary to apply config to a device should hopefully be covered by the NED, so that they are called by it when it applies the device config. But if that's not the case then you might need to apply them from your packages. What kind of API calls is it? Are you in your solution applying them from the create callback of the lower service?

View solution in original post

9 Replies 9

hazad
Cisco Employee
Cisco Employee

The concept of stacked services is to let a service create another service. So in your case a VRF service would create a subnet service, by applying a template containing the service input data, which is also how the VRF service passes data to the subnet service. But I wonder if stacked services are the solution for this. You might also want to look into nano-services and post-actions. That could potentially be another approach, if you're looking for options. But if you're dedicated to stacking services, then I'd recommend that you do so in such a way where the lowest services that are configuring the devices, configures not more than one device per service.

Any API call necessary to apply config to a device should hopefully be covered by the NED, so that they are called by it when it applies the device config. But if that's not the case then you might need to apply them from your packages. What kind of API calls is it? Are you in your solution applying them from the create callback of the lower service?

Hi Hazad,

Thank you for your reply  

In my case I am trying to demonstrate NSO as a replacement to an existing provisioning tool. In each addition of a subnet to a VRF, the deployment requires creating the subnet in a firewall accessed through REST API and DCHP/DNS related task also through a REST API. 

Creating a simple python-and-template service, it is not that intuitive to perform these operations when deleting a subnet as the service (vrf) is only updated/modified, and not deleted, when a subnet is removed, which is the reason that I want to split the service.

In a create operation, the lower subnet service would be deploying config to a set of distribution routers and range of access switches.

Currently, I've structured the service model as:

 

 

list vrf {
      key "vrfname";
      unique "customernumber";

      uses ncs:service-data;
      ncs:servicepoint "vrf";

      leaf vrfname {
        tailf:info "VRF name";
        mandatory true;
        type string;
      }
      leaf customername {
        tailf:info "Customer name";
        mandatory true;
        type string;
      }
      leaf customernumber {
        tailf:info "Unique customernumber";
        mandatory true;
        type uint8 {
          range 1..255;
        }
      }
      leaf firewall {
        tailf:info "Activate firewall features for vrf";
        type boolean;
        default true;
      }
      list l3-segment {
        tailf:info "Subnet in VRF";

        key "group segment-number";

        leaf group {
          tailf:info "Switch group in distribution block";
          mandatory true;
          type leafref {
            path "/ncs:devices/ncs:device-group/ncs:name";
          }
          must ". != contains(current(),'-')";
        }
        leaf segment-nummer {
          tailf:info "Segment number in group";
          mandatory true;
          type enumeration {
            enum 1 {
              value 1;
              description "First segment in group";
            }
          }
        }
        leaf dhcp {
          tailf:info "Activate DHCP for segment";
          type boolean;
          default true;
        }
        list access-port {
          tailf:info "Access port members of VLAN";

          key "access-switch port";

          leaf access-switch {
            mandatory true;
            type string;
            tailf:non-strict-leafref {
              path "/ncs:devices/ncs:device-group[ncs:name=current()/../../group]/ncs:member";
            }
          }
          leaf port {
            type leafref {
              path "/ncs:devices/ncs:device[ncs:name=current()/../asw]/ncs:config/ios:interface/ios:GigabitEthernet/ios:name";
            }
            must "contains(deref(current())/../ios:description,'Available') or (contains(deref(current())/../ios:description, current()/../../../customername) and contains(deref(current())/../ios:description, current()/../../segment-number))" {
                  tailf:dependency "/ncs:devices/ncs:device/ncs:config/ios:interface/ios:GigabitEthernet/ios:description";
            }
          }
        }
      }
    }
}

 

- Johnny

Hazad,

I'm trying to get my head wrapped around how the nano-services can help me on this. How would I use the nano-services to trigger a (python) function to call an API only when the operation is actually committed? Can you show an example?

I've tried reading through the documentation and the examples, but haven't really cracked it yet

In my example I would need to perform one API call when a VRF is created and deleted (and only when the commit is performed) and the same for each time a l3-segment is added to the vrf element (again, only when committed).

/Johnny

Do you need a nano service for that? Or perhaps using the post-modification callback is enough?
https://developer.cisco.com/docs/nso/guides/services-deep-dive/#service-callbacks
Example:
examples.ncs/development-guide/services/iface-postmod-py

hazad
Cisco Employee
Cisco Employee

The post-modification callback would work if you're going for the stacked service approach. I guess you'd have to do that if you want different API calls for different components of a service, all managed through one servicepoint.

I was thinking about utilizing the post-action of a nano-service.
https://developer.cisco.com/docs/nso/guides/nano-services-for-staged-provisioning/#managing-side-effects
So you can create states and components for each subnet, where you trigger a post-action on the appropriate plan state. The post-action will trigger an action callback that will do the API call. Post-actions will only trigger once though, which is when the state transitions to "reached" for create and "not-reached" for delete. The post-modification callback will on the other hand run on each re-deploy of a service, so you could potentially invoke the API call for each service re-deploy. You can of course control this by applying some python/java logic, so that it doesn't do the API call on each and every re-deploy.

The following example is using post-actions in the vrouter service:
examples.ncs/development-guide/nano-services/netsim-vrouter

My issue is that the post-mod callback is executed before the transaction is committed. 

So in my example, that when a service is created and deleted I need to call an API to perform changes outside of the scope of NSO. The problem arises when the user initiates a deletion or creation of service, but reverts midway - at this point the post-modification has already performed the API call, but doesn't perform the opposite "op" when the revert is executed. 

@Johnny Karms Pedersen, understood regarding the need for the transaction to be committed before the callback. A simple nano service plan:

 

  ncs:plan-outline my-plan {
    ncs:component-type "my-ns-prefix:my-plan-component-type-id" {
      ncs:state "ncs:init" {
        ncs:create {
          ncs:nano-callback;
          ncs:post-action-node "$SERVICE" {
            ncs:action-name "my-create-action";
            ncs:result-expr "result = 'true'";
          }
        }
        ncs:delete {
          ncs:post-action-node "$SERVICE" {
            ncs:action-name "my-delete-action";
            ncs:result-expr "result = 'true'";
          }
        }
      }
      ncs:state "ncs:ready";
    }
  }

 

Note that there will be a re-deploy after the "my-create-action", but that is not an issue if your service create() callback produces the same change as when it ran before the action (i.e. no CDB change compared to the first create() callback invocation so no additional commit needed)

There are lots of examples in the NSO example set. See, for example, 
examples.ncs/development-guide/concurrency-model/perf-stack/package-repository/rfs-t3/src/yang/t3.yang
as a reference or the example @hazad mentioned.

 

snovello
Cisco Employee
Cisco Employee

It would seem the simplest method would be to use a NED for the firewall and the DNS/DHCP server, then you just express in the template what you intended configuration is. So just as usual with NSO you specify the create case and let NSO manage the issues with modify and delete.

There are NEDs for many firewalls, using direct APIs on the FW or via vendor specific controllers. For DNS/DHCP there are some NEDs, for example for Cisco Prime Network Registrar or BIND.

 

 

Yes that would be the easiest