From c3763b461bc4b7420712d1caae7a62583a3e586f Mon Sep 17 00:00:00 2001 From: Abhi Keshav Date: Mon, 19 Mar 2018 12:10:42 -0700 Subject: [PATCH 1/2] Support for yang actions in path API * Add support for parsing yang 1.1 module capabilities * Tests, docs --- README.md | 29 +- generate.py | 21 +- sdk/README.md | 15 +- .../core/docsgen/api/path/nodes/data_node.rst | 24 + .../api/path/sessions/netconf_session.rst | 7 + .../docsgen/api/path/sessions/session.rst | 2 +- sdk/cpp/core/src/common_utilities.cpp | 24 + sdk/cpp/core/src/common_utilities.hpp | 4 +- sdk/cpp/core/src/ietf_parser.cpp | 152 +++++- sdk/cpp/core/src/ietf_parser.hpp | 1 + sdk/cpp/core/src/path/capability.cpp | 12 + sdk/cpp/core/src/path/data_node.cpp | 51 ++ .../core/src/path/netconf_model_provider.cpp | 1 + sdk/cpp/core/src/path/netconf_session.cpp | 152 ++++-- sdk/cpp/core/src/path/path.cpp | 12 +- sdk/cpp/core/src/path/path_private.hpp | 8 + sdk/cpp/core/src/path/restconf_session.cpp | 23 +- sdk/cpp/core/src/path/root_data_node.cpp | 2 +- sdk/cpp/core/src/path_api.hpp | 41 +- sdk/cpp/core/tests/CMakeLists.txt | 7 +- sdk/cpp/core/tests/confd/ydktest/Makefile | 5 +- sdk/cpp/core/tests/mock_data.hpp | 15 +- sdk/cpp/core/tests/models/.gitignore | 4 + .../tests/models/ydktest-sanity-action.yang | 56 +++ .../core/tests/test_capabilities_parser.cpp | 437 ++++++++++++++++++ sdk/cpp/core/tests/test_codec.cpp | 46 ++ sdk/cpp/tests/CMakeLists.txt | 2 +- ...est_core_netconf.cpp => test_path_api.cpp} | 45 +- sdk/cpp/tests/test_restconf_provider.cpp | 13 + .../core/docsgen/api/path/data_node.rst | 21 +- .../core/docsgen/api/path/netconf_session.rst | 8 + sdk/python/core/python.cpp | 13 +- sdk/python/core/tests/test_sanity_path.py | 22 +- test/test_package_centos.sh | 1 - test/test_package_ubuntu.sh | 1 - test/tests.sh | 20 +- 36 files changed, 1196 insertions(+), 101 deletions(-) create mode 100644 sdk/cpp/core/tests/models/.gitignore create mode 100644 sdk/cpp/core/tests/models/ydktest-sanity-action.yang rename sdk/cpp/tests/{test_core_netconf.cpp => test_path_api.cpp} (94%) diff --git a/README.md b/README.md index f053a7180..d0c43ad9e 100644 --- a/README.md +++ b/README.md @@ -78,13 +78,26 @@ Centos (Fedora-based): **Install prebuilt libydk binary:** ``` - $ sudo yum install epel-release libssh-devel gcc-c++ + $ sudo yum install epel-release + $ sudo yum install libssh-devel gcc-c++ $ sudo yum install https://devhub.cisco.com/artifactory/rpm-ydk/0.7.0/libydk-0.7.0-1.x86_64.rpm + + # Upgrade compiler to gcc 5.* + $ yum install centos-release-scl -y > /dev/null + $ yum install devtoolset-4-gcc* -y > /dev/null + $ ln -sf /opt/rh/devtoolset-4/root/usr/bin/gcc /usr/bin/cc + $ ln -sf /opt/rh/devtoolset-4/root/usr/bin/g++ /usr/bin/c++ ``` **To build from source:** ``` -$ sudo yum install epel-release -$ sudo yum install libxml2-devel libxslt-devel libssh-devel libtool gcc-c++ pcre-devel cmake3 clang libcurl-devel rpm-build redhat-lsb + $ sudo yum install epel-release + $ sudo yum install libxml2-devel libxslt-devel libssh-devel libtool gcc-c++ pcre-devel cmake3 clang libcurl-devel rpm-build redhat-lsb + + # Upgrade compiler to gcc 5.* + $ yum install centos-release-scl -y > /dev/null + $ yum install devtoolset-4-gcc* -y > /dev/null + $ ln -sf /opt/rh/devtoolset-4/root/usr/bin/gcc /usr/bin/cc + $ ln -sf /opt/rh/devtoolset-4/root/usr/bin/g++ /usr/bin/c++ ``` ## macOS @@ -149,6 +162,7 @@ Options: --version show program's version number and exit -h, --help show this help message and exit -p, --python Select Python language. This is currently the default option + -l, --libydk Generate the libydk core package (required for using YDK Python, Go, C++) -c, --cpp Select C++ language -g, --go Select Go language --core Generate the core for the selected language @@ -230,12 +244,15 @@ First, generate the core and install it: First generate and install ``libydk`` (**required for C++, Go or Python**): ``` -$ ./generate.py --cpp --core +$ ./generate.py --libydk $ cd gen-api/cpp/ydk/build -$ make && sudo make install +$ make -# To create the ``libydk`` binary package to use for later installation, run the below command after the above +# To create the libydk binary package to use for later installation, run the below command $ make package + +# To install the compiled libydk code, run the below command after the above +$ [sudo] make install ``` For python: diff --git a/generate.py b/generate.py index b6dd49c5a..c5181053d 100755 --- a/generate.py +++ b/generate.py @@ -233,7 +233,7 @@ def generate_adhoc_bundle(adhoc_bundle_name, adhoc_bundle_files): return adhoc_bundle_file.name -def preconfigure_generated_cpp_code(output_directory): +def preconfigure_generated_cpp_code(output_directory, generate_libydk): cpp_sdk_root = os.path.join(output_directory) cmake_build_dir = os.path.join(output_directory, 'build') if os.path.exists(cmake_build_dir): @@ -250,7 +250,10 @@ def preconfigure_generated_cpp_code(output_directory): except subprocess.CalledProcessError as e: print('\nERROR: Failed to configure build!\n') sys.exit(e.returncode) - print('\nSuccessfully generated code at {0}.\nTo build and install, run "make && [sudo] make install" from {1}'.format(output_directory, cmake_build_dir)) + make_command = 'To build and install, run "make && [sudo] make install" from {0}'.format(cmake_build_dir) + if generate_libydk: + make_command = make_command+'\n\nTo build the libydk package, run "make && make package" from {0}'.format(cmake_build_dir) + print('\nSuccessfully generated code at {0}.\n\n{1}'.format(output_directory, make_command)) print('\n=================================================') print('Successfully generated C++ YDK at %s' % (cpp_sdk_root,)) print('Please refer to the README for information on how to use YDK\n') @@ -316,6 +319,12 @@ def _get_time_taken(start_time): default=False, help="Generate Go SDK") + parser.add_argument( + "-l", "--libydk", + action="store_true", + default=False, + help="Generate libydk core package") + parser.add_argument( "-v", "--verbose", action="store_true", @@ -366,7 +375,11 @@ def _get_time_taken(start_time): output_directory = options.output_directory language = '' - if options.cpp: + if options.libydk: + options.cpp = True + options.core = True + language = 'cpp' + elif options.cpp: language = 'cpp' elif options.go: language = 'go' @@ -426,7 +439,7 @@ def _get_time_taken(start_time): print('\nCreating {0} package...\n'.format(language)) if options.cpp: - preconfigure_generated_cpp_code(output_directory) + preconfigure_generated_cpp_code(output_directory, options.libydk) elif options.go: # todo: implement go packaging with the output_directory pass diff --git a/sdk/README.md b/sdk/README.md index 970d9b6ff..f8ecf1555 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -7,14 +7,27 @@ sdk ├── _docsgen_common # Common documents for C++, Python, Go ├── cpp │   ├── core # C++ core +│   | ├── docsgen # Core documentation +│   | ├-- src # Source code +│   | ├-- samples # Samples +│   | └── tests # Core tests +│   ├── install │   ├── packages │   ├── samples -│   └── tests +│   └── tests # C++ bundle tests ├── go │   ├── core # Go core +│   | ├── docsgen # Core documentation +│   | ├-- samples # Samples +│   | ├-- tests # Core & bundle tests +│   | └── ydk # Source code │   └── packages └── python ├── core # Python core +│   | ├── docsgen # Core documentation +│   | ├-- samples # Samples +│   | ├-- tests # Core & bundle tests +│   | └── ydk # Source code └── packages ``` diff --git a/sdk/cpp/core/docsgen/api/path/nodes/data_node.rst b/sdk/cpp/core/docsgen/api/path/nodes/data_node.rst index b882eda92..0ead68093 100644 --- a/sdk/cpp/core/docsgen/api/path/nodes/data_node.rst +++ b/sdk/cpp/core/docsgen/api/path/nodes/data_node.rst @@ -67,6 +67,20 @@ DataNode :raises: :cpp:class:`YInvalidArgumentError` In case the argument is invalid. :raises: :cpp:class:`YPathError` In case the path is invalid. + .. cpp:function:: virtual DataNode& create_action(const std::string& path) + + Creates a :cpp:class:`DataNode` representing a YANG 1.1 action corresponding to the path and set its value. + + This methods creates a :cpp:class:`DataNode` tree based on the path passed in. The path expression must identify a single node. + + The returned :cpp:class:`DataNode` is the last node created (the terminal part of the path). + + :param path: The XPath expression identifying the node. + :param value: The string representation of the value to set. + :return: Pointer to :cpp:class:`DataNode` created. + :raises: :cpp:class:`YInvalidArgumentError` In case the argument is invalid. + :raises: :cpp:class:`YPathError` In case the path is invalid. + .. cpp:function:: virtual void set_value(const std::string& value) Set the value of this :cpp:class:`DataNode`. @@ -104,6 +118,16 @@ DataNode :return: Pointer to the root :cpp:class:`DataNode` of this tree. + .. cpp:function:: virtual std::shared_ptr operator()(const Session& session) + + Execute/Invoke the action contained in the DataNode through the given Session and return the output of the action as a DataNode. + + :param session: The Session. + :return: Pointer to the :cpp:class:`DataNode` or ``nullptr`` if none exists. + + .. cpp:function:: virtual bool has_action() const + + :return: `true` if the DataNode contains any action node created by invoking ``create_action``. .. cpp:function:: virtual void add_annotation(const Annotation& an) diff --git a/sdk/cpp/core/docsgen/api/path/sessions/netconf_session.rst b/sdk/cpp/core/docsgen/api/path/sessions/netconf_session.rst index bb82ccb8a..aa5dbe773 100644 --- a/sdk/cpp/core/docsgen/api/path/sessions/netconf_session.rst +++ b/sdk/cpp/core/docsgen/api/path/sessions/netconf_session.rst @@ -103,6 +103,13 @@ NetconfSession :param rpc: Reference to the :cpp:class:`Rpc` node. :return: Shared pointer to the :cpp:class:`DataNode` representing the output. + .. cpp:function:: virtual std::shared_ptr invoke(path::DataNode& datanode) const + + Invokes or executes the given DataNode containing a YANG 1.1 action and returns a :cpp:class:`DataNode` pointer if the action has an output modeled in YANG. + + :param datanode: Reference to the :cpp:class:`DataNode` node. + :return: Pointer to the :cpp:class:`DataNode` representing the output. + .. cpp:function:: std::vector get_capabilities() const Returns a vector of the client's capabilities diff --git a/sdk/cpp/core/docsgen/api/path/sessions/session.rst b/sdk/cpp/core/docsgen/api/path/sessions/session.rst index afd41376a..5b640d5e7 100644 --- a/sdk/cpp/core/docsgen/api/path/sessions/session.rst +++ b/sdk/cpp/core/docsgen/api/path/sessions/session.rst @@ -12,7 +12,7 @@ Session :return: Reference to the :cpp:class:`RootSchemaNode` or ``nullptr`` if one could not be created. - .. cpp:function:: virtual std::shared_ptr invoke(Rpc& rpc) const + .. cpp:function:: virtual std::shared_ptr invoke(path::Rpc& rpc) const Invokes or executes the given rpc and returns a :cpp:class:`DataNode` pointer if the Rpc has an output modeled in YANG. diff --git a/sdk/cpp/core/src/common_utilities.cpp b/sdk/cpp/core/src/common_utilities.cpp index 8be0ae45f..697f9f45a 100644 --- a/sdk/cpp/core/src/common_utilities.cpp +++ b/sdk/cpp/core/src/common_utilities.cpp @@ -21,6 +21,8 @@ // ////////////////////////////////////////////////////////////////// +#include + #include "common_utilities.hpp" #include "entity_data_node_walker.hpp" #include "xml_subtree_codec.hpp" @@ -142,4 +144,26 @@ string get_xml_subtree_filter_payload(Entity & entity, const ServiceProvider & p YLOG_DEBUG("Encoding the subtree filter request using XML subtree codec"); return xml_subtree_codec.encode(entity, provider.get_session().get_root_schema()); } + +vector get_union(vector & v1, vector & v2) +{ + set all; + YLOG_DEBUG("Performing union of vectors with {} & {} elements", v1.size(), v2.size()); + for(auto& s: v1) + { + all.insert(s); + } + for(auto& s: v2) + { + all.insert(s); + } + YLOG_DEBUG("Union contains {} elements", all.size()); + vector v; + for(auto& s: all) + { + YLOG_DEBUG("Adding unioned element: {}", s); + v.push_back(s); + } + return v; +} } diff --git a/sdk/cpp/core/src/common_utilities.hpp b/sdk/cpp/core/src/common_utilities.hpp index 0ed7fb54e..e9f6942cd 100644 --- a/sdk/cpp/core/src/common_utilities.hpp +++ b/sdk/cpp/core/src/common_utilities.hpp @@ -24,8 +24,6 @@ #ifndef YDK_UTILITIES #define YDK_UTILITIES -#include -#include #include #include @@ -45,6 +43,8 @@ namespace ydk std::shared_ptr read_datanode(Entity & filter, std::shared_ptr read_data_node); std::string get_data_payload(Entity & entity, const ServiceProvider & provider); std::string get_xml_subtree_filter_payload(Entity & entity, const ServiceProvider & provider); + + std::vector get_union(std::vector & v1, std::vector & v2); } #endif /* YDK_UTILITIES */ diff --git a/sdk/cpp/core/src/ietf_parser.cpp b/sdk/cpp/core/src/ietf_parser.cpp index 941685025..8d0eaa487 100644 --- a/sdk/cpp/core/src/ietf_parser.cpp +++ b/sdk/cpp/core/src/ietf_parser.cpp @@ -18,9 +18,10 @@ #include #include "ietf_parser.hpp" +#include "logger.hpp" #include "path_api.hpp" +#include "xml_util.hpp" #include "ydk_yang.hpp" -#include "logger.hpp" using namespace std; @@ -33,12 +34,42 @@ static xmlNodePtr drop_hello_tag(xmlNodePtr root) auto cur = root->xmlChildrenNode; while (cur != NULL) { - if ((!xmlStrcmp(cur->name, (const xmlChar *) "capabilities"))) + if ((!xmlStrcmp(cur->name, to_xmlchar("capabilities")))) return cur; cur = cur->next; } return root; } + +static xmlNodePtr check_and_return_root(xmlDocPtr doc) +{ + if (doc == NULL) + { + YLOG_INFO("Empty capabilities"); + return NULL; + } + xmlNodePtr cur = xmlDocGetRootElement(doc); + if (cur == NULL) + { + YLOG_INFO("Empty capabilities"); + return NULL; + } + return cur; +} + +static string get_xml_node_content(xmlNodePtr n) +{ + string c; + xmlChar * buffer = xmlNodeGetContent(n); + if(buffer != NULL) + { + ostringstream os; + os << buffer; + xmlFree(buffer); + c = os.str(); + } + return c; +} ////////////////////////////////////////// //// IetfCapabilitiesXmlParser ////////////////////////////////////////// @@ -55,27 +86,124 @@ IetfCapabilitiesXmlParser::~IetfCapabilitiesXmlParser() } } -vector IetfCapabilitiesXmlParser::parse(const string & capabilities_buffer) +vector IetfCapabilitiesXmlParser::parse_yang_1_1(const std::string & buffer) { - doc = xmlReadMemory(capabilities_buffer.c_str(), capabilities_buffer.size(), "noname.xml", NULL, 0); - vector capabilities{}; - if (doc == NULL) + + doc = xmlReadMemory(buffer.c_str(), buffer.size(), "noname.xml", NULL, 0); + xmlNodePtr cur = check_and_return_root(doc); + if(cur == NULL) { - YLOG_INFO("Empty capabilities"); return {}; } - xmlNodePtr cur = xmlDocGetRootElement(doc); - if (cur == NULL) + + if (xmlStrcmp(cur->name, to_xmlchar("rpc-reply")) != 0) + { + YLOG_INFO("Unexpected XML"); + return {}; + } + cur = cur->xmlChildrenNode; + while(cur != NULL) + { + if (xmlStrcmp(cur->name, to_xmlchar("data")) == 0) + { + xmlNodePtr c = cur->xmlChildrenNode; + while(c != NULL) + { + if (xmlStrcmp(c->name, to_xmlchar("modules-state")) == 0) + { + xmlNodePtr c1 = c->xmlChildrenNode; + while(c1 != NULL) + { + if (xmlStrcmp(c1->name, to_xmlchar("module")) == 0) + { + string module_name, revision, name_space; + vector features; + vector deviations; + + xmlNodePtr c2 = c1->xmlChildrenNode; + while(c2 != NULL) + { + if (xmlStrcmp(c2->name, to_xmlchar("name")) == 0) + { + module_name = get_xml_node_content(c2); + } + else if (xmlStrcmp(c2->name, to_xmlchar("revision")) == 0) + { + revision = get_xml_node_content(c2); + } + else if (xmlStrcmp(c2->name, to_xmlchar("namespace")) == 0) + { + name_space = get_xml_node_content(c2); + } + else if (xmlStrcmp(c2->name, to_xmlchar("feature")) == 0) + { + features.push_back(get_xml_node_content(c2)); + } + else if (xmlStrcmp(c2->name, to_xmlchar("deviation")) == 0) + { + xmlNodePtr c3 = c2->xmlChildrenNode; + while(c3 != NULL) + { + if (xmlStrcmp(c3->name, to_xmlchar("name")) == 0) + { + deviations.push_back(get_xml_node_content(c3)); + } + c3 = c3->next; + } + } + c2 = c2->next; + } + ostringstream capability; + capability<next; + } + } + c = c->next; + } + } + cur = cur->next; + } + return capabilities; +} + +vector IetfCapabilitiesXmlParser::parse(const string & capabilities_buffer) +{ + vector capabilities{}; + + doc = xmlReadMemory(capabilities_buffer.c_str(), capabilities_buffer.size(), "noname.xml", NULL, 0); + xmlNodePtr cur = check_and_return_root(doc); + if(cur == NULL) { - YLOG_INFO("Empty capabilities"); return {}; } // drop hello cur = drop_hello_tag(cur); - if (xmlStrcmp(cur->name, (const xmlChar *) "capabilities") != 0) + if (xmlStrcmp(cur->name, to_xmlchar("capabilities")) != 0) { YLOG_INFO("Unexpected XML"); return {}; @@ -83,7 +211,7 @@ vector IetfCapabilitiesXmlParser::parse(const string & capabilities_buff cur = cur->xmlChildrenNode; while(cur != NULL) { - if (xmlStrcmp(cur->name, (const xmlChar *) "capability") == 0) + if (xmlStrcmp(cur->name, to_xmlchar("capability")) == 0) { xmlChar * capability_buffer = xmlNodeGetContent(cur); if(capability_buffer != NULL) diff --git a/sdk/cpp/core/src/ietf_parser.hpp b/sdk/cpp/core/src/ietf_parser.hpp index 7efe95a33..363684c02 100644 --- a/sdk/cpp/core/src/ietf_parser.hpp +++ b/sdk/cpp/core/src/ietf_parser.hpp @@ -36,6 +36,7 @@ class IetfCapabilitiesXmlParser : public CapabilitiesXmlParser ~IetfCapabilitiesXmlParser(); std::vector parse(const std::string & buffer); + std::vector parse_yang_1_1(const std::string & buffer); private: xmlDocPtr doc; diff --git a/sdk/cpp/core/src/path/capability.cpp b/sdk/cpp/core/src/path/capability.cpp index afc6dfa4c..7333fb01d 100644 --- a/sdk/cpp/core/src/path/capability.cpp +++ b/sdk/cpp/core/src/path/capability.cpp @@ -117,3 +117,15 @@ ydk::path::Capability::operator==(const ydk::path::Capability& cap) return true; } + +namespace ydk +{ +namespace path +{ +std::ostream& operator<< (std::ostream& stream, const Capability& cap) +{ + stream<populate_new_schemas_from_path(path); } +std::shared_ptr +ydk::path::DataNodeImpl::operator()(const ydk::path::Session& session) +{ + return session.invoke(*this); +} + +ydk::path::DataNode& +ydk::path::DataNodeImpl::create_action(const std::string& path) +{ + return create_datanode(path, ""); +} + ydk::path::DataNode& ydk::path::DataNodeImpl::create_datanode(const std::string& path, const std::string& value) { @@ -527,3 +539,42 @@ ydk::path::DataNodeImpl::annotations() return ann; } + +static bool statement_is_action(const ydk::path::Statement & s) +{ + return (s.keyword == "action"); +} + +std::string ydk::path::DataNodeImpl::get_action_node_path() const +{ + if(statement_is_action(get_schema_node().get_statement())) + { + return get_path(); + } + + for(auto & child_data_node : get_children()) + { + auto p = child_data_node->get_action_node_path(); + if(p.size() > 0) + { + return p; + } + } + return ""; +} + +bool ydk::path::DataNodeImpl::has_action_node() const +{ + if(statement_is_action(get_schema_node().get_statement())) + { + return true; + } + for(auto & child_data_node : get_children()) + { + if(child_data_node->has_action_node()) + { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/sdk/cpp/core/src/path/netconf_model_provider.cpp b/sdk/cpp/core/src/path/netconf_model_provider.cpp index 79d412d4a..11deb7ac4 100644 --- a/sdk/cpp/core/src/path/netconf_model_provider.cpp +++ b/sdk/cpp/core/src/path/netconf_model_provider.cpp @@ -118,6 +118,7 @@ string NetconfModelProvider::get_model(const string& name, const string& version data_end -= 1; model = reply.substr(data_start, data_end-data_start+1); + YLOG_DEBUG("Removing CDATA tags"); auto cdata_start = model.find(" - #include +#include "../common_utilities.hpp" #include "../entity_data_node_walker.hpp" #include "../errors.hpp" #include "../ietf_parser.hpp" +#include "../logger.hpp" #include "../netconf_ssh_client.hpp" #include "../netconf_tcp_client.hpp" -#include "../netconf_provider.hpp" #include "../types.hpp" #include "../ydk_yang.hpp" -#include "../logger.hpp" + #include "netconf_model_provider.hpp" -#include "../common_utilities.hpp" using namespace std; @@ -50,19 +48,24 @@ static path::SchemaNode* get_schema_for_operation(path::RootSchemaNode& root_sch static shared_ptr create_rpc_instance(path::RootSchemaNode & root_schema, string rpc_name); static path::DataNode& create_rpc_input(path::Rpc & netconf_rpc); +static bool supports_yang_1_1(vector & caps); + static bool is_candidate_supported(vector capbilities); static void create_input_target(path::DataNode & input, bool candidate_supported); static void create_input_source(path::DataNode & input, bool config); static void create_input_error_option(path::DataNode & input); static string get_annotated_config_payload(path::RootSchemaNode & root_schema, path::Rpc & rpc, path::Annotation & annotation); static string get_commit_rpc_payload(); -static shared_ptr handle_edit_reply(string reply, NetconfClient & client, bool candidate_supported); +static string get_caps_rpc_payload(); +static shared_ptr handle_crud_edit_reply(string reply, NetconfClient & client, bool candidate_supported); static string get_read_rpc_name(bool config); static bool is_config(path::Rpc & rpc); static string get_filter_payload(path::Rpc & ydk_rpc); static string get_netconf_payload(path::DataNode & input, const string& data_tag, const string& data_value); -static shared_ptr handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::Rpc & rpc); +static shared_ptr handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, const string& rpc_path); +static void check_rpc_reply_for_error(const string& reply); +static void log_rpc_request(const string& payload); const char* CANDIDATE = "urn:ietf:params:netconf:capability:candidate:1.0"; const string PROTOCOL_SSH = "ssh"; @@ -71,7 +74,6 @@ const string PROTOCOL_TCP = "tcp"; static bool is_netconf_get_rpc(path::Rpc & rpc); static shared_ptr handle_netconf_get_output(const string & reply, path::RootSchemaNode & root_schema); -// static shared_ptr handle_read_reply(string reply, path::RootSchemaNode & root_schema); NetconfSession::NetconfSession(path::Repository & repo, const string& address, @@ -189,11 +191,18 @@ void NetconfSession::initialize_repo(path::Repository & repo, bool on_demand) } } - auto lookup_table = capabilities_parser.get_lookup_table(server_capabilities); + vector yang_caps; + vector empty_caps; + vector all_caps; - std::vector yang_caps; - std::vector empty_caps; - std::vector all_caps = capabilities_parser.parse(server_capabilities); + if(supports_yang_1_1(server_capabilities)) + { + auto caps_1_1 = get_yang_1_1_capabilities(); + server_capabilities = get_union(server_capabilities, caps_1_1); + } + + all_caps = capabilities_parser.parse(server_capabilities); + auto lookup_table = capabilities_parser.get_lookup_table(server_capabilities); if (on_demand) yang_caps = capabilities_parser.parse(empty_caps); @@ -224,7 +233,7 @@ path::RootSchemaNode& NetconfSession::get_root_schema() const return *root_schema; } -shared_ptr NetconfSession::handle_read(path::Rpc& ydk_rpc) const +shared_ptr NetconfSession::handle_crud_read(path::Rpc& ydk_rpc) const { //for now we only support crud rpc's bool config = is_config(ydk_rpc); @@ -234,10 +243,11 @@ shared_ptr NetconfSession::handle_read(path::Rpc& ydk_rpc) const string filter_value = get_filter_payload(ydk_rpc); string netconf_payload = get_netconf_payload(input, "filter", filter_value); - return handle_rpc_output(execute_payload(netconf_payload), *root_schema, *netconf_rpc ); + + return handle_netconf_get_output(execute_payload(netconf_payload), *root_schema); } -shared_ptr NetconfSession::handle_edit(path::Rpc& ydk_rpc, path::Annotation annotation) const +shared_ptr NetconfSession::handle_crud_edit(path::Rpc& ydk_rpc, path::Annotation annotation) const { //for now we only support crud rpc's bool candidate_supported = is_candidate_supported(server_capabilities); @@ -252,7 +262,7 @@ shared_ptr NetconfSession::handle_edit(path::Rpc& ydk_rpc, path: string netconf_payload = get_netconf_payload(input, "config", config_payload); ly_verb(LY_LLVRB); // enable libyang logging after payload has been created - return handle_edit_reply(execute_payload(netconf_payload), *client, candidate_supported); + return handle_crud_edit_reply(execute_payload(netconf_payload), *client, candidate_supported); } shared_ptr NetconfSession::handle_netconf_operation(path::Rpc& ydk_rpc) const @@ -262,23 +272,40 @@ shared_ptr NetconfSession::handle_netconf_operation(path::Rpc& y string payload{""}; netconf_payload = payload + netconf_payload + ""; - YLOG_INFO("=============Generating payload to send to device============="); - YLOG_INFO("\n{}", netconf_payload); - YLOG_INFO("\n"); + log_rpc_request(netconf_payload); string reply = execute_payload(netconf_payload); - if (ydk_rpc.has_output_node()) + check_rpc_reply_for_error(reply); + + if (is_netconf_get_rpc(ydk_rpc)) { - return handle_rpc_output(reply, *root_schema, ydk_rpc); + return handle_netconf_get_output(reply, *root_schema); } - if(reply.find("") == string::npos) + else if (ydk_rpc.has_output_node()) { - YLOG_ERROR("Did not receive OK reply from the device"); - throw(YServiceProviderError{reply}); + return handle_rpc_output(reply, *root_schema, ydk_rpc.get_input_node().get_path()); } return nullptr; } +shared_ptr NetconfSession::invoke(path::DataNode& datanode) const +{ + if(!datanode.has_action_node()) + { + YLOG_ERROR("Datanode {} does not contain any action nodes", datanode.get_path()); + throw(YServiceProviderError{"Datanode does not contain any action nodes: " + datanode.get_path()}); + } + path::Codec codec_service{}; + auto netconf_payload = codec_service.encode(datanode, EncodingFormat::XML, true); + + netconf_payload = "\n" + netconf_payload + ""; + log_rpc_request(netconf_payload); + string reply = execute_payload(netconf_payload); + check_rpc_reply_for_error(reply); + + return handle_rpc_output(reply, *root_schema, datanode.get_action_node_path()); +} + shared_ptr NetconfSession::invoke(path::Rpc& rpc) const { path::SchemaNode* create_schema = get_schema_for_operation(*root_schema, "ydk:create"); @@ -294,11 +321,11 @@ shared_ptr NetconfSession::invoke(path::Rpc& rpc) const { //for each child node in datanode add the nc:operation attribute path::Annotation an{IETF_NETCONF_MODULE_NAME, "operation", rpc_schema == delete_schema ? "delete" : "merge"}; - return handle_edit(rpc, an); + return handle_crud_edit(rpc, an); } else if(rpc_schema == read_schema) { - return handle_read(rpc); + return handle_crud_read(rpc); } else { @@ -310,13 +337,36 @@ shared_ptr NetconfSession::invoke(path::Rpc& rpc) const string NetconfSession::execute_payload(const string & payload) const { - std::string reply = client->execute_payload(payload); + string reply = client->execute_payload(payload); YLOG_INFO("=============Reply payload received from device============="); YLOG_INFO("\n{}", reply); YLOG_INFO("\n"); return reply; } +vector NetconfSession::get_yang_1_1_capabilities() const +{ + IetfCapabilitiesXmlParser parser{}; + IetfCapabilitiesParser capabilities_parser{}; + string payload = get_caps_rpc_payload(); + + YLOG_INFO("=============Requesting YANG 1.1 capabilities============="); + string reply = execute_payload(payload); + return parser.parse_yang_1_1(reply); +} + +static bool supports_yang_1_1(vector & caps) +{ + for(string &c : caps ) + { + if(c.find("urn:ietf:params:netconf:capability:yang-library:1.0") != string::npos) + { + return true; + } + } + return false; +} + static shared_ptr create_rpc_instance(path::RootSchemaNode & root_schema, string rpc_name) { auto rpc = shared_ptr(root_schema.create_rpc(rpc_name)); @@ -332,6 +382,19 @@ static path::DataNode& create_rpc_input(path::Rpc & netconf_rpc) return netconf_rpc.get_input_node(); } +static string get_caps_rpc_payload() +{ + return R"( + + + + + + + + )"; +} + static string get_commit_rpc_payload() { return "" @@ -427,13 +490,11 @@ static string get_netconf_payload(path::DataNode & input, const string & data_t string payload{"\n"}; payload+=codec_service.encode(input, EncodingFormat::XML, true); payload+=""; - YLOG_INFO("=============Generating payload to send to device============="); - YLOG_INFO("\n{}", payload.c_str()); - YLOG_INFO("\n"); + log_rpc_request(payload); return payload; } -static shared_ptr handle_edit_reply(string reply, NetconfClient & client, bool candidate_supported) +static shared_ptr handle_crud_edit_reply(string reply, NetconfClient & client, bool candidate_supported) { if(reply.find("") == string::npos) { @@ -505,14 +566,9 @@ static shared_ptr handle_netconf_get_output(const string & reply return datanode; } -static shared_ptr handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::Rpc & rpc) +static shared_ptr handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, const string& rpc_path) { - if (is_netconf_get_rpc(rpc)) - { - return handle_netconf_get_output(reply, root_schema); - } - - path::Codec codec_service{}; + path::Codec codec_service{}; auto data_start = reply.find("", data_start); @@ -521,11 +577,13 @@ static shared_ptr handle_rpc_output(const string & reply, path:: data_start = data_start_end + 1; string data = reply.substr(data_start, data_end - data_start); + if(data.find("") != string::npos) + return nullptr; shared_ptr datanode = codec_service.decode_rpc_output( root_schema, data, - rpc.get_schema_node().get_path(), + rpc_path, EncodingFormat::XML); return datanode; } @@ -559,6 +617,22 @@ static path::SchemaNode* get_schema_for_operation(path::RootSchemaNode & root_sc return c[0]; } +static void check_rpc_reply_for_error(const string& reply) +{ + if(reply.find(" perform_decode(ydk::path::RootSchema return std::shared_ptr(rd); } -static const struct lyd_node* create_ly_rpc_node(ydk::path::RootSchemaNodeImpl & rs_impl, const std::string & rpc_path) +static const struct lyd_node* create_lyd_node_for_rpc(ydk::path::RootSchemaNodeImpl & rs_impl, const std::string & rpc_path) { const struct lyd_node* rpc = lyd_new_path(NULL, rs_impl.m_ctx, rpc_path.c_str(), NULL, LYD_ANYDATA_SXML, 0); if( rpc == nullptr || ly_errno ) @@ -230,7 +234,7 @@ ydk::path::Codec::~Codec() std::string ydk::path::Codec::encode(const ydk::path::DataNode& dn, ydk::EncodingFormat format, bool pretty) { - YLOG_DEBUG("Encoding data node {} to {} formated string", dn.get_path(), format==ydk::EncodingFormat::JSON ? "JSON" : "XML"); + YLOG_DEBUG("Encoding data node '{}' to {} formated string", dn.get_path(), format==ydk::EncodingFormat::JSON ? "JSON" : "XML"); std::string ret{}; if (typeid(dn) == typeid(RootDataImpl)) { std::vector> data_nodes = dn.get_children(); @@ -284,9 +288,11 @@ std::shared_ptr ydk::path::Codec::decode_rpc_output(RootSchemaNode & root_schema, const std::string& buffer, const std::string & rpc_path, EncodingFormat format) { + YLOG_DEBUG( "Decoding output for RPC '{}'. Output is: {}", rpc_path, buffer); + RootSchemaNodeImpl & rs_impl = get_root_schema_impl(root_schema); rs_impl.populate_new_schemas_from_payload(buffer, format); - const struct lyd_node* rpc = create_ly_rpc_node(rs_impl, rpc_path); + const struct lyd_node* rpc = create_lyd_node_for_rpc(rs_impl, rpc_path); struct lyd_node* root = lyd_parse_mem(rs_impl.m_ctx, buffer.c_str(), get_ly_format(format), LYD_OPT_TRUSTED | LYD_OPT_RPCREPLY, rpc, NULL); diff --git a/sdk/cpp/core/src/path/path_private.hpp b/sdk/cpp/core/src/path/path_private.hpp index e7c25cced..ceccfd4e1 100644 --- a/sdk/cpp/core/src/path/path_private.hpp +++ b/sdk/cpp/core/src/path/path_private.hpp @@ -188,6 +188,14 @@ namespace ydk { virtual DataNode& create_datanode(const std::string& path, const std::string& value); + DataNode& create_action(const std::string& path); + + std::shared_ptr operator()(const Session& session); + + bool has_action_node() const; + + std::string get_action_node_path() const; + void set_value(const std::string& value); virtual std::string get_value() const; diff --git a/sdk/cpp/core/src/path/restconf_session.cpp b/sdk/cpp/core/src/path/restconf_session.cpp index 5f0253841..0cd0a545d 100644 --- a/sdk/cpp/core/src/path/restconf_session.cpp +++ b/sdk/cpp/core/src/path/restconf_session.cpp @@ -44,7 +44,7 @@ namespace path { static const std::string default_capabilities_url = "/ietf-restconf-monitoring:restconf-state/capabilities"; -static std::shared_ptr handle_read_reply(const string & reply, path::RootSchemaNode & root_schema, EncodingFormat encoding); +static std::shared_ptr handle_crud_read_reply(const string & reply, path::RootSchemaNode & root_schema, EncodingFormat encoding); static path::SchemaNode* get_schema_for_operation(path::RootSchemaNode & root_schema, const string & operation); static string get_encoding_string(EncodingFormat encoding); @@ -117,6 +117,13 @@ path::RootSchemaNode& RestconfSession::get_root_schema() const return *root_schema; } +std::shared_ptr RestconfSession::invoke( + path::DataNode& rpc) const +{ + throw(YOperationNotSupportedError{"action datanode is not supported!"}); + return nullptr; +} + std::shared_ptr RestconfSession::invoke( path::Rpc& rpc) const { @@ -130,15 +137,15 @@ std::shared_ptr RestconfSession::invoke( if(rpc_schema == create_schema || rpc_schema == update_schema) { - return handle_edit(rpc, edit_method); + return handle_crud_edit(rpc, edit_method); } else if(rpc_schema == read_schema) { - return handle_read(rpc); + return handle_crud_read(rpc); } else if(rpc_schema == delete_schema) { - return handle_edit(rpc, "DELETE"); + return handle_crud_edit(rpc, "DELETE"); } else { @@ -149,7 +156,7 @@ std::shared_ptr RestconfSession::invoke( return datanode; } -std::shared_ptr RestconfSession::handle_read(path::Rpc& rpc) const +std::shared_ptr RestconfSession::handle_crud_read(path::Rpc& rpc) const { path::Codec codec_service{}; @@ -175,10 +182,10 @@ std::shared_ptr RestconfSession::handle_read(path::Rpc& rpc) con } YLOG_INFO("Performing GET on URL {}", url); - return handle_read_reply( client->execute("GET", url, ""), *root_schema, encoding); + return handle_crud_read_reply( client->execute("GET", url, ""), *root_schema, encoding); } -std::shared_ptr RestconfSession::handle_edit(path::Rpc& rpc, const string & operation) const +std::shared_ptr RestconfSession::handle_crud_edit(path::Rpc& rpc, const string & operation) const { path::Codec codec_service{}; auto entity = rpc.get_input_node().find("entity"); @@ -199,7 +206,7 @@ std::shared_ptr RestconfSession::handle_edit(path::Rpc& rpc, con return nullptr; } -static std::shared_ptr handle_read_reply(const string & reply, path::RootSchemaNode & root_schema, EncodingFormat encoding) +static std::shared_ptr handle_crud_read_reply(const string & reply, path::RootSchemaNode & root_schema, EncodingFormat encoding) { path::Codec codec_service{}; diff --git a/sdk/cpp/core/src/path/root_data_node.cpp b/sdk/cpp/core/src/path/root_data_node.cpp index 22bc33eff..94f8d9017 100644 --- a/sdk/cpp/core/src/path/root_data_node.cpp +++ b/sdk/cpp/core/src/path/root_data_node.cpp @@ -212,7 +212,7 @@ ydk::path::RootDataImpl::find(const std::string& path) } auto s = get_schema_node().get_statement(); - if(s.keyword == "rpc") + if(s.keyword == "rpc" || s.keyword == "action") { schema_path+="input/"; } diff --git a/sdk/cpp/core/src/path_api.hpp b/sdk/cpp/core/src/path_api.hpp index 958e88df8..8ec2cc211 100644 --- a/sdk/cpp/core/src/path_api.hpp +++ b/sdk/cpp/core/src/path_api.hpp @@ -42,6 +42,7 @@ namespace ydk { class NetconfClient; class RestconfClient; +class CapabilitiesParser; namespace path { @@ -725,6 +726,8 @@ class DataNode{ /// virtual DataNode& create_datanode(const std::string& path); + virtual DataNode& create_action(const std::string& path) = 0; + /// /// @brief create a DataNode corresponding to the path and set its value /// @@ -744,6 +747,18 @@ class DataNode{ /// virtual DataNode& create_datanode(const std::string& path, const std::string& value) = 0; + /// + /// @brief execute/invoke the action through the given session. + /// + /// @param[in] session The Session + /// @areturn pointer to the DataNode or nullptr if none exists + /// + virtual std::shared_ptr operator()(const Session& session) = 0; + + virtual bool has_action_node() const = 0; + + virtual std::string get_action_node_path() const = 0; + /// /// @brief set the value of this DataNode. /// @@ -868,6 +883,8 @@ struct Capability { std::vector deviations; }; +std::ostream& operator<< (std::ostream& stream, const Capability& value); + /// /// @brief interface for module provider. /// @@ -1004,6 +1021,17 @@ class Session /// @return The pointer to the DataNode representing the output. /// virtual std::shared_ptr invoke(Rpc& rpc) const = 0 ; + + /// + /// @brief invoke the Action + /// + /// invokes or executes the action defined inside the given DataNode and Returns a DataNode pointer + /// if the action has an output modelled in YANG. + /// + /// @param[in] pointer to the Rpc node + /// @return The pointer to the DataNode representing the output. + /// + virtual std::shared_ptr invoke(DataNode& rpc) const = 0 ; }; class NetconfSession : public Session { @@ -1049,12 +1077,14 @@ class NetconfSession : public Session { RootSchemaNode& get_root_schema() const; std::shared_ptr invoke(Rpc& rpc) const; + std::shared_ptr invoke(DataNode& rpc) const; std::vector get_capabilities() const; private: - std::shared_ptr handle_edit( + std::vector get_yang_1_1_capabilities() const; + std::shared_ptr handle_crud_edit( Rpc& rpc, Annotation ann) const; - std::shared_ptr handle_read(Rpc& rpc) const; + std::shared_ptr handle_crud_read(Rpc& rpc) const; std::shared_ptr handle_netconf_operation(Rpc& ydk_rpc) const; void initialize_client_with_key(const std::string& address, const std::string& username, @@ -1101,11 +1131,12 @@ class RestconfSession : public Session { RootSchemaNode& get_root_schema() const; std::shared_ptr invoke(Rpc& rpc) const; + std::shared_ptr invoke(DataNode& rpc) const; private: void initialize(Repository & repo); - std::shared_ptr handle_edit(Rpc& rpc, const std::string & yfilter) const; - std::shared_ptr handle_read(Rpc& rpc) const; + std::shared_ptr handle_crud_edit(Rpc& rpc, const std::string & yfilter) const; + std::shared_ptr handle_crud_read(Rpc& rpc) const; private: std::shared_ptr client; std::shared_ptr root_schema; @@ -1120,7 +1151,7 @@ class RestconfSession : public Session { /// /// -/// @brief An instance of the YANG schmea rpc node +/// @brief An instance of the YANG schema rpc node /// /// Instances of this class represent a YANG rpc and are modelled as Callables. /// The input data node tree is used to populate the input parameters to the rpc diff --git a/sdk/cpp/core/tests/CMakeLists.txt b/sdk/cpp/core/tests/CMakeLists.txt index bce0f43be..c72668de4 100644 --- a/sdk/cpp/core/tests/CMakeLists.txt +++ b/sdk/cpp/core/tests/CMakeLists.txt @@ -13,7 +13,12 @@ set(core_tests_src bgptest.cpp set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") -set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 -fprofile-arcs -ftest-coverage") +if(COVERAGE STREQUAL True) + set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 -fprofile-arcs -ftest-coverage") + set(CMAKE_BUILD_TYPE debug) +else() + set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0") +endif() foreach(test_name IN LISTS core_tests) add_executable(${test_name} ${test_name}.cpp) diff --git a/sdk/cpp/core/tests/confd/ydktest/Makefile b/sdk/cpp/core/tests/confd/ydktest/Makefile index 675dd1ca2..e9cf67247 100644 --- a/sdk/cpp/core/tests/confd/ydktest/Makefile +++ b/sdk/cpp/core/tests/confd/ydktest/Makefile @@ -31,7 +31,7 @@ all: openconfig-platform.deviation\ ietf-interfaces.fxs openconfig-types.fxs openconfig-routing-policy.fxs openconfig-platform.fxs openconfig-interfaces.fxs openconfig-bgp.fxs\ openconfig-platform-types.fxs openconfig-platform-transceiver.fxs oc-pattern.fxs main.fxs main-aug1.fxs iana-if-type.fxs\ openconfig-transport-types.fxs ietf-aug-base-1.fxs ietf-aug-base-2.fxs openconfig-terminal-device.fxs openconfig-if-ethernet.fxs\ - ydktest-aug-ietf-1.fxs ydktest-aug-ietf-2.fxs ydktest-aug-ietf-4.fxs ydktest-aug-ietf-5.fxs\ + ydktest-aug-ietf-1.fxs ydktest-aug-ietf-2.fxs ydktest-aug-ietf-4.fxs ydktest-aug-ietf-5.fxs ydktest-sanity-action.fxs\ $(CDB_DIR) ssh-keydir @echo "Build complete" @@ -131,6 +131,9 @@ ydktest-sanity.yang: ydktest-types.yang ydktest-sanity-submodule.yang ydktest-types.yang: cp ../../models/ydktest-types.yang ydktest-types.yang +ydktest-sanity-action.yang: + cp ../../models/ydktest-sanity-action.yang ydktest-sanity-action.yang + main.yang: cp ../../models/main@2015-11-17.yang main.yang diff --git a/sdk/cpp/core/tests/mock_data.hpp b/sdk/cpp/core/tests/mock_data.hpp index 7ce3fe64d..c1ae45054 100644 --- a/sdk/cpp/core/tests/mock_data.hpp +++ b/sdk/cpp/core/tests/mock_data.hpp @@ -43,7 +43,8 @@ static std::unordered_map test_openconfig_lo {"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", {"ietf-netconf-monitoring", ""}}, {"urn:ietf:params:xml:ns:yang:ietf-interfaces", {"ietf-interfaces", ""}}, {"http://cisco.com/ns/yang/ydk", {"ydk", ""}}, - {"http://cisco.com/ns/yang/ydktest-sanity", {"ydktest-sanity", ""}} + {"http://cisco.com/ns/yang/ydktest-sanity", {"ydktest-sanity", ""}}, + {"http://cisco.com/ns/yang/ydktest-action", {"ydktest-sanity-action", ""}} }; @@ -84,6 +85,15 @@ class MockSession : public ydk::path::Session return nullptr; } + + std::shared_ptr invoke(ydk::path::DataNode& rpc) const + { + ydk::path::Codec s{}; + + std::cout << s.encode(rpc, ydk::EncodingFormat::XML, true) << std::endl; + + return nullptr; + } private: std::string m_searchdir; std::vector m_capabilities; @@ -104,7 +114,8 @@ static std::vector test_openconfig { {"ietf-netconf-monitoring", ""}, {"ietf-interfaces", ""}, {"ydk", ""}, - {"ydktest-sanity", ""} + {"ydktest-sanity", ""}, + {"ydktest-sanity-action", ""} }; diff --git a/sdk/cpp/core/tests/models/.gitignore b/sdk/cpp/core/tests/models/.gitignore new file mode 100644 index 000000000..809a2258d --- /dev/null +++ b/sdk/cpp/core/tests/models/.gitignore @@ -0,0 +1,4 @@ +ydktest-sanity.yang +ydktest-sanity-augm.yang +ydktest-sanity-types.yang +iana-if-type.yang \ No newline at end of file diff --git a/sdk/cpp/core/tests/models/ydktest-sanity-action.yang b/sdk/cpp/core/tests/models/ydktest-sanity-action.yang new file mode 100644 index 000000000..31bad056e --- /dev/null +++ b/sdk/cpp/core/tests/models/ydktest-sanity-action.yang @@ -0,0 +1,56 @@ +module ydktest-sanity-action { + yang-version 1.1; + + /*** NAMESPACE / PREFIX DEFINITION ***/ + + namespace "http://cisco.com/ns/yang/ydktest-action"; + + + prefix "ydk-a"; + + /*** LINKAGE (IMPORTS / INCLUDES) ***/ + + organization "Cisco Systems, Inc."; + + contact + "Cisco Systems, Inc. + Customer Service + + Postal: 170 West Tasman Drive + San Jose, CA 95134 + + Tel: +1 800 553-NETS + + E-mail: cs-yang@cisco.com"; + + description + "This module contains a collection of YANG definitions + for sanity package. + + This module contains definitions + for the following management objects: + + Copyright (c) 2013-2014 by Cisco Systems, Inc. + All rights reserved."; + + revision "2018-03-17" { + description + "Initial revision."; + } + + container data { + action action-node { + input { + leaf test { + type string; + } + } + output { + leaf t { + type string; + } + } + } + } + +} diff --git a/sdk/cpp/core/tests/test_capabilities_parser.cpp b/sdk/cpp/core/tests/test_capabilities_parser.cpp index 3ba2aabfc..4d4203ca5 100644 --- a/sdk/cpp/core/tests/test_capabilities_parser.cpp +++ b/sdk/cpp/core/tests/test_capabilities_parser.cpp @@ -27,6 +27,7 @@ #include #include "../src/ietf_parser.hpp" +#include "../src/path_api.hpp" #include "../src/opendaylight_parser.hpp" #include "catch.hpp" @@ -100,6 +101,7 @@ TEST_CASE("test_error") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -108,6 +110,7 @@ TEST_CASE("test_error_1") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -116,6 +119,7 @@ TEST_CASE("test_error_2") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -124,6 +128,7 @@ TEST_CASE("test_error_3") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -142,3 +147,435 @@ TEST_CASE("test_odl") INFO(n.first + ", " + n.second->host.get()); } } + +TEST_CASE("yang_1_1_xml_caps") +{ + string c = R"( + + + + + iana-crypt-hash + 2014-08-06 + urn:ietf:params:xml:ns:yang:iana-crypt-hash + crypt-hash-sha-512 + crypt-hash-sha-256 + crypt-hash-md5 + import + + + iana-if-type + 2014-05-08 + urn:ietf:params:xml:ns:yang:iana-if-type + import + + + ietf-aug-base-1 + 2016-07-01 + http://cisco.com/ns/yang/ietf-aug-base-1 + implement + + + ietf-aug-base-2 + 2016-07-01 + http://cisco.com/ns/yang/ietf-aug-base-2 + implement + + + ietf-inet-types + 2013-07-15 + urn:ietf:params:xml:ns:yang:ietf-inet-types + import + + + ietf-interfaces + 2014-05-08 + urn:ietf:params:xml:ns:yang:ietf-interfaces + pre-provisioning + if-mib + arbitrary-names + implement + + + ietf-netconf + 2011-06-01 + urn:ietf:params:xml:ns:netconf:base:1.0 + implement + + + ietf-netconf-acm + 2012-02-22 + urn:ietf:params:xml:ns:yang:ietf-netconf-acm + implement + + + ietf-netconf-monitoring + 2010-10-04 + urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring + implement + + + ietf-netconf-notifications + 2012-02-06 + urn:ietf:params:xml:ns:yang:ietf-netconf-notifications + implement + + + ietf-netconf-with-defaults + 2011-06-01 + urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults + implement + + + ietf-restconf-monitoring + 2016-08-15 + urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring + implement + + + ietf-yang-library + 2016-06-21 + urn:ietf:params:xml:ns:yang:ietf-yang-library + implement + + + ietf-yang-types + 2013-07-15 + urn:ietf:params:xml:ns:yang:ietf-yang-types + import + + + main + 2015-11-17 + http://cisco.com/ns/yang/main + implement + + + main-aug1 + 2015-11-17 + http://cisco.com/ns/yang/main-aug1 + implement + + + oc-pattern + 2015-11-17 + http://cisco.com/ns/yang/oc-pattern + implement + + + openconfig-bgp + 2016-06-21 + http://openconfig.net/yang/bgp + implement + + openconfig-bgp-common + 2016-06-21 + + + openconfig-bgp-common-structure + 2016-06-21 + + + openconfig-bgp-global + 2016-06-21 + + + openconfig-bgp-neighbor + 2016-06-21 + + + openconfig-bgp-peer-group + 2016-06-21 + + + openconfig-bgp-common-multiprotocol + 2016-06-21 + + + + openconfig-bgp-policy + 2016-06-21 + http://openconfig.net/yang/bgp-policy + implement + + + openconfig-bgp-types + 2016-06-21 + http://openconfig.net/yang/bgp-types + import + + + openconfig-extensions + 2015-10-09 + http://openconfig.net/yang/openconfig-ext + import + + + openconfig-if-ethernet + 2016-05-26 + http://openconfig.net/yang/interfaces/ethernet + implement + + + openconfig-interfaces + 2016-05-26 + http://openconfig.net/yang/interfaces + implement + + + openconfig-platform + 2016-06-06 + http://openconfig.net/yang/platform + + cisco-xr-openconfig-platform-deviations + 2016-10-16 + + implement + + + openconfig-platform-transceiver + 2016-05-24 + http://openconfig.net/yang/platform/transceiver + implement + + + openconfig-platform-types + 2016-06-06 + http://openconfig.net/yang/platform-types + import + + + openconfig-policy-types + 2016-05-12 + http://openconfig.net/yang/policy-types + import + + + openconfig-routing-policy + 2016-05-12 + http://openconfig.net/yang/routing-policy + implement + + + openconfig-terminal-device + 2016-06-17 + http://openconfig.net/yang/terminal-device + implement + + + openconfig-transport-types + 2016-06-17 + http://openconfig.net/yang/transport-types + import + + + openconfig-types + 2016-05-31 + http://openconfig.net/yang/openconfig-types + import + + + tailf-aaa + 2015-06-16 + http://tail-f.com/ns/aaa/1.1 + implement + + + tailf-acm + 2013-03-07 + http://tail-f.com/yang/acm + implement + + + tailf-common-monitoring + 2013-06-14 + http://tail-f.com/yang/common-monitoring + import + + + tailf-confd-monitoring + 2013-06-14 + http://tail-f.com/yang/confd-monitoring + implement + + + tailf-kicker + 2016-11-24 + http://tail-f.com/ns/kicker + implement + + + tailf-netconf-monitoring + 2016-11-24 + http://tail-f.com/yang/netconf-monitoring + implement + + + tailf-webui + 2013-03-07 + http://tail-f.com/ns/webui + implement + + + ydktest-aug-ietf-1 + 2016-06-17 + http://cisco.com/ns/yang/yaug-one + implement + + + ydktest-aug-ietf-2 + 2016-06-22 + http://cisco.com/ns/yang/yaug-two + implement + + + ydktest-aug-ietf-4 + 2016-06-27 + http://cisco.com/ns/yang/yaug-four + implement + + + ydktest-aug-ietf-5 + 2017-07-26 + http://cisco.com/ns/yang/yaug-five + implement + + + ydktest-filterread + 2015-11-17 + http://cisco.com/ns/yang/ydk-filter + implement + + + ydktest-sanity + 2015-11-17 + http://cisco.com/ns/yang/ydktest-sanity + ipv6-privacy-autoconf + ipv4-non-contiguous-netmasks + implement + + ydktest-sanity-submodule + 2016-04-25 + + + + ydktest-sanity-augm + 2015-11-17 + http://cisco.com/ns/yang/ydktest-sanity-augm + implement + + + ydktest-sanity-types + 2016-04-11 + http://cisco.com/ns/yang/ydktest-sanity-types + import + + + ydktest-types + 2016-05-23 + http://cisco.com/ns/yang/ydktest-types + import + + + + +)"; + + string hello=R"( + + +urn:ietf:params:netconf:base:1.0 +urn:ietf:params:netconf:base:1.1 +urn:ietf:params:netconf:capability:writable-running:1.0 +urn:ietf:params:netconf:capability:candidate:1.0 +urn:ietf:params:netconf:capability:confirmed-commit:1.0 +urn:ietf:params:netconf:capability:confirmed-commit:1.1 +urn:ietf:params:netconf:capability:xpath:1.0 +urn:ietf:params:netconf:capability:validate:1.0 +urn:ietf:params:netconf:capability:validate:1.1 +urn:ietf:params:netconf:capability:rollback-on-error:1.0 +urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all-tagged +urn:ietf:params:netconf:capability:yang-library:1.0?revision=2016-06-21&module-set-id=fba28be41dbd9b62782518ee42d32f29 +http://tail-f.com/ns/netconf/actions/1.0 +http://tail-f.com/ns/netconf/extensions +http://cisco.com/ns/yang/ietf-aug-base-1?module=ietf-aug-base-1&revision=2016-07-01 +http://cisco.com/ns/yang/ietf-aug-base-2?module=ietf-aug-base-2&revision=2016-07-01 +http://cisco.com/ns/yang/main?module=main&revision=2015-11-17 +http://cisco.com/ns/yang/main-aug1?module=main-aug1&revision=2015-11-17 +http://cisco.com/ns/yang/oc-pattern?module=oc-pattern&revision=2015-11-17 +http://cisco.com/ns/yang/yaug-five?module=ydktest-aug-ietf-5&revision=2017-07-26 +http://cisco.com/ns/yang/yaug-four?module=ydktest-aug-ietf-4&revision=2016-06-27 +http://cisco.com/ns/yang/yaug-one?module=ydktest-aug-ietf-1&revision=2016-06-17 +http://cisco.com/ns/yang/yaug-two?module=ydktest-aug-ietf-2&revision=2016-06-22 +http://cisco.com/ns/yang/ydk-filter?module=ydktest-filterread&revision=2015-11-17 +http://cisco.com/ns/yang/ydktest-sanity?module=ydktest-sanity&revision=2015-11-17&features=ipv6-privacy-autoconf,ipv4-non-contiguous-netmasks +http://cisco.com/ns/yang/ydktest-sanity-augm?module=ydktest-sanity-augm&revision=2015-11-17 +http://cisco.com/ns/yang/ydktest-sanity-types?module=ydktest-sanity-types&revision=2016-04-11 +http://cisco.com/ns/yang/ydktest-types?module=ydktest-types&revision=2016-05-23 +http://openconfig.net/yang/bgp?module=openconfig-bgp&revision=2016-06-21 +http://openconfig.net/yang/bgp-policy?module=openconfig-bgp-policy&revision=2016-06-21 +http://openconfig.net/yang/bgp-types?module=openconfig-bgp-types&revision=2016-06-21 +http://openconfig.net/yang/interfaces?module=openconfig-interfaces&revision=2016-05-26 +http://openconfig.net/yang/interfaces/ethernet?module=openconfig-if-ethernet&revision=2016-05-26 +http://openconfig.net/yang/openconfig-ext?module=openconfig-extensions&revision=2015-10-09 +http://openconfig.net/yang/openconfig-types?module=openconfig-types&revision=2016-05-31 +http://openconfig.net/yang/platform?module=openconfig-platform&revision=2016-06-06&deviations=cisco-xr-openconfig-platform-deviations +http://openconfig.net/yang/platform-types?module=openconfig-platform-types&revision=2016-06-06 +http://openconfig.net/yang/platform/transceiver?module=openconfig-platform-transceiver&revision=2016-05-24 +http://openconfig.net/yang/policy-types?module=openconfig-policy-types&revision=2016-05-12 +http://openconfig.net/yang/routing-policy?module=openconfig-routing-policy&revision=2016-05-12 +http://openconfig.net/yang/terminal-device?module=openconfig-terminal-device&revision=2016-06-17 +http://openconfig.net/yang/transport-types?module=openconfig-transport-types&revision=2016-06-17 +http://tail-f.com/ns/aaa/1.1?module=tailf-aaa&revision=2015-06-16 +http://tail-f.com/ns/kicker?module=tailf-kicker&revision=2016-11-24 +http://tail-f.com/ns/webui?module=tailf-webui&revision=2013-03-07 +http://tail-f.com/yang/acm?module=tailf-acm&revision=2013-03-07 +http://tail-f.com/yang/common-monitoring?module=tailf-common-monitoring&revision=2013-06-14 +http://tail-f.com/yang/confd-monitoring?module=tailf-confd-monitoring&revision=2013-06-14 +http://tail-f.com/yang/netconf-monitoring?module=tailf-netconf-monitoring&revision=2016-11-24 +urn:ietf:params:xml:ns:yang:iana-crypt-hash?module=iana-crypt-hash&revision=2014-08-06&features=crypt-hash-sha-512,crypt-hash-sha-256,crypt-hash-md5 +urn:ietf:params:xml:ns:yang:iana-if-type?module=iana-if-type&revision=2014-05-08 +urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15 +urn:ietf:params:xml:ns:yang:ietf-interfaces?module=ietf-interfaces&revision=2014-05-08&features=pre-provisioning,if-mib,arbitrary-names +urn:ietf:params:xml:ns:yang:ietf-netconf-acm?module=ietf-netconf-acm&revision=2012-02-22 +urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04 +urn:ietf:params:xml:ns:yang:ietf-netconf-notifications?module=ietf-netconf-notifications&revision=2012-02-06 +urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring?module=ietf-restconf-monitoring&revision=2016-08-15 +urn:ietf:params:xml:ns:yang:ietf-yang-library?module=ietf-yang-library&revision=2016-06-21 +urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2013-07-15 +urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01 +urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults?module=ietf-netconf-with-defaults&revision=2011-06-01 + +1466)"; + + IetfCapabilitiesXmlParser p; + auto v1 = p.parse_yang_1_1(c); + REQUIRE(v1.size()!=0); + + auto v2 = p.parse(hello); + REQUIRE(v2.size()!=0); + + IetfCapabilitiesParser capabilities_parser{}; + map m; + auto a1 = capabilities_parser.parse(v1); + REQUIRE(a1.size()!=0); + for(auto &s:a1) + { + cout<second); + REQUIRE(same == true); + } + + REQUIRE(a1.size()==a2.size()); +} diff --git a/sdk/cpp/core/tests/test_codec.cpp b/sdk/cpp/core/tests/test_codec.cpp index 2c7c3e9e0..9f264d0f3 100644 --- a/sdk/cpp/core/tests/test_codec.cpp +++ b/sdk/cpp/core/tests/test_codec.cpp @@ -50,3 +50,49 @@ TEST_CASE( "test_codec_rpc" ) auto x2 = s.encode(*d2, EncodingFormat::XML, false); REQUIRE(x2=="module xyz { } "); } + +TEST_CASE( "test_codec_action" ) +{ + ydk::path::Codec s{}; + std::string searchdir{TEST_HOME}; + mock::MockSession sp{searchdir, test_openconfig}; + + auto & schema = sp.get_root_schema(); + + string pl2 = "xyz"; + auto d2 = s.decode_rpc_output(schema, pl2, "/ydktest-sanity-action:data/action-node", EncodingFormat::XML); + REQUIRE(d2!=nullptr); + auto x2 = s.encode(*d2, EncodingFormat::XML, false); + REQUIRE(x2=="xyz"); +} + +TEST_CASE( "test_codec_action_datanode" ) +{ + ydk::path::Codec s{}; + std::string searchdir{TEST_HOME}; + mock::MockSession sp{searchdir, test_openconfig}; + + auto & schema = sp.get_root_schema(); + + auto & data = schema.create_datanode("ydktest-sanity-action:data"); + auto & a = data.create_action("action-node"); + a.create_datanode("test", "xyz"); + REQUIRE_NOTHROW((data)(sp)); + + REQUIRE(data.get_action_node_path() == "/ydktest-sanity-action:data/action-node"); +} +/* +TEST_CASE( "test_codec_ok" ) +{ + ydk::path::Codec s{}; + std::string searchdir{TEST_HOME}; + mock::MockSession sp{searchdir, test_openconfig}; + + auto & schema = sp.get_root_schema(); + + string pl2 = ""; + auto d2 = s.decode_rpc_output(schema, pl2, "/ietf-netconf:edit-config", EncodingFormat::XML); + REQUIRE(d2!=nullptr); + auto x2 = s.encode(*d2, EncodingFormat::XML, false); + REQUIRE(x2==""); +}*/ \ No newline at end of file diff --git a/sdk/cpp/tests/CMakeLists.txt b/sdk/cpp/tests/CMakeLists.txt index 59f1439d2..ff8722cb4 100644 --- a/sdk/cpp/tests/CMakeLists.txt +++ b/sdk/cpp/tests/CMakeLists.txt @@ -9,7 +9,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") set(bundle_tests_src main.cpp test_c_api.cpp - test_core_netconf.cpp test_crud.cpp test_errors.cpp test_executor_service.cpp @@ -20,6 +19,7 @@ set(bundle_tests_src test_netconf_tcp_client.cpp test_on_demand_loading.cpp test_opendaylight.cpp + test_path_api.cpp test_restconf_client.cpp test_restconf_provider.cpp test_sanity_codec.cpp diff --git a/sdk/cpp/tests/test_core_netconf.cpp b/sdk/cpp/tests/test_path_api.cpp similarity index 94% rename from sdk/cpp/tests/test_core_netconf.cpp rename to sdk/cpp/tests/test_path_api.cpp index d25c70c35..2b305584d 100644 --- a/sdk/cpp/tests/test_core_netconf.cpp +++ b/sdk/cpp/tests/test_path_api.cpp @@ -146,7 +146,7 @@ TEST_CASE( "rpc_escape" ) // convert result DataNode to XML and print auto yang = s.encode( *result, ydk::EncodingFormat::XML, true); - std::cout< + + xyz + + +)"; + auto x = s.encode(native, ydk::EncodingFormat::XML, true); + REQUIRE(x==expected); + + // run test knowing confd will reject action because it is not implemented on device + REQUIRE_THROWS_AS((native)(session), ydk::YServiceProviderError); + + auto & runner = schema.create_datanode("ydktest-sanity:runner"); + // run test knowing session will reject datanode without action + REQUIRE_THROWS_AS((runner)(session), ydk::YServiceProviderError); +} + +TEST_CASE("get_schema_decode") +{ + ydk::path::NetconfSession session{"127.0.0.1", "admin", "admin", 12022}; + ydk::path::RootSchemaNode& schema = session.get_root_schema(); + ydk::path::Codec s{}; + + auto rpc = schema.create_rpc("ietf-netconf-monitoring:get-schema"); + rpc->get_input_node().create_datanode("identifier", "ydktest-sanity-action"); + + auto o = (*rpc)(session); + + auto xml = s.encode(*o, ydk::EncodingFormat::XML, true); + cout<`. + .. py:method:: invoke(datanode) + + :param datanode: (:py:class:`Rpc`) Given DataNode containing YANG 1.1 action to be executed. + + Invokes or executes the given DataNode and returns a :py:class:`DataNode` pointer if the action has an output modelled in YANG. + + :returns: :py:class:`DataNode`. + .. py:method:: get_capabilities() Returns a list of capabilities of the client diff --git a/sdk/python/core/python.cpp b/sdk/python/core/python.cpp index a45d3ccd2..25b6ed6eb 100644 --- a/sdk/python/core/python.cpp +++ b/sdk/python/core/python.cpp @@ -292,7 +292,8 @@ PYBIND11_MODULE(ydk_, ydk) class_(path, "Session") .def("get_root_schema", &ydk::path::Session::get_root_schema, return_value_policy::reference) - .def("invoke", &ydk::path::Session::invoke, return_value_policy::reference); + .def("invoke", (std::shared_ptr (ydk::path::Session::*)(ydk::path::Rpc& rpc) const) &ydk::path::Session::invoke, return_value_policy::reference) + .def("invoke", (std::shared_ptr (ydk::path::Session::*)(ydk::path::DataNode& rpc) const) &ydk::path::Session::invoke, return_value_policy::reference); class_(path, "NetconfSession") .def("__init__", @@ -430,7 +431,8 @@ PYBIND11_MODULE(ydk_, ydk) arg("timeout")=-1) .def("get_root_schema", &ydk::path::NetconfSession::get_root_schema, return_value_policy::reference) - .def("invoke", &ydk::path::NetconfSession::invoke, return_value_policy::reference) + .def("invoke", (std::shared_ptr (ydk::path::NetconfSession::*)(ydk::path::Rpc& rpc) const) &ydk::path::NetconfSession::invoke, return_value_policy::reference) + .def("invoke", (std::shared_ptr (ydk::path::NetconfSession::*)(ydk::path::DataNode& rpc) const) &ydk::path::NetconfSession::invoke, return_value_policy::reference) .def("get_capabilities", &ydk::path::NetconfSession::get_capabilities, return_value_policy::reference); class_(path, "RestconfSession") @@ -454,7 +456,8 @@ PYBIND11_MODULE(ydk_, ydk) arg("config_url_root"), arg("state_url_root")) .def("get_root_schema", &ydk::path::RestconfSession::get_root_schema, return_value_policy::reference) - .def("invoke", &ydk::path::RestconfSession::invoke, return_value_policy::reference); + .def("invoke", (std::shared_ptr (ydk::path::RestconfSession::*)(ydk::path::Rpc& rpc) const) &ydk::path::RestconfSession::invoke, return_value_policy::reference) + .def("invoke", (std::shared_ptr (ydk::path::RestconfSession::*)(ydk::path::DataNode& rpc) const) &ydk::path::RestconfSession::invoke, return_value_policy::reference); class_(path, "Statement") .def(init(), arg("keyword"), arg("arg")) @@ -475,6 +478,7 @@ PYBIND11_MODULE(ydk_, ydk) class_>(path, "DataNode") .def("get_schema_node", &ydk::path::DataNode::get_schema_node, return_value_policy::reference) .def("get_path", &ydk::path::DataNode::get_path, return_value_policy::reference) + .def("create_action", &ydk::path::DataNode::create_action, return_value_policy::reference) .def("create_datanode", (ydk::path::DataNode& (ydk::path::DataNode::*)(const string&)) &ydk::path::DataNode::create_datanode, return_value_policy::reference, arg("path")) .def("create_datanode", (ydk::path::DataNode& (ydk::path::DataNode::*)(const string&, const string&)) &ydk::path::DataNode::create_datanode, return_value_policy::reference, arg("path"), arg("value")) .def("get_value", &ydk::path::DataNode::get_value, return_value_policy::reference) @@ -484,7 +488,8 @@ PYBIND11_MODULE(ydk_, ydk) .def("find", &ydk::path::DataNode::find, return_value_policy::reference, arg("path")) .def("add_annotation", &ydk::path::DataNode::add_annotation, return_value_policy::reference, arg("annotation")) .def("remove_annotation", &ydk::path::DataNode::remove_annotation, return_value_policy::reference, arg("annotation")) - .def("annotations", &ydk::path::DataNode::annotations, return_value_policy::reference); + .def("annotations", &ydk::path::DataNode::annotations, return_value_policy::reference) + .def("__call__", &ydk::path::DataNode::operator(), arg("service_provider")); class_>(path, "RootSchemaNode") .def("get_path", &ydk::path::RootSchemaNode::get_path, return_value_policy::reference) diff --git a/sdk/python/core/tests/test_sanity_path.py b/sdk/python/core/tests/test_sanity_path.py index 724db52b2..48c19a291 100644 --- a/sdk/python/core/tests/test_sanity_path.py +++ b/sdk/python/core/tests/test_sanity_path.py @@ -20,6 +20,7 @@ import unittest import logging +from ydk.errors import YPYError from ydk.path.sessions import NetconfSession from ydk.path import Codec from ydk.types import EncodingFormat @@ -50,7 +51,7 @@ def _delete_runner(self): create_rpc = self.root_schema.create_rpc("ydk:delete") create_rpc.get_input_node().create_datanode("entity", xml) # RuntimeError: YCoreError: YCodecError:Schema node not found.. Path: input/config if invoked - # create_rpc(self.nc_session) + create_rpc(self.nc_session) def tearDown(self): # RuntimeError: YCoreError: YCodecError:Schema node not found.. Path: input/config if invoked @@ -175,6 +176,25 @@ def test_create_gre_tunnel_on_demand(self): crud_service = CRUDService(); crud_service.create(provider, native) + def test_anyxml(self): + expected=''' + + xyz + + +''' + native = self.root_schema.create_datanode("ydktest-sanity-action:data", "") + a = native.create_action("action-node") + a.create_datanode("test", "xyz") + + xml = self.codec.encode(native, EncodingFormat.XML, True) + self.assertEqual(xml, expected) + + try: + native(self.nc_session) + except Exception as e: + self.assertEqual(isinstance(e, RuntimeError), True) + def enable_logging(level): log = logging.getLogger('ydk') log.setLevel(level) diff --git a/test/test_package_centos.sh b/test/test_package_centos.sh index fd20461e7..feefa69d1 100755 --- a/test/test_package_centos.sh +++ b/test/test_package_centos.sh @@ -79,7 +79,6 @@ yum install -y libydk*.rpm print_msg "Installing ydk-py" cd sdk/python/core -export YDK_COVERAGE= python setup.py sdist ${PIP_BIN} install -v dist/ydk*.tar.gz diff --git a/test/test_package_ubuntu.sh b/test/test_package_ubuntu.sh index 8dfbec1d2..1fcd3f980 100755 --- a/test/test_package_ubuntu.sh +++ b/test/test_package_ubuntu.sh @@ -79,7 +79,6 @@ gdebi -n libydk*.deb print_msg "Installing ydk-py" cd sdk/python/core -export YDK_COVERAGE= python setup.py sdist ${PIP_BIN} install -v dist/ydk*.tar.gz diff --git a/test/tests.sh b/test/tests.sh index 80af1fe57..7b2bb47c2 100755 --- a/test/tests.sh +++ b/test/tests.sh @@ -157,6 +157,7 @@ function install_test_cpp_core { print_msg "Installing / testing cpp core" install_cpp_core run_cpp_core_test + generate_libydk } function install_cpp_core { @@ -168,8 +169,16 @@ function install_cpp_core { print_msg "Compiling with coverage" ${CMAKE_BIN} -DCOVERAGE=True .. && sudo make install +} + +function generate_libydk { + print_msg "Generating libydk package for later testing" + cd $YDKGEN_HOME + run_test generate.py --libydk + cd gen-api/cpp/ydk/build sudo make package || true cp libydk*rpm libydk*deb /ydk-gen &> /dev/null + cd - } function run_cpp_core_test { @@ -224,8 +233,8 @@ function generate_install_specified_cpp_bundle { bundle_profile=$1 bundle_name=$2 - run_test generate.py --bundle $bundle_profile --cpp --generate-doc &> /dev/null - cd gen-api/cpp/$2/build + run_test generate.py --bundle ${bundle_profile} --cpp --generate-doc &> /dev/null + cd gen-api/cpp/${bundle_name}/build run_exec_test sudo make install cd - } @@ -370,9 +379,10 @@ function py_sanity_ydktest { function py_sanity_doc_gen { print_msg "Generating docs" - run_test generate.py --core --cpp --generate-doc &> /dev/null - run_test generate.py --core --go --generate-doc &> /dev/null - run_test generate.py --core --python --generate-doc &> /dev/null + sudo rm -rf gen-api/cpp/ydk + run_test generate.py --core --cpp --generate-doc > /dev/null + run_test generate.py --core --go --generate-doc > /dev/null + run_test generate.py --core --python --generate-doc > /dev/null } function py_sanity_ydktest_gen { From 702fce4645a7e59ea91e32022fca0cd628c9d135 Mon Sep 17 00:00:00 2001 From: Abhi Keshav Date: Wed, 21 Mar 2018 15:55:36 -0700 Subject: [PATCH 2/2] Codacy --- sdk/cpp/core/tests/test_capabilities_parser.cpp | 12 ++++++++---- sdk/python/core/tests/test_sanity_path.py | 3 +-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sdk/cpp/core/tests/test_capabilities_parser.cpp b/sdk/cpp/core/tests/test_capabilities_parser.cpp index 4d4203ca5..93322125e 100644 --- a/sdk/cpp/core/tests/test_capabilities_parser.cpp +++ b/sdk/cpp/core/tests/test_capabilities_parser.cpp @@ -101,8 +101,9 @@ TEST_CASE("test_error") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); - caps = parser.parse_yang_1_1(""); + REQUIRE(caps.size() == 0); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -110,8 +111,9 @@ TEST_CASE("test_error_1") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); - caps = parser.parse_yang_1_1(""); + REQUIRE(caps.size() == 0); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -119,8 +121,9 @@ TEST_CASE("test_error_2") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); - caps = parser.parse_yang_1_1(""); + REQUIRE(caps.size() == 0); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } @@ -128,8 +131,9 @@ TEST_CASE("test_error_3") { IetfCapabilitiesXmlParser parser{}; vector caps = parser.parse(""); - caps = parser.parse_yang_1_1(""); + REQUIRE(caps.size() == 0); + caps = parser.parse_yang_1_1(""); REQUIRE(caps.size() == 0); } diff --git a/sdk/python/core/tests/test_sanity_path.py b/sdk/python/core/tests/test_sanity_path.py index 48c19a291..2b936c40e 100644 --- a/sdk/python/core/tests/test_sanity_path.py +++ b/sdk/python/core/tests/test_sanity_path.py @@ -20,7 +20,6 @@ import unittest import logging -from ydk.errors import YPYError from ydk.path.sessions import NetconfSession from ydk.path import Codec from ydk.types import EncodingFormat @@ -176,7 +175,7 @@ def test_create_gre_tunnel_on_demand(self): crud_service = CRUDService(); crud_service.create(provider, native) - def test_anyxml(self): + def test_anyxml_action(self): expected=''' xyz