Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support any rpc in path API #498

Merged
2 commits merged into from Jul 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions sdk/cpp/core/src/netconf_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ static std::shared_ptr<path::DataNode> handle_edit_reply(string reply, NetconfCl
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, string data_tag, string data_value);
static std::shared_ptr<path::DataNode> handle_read_reply(string reply, path::RootSchemaNode & root_schema);
static string get_netconf_payload(path::DataNode & input, const string & data_tag, const string & data_value);
static std::shared_ptr<path::DataNode> handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::Rpc & rpc);

const char* CANDIDATE = "urn:ietf:params:netconf:capability:candidate:1.0";
const string PROTOCOL_SSH = "ssh";
Expand Down Expand Up @@ -154,7 +154,7 @@ std::shared_ptr<path::DataNode> NetconfServiceProvider::handle_read(path::Rpc& y
std::string filter_value = get_filter_payload(ydk_rpc);

string netconf_payload = get_netconf_payload(input, "filter", filter_value);
return handle_read_reply(execute_payload(netconf_payload), *root_schema );
return handle_rpc_output(execute_payload(netconf_payload), *root_schema, *netconf_rpc );
}

std::shared_ptr<path::DataNode> NetconfServiceProvider::handle_edit(path::Rpc& ydk_rpc, path::Annotation annotation) const
Expand Down Expand Up @@ -187,9 +187,9 @@ std::shared_ptr<path::DataNode> NetconfServiceProvider::handle_netconf_operation
YLOG_INFO("\n");

std::string reply = execute_payload(netconf_payload);
if (ydk_rpc.get_schema_node().get_path().find("get") != string::npos or ydk_rpc.get_schema_node().get_path().find("get-config") != string::npos)
if (ydk_rpc.has_output_node())
{
return handle_read_reply(reply, *root_schema);
return handle_rpc_output(reply, *root_schema, ydk_rpc);
}
if(reply.find("<ok/>") == std::string::npos)
{
Expand Down Expand Up @@ -221,14 +221,9 @@ std::shared_ptr<path::DataNode> NetconfServiceProvider::invoke(path::Rpc& rpc) c
{
return handle_read(rpc);
}
else if(rpc_schema->get_path().find("ietf-netconf:")!= string::npos)
{
return handle_netconf_operation(rpc);
}
else
{
YLOG_ERROR("rpc is not supported");
throw(YCPPOperationNotSupportedError{"rpc is not supported!"});
return handle_netconf_operation(rpc);
}

return datanode;
Expand All @@ -238,7 +233,7 @@ std::string NetconfServiceProvider::execute_payload(const std::string & payload)
{
std::string reply = client->execute_payload(payload);
YLOG_INFO("=============Reply payload received from device=============");
YLOG_INFO(reply.c_str());
YLOG_INFO("{}", reply.c_str());
YLOG_INFO("\n");
return reply;
}
Expand Down Expand Up @@ -343,7 +338,7 @@ static string get_filter_payload(path::Rpc & ydk_rpc)
return datanode->get_value();
}

static string get_netconf_payload(path::DataNode & input, string data_tag, string data_value)
static string get_netconf_payload(path::DataNode & input, const string & data_tag, const string & data_value)
{
path::Codec codec_service{};
input.create_datanode(data_tag, data_value);
Expand Down Expand Up @@ -387,7 +382,13 @@ static std::shared_ptr<path::DataNode> handle_edit_reply(string reply, NetconfCl
return nullptr;
}

static std::shared_ptr<path::DataNode> handle_read_reply(string reply, path::RootSchemaNode & root_schema)
static bool is_netconf_get_rpc(path::Rpc & rpc)
{
return (rpc.get_schema_node().get_path() == "/ietf-netconf:get"
or rpc.get_schema_node().get_path() == "/ietf-netconf:get-config");
}

static std::shared_ptr<path::DataNode> handle_netconf_get_output(const string & reply, path::RootSchemaNode & root_schema)
{
path::Codec codec_service{};
auto empty_data = reply.find("<data/>");
Expand All @@ -396,7 +397,7 @@ static std::shared_ptr<path::DataNode> handle_read_reply(string reply, path::Roo
YLOG_INFO( "Found empty data tag");
return nullptr;
}

auto data_start = reply.find("<data>");
if(data_start == std::string::npos)
{
Expand All @@ -410,18 +411,43 @@ static std::shared_ptr<path::DataNode> handle_read_reply(string reply, path::Roo
YLOG_ERROR( "No end data tag found in reply sent by device {}", reply);
throw(YCPPError{"No end data tag found"});
}

string data = reply.substr(data_start, data_end-data_start);

auto datanode = std::shared_ptr<path::DataNode>(codec_service.decode(root_schema, data, EncodingFormat::XML));

if(!datanode){
YLOG_ERROR( "Codec service failed to decode datanode");
throw(YCPPError{"Problems deserializing output"});
}
return datanode;
}

static std::shared_ptr<path::DataNode> handle_rpc_output(const string & reply, path::RootSchemaNode & root_schema, path::Rpc & rpc)
{
if (is_netconf_get_rpc(rpc))
{
return handle_netconf_get_output(reply, root_schema);
}

path::Codec codec_service{};

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_end -= 1;
string data = reply.substr(data_start, data_end-data_start+1);

std::shared_ptr<path::DataNode> datanode = codec_service.decode_rpc_output(
root_schema,
data,
rpc.get_schema_node().get_path(),
EncodingFormat::XML);
return datanode;
}

static string get_read_rpc_name(bool config)
{
if(config)
Expand Down
6 changes: 0 additions & 6 deletions sdk/cpp/core/src/netconf_ssh_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,6 @@ nc_rpc* NetconfSSHClient::build_rpc_request(const string & payload)
YLOG_ERROR("Could not build rpc payload: {}", payload );
throw(YCPPClientError{"Could not build payload"});
}
else if(NC_RPC_UNKNOWN==nc_rpc_get_type(rpc))
{
nc_rpc_free(rpc);
YLOG_ERROR("Rpc type is unknown");
throw(YCPPClientError{"Could not build payload"});
}
return rpc;
}

Expand Down
76 changes: 59 additions & 17 deletions sdk/cpp/core/src/path/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,42 +232,84 @@ ydk::path::Codec::encode(const ydk::path::DataNode& dn, ydk::EncodingFormat form

}

std::shared_ptr<ydk::path::DataNode>
ydk::path::Codec::decode(const RootSchemaNode & root_schema, const std::string& buffer, EncodingFormat format)
static LYD_FORMAT get_ly_format(ydk::EncodingFormat format)
{
LYD_FORMAT scheme = LYD_XML;
if (format == EncodingFormat::JSON)
if (format == ydk::EncodingFormat::JSON)
{
YLOG_DEBUG("Performing decode operation on JSON");
ydk::YLOG_DEBUG("Performing decode operation on JSON");
scheme = LYD_JSON;
}
else
{
YLOG_DEBUG("Performing decode operation on XML");
ydk::YLOG_DEBUG("Performing decode operation on XML");
}
return scheme;
}

static const ydk::path::RootSchemaNodeImpl & get_root_schema_impl(const ydk::path::RootSchemaNode & root_schema)
{
const ydk::path::RootSchemaNodeImpl & rs_impl = dynamic_cast<const ydk::path::RootSchemaNodeImpl &>(root_schema);
return rs_impl;
}

const RootSchemaNodeImpl & rs_impl = dynamic_cast<const RootSchemaNodeImpl &>(root_schema);
static std::shared_ptr<ydk::path::DataNode> perform_decode(const ydk::path::RootSchemaNodeImpl & rs_impl, struct lyd_node *root)
{
ydk::YLOG_DEBUG("Performing decode operation");
ydk::path::RootDataImpl* rd = new ydk::path::RootDataImpl{rs_impl, rs_impl.m_ctx, "/"};
rd->m_node = root;

struct lyd_node *root = lyd_parse_mem(rs_impl.m_ctx, buffer.c_str(), scheme, LYD_OPT_TRUSTED | LYD_OPT_GET);
if( root == nullptr || ly_errno )
struct lyd_node* dnode = rd->m_node;
do
{
rd->child_map.insert(std::make_pair(rd->m_node, std::make_shared<ydk::path::DataNodeImpl>(rd, rd->m_node, nullptr)));
dnode = dnode->next;
} while(dnode && dnode != nullptr && dnode != root);

return std::shared_ptr<ydk::path::DataNode>(rd);
}

std::shared_ptr<ydk::path::DataNode>
ydk::path::Codec::decode(const RootSchemaNode & root_schema, const std::string& buffer, EncodingFormat format)
{
const RootSchemaNodeImpl & rs_impl = get_root_schema_impl(root_schema);
struct lyd_node *root = lyd_parse_mem(rs_impl.m_ctx, buffer.c_str(),
get_ly_format(format), LYD_OPT_TRUSTED | LYD_OPT_GET);

if( root == nullptr || ly_errno )
{
YLOG_ERROR( "Parsing failed with message {}", ly_errmsg());
throw(YCPPCodecError{YCPPCodecError::Error::XML_INVAL});
}
return perform_decode(rs_impl, root);
}

static const struct lyd_node* create_ly_rpc_node(const 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 )
{
ydk::YLOG_ERROR( "Parsing failed with message {}", ly_errmsg());
throw(ydk::path::YCPPCodecError{ydk::path::YCPPCodecError::Error::XML_INVAL});
}
return rpc;
}

YLOG_DEBUG("Performing decode operation");
RootDataImpl* rd = new RootDataImpl{rs_impl, rs_impl.m_ctx, "/"};
rd->m_node = root;
std::shared_ptr<ydk::path::DataNode>
ydk::path::Codec::decode_rpc_output(const RootSchemaNode & root_schema, const std::string& buffer,
const std::string & rpc_path, EncodingFormat format)
{
const RootSchemaNodeImpl & rs_impl = get_root_schema_impl(root_schema);
const struct lyd_node* rpc = create_ly_rpc_node(rs_impl, rpc_path);

struct lyd_node* dnode = rd->m_node;
do
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);
if( root == nullptr || ly_errno )
{
rd->child_map.insert(std::make_pair(rd->m_node, std::make_shared<DataNodeImpl>(rd, rd->m_node, nullptr)));
dnode = dnode->next;
} while(dnode && dnode != nullptr && dnode != root);
YLOG_ERROR( "Parsing failed with message {}", ly_errmsg());
throw(YCPPCodecError{YCPPCodecError::Error::XML_INVAL});
}

return std::shared_ptr<ydk::path::DataNode>(rd);
return perform_decode(rs_impl, root);
}
#undef SLASH_CHAR
4 changes: 3 additions & 1 deletion sdk/cpp/core/src/path/path_private.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,11 @@ namespace ydk {

SchemaNode& get_schema_node() const;

bool has_output_node() const;


SchemaNodeImpl& schema_node;
std::unique_ptr<DataNode> data_node;
std::unique_ptr<DataNodeImpl> data_node;

private:
const std::shared_ptr<RepositoryPtr> m_priv_repo;
Expand Down
2 changes: 1 addition & 1 deletion sdk/cpp/core/src/path/repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ void libyang_log_callback(LY_LOG_LEVEL level, const char *msg, const char *path)
case LY_LLWRN:
case LY_LLVRB:
case LY_LLDBG:
YLOG_DEBUG("Libyang TRACE: {}", error_message.str());
YLOG_DEBUG("Libyang DEBUG: {}", error_message.str());
break;
}
}
Expand Down
49 changes: 47 additions & 2 deletions sdk/cpp/core/src/path/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ ydk::path::RpcImpl::RpcImpl(SchemaNodeImpl& sn, struct ly_ctx* ctx, const std::s
struct lyd_node* dnode = lyd_new_path(nullptr, ctx, sn.get_path().c_str(), (void*)"", LYD_ANYDATA_SXML, 0);

if(!dnode){
YLOG_ERROR("Cannot find DataNode with path {}", sn.get_path());
throw(YCPPIllegalStateError{"Illegal state"});
YLOG_ERROR("Cannot find RPC with path {}", sn.get_path());
throw(YCPPModelError{"Invalid RPC"});
}

data_node = std::make_unique<DataNodeImpl>(nullptr, dnode, m_priv_repo);
Expand Down Expand Up @@ -73,3 +73,48 @@ ydk::path::RpcImpl::get_schema_node() const
{
return schema_node;
}

static bool is_output(const std::string & str)
{
return str == "output";
}

static bool is_part_of_output(lys_node* node_result)
{
lys_node* parent = node_result->parent;
if(parent == NULL)
{
return is_output(node_result->name);
}
if(parent->parent == NULL)
{
return is_output(parent->name);
}
else
{
while(parent->parent)
{
if(is_output(parent->name))
return true;
parent = parent->parent;
}
}
return false;
}

bool ydk::path::RpcImpl::has_output_node() const
{
ly_verb(LY_LLSILENT); //turn off libyang logging at the beginning
ly_set* result_set = lys_find_xpath(data_node->m_node->schema, "*", LYS_FIND_OUTPUT);
ly_verb(LY_LLVRB); // enable libyang logging after find has completed
if(result_set && result_set->number > 0)
{
for(size_t i=0; i < result_set->number; i++)
{
lys_node* node_result = result_set->set.s[i];
if(is_part_of_output(node_result))
return true;
}
}
return false;
}
3 changes: 3 additions & 0 deletions sdk/cpp/core/src/path_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ class Codec
/// @throws YCPPInvalidArgumentError if the arguments are invalid.
///
std::shared_ptr<DataNode> decode(const RootSchemaNode & root_schema, const std::string& buffer, EncodingFormat format);
std::shared_ptr<DataNode> decode_rpc_output(const RootSchemaNode & root_schema, const std::string& buffer, const std:: string & rpc_path, EncodingFormat format);
};

///
Expand Down Expand Up @@ -1037,6 +1038,8 @@ class Rpc
///
virtual DataNode& get_input_node() const = 0;

virtual bool has_output_node() const = 0;

///
/// @brief return the SchemaNode associated with this rpc
///
Expand Down
1 change: 1 addition & 0 deletions sdk/cpp/core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(YDK_UNITTEST_TARGET_NAME "ydk_core_test")

set(core_tests_src bgptest.cpp
core_test.cpp
test_codec.cpp
test_entity.cpp
test_value.cpp
test_value_list.cpp
Expand Down
Loading