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

Enhancement #811 for C++ #812

Merged
10 commits merged into from
Jul 25, 2018
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ gen-api/
scripts
sdk/cpp/core/tests/confd/ydktest/cli-history
sdk/python/core/ydk/models/ydktest
test/gnmi*
yang/nxos*
grpc
protobuf-3.5.0
sdk/cpp/gnmi
24 changes: 19 additions & 5 deletions sdk/cpp/core/docsgen/api/ydk/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ YANG leaf, list and leaf-list

Returns size of the list.

.. cpp:function:: std::shared_ptr<Entity> pop(const std::string& key)

Removes from `YList` an entity, which has matching `key` value.

:param key: ``strings``, which represent list entity key(s).
:return: ``std::shared_ptr<Entity>`` for the removed entity, or nullptr matching `key` value is not found.

.. cpp:function:: std::shared_ptr<Entity> pop(const std::size_t item)

Removes from `YList` an entity, which has sequence number `item`. The sequence number is defined by the order in which the entity was added to the list.

:param key: ``std::size_t``, which represents entity sequential number in the list (starts from 0).
:return: ``std::shared_ptr<Entity>`` for the found entity.
:raises: `YInvalidArgumentError` exception if `item` value is out of bounds.

.. cpp:class:: YLeafList

Concrete class that represents a YANG leaf-list, to which multiple instances of data can be appended to.
Expand Down Expand Up @@ -145,7 +160,7 @@ YANG type
Example usage
~~~~~~~~~~~~~~~

Examples of instantiating and using objects of :cpp:class:`Entity<Entity>` type are shown below
Examples of how to instantiate and use objects of :cpp:class:`Entity<Entity>` type:

.. code-block:: c++
:linenos:
Expand All @@ -160,7 +175,7 @@ Examples of instantiating and using objects of :cpp:class:`Entity<Entity>` type
// Append the afi-safi to the YList instance
bgp->global->afi_safis->afi_safi.append(afi_safi);

Examples of assigning values to leafs are shown below
Examples of how to assign values to leafs:

.. code-block:: c++
:linenos:
Expand All @@ -182,7 +197,7 @@ Examples of assigning values to leafs are shown below
node->bits_value["first-position"] = true // bits
node->bits_value["second-position"] = false // bits

Examples of appending values to leaf-lists are shown below
Examples of how to append values to leaf-lists:

.. code-block:: c++
:linenos:
Expand All @@ -202,8 +217,7 @@ Examples of appending values to leaf-lists are shown below
bits_value["first-position"] = false; // bits
node->bits_values.append(bits_value); // bits


Example of accessing YList entities
Examples of how to access YList entities

.. code-block:: c++
:linenos:
Expand Down
53 changes: 23 additions & 30 deletions sdk/cpp/core/src/crud_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,44 +183,37 @@ execute_rpc(ydk::ServiceProvider & provider, vector<Entity*> & filter_list,
if (rnd == nullptr)
return result_list;

// Build mapping of entity path to entity; needed to calculate top_entity
map<string,Entity*> path_to_entity{};
for (Entity* entity : filter_list) {
string internal_key = "/" + entity->get_segment_path();
path_to_entity[internal_key] = entity;
// Build mapping of DataNode path to DataNode pointer.
// Use this mapping to retain order of filter list in results.
map<string,shared_ptr<path::DataNode>> path_to_datanode{};
for (auto dn : rnd->get_children()) {
string internal_key = dn->get_path().substr(1);
path_to_datanode[internal_key] = dn;
}

// Return all children of the root node
vector<shared_ptr<path::DataNode>> data_nodes = rnd->get_children();
for (auto dn : data_nodes) {
string internal_key = dn->get_path();
Entity* entity;
if(path_to_entity.find(internal_key) == path_to_entity.end())
{
YLOG_DEBUG("Searching for filter using yang name: {}", internal_key);
bool found = false;
for(auto e:path_to_entity)
{
if(internal_key.find(e.second->yang_name) != string::npos)
{
YLOG_DEBUG("Found filter entity: {}", e.second->yang_name);
entity = e.second;
found = true;
// Build resulting list of entities
for (Entity* entity : filter_list) {
string internal_key = entity->get_segment_path();
shared_ptr<path::DataNode> datanode = path_to_datanode[internal_key];
if (!datanode) {
YLOG_DEBUG("Searching for datanode using entity yang name '{}'", entity->yang_name);
path_to_datanode.erase(internal_key);
for (auto dn_entry : path_to_datanode) {
if (dn_entry.first.find(entity->yang_name) != string::npos && dn_entry.second) {
datanode = dn_entry.second;
break;
}
}
if(!found)
{
YLOG_ERROR("Could not find filter entity: {}", internal_key);
throw(YServiceProviderError{"Could not find filter entity " + internal_key});
}
}
else
{
entity = path_to_entity[internal_key];
if (datanode) {
result_list.push_back( read_datanode(*entity, datanode));
}
else {
YLOG_DEBUG("CRUD read operation did not return data node on entity '{}'; returning nullptr.", internal_key);
result_list.push_back(nullptr);
}
result_list.push_back( read_datanode(*entity, dn));
}

return result_list;
}

Expand Down
39 changes: 28 additions & 11 deletions sdk/cpp/core/src/netconf_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,20 +270,37 @@ get_from_list(NetconfServiceProvider& provider, DataStore source, vector<Entity*
if (result_datanode == nullptr)
return result_list;

// Build mapping of entity path to entity; needed to calculate top_entity
map<string,Entity*> path_to_entity{};
for (Entity* entity : filter_list) {
string internal_key = "/" + entity->get_segment_path();
path_to_entity[internal_key] = entity;
// Build mapping of DataNode path to DataNode pointer.
// Use this mapping to retain order of filter list in results.
map<string,shared_ptr<path::DataNode>> path_to_datanode{};
for (auto dn : result_datanode->get_children()) {
string internal_key = dn->get_path().substr(1);
path_to_datanode[internal_key] = dn;
}

// Return all children of the root node
vector<shared_ptr<path::DataNode>> data_nodes = result_datanode->get_children();
for (auto dn : data_nodes) {
string internal_key = dn->get_path();
Entity* entity = path_to_entity[internal_key];
result_list.push_back( read_datanode(*entity, dn));
// Build resulting list of entities
for (Entity* entity : filter_list) {
string internal_key = entity->get_segment_path();
shared_ptr<path::DataNode> datanode = path_to_datanode[internal_key];
if (!datanode) {
YLOG_DEBUG("Searching for datanode using entity yang name '{}'", entity->yang_name);
path_to_datanode.erase(internal_key);
for (auto dn_entry : path_to_datanode) {
if (dn_entry.first.find(entity->yang_name) != string::npos && dn_entry.second) {
datanode = dn_entry.second;
break;
}
}
}
if (datanode) {
result_list.push_back( read_datanode(*entity, datanode));
}
else {
YLOG_DEBUG("Netconf Service 'get' operation did not return data node on entity '{}'; returning nullptr.", internal_key);
result_list.push_back(nullptr);
}
}

return result_list;
}

Expand Down
40 changes: 40 additions & 0 deletions sdk/cpp/core/src/path/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,4 +312,44 @@ ydk::path::Codec::decode_rpc_output(RootSchemaNode & root_schema, const std::str
return perform_decode(rs_impl, root);
}

std::shared_ptr<ydk::path::DataNode>
ydk::path::Codec::decode_json_output(RootSchemaNode & root_schema, const std::vector<std::string> & buffer_list)
{
YLOG_DEBUG("ydk::path::Codec: Decoding JSON formatted list of payloads");

RootSchemaNodeImpl & rs_impl = get_root_schema_impl(root_schema);

ydk::path::RootDataImpl* rd = new ydk::path::RootDataImpl{rs_impl, rs_impl.m_ctx, "/"};
lyd_node* prev_sibling = nullptr;
lyd_node* first_sibling = nullptr;

for (auto buffer : buffer_list) {
rs_impl.populate_new_schemas_from_payload(buffer, ydk::EncodingFormat::JSON);

struct lyd_node *dnode = lyd_parse_mem(rs_impl.m_ctx, buffer.c_str(), LYD_JSON, LYD_OPT_TRUSTED | LYD_OPT_GET);
if (dnode == nullptr || ly_errno) {
YLOG_ERROR( "Parsing failed with message {}", ly_errmsg());
throw(YCodecError{YCodecError::Error::XML_INVAL});
}

// Attach first node to the root
if (!rd->m_node) {
rd->m_node = dnode;
first_sibling = dnode;
}

// Populate children map and connect siblings
rd->child_map.insert(std::make_pair(dnode, std::make_shared<ydk::path::DataNodeImpl>(rd, dnode, nullptr)));

if (prev_sibling) {
prev_sibling->next = dnode;
dnode->prev = prev_sibling;
}
prev_sibling = dnode;
first_sibling->prev = dnode;
}

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

#undef SLASH_CHAR
1 change: 1 addition & 0 deletions sdk/cpp/core/src/path_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ class Codec
///
std::shared_ptr<DataNode> decode(RootSchemaNode & root_schema, const std::string& buffer, EncodingFormat format);
std::shared_ptr<DataNode> decode_rpc_output(RootSchemaNode & root_schema, const std::string& buffer, const std:: string & rpc_path, EncodingFormat format);
std::shared_ptr<ydk::path::DataNode> decode_json_output(RootSchemaNode & root_schema, const std::vector<std::string> & buffer_list);
};

///
Expand Down
3 changes: 3 additions & 0 deletions sdk/cpp/core/src/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ class YList
void extend(std::initializer_list<std::shared_ptr<Entity>> ep_list);
std::string build_key(std::shared_ptr<Entity> ep);

std::shared_ptr<Entity> pop(const std::string& key);
std::shared_ptr<Entity> pop(const std::size_t item);

private:
std::vector<std::string> ylist_key_names;
std::map<std::string,std::shared_ptr<Entity>> entity_map;
Expand Down
32 changes: 32 additions & 0 deletions sdk/cpp/core/src/value_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,36 @@ YList::len() const
return key_vector.size();
}

shared_ptr<Entity>
YList::pop(const string& key)
{
for (vector<string>::iterator it = key_vector.begin() ; it != key_vector.end(); ++it) {
if (*it == key) {
shared_ptr<Entity> found = entity_map[key];
entity_map.erase(key);
key_vector.erase(it);
return found;
}
}
return nullptr;
}

shared_ptr<Entity>
YList::pop(const std::size_t item)
{
if (item < key_vector.size()) {
vector<string>::iterator it = key_vector.begin();
for (size_t i = 0; i < item; i++) ++it;
shared_ptr<Entity> found = entity_map[*it];
entity_map.erase(*it);
key_vector.erase(it);
return found;
}
else {
YLOG_ERROR("Index value {} is out of range [0,{}]", item, key_vector.size());
throw(YInvalidArgumentError{"Index value is out of range"});
}
return nullptr;
}

}
46 changes: 36 additions & 10 deletions sdk/cpp/core/tests/test_codec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,44 @@ TEST_CASE( "test_submodule_feature" )
auto xml_rt = s.encode(*real_dn, ydk::EncodingFormat::XML, true);
REQUIRE(xml == xml_rt);
}
/*
TEST_CASE( "test_codec_ok" )

TEST_CASE( "test_decode_multiple_json" )
{
ydk::path::Codec s{};
std::string searchdir{TEST_HOME};
mock::MockSession sp{searchdir, test_openconfig};

auto & schema = sp.get_root_schema();

string pl2 = "<ok/>";
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=="<ok/>");
}*/
std::string json_int_payload = R"({
"openconfig-interfaces:interfaces": {
"interface": [
{
"name": "Loopback10",
"config": {
"name": "Loopback10"
}
}
]
}
}
)";
std::string json_bgp_payload = R"({
"openconfig-bgp:bgp": {
"global": {
"config": {
"as": 65172
}
}
}
}
)";
std::vector<std::string> payload_list = {json_int_payload, json_bgp_payload};

ydk::path::Codec s{};

auto rdn = s.decode_json_output(schema, payload_list);
REQUIRE(rdn != nullptr);

auto dn = dynamic_cast<ydk::path::DataNode*> (rdn.get());
auto json_str = s.encode(*dn, EncodingFormat::JSON, true);
REQUIRE(json_str == json_int_payload + json_bgp_payload);
}
25 changes: 23 additions & 2 deletions sdk/cpp/core/tests/test_entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,21 +463,42 @@ TEST_CASE("test_ylist")
auto keys = ylist_0.keys();
REQUIRE(vector_to_string(keys) == R"("1000000", "1000001")");

auto ep = ylist_0.pop(1);
REQUIRE(ep != nullptr);
auto test_entity = dynamic_cast<TestEntity*> (ep.get());
REQUIRE(test_entity->name == test2->name);
REQUIRE(ylist_0.len() == 1);

ep = ylist_0.pop(0);
REQUIRE(ep != nullptr);
test_entity = dynamic_cast<TestEntity*> (ep.get());
REQUIRE(test_entity->name == test1->name);
REQUIRE(ylist_0.len() == 0);

ylist_1.extend({test1, test2});
REQUIRE(ylist_1.len() == 2);

keys = ylist_1.keys();
REQUIRE(vector_to_string(keys) == R"("test1", "test2")");

auto ep = ylist_1[0];
ep = ylist_1[0];
REQUIRE(ep != nullptr);
auto test_entity = dynamic_cast<TestEntity*> (ep.get());
test_entity = dynamic_cast<TestEntity*> (ep.get());
REQUIRE(test_entity->name == test1->name);

ep = ylist_1["test2"];
REQUIRE(ep != nullptr);
test_entity = dynamic_cast<TestEntity*> (ep.get());
REQUIRE(test_entity->name == test2->name);

ep = ylist_1.pop("test1");
REQUIRE(ep != nullptr);
REQUIRE(ylist_1.len() == 1);
test_entity = dynamic_cast<TestEntity*> (ep.get());
REQUIRE(test_entity->name == test1->name);

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

//TODO Test for issue #800 to be resolved
Expand Down
6 changes: 4 additions & 2 deletions sdk/cpp/tests/test_netconf_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@ TEST_CASE("get_edit_copy_config")

vector<Entity*> copy_config_list{};
for (auto ent : get_config_list) {
//print_entity(ent, provider.get_session().get_root_schema());
copy_config_list.push_back(ent.get());
if (ent) {
//print_entity(ent, provider.get_session().get_root_schema());
copy_config_list.push_back(ent.get());
}
}

// Copy config to candidate
Expand Down
Loading