// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2007 International Computer Science Institute // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software") // to deal in the Software without restriction, subject to the conditions // listed in the XORP LICENSE file. These conditions include: you must // preserve this copyright notice, and you cannot mention the copyright // holders in advertising related to the Software without their permission. // The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This // notice is a summary of the XORP LICENSE file; the license in that file is // legally binding. #ident "$XORP: xorp/rtrmgr/master_conf_tree_node.cc,v 1.25 2007/02/16 22:47:22 pavlin Exp $" #include "rtrmgr_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include "command_tree.hh" #include "master_conf_tree_node.hh" #include "module_command.hh" #include "template_commands.hh" #include "template_tree_node.hh" #include "util.hh" extern int booterror(const char *s) throw (ParseError); MasterConfigTreeNode::MasterConfigTreeNode(bool verbose) : ConfigTreeNode(verbose), _actions_pending(0), _actions_succeeded(true), _cmd_that_failed(NULL) { } MasterConfigTreeNode::MasterConfigTreeNode(const string& nodename, const string& path, const TemplateTreeNode* ttn, MasterConfigTreeNode* parent, const ConfigNodeId& node_id, uid_t user_id, bool verbose) : ConfigTreeNode(nodename, path, ttn, parent, node_id, user_id, /* clientid */ 0, verbose), _actions_pending(0), _actions_succeeded(true), _cmd_that_failed(NULL) { } MasterConfigTreeNode::MasterConfigTreeNode(const MasterConfigTreeNode& ctn) : ConfigTreeNode(ctn), _actions_pending(0), _actions_succeeded(true), _cmd_that_failed(NULL) { } ConfigTreeNode* MasterConfigTreeNode::create_node(const string& segment, const string& path, const TemplateTreeNode* ttn, ConfigTreeNode* parent_node, const ConfigNodeId& node_id, uid_t user_id, uint32_t clientid, bool verbose) { UNUSED(clientid); MasterConfigTreeNode *new_node, *parent; parent = dynamic_cast(parent_node); // sanity check - all nodes in this tree should be Master nodes if (parent_node != NULL) XLOG_ASSERT(parent != NULL); new_node = new MasterConfigTreeNode(segment, path, ttn, parent, node_id, user_id, verbose); return reinterpret_cast(new_node); } ConfigTreeNode* MasterConfigTreeNode::create_node(const ConfigTreeNode& ctn) { MasterConfigTreeNode *new_node; const MasterConfigTreeNode *orig; // sanity check - all nodes in this tree should be Master nodes orig = dynamic_cast(&ctn); XLOG_ASSERT(orig != NULL); new_node = new MasterConfigTreeNode(*orig); return new_node; } void MasterConfigTreeNode::command_status_callback(const Command* cmd, bool success) { debug_msg("command_status_callback node %s cmd %s\n", _segname.c_str(), cmd->str().c_str()); debug_msg("actions_pending = %d\n", _actions_pending); XLOG_ASSERT(_actions_pending > 0); if (_actions_succeeded && (success == false)) { _actions_succeeded = false; _cmd_that_failed = cmd; } _actions_pending--; } void MasterConfigTreeNode::find_changed_modules(set& changed_modules) const { if (_template_tree_node != NULL) { // XXX: ignore deprecated subtrees if (_template_tree_node->is_deprecated()) return; } if ((_template_tree_node != NULL) && (!_existence_committed || !_value_committed)) { const BaseCommand *base_cmd = NULL; const Command *cmd = NULL; set modules; set::const_iterator iter; if (_deleted) { base_cmd = _template_tree_node->const_command("%delete"); if (base_cmd == NULL) { // No need to go to children return; } cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) changed_modules.insert(*iter); return; } if (!_existence_committed) { if (! _template_tree_node->module_name().empty()) changed_modules.insert(_template_tree_node->module_name()); base_cmd = _template_tree_node->const_command("%create"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) changed_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%activate"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) changed_modules.insert(*iter); } } else if (!_value_committed) { base_cmd = _template_tree_node->const_command("%set"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) changed_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%update"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) changed_modules.insert(*iter); } } } list::const_iterator li; for (li = _children.begin(); li != _children.end(); ++li) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*li); child->find_changed_modules(changed_modules); } } void MasterConfigTreeNode::find_active_modules(set& active_modules) const { if (_template_tree_node != NULL) { // XXX: ignore deprecated subtrees if (_template_tree_node->is_deprecated()) return; } if (_deleted) { // XXX: ignore deleted subtrees return; } if (_template_tree_node != NULL) { const BaseCommand *base_cmd; const Command *cmd; set modules; set::const_iterator iter; // // XXX: If the module's top-level node is not deleted explicitly, then // the module is considered still active. // if (_template_tree_node->is_module_root_node()) active_modules.insert(_template_tree_node->module_name()); base_cmd = _template_tree_node->const_command("%create"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) active_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%activate"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) active_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%update"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) active_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%set"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) active_modules.insert(*iter); } } // // Process the subtree // list::const_iterator li; for (li = _children.begin(); li != _children.end(); ++li) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*li); child->find_active_modules(active_modules); } } void MasterConfigTreeNode::find_all_modules(set& all_modules) const { if (_template_tree_node != NULL) { // XXX: ignore deprecated subtrees if (_template_tree_node->is_deprecated()) return; } if (_template_tree_node != NULL) { const BaseCommand *base_cmd = NULL; const Command *cmd = NULL; set modules; set::const_iterator iter; // // XXX: If the module's top-level node is in the configuration, then // the module is added to the list. // if (_template_tree_node->is_module_root_node()) all_modules.insert(_template_tree_node->module_name()); base_cmd = _template_tree_node->const_command("%create"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) all_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%activate"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) all_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%update"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) all_modules.insert(*iter); } base_cmd = _template_tree_node->const_command("%set"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); modules = cmd->affected_modules(); for (iter = modules.begin(); iter != modules.end(); ++iter) all_modules.insert(*iter); } } list::const_iterator li; for (li = _children.begin(); li != _children.end(); ++li) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*li); child->find_all_modules(all_modules); } } void MasterConfigTreeNode::initialize_commit() { _actions_pending = 0; _actions_succeeded = true; _cmd_that_failed = NULL; list::iterator iter; for (iter = _children.begin(); iter != _children.end(); ++iter) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*iter); child->initialize_commit(); } } bool MasterConfigTreeNode::children_changed() { if (_existence_committed == false || _value_committed == false) return true; list::iterator iter; for (iter = _children.begin(); iter != _children.end(); ++iter) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*iter); if (child->children_changed()) { return true; } } return false; } bool MasterConfigTreeNode::commit_changes(TaskManager& task_manager, bool do_commit, int depth, int last_depth, string& error_msg, bool& needs_activate, bool& needs_update) { bool success = true; const BaseCommand *base_cmd = NULL; const Command *cmd = NULL; bool changes_made = false; debug_msg("*****COMMIT CHANGES node >%s< >%s<\n", _path.c_str(), _value.c_str()); if (do_commit) debug_msg("do_commit\n"); if (_existence_committed == false) debug_msg("_existence_committed == false\n"); if (_value_committed == false) debug_msg("_value_committed == false\n"); if (_template_tree_node != NULL) { // XXX: ignore deprecated subtrees if (_template_tree_node->is_deprecated()) return success; } // Don't bother to recurse if no child node has any changes to commit. // Calling this every time is rather inefficient, but for the // scales we're talking about, it's not a big deal. if (children_changed() == false) { debug_msg("No children changed\n"); return success; } else { debug_msg("Children have changed\n"); } // The root node has a NULL template if (_template_tree_node != NULL) { // Do we have to start any modules to implement this functionality base_cmd = _template_tree_node->const_command("%modinfo"); const ModuleCommand* modcmd = dynamic_cast(base_cmd); if (modcmd != NULL) { if (modcmd->start_transaction(*this, task_manager) != XORP_OK) { error_msg = c_format("Start Transaction failed for " "module %s\n", modcmd->module_name().c_str()); return false; } } if ((_existence_committed == false || _value_committed == false)) { debug_msg("we have changes to handle\n"); // First handle deletions if (_deleted) { if (do_commit == false) { // No point in checking further return true; } else { // // We expect a deleted node here to have previously // had its existence committed. // (_existence_committed == true) but its // non-existence not previously committed // (_value_committed == false) // XLOG_ASSERT(_existence_committed); XLOG_ASSERT(!_value_committed); base_cmd = _template_tree_node->const_command("%delete"); if (base_cmd != NULL) { cmd = reinterpret_cast(base_cmd); int actions = cmd->execute(*this, task_manager); if (actions < 0) { // Bad stuff happenned // XXX now what? error_msg = "Something went wrong.\n"; error_msg += c_format("The problem was with \"%s\"\n", path().c_str()); error_msg += "WARNING: Partially commited changes exist\n"; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } _actions_pending += actions; // No need to go on to delete children return true; } } } else { // Check any %allow commands that might prevent us // going any further base_cmd = _template_tree_node->const_command("%allow"); if (base_cmd == NULL) { // Try allow-range base_cmd = _template_tree_node->const_command("%allow-range"); } if (base_cmd != NULL) { const AllowCommand* allow_cmd; debug_msg("found ALLOW command: %s\n", cmd->str().c_str()); allow_cmd = dynamic_cast(base_cmd); XLOG_ASSERT(allow_cmd != NULL); if (allow_cmd->verify_variables(*this, error_msg) != true) { // // Commit_changes should always be run first // with do_commit not set, so there can be no // Allow command errors. // // XLOG_ASSERT(do_commit == false); error_msg = c_format("Bad value for \"%s\": %s; ", path().c_str(), error_msg.c_str()); error_msg += "No changes have been committed. "; error_msg += "Correct this error and try again."; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } } // Check that the operator is OK base_cmd = _template_tree_node->const_command("%allow-operator"); if (base_cmd == NULL) { /* no explicit command, so only ":" is allowed */ if (_operator != OP_NONE && _operator != OP_ASSIGN) { error_msg = c_format("Bad operator for \"%s\": " "operator %s was specified, " "only ':' is allowed\n", path().c_str(), operator_to_str(_operator).c_str()); error_msg += "No changes have been committed. "; error_msg += "Correct this error and try again."; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } } else { const AllowCommand* allow_cmd; debug_msg("found ALLOW command: %s\n", cmd->str().c_str()); allow_cmd = dynamic_cast(base_cmd); XLOG_ASSERT(allow_cmd != NULL); if (allow_cmd->verify_variables(*this, error_msg) != true) { error_msg = c_format("Bad operator for \"%s\": %s; ", path().c_str(), error_msg.c_str()); error_msg += "No changes have been committed. "; error_msg += "Correct this error and try again."; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } } // // Next, we run any "create" or "set" commands. // Note that if there is no "create" command, then we run // the "set" command instead. // base_cmd = NULL; if (_existence_committed == false) base_cmd = _template_tree_node->const_command("%create"); if (base_cmd == NULL) base_cmd = _template_tree_node->const_command("%set"); if (base_cmd == NULL) { debug_msg("no appropriate command found\n"); } else { cmd = reinterpret_cast(base_cmd); debug_msg("found commands: %s\n", cmd->str().c_str()); int actions = cmd->execute(*this, task_manager); if (actions < 0) { error_msg = c_format("Parameter error for \"%s\"\n", path().c_str()); error_msg += "No changes have been committed.\n"; error_msg += "Correct this error and try again.\n"; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } _actions_pending += actions; } } } } list sorted_children = _children; sort_by_template(sorted_children); list::iterator iter, prev_iter; iter = sorted_children.begin(); if (changes_made) last_depth = depth; while (iter != sorted_children.end()) { prev_iter = iter; ++iter; debug_msg(" child: %s\n", (*prev_iter)->path().c_str()); string child_error_msg; MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*prev_iter); success = child->commit_changes(task_manager, do_commit, depth + 1, last_depth, child_error_msg, needs_activate, needs_update); error_msg += child_error_msg; if (success == false) { return false; } } // // Take care of %activate and %update commands on the way back out. // do { if (_deleted) break; if (_template_tree_node == NULL) break; // The %activate command if (needs_activate || (_existence_committed == false)) { base_cmd = _template_tree_node->const_command("%activate"); if (base_cmd == NULL) { if (_existence_committed == false) needs_activate = true; } else { cmd = reinterpret_cast(base_cmd); debug_msg("found commands: %s\n", cmd->str().c_str()); needs_activate = false; int actions = cmd->execute(*this, task_manager); if (actions < 0) { error_msg = c_format("Parameter error for \"%s\"\n", path().c_str()); error_msg += "No changes have been committed.\n"; error_msg += "Correct this error and try again.\n"; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } _actions_pending += actions; } break; } // The %update command if (needs_update || (_value_committed == false)) { base_cmd = _template_tree_node->const_command("%update"); if (base_cmd == NULL) { if (_value_committed == false) needs_update = true; } else { cmd = reinterpret_cast(base_cmd); debug_msg("found commands: %s\n", cmd->str().c_str()); needs_update = false; int actions = cmd->execute(*this, task_manager); if (actions < 0) { error_msg = c_format("Parameter error for \"%s\"\n", path().c_str()); error_msg += "No changes have been committed.\n"; error_msg += "Correct this error and try again.\n"; XLOG_WARNING("%s\n", error_msg.c_str()); return false; } _actions_pending += actions; } break; } break; } while (false); if (_template_tree_node != NULL) { base_cmd = _template_tree_node->const_command("%modinfo"); if (base_cmd != NULL) { const ModuleCommand* modcmd = dynamic_cast(base_cmd); if (modcmd != NULL) { if (modcmd->end_transaction(*this, task_manager) != XORP_OK) { error_msg = c_format("End Transaction failed for module %s\n", modcmd->module_name().c_str()); return false; } } } } debug_msg("Result: %s\n", error_msg.c_str()); debug_msg("COMMIT, leaving node >%s<\n", _path.c_str()); debug_msg("final node %s actions_pending = %d\n", _segname.c_str(), _actions_pending); return success; } bool MasterConfigTreeNode::check_commit_status(string& error_msg) const { debug_msg("ConfigTreeNode::check_commit_status %s\n", _segname.c_str()); if ((_existence_committed == false) || (_value_committed == false)) { XLOG_ASSERT(_actions_pending == 0); if (_actions_succeeded == false) { error_msg = "WARNING: Commit Failed\n"; error_msg += c_format(" Error in %s command for %s\n", _cmd_that_failed->str().c_str(), _path.c_str()); error_msg += c_format(" State may be partially committed - " "suggest reverting to previous state\n"); return false; } if (_deleted) { const BaseCommand* cmd = _template_tree_node->const_command("%delete"); if (cmd != NULL) { // // No need to check the children if we succeeded in // deleting this node. // return true; } } } list::const_iterator iter; bool result = true; for (iter = _children.begin(); iter != _children.end(); ++iter) { MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*iter); debug_msg(" child: %s\n", child->path().c_str()); result = child->check_commit_status(error_msg); if (result == false) return false; } return result; } void MasterConfigTreeNode::finalize_commit() { debug_msg("MasterConfigTreeNode::finalize_commit %s\n", _segname.c_str()); if (_deleted) { debug_msg("node deleted\n"); // // Delete the entire subtree. We expect a deleted node here // to have previously had its existence committed // (_existence_committed == true) but its non-existence not // previously committed (_value_committed == false) // XLOG_ASSERT(_existence_committed); XLOG_ASSERT(!_value_committed); delete_subtree_silently(); // No point in going further return; } if ((_existence_committed == false) || (_value_committed == false)) { debug_msg("node finalized\n"); XLOG_ASSERT(_actions_pending == 0); XLOG_ASSERT(_actions_succeeded); _existence_committed = true; _value_committed = true; _deleted = false; _committed_value = _value; _committed_operator = _operator; _committed_user_id = _user_id; _committed_modification_time = _modification_time; } // // Note: finalize_commit may delete the child, so we need to be // careful the iterator stays valid. // list::iterator iter, prev_iter; iter = _children.begin(); while (iter != _children.end()) { prev_iter = iter; ++iter; MasterConfigTreeNode *child = (MasterConfigTreeNode*)(*prev_iter); child->finalize_commit(); } }