04-21-2020 03:25 PM
Hi folks,
I'm looking for a way to generate NETCONF XML payload for an edit-config operation without actually performing a CRUD execution against an actual device. The CodecService seems to be intended for this, but I can't seem make use of YFilter settings for my entity containers when using CodecService. For example...
from ydk.services import CodecService from ydk.providers import CodecServiceProvider from ydk.filters import YFilter from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native \ as xe_native provider = CodecServiceProvider(type="xml") codec = CodecService() native = xe_native.Native() vlan = native.Vlan() vlan.yfilter = YFilter.replace vlan_list_inst = vlan.VlanList() vlan_list_inst.id = 100 vlan_list_inst.name = "TEST_VLAN" vlan.vlan_list.append(vlan_list_inst) native.vlan = vlan print(codec.encode(provider, native))
In the above Python script, I've set vlan.yfilter to "YFilter.replace" with the goal of replacing all of the configuration in the "vlan" portion of my configuration with the single VLAN that I've created. The expectation is that this would generate the following XML...
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native"> <vlan operation="replace"> <vlan-list xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-vlan"> <id>100</id> <name>TEST_VLAN</name> </vlan-list> </vlan> </native>
Instead, I get an error generated, which I assume is the result of CodeService not supporting certain yfilter values...
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/ydk/errors/error_handler.py", line 112, in helper return func(self, provider, entity, *args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/ydk/services/codec_service.py", line 78, in encode return self._encode(provider, entity_holder, pretty, subtree) File "/usr/local/lib/python2.7/dist-packages/ydk/services/codec_service.py", line 110, in _encode return result File "/usr/lib/python2.7/contextlib.py", line 35, in __exit__ self.gen.throw(type, value, traceback) File "/usr/local/lib/python2.7/dist-packages/ydk/errors/error_handler.py", line 82, in handle_runtime_error _raise(_exc) File "/usr/local/lib/python2.7/dist-packages/ydk/errors/error_handler.py", line 56, in _raise raise exc ydk.errors.YInvalidArgumentError: Cannot find module with given namespace.
Is there any way to generate the above XML as desired? If CodecService can't do it, I'd be happy to entertain using CRUDService assuming there's a way I can setup a dummy device and capture the XML output that would have been sent to it.
If you're curious about the use case, we're generating NETCONF XML output for planned config changes so that it can be posted to a Git repository for peer review before implementing the actual changes.
Thanks for your help!
Solved! Go to Solution.
04-21-2020 07:05 PM
Hello Jason
There are two issues:
1. Your vlan model is incorrect. Here is how it should be:
from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native as xe_native
native = xe_native.Native()
vlan = native.vlan # vlan instance is already present in native
vlan_list_inst = vlan.VlanList()
vlan_list_inst.id = 100
vlan_list_inst.name = "TEST_VLAN"
vlan.vlan_list.append(vlan_list_inst)
2. The CodecService is not suitable for building XML payload with defined filters. The CradService adInstead you can use XmlSubtreeCodec from ydk.entity_utils package:
from ydk.entity_utils import XmlSubtreeCodec
from ydk.providers import CodecServiceProvider, NetconfServiceProvider
from ydk.filters import YFilter
from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native as xe_native
provider = CodecServiceProvider(type='xml')
provider.initialize('cisco_ios_xe', '/Users/ygorelik/ydk-gen/venv/lib/python3.7/site-packages/ydk/models/cisco_ios_xe/_yang')
root_schema = provider.get_root_schema('cisco_ios_xe')
xml_codec = XmlSubtreeCodec()
native = xe_native.Native()
vlan = native.vlan
vlan_list_inst = vlan.VlanList()
vlan_list_inst.id = 100
vlan_list_inst.name = "TEST_VLAN"
vlan.vlan_list.append(vlan_list_inst)
vlan.yfilter = YFilter.replace
xml = xml_codec.encode(native, root_schema)
print(xml)
The result of this script seems what you are looking for:
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<vlan operation="replace">
<vlan-list xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-vlan">
<id>100</id>
<name>TEST_VLAN</name>
</vlan-list>
</vlan>
</native>
Please note that XmlSubtreeCodec does not validate the entity against YANG models and it requires to provide root schema (extra step in provider initialization).
04-21-2020 07:05 PM
Hello Jason
There are two issues:
1. Your vlan model is incorrect. Here is how it should be:
from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native as xe_native
native = xe_native.Native()
vlan = native.vlan # vlan instance is already present in native
vlan_list_inst = vlan.VlanList()
vlan_list_inst.id = 100
vlan_list_inst.name = "TEST_VLAN"
vlan.vlan_list.append(vlan_list_inst)
2. The CodecService is not suitable for building XML payload with defined filters. The CradService adInstead you can use XmlSubtreeCodec from ydk.entity_utils package:
from ydk.entity_utils import XmlSubtreeCodec
from ydk.providers import CodecServiceProvider, NetconfServiceProvider
from ydk.filters import YFilter
from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native as xe_native
provider = CodecServiceProvider(type='xml')
provider.initialize('cisco_ios_xe', '/Users/ygorelik/ydk-gen/venv/lib/python3.7/site-packages/ydk/models/cisco_ios_xe/_yang')
root_schema = provider.get_root_schema('cisco_ios_xe')
xml_codec = XmlSubtreeCodec()
native = xe_native.Native()
vlan = native.vlan
vlan_list_inst = vlan.VlanList()
vlan_list_inst.id = 100
vlan_list_inst.name = "TEST_VLAN"
vlan.vlan_list.append(vlan_list_inst)
vlan.yfilter = YFilter.replace
xml = xml_codec.encode(native, root_schema)
print(xml)
The result of this script seems what you are looking for:
<native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
<vlan operation="replace">
<vlan-list xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-vlan">
<id>100</id>
<name>TEST_VLAN</name>
</vlan-list>
</vlan>
</native>
Please note that XmlSubtreeCodec does not validate the entity against YANG models and it requires to provide root schema (extra step in provider initialization).
04-22-2020 04:36 PM - edited 04-22-2020 04:37 PM
Hey Yan - thanks for the quick reply! I followed the steps you shared and was able to generate the desired configuration.
A few follow-up questions and comments...
In your code, you referenced the absolute directory of your cisco_ios_xe package in provider.initialize(). I wanted to be able to retrieve the relative directory of the cisco_ios_xe package for portability of this code, so I did the following:
import ydk.models.cisco_ios_xe as xe
<code removed for brevity> provider.initialize('cisco_ios_xe', xe.__path__[0] + '/_yang')
This seems to work for me. However, if there's an better way to capture this path that you're aware of (e.g. a solution that doesn't require actually importing the full cisco_ios_xe package), please let me know.
You commented that one of the key limitations here is that XMLSubtreeCodec does not validate the entity against YANG models. I was under the impression that this validation was implicit when modifying properties of the entity (e.g. if I try to assign an invalid data type to a leaf of an entity, an error would be generated during the assignment action). Is there an additional level of validation that typically occurs during the XML generation?
Depending upon the answer to the above question, I also wanted to get your thoughts on how I might be able to validate the entity against YANG models outside of the XML generation. e.g. Is there a validation method that I could run against my entity prior to calling the XMLSubtreeCodec for XML generation such that I could still have the benefit of YANG model verification?
Thanks for all of your help,
Jason
04-23-2020 04:51 PM
Hi Jason
Your improvement of the script to find path to repository is definitely better than specifying absolute path. But more accurate solution, which works in Python 2 and 3, would be:
from ydk.models.cisco_ios_xe import Cisco_IOS_XE_native as xe_native
import os
provider = CodecServiceProvider(type='xml')
provider.initialize('cisco_ios_xe',
os.path.join(os.path.dirname(xe_native.__file__), '_yang'))
Regarding validation. If this is critical, then I would suggest first to use CodecService on entity without setting operation filters (that will validate the entity structure and leaves' values), then add filters and repeat encoding with XmlSubtreeCodec.
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide