cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
2037
Views
5
Helpful
10
Replies

Sample apps for encoding ACL using OpenConfig

deom
Cisco Employee
Cisco Employee

Related image

Hello Community, 

 

I brought the last set of ACL sample apps for basic level ydk-py. 

You can encode OpenConfig ACL configurations with this set of sample applications.

 

cd-encode-oc-acl-10-ydk.py - encode boilerplate
cd-encode-oc-acl-30-ydk.py - allow one host
cd-encode-oc-acl-32-ydk.py - allow multiple hosts
cd-encode-oc-acl-34-ydk.py - multiple hosts w/ codepoint

 

Using the following link, you can also get into the parent directory of the provided sample applications:

https://github.com/CiscoDevNet/ydk-py-samples/tree/master/samples/basic/codec/models/openconfig/openconfig-acl

 

In order to run any encoding sample applications, you don't need to access to server, but you can simply run the application in Terminal as follows:

./cd-encode-oc-acl-30-ydk.py

 

 

Now, we have three sets of OpenConfig ACL sample applications. 

 

If you did not check out the previous ACL sample app posts yet, please visit the following links to get more information: 

ACL Sample apps with OpenConfig/NETCONF

ACL Sample apps with OpenConfig/gNMI

 

Cheers!

10 Replies 10

horseinthesky
Level 1
Level 1

Hello.
I've tried to generate XML config using examples provided but it failes with the following exception:

 python3 acl2.py
2019-10-18 13:22:47,441 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Value "173.31.1.0/24" does not satisfy the constraint "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))$" (range, length, or pattern). Path: '/openconfig-acl:acl/acl-sets/acl-set[name='ACL3'][type='openconfig-acl:ACL_IPV4']/acl-entries/acl-entry[sequence-id='20']/ipv4/config/source-address'
Traceback (most recent call last):
  File "acl2.py", line 92, in <module>
    main()
  File "acl2.py", line 84, in main
    print(codec.encode(provider, acl))
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 112, in helper
    return func(self, provider, entity, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/ydk/services/codec_service.py", line 78, in encode
    return self._encode(provider, entity_holder, pretty, subtree)
  File "/usr/local/lib/python3.6/dist-packages/ydk/services/codec_service.py", line 110, in _encode
    return result
  File "/usr/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 82, in handle_runtime_error
    _raise(_exc)
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 54, in _raise
    exec("raise exc from None")
  File "<string>", line 1, in <module>
ydk.errors.YModelError:  Value "173.31.1.0/24" does not satisfy the constraint "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))$" (range, length, or pattern). Path: /openconfig-acl:acl/acl-sets/acl-set[name='ACL3'][type='openconfig-acl:ACL_IPV4']/acl-entries/acl-entry[sequence-id='20']/ipv4/config/source-address

The script is:

import logging

from ydk.services import CodecService, CRUDService
from ydk.providers import CodecServiceProvider, NetconfServiceProvider
from ydk.models.openconfig import openconfig_acl as oc_acl

DEVICES = ['10.10.40.4', '10.10.30.5', '10.10.30.6']


def config_acl(acl):
    """Add config data to acl object."""
    # acl-set configuration
    acl_set = acl.AclSets.AclSet()
    acl_set.name = "ACL3"
    acl_set.type = oc_acl.ACLIPV4()
    acl_set.config.name = "ACL3"
    acl_set.config.type = oc_acl.ACLIPV4()
    acl_set.acl_entries = acl_set.AclEntries()
    # acl-entry with sequence number 10
    acl_entry = acl_set.acl_entries.AclEntry()
    acl_entry.sequence_id = 10
    acl_entry.config.sequence_id = 10
    acl_set.acl_entries.acl_entry.append(acl_entry)
    # acl-entry with sequence number 20
    acl_entry = acl_set.acl_entries.AclEntry()
    acl_entry.sequence_id = 20
    acl_entry.config.sequence_id = 20
    acl_entry.actions.config.forwarding_action = oc_acl.REJECT()
    acl_entry.ipv4.config.source_address = "173.31.1.0/24"
    acl_entry.ipv4.config.destination_address = "172.16.0.0/16"
    acl_set.acl_entries.acl_entry.append(acl_entry)
    # acl-entry with sequence number 30
    acl_entry = acl_set.acl_entries.AclEntry()
    acl_entry.sequence_id = 30
    acl_entry.config.sequence_id = 30
    acl_entry.actions.config.forwarding_action = oc_acl.REJECT()
    acl_entry.ipv4.config.source_address = "172.31.2.0/24"
    acl_entry.ipv4.config.destination_address = "172.16.0.0/16"
    acl_entry.ipv4.config.dscp = 46
    acl_set.acl_entries.acl_entry.append(acl_entry)
    # acl-entry with sequence number 40
    acl_entry = acl_set.acl_entries.AclEntry()
    acl_entry.sequence_id = 40
    acl_entry.config.sequence_id = 40
    acl_entry.actions.config.forwarding_action = oc_acl.REJECT()
    acl_entry.ipv4.config.source_address = "172.31.3.0/24"
    acl_entry.ipv4.config.destination_address = "172.16.0.0/16"
    acl_set.acl_entries.acl_entry.append(acl_entry)
    # acl-entry with sequence number 50
    acl_entry = acl_set.acl_entries.AclEntry()
    acl_entry.sequence_id = 50
    acl_entry.config.sequence_id = 50
    acl_entry.actions.config.forwarding_action = oc_acl.ACCEPT()
    acl_set.acl_entries.acl_entry.append(acl_entry)
    acl.acl_sets.acl_set.append(acl_set)


def get_netconf_provider(ip):
    provider = NetconfServiceProvider(
        address=ip,
        port=22,
        username='admin',
        password='admin',
        protocol='ssh'
    )
    return provider


def main():
    logger = logging.getLogger('ydk')
    logger.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    formatter = logging.Formatter(("%(asctime)s - %(name)s - "
                                   "%(levelname)s - %(message)s"))
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    provider = CodecServiceProvider(type='xml')
    codec = CodecService()

    acl = oc_acl.Acl()
    config_acl(acl)

    print(codec.encode(provider, acl))

    # crud = CRUDService()
    # provider = get_netconf_provider(DEVICES[2])
    # crud.create(provider, acl)


if __name__ == '__main__':
    main()

And I have the following versions of ydk packets

pip freeze | grep ydk                 
ydk==0.8.4
ydk-models-cisco-ios-xe==16.9.3
ydk-models-cisco-ios-xr==6.5.1
ydk-models-ietf==0.1.5.post2
ydk-models-openconfig==0.1.6.post1

Could you please help tme to find the answer?

Hello

You are facing well known issue, which is specific to openconfig YANG models. It is well documented issue, see discussion here. I have opened YDK enhancement to overcome this issue in YDK as recommended.

In the meanwhile I can recommend a workaround, which depends on how you are going to use YDK. If this is just for converting data model API to XML payload as it is described bellow, I would recommend to use XmlSubtreeCodec instead of CodecService. Note that in this case the converted data will not be validated. The XmlSubtreeCodec API is described here.

Please let me know if this workaround is not sufficient for your project.

Regards,

Yan

Yan Gorelik
YDK Solutions

Thank you for quick response.
I'm just starting my YDK journey and don't know what it is capable of. Need to spend some time to be able to apply it in my current infrastructure.

I am happy to report that the issue has been fixed. See details on the GitHub.

In order to apply the fix you need to recompile and reinstall YDK-0.8.4 core library and Python core package. Here is the procedure:

  1. git clone -b 0.8.4 https://github.com/CiscoDevNet/ydk-gen.git
  2. cd ydk-gen/sdk/cpp/core
  3. mkdir build
  4. cd build
  5. cmake ..
  6. make
  7. sudo make install
  8. # Activate Python virtual environment if needed
  9. pip install -U ydk
  10. # Run your openconfig script

Please let me know if you have any installation issue.

Good luck!

Yan Gorelik
YDK Solutions

Did this but nothing changed.
I do not understand anything in C++ so I must ask: how pip install -U connected with what I've done before?

It is don't do anything:

pip install -U ydk
WARNING: The directory '/home/horseinthesky/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
WARNING: The directory '/home/horseinthesky/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Requirement already up-to-date: ydk in /usr/local/lib/python3.6/dist-packages (0.8.4)
Requirement already satisfied, skipping upgrade: pybind11>=2.1.1 in /usr/local/lib/python3.6/dist-packages (from ydk) (2.2.4)


So the result is the same:

python3 openconfig_acl.py
Traceback (most recent call last):
File "openconfig_acl.py", line 49, in <module>
print(codec.encode(provider, acl))
File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 112, in helper
return func(self, provider, entity, *args, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/ydk/services/codec_service.py", line 78, in encode
return self._encode(provider, entity_holder, pretty, subtree)
File "/usr/local/lib/python3.6/dist-packages/ydk/services/codec_service.py", line 110, in _encode
return result
File "/usr/lib/python3.6/contextlib.py", line 99, in __exit__
self.gen.throw(type, value, traceback)
File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 82, in handle_runtime_error
_raise(_exc)
File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 54, in _raise
exec("raise exc from None")
File "<string>", line 1, in <module>
ydk.errors.YModelError: Value "172.31.255.1/32" does not satisfy the constraint "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/(([0-9])|([1-2][0-9])|(3[0-2]))$" (range, length, or pattern). Path: /openconfig-acl:acl/acl-sets/acl-set[name='ACL1'][type='openconfig-acl:ACL_IPV4']/acl-entries/acl-entry[sequence-id='20']/ipv4/config/source-address

Needed to recompile the libydk from source and install it https://github.com/CiscoDevNet/ydk-gen#build-from-source
And then reinstall ydk python core package after libydk got changed:

python3 generate.py --core -is

Alternatively you can install YDK from PyPi:

pip3 install -U ydk

horseinthesky
Level 1
Level 1

Now it fails validation against device models I guess:

2019-12-03 11:26:45,055 - ydk - INFO - Path where models are to be downloaded: /home/horseinthesky/.ydk/10.10.30.6
2019-12-03 11:26:45,064 - ydk - INFO - Connected to 10.10.30.6 on port 22 using ssh with timeout of -1
2019-12-03 11:26:45,064 - ydk - INFO - Executing CRUD create operation on [openconfig-acl:acl]
2019-12-03 11:26:45,064 - ydk - INFO - Executing 'edit-config' RPC on [openconfig-acl:acl]
2019-12-03 11:26:47,884 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Internal error (/home/horseinthesky/ydk-gen/gen-api/cpp/ydk/build/project_libyang/src/project_libyang/src/parser_yang.c:4466).
2019-12-03 11:26:47,884 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Module "cisco-xe-openconfig-acl-deviation" parsing failed.
2019-12-03 11:26:47,885 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Internal error (/home/horseinthesky/ydk-gen/gen-api/cpp/ydk/build/project_libyang/src/project_libyang/src/parser_yang.c:4466).
2019-12-03 11:26:47,885 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Module "cisco-xe-openconfig-acl-deviation" parsing failed.
2019-12-03 11:26:47,887 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Internal error (/home/horseinthesky/ydk-gen/gen-api/cpp/ydk/build/project_libyang/src/project_libyang/src/parser_yang.c:4466).
2019-12-03 11:26:47,887 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Module "cisco-xe-openconfig-acl-deviation" parsing failed.
2019-12-03 11:26:47,888 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Internal error (/home/horseinthesky/ydk-gen/gen-api/cpp/ydk/build/project_libyang/src/project_libyang/src/parser_yang.c:4466).
2019-12-03 11:26:47,888 - ydk - ERROR - Data is invalid according to the yang model. Libyang error: Module "cisco-xe-openconfig-acl-deviation" parsing failed.
2019-12-03 11:26:47,888 - ydk - ERROR - Could not fetch schema node 'name'
Traceback (most recent call last):
  File "openconfig_acl.py", line 67, in <module>
    crud.create(provider, acl)
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 112, in helper
    return func(self, provider, entity, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/ydk/services/crud_service.py", line 49, in create
    return _crud_update(provider, entity, self._crud.create)
  File "/usr/local/lib/python3.6/dist-packages/ydk/services/crud_service.py", line 70, in _crud_update
    return crud_call(provider, entity)
  File "/usr/lib/python3.6/contextlib.py", line 99, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 82, in handle_runtime_error
    _raise(_exc)
  File "/usr/local/lib/python3.6/dist-packages/ydk/errors/error_handler.py", line 54, in _raise
    exec("raise exc from None")
  File "<string>", line 1, in <module>
ydk.errors.YCoreError:  Could not fetch schema node: name
2019-12-03 11:26:47,956 - ydk - INFO - Disconnected from device

I tested the script on sandboxes for IOS-XR-6.5.3 and IOS-XE-16.9.3. It works perfectly well on IOS-XR including parsing of deviation file cisco-xr-openconfig-acl-deviations.yang received from the device. But the script fails on IOS-XE while parsing deviation file cisco-xe-openconfig-acl-deviation.yang (same internal error). Then I removed all 'deviation' statements in the failed file located in repository under $HOME/.ydk/<device-IP> and repeated the test. This time it all worked fine. I am guessing that something is wrong with the deviation file for IOS-XE-16.9.3.

Note. When YANG file in default repository is changed, it is not get overwritten by YDK when synchronizing repository with YANG models on device.

I used Libyang utility yanglint to analyze cisco-xe-openconfig-acl-deviation.yang parsing. From your ydk-gen location run:

cd sdk/cpp/core/build/project_libyang/src/project_libyang-build
./yanglint -p /Users/ygorelik/.ydk/ios-xe-mgmt.cisco.com /Users/ygorelik/.ydk/ios-xe-mgmt.cisco.com/cisco-xe-openconfig-acl-deviation.yang 

I found that some of the deviation statements are incorrect. Here is the module content with my comments:

module cisco-xe-openconfig-acl-deviation {
namespace "http://cisco.com/ns/cisco-xe-openconfig-acl-deviation";

prefix acl-devs;

import openconfig-acl {
prefix oc-acl;
}

import tailf-common { prefix tailf; }

organization
"Cisco Systems, Inc.";

contact
"Cisco Systems, Inc.
Customer Service

Postal: 170 W Tasman Drive
San Jose, CA 95134

Tel: +1 1800 553-NETS

E-mail: cs-yang@cisco.com";

description
"This module defines deviation statements for openconfig-acl module.";

revision 2017-08-25 {
description
"Added deviation to remove leafref from ingress/egress acls because of IOS allowing
access-groups on interfaces when the access-group doesn't exit";
}

revision 2017-08-22 {
description
"Remove deviation to require at least one ingress or egress acl on interface";
}

revision 2017-05-04 {
description
"Add";
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set" {
deviate add {
unique "oc-acl:name";
}
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry" {
deviate add {
unique "oc-acl:actions/oc-acl:config/oc-acl:forwarding-action "
+ "oc-acl:l2/oc-acl:config/oc-acl:source-mac "
+ "oc-acl:l2/oc-acl:config/oc-acl:destination-mac "
+ "oc-acl:l2/oc-acl:config/oc-acl:ethertype";
}
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry"
+ "/oc-acl:l2/oc-acl:config/oc-acl:source-mac" {
deviate add {
default 00:00:00:00:00:00;
}
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry"
+ "/oc-acl:l2/oc-acl:config/oc-acl:source-mac-mask" {
deviate add {
must "../oc-acl:source-mac" {
error-message "When source-mac-mask is set, source-mac must be set too";
}
}
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry"
+ "/oc-acl:l2/oc-acl:config/oc-acl:destination-mac" {
deviate add {
default 00:00:00:00:00:00;
}
}

deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry"
+ "/oc-acl:l2/oc-acl:config/oc-acl:destination-mac-mask" {
deviate add {
must "../oc-acl:destination-mac" {
error-message "When destination-mac-mask is set, destination-mac must be set too";
}
}
}
/* WRONG STATEMENTS BELOW THIS LINE
deviation "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/oc-acl:acl-entries/oc-acl:acl-entry"
+ "/oc-acl:actions/oc-acl:config/oc-acl:log-action" {
deviate add {
must ". = 'oc-acl:LOG_NONE' or ../../../../../type != 'oc-acl:ACL_L2'" {
error-message "Log action not supported for L2 ACL sets";
}
}
}

deviation "/oc-acl:acl/oc-acl:interfaces/oc-acl:interface/oc-acl:ingress-acl-sets/oc-acl:ingress-acl-set"
+ "/oc-acl:config/oc-acl:set-name" {
deviate replace {
type leafref {
tailf:no-leafref-check;
path "../../../../../../acl-sets/acl-set/config/name";
}
}
}

deviation "/oc-acl:acl/oc-acl:interfaces/oc-acl:interface/oc-acl:ingress-acl-sets/oc-acl:ingress-acl-set"
+ "/oc-acl:config/oc-acl:type" {
deviate replace {
type leafref {
tailf:no-leafref-check;
path "../../../../../../acl-sets/acl-set[name=current()/../set-name]" +
"/config/type";
}
}
}
deviation "/oc-acl:acl/oc-acl:interfaces/oc-acl:interface/oc-acl:egress-acl-sets/oc-acl:egress-acl-set"
+ "/oc-acl:config/oc-acl:set-name" {
deviate replace {
type leafref {
tailf:no-leafref-check;
path "../../../../../../acl-sets/acl-set/config/name";
}
}
}
deviation "/oc-acl:acl/oc-acl:interfaces/oc-acl:interface/oc-acl:egress-acl-sets/oc-acl:egress-acl-set"
+ "/oc-acl:config/oc-acl:type" {
deviate replace {
type leafref {
tailf:no-leafref-check;
path "../../../../../../acl-sets/acl-set[name=current()/../set-name]" +
"/config/type";
}
}
}
*/
}

 You might consider opening a bug for this IOS-XE YANG model issue.

Yan Gorelik
YDK Solutions

Yanglint
Could you please explain in more detail how to build yanglint binary(?) because I don't have build directory inside ydk-gen/sdk/cpp/core/ and how to use it to verify if models are correct?

Device models
This is another thing I am a little bit confused regarding YDK internal work: is it correct that YDK downloads YANG models from the device to validate the configuration against it? If so does it download only the models used for the particular configuration and not the whole models that are supported on the device?

yanglint

The yanglint is command line utility of Libyang. Please refer to Libyang documentation for details. The Libyang is a third party software, which is used by YDK to parse YANG models, load and output data, which are organized accordingly to the YANG models. The core YDK library - libydk - incorporates the Libyang library, which is compiled during libydk build. Therefore, in order to get yanglint utility you need compile the core YDK library from source. Please note that YDK uses private fork of libyang; its code is adjusted to YDK needs. The YDK code currently is not compatible with master branch due to recent significant change in API by Libyang developers.

Device models

In order to load YANG models to libyang we need create repository (directory) of *.yang files. It can be a limited set of files, which is needed to resolve all the dependencies. By default the repository is organized in directory $HOME/.ydk/<device-IP>. The Netconf protocol has capability to retrieve YANG models from device. When there is a need to load a module to libyang, the YDK looks for that module in repository. If not found, it attempts to retrieve the module from device and puts it to repository. This algorithm allows to have in repository only those models that are needed for particular task, and significantly save time on repeated runs of user application.

 

Yan Gorelik
YDK Solutions
Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: