[Xorp-hackers] [PATCH 2/2] xorp: rtrmgr: Include new command %unique-in

igorm at etf.rs igorm at etf.rs
Mon Apr 2 08:10:57 PDT 2012


From: Igor Maravic <igorm at etf.rs>

With %unique-in command we can set the scope in which our variables are unique.
It takes only on parameter - one of variable parents (or some older ancestor).

Before commit can start it will be checked if the variable is unique in given scope.
If it isn't, error will be returned.

It's only valid for leaf values.

Example template file:

grand-parent @: u32 {
    parent @: u32 {
        child: u32;
    }
}

grand-parent @: u32 {
   %create ...
   %update ...

   parent @: u32 {
       %create ...
       %update ...

       child {
           %unique-in: $(grand-parent.@);

           %set ...
       }

   }

}

This template file means that all child nodes in grand-parent's scope should have unique values.

With this template file this is allowed configuration:

grand-parent 2 {
   parent 2 {
      child: 3
   }
   parent 3 {
      child: 4
   }
}

grand-parent 3 {
   parent 2 {
       child: 4
   }
   parent 4 {
       child: 5
   }
}

This is not allowed configuration:

grand-parent 2 {
   parent 2 {
      child: 4
   }
   parent 3 {
      child: 4
   }
}

grand-parent 3 {
   parent 2 {
       child: 5
   }
}

Signed-off-by: Igor Maravic <igorm at etf.rs>
---
 xorp/rtrmgr/conf_tree_node.cc     |  129 ++++++++++++++++++++++++++++++++++++
 xorp/rtrmgr/conf_tree_node.hh     |    2 +
 xorp/rtrmgr/template_tree_node.cc |  130 ++++++++++++++++++++++++++++++++++++-
 xorp/rtrmgr/template_tree_node.hh |    6 ++
 4 files changed, 266 insertions(+), 1 deletions(-)

diff --git a/xorp/rtrmgr/conf_tree_node.cc b/xorp/rtrmgr/conf_tree_node.cc
index 7050ffa..ec2b649 100644
--- a/xorp/rtrmgr/conf_tree_node.cc
+++ b/xorp/rtrmgr/conf_tree_node.cc
@@ -666,6 +666,110 @@ ConfigTreeNode::check_config_tree(string& error_msg) const
     }
 
     //
+    // Check if this node should be unique
+    //
+    if ((!deleted()) && (_template_tree_node != NULL) && has_value()
+	    && !_template_tree_node->unique_in_node().empty()) {
+
+	VarType type = NONE;
+	const ConfigTreeNode *varname_node;
+
+	/**
+	 * Find root node in which this node should be unique
+	 */
+	varname_node = find_const_varname_node(_template_tree_node->unique_in_node(), type);
+
+	if (varname_node) {
+	    const list<string> unique_path = _template_tree_node->get_path_to_be_unique_in();
+
+	    list<const ConfigTreeNode*> parent_list;	// Presents list of direct parents of nodes
+							// that should be unique
+
+	    list<string>::const_iterator list_iter;
+	    parent_list.push_front(varname_node);	// If unique_path is empty then we've already
+							// found our direct parent
+
+	    for (list_iter = unique_path.begin();
+		    list_iter != unique_path.end(); ++list_iter) {
+		/**
+		 * In this for loop we are searching for children of nodes from parent_list,
+		 * that match criteria from current element of unique_path list.
+		 *
+		 * Parent list is emptied at the beginning of each iteration,
+		 * and it is going to be filled with all children that match certain criteria.
+		 */
+		list<const ConfigTreeNode*> old_parent_list = parent_list;
+		parent_list.clear();
+
+		const string name = *list_iter;
+		list<const ConfigTreeNode*>::const_iterator iter;
+
+		for (iter = old_parent_list.begin(); iter != old_parent_list.end();
+			++iter) {
+		    const ConfigTreeNode* child = *iter;
+		    list<const ConfigTreeNode*> founded_children;
+
+		    /**
+		     * Children nodes could be matched based on their name (tag nodes),
+		     * or according to their type (children of tag nodes).
+		     */
+		    if (name.find("@:=") != string::npos) {
+			string type_str = name.substr(3);
+			child->get_children_with_type(founded_children, type_str);
+		    } else {
+			child->get_children_with_name(founded_children, name);
+		    }
+		    /**
+		     * Add children to the parent_list
+		     */
+		    parent_list.merge(founded_children);
+		}
+	    }
+
+	    list<const ConfigTreeNode*>::const_iterator iter;
+	    set<string> values_to_check;
+
+	    /**
+	     * Here we actually check if there are two same values in the defined scope
+	     */
+	    for (iter = parent_list.begin();
+		    iter != parent_list.end(); ++iter) {
+		const ConfigTreeNode* parent = *iter;
+		list<const ConfigTreeNode*> founded_children;
+
+		parent->get_children_with_name(founded_children, _segname);
+
+		if (founded_children.size() != 1) {
+		    ostringstream oss;
+		    oss << founded_children.size();
+		    XLOG_ERROR("Found %s occurrences of %s on node %s. Expected 1!\n",
+			    oss.str().c_str(), _segname.c_str(), path().c_str());
+		    return false;
+		}
+		const ConfigTreeNode* child = *(founded_children.begin());
+
+		if (!child->deleted() && child->has_value()) {
+		    string child_value = child->value();
+		    if (values_to_check.find(child_value) != values_to_check.end()) {
+			error_msg = c_format("Node \"%s\"\n must be unique in \"%s\"\n",
+					     _path.c_str(), varname_node->path().c_str());
+			return false;
+		    } else {
+			values_to_check.insert(child_value);
+		    }
+		}
+
+	    }
+	} else {
+	    XLOG_ERROR("There is no node \"%s\"\n in which node \"%s\"\n"
+		    "should be unique\n", _template_tree_node->unique_in_node().c_str(),
+		    path().c_str());
+	    return false;
+	}
+
+    }
+
+    //
     // Verify the allowed configuration values
     //
     if (_template_tree_node != NULL) {
@@ -910,6 +1014,31 @@ ConfigTreeNode::module_root_node()
     return _parent->module_root_node();
 }
 
+void
+ConfigTreeNode::get_children_with_name(list<const ConfigTreeNode*>& children_ret,
+					const string& name_str) const
+{
+    children_ret.clear();
+    list<ConfigTreeNode*>::const_iterator iter;
+    for (iter = _children.begin(); iter != _children.end(); ++iter) {
+	ConfigTreeNode* child = *iter;
+	if (child->segname() == name_str)
+	    children_ret.push_back(child);
+    }
+}
+void
+ConfigTreeNode::get_children_with_type(list<const ConfigTreeNode*>& children_ret,
+					const string& type_str) const
+{
+    children_ret.clear();
+    list<ConfigTreeNode*>::const_iterator iter;
+    for (iter = _children.begin(); iter != _children.end(); ++iter) {
+	ConfigTreeNode* child = *iter;
+	if (child->typestr() == type_str)
+	    children_ret.push_back(child);
+    }
+}
+
 string
 ConfigTreeNode::show_subtree(bool show_top, int depth, int indent,
 			     bool do_indent, bool numbered, bool annotate,
diff --git a/xorp/rtrmgr/conf_tree_node.hh b/xorp/rtrmgr/conf_tree_node.hh
index ebd8fe9..50834d1 100644
--- a/xorp/rtrmgr/conf_tree_node.hh
+++ b/xorp/rtrmgr/conf_tree_node.hh
@@ -141,6 +141,8 @@ public:
     const ConfigTreeNode* const_parent() const { return _parent; }
     ConfigTreeNode* module_root_node();
     list<ConfigTreeNode*>& children() { return _children; }
+    void get_children_with_name(list<const ConfigTreeNode*>& children_ret, const string& name) const;
+    void get_children_with_type(list<const ConfigTreeNode*>& children_ret, const string& typestr) const;
     const list<ConfigTreeNode*>& const_children() const { return _children; }
     string show_subtree(bool show_top, int depth, int indent, bool do_indent,
 			bool numbered, bool annotate,
diff --git a/xorp/rtrmgr/template_tree_node.cc b/xorp/rtrmgr/template_tree_node.cc
index 8af4396..c44afa9 100644
--- a/xorp/rtrmgr/template_tree_node.cc
+++ b/xorp/rtrmgr/template_tree_node.cc
@@ -55,6 +55,7 @@ TemplateTreeNode::TemplateTreeNode(TemplateTree& template_tree,
       _varname(varname),
       _has_default(false),
       _is_tag(false),
+      _unique_in_node(""),
       _order(ORDER_UNSORTED),
       _verbose(template_tree.verbose()),
       _is_deprecated(false),
@@ -172,6 +173,60 @@ TemplateTreeNode::expand_template_tree(string& error_msg)
     }
 
     //
+    // Mark all referred unique variables
+    //
+    if (!unique_in_node().empty()) {
+	list<string> inverted_path_to_unique;
+	const string& unique_node = unique_in_node();
+	TemplateTreeNode* ttn;
+
+	ttn = find_varname_node(unique_node);
+	if (ttn == NULL) {
+	    error_msg = c_format("Invalid unique-in variable %s: "
+		    "not found", unique_node.c_str());
+	    return (false);
+	}
+
+	//Check if unique-in variable is from the same module as this variable
+	if (ttn->module_name() != module_name()) {
+	    error_msg = c_format("Invalid unique-in variable %s: "
+			    "should be from the module %s as %s",
+			    unique_node.c_str(), module_name().c_str(), _segname.c_str());
+	    return (false);
+	}
+
+	//Check if unique-in variable is parent for this variable
+	TemplateTreeNode* parent = _parent;
+
+	do {
+	    if (!parent) {
+		error_msg = c_format("Invalid unique-in variable %s:\n"
+			"it should be parent (or parent of parent, or parent of parent of parent...) of \"%s\"",
+			unique_node.c_str(), path().c_str());
+		return (false);
+	    }
+	    if (parent == ttn)
+		break;
+
+	    /**
+	     * Fill path to node in which it should be unique
+	     *
+	     * Write names of nodes for tag nodes,
+	     * and write "@:="+type for children of tag nodes
+	     */
+	    if (parent->segname() == "@")
+		inverted_path_to_unique.push_front(
+			parent->segname() + ":=" + parent->typestr());
+	    else
+		inverted_path_to_unique.push_front(parent->segname());
+
+	    parent = parent->parent();
+	} while (true);
+
+	_unique_in_path = inverted_path_to_unique;
+    }
+
+    //
     // Recursively expand all children nodes
     //
     list<TemplateTreeNode*>::iterator iter2;
@@ -241,6 +296,54 @@ TemplateTreeNode::check_template_tree(string& error_msg) const
     }
 
     //
+    // Check all referred unique variables
+    //
+    if (!is_leaf_value() && !unique_in_node().empty()) {
+	error_msg = c_format("Found %%unique-in command in node \"%s\" that "
+		"doesn't expect value",
+		path().c_str());
+	return false;
+    }
+    if (!unique_in_node().empty()) {
+	const string& unique_node = unique_in_node();
+	const TemplateTreeNode* ttn;
+
+	ttn = find_const_varname_node(unique_node);
+	if (ttn == NULL) {
+	    error_msg = c_format("Invalid unique-in variable %s: "
+		    "not found",
+		    unique_node.c_str());
+	    return (false);
+	}
+
+	//Check if unique-in variable is from the same module as this variable
+	if (ttn->module_name() != module_name()) {
+	    error_msg =
+		    c_format("Invalid unique-in variable %s: "
+			    "should be from the module %s as %s",
+			    unique_node.c_str(), module_name().c_str(), _segname.c_str());
+	    return (false);
+	}
+
+	//Check if unique-in variable is parent for this variable
+	TemplateTreeNode* parent = _parent;
+
+	do {
+	    if (!parent) {
+		error_msg = c_format("Invalid unique-in variable %s: "
+			"it should be (grand)parent of %s",
+			unique_node.c_str(), _segname.c_str());
+		return (false);
+	    }
+	    if (parent == ttn)
+		break;
+
+	    parent = parent->parent();
+	} while (true);
+    }
+
+
+    //
     // Check specific commands for this node
     //
     // XXX: only leaf nodes should have %set command
@@ -355,6 +458,14 @@ TemplateTreeNode::add_cmd(const string& cmd) throw (ParseError)
 	    command = new DummyBaseCommand(*this, cmd);
 	    _cmd_map[cmd] = command;
 	}
+    } else if (cmd == "%unique-in") {
+	if (!is_leaf_value()) {
+	    error_msg = c_format("Invalid command \"%s\".\n", cmd.c_str());
+	    error_msg += "This command only applies to leaf nodes that ";
+	    error_msg += "have values and only if the value is allowed ";
+	    error_msg += "to be changed.\n";
+	    xorp_throw(ParseError, error_msg);
+	}
     } else if (cmd == "%mandatory") {
 	// Nothing to do
     } else {
@@ -362,7 +473,7 @@ TemplateTreeNode::add_cmd(const string& cmd) throw (ParseError)
 	error_msg += "Valid commands are %create, %delete, %set, %unset, ";
 	error_msg += "%get, %default, %modinfo, %activate, %update, %allow, ";
 	error_msg += "%allow-range, %mandatory, %deprecated, %user-hidden, ";
-	error_msg += "%read-only, %permanent, %order\n";
+	error_msg += "%read-only, %permanent, %order, %unique-in\n";
 	xorp_throw(ParseError, error_msg);
     }
 }
@@ -533,6 +644,23 @@ TemplateTreeNode::add_action(const string& cmd,
 		_mandatory_config_nodes.push_back(varname);
 	    }
 	}
+    } else if (cmd == "%unique-in") {
+	// Add all new  variables
+	if (action_list.size() == 1) {
+	    list<string>::const_iterator li = action_list.begin();
+	    if (!_unique_in_node.empty()) {
+		error_msg = c_format("There can be only one declaration of %%unique-in argument"
+			"for node. Previous was %s in node \"%s\"",
+			_unique_in_node.c_str(), path().c_str());
+		xorp_throw(ParseError, error_msg);
+	    }
+	    _unique_in_node = *li;
+	} else {
+	    error_msg = c_format("Invalid number of %%unique-in arguments: "
+		    "%u (expected 1)",
+		    XORP_UINT_CAST(action_list.size()));
+	    xorp_throw(ParseError, error_msg);
+	}
     } else {
 	// the master tree will deal with these
     }
diff --git a/xorp/rtrmgr/template_tree_node.hh b/xorp/rtrmgr/template_tree_node.hh
index 106da1a..0f44485 100644
--- a/xorp/rtrmgr/template_tree_node.hh
+++ b/xorp/rtrmgr/template_tree_node.hh
@@ -112,6 +112,8 @@ public:
     bool is_module_root_node() const;
     bool is_leaf_value() const;
 
+    const list<string>& get_path_to_be_unique_in() const { return _unique_in_path; }
+
     list<ConfigOperator> allowed_operators() const;
 
 #if 0
@@ -137,6 +139,7 @@ public:
     bool verify_variables(const ConfigTreeNode& ctn, string& error_msg) const;
 
     const list<string>& mandatory_config_nodes() const { return _mandatory_config_nodes; }
+    const string& unique_in_node() const { return _unique_in_node; }
     const string& help() const;
     const string& help_long() const;
 
@@ -222,6 +225,9 @@ private:
 
     list<string>	_mandatory_config_nodes;
 
+    string		_unique_in_node;
+    list<string>	_unique_in_path;
+
     int _child_number;
 
     TTSortOrder _order;
-- 
1.7.5.4



More information about the Xorp-hackers mailing list