Skip to content

Commit

Permalink
Resolved #871 to 0.8.3
Browse files Browse the repository at this point in the history
  • Loading branch information
ygorelik authored and Abhi Keshav committed May 15, 2019
1 parent e7cafde commit e933126
Show file tree
Hide file tree
Showing 29 changed files with 229 additions and 67 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ matrix:
- os: osx
osx_image: xcode8.3
language: generic
go: 1.9
- os: osx
env: GNMI=true
osx_image: xcode8.3
language: generic
go: 1.9
# - env: DOCKER=true OS_TYPE=centos OS_VERSION=centos7 PYTHON_VERSION=2.7
# - env: DOCKER=true OS_TYPE=centos OS_VERSION=centos7 PYTHON_VERSION=2.7 GNMI=true
- env: DOCKER=true OS_TYPE=centos OS_VERSION=centos7 PYTHON_VERSION=3.6
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### 2019-05-15 version 0.8.3

#### Resolved GitHub issues
* YDK return value of YANG action missing some attributes ([#871](https://github.com/CiscoDevNet/ydk-gen/issues/871))
* Duplicate code in generated cisco-ios-xe Go bundle ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891))
* Memory leaks in YDK C++ core ([#899](https://github.com/CiscoDevNet/ydk-gen/issues/899))

Expand Down
1 change: 1 addition & 0 deletions sdk/cpp/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### 2019-05-15 version 0.8.3

#### Resolved GitHub issues
* YDK return value of YANG action missing some attributes ([#871](https://github.com/CiscoDevNet/ydk-gen/issues/871))
* Duplicate code in generated cisco-ios-xe Go bundle ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891))
* Memory leaks in YDK C++ core ([#899](https://github.com/CiscoDevNet/ydk-gen/issues/899))

Expand Down
15 changes: 11 additions & 4 deletions sdk/cpp/core/docsgen/api/path/sessions/netconf_session.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,33 @@ NetconfSession
:param on_demand: Enable on demand downloading by default.
:param timeout: The timeout in microseconds, -1 for infinite timeout, 0 for non-blocking

.. cpp:function:: virtual path::RootSchemaNode& get_root_schema() const
.. cpp:function:: path::RootSchemaNode& get_root_schema() const

Returns the :cpp:class:`RootSchemaNode<path::RootSchemaNode>` tree supported by this instance of the ``NetconfSession``.

:return: Pointer to the :cpp:class:`RootSchemaNode<path::RootSchemaNode>` or ``nullptr`` if one could not be created.

.. cpp:function:: virtual std::shared_ptr<path::DataNode> invoke(path::Rpc& rpc) const
.. cpp:function:: std::shared_ptr<path::DataNode> invoke(path::Rpc& rpc) const

Invokes or executes the given rpc and returns a :cpp:class:`DataNode<DataNode>` pointer if the Rpc has an output modelled in YANG.
Invokes or executes the given RPC and returns a :cpp:class:`DataNode<DataNode>` pointer if the Rpc has an output modelled in YANG.

:param rpc: Reference to the :cpp:class:`Rpc<Rpc>` node.
:return: Shared pointer to the :cpp:class:`DataNode<DataNode>` representing the output.

.. cpp:function:: virtual std::shared_ptr<path::DataNode> invoke(path::DataNode& datanode) const
.. cpp:function:: std::shared_ptr<path::DataNode> invoke(path::DataNode& datanode) const

Invokes or executes the given DataNode containing a YANG 1.1 action and returns a :cpp:class:`DataNode<DataNode>` pointer if the action has an output modeled in YANG.

:param datanode: Reference to the :cpp:class:`DataNode<DataNode>` node.
:return: Pointer to the :cpp:class:`DataNode<DataNode>` representing the output.

.. cpp:function:: std::string execute_netconf_operation(path::Rpc& rpc) const

Sends the specified RPC to device (similar to `invoke` function) and returns device response in XML encoded string.

:param rpc: Reference to the :cpp:class:`Rpc<Rpc>` node.
:return: std::string, which represents the RPC output.

.. cpp:function:: std::vector<std::string> get_capabilities() const

Returns a vector of the client's capabilities
Expand Down
6 changes: 3 additions & 3 deletions sdk/cpp/core/docsgen/api/path/sessions/restconf_session.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ RestconfSession
:param config_url_root: To provider backwards compatibility with older drafts of restconf RFC, this can be "/config" or "/data" (which is the default)
:param state_url_root: To provider backwards compatibility with older drafts of restconf RFC, this can be "/operational" or "/data" (which is the default)

.. cpp:function:: virtual path::RootSchemaNode& get_root_schema() const
.. cpp:function:: path::RootSchemaNode& get_root_schema() const

Returns the :cpp:class:`RootSchemaNode<path::RootSchemaNode>` tree supported by this instance of the ``RestconfSession``.

:return: Reference to the :cpp:class:`RootSchemaNode<path::RootSchemaNode>` or ``nullptr`` if one could not be created.

.. cpp:function:: virtual std::shared_ptr<DataNode> invoke(path::Rpc& rpc) const
.. cpp:function:: std::shared_ptr<DataNode> invoke(path::Rpc& rpc) const

Invokes or executes the given rpc and returns a :cpp:class:`DataNode<DataNode>` pointer if the Rpc has an output modeled in YANG.

:param rpc: Reference to the :cpp:class:`Rpc<Rpc>` node.
:return: Pointer to the :cpp:class:`DataNode<DataNode>` representing the output.

.. cpp:function:: virtual ~RestconfSession()
.. cpp:function:: ~RestconfSession()
106 changes: 84 additions & 22 deletions sdk/cpp/core/src/netconf_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ 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<path::DataNode> handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, const string& rpc_path);
static shared_ptr<path::DataNode> handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::DataNode & input);
static void check_rpc_reply_for_error(const string& reply);
static void log_rpc_request(const string& payload);

Expand All @@ -74,6 +74,7 @@ const string PROTOCOL_TCP = "tcp";
static bool is_netconf_get_rpc(path::Rpc & rpc);
static shared_ptr<path::DataNode> netconf_output_to_datanode(const string & data, path::RootSchemaNode & root_schema);
static string get_netconf_output(const string & reply);
static string extract_rpc_output(const string & reply);

NetconfSession::NetconfSession(path::Repository & repo,
const string& address,
Expand Down Expand Up @@ -301,7 +302,7 @@ shared_ptr<path::DataNode> NetconfSession::handle_netconf_operation(path::Rpc& y
}
else if (ydk_rpc.has_output_node())
{
return handle_rpc_output(reply, *root_schema, ydk_rpc.get_input_node().get_path());
return handle_rpc_output(reply, *root_schema, ydk_rpc.get_input_node());
}
return nullptr;
}
Expand All @@ -322,11 +323,9 @@ std::string NetconfSession::execute_netconf_operation(path::Rpc& ydk_rpc) const
{
return get_netconf_output(reply);
}
else if (ydk_rpc.has_output_node())
{
handle_rpc_output(reply, *root_schema, ydk_rpc.get_input_node().get_path());
else {
return extract_rpc_output(reply);
}
return {};
}

shared_ptr<path::DataNode> NetconfSession::invoke(path::DataNode& datanode) const
Expand All @@ -344,7 +343,7 @@ shared_ptr<path::DataNode> NetconfSession::invoke(path::DataNode& datanode) cons
string reply = execute_payload(netconf_payload);
check_rpc_reply_for_error(reply);

return handle_rpc_output(reply, *root_schema, datanode.get_action_node_path());
return handle_rpc_output(reply, *root_schema, datanode);
}

shared_ptr<path::DataNode> NetconfSession::invoke(path::Rpc& rpc) const
Expand Down Expand Up @@ -379,7 +378,7 @@ shared_ptr<path::DataNode> NetconfSession::invoke(path::Rpc& rpc) const
string NetconfSession::execute_payload(const string & payload) const
{
string reply = client->execute_payload(payload);
YLOG_INFO("============= Reply RPC received from device =============\n{}", reply);
YLOG_INFO("============= Received RPC from device =============\n{}", reply);
return reply;
}

Expand Down Expand Up @@ -559,10 +558,10 @@ static shared_ptr<path::DataNode> handle_crud_edit_reply(string reply, NetconfCl
//need to send the commit request
string commit_payload = get_commit_rpc_payload();

YLOG_INFO("============= Executing commit =============\n{}\n", commit_payload);
YLOG_INFO("============= Executing commit =============\n{}", commit_payload);
reply = client.execute_payload(commit_payload);

YLOG_INFO("============= Reply RPC received from device =============\n{}", reply);
YLOG_INFO("============= RPC received from device =============\n{}", reply);
if(reply.find("<ok/>") == string::npos)
{
YLOG_ERROR("RPC error occurred: {}", reply);
Expand Down Expand Up @@ -618,24 +617,87 @@ static shared_ptr<path::DataNode> netconf_output_to_datanode(const string & data
return datanode;
}

static shared_ptr<path::DataNode> handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, const string& rpc_path)
static string
extract_rpc_data(const string & reply, const string & start_tag, const string & end_tag, bool first_tag=false)
{
auto data_start = reply.find(start_tag);
auto data_end = reply.rfind(end_tag);
if (data_start == string::npos || data_end == string::npos || (first_tag && data_start > 0)) {
return reply;
}
if (start_tag.find("<") == 0 && start_tag.find("<!") != 0) {
auto data_start_end = reply.find(">", data_start);
data_start = data_start_end + 1;
}
else {
data_start += start_tag.length();
}
string data = trim( reply.substr(data_start, data_end - data_start) );
return data;
}

static string
extract_rpc_output(const string & reply)
{
path::Codec codec_service{};
string rpc_output = extract_rpc_data(reply, "<rpc-reply ", "</rpc-reply>");
if (rpc_output.length() == reply.length()) {
// Try with Netconf namespace prefix
rpc_output = extract_rpc_data(reply, "<nc:rpc-reply ", "</nc:rpc-reply>");
}

string reply_data = rpc_output;
rpc_output = extract_rpc_data(reply_data, "<data", "</data>", true);
if (rpc_output.length() == reply_data.length()) {
rpc_output = extract_rpc_data(reply_data, "<nc:data", "</nc:data>", true);
}

rpc_output = extract_rpc_data(rpc_output, "<![CDATA[", "]]>", true);
return rpc_output;
}

shared_ptr<path::DataNode>
NetconfSession::handle_action_rpc_output(const string & reply, path::DataNode& action_dn)
{
return handle_rpc_output(reply, *root_schema, action_dn);
}

static shared_ptr<path::DataNode>
handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::DataNode& input_dn)
{
path::Codec codec_service{};

string data = extract_rpc_data(reply, "<rpc-reply ", "</rpc-reply>");
if (data.length() == reply.length()) {
// Try with Netconf namespace prefix
data = extract_rpc_data(reply, "<nc:rpc-reply ", "</nc:rpc-reply>");
}

string data_node_path = input_dn.get_action_node_path();
if (data_node_path.empty())
{
if(reply.find("<ok/>") != string::npos)
return nullptr;

auto data_start = reply.find("<rpc-reply ");
auto data_end = reply.find("</rpc-reply>", data_start);
//need to find the end of the "<rpc-reply start tag
auto data_start_end = reply.find(">", data_start);
data_start = data_start_end + 1;
data_node_path = input_dn.get_path();
}
else {
if (reply.find("<data/>") != string::npos || reply.find("<nc:data/>") != string::npos)
{
YLOG_INFO( "Found empty data tag");
return nullptr;
}

string data = reply.substr(data_start, data_end - data_start);
if(data.find("<ok/>") != string::npos)
return nullptr;
string rpc_output = data;
data = extract_rpc_data(rpc_output, "<data", "</data>", true);
if (data.length() == reply.length()) {
data = extract_rpc_data(reply, "<nc:data", "</nc:data>", true);
}
}

shared_ptr<path::DataNode> datanode = codec_service.decode_rpc_output(
root_schema,
data,
rpc_path,
data_node_path,
EncodingFormat::XML);
return datanode;
}
Expand Down Expand Up @@ -680,7 +742,7 @@ static void check_rpc_reply_for_error(const string& reply)

static void log_rpc_request(const string& payload)
{
YLOG_INFO("============= Generated RPC to send to device =============\n{}\n", payload);
YLOG_INFO("============= Sending RPC to device =============\n{}", payload);
}

} //namespace path
Expand Down
10 changes: 4 additions & 6 deletions sdk/cpp/core/src/path/data_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ ydk::path::DataNodeImpl::get_path() const
void
ydk::path::DataNodeImpl::populate_new_schemas_from_path(const std::string& path)
{
if (path.empty()) return;
YLOG_DEBUG("Populating schema for '{}'", path);
check_ly_schema_node_for_path(m_node, path);
auto snode = reinterpret_cast<SchemaNodeImpl*>(m_node->schema->priv);
snode->populate_new_schemas_from_path(path);
Expand Down Expand Up @@ -139,13 +141,9 @@ ydk::path::DataNodeImpl::create_datanode(const std::string& path, const std::str
YLOG_DEBUG("Replacing value '{}' with '{}'", value, v);
}
}

YLOG_DEBUG("Populating schemas for path '{}'", path);
populate_new_schemas_from_path(path);
if (!v.empty()) {
YLOG_DEBUG("Populating schemas for value '{}'", v);
populate_new_schemas_from_path(v);
}
populate_new_schemas_from_path(v);

return create_helper(path, v);
}

Expand Down
5 changes: 2 additions & 3 deletions sdk/cpp/core/src/path/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ ydk::path::Codec::encode(const ydk::path::DataNode& dn, ydk::EncodingFormat form
throw(ydk::YInvalidArgumentError{"No data in data node"});
}
char* buffer = nullptr;
//lyd_node* temp_node = m_node->prev->next; m_node->prev->next = nullptr; // Fixing libyang bug in printer_json
if(!lyd_print_mem(&buffer, m_node, scheme, (pretty ? LYP_FORMAT : 0)|LYP_WD_ALL|LYP_KEEPEMPTYCONT)) {
if(!buffer)
{
Expand All @@ -269,7 +268,6 @@ ydk::path::Codec::encode(const ydk::path::DataNode& dn, ydk::EncodingFormat form
ret = buffer;
std::free(buffer);
}
//m_node->prev->next = temp_node; // Fixing libyang bug in printer_json
}
return ret;
}
Expand Down Expand Up @@ -310,8 +308,9 @@ ydk::path::Codec::decode_rpc_output(RootSchemaNode & root_schema, const std::str
YLOG_ERROR( "Parsing failed with message {}", ly_errmsg());
throw(YCodecError{YCodecError::Error::XML_INVAL});
}
auto dn = perform_decode(rs_impl, root);
if (rpc) lyd_free(rpc);
return perform_decode(rs_impl, root);
return dn;
}

std::shared_ptr<ydk::path::DataNode>
Expand Down
1 change: 0 additions & 1 deletion sdk/cpp/core/src/path/repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ ydk::path::RepositoryPtr::get_new_ly_modules_from_path(ly_ctx* ctx,
const std::string& path,
const std::unordered_map<std::string, path::Capability>& lookup_table)
{
//YLOG_DEBUG("Getting new modules from '{}'", path);
auto module_names = path::segmentalize_module_names(path);
return get_new_ly_modules_from_lookup(ctx, module_names, lookup_table);
}
Expand Down
6 changes: 4 additions & 2 deletions sdk/cpp/core/src/path/root_schema_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,10 @@ ydk::path::RootSchemaNodeImpl::populate_new_schemas_from_payload(const std::stri
}

void
ydk::path::RootSchemaNodeImpl::populate_new_schemas_from_path(const std::string& path) {
YLOG_DEBUG("Getting new modules for '{}'", path);
ydk::path::RootSchemaNodeImpl::populate_new_schemas_from_path(const std::string& path)
{
if (path.empty()) return;
YLOG_DEBUG("Getting new modules for path '{}'", path);
auto new_modules = m_priv_repo->get_new_ly_modules_from_path(m_ctx, path, m_name_namespace_lookup);
populate_new_schemas(new_modules);
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/cpp/core/src/path/schema_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ ydk::path::SchemaNodeImpl::get_root() const noexcept

void
ydk::path::SchemaNodeImpl::populate_new_schemas_from_path(const string& path) {
if (path.empty()) return;
YLOG_DEBUG("Looking to populate schemas for '{}'", path);
auto snode = const_cast<SchemaNode*>(&get_root());
auto rsnode = reinterpret_cast<RootSchemaNodeImpl*>(snode);
//YLOG_DEBUG("Ready to populate schemas for {}", path);
rsnode->populate_new_schemas_from_path(path);
}

Expand Down
4 changes: 3 additions & 1 deletion sdk/cpp/core/src/path_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "errors.hpp"
#include "types.hpp"
#include "validation_service.hpp"
#include "path_api.hpp"

namespace ydk {

Expand Down Expand Up @@ -1085,6 +1084,9 @@ class NetconfSession : public Session {
std::vector<std::string> get_capabilities() const;
std::string execute_netconf_operation(Rpc& netconf_rpc) const;

// This function is for YDK internal tests only
std::shared_ptr<path::DataNode> handle_action_rpc_output(const std::string & rpc_reply, path::DataNode& action_dn);

private:
std::vector<std::string> get_yang_1_1_capabilities() const;
std::shared_ptr<DataNode> handle_crud_edit(
Expand Down
4 changes: 4 additions & 0 deletions sdk/cpp/core/tests/test_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ TEST_CASE("test_ylist")

ep = ylist_1.pop("test1");
REQUIRE(ep == nullptr);

delete list_holder;
}

//TODO Test for issue #800 to be resolved
Expand All @@ -520,4 +522,6 @@ TEST_CASE("test_ylist")
//
// auto ep = ylist["test1"];
// REQUIRE(ep != nullptr);
//
// delete list_holder;
//}
Loading

0 comments on commit e933126

Please sign in to comment.