CiscoDevNet / ydk-py

This project has been archived and the repository will no longer be updated. Python SDK generated from YANG data models.

Home Page:http://ydk.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

client-side validation

jedelman8 opened this issue · comments

So I was hoping to see better client side validation with the oc bgp model.

I first tried setting a bad ASN and that doesn't do a client side check, but it supports numbers way higher than the device supports, so it's deceiving.

I then tried a bad IP as a router id. But it's still making the request.

>>> bgp.global_.config.router_id = 'a300.1.1.1'
>>> crud.create(provider, bgp)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/ydk/services/crud_service.py", line 61, in create
    self._execute_crud_operation_on_provider(provider, entity, 'CREATE', False)
  File "/usr/local/lib/python2.7/dist-packages/ydk/services/crud_service.py", line 166, in _execute_crud_operation_on_provider
    operation
  File "/usr/local/lib/python2.7/dist-packages/ydk/services/service.py", line 37, in execute_payload
    reply = provider.execute(payload, operation)
  File "/usr/local/lib/python2.7/dist-packages/ydk/providers/netconf_provider.py", line 90, in execute
    return self.sp_instance.execute_operation(payload, operation)
  File "/usr/local/lib/python2.7/dist-packages/ydk/providers/_provider_plugin.py", line 224, in execute_operation
    return self._handle_rpc_reply(operation, payload, reply_str.xml)
  File "/usr/local/lib/python2.7/dist-packages/ydk/providers/_provider_plugin.py", line 246, in _handle_rpc_reply
    self._handle_rpc_error(payload, reply_str, pathlist)
  File "/usr/local/lib/python2.7/dist-packages/ydk/providers/_provider_plugin.py", line 262, in _handle_rpc_error
    raise YPYServiceProviderError(error_code=YPYErrorCode.SERVER_REJ, error_msg=reply_str)
ydk.errors.YPYServiceProviderError: Server rejected request.
        error-type: protocol
        error-tag: bad-element
        error-severity: error
        error-path: ns1:bgp/global/config/router-id
        bad-element: router-id

Is there anything that shows a better use case of client side validation with ydk-py using the oc bgp model?

For the AS number, YDK is following the model and the XR deviation. It's really a model mapping issue. Wrt router id, have you validated against the regular expression in the model?

Based on the above, I think YDK's regular expression validation needs to be fixed. BTW, we are using the issue tracker in YDK-Gen to track all issues. Thanks

@abhikeshav It's interesting you have two repos for this vs. keeping gen and py in the same repo. Anyway, why is there a regex in the YDK as opposed to the constraints built-in to the model?

@111pontes I didn't validate against the regex becuase regex's are never fun plus I tried setting a router id with an octet of 300 (300.1.1.1) and then even put a letter in with a300.1.1.1 - shouldn't either of them be caught?

On a side note, can you clarify if these constraints are being read in from the yang model?

Also, why not enforce the client side check as soon as the object is trying to be created rather than when crun.create is called. The intelligence is already there and this would be ideal:

>>> bgp.global_.config.router_id = 'a300.1.1.1'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: name 'a300.1.1.1' is not a valid router_id  (this is a mock up)

ydk-py and ydk-gen have very different audiences. Yes, one repo is certainly possible, but that would make it significantly less usable for ydk-py which are a majority. Since ydk-py is generated by ydk-gen, issues and their fixes reside on the latter. That approach guarantees that fixes are present in all future versions of ydk-py.

@jedelman8
Thanks for your suggestion about the validating at assignment. Definitely worth thinking about.

ydk-py python modules are generated from yang models using the ydk-gen tool. ydk-py consists of API and metadata, the latter of which includes the constraints information. So the regex from the yang model will be present in ydk-py's metadata.

Cool, thanks @abhikeshav.

I have been looking at the same kind of issue, my test code is below:

from ydk.services import CRUDService
from ydk.providers import NetconfServiceProvider
from ydk.models.openconfig import openconfig_bgp as oc_bgp
from ydk.errors import YPYModelError

provider = NetconfServiceProvider(address='xrv', port=830, username='cisco', password='cisco', protocol='ssh')

crud_service = CRUDService()

bgp_config = oc_bgp.Bgp()

initial_bgp = crud_service.read(provider, bgp_config)

print(initial_bgp.global_.config.as_)

bgp_config.global_.config.as_ = 65001

crud_service.delete(provider, bgp_config)

crud_service.create(provider, bgp_config)

new_bgp = crud_service.read(provider, bgp_config)

print(new_bgp.global_.config.as_)

crud_service.create(provider, initial_bgp)

bgp_config.global_.config.as_ = -1

try:
crud_service.create(provider, bgp_config)
except YPYModelError as e:
print("That didn't work, as expected - " + str(e))

try:
current_bgp = crud_service.read(provider, bgp_config)
print(current_bgp.global_.config.as_)
except YPYModelError as e:
print("That didn't work, which is unexpected - " + str(e))

provider.close()

What I expect is that this create complains as -1 is not a valid value. And that is what happens and all is fine.

BUT, when I then try to read the configuration again, just to prove that it has not changed, I get the same error. This is the full output:

65001
65001
That didn't work, as expected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))
That didn't work, which is unexpected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))

So, it looks like something is not right here.

Nathan,

You're still using the bgp_config object you created originally as a parameter to the query. Thus you are asking to read back the BGP config with AS = -1, which is invalid. Thus, with your code as is, the error is entirely expected.

Try the read but with a bgp_config object you haven't setup with "bgp_config.global_.config.as_ = -1".

Cheers,

Einar

On Oct 16, 2016, at 7:44 AM, Nathan John Sowatskey <notifications@github.com mailto:notifications@github.com> wrote:

I have been looking at the same kind of issue, my test code is below:

from ydk.services import CRUDService
from ydk.providers import NetconfServiceProvider
from ydk.models.openconfig import openconfig_bgp as oc_bgp
from ydk.errors import YPYModelError

provider = NetconfServiceProvider(address='xrv', port=830, username='cisco', password='cisco', protocol='ssh')

crud_service = CRUDService()

bgp_config = oc_bgp.Bgp()

initial_bgp = crud_service.read(provider, bgp_config)

print(initial_bgp.global_.config.as_)

bgp_config.global_.config.as_ = 65001

crud_service.delete(provider, bgp_config)

crud_service.create(provider, bgp_config)

new_bgp = crud_service.read(provider, bgp_config)

print(new_bgp.global_.config.as_)

crud_service.create(provider, initial_bgp)

bgp_config.global_.config.as_ = -1

try:
crud_service.create(provider, bgp_config)
except YPYModelError as e:
print("That didn't work, as expected - " + str(e))

try:
current_bgp = crud_service.read(provider, bgp_config)
print(current_bgp.global_.config.as_)
except YPYModelError as e:
print("That didn't work, which is unexpected - " + str(e))

provider.close()

What I expect is that this create complains as -1 is not a valid value. And that is what happens and all is fine.

BUT, when I then try to read the configuration again, just to prove that it has not changed, I get the same error. This is the full output:

65001
65001
That didn't work, as expected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))
That didn't work, which is unexpected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))

So, it looks like something is not right here.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub #14 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AFKGcFOA50NN39ZEx1O01SEk4HBQGwrDks5q0cfcgaJpZM4Jrh3E.

https://cloud.githubusercontent.com/assets/143418/17495839/a5054eac-5d88-11e6-95fc-7290892c7bb5.png https://cloud.githubusercontent.com/assets/143418/15842166/7c72db34-2c0b-11e6-9aed-b52498112777.png https://github.com/CiscoDevNet/ydk-py #14 (comment)

Got it, that makes sense.

So the style here is to have a bgp_config object that remains untouched, as it were, for reads.

I wonder whether there should be a specific static member defined for that purpose, as it might be a common pattern?

Thanks

Nathan

On 17 Oct 2016, at 11:37, Einar Nilsen-Nygaard notifications@github.com wrote:

Nathan,

You're still using the bgp_config object you created originally as a parameter to the query. Thus you are asking to read back the BGP config with AS = -1, which is invalid. Thus, with your code as is, the error is entirely expected.

Try the read but with a bgp_config object you haven't setup with "bgp_config.global_.config.as_ = -1".

Cheers,

Einar

On Oct 16, 2016, at 7:44 AM, Nathan John Sowatskey <notifications@github.com mailto:notifications@github.com> wrote:

I have been looking at the same kind of issue, my test code is below:

from ydk.services import CRUDService
from ydk.providers import NetconfServiceProvider
from ydk.models.openconfig import openconfig_bgp as oc_bgp
from ydk.errors import YPYModelError

provider = NetconfServiceProvider(address='xrv', port=830, username='cisco', password='cisco', protocol='ssh')

crud_service = CRUDService()

bgp_config = oc_bgp.Bgp()

initial_bgp = crud_service.read(provider, bgp_config)

print(initial_bgp.global_.config.as_)

bgp_config.global_.config.as_ = 65001

crud_service.delete(provider, bgp_config)

crud_service.create(provider, bgp_config)

new_bgp = crud_service.read(provider, bgp_config)

print(new_bgp.global_.config.as_)

crud_service.create(provider, initial_bgp)

bgp_config.global_.config.as_ = -1

try:
crud_service.create(provider, bgp_config)
except YPYModelError as e:
print("That didn't work, as expected - " + str(e))

try:
current_bgp = crud_service.read(provider, bgp_config)
print(current_bgp.global_.config.as_)
except YPYModelError as e:
print("That didn't work, which is unexpected - " + str(e))

provider.close()

What I expect is that this create complains as -1 is not a valid value. And that is what happens and all is fine.

BUT, when I then try to read the configuration again, just to prove that it has not changed, I get the same error. This is the full output:

65001
65001
That didn't work, as expected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))
That didn't work, which is unexpected -
Bgp.Global.Config.as_: (INVALID_VALUE, Value is invalid: -1 not in range (0, 4294967295))

So, it looks like something is not right here.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub #14 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AFKGcFOA50NN39ZEx1O01SEk4HBQGwrDks5q0cfcgaJpZM4Jrh3E.

https://cloud.githubusercontent.com/assets/143418/17495839/a5054eac-5d88-11e6-95fc-7290892c7bb5.png https://cloud.githubusercontent.com/assets/143418/15842166/7c72db34-2c0b-11e6-9aed-b52498112777.png https://github.com/CiscoDevNet/ydk-py #14 (comment)

You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.


Nathan John Sowatskey
Consulting Engineer - Programmable Infrastructure, DevOps, IoT and SDN
nathan@nathan.to
www.linkedin.com/in/nathandevops
XMPP: nathandotto@im.koderoot.net
Google: nathanjohnsowatskey@gmail.com
Skype: nathan_sowatskey
Twitter: NathanDotTo
GitHub: https://github.com/DevOps4Networks
http://www.kipling.org.uk/poems_if.htm

So the style here is to have a bgp_config object that remains untouched, as it were, for reads.

Yes.

I wonder whether there should be a specific static member defined for that purpose, as it might be a common pattern?

I'm not sure. This is an optimization that only really adds value if you will be frequently making parameterless queries such that just doing:

# assume all imports in place, etc.

try:
    current_bgp = crud_service.read(provider, oc_bgp.Bgp())
    print(current_bgp.global_.config.as_)
except YPYModelError as e:
    print("That didn't work, which is unexpected - " + str(e))

...becomes a performance issue. What you're suggesting might be:

# assume all imports in place, etc.

try:
    current_bgp = crud_service.read(provider, oc_bgp.Bgp.static_member_for_query)
    print(current_bgp.global_.config.as_)
except YPYModelError as e:
    print("That didn't work, which is unexpected - " + str(e))

Forgive the name, just there for obviousness. I don't see what it adds in general.

Cheers,

Einar

Then the idiom should probably be to do this:

read_bgp = oc_bgp.Bgp()

And to use that read_bgp for read purposes, but not for config purposes.

Thanks

Nathan

On 17 Oct 2016, at 12:03, Einar Nilsen-Nygaard notifications@github.com wrote:

So the style here is to have a bgp_config object that remains untouched, as it were, for reads.

Yes.

I wonder whether there should be a specific static member defined for that purpose, as it might be a common pattern?

I'm not sure. This is an optimization that only really adds value if you will be frequently making parameterless queries such that just doing:

assume all imports in place, etc.

try:
current_bgp = crud_service.read(provider, oc_bgp.Bgp())
print(current_bgp.global_.config.as_)
except YPYModelError as e:
print("That didn't work, which is unexpected - " + str(e))

...becomes a performance issue.

Cheers,

Einar


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.


Nathan John Sowatskey
Consulting Engineer - Programmable Infrastructure, DevOps, IoT and SDN
nathan@nathan.to
www.linkedin.com/in/nathandevops
XMPP: nathandotto@im.koderoot.net
Google: nathanjohnsowatskey@gmail.com
Skype: nathan_sowatskey
Twitter: NathanDotTo
GitHub: https://github.com/DevOps4Networks
http://www.kipling.org.uk/poems_if.htm

Yes, that works.

@NathanDotTo, note that the second parameter passed to the read operation is a filter object. On the other hand, the second parameter passed to the create operation is a configuration object. There is a subtle, but important difference. The CRUD documentation differentiates both parameters:
http://ydk.cisco.com/py/docs/ydk.services.html#crudservice-provides-create-read-update-delete-api-s
You should be able to set some values in a filter (e.g. list key value).

Thank you, that makes perfect sense.

Regards

Nathan

On 17 Oct 2016, at 17:56, Santiago Álvarez notifications@github.com wrote:

@NathanDotTo, note that the second parameter passed to the read operation is a filter object. On the other hand, the second parameter passed to the create operation is a configuration object. There is a subtle, but important difference. The CRUD documentation differentiates both parameters:
http://ydk.cisco.com/py/docs/ydk.services.html#crudservice-provides-create-read-update-delete-api-s
You should be able to set some values in a filter (e.g. list key value).


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.


Nathan John Sowatskey
Consulting Engineer - Programmable Infrastructure, DevOps, IoT and SDN
nathan@nathan.to
www.linkedin.com/in/nathandevops
XMPP: nathandotto@im.koderoot.net
Google: nathanjohnsowatskey@gmail.com
Skype: nathan_sowatskey
Twitter: NathanDotTo
GitHub: https://github.com/DevOps4Networks
http://www.kipling.org.uk/poems_if.htm

I believe this issue was resolved. If further clarification is needed, please use the YDK community or, if an issue has been identified, report it in ydk-gen.