cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
1319
Views
6
Helpful
9
Replies

NSO Python create nested service call

ArneV
Level 1
Level 1

Is it possible for an nso (python) service to call an other nso (python) service?

For instance:

service X -> service Y

I was looking at cb_post_modification, but it looks like it doesn't achieve the desired result.

Example python code of service X:

 

class ServiceCallbacks(Service):
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')

        template = ncs.template.Template(service)
        template_vars = ncs.template.Variables()
        
    @service.post_modification
    def cb_post_modification(self, tctx, op, kp, root, proplist):
        self.log.info('Service postmod(service=', kp, ')')
        service = ncs.maagic.cd(root, kp)
        leafref = root.services.X[service.X]
        with ncs.maapi.single_write_trans('admin', 'python') as t:
                root = ncs.maagic.get_root(t)
                root.services.Y.create(leafref)
                t.apply()

 

 Also because service Y uses a leafref, i am not sure how it should be referenced

2 Accepted Solutions

Accepted Solutions

mmollber
Cisco Employee
Cisco Employee

Hi, I am not sure I follow exactly what you are trying to achieve but I will try to answer anyway.

One service creating another service is something we usually call a stacked service.

See the following example:

 

 

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

    leaf dummy {
      type inet:ipv4-address;
    }
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')

        servicey = root.servicey.create(service.name + '-created-y')
        servicey.dummy = '127.0.0.1'
        servicey.device = 'c0'
admin@ncs% commit dry-run
cli {
    local-node {
        data +servicey test-service-created-y {
             +    device c0;
             +    dummy 127.0.0.1;
             +}
             +servicex test-service {
             +}
    }
}

 

 

Here ServiceX will create ServiceY inside the create callback. The leafref to the device is referenced by name "c0". If there is no such device the commit will fail with "Aborted: illegal reference 'servicey test-service-created-y device'

View solution in original post

mmollber
Cisco Employee
Cisco Employee

In this case the GUI is trying to be helpful by validating the changes before doing commit. You will see the same thing in the CLI if you do "validate".

admin@ncs% commit dry-run
cli {
    local-node {
        data -servicey newservice {
             -}
             -servicex newservice {
             -}
    }
}
admin@ncs% validate
Failed: illegal reference 'servicey newservice name'
[error][2023-05-04 13:38:05]

And this isn't incorrect since ServiceY requires a ServiceX to exist, but behaves differently in the CLI because it doesn't prevent the commit the way the GUI does. In order to remove this dependency you can use a non-strict leafref.

leaf name {
      type string;
      tailf:non-strict-leafref {
        path "/ncs:services/X:X/X:name";
      }
    }

Since ServiceY has a dependency on ServiceX these types of issues can occur.

I am curious why you want to have a leafref to ServiceX from ServiceY and if there is an alternative solution to your problem. These kinds of circular dependencies can be difficult to work with.

View solution in original post

9 Replies 9

mmollber
Cisco Employee
Cisco Employee

Hi, I am not sure I follow exactly what you are trying to achieve but I will try to answer anyway.

One service creating another service is something we usually call a stacked service.

See the following example:

 

 

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

    leaf dummy {
      type inet:ipv4-address;
    }
    @service.create
    def cb_create(self, tctx, root, service, proplist):
        self.log.info('Service create(service=', service._path, ')')

        servicey = root.servicey.create(service.name + '-created-y')
        servicey.dummy = '127.0.0.1'
        servicey.device = 'c0'
admin@ncs% commit dry-run
cli {
    local-node {
        data +servicey test-service-created-y {
             +    device c0;
             +    dummy 127.0.0.1;
             +}
             +servicex test-service {
             +}
    }
}

 

 

Here ServiceX will create ServiceY inside the create callback. The leafref to the device is referenced by name "c0". If there is no such device the commit will fail with "Aborted: illegal reference 'servicey test-service-created-y device'

Creation seems to work perfect.

Only the deletion of the stacked service doesn't seems to work.

 

mmollber
Cisco Employee
Cisco Employee

What does your code look like now?

This is what you should see when deleting servicex.

admin@ncs% delete servicex test-service
[ok][2023-05-04 08:13:27]

[edit]
admin@ncs% commit dry-run
cli {
    local-node {
        data -servicey test-service-created-y {
             -    device c0;
             -    dummy 127.0.0.1;
             -}
             -servicex test-service {
             -}
    }
}
[ok][2023-05-04 08:13:28]

 

Just tested, if i delete it from the ncs_cli, it can be deleted.

But if i delete it from the GUI (NSO 5.6.1), then it gives a reference error. (a bug maybe?)

mmollber
Cisco Employee
Cisco Employee

It should work for both, I did not see any issues using the GUI.

I saw that you used post_modification in your first post and just want to mention that configuration set in post_mod (and pre_mod) is not automatically removed when the service is deleted. Those callbacks are handled differently.

Config now looks like this, already removed the post_modification  
Yang service X

key "name";
      leaf name{
        type string;
      }

Python Service X

class ServiceCallbacks(Service):

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

        template = ncs.template.Template(service)
        template_vars = ncs.template.Variables()
        root.services.Y.create(service.X)

Yang service Y

      key name ;
      leaf name {
                type leafref {
                  path "/ncs:services/X:X/X:name";
              }
      }

 

mmollber
Cisco Employee
Cisco Employee

In this case the GUI is trying to be helpful by validating the changes before doing commit. You will see the same thing in the CLI if you do "validate".

admin@ncs% commit dry-run
cli {
    local-node {
        data -servicey newservice {
             -}
             -servicex newservice {
             -}
    }
}
admin@ncs% validate
Failed: illegal reference 'servicey newservice name'
[error][2023-05-04 13:38:05]

And this isn't incorrect since ServiceY requires a ServiceX to exist, but behaves differently in the CLI because it doesn't prevent the commit the way the GUI does. In order to remove this dependency you can use a non-strict leafref.

leaf name {
      type string;
      tailf:non-strict-leafref {
        path "/ncs:services/X:X/X:name";
      }
    }

Since ServiceY has a dependency on ServiceX these types of issues can occur.

I am curious why you want to have a leafref to ServiceX from ServiceY and if there is an alternative solution to your problem. These kinds of circular dependencies can be difficult to work with.

With the non-strict-leafref it works perfectly.

For us its important service X is an service which only exists in the cdb and relates to different services. 

Except in some cases there is a little bit of configuration needed, and we don't want to create 1 very complex service.

snovello
Cisco Employee
Cisco Employee

Yes you can.

But you should not be creating a separate transaction. Just create the service directly in the create callback if you want to do it in python, or alternatively and I think typically better, I would add the service that needs to be created into the template of the X service.

A leafref is just a leaf so it should just be referenced like any leaf. Setting the type as  leafref just constrains its value.