| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "ZkTreeUtil.h" |
| |
| #include <map> |
| #include <iostream> |
| #include <log4cxx/logger.h> |
| #include <boost/algorithm/string.hpp> |
| #include <boost/algorithm/string/split.hpp> |
| |
| namespace zktreeutil |
| { |
| using std::map; |
| using std::pair; |
| |
| static ZkTreeNodeSptr loadZkTree_ (ZooKeeperAdapterSptr zkHandle, |
| const string& path) |
| { |
| // Extract the node value |
| string value = zkHandle->getNodeData(path); |
| |
| // Extract nodename from the path |
| string nodename = "/"; |
| if (path != "/") |
| { |
| vector< string > nodes; |
| boost::split(nodes, path, boost::is_any_of ("/") ); |
| nodename = nodes[nodes.size()-1]; |
| } |
| |
| // Create tree-node with name and value |
| ZkTreeNodeSptr nodeSptr = ZkTreeNodeSptr (new ZkTreeNode (nodename, value)); |
| std::cerr << "[zktreeutil] loaded nodename: " |
| << nodename |
| << " value: " |
| << value |
| << std::endl; |
| |
| // Load all the children |
| vector< string > cnodes = zkHandle->getNodeChildren (path); |
| for (unsigned i = 0; i < cnodes.size(); i++) |
| nodeSptr->addChild (loadZkTree_ (zkHandle, cnodes[i])); |
| |
| // Return the constructed node |
| return nodeSptr; |
| } |
| |
| static ZkTreeNodeSptr loadZkTreeXml_ (xmlNode* xmlNodePtr) |
| { |
| // Null check |
| if (xmlNodePtr == NULL) |
| { |
| std::cerr << "[zktreeutil] empty XML node encountered" << std::endl; |
| exit (-1); |
| } |
| |
| // Get the node name |
| xmlChar* name = xmlGetProp (xmlNodePtr, BAD_CAST "name"); |
| string nameStr = (const char*)name; |
| std::cerr << "[zktreeutil] node name: " << nameStr; |
| xmlFree (name); |
| // Get the node value |
| string valueStr; |
| xmlChar* value = xmlGetProp (xmlNodePtr, BAD_CAST "value"); |
| if (value) |
| { |
| valueStr = (const char*)value; |
| std::cerr << " value: " << valueStr; |
| } |
| xmlFree (value); |
| // Get the ignore flag |
| bool doIgnore = false; |
| xmlChar* ignore = xmlGetProp (xmlNodePtr, BAD_CAST "ignore"); |
| if (ignore) |
| { |
| string ignoreStr = (const char*) ignore; |
| if (ignoreStr == "true" || ignoreStr == "yes" || ignoreStr == "1") |
| { |
| doIgnore = true; |
| std::cerr << " <ignore:>"; |
| } |
| } |
| xmlFree (ignore); |
| std::cerr << std::endl; |
| |
| // Create the zk node |
| ZkTreeNodeSptr nodeSptr = |
| ZkTreeNodeSptr (new ZkTreeNode (nameStr, |
| ZkNodeData (valueStr, doIgnore))); |
| |
| // Load the children |
| for (xmlNode* chldNode = xmlNodePtr->children; |
| chldNode; |
| chldNode = chldNode->next) |
| if (chldNode->type == XML_ELEMENT_NODE) |
| nodeSptr->addChild (loadZkTreeXml_ (chldNode)); |
| |
| // Return the loaded node |
| return nodeSptr; |
| } |
| |
| static void writeZkTree_ (ZooKeeperAdapterSptr zkHandle, |
| const ZkTreeNodeSptr zkNodeSptr, |
| const string& path) |
| { |
| // Create the path in zk-tree |
| zkHandle->createNode(path.c_str(), "", 0, false); |
| std::cerr << "[zktreeutil] created key: " << path << std::endl; |
| // Set value for the path |
| string value = zkNodeSptr->getData().value; |
| if (value != "") |
| { |
| zkHandle->setNodeData (path.c_str(), value.c_str()); |
| std::cerr << "[zktreeutil] set value: " << std::endl; |
| } |
| |
| // Go deep to write the subtree rooted in the node, if not to be ignored |
| if (!(zkNodeSptr->getData().ignoreUpdate)) |
| { |
| for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) |
| { |
| ZkTreeNodeSptr childNodeSptr = zkNodeSptr->getChild (i); |
| // Add the node name into the path and write in zk-tree |
| string cpath = ((path != "/")? path : "") |
| + string("/") |
| + childNodeSptr->getKey(); |
| writeZkTree_ (zkHandle, childNodeSptr, cpath); |
| } |
| } |
| |
| return; |
| } |
| |
| static void addTreeZkAction_ (const ZkTreeNodeSptr zkNodeSptr, |
| const string& path, |
| vector< ZkAction >& actions) |
| { |
| // Create the key |
| actions.push_back (ZkAction (ZkAction::CREATE, path)); |
| |
| // Set value for the new key |
| if (zkNodeSptr->getData().value != "") |
| actions.push_back (ZkAction (ZkAction::VALUE, |
| path, |
| zkNodeSptr->getData().value)); |
| |
| // Add all the children |
| for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) |
| { |
| ZkTreeNodeSptr childSptr = zkNodeSptr->getChild (i); |
| string cpath = path + string("/") + childSptr->getKey(); |
| addTreeZkAction_ (childSptr, cpath, actions); |
| } |
| |
| return; |
| } |
| |
| static xmlNodePtr dumpZkTreeXml_ (const ZkTreeNodeSptr zkNodeSptr) |
| { |
| // Create xml node with zknode name and value |
| string nodename = zkNodeSptr->getKey (); |
| string value = zkNodeSptr->getData().value; |
| xmlNodePtr node = xmlNewNode(NULL, BAD_CAST "zknode"); |
| xmlNewProp (node, BAD_CAST "name", BAD_CAST nodename.c_str()); |
| if (value.length()) |
| xmlNewProp (node, BAD_CAST "value", BAD_CAST value.c_str()); |
| |
| // Add all the children rotted at this node |
| for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) |
| xmlAddChild (node, dumpZkTreeXml_ (zkNodeSptr->getChild (i))); |
| |
| // Return xml node |
| return node; |
| } |
| |
| static void dumpZkTree_ (const ZkTreeNodeSptr zkNodeSptr, |
| int maxLevel, |
| int level, |
| vector< bool >& masks) |
| { |
| // Check the max. dlevel to be dumped |
| if (level > maxLevel) |
| return; |
| |
| |
| // Create branch |
| for (int i=0; i < level; i++) |
| { |
| if ( i== level-1) std::cout << "| "; |
| else if (masks[i]) std::cout << " "; |
| else std::cout << "| "; |
| } |
| std::cout << std::endl; |
| for (int i=0; i < level-1; i++) |
| { |
| if (masks[i]) std::cout << " "; |
| else std::cout << "| "; |
| } |
| |
| // Dump the node name and value |
| std::cout << "|--[" << zkNodeSptr->getKey(); |
| if (zkNodeSptr->getData().value != "") |
| std::cout << " => " << zkNodeSptr->getData().value; |
| std::cout << "]" << std::endl; |
| |
| // Dump all the children |
| for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) |
| { |
| // Add mask for last child |
| if (i == zkNodeSptr->numChildren()-1) |
| masks.push_back(true); |
| else |
| masks.push_back(false); |
| dumpZkTree_ (zkNodeSptr->getChild (i), maxLevel, level+1, masks); |
| } |
| |
| masks.pop_back(); |
| return; |
| } |
| |
| static ZkTreeNodeSptr traverseBranch_ (const ZkTreeNodeSptr& zkRootSptr, |
| const string& path) |
| { |
| // Check if the tree is loaded into memory |
| if (zkRootSptr == NULL) |
| { |
| string errMsg = "[zktreeutil] null root passed for traversing"; |
| std::cout << errMsg << std::endl; |
| throw std::logic_error (errMsg); |
| } |
| |
| // Split the path and add intermediate znodes |
| vector< string > nodes; |
| boost::split(nodes, path, boost::is_any_of ("/") ); |
| |
| // Start traversing the tree |
| ZkTreeNodeSptr currNodeSptr = zkRootSptr; |
| for (unsigned znode_idx = 1; znode_idx < nodes.size(); znode_idx++) |
| { |
| bool found = false; |
| for (unsigned i=0; i < currNodeSptr->numChildren(); i++) |
| { |
| ZkTreeNodeSptr childNodeSptr = currNodeSptr->getChild(i); |
| if (childNodeSptr->getKey() == nodes[znode_idx]) |
| { |
| // Found! go to the znode |
| currNodeSptr = childNodeSptr; |
| found = true; |
| break; |
| } |
| } |
| if (!found) // No such znode found; return NULL node-ptr |
| { |
| string errMsg = string("[zktreeutil] unknown znode during traversal: ") |
| + nodes[znode_idx]; |
| std::cout << errMsg << std::endl; |
| throw std::logic_error (errMsg); |
| } |
| } |
| |
| return currNodeSptr; |
| } |
| |
| static ZkTreeNodeSptr createAncestors_ (const string& path) |
| { |
| // Create the root znode |
| ZkTreeNodeSptr zkRootSptr = ZkTreeNodeSptr (new ZkTreeNode ("/")); |
| ZkTreeNodeSptr currNodeSptr = zkRootSptr; |
| // Split the path and add intermediate znodes |
| vector< string > nodes; |
| boost::split(nodes, path, boost::is_any_of ("/") ); |
| for (unsigned i=1; i < nodes.size()-1; i++) |
| { |
| ZkTreeNodeSptr childNodeSptr = ZkTreeNodeSptr (new ZkTreeNode (nodes[i])); |
| currNodeSptr->addChild (childNodeSptr); |
| currNodeSptr = childNodeSptr; |
| } |
| |
| //Return the root of the branch |
| return zkRootSptr; |
| } |
| |
| ZooKeeperAdapterSptr ZkTreeUtil::get_zkHandle (const string& zkHosts) |
| { |
| try |
| { |
| // Create an instance of ZK adapter. |
| ZooKeeperConfig config (zkHosts, 10000); |
| ZooKeeperAdapterSptr zkHandleSptr = |
| ZooKeeperAdapterSptr (new ZooKeeperAdapter (config)); |
| return zkHandleSptr; |
| } |
| catch (const ZooKeeperException &e) |
| { |
| std::cerr << "[zktreeutil] zooKeeper exception caught: " |
| << e.what() |
| << std::endl; |
| throw; |
| } |
| catch (std::exception &stde) |
| { |
| std::cerr << "[zktreeutil] standard exception caught: " |
| << stde.what() |
| << std::endl; |
| throw; |
| } |
| catch (...) |
| { |
| std::cerr |
| << "[zktreeutil] unknown exception while connecting to zookeeper" |
| << std::endl; |
| throw; |
| } |
| } |
| |
| |
| void ZkTreeUtil::loadZkTree (const string& zkHosts, |
| const string& path, |
| bool force) |
| { |
| // Check if already loaded |
| if (loaded_ && !force) |
| { |
| std::cerr << "[zktreeutil] zk-tree already loaded into memory" |
| << std::endl; |
| return; |
| } |
| |
| // Connect to ZK server |
| ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); |
| std::cerr << "[zktreeutil] connected to ZK serverfor reading" |
| << std::endl; |
| |
| // Check the existence of the path to znode |
| if (!zkHandle->nodeExists (path)) |
| { |
| string errMsg = string("[zktreeutil] path does not exists : ") + path; |
| std::cout << errMsg << std::endl; |
| throw std::logic_error (errMsg); |
| } |
| |
| // Load the rooted (sub)tree |
| ZkTreeNodeSptr zkSubrootSptr = loadZkTree_ (zkHandle, path); |
| |
| // Create the ancestors before loading the rooted subtree |
| if (path != "/") |
| { |
| zkRootSptr_ = createAncestors_(path); |
| string ppath = path.substr (0, path.rfind('/')); |
| ZkTreeNodeSptr parentSptr = traverseBranch_( zkRootSptr_, ppath); |
| parentSptr->addChild (zkSubrootSptr); |
| } |
| else // Loaded entire zk-tree |
| { |
| zkRootSptr_ = zkSubrootSptr; |
| } |
| |
| // Set load flag |
| loaded_ = true; |
| return; |
| } |
| |
| void ZkTreeUtil::loadZkTreeXml (const string& zkXmlConfig, |
| bool force) |
| { |
| // Check if already loaded |
| if (loaded_ && !force) |
| { |
| std::cerr << "[zktreeutil] zk-tree already loaded into memory" |
| << std::endl; |
| return; |
| } |
| |
| // Parse the file and get the DOM |
| xmlDocPtr docPtr = xmlReadFile(zkXmlConfig.c_str(), NULL, 0); |
| if (docPtr == NULL) { |
| std::cerr << "[zktreeutil] could not parse XML file " |
| << zkXmlConfig |
| << std::endl; |
| exit (-1); |
| } |
| std::cerr << "[zktreeutil] zk-tree XML parsing successful" |
| << std::endl; |
| |
| // Get the root element node |
| xmlNodePtr rootPtr = xmlDocGetRootElement(docPtr); |
| // Create the root zk node |
| zkRootSptr_ = ZkTreeNodeSptr (new ZkTreeNode ("/")); |
| // Load the rooted XML tree |
| for (xmlNode* chldNode = rootPtr->children; |
| chldNode; |
| chldNode = chldNode->next) |
| { |
| if (chldNode->type == XML_ELEMENT_NODE) |
| zkRootSptr_->addChild (loadZkTreeXml_ (chldNode)); |
| } |
| |
| // set oad flag |
| loaded_ = true; |
| // Cleanup stuff |
| xmlFreeDoc(docPtr); |
| xmlCleanupParser(); |
| return; |
| } |
| |
| void ZkTreeUtil::writeZkTree (const string& zkHosts, |
| const string& path, |
| bool force) const |
| { |
| // Connect to ZK server |
| ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); |
| std::cerr << "[zktreeutil] connected to ZK server for writing" |
| << std::endl; |
| |
| // Go to the rooted subtree |
| ZkTreeNodeSptr zkRootSptr = traverseBranch_ (zkRootSptr_, path); |
| |
| // Cleanup before write if forceful write enabled |
| if (force) |
| { |
| if (path != "/") // remove the subtree rooted at the znode |
| { |
| // Delete the subtree rooted at the znode before write |
| if (zkHandle->nodeExists (path)) |
| { |
| std::cerr << "[zktreeutil] deleting subtree rooted at " |
| << path |
| << "..." |
| << std::endl; |
| zkHandle->deleteNode (path, true); |
| } |
| } |
| else // remove the rooted znodes |
| { |
| std::cerr << "[zktreeutil] deleting rooted zk-tree" |
| << "..." |
| << std::endl; |
| // Get the root's children |
| vector< string > cnodes = zkHandle->getNodeChildren ("/"); |
| for (unsigned i=0; i < cnodes.size(); i++) |
| { |
| if ( cnodes[i] != "/zookeeper") // reserved for zookeeper use |
| zkHandle->deleteNode(cnodes[i], true); |
| } |
| } |
| } |
| |
| // Start tree construction |
| writeZkTree_ (zkHandle, zkRootSptr, path); |
| return; |
| } |
| |
| void ZkTreeUtil::dumpZkTree (bool xml, int depth) const |
| { |
| if (xml) |
| { |
| // Creates a new document, a node and set it as a root node |
| xmlDocPtr docPtr = xmlNewDoc(BAD_CAST "1.0"); |
| xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "root"); |
| xmlDocSetRootElement(docPtr, rootNode); |
| |
| // Add all the rooted children |
| for (unsigned i=0; i < zkRootSptr_->numChildren(); i++) |
| xmlAddChild (rootNode, dumpZkTreeXml_ (zkRootSptr_->getChild (i))); |
| |
| // Dumping document to stdio or file |
| xmlSaveFormatFileEnc("-", docPtr, "UTF-8", 1); |
| |
| // Cleanup stuff |
| xmlFreeDoc(docPtr); |
| xmlCleanupParser(); |
| return; |
| } |
| |
| // Dump text |
| std::cout << "/" << std::endl; |
| vector< bool > masks; |
| for (unsigned i=0; i < zkRootSptr_->numChildren(); i++) |
| { |
| if (i == zkRootSptr_->numChildren()-1) |
| masks.push_back(true); |
| else |
| masks.push_back(false); |
| dumpZkTree_ (zkRootSptr_->getChild (i), depth, 1, masks); |
| } |
| |
| return; |
| } |
| |
| vector< ZkAction > ZkTreeUtil::diffZkTree (const string& zkHosts, |
| const string& path) const |
| { |
| // Action container |
| vector< ZkAction > actions; |
| |
| if (!loaded_) |
| { |
| std::cout << "[zktreeutil] zk-tree not loaded for diff" |
| << std::endl; |
| exit (-1); |
| } |
| |
| // Load the rooted subtree from zookeeper |
| ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); |
| std::cerr << "[zktreeutil] connected to ZK server for reading" |
| << std::endl; |
| ZkTreeNodeSptr zkLiveRootSptr = loadZkTree_ (zkHandle, path); |
| |
| // Go to the saved rooted subtree |
| ZkTreeNodeSptr zkLoadedRootSptr = |
| traverseBranch_ (zkRootSptr_, path); |
| |
| // Check the root value first |
| if (zkLoadedRootSptr->getData().value |
| != zkLiveRootSptr->getData().value) |
| { |
| actions.push_back (ZkAction (ZkAction::VALUE, |
| path, |
| zkLoadedRootSptr->getData().value, |
| zkLiveRootSptr->getData().value)); |
| } |
| |
| // Start traversal from root |
| vector< string > ppaths; |
| vector< pair< ZkTreeNodeSptr, ZkTreeNodeSptr > > commonNodes; |
| ppaths.push_back ((path != "/")? path : ""); |
| commonNodes.push_back (pair< ZkTreeNodeSptr, ZkTreeNodeSptr > |
| (zkLoadedRootSptr, zkLiveRootSptr)); |
| |
| for (unsigned j=0; j < commonNodes.size(); j++) |
| { |
| // Get children of loaded tree |
| map< string, ZkTreeNodeSptr > loadedChildren; |
| for (unsigned i=0; i < commonNodes[j].first->numChildren(); i++) |
| { |
| ZkTreeNodeSptr childSptr = commonNodes[j].first->getChild (i); |
| loadedChildren[childSptr->getKey()] = childSptr; |
| } |
| // Get children of live tree |
| map< string, ZkTreeNodeSptr > liveChildren; |
| for (unsigned i=0; i < commonNodes[j].second->numChildren(); i++) |
| { |
| ZkTreeNodeSptr childSptr = commonNodes[j].second->getChild (i); |
| liveChildren[childSptr->getKey()] = childSptr; |
| } |
| |
| // Start comparing the children |
| for (map< string, ZkTreeNodeSptr >::const_iterator it = |
| loadedChildren.begin(); |
| it != loadedChildren.end(); |
| it++) |
| { |
| bool ignoreKey = it->second->getData().ignoreUpdate; |
| string loadedVal = it->second->getData().value; |
| // Path to this node |
| string path = ppaths[j] + string("/") + it->first; |
| |
| map< string, ZkTreeNodeSptr >::const_iterator jt = |
| liveChildren.find (it->first); |
| if (jt != liveChildren.end()) |
| { |
| // Key is present in live zk-tree |
| string liveVal = jt->second->getData().value; |
| // Check value for the key, if not ignored |
| if (!ignoreKey) |
| { |
| if (loadedVal != liveVal) |
| { |
| // Value differs, set the new value for the key |
| actions.push_back (ZkAction (ZkAction::VALUE, |
| path, |
| loadedVal, |
| liveVal)); |
| } |
| |
| // Add node to common nodes |
| ppaths.push_back (path); |
| commonNodes.push_back (pair< ZkTreeNodeSptr, ZkTreeNodeSptr > |
| (it->second, jt->second)); |
| } |
| |
| // Remove the live zk node |
| liveChildren.erase (it->first); |
| } |
| else |
| { |
| // Add the subtree rooted to this node, if not ignored |
| if (!ignoreKey) |
| addTreeZkAction_ (it->second, path, actions); |
| } |
| } |
| |
| // Remaining live zk nodes to be deleted |
| for (map< string, ZkTreeNodeSptr >::const_iterator it = liveChildren.begin(); |
| it != liveChildren.end(); it++) |
| { |
| string path = ppaths[j] + string("/") + it->first; |
| actions.push_back (ZkAction (ZkAction::DELETE, path)); |
| } |
| } |
| // return the diff actions |
| return actions; |
| } |
| |
| void ZkTreeUtil::executeZkActions (const string& zkHosts, |
| const vector< ZkAction >& zkActions, |
| int execFlags) const |
| { |
| // Execute the diff zk actions |
| if (zkActions.size()) |
| { |
| // Connect to Zookeeper for writing |
| ZooKeeperAdapterSptr zkHandleSptr; |
| if ((execFlags & EXECUTE) |
| || (execFlags & INTERACTIVE)) |
| { |
| zkHandleSptr = get_zkHandle (zkHosts); |
| std::cerr << "[zktreeutil] connected to ZK server for writing" |
| << std::endl; |
| } |
| |
| for (unsigned i=0; i < zkActions.size(); i++) |
| { |
| if (zkActions[i].action == ZkAction::CREATE) |
| { |
| if (execFlags & PRINT) |
| std::cout << "CREAT- key:" << zkActions[i].key << std::endl; |
| if (execFlags & EXECUTE) |
| { |
| if (execFlags & INTERACTIVE) |
| { |
| string resp; |
| std::cout << "Execute this action?[yes/no]: "; |
| std::getline(std::cin, resp); |
| if (resp != "yes") |
| continue; |
| } |
| zkHandleSptr->createNode(zkActions[i].key.c_str(), "", 0, false); |
| } |
| } |
| else if (zkActions[i].action == ZkAction::DELETE) |
| { |
| if (execFlags & PRINT) |
| std::cout << "DELET- key:" << zkActions[i].key << std::endl; |
| if (execFlags & EXECUTE) |
| { |
| if (execFlags & INTERACTIVE) |
| { |
| string resp; |
| std::cout << "Execute this action?[yes/no]: "; |
| std::getline(std::cin, resp); |
| if (resp != "yes") |
| continue; |
| } |
| zkHandleSptr->deleteNode(zkActions[i].key.c_str(), true); |
| } |
| } |
| else if (zkActions[i].action == ZkAction::VALUE) |
| { |
| if (execFlags & PRINT) |
| { |
| std::cout << "VALUE- key:" |
| << zkActions[i].key |
| << " value:" << zkActions[i].newval; |
| if (zkActions[i].oldval != "") |
| std::cout << " old_value:" << zkActions[i].oldval; |
| std::cout << std::endl; |
| } |
| if (execFlags & EXECUTE) |
| { |
| if (execFlags & INTERACTIVE) |
| { |
| string resp; |
| std::cout << "Execute this action?[yes/no]: "; |
| std::getline(std::cin, resp); |
| if (resp != "yes") |
| continue; |
| } |
| zkHandleSptr->setNodeData (zkActions[i].key, zkActions[i].newval); |
| } |
| } |
| } |
| } |
| |
| return; |
| } |
| |
| } |
| |