cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
512
Views
0
Helpful
8
Replies

Python and template : A variable value has not been assigned

Noel Cantenot
Level 1
Level 1

Hello,

I created a python-and-template skeleton for Vlan service.
Very simple yang:
- vlan ID
- description
- list of switches with an IP address.

The XML configure a "interface vlan <ID> with IP and description.

The idea is to use python to "calculate" the description. In this example, the "calculation" is a string 'Configured by NSO'.

But when I commit, I get the error 'A variable value has not been assigned to: DESCRIPTION'.
Servicepoints seem OK; if I replace $DESCRIPTION by a sring n XML, everything works fine…
So it seems it's an issue in communication between Python and XML.

Any idea ?

 

admin@ncs(config)# services cfs-vlan VLAN200 cfs-vlan-id 200 cfs_description "*** vlan 200 ***" cfs-switch nx1 cfs-ip-add 200.1.1.1/24
admin@ncs(config-cfs-switch-nx1)# commit dry-run
Aborted: A variable value has not been assigned to: DESCRIPTION
admin@ncs(config-cfs-switch-nx1)#


YANG:

module cfs-vlan {

  namespace "http://example.com/cfs-vlan";
  prefix cfs-vlan;

  augment /ncs:services {
    list cfs-vlan {
      key cfs-name;

      leaf cfs-name {
        type string;
      }

      uses ncs:service-data;
      ncs:servicepoint "cfs-vlan-servicepoint";

      leaf cfs-vlan-id {
        type uint32;
      }

      leaf cfs_description {
        type string;
      }

      list cfs-switch {
        key cfs-switch-name;
        unique "cfs-ip-add";

        leaf cfs-switch-name {
          type leafref {
            path "/ncs:devices/ncs:device/ncs:name";
          }
        }

        leaf cfs-ip-add {
          type string;
        }
      }
    }
  }
}



XML:

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="cfs-vlan-servicepoint">
  <services xmlns="http://tail-f.com/ns/ncs">

    <?foreach {/cfs-switch}?>
	<rfs-router-intf xmlns="http://example.com/rfs-router-intf">
        <rfs-name>{cfs-switch-name)}</rfs-name>
          <rfs-device-name>{cfs-switch-name}</rfs-device-name>
          <rfs-vlan>
	    .../...
            <rfs-description>{$DESCRIPTION}</rfs-description>
          </rfs-vlan>
  		</rfs-router-intf>
    <?end?>

  </services>
</config-template>



PYTHON:

class ServiceCallbacks(Service):
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')
        vars = ncs.template.Variables()
        vars.add('DESCRIPTION', 'Configured by NSO')
        template = ncs.template.Template(service)
        template.apply('cfs-vlan-template', vars)

class Main(ncs.application.Application):
    def setup(self):
        self.log.info('Main RUNNING')
        self.register_service('cfs-vlan-servicepoint', ServiceCallbacks)



MAKEFILE:

[root@nso-dev-nc-nso6 src]#make
pylint --disable=R,C --reports=n ../python/cfs_vlan/*.py  || (test $? -ge 4)
************* Module cfs_vlan.main
/root/nso-instance-NOEL/packages/cfs-vlan/python/cfs_vlan/main.py:17:8: W0622: Redefining built-in 'vars' (redefined-builtin)
/root/nso-instance-NOEL/packages/cfs-vlan/python/cfs_vlan/main.py:15:49: W0212: Access to a protected member _path of a client class (protected-access)

------------------------------------------------------------------
Your code has been rated at 8.67/10 (previous run: 8.67/10, +0.00)


1 Accepted Solution

Accepted Solutions

You have the servicepont in the template and you have it in the python code. The servicepoint in the template is overriding the one in the python so the python code is not being executed.

This happens when you start with a template service and then convert to a python+template and just copy over the template file - I've done it a few times.. better is to keep the skeleton generted template first and last lines and just include the content.

All you need to do is remove the servicepoint attribute in the first line of the template.

 

 

 

View solution in original post

8 Replies 8

Marcel Zehnder
Spotlight
Spotlight

Can you post the tree output of your package (cd /path/to/your/packages && tree cfs-vlan) and the content of the file /path/to/your/packages/cfs-vlan/package-meta-data.xml?

[root@nso-dev-nc-nso6 cfs-vlan]# pwd
/root/nso-instance-NOEL/packages/cfs-vlan

[root@nso-dev-nc-nso6 cfs-vlan]# more package-meta-data.xml
<ncs-package xmlns="http://tail-f.com/ns/ncs-packages">
  <name>cfs-vlan</name>
  <package-version>1.0</package-version>
  <description>Generated Python package</description>
  <ncs-min-version>6.3</ncs-min-version>

  <component>
    <name>main</name>
    <application>
      <python-class-name>cfs_vlan.main.Main</python-class-name>
    </application>
  </component>
</ncs-package>
[root@nso-dev-nc-nso6 cfs-vlan]#

 

 

 

 

[root@nso-dev-nc-nso6 cfs-vlan]# tree
.
├── load-dir
│   └── cfs-vlan.fxs
├── package-meta-data.xml
├── python
│   └── cfs_vlan
│       ├── __init__.py
│       └── main.py
├── README
├── src
│   ├── Makefile
│   └── yang
│       └── cfs-vlan.yang
├── templates
│   └── cfs-vlan-template.xml
└── test
    ├── internal
    │   ├── lux
    │   │   ├── Makefile
    │   │   └── service
    │   │       ├── dummy-device.xml
    │   │       ├── dummy-service.xml
    │   │       ├── Makefile
    │   │       ├── pyvm.xml
    │   │       └── run.lux
    │   └── Makefile
    └── Makefile

10 directories, 16 files

Marcel Zehnder
Spotlight
Spotlight

Looking good from my point of view (however, I didn't use NSO much in the last couple of months).

Can you try, delete the loop in the template and instead do the loop in the Python code?

TEMPLATE

 

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="cfs-vlan-servicepoint">
  <services xmlns="http://tail-f.com/ns/ncs">
	<rfs-router-intf xmlns="http://example.com/rfs-router-intf">
        <rfs-name>{$SWITCH}</rfs-name>
          <rfs-device-name>{$SWITCH}</rfs-device-name>
          <rfs-vlan>
	    .../...
            <rfs-description>{$DESCRIPTION}</rfs-description>
          </rfs-vlan>
  		</rfs-router-intf>
  </services>
</config-template>

 

PYTHON

 

class ServiceCallbacks(Service):
    .create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')
        vars = ncs.template.Variables()
        template = ncs.template.Template(service)
        
        for sw in service.cfs-switch:
            vars.add('SWITCH', sw)
            vars.add('DESCRIPTION', 'Configured by NSO')
            template.apply('cfs-vlan-template', vars)

class Main(ncs.application.Application):
    def setup(self):
        self.log.info('Main RUNNING')
        self.register_service('cfs-vlan-servicepoint', ServiceCallbacks)

 

<config-template xmlns="http://tail-f.com/ns/config/1.0" servicepoint="cfs-vlan-servicepoint">
  <services xmlns="http://tail-f.com/ns/ncs">
	<rfs-router-intf xmlns="http://example.com/rfs-router-intf">
        <rfs-name>{$SWITCH}</rfs-name>
          <rfs-device-name>{$SWITCH}</rfs-device-name>
          <rfs-vlan>
 		        <rfs-vlan-id>{$VLANID}</rfs-vlan-id>
            <rfs-ip-add>{$IPADD}</rfs-ip-add>
            <rfs-description>{$DESCRIPTION}</rfs-description>
          </rfs-vlan>
  		</rfs-router-intf>
  </services>
</config-template>


class ServiceCallbacks(Service):

    # The create() callback is invoked inside NCS FASTMAP and
    # must always exist.
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')
        vars = ncs.template.Variables()
        template = ncs.template.Template(service)

        for sw in service.cfs-switch:
            vars.add('SWITCH', sw)
            vars.add('DESCRIPTION', 'Configured by NSO')
            vars.add('VLANID', '12')
            vars.add('IPADD', '10.1.1.1/24')
            template.apply('cfs-vlan-template', vars)

admin@ncs(config-cfs-switch-nx1)# commit dry-run
Aborted: A variable value has not been assigned to: DESCRIPTION, IPADD, SWITCH, VLANID

Also delete the servicepoint from the template:

<config-template xmlns="http://tail-f.com/ns/config/1.0">
  <services xmlns="http://tail-f.com/ns/ncs">
	<rfs-router-intf xmlns="http://example.com/rfs-router-intf">
        <rfs-name>{$SWITCH}</rfs-name>
          <rfs-device-name>{$SWITCH}</rfs-device-name>
          <rfs-vlan>
 		        <rfs-vlan-id>{$VLANID}</rfs-vlan-id>
            <rfs-ip-add>{$IPADD}</rfs-ip-add>
            <rfs-description>{$DESCRIPTION}</rfs-description>
          </rfs-vlan>
  		</rfs-router-intf>
  </services>
</config-template>

You have the servicepont in the template and you have it in the python code. The servicepoint in the template is overriding the one in the python so the python code is not being executed.

This happens when you start with a template service and then convert to a python+template and just copy over the template file - I've done it a few times.. better is to keep the skeleton generted template first and last lines and just include the content.

All you need to do is remove the servicepoint attribute in the first line of the template.

 

 

 

And yes it works !!!

Thanks !!