cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1598
Views
15
Helpful
3
Replies

Setting leaf value programatically when creating a service

drazen.kedmenec
Level 1
Level 1

Hi,

 

As part of service creation, I want to set leaf value using python code, rather than supplying it via CLI/API. This is my goal:

  • If the leaf value was not set in CLI, autogenerate the value and set it during service creation using Python
  • If the leaf value was set in CLI, use that value
  • The value can be changed from CLI after service is created

I tried testing with very simple model, but setting the leaf value from Python does not work. Let me illustrate this with a simple model:

  list l3vpn {
    key "name";
    uses ncs:service-data;
    ncs:servicepoint l3vpn-servicepoint;
    leaf name {
      type string;
    }
    leaf vpnid {
      type uint32;
    }
    leaf text {
      type string;
    }
  }

In this model, "vpnid" is the leaf value that can be autogenerated. Test Python code is very simple; it sets the leaf value if nothing was supplied from the model.

class ServiceCallbacks(Service):
    @Service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('############ l3vpn service create ###############')
        self.log.info('DEBUG: Service create(service=', service._path, ')')
        self.log.info('DEBUG: service VPN_ID={}'.format(service.vpnid))
        self.log.info('DEBUG: root VPN_ID={}'.format(root.l3vpn[service.name].vpnid))
        vpnid = service.vpnid
        if not vpnid:
            vpnid = 101
            self.log.info('DEBUG: Autogenerated vpnid = {}'.format(vpnid))
            service.vpnid = vpnid

Now, when I create the service and define "vpnid", everything works fine (Python debug is also shown). I can change "vpnid" parameter normally, and the new value entered via CLI shows up in "service" object in Python:

admin@ncs(config)# l3vpn test01 vpnid 123 text a
admin@ncs(config-l3vpn-test01)# commit dry-run
cli {
    local-node {
        data +l3vpn test01 {
             +    vpnid 123;
             +}
    }
}
Python debug:
############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test01})
DEBUG: service VPN_ID=123
DEBUG: root VPN_ID=123

Changing vpnid:
admin@ncs(config-l3vpn-test01)# vpnid 321
admin@ncs(config-l3vpn-test01)# commit dry-run
cli {
local-node {
data +l3vpn test01 {
+ vpnid 321;
+}
}
}
Python debug:
############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test01})
DEBUG: service VPN_ID=321
DEBUG: root VPN_ID=321

When I try to create service without setting "vpnid", and let Python set the value, unexpected things start happening.

  • At the first glance, it looks like NSO accepted the value set from Python, and placed it in to the model as expected.
admin@ncs(config-l3vpn-test01)# l3vpn test02 text b
admin@ncs(config-l3vpn-test02)# commit dry-run
cli {
    local-node {
        data +l3vpn test02 {
             +    vpnid 101;
             +}
    }
}
Python debug:
############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test02})
DEBUG: service VPN_ID=None
DEBUG: root VPN_ID=None
DEBUG: Autogenerated vpnid = 101
  • Service values in CLI look just fine. Value of "vpnid" is set normally, as expected.
admin@ncs(config-l3vpn-test02)# show full l3vpn test02
l3vpn test02
 vpnid 101
 text  b
  • When I try to change the autogenerated value, I can see that the value I entered via CLI does not get propagated to the "service" object. Nor does value that is actually set on the leaf
admin@ncs(config-l3vpn-test02)# vpnid 111
admin@ncs(config-l3vpn-test02)# commit dry-run
cli {
}
Python debug:
############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test02})
DEBUG: service VPN_ID=None
DEBUG: root VPN_ID=None
DEBUG: Autogenerated vpnid = 101

"Service" object in Python does not recognise leaf value already set in the created service, nor can I see the new value I entered using CLI. Actually, "service" object somehow remembers the original value of the service, when it was first created. Similar outcome results when I try to programatically change the leaf value for a service that was created with "vpnid" supplied from CLI:

Create the service with "vpnid" set:

admin@ncs(config)# l3vpn test01 vpnid 123 text a
admin@ncs(config-l3vpn-test01)# commit dry-run
cli {
local-node {
data +l3vpn test01 {
+ vpnid 123;
+}
}
} ############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test01})
DEBUG: service VPN_ID=123
DEBUG: root VPN_ID=123
A simple change in Python code to force leaf value change from Python: #if not vpnid: if vpnid: vpnid = 101 self.log.info('DEBUG: Autogenerated vpnid = {}'.format(vpnid)) service.vpnid = vpnid
Changing "text" value of the existing service should initiate Python code:

admin@ncs(config-l3vpn-test01)# text aa
admin@ncs(config-l3vpn-test01)# commit dry-run
cli {
local-node {
data l3vpn test01 {
- vpnid 123;
+ vpnid 101;
- text a;
+ text aa;
}
}
}

Python debug shows that "service" object reported initial "vpnid" value, and that autogenerated value was set in the service:

############ l3vpn service create ###############
DEBUG: Service create(service=/l3vpn:l3vpn{test01})
DEBUG: service VPN_ID=123
DEBUG: root VPN_ID=123
DEBUG: Autogenerated vpnid = 101

At this stage, when I try to change "vpnid" value from CLI, I get the same result as before. "service" object reports the initial "vpnid" value set on the object (123), does not report updated value (101), and I cannot see the value I actually entered using CLI.

admin@ncs(config-l3vpn-test01)# vpnid 543
admin@ncs(config-l3vpn-test01)# commit dry-run
cli {
}

"service" object reports the initial "vpnid" value set when object was first created. I cannot see value "543" entered via GUI:
############ l3vpn service create ############### DEBUG: Service create(service=/l3vpn:l3vpn{test01}) DEBUG: service VPN_ID=123 DEBUG: root VPN_ID=123 DEBUG: Autogenerated vpnid = 101

Additional tests show that you can programatically change leaf value for another service that exists in NSO. That change works fine. It just does not work for the object that is being created/updated, i.e. the object that calls Service.create callback. I also tried setting "config false;" for the leaf, with the same outcome.

 

Is there a way to set leaf value programatically when creating/updating object, and then changing that value at later time?

1 Accepted Solution

Accepted Solutions

lightfep1
Level 1
Level 1

Try moving your check to the pre_modification and if needed setting the value before the modification.  here is a quick code example:

 

@Service.pre_modification
def cb_pre_modification(self, tctx, op, kp, root, proplist):
   #checking anything other than delete request
   if op is not 2:
# getting the key for the service deployment
newvar = re.search('{(.*)}', str(kp)) l3vpn = root.l3vpn[str(newvar.group(1))] # if no value then setting to 101
if not l3vpn.vpnid: l3vpn.vpnid = 101

Here is my CLI output showing I added a service with no vpnid, one with a vpnid and finally I changed the vpnid on the first service request. 

 

eplight@ncs(config)# l3vpn newvpn text "without vpnid"
eplight@ncs(config-l3vpn-newvpn)# commit
eplight@ncs(config-l3vpn-newvpn)# top
eplight@ncs(config)# l3vpn secondvpn text "with vpnid" vpnid 55555
eplight@ncs(config-l3vpn-secondvpn)# commit
Commit complete.
eplight@ncs(config-l3vpn-secondvpn)# top
eplight@ncs(config)# show full-configuration l3vpn
l3vpn newvpn
 vpnid 101
 text  "without vpnid"
!
l3vpn secondvpn
 vpnid 55555
 text  "with vpnid"
!
eplight@ncs(config)# l3vpn newvpn vpnid 333333
eplight@ncs(config-l3vpn-newvpn)# commit
Commit complete.
eplight@ncs(config-l3vpn-newvpn)# top
eplight@ncs(config)# show full-configuration l3vpn
l3vpn newvpn
 vpnid 333333
 text  "without vpnid"
!
l3vpn secondvpn
 vpnid 55555
 text  "with vpnid"
!

View solution in original post

3 Replies 3

lightfep1
Level 1
Level 1

Try moving your check to the pre_modification and if needed setting the value before the modification.  here is a quick code example:

 

@Service.pre_modification
def cb_pre_modification(self, tctx, op, kp, root, proplist):
   #checking anything other than delete request
   if op is not 2:
# getting the key for the service deployment
newvar = re.search('{(.*)}', str(kp)) l3vpn = root.l3vpn[str(newvar.group(1))] # if no value then setting to 101
if not l3vpn.vpnid: l3vpn.vpnid = 101

Here is my CLI output showing I added a service with no vpnid, one with a vpnid and finally I changed the vpnid on the first service request. 

 

eplight@ncs(config)# l3vpn newvpn text "without vpnid"
eplight@ncs(config-l3vpn-newvpn)# commit
eplight@ncs(config-l3vpn-newvpn)# top
eplight@ncs(config)# l3vpn secondvpn text "with vpnid" vpnid 55555
eplight@ncs(config-l3vpn-secondvpn)# commit
Commit complete.
eplight@ncs(config-l3vpn-secondvpn)# top
eplight@ncs(config)# show full-configuration l3vpn
l3vpn newvpn
 vpnid 101
 text  "without vpnid"
!
l3vpn secondvpn
 vpnid 55555
 text  "with vpnid"
!
eplight@ncs(config)# l3vpn newvpn vpnid 333333
eplight@ncs(config-l3vpn-newvpn)# commit
Commit complete.
eplight@ncs(config-l3vpn-newvpn)# top
eplight@ncs(config)# show full-configuration l3vpn
l3vpn newvpn
 vpnid 333333
 text  "without vpnid"
!
l3vpn secondvpn
 vpnid 55555
 text  "with vpnid"
!

Thanks, this is exactly what I was looking for. It works just as you described.

smansor
Cisco Employee
Cisco Employee

I've done this before but in Java - but I'm sure it's theoretically the same. 

You can modify your service leaf value in preMod(), which executes before your create() function. 

Then in the create() function, you will see the new value. 

 

@ServiceCallback(servicePoint = "NCPF5Service-servicepoint", callType = ServiceCBType.PRE_MODIFICATION)
	public Properties preModification(ServiceContext context, ServiceOperationType operation, ConfPath path,
			Properties opaque) throws IOException, ConfException {

		if (ServiceOperationType.UPDATE.equals(operation)) {
			try {
				logger.info("--- [ PRE_MODIFICATION " + operation.toString() + " ] ---");

				NavuNode service = context.getServiceNode();

				logger.info("--- Processing Security Firewall Rule List ---");
				// set your service leaf value here
				.
				.
				service.container("security-firewall").list(parentListName).elem(parentLeafKeyValue)
						.leaf("rule-name-order-placement").set(ruleName);	
				.
				.
				.
				logger.info("--- [ COMPLETE ] ---");

				return opaque;

			} catch (DpCallbackException dpe) {
				dpe.printStackTrace();
				throw dpe;
			} catch (ConfException e) {
				e.printStackTrace();
				throw new DpCallbackException("Unable to apply pre-mod ", e);
			}
		}
		return opaque;
	}