static unsigned rw_pb_create_field_indices(const rw_yang_pb_msgdesc_t* ypbc_msgdesc, YangNode* ynode, std::vector<int>& fields) { RW_ASSERT(ypbc_msgdesc); RW_ASSERT(ynode); auto ypbc_fdescs = ypbc_msgdesc->ypbc_flddescs; unsigned nkeys = 0; if (ynode->get_stmt_type() == RW_YANG_STMT_TYPE_LIST) { for (YangKeyIter yki = ynode->key_begin(); yki != ynode->key_end(); yki++) { YangNode *knode = yki->get_key_node(); for (unsigned i = 0; i < ypbc_msgdesc->num_fields; i++) { if (!strcmp(knode->get_name(), ypbc_fdescs[i].yang_node_name)) { fields.push_back(ypbc_fdescs[i].pbc_order); } } nkeys++; } } RW_ASSERT(fields.size() == nkeys); // Remaining fields in the yang-order, though it doesn't matter. for (unsigned i = 0; i < ypbc_msgdesc->num_fields; i++) { if (!(ypbc_fdescs[i].pbc_fdesc->rw_flags & RW_PROTOBUF_FOPT_KEY)) { fields.push_back(ypbc_fdescs[i].pbc_order); } } return nkeys; }
TEST (JsonSchema, JsonAugmentTest) { TEST_DESCRIPTION ("Test for simple yang to json conversion"); YangModelNcx* model = YangModelNcx::create_model(); YangModel::ptr_t p(model); ASSERT_TRUE(model); YangNode* root = model->get_root_node(); ASSERT_TRUE(root); YangModule* tnaa1 = model->load_module("test-json-schema"); ASSERT_TRUE(tnaa1); YangAugment* person = tnaa1->get_first_augment(); ASSERT_TRUE(person); YangNode* node = person->get_target_node(); std::stringstream oss(node->to_json_schema(true)); std::cout << oss.str() << std::endl; pt::ptree tree; pt::read_json(oss, tree); bool found_comp_list = false; BOOST_FOREACH(const pt::ptree::value_type& val, tree.get_child("person.properties")) { auto name = val.second.get<std::string>("name"); if (name == "test-json-schema:company-list") { found_comp_list = true; EXPECT_STREQ (val.second.get<std::string>("type").c_str(), "list"); EXPECT_STREQ (val.second.get<std::string>("cardinality").c_str(), "0..N"); BOOST_FOREACH (const pt::ptree::value_type& v, val.second.get_child("properties")) { auto iname = v.second.get<std::string>("name"); std::cout << iname << std::endl; if (iname == "iref1") { const auto& dtype = v.second.get_child("data-type.idref"); EXPECT_STREQ (dtype.get<std::string>("base").c_str(), "tjs:riftio"); } if (iname == "iref2") { const auto& dtype = v.second.get_child("data-type.idref"); EXPECT_STREQ (dtype.get<std::string>("base").c_str(), "tjs:cloud-platform"); } } } }
rw_yang_netconf_op_status_t XMLDocMerger::do_delete( YangNode* child_ynode, XMLNode* new_node, XMLNode* new_child, XMLNode* delta_child) { rw_yang_netconf_op_status_t status = RW_YANG_NETCONF_OP_STATUS_OK; // The deletion of last case node should add default-case default-values // This is taken care by fill_defaults() XMLNode* remove_node = new_child; xml_data_presence_t data_presence = check_if_data_exists(child_ynode, new_node, delta_child, &remove_node); switch(data_presence) { case XML_DATA_MISSING: { if (current_operation_ == XML_EDIT_OP_DELETE) { std::string const error_msg = "Node (" + new_node->get_local_name() + ") has missing data"; report_error(delta_child, RW_YANG_NETCONF_OP_STATUS_DATA_MISSING, error_msg.c_str()); return RW_YANG_NETCONF_OP_STATUS_DATA_MISSING; } return status; } case XML_DATA_EXISTS: { // If a leaf node has default value and is deleted, then don't report it, // just delete the node, the default value will added when fill_defaults // is invoked. if (child_ynode->get_default_value() == nullptr) { // Neither a leaf nor a leaf with default value report_delete(remove_node); } else { // has default value, if it is part of a case and is the last node, and // is not part of the default case then report deletion YangNode* ycase = child_ynode->get_case(); YangNode* ychoice = child_ynode->get_choice(); if (ychoice && ycase != ychoice->get_default_case()) { bool found = false; // Iterate thru the subtree to find if any node in the existing case // exists. If so, don't report for (XMLNodeIter it = new_node->child_begin(); it != new_node->child_end(); ++it) { XMLNode* xnode = &(*it); if (xnode != remove_node && xnode->get_yang_node()->get_case() == ycase) { found = true; } } if (!found) { report_delete(remove_node); } } } new_node->remove_child(remove_node); break; } case XML_DATA_LIST_EXISTS: { // remove all the data with the list name for (XMLNode* xchild = new_node->get_first_child(); xchild; ) { if (xchild->get_local_name() == child_ynode->get_name() && xchild->get_name_space() == child_ynode->get_ns()) { XMLNode* next_node = xchild->get_next_sibling(); // Multiple keyspecs, report the deletion and remove it from dom report_delete(xchild); new_node->remove_child(xchild); xchild = next_node; } else { xchild = xchild->get_next_sibling(); } } break; } } return status; }
void XMLDocMerger::fill_defaults( YangNode* ynode, XMLNode* new_node, XMLNode* update_node, bool* update_required ) { YangNode* yn = nullptr; XMLNode* xchild = nullptr; for (YangNodeIter it = ynode->child_begin(); it != ynode->child_end(); ++it) { yn = &(*it); const char* default_val = yn->get_default_value(); if (!default_val && !yn->has_default()) { // Only default values or containers with default leaf descendents continue; } // Default values may be within a choice/case. Check if the choice has // another case. If the same case is present YangNode* ychoice = yn->get_choice(); YangNode* ycase = yn->get_case(); YangNode* other_choice = nullptr; YangNode* other_case = nullptr; bool add_default = true; bool subtree_update = false; if (ychoice && ycase && (ychoice->get_default_case() != ycase)) { add_default = false; } for (XMLNodeIter xit = new_node->child_begin(); xit != new_node->child_end(); ++xit) { xchild = &(*xit); if (xchild->get_local_name() == yn->get_name() && xchild->get_name_space() == yn->get_ns()) { if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER) { // The container has a default descendant node XMLNode* update_child = nullptr; bool created = false; if ((update_child = update_node->find( yn->get_name(), yn->get_ns())) == nullptr) { update_child = update_node->add_child(yn); created = true; } fill_defaults(yn, xchild, update_child, &subtree_update); if (!subtree_update && created) { update_node->remove_child(update_child); } *update_required = (*update_required || subtree_update); } // Default node already present in the new-dom add_default = false; break; } if (!ychoice) { // Not part of a choice continue; } other_choice = xchild->get_yang_node()->get_choice(); if (!other_choice || (other_choice != ychoice)) { // Other node is not a choice, or not the same choice, not conflicting continue; } other_case = xchild->get_yang_node()->get_case(); if (other_case && (ycase != other_case)) { // There is a node with conflicting case. Hence no default add_default = false; break; } // Same case, in-case the case is not default, some-other node in the same // case is set. Then add this default, unless the same node is found in // the new-dom. add_default = true; } if (add_default) { XMLNode* xn = new_node->add_child(yn, default_val); RW_ASSERT(xn); XMLNode* un = update_node->add_child(yn, default_val); if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER) { // The container has a default descendant node fill_defaults(yn, xn, un, &subtree_update); if (!subtree_update) { new_node->remove_child(xn); update_node->remove_child(un); } *update_required = (*update_required || subtree_update); } else { *update_required = true; } } } }
// ATTN: This function seriously needs re-organization, will be done // shortly. rw_yang_netconf_op_status_t XMLDocMerger::do_edit(XMLNode* new_node, XMLNode* delta_node, XMLNode* update_node, bool* update_required) { rw_yang_netconf_op_status_t status = RW_YANG_NETCONF_OP_STATUS_OK; YangNode* ynode = new_node->get_descend_yang_node(); XMLEditDefaultOperation parent_op = current_operation_; for(XMLNodeIter it = delta_node->child_begin(); it != delta_node->child_end(); ++it) { XMLNode* delta_child = &(*it); std::string child_name = delta_child->get_local_name(); std::string child_ns = delta_child->get_name_space(); YangNode* child_ynode = ynode->search_child(child_name.c_str(), child_ns.c_str()); if (child_ynode == nullptr) { // Incoming node is not in our model and thus is an error std::string const err_msg = "Cannot find child ("+child_name+") of node ("+delta_node->get_local_name()+")"; report_error(delta_node, RW_YANG_NETCONF_OP_STATUS_INVALID_VALUE, err_msg.c_str()); return RW_YANG_NETCONF_OP_STATUS_INVALID_VALUE; } // Set the current node operation (default=merge) status = set_current_operation(delta_child); if (status != RW_YANG_NETCONF_OP_STATUS_OK) { return status; } bool subtree_update = false; XMLNode* new_child = new_node->find(child_name.c_str(), child_ns.c_str()); if (new_child == nullptr) { // Node not found in existing config, edit-config on new node status = do_edit_new(child_ynode, new_node, delta_child, update_node, &subtree_update); } else { status = do_edit_existing(child_ynode, new_node, new_child, delta_child, update_node, &subtree_update); } *update_required = (*update_required || subtree_update); if (status != RW_YANG_NETCONF_OP_STATUS_OK) { return status; } current_operation_ = parent_op; } if (current_operation_ == XML_EDIT_OP_REPLACE) { // Iterate thru the config dom node and find the elements not in delta // Those are marked for deletion. do_delete_missing(new_node, delta_node); } // Add defaults fill_defaults(ynode, new_node, update_node, update_required); if (!(*update_required)) { // No updates on this subtree. Either it is delete/remove operation or // the config is not changed. So remove the update subtree. XMLNode* parent = update_node->get_parent(); if (parent) { parent->remove_child(update_node); } } if (new_node->get_first_child() == nullptr && ynode->get_stmt_type() == RW_YANG_STMT_TYPE_CONTAINER && !ynode->is_presence()) { // No children for a non-presence container, they are only present to // maintain hierarchy. Remove it XMLNode* parent = new_node->get_parent(); if (parent) { parent->remove_child(new_node); } } return status; }
virtual rw_xml_next_action_t visit(XMLNode* node, XMLNode** path, int32_t level) { if (node == nullptr) { return RW_XML_ACTION_TERMINATE; } YangNode *yn = node->get_yang_node(); if (nullptr == yn) { return RW_XML_ACTION_NEXT; } if (yn->get_stmt_type() == RW_YANG_STMT_TYPE_LIST) { return RW_XML_ACTION_NEXT_SIBLING; } RW_ASSERT(to_node_); // This is the root node then this is the first time call if (level == 0) { RW_ASSERT(level_ == -1); std::string text = node->get_text_value(); if (node->get_yang_node()->is_leafy() && node->get_yang_node()->get_type()->get_leaf_type() != RW_YANG_LEAF_TYPE_EMPTY && text.find_first_not_of("\t\n\v\f\r ") != std::string::npos) { if (text.find_first_of ("http")) { RW_CRASH(); } curr_node_ = to_node_->add_child(node->get_yang_node(), node->get_text_value().c_str()); } else { curr_node_ = to_node_->add_child(node->get_yang_node()); } path_[++level_].first = node; path_[level_].second = curr_node_; return RW_XML_ACTION_NEXT; } XMLNode *from_parent = path[level-1]; RW_ASSERT(from_parent); // Walk up our path and find the corresponding parent in teh vistor's tree while(level_ >= 0) { if (path_[level_].first == from_parent) { // Found a common parent - break and add the new node XMLNode *to_parent = static_cast<XMLNode*>(path_[level_].second); std::string text = node->get_text_value(); if (text.find_first_not_of("\t\n\v\f\r ") != std::string::npos) { // A bit of a hack to ensure that white spaces are not inserted to the // XML. This is a problem for UT that read from files curr_node_ = to_parent->add_child(node->get_yang_node(), node->get_text_value().c_str()); } else { curr_node_ = to_parent->add_child(node->get_yang_node()); } // Replace the node in the current path with the new node. path_[++level_].first = node; path_[level_].second = curr_node_; break; } level_--; } RW_ASSERT(level_ > 0); return RW_XML_ACTION_NEXT; }
XMLNode* get_node_by_hkeypath (XMLNode *node, confd_hkeypath_t *keypath, uint16_t stop_at) { const char *ns = 0; int i = keypath->len; while (i > stop_at) { confd_value_t *val = &keypath->v[i-1][0]; const char *tag = 0; char value[CONFD_MAX_STRING_LENGTH]; struct confd_cs_node *cs_node = confd_find_cs_node (keypath, keypath->len - (i - 1)); RW_ASSERT(cs_node); RW_ASSERT(C_XMLTAG == val->type); tag = confd_hash2str (val->val.xmltag.tag); ns = confd_hash2str (val->val.xmltag.ns); node = node->find (tag, ns); if (nullptr == node) { return nullptr; } i--; if (i == 0) { return node; } YangNode *yn = node->get_yang_node(); if (!yn->is_listy()) { continue; } if (!yn->has_keys()) { // Keyless list - there should be only a single "key-value", which should // be a ptr to a sibling value. There is no list node parent in XML. RW_ASSERT(keypath->v[i-1][0].type == C_INT64); RW_ASSERT(keypath->v[i-1][1].type == C_NOEXISTS); XMLNode *sibling = (XMLNode *) (keypath->v[i-1][0].val.i64); if (nullptr == sibling) { return nullptr; } RW_ASSERT (sibling->get_parent() == node->get_parent()); // There could be multiple keyless lists node = sibling; i --; continue; } bool matched = true; // The node has moved to the *first* list entry that it could find. // go through all its siblings with matching names to see if any match // all the keys. while (node) { matched = true; for (int j = 0; val->type != C_NOEXISTS; j++) { val = &keypath->v[i-1][j]; RW_ASSERT(cs_node->info.keys[j]); tag = confd_hash2str(cs_node->info.keys[j]); switch (val->type) { case C_NOEXISTS: case C_XMLEND: case C_BIT32: case C_BIT64: case C_XMLTAG: RW_CRASH(); break; case C_ENUM_VALUE: if (nullptr == node->find_enum (tag, ns, CONFD_GET_ENUM_VALUE (val))) { matched = false; } break; default: size_t len = confd_pp_value (value, sizeof (value), val); RW_ASSERT (len < sizeof (value)); if (!len || nullptr == node->find_value (tag, value, ns)) { matched = false; } break; } if (!matched) { break; } val = &keypath->v[i-1][j+1]; } if (matched) { break; } node = node->get_next_sibling(node->get_local_name(), node->get_name_space()); } if (!matched) { return nullptr; } // Account for the key i--; } return node; }
rw_status_t get_confd_case ( rw_yang::XMLNode *root, confd_hkeypath_t *keypath, confd_value_t *choice, confd_value_t *case_val) { RW_ASSERT (case_val); rw_yang::XMLNode *node = get_node_by_hkeypath (root, keypath, 0); if (nullptr == node) { return RW_STATUS_FAILURE; } XMLNodeList::uptr_t children(node->get_children()); for (size_t i = 0; i < children->length(); i++) { XMLNode* child = children->at(i); YangNode *yn = child->get_descend_yang_node(); if (nullptr == yn) { continue; } YangNode *yn_case = yn->get_case(); if (nullptr == yn_case) { continue; } // The comparision starts with the choice.. YangNode *yn_choice = yn_case->get_choice(); RW_ASSERT (yn_choice); while (choice->type != C_NOEXISTS) { RW_ASSERT (choice->type == C_XMLTAG); const char * tag = confd_hash2str (choice->val.xmltag.tag); int n_s = choice->val.xmltag.ns; const char *ns = confd_hash2str (n_s); if (strcmp (tag, yn_choice->get_name()) || strcmp (ns, yn_choice->get_ns())) { break; } // The case matches - check if choice exists choice++; if (C_NOEXISTS == choice->type) { case_val->type = C_XMLTAG; case_val->val.xmltag.ns = n_s; case_val->val.xmltag.tag = confd_str2hash (yn_case->get_name()); return RW_STATUS_SUCCESS; } RW_ASSERT (choice->type == C_XMLTAG); yn_case = yn_choice->get_case(); // Can this be null?? RW_ASSERT (yn_case); tag = confd_hash2str (choice->val.xmltag.tag); ns = confd_hash2str (choice->val.xmltag.ns); if (strcmp (tag, yn_case->get_name()) || strcmp (ns, yn_case->get_ns())) { break; } } } return RW_STATUS_FAILURE; }
rw_tree_walker_status_t add_xml_child (XMLNode *parent, rw_confd_value_t *confd, XMLNode*& child) { confd_cs_node *cs_node = confd->cs_node; confd_value_t *child_src = confd->value; const char *name = confd_hash2str (confd->cs_node->tag); const char *ns = confd_hash2str (confd->cs_node->ns); YangNode *child_yn = nullptr; child = nullptr; YangNode *yn = parent->get_yang_node(); if (!yn) { return RW_TREE_WALKER_FAILURE; } child_yn = yn->search_child (name, ns); if (!child_yn) { return RW_TREE_WALKER_FAILURE; } if (!child_yn->is_leafy()) { child = parent->add_child(child_yn); if (child) { return RW_TREE_WALKER_SUCCESS; } return RW_TREE_WALKER_FAILURE; } char value[1024]; char *val_p = nullptr; switch (child_src->type) { default: case C_QNAME: case C_DATETIME: case C_DATE: case C_TIME: case C_DURATION: case C_NOEXISTS: case C_STR: case C_SYMBOL: case C_BIT32: case C_BIT64: case C_XMLBEGIN: case C_XMLEND: case C_OBJECTREF: case C_UNION: case C_PTR: case C_CDBBEGIN: case C_OID: case C_DEFAULT: case C_IDENTITYREF: case C_XMLBEGINDEL: case C_DQUAD: case C_HEXSTR: RW_CRASH(); break; case C_XMLTAG: break; case C_BUF: case C_INT8: case C_INT16: case C_INT32: case C_INT64: case C_UINT8: case C_UINT16: case C_UINT32: case C_UINT64: case C_DOUBLE: case C_BOOL: case C_ENUM_VALUE: case C_BINARY: case C_DECIMAL64: case C_IPV4: case C_IPV6: case C_IPV4PREFIX: case C_IPV4_AND_PLEN: case C_IPV6_AND_PLEN: case C_IPV6PREFIX: { struct confd_type *ct = cs_node->info.type; int len = confd_val2str (ct, child_src, value, sizeof (value)); if (len < 0) { return RW_TREE_WALKER_FAILURE; } val_p = &value[0]; } break; case C_LIST: { // ATTN: ATTN: // This code is inefficient. TAIL-F has been asked what the best method // for doing this is. Once TAILF responds, change this code. // ATTN: ATTN: // This also needs to change when protobuf representation of a leaf list // transitions to becoming a list of single elements struct confd_type *ct = confd_get_leaf_list_type (cs_node); RW_ASSERT(ct); int len = confd_val2str (ct, child_src, value, sizeof (value)); if (len < 0) { return RW_TREE_WALKER_FAILURE; } val_p = &value[0]; } break; } child = parent->add_child (child_yn, val_p); if (child) { return RW_TREE_WALKER_SUCCESS; } return RW_TREE_WALKER_FAILURE; }