Coder Social home page Coder Social logo

ciscodevnet / ydk-gen Goto Github PK

View Code? Open in Web Editor NEW
133.0 30.0 73.0 164.84 MB

Generate model-driven APIs from YANG models

Home Page: http://ciscodevnet.github.io/ydk-gen/

License: Apache License 2.0

Shell 2.42% Python 16.47% Makefile 14.99% Batchfile 0.34% C++ 54.70% CMake 1.90% Ruby 0.06% CSS 0.01% C 0.25% Go 8.85% Dockerfile 0.01%
yang python c-plus-plus c-plus-plus-11 ydk

ydk-gen's Introduction

Codacy Badge License Build Status codecov Docker Automated build

ydk-logo-128

YANG Development Kit

Table of Contents

Overview

The YANG Development Kit (YDK) is a software development tool, which provides API for building applications based on YANG models. The YDK allows generate YANG model API and provides services to apply generated API over various communication protocols. Currently implemented protocols are: Netconf, Restconf, OpenDaylight and gNMI. YDK provides CRUD and protocol specific services over above protocols. YDK also provides codec services to translate API models to/from XML and JSON encoded strings.

The YDK is a multi-language software. Currently supported languages are Python, Go, and C++.

Other tools and libraries are used to deliver YDK functionality:

  • YANG model analysis and code generation is implemented using APIs from the pyang library
  • Documentation is generated using Sphinx
  • Runtime YANG model analysis is done using libyang
  • C++ to python bindings are created using pybind11
  • C++ uses catch and spdlog for tests and logging respectively

The output of ydk-gen is either a core package, that defines main services and providers, or add-on service package like gNMI Service, or a module bundle, consisting of programming language APIs derived from YANG models. Each module bundle is generated using a bundle profile and the ydk-gen tool. Developers can either use pre-packaged generated bundles, or define their own bundle, consisting of a set of YANG models, using a bundle profile (e.g. ietf_0_1_5.json). This gives the developer an ability to customize scope of their bundle based on their requirements.

Backward Compatibility

The YDK-0.8.6 core is backward compatible with all previously generated model bundles starting from release of YDK-0.7.3. However the YDK-0.8.6 generates different code and model API comparing to YDK-0.8.4. The YDK-0.8.6 generated code is not compatible with YDK-0.7.2 and earlier bundle packages due to changes in modeling and handling of YList objects.

NOTE. Starting from release 0.8.6 the YDK does not support Python2 interpreter as it was deprecated.

Docker

A docker image is automatically built with the latest ydk-gen commit. This docker can be used to run ydk-gen without installing anything natively on your platform.

To use the docker image, install docker on your system and run the below command. See the docker documentation for more details.

docker run -it ydksolutions/ydk-gen:0.8.6.5

System Requirements

The YDK is currently supported on the following platforms including native installations, virtual machines, and docker images:

  • Linux Ubuntu Xenial (16.04 LTS), Bionic (18.04 LTS), Focal (20.04 LTS), and Jammy (22.04 LTS)
  • Linux CentOS versions 7 and Centos Stream 8 (Centos 8.x has been EOL as of December 31 of 2021)
  • Linux RHEL version 7.x and 8.x
  • MacOS up to 11.6.2 (Big Sur)

On Windows 10 the Linux virtual machine can run using Windows Subsystem for Linux (WSL); check this for virtual machine installation procedure. The YDK has been tested in such environment on Ubuntu Bionic (18.04 LTS) and Focal (20.04 LTS) images obtained from Microsoft Store.

On supported platforms the YDK can be installed using installation script. On other platforms the YDK should be installed manually from source. For both the methods the user must install git package prior to the installation procedure.

All YDK core components are based on C and C++ code. These components compiled using default compilers for the supported platform. Corresponding binaries, libraries, and header files are installed in default locations, which are /usr/local/bin, /usr/local/lib, and /usr/local/include. The user must have sudo access in order to install YDK core components to these locations. Make sure the sudo package is installed on your platform prior to the YDK installation procedure.

Core Installation

Installation script

For YDK installation it is recommended to use script install_ydk.sh from ydk-gen git repository. The script detects platform OS, installs all the dependencies and builds complete set of YDK components for specified language.

If the script installs any YDK component, it also creates an environment activation file '.env' in the 'ydk-gen' directory, which can be used to activate YDK runtime environment identical to the installation environment. To activate YDK runtime environment simply run this command once in bash shell:

cd ydk-gen
source .env

The YDK extensively uses Python scripts for building its components and model API packages (bundles). In order to isolate YDK Python environment from system installation, the script can build Python3 virtual environment. If built, the user must manually activate virtual environment when generating model bundles and/or running YDK based application. By default the Python virtual environment is installed under $HOME/venv directory. For different location the PYTHON_VENV environment variable should be set to that location.

NOTE. It is strongly recommended to use Python virtual environment on Centos/RHEL and Mac platforms.

When installing YDK for Python or Go programming languages, the third party dependencies and C++ packages must be installed first. This step requires sudo/root access to the installation platform. Here is simple example of core YDK installation for Python programming language and Python virtual environment:

git clone https://github.com/ygorelik/ydk-gen.git
cd ydk-gen
export YDKGEN_HOME=`pwd`  # optional
export PYTHON_VENV=$HOME/ydk_vne  # optional
./install_ydk.sh --cpp --core --venv   # This step requires sudo access!
./install_ydk.sh --py --core --venv

The script also allows installing individual components like dependencies, core, and service packages for specified programming language or for all supported languages. Full set of script capabilities could be viewed like this:

./install_ydk.sh --help
usage: install_ydk [ {--cpp|--py|--go|--all} ] [-c] [-s gnmi] [-h] [-n] [-v]
Options and arguments:
  --cpp                 install YDK for C++ programming language;
                        requires sudo access for dependencies and libraries installation
  --go                  install YDK for Go programming language
  --py|--python         install YDK for Python programming language
  --all                 install YDK for all available programming languages;
                        requires sudo access for dependencies and libraries installation
  -v|--venv             create python virtual environment
  -c|--core             install YDK core package
  -s|--service gnmi     install gNMI service package
  -n|--no-deps          skip installation of dependencies;
                        applicable only with --cpp and --all options
  -h|--help             print this help message and exit

Environment variables:
YDKGEN_HOME         specifies location of ydk-gen git repository;
                    if not set, $HOME/ydk-gen is assumed
PYTHON_VENV         specifies location of python virtual environment;
                    if not set, $HOME/venv is assumed
GOROOT              specifies installation directory of go software;
                    if not set, /usr/local/go is assumed
GOPATH              specifies location of go source directory;
                    if not set, $HOME/go is assumed
C_INCLUDE_PATH      location of C include files;
                    if not set, /usr/local/include is assumed
CPLUS_INCLUDE_PATH  location of C++ include files;
                    if not set, /usr/local/include is assumed
CMAKE_LIBRARY_PATH  Location of Python shared libraries;
                    if not set, default system library location is assumed

If user environment is different from the default one (different Python installation or different location of libraries) then building from source method should be used.

Building from source

Environment variables

In some OS configurations during YDK package installation the cmake fails to find C/C++ headers for previously installed YDK libraries. In this case the header files location must be specified explicitly (in below commands the default location is shown):

  export C_INCLUDE_PATH=/usr/local/include
  export CPLUS_INCLUDE_PATH=/usr/local/include

When non-standard Python installation is used or there are multiple installations of Python on the platform, the PATH and CMAKE_LIBRARY_PATH environment variables must be set accordingly in order for the installation scripts to pick up correct Python binaries and shared libraries.

Installing third party dependencies

If user platform is supported one, it is recommended to use ydk-gen/install_ydk.sh script. The script will also install Python virtual environment in default or specified location, when '--venv' is specified.

# Clone ydk-gen from GitHub
git clone https://github.com/ygorelik/ydk-gen.git
cd ydk-gen

# Define optional environment variables and install dependencies
export YDKGEN_HOME=`pwd`  
export PYTHON_VENV=$HOME/ydk_venv
./install_ydk.sh -v   # also builds Python virtual environment

For unsupported platforms it is recommended to follow logic of ydk-gen/test/dependencies-* scripts.

Installing core components

# If created, activate YDK runtime environment
source .env

# Generate and install YDK core library
python3 generate.py -is --core --cpp

# For Python programming language add
python3 generate.py -i --core --py

# For Go programming language add
python3 generate.py -i --core --go

Adding gNMI service

In order to enable YDK support for gNMI protocol, which is optional, the user need install third party software and YDK gNMI service package.

gNMI service installation

Here is simple example how gNMI service package for Python virtual environment could be added:

cd ydk-gen
./install_ydk.sh --cpp --service gnmi -v  # requires sudo access
./install_ydk.sh --py --service gnmi -v

Runtime environment

When YDK is installed using 'install_ydk.sh' script, the runtime environment is set by running 'source .env' in bash shell. The below information is applicable only when YDK is installed manually, which is not recommended.

There is an open issue with gRPC on Centos/RHEL, which requires an extra step before running any YDK gNMI application. See this issue on GRPC GitHub for details. As a workaround, the YDK based application runtime environment must include setting of LD_LIBRARY_PATH variable:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64

Generate YDK components

All the YDK components/packages can be generated by using Python script generate.py. To get all of its options run:

cd ydk-gen
source .env
python3 generate.py --help
usage: generate.py [-h] [-l] [--core] [--service SERVICE] [--bundle BUNDLE]
                   [--adhoc-bundle-name ADHOC_BUNDLE_NAME]
                   [--adhoc-bundle ADHOC_BUNDLE [ADHOC_BUNDLE ...]]
                   [--generate-meta] [--generate-doc] [--generate-tests]
                   [--output-directory OUTPUT_DIRECTORY] [--cached-output-dir]
                   [-p] [-c] [-g] [-v] [-o]

Generate YDK artifacts:

optional arguments:
  -h, --help            show this help message and exit
  -l, --libydk          Generate libydk core package
  --core                Generate and/or install core library
  --service SERVICE     Location of service profile JSON file
  --bundle BUNDLE       Location of bundle profile JSON file
  --adhoc-bundle-name ADHOC_BUNDLE_NAME
                        Name of the adhoc bundle
  --adhoc-bundle ADHOC_BUNDLE [ADHOC_BUNDLE ...]
                        Generate an SDK from a specified list of files
  --generate-meta       Generate meta-data for Python bundle
  --generate-doc        Generate documentation
  --generate-tests      Generate tests
  --output-directory OUTPUT_DIRECTORY
                        The output directory where the sdk will get created.
  --cached-output-dir   The output directory specified with --output-directory
                        includes a cache of previously generated gen-
                        api/<language> files under a directory called 'cache'.
                        To be used to generate docs for --core
  -p, --python          Generate Python SDK
  -c, --cpp             Generate C++ SDK
  -g, --go              Generate Go SDK
  -v, --verbose         Verbose mode
  -o, --one-class-per-module
                        Generate separate modules for each python class
                        corresponding to YANG containers or lists.

The below steps specify how to use generate.py to generate YDK core, model bundle, and service packages. In all the examples the script is executed from ydk-gen directory. It is assumed that Python virtual environment is activated.

Build model bundle profile

The first step in using ydk-gen is either using one of the already built bundle profiles or constructing your own bundle profile, consisting of the YANG models you are interested to include into the bundle:

Construct a bundle profile file, such as ietf_0_1_5.json and specify its dependencies.

A sample bundle profile file is described below. The file is in a JSON format. The profile must define the "name", "version" and "description" of the bundle, and then the "core_version", which refers to the version of the ydk core package that you want to use with this bundle. The "name" of the bundle will form part of the installation path of the bundle. All other attributes, like "author" and "copyright", are optional and will not affect the bundle generation.

{
    "name":"cisco-ios-xr",
    "version": "6.5.3",
    "core_version": "0.8.6",
    "author": "Cisco",
    "copyright": "Cisco",
    "description": "Cisco IOS-XR Native Models From Git",

The "models" section of the profile describes sources of YANG models. It could contain combination of elements:

  • "dir" - list of relative directory paths containing YANG files
  • "file" - list of relative YANG file paths
  • "git" - git repository, where YANG files are located

The sample below shows the use of git sources only. Other examples can be found in profiles directory README.md.

Each "git" source must specify "url" - git repository URL, and "commits" list. The specified URL must allow the repository to be cloned without user intervention. Each element in "commits" list can specify:

  • "commitid" - optional specification of a commit ID in string format. If not specified the HEAD revision is assumed. The further specified directories and files will be copied from the context of this commit.
  • "dir" - optional list of relative directory paths within the git repository. All *.yang files in specified directory and any sub-directories will be pulled into the generated bundle.
  • "file" - optional list of relative *.yang file paths within the git repository.

Only directory examples are shown in this example.

    "models": {
        "git": [
            {
                "url": "https://github.com/YangModels/yang.git",
                "commits": [
                  {
                    "dir": [
                        "vendor/cisco/xr/653"
                    ]
                  }
                ]
            },
            {
                "url": "https://github.com/YangModels/yang.git",
                "commits": [
                  {
                    "commitid": "f6b4e2d59d4eedf31ae8b2fa3119468e4c38259c",
                    "dir": [
                        "experimental/openconfig/bgp",
                        "experimental/openconfig/policy"
                    ]
                  }
                ]
            }
        ]
    },

Generate and install model bundle

Generate model bundle using a bundle profile and install it. YDK runtime environment must be activated prior to these procedures.

For Python

If applicable, the Python virtual environment must be activated prior to these procedures

python3 generate.py --python --bundle profiles/bundles/<name-of-profile>.json -i

Check Python packages installed:

pip list | grep ydk
ydk (0.8.6.5)
ydk-models-<name-of-bundle> (0.5.1)
...

For Go

python3 generate.py --go --bundle profiles/bundles/<name-of-profile>.json -i

For C++

python3 generate.py --cpp --bundle profiles/bundles/<name-of-profile>.json -is

Writing your first app

Now, you can start creating apps based on the models in your bundle. Assuming you have generated a python bundle, the models will be available for importing in your app under ydk.models.<name-of-your-bundle>. For examples, see C++ samples. Also refer to the documentation for python, Go and C++.

Generating Documentation

In order to generate YDK core and bundles documentation, the --generate-doc option of generate.py script is used, while generating core package. Therefore the user should generate all the bundles without the --generate-doc option prior to the documentation generation. For example, the below sequence of commands will generate the documentation for the three python bundles and the python core (for C++, use --cpp; for Go, use --go).

python3 generate.py --python --bundle profiles/bundles/ietf_0_1_1.json
python3 generate.py --python --bundle profiles/bundles/openconfig_0_1_1.json
python3 generate.py --python --bundle profiles/bundles/cisco_ios_xr_6_1_1.json
python3 generate.py --python --core --generate-doc

Note. The documentation generation for bundles can take few hours due to their size. If you have previously generated documentation using the --cached-output-dir --output-directory <dir> option, the add-on documentation generation time can be reduced. Adding cisco-ios-xr documentation as an example:

mkdir gen-api/cache
mv gen-api/python gen-api/cache

python3 generate.py --python --bundle profiles/bundles/cisco_ios_xr_6_6_3.json
python3 generate.py --python --core --generate-doc --output-directory gen-api --cached-output-dir

Generating an "Adhoc" Bundle

When YANG models available on the hard drive, there is capability to generate small model bundles, which include just few models. It is called an "adhoc" bundle. Such a bundle generated without profile directly from command line. Here is simple example:

python3 generate.py -i --adhoc-bundle-name test --adhoc-bundle \
    /opt/git-repos/clean-yang/vendor/cisco/xr/621/Cisco-IOS-XR-ipv4-bgp-oper*.yang \
    /opt/git-repos/clean-yang/vendor/cisco/xr/621/Cisco-IOS-XR-types.yang
    /opt/git-repos/clean-yang/vendor/cisco/xr/621/Cisco-IOS-XR-ipv4-bgp-datatypes.yang

This will generate a bundle that contains files specified in the --adhoc-bundle option and create Python package ydk-models-test-0.1.0.tar.gz, which has dependency on the base IETF bundle. Note that all dependencies for the bundle must be listed. It is expected that this option will be typically used for generating point model bundles for specific testing. The --verbose option is automatically enabled to quickly and easily let the user see if dependencies have been satisfied.

Notes

Python requirements

Starting from release 0.8.5 YDK supports only Python3 version. At least Python 3.5 along with corresponding pip3 utility must be installed on your system. It is also required for Python installation to include corresponding shared library. As example:

  • python3.6m - /usr/lib/x86_64-linux-gnu/libpython3.6m.so

It is recommended to follow Core Installation procedure to ensure presence of shared Python libraries.

C++ requirements

In some OS configurations during YDK package installation the cmake fails to find C/C++ headers for installed YDK libraries. In this case the header location must be specified explicitly:

export C_INCLUDE_PATH=/usr/local/include
export CPLUS_INCLUDE_PATH=/usr/local/include

Directory structure

gen-api         - generated code and packages for core and bundles
                    - python (Python SDK)
                    - go (Go SDK)
                    - cpp (C++ SDK)

3d_party        - suplemental code for third party software
profiles        - profile files used during generation
sdk             - sdk core and stubs for python, go and cpp
test            - dependencies and unit test shell scripts
yang            - some YANG models used for testing
ydkgen          - python code to extend generate.py script 

create_ydk_sdk_for_github.sh - convenience script to generate language specific repositories
generate_bundles.sh          - convinience script to generate core and bundle packages for deployment

generate.py     - script used to generate SDK for YANG models
install_ydk.sh  - YDK core components installation script
requirements.txt- python dependencies used during installation
README.md       - installation and usage notes

Troubleshooting

Sometimes, developers using ydk-gen may run across errors when generating a YDK bundle using generate.py with some yang models. If there are issues with the profile file being used, such JSON formatting errors will be evident. Other times, when the problem is not so evident, it is recommended to try running the script with the [--verbose|-v] flag, which may reveal syntax problems with the YANG models being used. For example:

python3 generate.py --python --bundle profiles/bundles/ietf_0_1_1.json --verbose

Also, it may be a good idea to obtain a local copy of the YANG models and compile them using pyang to ensure the validity of the models and completeness of the model package:

cd /path/to/yang/models
pyang *.yang

Running Unit Tests

The repository includes complete set of unit tests for all supported languages. To invoke the test for specific language, simply get to 'ydk-gen' directory, activate runtime environment, if it was not done previously, and run language specific script. The script will install core and bundle packages and then perform the unit tests.

C++

```
cd ydk-gen
source .env  # if previously not run
./test/run_cpp_tests
```

Python

```
cd ydk-gen
source .env  # if previously not run
./test/run_py_tests
```

Go

```
cd ydk-gen
source .env  # if previously not run
./test/run_go_tests
```

Documentation and Support

Release Notes

The current YDK release version is 0.8.6.5.

YDK-Gen is licensed under the Apache 2.0 License.

ydk-gen's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ydk-gen's Issues

Handle choice/case

Make it obvious in documentation. Indicate which elements are mutually exclusive

Also, if the --choice-subsstatements-as-subclasses option is chosen when running ydk-gen, all the cases of a choice statement should be generated as subclasses of a parent class.

Currently, the cases of a choice statement are modeled as Properties. So, below yang produces below python code.

choice rule-type {
          description
            "This choice matches if all leafs present in the rule
             match the request.  If no leafs are present, the
             choice matches all requests.";
          case protocol-operation {
            leaf rpc-name {
              type union {
                type matchall-string-type;
                type string;
              }
              description
                "This leaf matches if it has the value '*' or if
                 its value equals the requested protocol operation
                 name.";
            }
          }
          case notification {
            leaf notification-name {
              type union {
                type matchall-string-type;
                type string;
              }
              description
                "This leaf matches if it has the value '*' or if its
                 value equals the requested notification name.";
            }
          }
          case data-node {
            leaf path {
              type node-instance-identifier;
              mandatory true;
              description
                "Data Node Instance Identifier associated with the
                 data node controlled by this rule.

                 Configuration data or state data instance
                 identifiers start with a top-level data node.  A
                 complete instance identifier is required for this
                 type of path value.

                 The special value '/' refers to all possible
                 datastore contents.";
            }
          }
        }
self.notification_name = None
self.path = None
self.rpc_name = None

If this option is chosen, below python code will be produced instead :

self.rule_type = RuleType()

class RuleType(object):
     pass

class NotificationName(RuleType):
    pass

class Path(RuleType):
    pass

class(RuleType):
    pass

RuleType is not from the choice, rather it should be from the container or list that is the parent of the choice. No class should be generated for the choice. (In the case where choice is a top level element under the module, the case classes would inherit themodule class)

Note in the case there are 2 choices under the given statement , this generation algorithm should not be used

Split ydk model API python modules into smaller source files

Background

Currently, there is a problem with the size of the individual python modules generated for some of the YANG models. For example, currently, ned.py is around 125 MB. This make it difficult for someone to read the code and makes it hard to even load the file in some editors like eclipse.

Proposal

We can make use of the fact that python allows a single module to be split into multiple source files. Then a __init__.py file can be used to unify the different source files into a single logical module. See this link to python cookbook for more details.

So, for example, this is the current organization of the ned module:

~/ydk-gen/gen-api/python/ydk/models > ls -l ned
total 177288
-rw-r--r--  1 abhirame  staff         0 Mar  4 11:49 __init__.py
drwxr-xr-x  4 abhirame  staff       136 Mar  4 11:50 _meta/
-rw-r--r--  1 abhirame  staff  90770431 Mar  4 11:50 ned.py
~/ydk-gen/gen-api/python/ydk/models > cat ned/__init__.py 
~/ydk-gen/gen-api/python/ydk/models >

Using the organization from the python cookbook, the below could be the new organization:

~/ydk-gen/gen-api/python/ydk/models > ls -l ned
total 177288
-rw-r--r--  1 abhirame  staff         0 Mar  4 11:49 __init__.py
drwxr-xr-x  4 abhirame  staff       136 Mar  4 11:50 _meta/
-rw-r--r--  1 abhirame  staff  70431 Mar  4 11:50 ned_native_service.py
-rw-r--r--  1 abhirame  staff  70431 Mar  4 11:50 ned_native_archive.py
-rw-r--r--  1 abhirame  staff  70431 Mar  4 11:50 ned_native_banner.py
...

~/ydk-gen/gen-api/python/ydk/models > cat ned/__init__.py 
from .ned_native_service import Native.Service
from .ned_native_archive import Native.Archive
from .ned_native_banner import Native.Banner
...
~/ydk-gen/gen-api/python/ydk/models >

TCP support for ncclient is not supported

When trying to connect via tcp, ydk throws an error:

Establishing connection with device 10.8.19.3:830 using tcp:
Traceback (most recent call last):
File "yang_mplste_oper.py", line 53, in
main()
File "yang_mplste_oper.py", line 39, in main
session = NetconfServiceProvider(address=o.host, port=o.port, username = o.username, password = o.password, protocol = o.proto)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/providers/netconf_provider.py", line 66, in init
self._session_config,
AttributeError: 'NetconfServiceProvider' object has no attribute ‘_session_config'

Looks like NetconfServiceProvider passes tcp to ncclient in which it is not supported.

Proposed fixed:

Disable tcp support.

SDK version shouldn't be hard coded

The version of the generated SDK shouldn't be hard coded in the SDK files. The generation profile already specifies the version number. That version should be added to the SDK package during generation.

Make use of __init__.py to organize the services and providers as a single ydk module from external standpoint

Background

Currently, the providers and services API's are organized into separate modules. So, anyone writing an app has to write multiple import statements

from ydk.services import CRUDService
from ydk.providers import NetconfServiceProvider

In the future if we add more services and providers, the app would have to import the new classes from separate modules as well.

Proposal

We can make use of the fact that python allows a single module to be split into multiple source files. Then a __init__.py file can be used to unify the different source files into a single logical module. See this link to python cookbook for more details.

By adding an __init__.py to the ydk directory and loading all the services and providers module in it, we can unify the whole of ydk into a single logical module.

ydk/__init__.py will look something like the below:

from .providers import NetconfServiceProvider
from .services import CRUDService, ExecutorService

So, in an app, all the classes needed for that app can be imported with a single statement:

from ydk import CRUDService, NetconfServiceProvider, Empty

AttributeError: 'lxml.etree._Element' object has no attribute ‘isidigit'

Seeing the following error:

File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/services/crud_service.py", line 129, in read
return self.create_read_return_value(payload, read_filter)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/services/crud_service.py", line 162, in create_read_return_value
DmTree._bind_to_object(payload, entity)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 157, in _bind_to_object
DmTree._bind_to_object_helper(curr_rt, entity, pretty_p='|-')
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 197, in _bind_to_object_helper
DmTree._bind_to_object_helper(rtchild, child, pretty_p+'-l')
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 185, in _bind_to_object_helper
DmTree._bind_to_object_helper(rt[0], instance, pretty_p+'-|')
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 185, in _bind_to_object_helper
DmTree._bind_to_object_helper(rt[0], instance, pretty_p+'-|')
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 268, in _bind_to_object_helper
entity.dict[member.presentation_name] = DmTree._to_real_union_type_helper(rt, member,entity)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/_core/_dmtree.py", line 93, in _to_real_union_type_helper
elif contained_member.ptype == 'int' and rt[0].text is not None and rt[0].isidigit():
AttributeError: 'lxml.etree._Element' object has no attribute ‘isidigit'

Proposed fix:

rt[0].isdigit() should be rt[0].text.isdigit

Generate model exercising test generator

This task will generate a unit test that creates an object graph that is part of a payload of an RPC, serializes the RPC then deserailizes the RPC and extracts the object graph. The object graphs are then compared to ensure that they are the same.

Note this tests

  1. The encoder/decoder logic
  2. Exercises all the generated api making sure that it confirms to python syntax

Add timeout parameter for NetconfServiceProvider, to be passed down to ncclient

When retriving data from a model with a lot of oper data, ncclient will timeout causing TimeoutExpiredError to be thrown.

Establishing connection with device 10.8.19.1:830 using ssh:
Connection established
Traceback (most recent call last):
File "yang_mplste_oper.py", line 53, in
main()
File "yang_mplste_oper.py", line 44, in main
crud_service.read(session, mpls_te)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/services/crud_service.py", line 127, in read
payload = self.execute_crud_operation_on_provider(provider, read_filter, 'READ', only_config)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/services/crud_service.py", line 140, in execute_crud_operation_on_provider
only_config
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/services/service.py", line 31, in execute_payload
payload)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ydk/providers/_provider_plugin.py", line 330, in execute_operation
reply_str = sp_rpc._request(payload)
File "/home/gregorbr/yang_test/local/lib/python2.7/site-packages/ncclient/operations/rpc.py", line 342, in _request
raise TimeoutExpiredError('ncclient timed out while waiting for an rpc reply.')
ncclient.operations.errors.TimeoutExpiredError: ncclient timed out while waiting for an rpc reply.

Proposed fix:

session = NetconfServiceProvider(timeout=300,....) #add new argument to set timeout

YDK bundles

Background

Currently, YDK covers all the YANG model APIs, including openconfig, IETF, Cisco etc under the same python package and this is published as the ydk-py python package. The main reason why bundles are needed is that there needs to be a way to separately generate python packages for YDK model APIs for various YANG module repositories like openconfig, IETF, Cisco XR, Cisco XE etc. This will have more real world uses.

Bundling is meant as a extension of and replacement to the profile mechanism, which ydk-gen currently makes use of. Currently, ydk-gen generates a set of model APIs based on a profile and combined with the runtime YDK components (providers & services), creates a pip installable package (model APIs + YDK providers & services) called ydk-py.

Proposal

With the introduction of bundles, ydk-gen will no longer generate a pip package called ydk-py. Instead the new pip package will only contain the model API's for a given set of YANG models which are specified in the bundle JSON file. For example, ydk-gen can generate a pip installable package (model APIs) called ietf-bundle.

Currently, ydk-gen operates in the below way:

  1. Parse profile JSON
  2. Download yang models
  3. Using pyang, parse YANG files and return modules which are trees of Statement python objects
  4. Generate expanded YDK API model Packages from the above Statement trees
  5. Pass the ydk Packages to the PythonModulePrinter to print the YDK model API files

Package structure looks like below:

ydk-py
 |
 |-ydk
    |-models
       |
       |- ietf_interface
       | - ietf_system
    |-providers
    |-services
 |-samples

By introducing the concept of bundling, the flow will be like the below:

Resolve bundle (This sub-task is tracked in #26)

  1. Parse bundle JSON
  2. Download yang models (e.g. openconfig yang models) specified in the bundle plus any dependent YANG models from a different bundle (e.g. IETF yang models) which can also be specified in the main bundle

Build API model for bundle(s)
3. Using pyang, parse YANG files and return modules which are trees of Statement python objects
4. Generate expanded YDK API model Packages from the above Statement trees. Here, each bundle may utilize different code generation strategies, depending on the YANG model structure of the models in the bundle. For example, bundles can make use of choice statement inheritance or collapse containers with single list
5. Assign the list of YDK Packages as children of Bundle objects (by making use of the bundle JSON file)

Print model APIs for bundle(s)
6. Pass the ydk Bundles to the YDK Printer to print the YDK model API files for each bundle and generate the pip installable bundle (can be a python package and in the future, can be C++ library/ Ruby gem)

Package structure will look like below:

ietf-bundle
 |
 |-models
    |
    |- ietf_interface
    | - ietf_system
 |-samples

Using the groupings-as-class option for ydk-gen fails for 0.4.0 profile

~/ydk-gen > python ./generate.py -p --no-doc --profile profiles/ydk/ydk_0_4_0.json --verbose --groupings-as-class

Traceback (most recent call last):
File "./generate.py", line 151, in
options.groupings_as_class)
File "/ydkgen/init.py", line 316, in generate
modules)
File "/ydkgen/api_model.py", line 1279, in generate_grouping_class_api_model
create_grouping_class_api_model(m, p)
File "/ydkgen/api_model.py", line 1189, in create_grouping_class_api_model
stmt.i_groupings[grouping_name], element)
File "/ydkgen/api_model.py", line 1260, in create_grouping_class_api_model
create_grouping_class_api_model(child_stmt, element)
File "/ydkgen/api_model.py", line 1260, in create_grouping_class_api_model
create_grouping_class_api_model(child_stmt, element)
File "/ydkgen/api_model.py", line 1223, in create_grouping_class_api_model
enum_type = _get_enum_type_stmt(stmt)
File "/ydkgen/api_model.py", line 766, in
_get_enum_type_stmt = lambda stmt: _get_type_stmt(stmt, EnumerationTypeSpec)
File "/ydkgen/api_model.py", line 753, in _get_type_stmt
type_stmt = type_spec.i_target_node.search_one('type')
AttributeError: 'PathTypeSpec' object has no attribute 'i_target_node'

Code generation service

This enables users to handle below use cases as part of YDK app maker (YAM)

  • Have netconf, restconf payload and want to migrate to YDK based app with minimal effort via tool.
  • Looking for an educational channel on how to use YDK objects.
  • Yang data model payload transcoding (convert XML to YDK to JSON to YDK python to .... or vice versa)
class CodeGenerationServiceProvider(object):
    """ Code generation provider"""
   def __init__(self, type):
        '''type is 'xml' or 'json' '''

class CodeGenerationService(object):
    """ Code generation service to generate code from XML/JSON payload """
   def __init__(self):
        pass
    def generate(self, provider, payload):
        '''This will return a python script snippet which can be used in a YDK app'''

It is preferable to continue using the existing service/provider abstraction

CRUD should support top entity like function

Currently CRUD operations work on a single entity. This does not allow a single create operation to work on multiple entities that do not have a common parent. By introducing a Top entity which represents that datastore (for lack of better term), we can allow these operations to happen

Proposal is to create a class per module (for those modules/packages that have classes/enums defined in them).
These module classes will have properties for the top level elements for example the oc-bgp module will have a top level class Module this class will have one property which is hte top level Bgp class that corresponds to the container bgp.

Note this class must be at the same level as the Bgp classes and should appear after the top level classes have been defined. Further more this class should not have any nested type definitions

Example 👎

module A {

container A {
..
}
leaf b;

list C {
...
}
}

will produce

class A(object): --> container A

class C(object): --> list C

Now the new module class

class AModule(object):
def init(self):
self.a = A()
self.b = None --> for leaf B
self.c = YList() ---> list of C's

Investigate publishing ydk-gen as an executable on pip

As suggested by Santiago:

  • Pip will automatically take care of the dependencies
  • Also, we can potentially create man pages with documentation
  • This way, we don't need install.sh to install the dependencies/create virtual env

Investigate removing doctrings from python model APIs

From Lily's suggestion:

Every python class is getting a class docstring written into it. I was wondering if this is still necessary since the exact same docstring is already getting written into each file’s respective documentation file?

Support for DELETE obejct on list level

Background

Currently the DELETE object supports deletion on leaf and container level for CRUD update operation, which provide user with flexibility to delete and create attribute at the same time. However we lack the support for using DELETE object on list.

Proposal

We could add an attribute or property _delete on python class created from list statement, and with this attribute being set as DELETE object, we could provide user the capability to delete list element with specified list key. For example,

    bgp_cfg = bgp.Bgp()

    bgp_cfg.global_.config.as_ = 65001

    ipv4_afsf = bgp_cfg.global_.afi_safis.AfiSafi()
    ipv4_afsf.afi_safi_name = 'ipv4-unicast'
    ipv4_afsf.config.afi_safi_name = 'ipv4-unicast'
    ipv4_afsf.config.enabled = True

    ipv6_afsf = bgp_cfg.global_.afi_safis.AfiSafi()
    ipv6_afsf.afi_safi_name = 'ipv6-unicast'
    ipv6_afsf.config.afi_safi_name = 'ipv6-unicast'
    ipv6_afsf.config.enabled = True
    ipv6_afsf.delete = DELETE()

    bgp_cfg.global_.afi_safis.afi_safi.append(ipv4_afsf)
    bgp_cfg.global_.afi_safis.afi_safi.append(ipv6_afsf)

CRUD update operation on bgp_cfg will delete this ipv6 afsf and create ipv4 afsf.

Investigate travis environment variables.

Currently the url for github repo used in .travis.yml is hardcoded, so we need to change the pointer to master repo after each merge, and before each commit for forked repo, which is inconvenient. There might be a better way to handle it.

Come up with solution for large model files in ydk-py

Currently, git push to the github repository gives the below warning:

remote: warning: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: warning: See http://git.io/iEPt8g for more information.
remote: warning: File ydk/models/infra/Cisco_IOS_XR_infra_policymgr_oper.py is 78.48 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB
remote: warning: File ydk/models/ipv4/Cisco_IOS_XR_ipv4_bgp_oper.py is 74.33 MB; this is larger than GitHub's recommended maximum file size of 50.00 MB

Need to come up with a solution to fix these warnings

Come up with workflow to upgrade YDK to newer version

If someone already has YDK installed and want to upgrade to a newer version of YDK, need to come up with some workflow in the README or some other document. Once this is available on 'pip install', may be this can happen little more smoothly

For errors reported directly by server (not caught by validation), rpc-reply error-tag, error-info should be reported

I ran across an issue where the yang models were incorrectly loaded/wrong version of yang models were loaded to the server. So, when I ran the sanity tests, the validation mechanism did not catch the error since the payload was valid. However, the server rejected them because of mismatched model. For this case, I think the error-tag and error-info from the RPC error can be captured in the exception raised, so that the user has a clear idea why the server rejected the request.

List keys should be documented

In the documentation for the corresponding python class, need to specify which of the leafs for a yang list are keys.

For example, for the XR native ISIS model, in the class Isis > Instances > Instance, the instance_name should be identified as the key

Claiming py3 support in setup.py

We have left boilerplate code in the setup.py that we generate for a ydk-py that has:

        # Specify the Python versions you support here. In particular, ensure
        # that you indicate whether you support Python 2, Python 3 or both.
        #'Programming Language :: Python :: 2',
        #'Programming Language :: Python :: 2.6',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.2',
        'Programming Language :: Python :: 3.3',
        'Programming Language :: Python :: 3.4',

The version requirement we have for ncclient is not a py3-compatible version, and I don;t think we have ever tested with py3, so I think we should take out the 3, 3.2, 3.3 and 3.4.

Presence classes and read

Consider the following model

container A {
    container B {
      presence "Active";

     list C {

     }
} 

When creating the above entity tree using the api

a = A()

The reference to B is set to None. In other words references to presence classes are set to None and have to explicitly initialized by the user. This is necessary in Create/Update

However when used as a filter in read we need to make sure that this read ?

Support for config replace

We need to provide support for config replace capability (both global and per-model). The current CRUDService abstraction will not meet the operational requirements of customers. The requirement for config replacement is expected to increase in popularity and the lack of this functionality will hinder adoption by customers.

Per-model config replace: Given a list of config objects, replace the config of that specific list of models with the provided objects.

Global config replace: Given a list of config objects, replace the entire config of the device with the config data in those models. Any other models/objects not provided as input will be deleted.

Improve performance of documentation generation

From Xiaoqin:

I read from this style guide: https://google.github.io/styleguide/pyguide.html#Strings
Avoid using the + and += operators to accumulate a string within a loop. Since strings are immutable, this creates unnecessary temporary objects and results in quadratic rather than linear running time. Instead, add each substring to a list and ''.join the list after the loop terminates (or, write each substring to a io.BytesIO buffer).

Yes: items = ['<table>']
     for last_name, first_name in employee_list:
         items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
     items.append('</table>')
     employee_table = ''.join(items)
No: employee_table = '<table>'
    for last_name, first_name in employee_list:
        employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
    employee_table += '</table>'

By changing += and + operators to list join, it might increase the speed for documentation.

Implement collapse containers with single list code generation strategy

Consider the following pattern in yang

              list bundle {
                .....
                container modules {
                        list module {
                        ...

In the expanded API generations strategy the source file will be encoded as follows

              class Bundle(object): --> corresponds to the list bundle
                def __init__(self):
                    self.modules = Modules() 

                class Modules(object): --> corresponds to the container modules
                    def __init__(self):
                        self.module = YList() --> corresponds to the list Module

                    class Module(object): --> corresponds to the list module     

If this leaf is set the generated api will skip the container modules and have the class Module as a child of the Bundle class like this

                   class Bundle(object):  ---> corresponds to the list bundle
                      def __init__(self):
                         self.modules = YList() --> corresponds to the list Module

                      class Module(obect):
                           ...

too many dependencies for generated ydk-py?

I noticed that these dependencies are in the requirements.txt for ydk-py:

Sphinx==1.4a1
sphinx-rtd-theme==0.1.9
XlsxWriter==0.7.3
GitPython==1.0.1

These seem like ydk-gen dependencies that should be removed?

ncclient exception

When running a series of YDK-Py apps, you may get the following thread exception:

Exception in thread session (most likely raised during interpreter shutdown):
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
File "/usr/local/lib/python2.7/dist-packages/ncclient/transport/ssh.py", line 554, in run <type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'debug'

May be related to ncclient:

Exception in thread session (most likely raised during interpreter shutdown):
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
File "/home/gitlab-runner/builds/77b7020d/0/ydk-dev/ydk-gen/myenv/local/lib/python2.7/site-packages/ncclient/transport/ssh.py", line 556, in run
File "/home/gitlab-runner/builds/77b7020d/0/ydk-dev/ydk-gen/myenv/local/lib/python2.7/site-packages/ncclient/transport/ssh.py", line 301, in close
File "/home/gitlab-runner/builds/77b7020d/0/ydk-dev/ydk-gen/myenv/local/lib/python2.7/site-packages/paramiko/transport.py", line 644, in close
File "/home/gitlab-runner/builds/77b7020d/0/ydk-dev/ydk-gen/myenv/local/lib/python2.7/site-packages/paramiko/transport.py", line 1536, in stop_thread
File "/usr/lib/python2.7/threading.py", line 960, in join
File "/usr/lib/python2.7/threading.py", line 363, in wait
<type 'exceptions.ValueError'>: list.remove(x): x not in list

Come up with scripts to do ydk-gen/ydk-py install in one shot

Currently, there are a lot of steps which need to be manually carried out to install ydk-gen or ydk-py. There needs to be a simple bash script which takes care of everything (including installing dependencies and setting up environment). This will make it easy for users to install and use ydk-gen and/or ydk-py

Address feedback from ydk user

Need to look into these issues:

  • the examples are great and for the run-of-the-mill code, the ramping up is good
  • For something more fancy, I.e., identity refs, things are more intricate and not very intuitive in the example I looked at
  • Debugging can be challenging. E.g., the encoder was complaining that “name” could not be encoded. (I had three “name” at different levels of the hierarchy, so I had to dig which of them was the reported one)
    • To debug, I had to modify the generated code to get extra info
    • The leaf is a yang string. The value I had was unicode in python (I had read it from a file in json) and the encoder was checking for str
    • Exceptions like 'invalid hierarchy' need a bit more detailed description on how to fix these
  • Constructors that take values for leaves may be useful

Support bundle file read and resolve YDK

This issue tracks reading bundle files written in json and resolving all the models and bundles that are referenced in these files as per the ydk model in the metaservice branch

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.