diff --git a/configuration/CMakeLists.txt b/configuration/CMakeLists.txt index 7f07597f482..63ff7ebf43c 100644 --- a/configuration/CMakeLists.txt +++ b/configuration/CMakeLists.txt @@ -1,5 +1,5 @@ ################################################################################ -# HPCC SYSTEMS software Copyright (C) 2016 HPCC Systems. +# HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,3 +15,4 @@ ################################################################################ HPCC_ADD_SUBDIRECTORY (configmgr) +HPCC_ADD_SUBDIRECTORY (cli) diff --git a/configuration/cli/CMakeLists.txt b/configuration/cli/CMakeLists.txt new file mode 100644 index 00000000000..dccf62a128c --- /dev/null +++ b/configuration/cli/CMakeLists.txt @@ -0,0 +1,17 @@ +################################################################################ +# HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems. +# +# Licensed 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. +################################################################################ + +HPCC_ADD_SUBDIRECTORY (envmod) diff --git a/configuration/cli/envmod/CMakeLists.txt b/configuration/cli/envmod/CMakeLists.txt new file mode 100644 index 00000000000..afb8a51ca51 --- /dev/null +++ b/configuration/cli/envmod/CMakeLists.txt @@ -0,0 +1,34 @@ +################################################################################ +# HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems. +# +# Licensed 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. +################################################################################ + +project ( envmod ) + +SET (SRC_FILES + envmod.cpp +) + +INCLUDE_DIRECTORIES( + ${CMAKE_BINARY_DIR} + ${HPCC_SOURCE_DIR}/system/include + ${HPCC_SOURCE_DIR}/configuration/configmgr/configmgrlib + ${HPCC_SOURCE_DIR}/configuration/configmgr/RapidJSON/include +) + +HPCC_ADD_EXECUTABLE ( envmod ${SRC_FILES} ) + +INSTALL ( TARGETS envmod RUNTIME DESTINATION ${EXEC_DIR} ) + +TARGET_LINK_LIBRARIES(envmod configmgr) diff --git a/configuration/cli/envmod/envmod.cpp b/configuration/cli/envmod/envmod.cpp new file mode 100644 index 00000000000..dc65b0c41a1 --- /dev/null +++ b/configuration/cli/envmod/envmod.cpp @@ -0,0 +1,414 @@ +/*############################################################################## + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + Licensed 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 +#include +#include +#include +#include "build-config.h" +#include "platform.h" +#include +#include +#include +#include "EnvironmentMgr.hpp" +#include "Exceptions.hpp" +#include "mod_template_support/EnvModTemplate.hpp" +#include "mod_template_support/TemplateExecutionException.hpp" + +// +// Inputs file format (json) +// +// { +// "input-name" : [ ], +// ... +// } + +bool processOptions(int argc, char *vargv[]); +bool validate(); +std::string getNextArg(int argc, char *argv[], int idx); +bool dirExists(const std::string &dirName); +bool fileExists(const std::string &fileName); +std::vector splitString(const std::string &input, const std::string &delim); + +// Default configuration directories +EnvironmentType envType = XML; +std::string masterSchemaFile = "environment.xsd"; +std::string configSchemaDir = COMPONENTFILES_DIR PATHSEPSTR "configschema" PATHSEPSTR "xsd" PATHSEPSTR; +std::string modTemplateFile; +std::string modTemplateSchemaFile = COMPONENTFILES_DIR PATHSEPSTR "configschema" PATHSEPSTR "templates" PATHSEPSTR "schema" PATHSEPSTR "ModTemplateSchema.json"; +std::string configSchemaPluginsDir = "xsd" PATHSEPSTR "plugins" PATHSEPSTR; +std::string envFile, envOutputFile; +struct InputDef { + std::string inputName; + std::string inputValue; +}; + +std::vector userInputs; +bool listInputs = false; + +EnvironmentMgr *pEnvMgr = nullptr; +EnvModTemplate *pTemplate = nullptr; + +class CliException : public std::exception +{ + public: + + explicit CliException(std::string reason) : m_reason(std::move(reason)) { } + virtual const char *what() const noexcept override + { + return m_reason.c_str(); + } + + private: + + std::string m_reason; +}; + + +void usage() +{ + // + // usage below documents options + std::cout << std::endl; + std::cout << "envmod envfile" << std::endl; + std::cout << std::endl; + std::cout << " Configuration Options:" << std::endl; + std::cout << " -d --schema-dir : path to schema files. default (" << configSchemaDir << ")" << std::endl; + std::cout << " -p --schema-plugins-dir : path to plugin files, default (" << configSchemaPluginsDir << ")" << std::endl; + std::cout << " -m --master-config : name of master schema file, default (" << masterSchemaFile << ")" << std::endl; + std::cout << std::endl; + std::cout << " Execution Options:" << std::endl; + std::cout << " -t --template : filepath to modification template - required" << std::endl; + std::cout << " -e --env : Optional filepath to environment file to modify" << std::endl; + std::cout << " Omit to validate the modification template" << std::endl; + std::cout << " --inputs : List the template inputs. If this option is specified, the template" << std::endl; + std::cout << " inputs are listed and the command exits." << std::endl; + std::cout << " --input-file : Optional full path to a file with defined inputs for the template" << std::endl; + std::cout << " These would be combined with any command line inputs" << std::endl; + std::cout << " -i --input : Assign the indicated value to the variable input." << std::endl; + std::cout << " The input must be defined in the template" << std::endl; + std::cout << " value may be a comma separated list (no blanks) for multi-value inputs" << std::endl; + std::cout << " Inputs given on the command line override any in an input file (--input-file)" << std::endl; + std::cout << " -o --output [fullpath] : Optional. If present, results of applying template are saved." << std::endl; + std::cout << " If fullpath is specified, results written to this file, " << std::endl; + std::cout << " If not specified, input env file is overwritten" << std::endl; + std::cout << " Omit the -o option to test the modification template" << std::endl; + std::cout << std::endl; +} + + +int main(int argc, char *argv[]) +{ + if (argc == 1) + { + usage(); + return 0; + } + + // + // Read options and validate + if (!processOptions(argc, argv)) + { + return 1; // get out now + } + + if (!validate()) + { + usage(); + return 1; + } + + // + // Create an environment manager reference and load the schema + try + { + pEnvMgr = getEnvironmentMgrInstance(envType); + std::cout << "Loading schema defined by " << masterSchemaFile << "..."; + + // note that these are hardcoded for HPCC at this time, but could be made into options + std::map cfgParms; + cfgParms["buildset"] = "buildset.xml"; // Not used right now, and probably never will be + cfgParms["support_libs"] = "libcfgsupport_addrequiredinstances"; + std::string pluginsPath = configSchemaPluginsDir; + + if (!pEnvMgr->loadSchema(configSchemaDir, masterSchemaFile, cfgParms)) + { + throw CliException(pEnvMgr->getLastSchemaMessage()); + } + } + catch (const ParseException &pe) + { + std::cout << "There was a problem creating the environment manager instance: " << pe.what() << std::endl; + } + catch(const CliException &e) + { + std::cout << "There was a problem loading the schema: " << e.what() << std::endl; + } + + + // + // Create the modification template + try + { + pTemplate = new EnvModTemplate(pEnvMgr, modTemplateSchemaFile); + pTemplate->loadTemplateFromFile(modTemplateFile); + } + catch (const TemplateException &te) + { + std::cout << "There was a problem loading the modification template: " << te.what() << std::endl; + return 1; + } + + // + // If list inputs was given, list them and get out + if (listInputs) + { + std::cout << "Template inputs:" << std::endl; + auto templateInputs = pTemplate->getVariables(); + for (auto &pInput : templateInputs) + { + std::cout << pInput->getName() << " - " << pInput->getDescription() << std::endl; + } + return 0; // get out + } + + // + // Process the input file if given here + + // + // If the user provided any inputs, assign them + if (!userInputs.empty()) + { + for (auto &userInput: userInputs) + { + try + { + auto pInput = pTemplate->getVariable(userInput.inputName); + auto values = splitString(userInput.inputValue, ","); + for (auto &value: values) + { + pInput->addValue(value); + } + } + catch (const TemplateException &te) + { + std::cout << "There was a problem: " << te.what() << std::endl; + return 0; // get out + } + } + } + + // + // If there is an environment, load it and apply + if (!envFile.empty()) + { + if (!pEnvMgr->loadEnvironment(envFile)) + { + std::cout << "There was a problem loading the environment: " << std::endl << pEnvMgr->getLastEnvironmentMessage() << std::endl; + return 1; + } + + try + { + pTemplate->execute(); + } + catch (const TemplateExecutionException &te) + { + std::cout << te.what() << std::endl; + return 1; + } + + // + // Write results + if (!envOutputFile.empty()) + { + pEnvMgr->saveEnvironment(envOutputFile); + std::cout << "Results written to " << envOutputFile << std::endl; + } + else + { + std::cout << "Resuls not saved." << std::endl; + } + } + else + { + std::cout << "No problems found in the modification template" << std::endl; + } + + std::cout << "Done" << std::endl; + return 0; +} + + +bool processOptions(int argc, char *argv[]) +{ + bool rc = true; + int idx = 1; + std::string optName, optVal; + bool checkDir = false; + + try + { + while (idx < argc) + { + optName = getNextArg(argc, argv, idx++); + + if (optName == "-d" || optName == "--schema-dir") + { + configSchemaDir = getNextArg(argc, argv, idx++) += PATHSEPSTR; + } + + else if (optName == "-p" || optName == "--schema-plugins-dir") + { + configSchemaPluginsDir = getNextArg(argc, argv, idx++) += PATHSEPSTR; + } + + else if (optName == "-m" || optName == "--master-config") + { + masterSchemaFile = getNextArg(argc, argv, idx++); + } + + else if (optName == "-t" || optName == "--template") + { + modTemplateFile = getNextArg(argc, argv, idx++); + } + else if (optName == "-e" || optName == "--env") + { + envFile = getNextArg(argc, argv, idx++); + } + else if (optName == "-o" || optName == "--output") + { + // this is the default if no filename given + envOutputFile = envFile; + if (idx < argc) + { + envOutputFile = getNextArg(argc, argv, idx++); + } + } + else if (optName == "-i" || optName == "--input") + { + std::string assign = getNextArg(argc, argv, idx++); + std::size_t equalPos = assign.find_first_of('='); + if (equalPos != std::string::npos) + { + InputDef input; + input.inputName = assign.substr(0, equalPos); + input.inputValue = assign.substr(equalPos+1); + userInputs.emplace_back(input); + } + else + { + throw CliException("Invalid input variable assignement: " + assign); + } + } + else if (optName == "--inputs") + { + listInputs = true; + } + + } + } + catch(const CliException &e) + { + std::cout << "There was an issue processing option: " << e.what() << std::endl; + rc = false; + } + return rc; +} + + +bool validate() +{ + + if (!dirExists(configSchemaDir)) + { + std::cout << "Schema directory " << configSchemaDir << " does not exist" << std::endl; + return false; + } + + if (!dirExists(configSchemaPluginsDir)) + { + std::cout << "Schema plugins directory " << configSchemaPluginsDir << " does not exist" << std::endl; + return false; + } + + if (!fileExists(configSchemaDir + masterSchemaFile)) + { + std::cout << "The master config file " << masterSchemaFile << " does not exist" << std::endl; + return false; + } + + if (!fileExists(modTemplateFile)) + { + std::cout << "The modification template " << modTemplateFile << " does not exist" << std::endl; + return false; + } + + if (!envFile.empty()) + { + if (!fileExists(envFile)) + { + std::cout << "The environment file " << envFile << " does not exist" << std::endl; + return false; + } + } + + return true; +} + + +std::string getNextArg(int argc, char *argv[], int idx) +{ + if (idx < argc) + { + return std::string(argv[idx]); + } + throw CliException("Arguments exhausted when more expected"); +} + + +bool dirExists(const std::string &dirName) +{ + bool rc = true; + struct stat info; + if (stat(dirName.c_str(), &info) != 0) + { + rc = false; + } + rc = ((info.st_mode&S_IFMT)==S_IFDIR); + return rc; +} + + +bool fileExists(const std::string &fileName) +{ + struct stat info; + return stat(fileName.c_str(), &info) == 0; +} + + +std::vector splitString(const std::string &input, const std::string &delim) +{ + size_t start = 0, end = 0, delimLen = delim.length(); + std::vector list; + + while (end != std::string::npos) + { + end = input.find(delim, start); + std::string item = input.substr(start, (end == std::string::npos) ? std::string::npos : end - start); + if (!item.empty()) + list.push_back(item); + start = ((end > (std::string::npos - delimLen)) ? std::string::npos : end + delimLen); + } + return list; +} diff --git a/configuration/configmgr/configmgrlib/CMakeLists.txt b/configuration/configmgr/configmgrlib/CMakeLists.txt index 79f1132d740..8a3c6fb383f 100644 --- a/configuration/configmgr/configmgrlib/CMakeLists.txt +++ b/configuration/configmgr/configmgrlib/CMakeLists.txt @@ -20,36 +20,39 @@ project ( configmgr ) if (USE_BOOST_REGEX) SET( SRCS - EnvironmentMgr.cpp - XMLEnvironmentMgr.cpp - XMLEnvironmentLoader.cpp - SchemaParser.cpp - SchemaItem.cpp - SchemaValue.cpp - SchemaTypeLimits.cpp - SchemaTypeIntegerLimits.cpp - SchemaTypeStringLimits.cpp - EnvironmentNode.cpp - EnvironmentValue.cpp - XSDComponentParser.cpp - XSDSchemaParser.cpp - XSDValueSetParser.cpp - Status.cpp - EnvironmentEventHandlers.cpp - Utils.cpp - InsertableItem.cpp - ConfigPath.cpp - EnvSupportLib.cpp - mod_template_support/EnvModTemplate.cpp - mod_template_support/Input.cpp - mod_template_support/TemplateException.cpp - mod_template_support/Operation.cpp - mod_template_support/OperationCreateNode.cpp - mod_template_support/Inputs.cpp - mod_template_support/IPAddressRangeInput.cpp - mod_template_support/IPAddressInput.cpp - mod_template_support/HostNameInput.cpp - mod_template_support/OperationNoop.cpp + + EnvironmentMgr.cpp + XMLEnvironmentMgr.cpp + XMLEnvironmentLoader.cpp + SchemaParser.cpp + SchemaItem.cpp + SchemaValue.cpp + SchemaTypeLimits.cpp + SchemaTypeIntegerLimits.cpp + SchemaTypeStringLimits.cpp + EnvironmentNode.cpp + EnvironmentValue.cpp + XSDComponentParser.cpp + XSDSchemaParser.cpp + XSDValueSetParser.cpp + Status.cpp + EnvironmentEventHandlers.cpp + Utils.cpp + InsertableItem.cpp + ConfigPath.cpp + EnvSupportLib.cpp + mod_template_support/EnvModTemplate.cpp + mod_template_support/Variable.cpp + mod_template_support/TemplateException.cpp + mod_template_support/Operation.cpp + mod_template_support/OperationCreateNode.cpp + mod_template_support/OperationFindNode.cpp + mod_template_support/OperationModifyNode.cpp + mod_template_support/Variables.cpp + mod_template_support/IPAddressRangeVariable.cpp + mod_template_support/IPAddressVariable.cpp + mod_template_support/HostNameVariable.cpp + mod_template_support/OperationDeleteNode.cpp ) INCLUDE_DIRECTORIES( diff --git a/configuration/configmgr/configmgrlib/EnvironmentMgr.cpp b/configuration/configmgr/configmgrlib/EnvironmentMgr.cpp index 844224b79c4..08dc30a78ed 100644 --- a/configuration/configmgr/configmgrlib/EnvironmentMgr.cpp +++ b/configuration/configmgr/configmgrlib/EnvironmentMgr.cpp @@ -208,7 +208,7 @@ std::shared_ptr EnvironmentMgr::getNewEnvironmentNode(const std { std::string itemType; std::vector initAttributeValues; - getInitAttributesFromItemType(inputItemType, itemType, initAttributeValues); + getPredefinedAttributeValues(inputItemType, itemType, initAttributeValues); std::shared_ptr pNewCfgItem = findInsertableItem(pParentNode, itemType); if (pNewCfgItem) { @@ -227,19 +227,21 @@ std::shared_ptr EnvironmentMgr::getNewEnvironmentNode(const std std::shared_ptr EnvironmentMgr::addNewEnvironmentNode(const std::string &parentNodeId, const std::string &inputItemType, - std::vector &initAttributes, Status &status) + std::vector &initAttributes, Status &status, bool allowInvalid, bool forceCreate) { std::shared_ptr pNewNode; std::shared_ptr pParentNode = findEnvironmentNodeById(parentNodeId); if (pParentNode) { std::string itemType; - std::vector initAttributeValues; - getInitAttributesFromItemType(inputItemType, itemType, initAttributeValues); + std::vector attributeValues; + getPredefinedAttributeValues(inputItemType, itemType, attributeValues); std::shared_ptr pNewCfgItem = findInsertableItem(pParentNode, itemType); if (pNewCfgItem) { - pNewNode = addNewEnvironmentNode(pParentNode, pNewCfgItem, initAttributes, status); + // concatenate input attribute values with any predefined values. + attributeValues.insert( attributeValues.end(), initAttributes.begin(), initAttributes.end() ); + pNewNode = addNewEnvironmentNode(pParentNode, pNewCfgItem, attributeValues, status, allowInvalid, forceCreate); if (pNewNode == nullptr) { status.addMsg(statusMsg::error, "Unable to create new node for itemType: " + inputItemType); @@ -263,7 +265,7 @@ std::shared_ptr EnvironmentMgr::addNewEnvironmentNode(const std std::shared_ptr EnvironmentMgr::addNewEnvironmentNode(const std::shared_ptr &pParentNode, const std::shared_ptr &pCfgItem, - std::vector &initAttributes, Status &status) + std::vector &initAttributes, Status &status, bool allowInvalid, bool forceCreate) { std::shared_ptr pNewEnvNode; @@ -274,7 +276,7 @@ std::shared_ptr EnvironmentMgr::addNewEnvironmentNode(const std addPath(pNewEnvNode); pNewEnvNode->initialize(); - pNewEnvNode->setAttributeValues(initAttributes, status, false, false); + pNewEnvNode->setAttributeValues(initAttributes, status, allowInvalid, forceCreate); pParentNode->addChild(pNewEnvNode); // @@ -309,22 +311,25 @@ std::shared_ptr EnvironmentMgr::addNewEnvironmentNode(const std std::shared_ptr EnvironmentMgr::findInsertableItem(const std::shared_ptr &pNode, const std::string &itemType) const { - std::shared_ptr pItem; + // todo -should there be an environment strict mode that forces schema compliance? + std::shared_ptr pSchemaItem = std::make_shared(itemType, "default"); // default item if none found std::vector insertableItems; pNode->getInsertableItems(insertableItems); for (auto &pInsertableIt: insertableItems) { if (pInsertableIt.m_pSchemaItem->getItemType() == itemType) { - pItem = pInsertableIt.m_pSchemaItem; + pSchemaItem = pInsertableIt.m_pSchemaItem; break; // we found the insertable item we wanted, so time to get out } } - return pItem; + + return pSchemaItem; } -void EnvironmentMgr::getInitAttributesFromItemType(const std::string &inputItemType, std::string &itemType, std::vector &initAttributes) const +void EnvironmentMgr::getPredefinedAttributeValues(const std::string &inputItemType, std::string &itemType, + std::vector &initAttributes) const { // // In case nothing specifed diff --git a/configuration/configmgr/configmgrlib/EnvironmentMgr.hpp b/configuration/configmgr/configmgrlib/EnvironmentMgr.hpp index 8b84f71c65b..8f4fccd1b57 100644 --- a/configuration/configmgr/configmgrlib/EnvironmentMgr.hpp +++ b/configuration/configmgr/configmgrlib/EnvironmentMgr.hpp @@ -54,8 +54,10 @@ class DECL_EXPORT EnvironmentMgr bool loadEnvironment(const std::string &qualifiedFilename); std::shared_ptr findEnvironmentNodeById(const std::string &nodeId) const; std::shared_ptr getNewEnvironmentNode(const std::string &parentNodeId, const std::string &inputItem, Status &status) const; - std::shared_ptr addNewEnvironmentNode(const std::string &parentNodeId, const std::string &configType, std::vector &initAttributes, Status &status); - std::shared_ptr addNewEnvironmentNode(const std::shared_ptr &pParentNode, const std::shared_ptr &pNewCfgItem, std::vector &initAttributes, Status &status); + std::shared_ptr addNewEnvironmentNode(const std::string &parentNodeId, const std::string &configType, + std::vector &initAttributes, Status &status, bool allowInvalid=false, bool forceCreate=false); + std::shared_ptr addNewEnvironmentNode(const std::shared_ptr &pParentNode, const std::shared_ptr &pNewCfgItem, + std::vector &initAttributes, Status &status, bool allowInvalid=false, bool forceCreate=false); bool removeEnvironmentNode(const std::string &nodeId); bool saveEnvironment(const std::string &qualifiedFilename); void discardEnvironment() { m_pRootNode = nullptr; m_nodeIds.clear();} @@ -76,7 +78,8 @@ class DECL_EXPORT EnvironmentMgr void assignNodeIds(const std::shared_ptr &pNode); void insertExtraEnvironmentData(std::shared_ptr pNode); std::shared_ptr findInsertableItem(const std::shared_ptr &pNode, const std::string &itemType) const; - void getInitAttributesFromItemType(const std::string &inputItemType, std::string &itemType, std::vector &initAttributes) const; + void getPredefinedAttributeValues(const std::string &inputItemType, std::string &itemType, + std::vector &initAttributes) const; protected: diff --git a/configuration/configmgr/configmgrlib/EnvironmentNode.cpp b/configuration/configmgr/configmgrlib/EnvironmentNode.cpp index a8b156dd6ae..ab8fd82b89b 100644 --- a/configuration/configmgr/configmgrlib/EnvironmentNode.cpp +++ b/configuration/configmgr/configmgrlib/EnvironmentNode.cpp @@ -160,7 +160,16 @@ void EnvironmentNode::setAttributeValue(const std::string &attrName, const std:: if (pEnvValue) { - pEnvValue->setValue(value, &status, allowInvalid); + // + // If the value is not empty, set the attribute to the new value. If empty, delete the attribute + if (!value.empty()) + { + pEnvValue->setValue(value, &status, allowInvalid); + } + else + { + m_attributes.erase(it); + } } else { @@ -385,10 +394,17 @@ void EnvironmentNode::doFetchNodes(ConfigPath &configPath, std::vectorisRoot()) { - std::shared_ptr pRoot = getRoot(); + std::shared_ptr pRoot = getRoot(); if (pRoot->getName() == pPathItem->getElementName()) { - pRoot->doFetchNodes(configPath, nodes); + if (configPath.isPathRemaining()) + { + pRoot->doFetchNodes(configPath, nodes); + } + else + { + nodes.emplace_back(pRoot); + } } else { @@ -475,12 +491,12 @@ void EnvironmentNode::doFetchNodes(ConfigPath &configPath, std::vector EnvironmentNode::getRoot() const +std::shared_ptr EnvironmentNode::getRoot() const { - if (!m_pParent.expired()) + std::shared_ptr pParent = m_pParent.lock(); + if (pParent) { - return m_pParent.lock()->getRoot(); + return pParent->getRoot(); } - std::shared_ptr ptr = shared_from_this(); - return ptr; + return std::const_pointer_cast(shared_from_this()); } diff --git a/configuration/configmgr/configmgrlib/EnvironmentNode.hpp b/configuration/configmgr/configmgrlib/EnvironmentNode.hpp index 2ab3f237c03..d67f01e01b4 100644 --- a/configuration/configmgr/configmgrlib/EnvironmentNode.hpp +++ b/configuration/configmgr/configmgrlib/EnvironmentNode.hpp @@ -34,7 +34,7 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this &pCfgItem, const std::string &elemName, const std::shared_ptr &pParent = nullptr) : + EnvironmentNode(const std::shared_ptr &pCfgItem, const std::string &elemName, const std::shared_ptr &pParent = std::shared_ptr()) : m_pSchemaItem(pCfgItem), m_name(elemName), m_pParent(pParent) { } ~EnvironmentNode() { } const std::string &getName() const { return m_name; } @@ -68,7 +68,7 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this &items) const; void initialize(); void fetchNodes(const std::string &path, std::vector> &nodes) const; - std::shared_ptr getRoot() const; + std::shared_ptr getRoot() const; void addEnvironmentInsertData(const std::string &envData) { m_insertData = envData; } const std::string &getEnvironmentInsertData() const { return m_insertData; } void clearEnvironmentInsertData() { m_insertData.clear(); } diff --git a/configuration/configmgr/configmgrlib/EnvironmentValue.cpp b/configuration/configmgr/configmgrlib/EnvironmentValue.cpp index 23f144464d1..8f77be0ec47 100644 --- a/configuration/configmgr/configmgrlib/EnvironmentValue.cpp +++ b/configuration/configmgr/configmgrlib/EnvironmentValue.cpp @@ -149,7 +149,7 @@ void EnvironmentValue::initialize() // until unique. prefix_ uses an _ between value and the number (value_3) // prefix# - always appends a number thus creating an incrementing unique value starting at 1 // (value1, value2, value3, ...) - if (type == "prefix" || type=="prefix_" || "prefix#") + if (type == "prefix" || type == "prefix_" || type == "prefix#") { std::string connector = (type == "prefix_") ? "_" : ""; std::string newName; diff --git a/configuration/configmgr/configmgrlib/XMLEnvironmentLoader.cpp b/configuration/configmgr/configmgrlib/XMLEnvironmentLoader.cpp index d9324d7994a..8fa225e85d2 100644 --- a/configuration/configmgr/configmgrlib/XMLEnvironmentLoader.cpp +++ b/configuration/configmgr/configmgrlib/XMLEnvironmentLoader.cpp @@ -99,7 +99,7 @@ void XMLEnvironmentLoader::parse(const pt::ptree &envTree, const std::shared_ptr } // - // Find elements in environment tree cooresponding to this config item, then parse each + // Find elements in environment tree corresponding to this config item, then parse each for (auto it = envTree.begin(); it != envTree.end(); ++it) { std::string elemName = it->first; diff --git a/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.cpp b/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.cpp index eb4cd9c4ef8..717720291cd 100644 --- a/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.cpp +++ b/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.cpp @@ -19,6 +19,7 @@ #include "XSDSchemaParser.hpp" #include "XMLEnvironmentLoader.hpp" #include "Exceptions.hpp" +#include "boost/version.hpp" bool XMLEnvironmentMgr::createParser() @@ -54,7 +55,15 @@ bool XMLEnvironmentMgr::save(std::ostream &out) pt::ptree envTree, topTree; serialize(envTree, m_pRootNode); topTree.add_child("Environment", envTree); - pt::write_xml(out, topTree); +// boost::property_tree::xml_writer_settings settings; + //pt::write_xml(out, topTree, pt::xml_parser::xml_writer_make_settings(' ', 4)); +#if BOOST_VERSION >= 105800 + pt::write_xml(out, topTree, pt::xml_parser::xml_writer_make_settings(' ', 4)); +#else + const char * myIndent = " "; + pt::write_xml(out, topTree, pt::xml_parser::xml_writer_make_settings(*myIndent , 4)); +#endif + } catch (const std::exception &e) { diff --git a/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.hpp b/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.hpp index ccfc37c5ad6..802cb0cb399 100644 --- a/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.hpp +++ b/configuration/configmgr/configmgrlib/XMLEnvironmentMgr.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace pt = boost::property_tree; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.cpp b/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.cpp index b335010f7c7..dcf8626248a 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.cpp @@ -18,23 +18,29 @@ #include "EnvModTemplate.hpp" #include "rapidjson/error/en.h" #include "TemplateException.hpp" +#include "TemplateExecutionException.hpp" #include "OperationCreateNode.hpp" -#include "OperationNoop.hpp" +#include "OperationFindNode.hpp" +#include "OperationModifyNode.hpp" +#include "OperationDeleteNode.hpp" #include #include #include "Utils.hpp" +// +// Good online resource for validation of modification templates is https://www.jsonschemavalidator.net/ +// Load the schecma (ModTemplateSchema.json) in the left window and the modification template in the right. -EnvModTemplate::EnvModTemplate(EnvironmentMgr *pEnvMgr, const std::string &fqSchemaFile) : m_pEnvMgr(pEnvMgr), m_pTemplate(nullptr), m_pSchema(nullptr) +EnvModTemplate::EnvModTemplate(EnvironmentMgr *pEnvMgr, const std::string &schemaFile) : m_pEnvMgr(pEnvMgr), m_pTemplate(nullptr), m_pSchema(nullptr) { if (m_pEnvMgr == nullptr) { - throw(TemplateException("Environment Modification Template requires a valid Environment Manager to be initialized")); + throw(TemplateException("Environment Modification Template requires a valid Environment Manager")); } // // Load and compile the schema document - std::ifstream jsonFile(fqSchemaFile); + std::ifstream jsonFile(schemaFile); rapidjson::IStreamWrapper jsonStream(jsonFile); rapidjson::Document sd; if (sd.ParseStream(jsonStream).HasParseError()) @@ -86,11 +92,10 @@ void EnvModTemplate::loadTemplateFromJson(const std::string &templateJson) void EnvModTemplate::loadTemplate(rapidjson::IStreamWrapper &stream) { - // // Cleanup anything that may be laying around. releaseTemplate(); - m_inputs.clear(); + m_variables.clear(); m_pTemplate = new rapidjson::Document(); @@ -117,9 +122,7 @@ void EnvModTemplate::loadTemplate(rapidjson::IStreamWrapper &stream) sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); msg += "Invalid document: " + std::string(sb.GetString()); - TemplateException te(msg); - te.setInvalidTemplate(true); - throw te; + throw TemplateException(msg, true); } } @@ -136,10 +139,10 @@ void EnvModTemplate::parseTemplate() // // Inputs - if (m_pTemplate->HasMember("inputs")) + if (m_pTemplate->HasMember("variables")) { - const rapidjson::Value &inputs = (*m_pTemplate)["inputs"]; - parseInputs(inputs); + const rapidjson::Value &variables = (*m_pTemplate)["variables"]; + parseVariables(variables); } // @@ -148,59 +151,118 @@ void EnvModTemplate::parseTemplate() } -void EnvModTemplate::parseInputs(const rapidjson::Value &inputs) +void EnvModTemplate::parseVariables(const rapidjson::Value &variables) { - for (auto &inputDef: inputs.GetArray()) + for (auto &varDef: variables.GetArray()) { - parseInput(inputDef); + parseVariable(varDef); } } -void EnvModTemplate::parseInput(const rapidjson::Value &input) +void EnvModTemplate::parseVariable(const rapidjson::Value &varValue) { rapidjson::Value::ConstMemberIterator it; - std::shared_ptr pInput; + std::shared_ptr pVariable; // // input name, make sure not a duplicate - std::string inputName(input.FindMember("name")->value.GetString()); - std::string type(input.FindMember("type")->value.GetString()); - pInput = inputValueFactory(type, inputName); + std::string varName(varValue.FindMember("name")->value.GetString()); + std::string type(varValue.FindMember("type")->value.GetString()); + pVariable = variableFactory(type, varName); // // Get and set the rest of the input values - it = input.FindMember("prompt"); - if (it != input.MemberEnd()) - pInput->setUserPrompt(it->value.GetString()); + it = varValue.FindMember("prompt"); + if (it != varValue.MemberEnd()) + pVariable->m_userPrompt = it->value.GetString(); else - pInput->setUserPrompt("Input value for " + inputName); + pVariable->m_userPrompt = "Input value for " + varName; - it = input.FindMember("description"); - if (it != input.MemberEnd()) pInput->setDescription(it->value.GetString()); + it = varValue.FindMember("description"); + if (it != varValue.MemberEnd()) pVariable->m_description = it->value.GetString(); - it = input.FindMember("tooltip"); - if (it != input.MemberEnd()) pInput->setTooltip(it->value.GetString()); + it = varValue.FindMember("values"); + if (it != varValue.MemberEnd()) + { + for (auto &val: it->value.GetArray()) + { + pVariable->addValue(trim(val.GetString())); + } + } - it = input.FindMember("value"); - if (it != input.MemberEnd()) pInput->setValue(trim(it->value.GetString())); + it = varValue.FindMember("prepared_value"); + if (it != varValue.MemberEnd()) pVariable->m_preparedValue = trim(it->value.GetString()); - it = input.FindMember("prepared_value"); - if (it != input.MemberEnd()) pInput->setPreparedValue(trim(it->value.GetString())); + it = varValue.FindMember("user_input"); + if (it != varValue.MemberEnd()) pVariable->m_userInput = it->value.GetBool(); - m_inputs.add(pInput); + m_variables.add(pVariable); } -std::shared_ptr EnvModTemplate::getInput(const std::string &name, bool throwIfNotFound) const +std::shared_ptr EnvModTemplate::getVariable(const std::string &name, bool throwIfNotFound) const { - return m_inputs.getInput(name, throwIfNotFound); + return m_variables.getVariable(name, throwIfNotFound); } -std::vector> EnvModTemplate::getInputs(int phase) const +std::vector> EnvModTemplate::getVariables(bool userInputOnly) const { - return m_inputs.all(phase); + std::vector> variables; + if (!userInputOnly) + { + variables.insert( variables.end(), m_variables.all().begin(), m_variables.all().end() ); + } + else + { + auto allVars = m_variables.all(); + for (auto &varIt: allVars) + { + if (varIt->m_userInput) + variables.emplace_back(varIt); + } + } + + return variables; +} + + +void EnvModTemplate::assignVariablesFromFile(const std::string &filepath) +{ + std::ifstream jsonFile(filepath); + rapidjson::IStreamWrapper jsonStream(jsonFile); + + // + // Format: + // { + // "variable-name" : [ "value1", "value2" , ..., "valuen"], + // ... + // } + + rapidjson::Document inputJson; + + // + // Parse and make sure it's valid JSON + inputJson.ParseStream(jsonStream); + if (inputJson.HasParseError()) + { + throw(TemplateException(&inputJson)); + } + + // + // go through all the inputs and assign values + for (auto itr = inputJson.MemberBegin(); + itr != inputJson.MemberEnd(); ++itr) + { + std::string inputName = itr->name.GetString(); + auto pInput = m_variables.getVariable(inputName); + auto valueArray = itr->value.GetArray(); + for (auto &val: valueArray) + { + pInput->addValue(val.GetString()); + } + } } @@ -220,49 +282,99 @@ void EnvModTemplate::parseOperation(const rapidjson::Value &operation) if (action == "create_node") { - pOp = parseCreateNodeOperation(operation); + pOp = std::make_shared(); } - else if (action == "noop" || action == "modify_node") + else if (action == "modify_node") { - pOp = parseOperationNoop(operation); + pOp = std::make_shared(); + } + else if (action == "find_node") + { + pOp = std::make_shared(); + } + else if (action == "delete_node") + { + pOp = std::make_shared(); } else { throw TemplateException("Unsupported operation '" + action + "' found", false); } + // + // Get the parent based on presence of key (schema validates presence of one of these) + auto it = operation.FindMember("parent_path"); + if (it != operation.MemberEnd()) + { + pOp->m_path = trim(it->value.GetString()); + } + else + { + it = operation.FindMember("parent_nodeid"); + pOp->m_parentNodeId = trim(it->value.GetString()); + } + + // + // Parse the data section + auto dataIt = operation.FindMember("data"); + if (dataIt != operation.MemberEnd()) + { + parseOperationCommonData(dataIt->value, pOp); + + // + // Parse specific operation type values + if (action == "create_node") + { + std::shared_ptr pCreateOp = std::dynamic_pointer_cast(pOp); + pCreateOp->m_nodeType = dataIt->value.FindMember("node_type")->value.GetString(); + } + else if (action == "find_node") + { + it = dataIt->value.FindMember("create"); + if (it != dataIt->value.MemberEnd()) + { + std::shared_ptr pFindOp = std::dynamic_pointer_cast(pOp); + pFindOp->m_createIfNotFound = it->value.GetBool(); + } + } + } + m_operations.emplace_back(pOp); } -void EnvModTemplate::parseOperationCommon(const rapidjson::Value &operation, std::shared_ptr pOp) +void EnvModTemplate::parseOperationCommonData(const rapidjson::Value &operationData, std::shared_ptr pOp) { rapidjson::Value::ConstMemberIterator it; + auto dataObj = operationData.GetObject(); + // - // Either a node ID or a path - it = operation.FindMember("parent_nodeid"); - if (it != operation.MemberEnd()) - pOp->setParentNodeId(it->value.GetString()); - else - pOp->setPath(operation.FindMember("path")->value.GetString()); + // Get the count (optional, default is 1) + it = dataObj.FindMember("count"); + if (it != dataObj.MemberEnd()) + pOp->m_count = trim(it->value.GetString()); // - // Get the count - it = operation.FindMember("count"); - if (it != operation.MemberEnd()) - pOp->setCount(trim(it->value.GetString())); + // Get the starting index (optional, for windowing into a range of values) + it = dataObj.FindMember("start_index"); + if (it != dataObj.MemberEnd()) + pOp->m_startIndex = trim(it->value.GetString()); // - // Get the starting index (for windowing into a range of values) - it = operation.FindMember("start_index"); - if (it != operation.MemberEnd()) - pOp->setStartIndex(trim(it->value.GetString())); + // Save node id + rapidjson::Value::ConstMemberIterator saveInfoIt = dataObj.FindMember("save_nodeid"); + if (saveInfoIt != dataObj.MemberEnd()) + { + pOp->m_saveNodeIdName = saveInfoIt->value.GetObject().FindMember("save_name")->value.GetString(); + it = saveInfoIt->value.GetObject().FindMember("duplicate_ok"); + if (it != saveInfoIt->value.MemberEnd()) pOp->m_duplicateSaveNodeIdInputOk = it->value.GetBool(); + } // // Get the attributes (if any) - it = operation.FindMember("attributes"); - if (it != operation.MemberEnd()) + it = dataObj.FindMember("attributes"); + if (it != dataObj.MemberEnd()) { rapidjson::Value::ConstMemberIterator attrValueIt; std::string attrName, attrValue; @@ -272,8 +384,12 @@ void EnvModTemplate::parseOperationCommon(const rapidjson::Value &operation, std // Get the attribute name and value. Note that these are required by the schema so the template would // have never loaded if either was missing. attrName = trim(attr.FindMember("name")->value.GetString()); - attrValue = trim(attr.FindMember("value")->value.GetString()); + auto valueIt = attr.FindMember("value"); + if (valueIt != attr.MemberEnd()) + { + attrValue = trim(attr.FindMember("value")->value.GetString()); + } modAttribute newAttribute(attrName, attrValue); attrValueIt = attr.FindMember("start_index"); @@ -282,62 +398,56 @@ void EnvModTemplate::parseOperationCommon(const rapidjson::Value &operation, std attrValueIt = attr.FindMember("do_not_set"); if (attrValueIt != attr.MemberEnd()) newAttribute.doNotSet = attrValueIt->value.GetBool(); - attrValueIt = attr.FindMember("save_value"); - if (attrValueIt != attr.MemberEnd()) newAttribute.saveValue = trim(attrValueIt->value.GetString()); - + saveInfoIt = attr.FindMember("save_value"); + if (saveInfoIt != attr.MemberEnd()) + { + newAttribute.saveValue = saveInfoIt->value.GetObject().FindMember("save_name")->value.GetString(); + attrValueIt = saveInfoIt->value.GetObject().FindMember("duplicate_ok"); + if (attrValueIt != saveInfoIt->value.MemberEnd()) + newAttribute.duplicateSaveValueOk = attrValueIt->value.GetBool(); + } pOp->addAttribute(newAttribute); } } -} - - -std::shared_ptr EnvModTemplate::parseCreateNodeOperation(const rapidjson::Value &operation) -{ - rapidjson::Value::ConstMemberIterator it; - std::shared_ptr pOp = std::make_shared(); - - parseOperationCommon(operation, pOp); - - // set the node type (schema ensures its presence) - pOp->setNodeType(operation.FindMember("node_type")->value.GetString()); // - // Get the potential name underwhich the newly created node ID will be saved - it = operation.FindMember("save_nodeid"); - if (it != operation.MemberEnd()) - pOp->setSaveNodeIdName(it->value.GetString()); - - return pOp; + // Throw on empty is a flag that will force an exception to be thrown during execution if no + // nodes are found to modify. + it = dataObj.FindMember("error_if_not_found"); + if (it != dataObj.MemberEnd()) + pOp->m_throwOnEmpty = it->value.GetBool(); } -std::shared_ptr EnvModTemplate::parseOperationNoop(const rapidjson::Value &operation) +void EnvModTemplate::execute() { - rapidjson::Value::ConstMemberIterator it; - std::shared_ptr pOp = std::make_shared(); - parseOperationCommon(operation, pOp); - - it = operation.FindMember("count"); - if (it != operation.MemberEnd()) - pOp->setCount(it->value.GetString()); - - return pOp; -} - - -bool EnvModTemplate::execute() -{ - bool rc = false; - - // - // Do final prep on inputs. This may set values for inputs that are dependent on previously set inputs - m_inputs.prepare(); + try + { + // + // Do final prep on inputs. This may set values for inputs that are dependent on previously set inputs + m_variables.prepare(); + } + catch (TemplateExecutionException &te) + { + te.setStep("Variable preparation"); + throw; + } + unsigned opNum = 1; for (auto &pOp: m_operations) { - pOp->execute(m_pEnvMgr, &m_inputs); + try + { + pOp->execute(m_pEnvMgr, &m_variables); + } + catch (TemplateExecutionException &te) + { + // + // Set the operation step number and rethrow so user can tell what step caused the problem + te.setStep("Operation step " + std::to_string(opNum)); + throw; + } + ++opNum; } - - return rc; } diff --git a/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.hpp b/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.hpp index 865fc6412a3..63d5f22fc61 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/EnvModTemplate.hpp @@ -24,8 +24,8 @@ #include "rapidjson/istreamwrapper.h" #include "TemplateException.hpp" #include -#include "Input.hpp" -#include "Inputs.hpp" +#include "Variable.hpp" +#include "Variables.hpp" #include "Operation.hpp" #include #include @@ -36,14 +36,15 @@ class EnvModTemplate public: - EnvModTemplate(EnvironmentMgr *pEnvMgr, const std::string &fqSchemaFile); + EnvModTemplate(EnvironmentMgr *pEnvMgr, const std::string &schemaFile); ~EnvModTemplate(); void loadTemplateFromJson(const std::string &templateJson); void loadTemplateFromFile(const std::string &fqTemplateFile); - std::shared_ptr getInput(const std::string &name, bool throwIfNotFound = true) const; - std::vector> getInputs(int phase = 0) const; - bool execute(); + std::shared_ptr getVariable(const std::string &name, bool throwIfNotFound = true) const; + std::vector> getVariables(bool userInputOnly = false) const; + void assignVariablesFromFile(const std::string &filepath); + void execute(); protected: @@ -51,13 +52,11 @@ class EnvModTemplate void releaseTemplate(); void loadTemplate(rapidjson::IStreamWrapper &stream); void parseTemplate(); - void parseInputs(const rapidjson::Value &inputs); - void parseInput(const rapidjson::Value &input); + void parseVariables(const rapidjson::Value &variables); + void parseVariable(const rapidjson::Value &varValue); void parseOperations(const rapidjson::Value &operations); void parseOperation(const rapidjson::Value &operation); - void parseOperationCommon(const rapidjson::Value &operation, std::shared_ptr pOp); - std::shared_ptr parseCreateNodeOperation(const rapidjson::Value &operation); - std::shared_ptr parseOperationNoop(const rapidjson::Value &operation); + void parseOperationCommonData(const rapidjson::Value &operationData, std::shared_ptr pOp); protected: @@ -65,7 +64,7 @@ class EnvModTemplate rapidjson::SchemaDocument *m_pSchema; rapidjson::Document *m_pTemplate; // same as GenericDocument > EnvironmentMgr *m_pEnvMgr; - Inputs m_inputs; + Variables m_variables; std::vector> m_operations; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.cpp b/configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.cpp similarity index 91% rename from configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.cpp rename to configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.cpp index 51459997586..26b6e739c11 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.cpp @@ -15,11 +15,11 @@ limitations under the License. ############################################################################## */ -#include "HostNameInput.hpp" +#include "HostNameVariable.hpp" #include "TemplateException.hpp" #include "Utils.hpp" -void HostNameInput::setValue(const std::string &value) { +void HostNameVariable::addValue(const std::string &value) { std::string hostname = trim(value); bool isValid = true; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.hpp b/configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.hpp similarity index 79% rename from configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.hpp index 3719b5a04e4..088dd77595d 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/HostNameInput.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/HostNameVariable.hpp @@ -18,16 +18,15 @@ #ifndef HPCCSYSTEMS_PLATFORM_HOSTNAMEINPUT_HPP #define HPCCSYSTEMS_PLATFORM_HOSTNAMEINPUT_HPP -#include "Input.hpp" -#include +#include "Variable.hpp" #include -class HostNameInput : public Input +class HostNameVariable : public Variable { public: - HostNameInput() = default; - ~HostNameInput() = default; - void setValue(const std::string &value); + explicit HostNameVariable(const std::string &name) : Variable(name) {} + ~HostNameVariable() override = default; + void addValue(const std::string &value) override; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.cpp b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.cpp similarity index 97% rename from configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.cpp rename to configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.cpp index c23245880a8..0a1a60190e5 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.cpp @@ -15,16 +15,16 @@ limitations under the License. ############################################################################## */ -#include "IPAddressRangeInput.hpp" +#include "IPAddressRangeVariable.hpp" #include "TemplateException.hpp" #include "Utils.hpp" -void IPAddressRangeInput::setValue(const std::string &range) +void IPAddressRangeVariable::addValue(const std::string &range) { // // Formats accepted: // iplist = ip[;ip] - // ip = a.b.c.d | a.b.c.g-h + // ip = a.b.c.d | a.b.c.g-h | a.b.c.d*10 m_values.clear(); std::vector ipDefs = splitString(range, ";"); diff --git a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.hpp b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.hpp similarity index 77% rename from configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.hpp index cc2b8a6cded..7685f69661d 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeInput.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressRangeVariable.hpp @@ -18,16 +18,15 @@ #ifndef HPCCSYSTEMS_PLATFORM_IPRANGE_HPP #define HPCCSYSTEMS_PLATFORM_IPRANGE_HPP -#include "Input.hpp" -#include +#include "Variable.hpp" #include -class IPAddressRangeInput : public Input +class IPAddressRangeVariable : public Variable { public: - IPAddressRangeInput() = default; - ~IPAddressRangeInput() = default; - void setValue(const std::string &value); + explicit IPAddressRangeVariable(const std::string &name) : Variable(name) {} + ~IPAddressRangeVariable() override = default; + void addValue(const std::string &value) override; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.cpp b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.cpp similarity index 94% rename from configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.cpp rename to configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.cpp index c3f00148a3e..abf92332bbe 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.cpp @@ -15,11 +15,11 @@ limitations under the License. ############################################################################## */ -#include "IPAddressInput.hpp" +#include "IPAddressVariable.hpp" #include "TemplateException.hpp" #include "Utils.hpp" -void IPAddressInput::setValue(const std::string &value) { +void IPAddressVariable::addValue(const std::string &value) { std::string ipAddr = trim(value); bool isValid = true; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.hpp b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.hpp similarity index 79% rename from configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.hpp index 9b8f76e8634..86061a8e68d 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/IPAddressInput.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/IPAddressVariable.hpp @@ -18,16 +18,15 @@ #ifndef HPCCSYSTEMS_PLATFORM_IPADDRESS_HPP #define HPCCSYSTEMS_PLATFORM_IPADDRESS_HPP -#include "Input.hpp" -#include +#include "Variable.hpp" #include -class IPAddressInput : public Input +class IPAddressVariable : public Variable { public: - IPAddressInput() = default; - ~IPAddressInput() = default; - void setValue(const std::string &value); + explicit IPAddressVariable(const std::string &name) : Variable(name) {} + ~IPAddressVariable() override = default; + void addValue(const std::string &value) override; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Operation.cpp b/configuration/configmgr/configmgrlib/mod_template_support/Operation.cpp index a4ca6b06fe8..8e988fb75c6 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Operation.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Operation.cpp @@ -16,7 +16,9 @@ ############################################################################## */ #include "Operation.hpp" -#include "TemplateException.hpp" +#include "TemplateExecutionException.hpp" +#include "EnvironmentNode.hpp" +#include "EnvironmentValue.hpp" void Operation::addAttribute(modAttribute &newAttribute) @@ -25,7 +27,7 @@ void Operation::addAttribute(modAttribute &newAttribute) } -bool Operation::execute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) +bool Operation::execute(EnvironmentMgr *pEnvMgr, Variables *pInputs) { bool rc = true; size_t count, startIndex; @@ -38,7 +40,7 @@ bool Operation::execute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) try { count = std::stoul(countStr); } catch (...) { - throw TemplateException("Non-numeric count found for count", false); + throw TemplateExecutionException("Non-numeric count found for count"); } // @@ -47,7 +49,7 @@ bool Operation::execute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) try { startIndex = std::stoul(startIdxStr); } catch (...) { - throw TemplateException("Non-numeric count found for start index", false); + throw TemplateExecutionException("Non-numeric count found for start index"); } // @@ -62,7 +64,7 @@ bool Operation::execute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) } -void Operation::assignAttributeCookedValues(Inputs *pInputs) +void Operation::assignAttributeCookedValues(Variables *pInputs) { // // go through the ones defined by the operation and set each (by name) @@ -71,3 +73,85 @@ void Operation::assignAttributeCookedValues(Inputs *pInputs) attr.cookedValue = pInputs->doValueSubstitution(attr.value); } } + + +void Operation::getParentNodeIds(EnvironmentMgr *pEnvMgr, Variables *pInputs) +{ + // + // Find the parent node(s). Either was input, or based on a path, which may match more than one node + if (!m_parentNodeId.empty()) + { + std::shared_ptr pInput = pInputs->getVariable(m_parentNodeId); + if (pInput) + { + std::size_t numIds = pInput->getNumValues(); + for (std::size_t idx = 0; idx < numIds; ++idx) + { + m_parentNodeIds.emplace_back(pInputs->doValueSubstitution(pInput->getValue(idx))); + } + } + else + { + m_parentNodeIds.emplace_back(pInputs->doValueSubstitution(m_parentNodeId)); + } + } + else + { + std::vector> envNodes; + std::string path = pInputs->doValueSubstitution(m_path); + pEnvMgr->fetchNodes(path, envNodes); + for (auto &envNode: envNodes) + m_parentNodeIds.emplace_back(envNode->getId()); + } +} + + +std::shared_ptr Operation::createInput(std::string inputName, const std::string &inputType, Variables *pInputs, bool existingOk) +{ + std::shared_ptr pInput; + + pInput = pInputs->getVariable(inputName, false); + + if (!pInput) + { + pInput = variableFactory(inputType, inputName); + pInputs->add(pInput); + } + else if (!existingOk) + { + std::string msg = "Attempt to create input value that already exists: " + inputName; + throw TemplateExecutionException(msg); + } + return pInput; +} + + +void Operation::createAttributeSaveInputs(Variables *pInputs) +{ + for (auto &attr: m_attributes) + { + // + // If this is a saved attribute value, make sure the input exists + if (!attr.saveValue.empty()) + { + createInput(attr.saveValue, "string", pInputs, attr.duplicateSaveValueOk); + } + } +} + + +void Operation::saveAttributeValues(Variables *pInputs, const std::shared_ptr &pEnvNode) +{ + for (auto &attr: m_attributes) + { + if (!attr.saveValue.empty()) + { + std::shared_ptr pInput = pInputs->getVariable(attr.saveValue); + auto pAttr = pEnvNode->getAttribute(attr.name); + if (pAttr) + { + pInput->addValue(pAttr->getValue()); + } + } + } +} diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Operation.hpp b/configuration/configmgr/configmgrlib/mod_template_support/Operation.hpp index 158a3923503..26c768a85b1 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Operation.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Operation.hpp @@ -18,16 +18,16 @@ #ifndef HPCCSYSTEMS_PLATFORM_OPERATION_HPP #define HPCCSYSTEMS_PLATFORM_OPERATION_HPP -#include "Input.hpp" -#include "Inputs.hpp" +#include "Variable.hpp" +#include "Variables.hpp" +#include "EnvironmentMgr.hpp" #include #include -#include struct modAttribute { modAttribute(const std::string &_name, const std::string &_value) : - name(_name), value(_value) { }; + name(_name), value(_value), duplicateSaveValueOk(false), doNotSet(false) { }; ~modAttribute() = default; std::string name; std::string value; @@ -35,6 +35,7 @@ struct modAttribute { std::string cookedValue; std::string saveValue; bool doNotSet; + bool duplicateSaveValueOk; }; class EnvironmentMgr; @@ -46,30 +47,34 @@ class Operation Operation() : m_count("1"), m_startIndex("0") {} virtual ~Operation() = default; - bool execute(EnvironmentMgr *pEnvMgr, Inputs *pInputs); - void setPath(const std::string &path) { m_path = path; } - std::string getPath() const { return m_path; } - void setParentNodeId(const std::string &id) { m_parentNodeId = id; } - std::string getParentNodeId() const { return m_parentNodeId; } - bool hasNodeId() const { return !m_parentNodeId.empty(); } + bool execute(EnvironmentMgr *pEnvMgr, Variables *pInputs); void addAttribute(modAttribute &newAttribute); - void assignAttributeCookedValues(Inputs *pInputs); - void setCount(const std::string &count) { m_count = count; } - void setStartIndex(const std::string &startIndex) { m_startIndex = startIndex; } + void assignAttributeCookedValues(Variables *pInputs); protected: - virtual void doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) = 0; + virtual void doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) = 0; + void getParentNodeIds(EnvironmentMgr *pEnvMgr, Variables *pInputs); + std::shared_ptr createInput(std::string inputName, const std::string &inputType, Variables *pInputs, bool existingOk); + void createAttributeSaveInputs(Variables *pInputs); + void saveAttributeValues(Variables *pInputs, const std::shared_ptr &pEnvNode); protected: std::string m_path; std::string m_parentNodeId; + std::vector m_parentNodeIds; std::vector m_attributes; std::string m_count; std::string m_startIndex; + bool m_throwOnEmpty = true; + std::string m_saveNodeIdName; + bool m_duplicateSaveNodeIdInputOk = false; + + + friend class EnvModTemplate; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.cpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.cpp index e121148f736..fbbcb1772e3 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.cpp @@ -20,68 +20,41 @@ #include "EnvironmentMgr.hpp" #include "EnvironmentNode.hpp" #include "EnvironmentValue.hpp" -#include "TemplateException.hpp" +#include "TemplateExecutionException.hpp" #include "Status.hpp" -#include - -void OperationCreateNode::doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) +void OperationCreateNode::doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) { std::shared_ptr pNewEnvNode; - std::vector parentNodeIds; // // Find the parent node(s). Either was input, or based on a path, which may match more than one node - if (!m_parentNodeId.empty()) - { - parentNodeIds.emplace_back(m_parentNodeId); - } - else - { - std::vector> envNodes; - pEnvMgr->fetchNodes(m_path, envNodes); - for (auto &envNode: envNodes) - parentNodeIds.emplace_back(envNode->getId()); - } + getParentNodeIds(pEnvMgr, pInputs); // // Create an input to hold the newly created node ID(s) if indicated. The IDs are saved as the node(s) is/are // created. - std::shared_ptr pSaveNodeIdInput; + std::shared_ptr pSaveNodeIdInput; if (!m_saveNodeIdName.empty()) { - pSaveNodeIdInput = inputValueFactory("string", m_saveNodeIdName); - pInputs->add(pSaveNodeIdInput); + pSaveNodeIdInput = createInput(m_saveNodeIdName, "string", pInputs, m_duplicateSaveNodeIdInputOk); } // - // If any attribute values are to be saved from the created node(s), create the inputs. - for (auto &attr: m_attributes) - { - // - // If this is a saved attribute value, make sure the input exists - if (!attr.saveValue.empty()) - { - std::shared_ptr pInput = pInputs->getInput(attr.saveValue, false); - if (pInput) - { - throw TemplateException("Attribute '" + attr.name + "' save value '" + attr.saveValue + "' already exists.", false); - } - pInput = inputValueFactory("string", attr.saveValue); - pInputs->add(pInput); - } - } - + // Create any attribute save inputs + createAttributeSaveInputs(pInputs); // // Execute for each parent node - if (!parentNodeIds.empty()) + if (!m_parentNodeIds.empty()) { - for (auto parentNodeId: parentNodeIds) + for (auto &parentNodeId: m_parentNodeIds) { Status status; + std::string nodeId = pInputs->doValueSubstitution(parentNodeId); + // // Get a new node for insertion (this does not insert the node, but rather returns an orphaned node that // can be inserted) @@ -102,15 +75,14 @@ void OperationCreateNode::doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) // // Add the new node to the environment - pNewEnvNode = pEnvMgr->addNewEnvironmentNode(parentNodeId, m_nodeType, attrValues, status); + pNewEnvNode = pEnvMgr->addNewEnvironmentNode(parentNodeId, m_nodeType, attrValues, status, true, true); if (pNewEnvNode) { // construct a status for just this new node's ID so we can see if there is an error Status newNodeStatus(status, pNewEnvNode->getId()); if (newNodeStatus.isError()) { - throw TemplateException("There was a problem adding the new node, status returned an error", - false); + throw TemplateExecutionException("There was a problem adding the new node, status returned an error"); } // @@ -121,29 +93,22 @@ void OperationCreateNode::doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) } // - // Save any required attribute values - for (auto &attr: m_attributes) - { - if (!attr.saveValue.empty()) - { - std::shared_ptr pInput = pInputs->getInput(attr.saveValue); - pInput->addValue(pNewEnvNode->getAttribute(attr.name)->getValue()); - } - } + // Save any attribute values to inputs for use later + saveAttributeValues(pInputs, pNewEnvNode); } else { - throw TemplateException("There was a problem adding the new node", false); + throw TemplateExecutionException("There was a problem adding the new node"); } } else { - throw TemplateException("Unable to get new node", false); + throw TemplateExecutionException("Unable to get new node"); } } } else { - throw TemplateException("Unable to find parent node", false); + throw TemplateExecutionException("Unable to find parent node"); } } diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.hpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.hpp index 83a0392b644..2d0aa91e556 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationCreateNode.hpp @@ -20,29 +20,25 @@ #include "Operation.hpp" -#include "../../RapidJSON/include/rapidjson/document.h" - class OperationCreateNode : public Operation { public: OperationCreateNode() = default; - ~OperationCreateNode() = default; - void setNodeType(const std::string &type) { m_nodeType = type; } - std::string getNodeType() const { return m_nodeType; } - void setSaveNodeIdName(const std::string &name) { m_saveNodeIdName = name; } - std::string getSaveNodeIdName() const { return m_saveNodeIdName; } + ~OperationCreateNode() override = default; protected: - void doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs); + void doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) override; + - private: + protected: std::string m_nodeType; - std::string m_saveNodeIdName; + + friend class EnvModTemplate; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.cpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.cpp new file mode 100644 index 00000000000..02026c59b65 --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.cpp @@ -0,0 +1,27 @@ + + +#include "OperationDeleteNode.hpp" +#include "EnvironmentMgr.hpp" +#include "TemplateExecutionException.hpp" + +void OperationDeleteNode::doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) +{ + // + // Get nodes + getParentNodeIds(pEnvMgr, pInputs); + + // + // If any node IDs found, go delete them + if (!m_parentNodeIds.empty()) + { + for (auto &parentNodeId: m_parentNodeIds) + { + std::string nodeId = pInputs->doValueSubstitution(parentNodeId); + pEnvMgr->removeEnvironmentNode(nodeId); + } + } + else if (m_throwOnEmpty) + { + throw TemplateExecutionException("Unable to find parent node"); + } +} diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.hpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.hpp similarity index 67% rename from configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.hpp index d3abe27f65c..6c6eaa06f75 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationDeleteNode.hpp @@ -15,24 +15,25 @@ limitations under the License. ############################################################################## */ -#ifndef HPCCSYSTEMS_PLATFORM_OPERATIONNOOP_HPP -#define HPCCSYSTEMS_PLATFORM_OPERATIONNOOP_HPP +#ifndef HPCCSYSTEMS_PLATFORM_OPERATIONDELETENODE_HPP +#define HPCCSYSTEMS_PLATFORM_OPERATIONDELETENODE_HPP #include "Operation.hpp" -class OperationNoop : public Operation +class OperationDeleteNode :public Operation { public: - OperationNoop() = default; - ~OperationNoop() = default; + OperationDeleteNode() = default; + ~OperationDeleteNode() override = default; protected: - void doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs); + void doExecute(EnvironmentMgr *pEnvMgr, Variables *pVariables) override; + friend class EnvModTemplate; }; -#endif //HPCCSYSTEMS_PLATFORM_OPERATIONNOOP_HPP +#endif //HPCCSYSTEMS_PLATFORM_OPERATIONDELETENODE_HPP diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.cpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.cpp new file mode 100644 index 00000000000..abd6816dda4 --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.cpp @@ -0,0 +1,93 @@ +/*############################################################################## + + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + + Licensed 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 "OperationFindNode.hpp" +#include "EnvironmentMgr.hpp" +#include "EnvironmentNode.hpp" +#include "TemplateExecutionException.hpp" +#include "Status.hpp" +#include "Operation.hpp" + + +void OperationFindNode::doExecute(EnvironmentMgr *pEnvMgr, Variables *pVariables) +{ + // + // Get nodes + getParentNodeIds(pEnvMgr, pVariables); + + // + // Save node ids if found + if (!m_parentNodeIds.empty()) + { + // + // The main reason to use a find nodes action is to either save the IDs of the matching nodes and/or attribute + // values from the matching nodes + std::shared_ptr pSaveNodeIdVar; + if (!m_saveNodeIdName.empty()) + { + pSaveNodeIdVar = createInput(m_saveNodeIdName, "string", pVariables, m_duplicateSaveNodeIdInputOk); + } + createAttributeSaveInputs(pVariables); + + if (pSaveNodeIdVar || !m_attributes.empty()) + { + for (auto &parentNodeId: m_parentNodeIds) + { + std::string nodeId = pVariables->doValueSubstitution(parentNodeId); + if (pSaveNodeIdVar) + { + pSaveNodeIdVar->addValue(nodeId); + } + + // + // Attributes would only be present if values were to be saved + if (!m_attributes.empty()) + { + auto pEnvNode = pEnvMgr->findEnvironmentNodeById(nodeId); + saveAttributeValues(pVariables, pEnvNode); + } + } + } + } + + // + // Otherwise, no nodes found, go create? Can only create if the target type was a path + else if (m_createIfNotFound && !m_path.empty()) + { + // + // To create the node, use the node type. If no node type was specified, use the last part of the path. + if (m_nodeType.empty()) + { + std::size_t lastSlashPos = m_path.find_last_of('/'); + if (lastSlashPos != std::string::npos) + { + m_nodeType = m_path.substr(lastSlashPos + 1); + m_path = m_path.substr(0, lastSlashPos); + } + else + { + throw TemplateExecutionException("Invalid path for find operation, unable to create node"); + } + } + OperationCreateNode::doExecute(pEnvMgr, pVariables); + } + else if (m_throwOnEmpty) + { + throw TemplateExecutionException("Unable to find parent node"); + } +} diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.hpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.hpp new file mode 100644 index 00000000000..d41cb7040fc --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationFindNode.hpp @@ -0,0 +1,45 @@ +/*############################################################################## + + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + + Licensed 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. +############################################################################## */ + +#ifndef HPCCSYSTEMS_PLATFORM_OPERATIONFINDNODE_HPP +#define HPCCSYSTEMS_PLATFORM_OPERATIONFINDNODE_HPP + +#include "OperationCreateNode.hpp" + +class OperationFindNode : public OperationCreateNode +{ + public: + + OperationFindNode() = default; + ~OperationFindNode() override = default; + + + protected: + + void doExecute(EnvironmentMgr *pEnvMgr, Variables *pVariables) override; + + + private: + + bool m_createIfNotFound = false; + + + friend class EnvModTemplate; +}; + + +#endif //HPCCSYSTEMS_PLATFORM_OPERATIONFINDNODE_HPP diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.cpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.cpp new file mode 100644 index 00000000000..f838206c3ed --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.cpp @@ -0,0 +1,65 @@ +/*############################################################################## + + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + + Licensed 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 "OperationModifyNode.hpp" + +#include "Operation.hpp" +#include "TemplateExecutionException.hpp" + + +void OperationModifyNode::doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) +{ + std::shared_ptr pEnvNode; + getParentNodeIds(pEnvMgr, pInputs); + + // + // Execute for each parent node + if (!m_parentNodeIds.empty()) + { + for (auto &parentNodeId: m_parentNodeIds) + { + Status status; + + std::string nodeId = pInputs->doValueSubstitution(parentNodeId); + pEnvNode = pEnvMgr->findEnvironmentNodeById(nodeId); + if (pEnvNode) + { + // + // Set the indicated attribute values + std::vector attrValues; + for (auto &attr: m_attributes) + { + if (!attr.doNotSet) + { + attrValues.emplace_back(NameValue(attr.name, attr.cookedValue)); + } + } + pEnvNode->setAttributeValues(attrValues, status, true, true); + } + else + { + // todo base execute method in Opeeration class should catch this exception and rethrow a standard exception + throw TemplateExecutionException("There was an error retrieving a node for modification"); + } + } + } + else if (m_throwOnEmpty) + { + throw TemplateExecutionException("Unable to find parent node"); + } + +} diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.hpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.hpp new file mode 100644 index 00000000000..2f675f62883 --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/OperationModifyNode.hpp @@ -0,0 +1,44 @@ +/*############################################################################## + + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + + Licensed 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. +############################################################################## */ + +#ifndef HPCCSYSTEMS_PLATFORM_OPERATIONMODIFYNODE_HPP +#define HPCCSYSTEMS_PLATFORM_OPERATIONMODIFYNODE_HPP + +#include "Operation.hpp" + +class OperationModifyNode : public Operation +{ + public: + + OperationModifyNode() = default; + ~OperationModifyNode() override = default; + void addAttributeForDeletion(std::string name) { m_deleteAttributes.emplace_back(name); } + + + protected: + + void doExecute(EnvironmentMgr *pEnvMgr, Variables *pInputs) override; + + + protected: + + std::vector m_deleteAttributes; + +}; + + +#endif //HPCCSYSTEMS_PLATFORM_OPERATIONMODIFYNODE_HPP diff --git a/configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.cpp b/configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.cpp deleted file mode 100644 index fbe43d30104..00000000000 --- a/configuration/configmgr/configmgrlib/mod_template_support/OperationNoop.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/*############################################################################## - - HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. - - Licensed 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 "OperationNoop.hpp" -#include "EnvironmentMgr.hpp" -#include "EnvironmentNode.hpp" - -void OperationNoop::doExecute(EnvironmentMgr *pEnvMgr, Inputs *pInputs) -{ - return; -} diff --git a/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.cpp b/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.cpp index 16c422284d9..12fadc5f827 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.cpp @@ -21,5 +21,4 @@ TemplateException::TemplateException(const rapidjson::Document *pDocument) { m_reason = "There was an error parsing the JSON, offset: " + std::to_string(pDocument->GetErrorOffset()) + ", error = " + rapidjson::GetParseError_En(pDocument->GetParseError()); - setBadJson(true); } diff --git a/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.hpp b/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.hpp index 0c678742b6f..0d86bce2c9c 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/TemplateException.hpp @@ -27,15 +27,11 @@ class TemplateException : public std::exception { public: - TemplateException(const rapidjson::Document *pDocument); - TemplateException(const std::string &reason, bool badTemplate = true) : m_reason(reason), m_invalidTemplate(badTemplate) { }; - TemplateException() { }; - - void setBadJson(bool isBad) { m_badJson = isBad; } - bool isBadJson() const { return m_badJson; } - void setInvalidTemplate(bool isBad) { m_invalidTemplate = isBad; } - bool isInvalidTemplate() const { return m_invalidTemplate; } + explicit TemplateException(const rapidjson::Document *pDocument); + explicit TemplateException(const std::string &reason, bool badTemplate = false) : m_reason(reason), m_invalidTemplate(badTemplate) { }; + TemplateException() = default; + bool isTemplateInvalid() const { return m_invalidTemplate; } const char *what() const throw() override { return m_reason.c_str(); @@ -45,7 +41,6 @@ class TemplateException : public std::exception private: std::string m_reason; - bool m_badJson = false; bool m_invalidTemplate = false; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/TemplateExecutionException.hpp b/configuration/configmgr/configmgrlib/mod_template_support/TemplateExecutionException.hpp new file mode 100644 index 00000000000..0718d3c9872 --- /dev/null +++ b/configuration/configmgr/configmgrlib/mod_template_support/TemplateExecutionException.hpp @@ -0,0 +1,51 @@ +/*############################################################################## + + HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®. + + Licensed 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. +############################################################################## */ + +#ifndef HPCCSYSTEMS_PLATFORM_TEMPLATEEXECUTIONEXCEPTION_HPP +#define HPCCSYSTEMS_PLATFORM_TEMPLATEEXECUTIONEXCEPTION_HPP + +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" + +class TemplateExecutionException : public std::exception +{ + public: + + TemplateExecutionException(const std::string &reason) : + m_reason(reason) { } + TemplateExecutionException() = default; + + void setStep(const std::string &step) + { + m_reason = "There was a problem executing " + step + ", the cause is " + m_reason; + } + + const char *what() const throw() override + { + return m_reason.c_str(); + } + + + private: + + std::string m_reason; +}; + + +#endif //HPCCSYSTEMS_PLATFORM_TEMPLATEEXECUTIONEXCEPTION_HPP diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Input.cpp b/configuration/configmgr/configmgrlib/mod_template_support/Variable.cpp similarity index 74% rename from configuration/configmgr/configmgrlib/mod_template_support/Input.cpp rename to configuration/configmgr/configmgrlib/mod_template_support/Variable.cpp index 8c4131a4373..7e55edda35f 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Input.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Variable.cpp @@ -15,45 +15,44 @@ limitations under the License. ############################################################################## */ -#include "Input.hpp" +#include "Variable.hpp" #include "TemplateException.hpp" -#include "IPAddressRangeInput.hpp" -#include "IPAddressInput.hpp" -#include "HostNameInput.hpp" +#include "IPAddressRangeVariable.hpp" +#include "IPAddressVariable.hpp" +#include "HostNameVariable.hpp" #include // todo add more types that correspond to XSD types so that modification template inputs can be validated earlier -std::shared_ptr inputValueFactory(const std::string &type, const std::string &name) +std::shared_ptr variableFactory(const std::string &type, const std::string &name) { - std::shared_ptr pInput; + std::shared_ptr pInput; if (type == "string") { - pInput = std::make_shared(); + pInput = std::make_shared(name); } else if (type == "iprange") { - pInput = std::make_shared(); + pInput = std::make_shared(name); } else if (type == "ipaddress") { - pInput = std::make_shared(); + pInput = std::make_shared(name); } else if (type == "hostname") { - pInput = std::make_shared(); + pInput = std::make_shared(name); } else { throw TemplateException("Invalid input type '" + type + "'"); } - pInput->setName(name); return pInput; } -std::string Input::getValue(size_t idx) const +std::string Variable::getValue(size_t idx) const { // // If no value assigned yet, throw an exception @@ -82,14 +81,14 @@ std::string Input::getValue(size_t idx) const } -void Input::setValue(const std::string &value) +void Variable::addValue(const std::string &value) { - if (!m_values.empty()) - { - m_values[0] = value; - } - else - { +// if (!m_values.empty()) +// { +// m_values[0] = value; +// } +// else +// { m_values.emplace_back(value); - } +// } } diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Input.hpp b/configuration/configmgr/configmgrlib/mod_template_support/Variable.hpp similarity index 62% rename from configuration/configmgr/configmgrlib/mod_template_support/Input.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/Variable.hpp index 16dc1ff032e..e811f1c23f0 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Input.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Variable.hpp @@ -24,32 +24,26 @@ #include #include -class Input; +class Variable; -std::shared_ptr inputValueFactory(const std::string &type, const std::string &name); +std::shared_ptr variableFactory(const std::string &type, const std::string &name); -class Input +class Variable { public: - Input() = default; - virtual ~Input() = default; + explicit Variable(const std::string &name) : m_name(name) {} + virtual ~Variable() = default; const std::string &getName() const { return m_name; } - void setName(const std::string &name) { m_name = name; } const std::string &getUserPrompt() const { return m_userPrompt; } - void setUserPrompt(const std::string &userPrompt) { m_userPrompt = userPrompt; } const std::string &getDescription() const { return m_description; } - void setDescription(const std::string &description) { m_description = description; } - const std::string &getTooltip() const { return m_tooltip; } - void setTooltip(const std::string &tooltip) { m_tooltip = tooltip; } size_t getNumValues() const { return m_values.size(); } - virtual void setValue(const std::string &value); - void addValue(const std::string &value) { m_values.emplace_back(value); } + virtual void addValue(const std::string &value); +// void addValue(const std::string &value) { m_values.emplace_back(value); } virtual std::string getValue(size_t idx) const; - void setPreparedValue(const std::string &value) { m_preparedValue = value; } + bool isUserInput() const { return m_userInput; } const std::string &getPreparedValue() const { return m_preparedValue; } - void setNonUserInput(bool val) { m_nonUserInput = val; } - bool needsUserInput() const { return !m_nonUserInput; } + protected: @@ -57,10 +51,12 @@ class Input std::string m_name; std::string m_userPrompt; std::string m_description; - std::string m_tooltip; std::string m_preparedValue; - bool m_nonUserInput = false; + bool m_userInput = true; std::vector m_values; + + + friend class EnvModTemplate; }; diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Inputs.cpp b/configuration/configmgr/configmgrlib/mod_template_support/Variables.cpp similarity index 73% rename from configuration/configmgr/configmgrlib/mod_template_support/Inputs.cpp rename to configuration/configmgr/configmgrlib/mod_template_support/Variables.cpp index 5e0e8f3142a..420cfea1292 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Inputs.cpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Variables.cpp @@ -15,59 +15,71 @@ limitations under the License. ############################################################################## */ -#include "Input.hpp" -#include "Inputs.hpp" +#include "Variable.hpp" +#include "Variables.hpp" #include "TemplateException.hpp" -void Inputs::add(const std::shared_ptr pNewInput) +void Variables::add(const std::shared_ptr pVariable) { - for (auto &pInput: m_inputValues) + for (auto &pVar: m_variables) { - if (pInput->getName() == pNewInput->getName()) + if (pVar->getName() == pVariable->getName()) { - throw TemplateException("Input '" + pNewInput->getName() + "' is a duplicate."); + throw TemplateException("Variable '" + pVariable->getName() + "' is a duplicate.", true); } } - m_inputValues.emplace_back(pNewInput); + m_variables.emplace_back(pVariable); } -std::shared_ptr Inputs::getInput(const std::string &name, bool throwIfNotFound) const +std::shared_ptr Variables::getVariable(const std::string &name, bool throwIfNotFound) const { - std::shared_ptr pRetInput; - for (auto &pInput: m_inputValues) + std::shared_ptr pRetVar; + std::string varName = name; + + // + // Accept both a regular string or a {{name}} string for the input name + std::size_t bracesStartPos = name.find("{{"); + if (bracesStartPos != std::string::npos) + { + std::size_t bracesEndPos = findClosingDelimiter(name, bracesStartPos,"{{", "}}"); + varName = name.substr(bracesStartPos + 2, bracesEndPos - bracesStartPos - 2); + } + + + for (auto &pVar: m_variables) { - if (pInput->getName() == name) + if (pVar->getName() == varName) { - pRetInput = pInput; + pRetVar = pVar; break; } } - if (!pRetInput && throwIfNotFound) + if (!pRetVar && throwIfNotFound) { - throw TemplateException("Unable to find input, name = '" + name + "'."); + throw TemplateException("Unable to find variable, name = '" + name + "'."); } - return pRetInput; + return pRetVar; } -void Inputs::prepare() +void Variables::prepare() { - for (auto &pInput: m_inputValues) + for (auto &pVar: m_variables) { - std::string preparedValue = pInput->getPreparedValue(); + std::string preparedValue = pVar->getPreparedValue(); if (!preparedValue.empty()) { - pInput->setValue(doValueSubstitution(preparedValue)); + pVar->addValue(doValueSubstitution(preparedValue)); } } } -std::string Inputs::doValueSubstitution(const std::string &value) const +std::string Variables::doValueSubstitution(const std::string &value) const { // // A value has the form {{name}}[{{index}}] where name and index can be simple strings and the index is optional @@ -92,7 +104,7 @@ std::string Inputs::doValueSubstitution(const std::string &value) const if (bracketStartPos != std::string::npos && sizePos != std::string::npos) { - throw TemplateException("Both [] and .size may not appear in a variable input"); + throw TemplateException("Both [] and .size may not appear in a variable variable"); } if (bracketStartPos != std::string::npos) @@ -109,11 +121,11 @@ std::string Inputs::doValueSubstitution(const std::string &value) const if (sizePos != std::string::npos) { - result = std::to_string(getInput(varName, true)->getNumValues()); + result = std::to_string(getVariable(varName, true)->getNumValues()); } else { - std::string substitueValue = doValueSubstitution(getInput(varName, true)->getValue(index)); + std::string substitueValue = doValueSubstitution(getVariable(varName, true)->getValue(index)); std::string newResult = result.substr(0, bracesStartPos); newResult += substitueValue; newResult += result.substr(bracesEndPos + 2); @@ -132,7 +144,7 @@ std::string Inputs::doValueSubstitution(const std::string &value) const -std::size_t Inputs::findClosingDelimiter(const std::string &input, std::size_t startPos, const std::string &openDelim, const std::string &closeDelim) const +std::size_t Variables::findClosingDelimiter(const std::string &input, std::size_t startPos, const std::string &openDelim, const std::string &closeDelim) const { std::size_t curPos = startPos + openDelim.length(); std::size_t openPos, closePos; @@ -174,7 +186,7 @@ std::size_t Inputs::findClosingDelimiter(const std::string &input, std::size_t s } -std::string Inputs::evaluate(const std::string &expr) const +std::string Variables::evaluate(const std::string &expr) const { std::size_t opPos; std::string result = expr; @@ -211,7 +223,7 @@ std::string Inputs::evaluate(const std::string &expr) const } -void Inputs::clear() +void Variables::clear() { - m_inputValues.clear(); + m_variables.clear(); } diff --git a/configuration/configmgr/configmgrlib/mod_template_support/Inputs.hpp b/configuration/configmgr/configmgrlib/mod_template_support/Variables.hpp similarity index 77% rename from configuration/configmgr/configmgrlib/mod_template_support/Inputs.hpp rename to configuration/configmgr/configmgrlib/mod_template_support/Variables.hpp index 7691aa26bd8..731078e3cca 100644 --- a/configuration/configmgr/configmgrlib/mod_template_support/Inputs.hpp +++ b/configuration/configmgr/configmgrlib/mod_template_support/Variables.hpp @@ -23,17 +23,17 @@ #include #include -class Input; +class Variable; -class Inputs +class Variables { public: - Inputs() = default; - ~Inputs() = default; - void add(const std::shared_ptr pInput); - const std::vector> &all(unsigned phase=0) const { return m_inputValues; } - std::shared_ptr getInput(const std::string &name, bool throwIfNotFound = true) const; + Variables() = default; + ~Variables() = default; + void add(const std::shared_ptr pVariable); + const std::vector> &all() const { return m_variables; } + std::shared_ptr getVariable(const std::string &name, bool throwIfNotFound = true) const; void setInputIndex(size_t idx) { m_curIndex = idx; } std::string doValueSubstitution(const std::string &value) const; void prepare(); @@ -48,7 +48,7 @@ class Inputs private: - std::vector> m_inputValues; + std::vector> m_variables; size_t m_curIndex = 0; }; diff --git a/initfiles/componentfiles/configschema/templates/AddKeys.json b/initfiles/componentfiles/configschema/templates/AddKeys.json new file mode 100644 index 00000000000..f297e5e9016 --- /dev/null +++ b/initfiles/componentfiles/configschema/templates/AddKeys.json @@ -0,0 +1,72 @@ +{ + "name" : "Add Key", + "type" : "modification", + "inputs" : [ + { + "name" : "keypair_name", + "type" : "string", + "value" : "mythor" + }, + { + "name" : "public_key", + "type" : "string", + "value" : "/home/jsmith/.ssh/id_rsa.pub.pem" + }, + { + "name" : "private_key", + "type" : "string", + "value" : "/home/jsmith/.ssh/id_rsa" + }, + { + "name" : "key_pair_name", + "type" : "string", + "value" : "mythor" + } + ], + "operations" : [ + { + "action" : "find_node", + "target_type" : "path", + "target" : "/Environment/EnvSettings/Keys", + "node_type" : "keys", + "save_nodeid" : "keys_node", + "create" : true + }, + { + "action" : "create_node", + "target_type" : "nodeid", + "target" : "{{keys_node}}", + "node_type" : "KeyPair", + "attributes" : [ + { + "name" : "name", + "value" : "{{keypair_name}}" + }, + { + "name" : "publicKey", + "value" : "{{public_key}}" + }, + { + "name" : "privakeKey", + "value" : "{{private_key}}" + } + ] + }, + { + "action" : "create_node", + "target_type" : "nodeid", + "target" : "{{keys_node}}", + "node_type" : "Cluster", + "attributes" : [ + { + "name" : "name", + "value" : "{{keypair_name}}" + }, + { + "name" : "keyPairName", + "value" : "{{keypair_name}}" + } + ] + } + ] +} diff --git a/initfiles/componentfiles/configschema/templates/schema/ModTemplateSchema.json b/initfiles/componentfiles/configschema/templates/schema/ModTemplateSchema.json index 1831d783c60..3d2dd241875 100644 --- a/initfiles/componentfiles/configschema/templates/schema/ModTemplateSchema.json +++ b/initfiles/componentfiles/configschema/templates/schema/ModTemplateSchema.json @@ -1,166 +1,349 @@ { - "$schema" : "http://json-schema.org/draft-04/schema#", - "description" : "Schema for validating environment modification template", - "type" : "object", - "properties" : { - "name" : { - "type" : "string" + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for validating environment modification template", + "type": "object", + "properties": { + "name": { + "type": "string", + "description" : "Name for the template" }, - "description" : { - "type" : "string" + "description": { + "type": "string", + "description" : "A description of the template" }, - "type" : { - "type" : "string", - "enum" : [ + "type": { + "type": "string", + "enum": [ "new", "modification" - ] - }, - "subtype" : { - "type" : "string" + ], + "description" : "Type of template. Mainly used for filtering templates." }, - "inputs" : { - "type" : "array", - "minItems" : 0, - "items" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" + "variables": { + "type": "array", + "minItems": 0, + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description" : "Name of the variable to which the input is assigned. This value is available using {{name}} anywhere in the template" }, - "type" : { - "type" : "string", - "enum" : [ + "type": { + "type": "string", + "enum": [ "string", "iprange", "ipaddress", "hostname" - ] - }, - "prompt" : { - "type" : "string" + ], + "description" : "The variable type." }, - "description" : { - "type" : "string" + "prompt": { + "type": "string", + "description" : "String shown to user during interactive input" }, - "tooltip" : { - "type" : "string" + "description": { + "type": "string", + "description" : "Description of what this value represents in the template." }, - "value" : { - "type" : "string" + "values": { + "type": "array", + "items" : { + "type" : "string" + }, + "description" : "Pre-assigned values" }, - "non_user_input" : { - "type" : "boolean" + "user_input": { + "type": "boolean", + "description" : "Set to true if user must input this value(s) (defaults to true)", + "default" : true }, - "prepared_value" : { - "type" : "string" + "prepared_value": { + "type": "string", + "description" : "Optional. Used to prepare the final value for use during template execution. Usually combined with other variable values of the form {{varname}}" } }, - "required" : [ + "required": [ "name", "type" ] - } - }, - "rules" : { - "type" : "object" + }, + "description" : "Array of variables for template execution. Some may require user input." }, - "operations" : { - "type" : "array", - "minItems" : 0, - "items" : { "$ref" : "#/definitions/operation"} + "operations": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/definitions/operation" + } } }, - "required" : [ + "required": [ "name", "type", "operations" ], - - "definitions" : { - - "operation" : { - "type" : "object", - "properties" : { - "action" : { - "type" : "string" - }, - "save_nodeid" : { - "type" : "string" + "definitions": { + "operation": { + "type": "object", + "properties": { + "description": { + "type": "string" }, - "path" : { - "type" : "string" + "action": { + "type" : "string", + "enum": [ + "create_node", + "modify_node", + "find_node", + "delete_node" + ], + "description" : "Action to perform for this operation" }, - "parent_nodeid" : { - "type" : "string" + "parent_path": { + "type": "string" }, - "node_type" : { - "type" : "string" - }, - "count" : { - "type" : "string" - }, - "start_index" : { - "type" : "string" - }, - "attributes" : { - "$ref" : "#/definitions/attribute_values" + "parent_nodeid": { + "type": "string" } }, - "allOf" : [ + "allOf": [ { - "oneOf" : [ - + "oneOf": [ + { + "properties": { + "action": { + "enum": [ + "create_node" + ] + }, + "data": { + "$ref": "#/definitions/create_data_info" + } + }, + "required": [ + "action", + "data" + ] + }, + { + "properties": { + "action": { + "enum": [ + "modify_node" + ] + }, + "data": { + "$ref": "#/definitions/modify_data_info" + } + }, + "required": [ + "action", + "data" + ] + }, { - "properties" : { - "action" : { "enum" : [ "create_node"]} + "properties": { + "action": { + "enum": [ + "find_node" + ] + }, + "data": { + "$ref": "#/definitions/find_data_info" + } }, - "required" : ["node_type"] + "required": [ + "action" + ] }, { - "properties" : { - "action" : { "enum" : ["modify_node", "noop"]} - } + "properties": { + "action": { + "enum": [ + "delete_node" + ] + }, + "data": { + "$ref": "#/definitions/delete_data_info" + } + }, + "required": [ + "action" + ] } ] }, { - "oneOf" : [ - { "required" : [ "path" ]}, - { "required" : [ "parent_nodeid" ]} + "oneOf": [ + { + "required": [ + "parent_path" + ] + }, + { + "required": [ + "parent_nodeid" + ] + } ] + } + ], + "description" : "Definition of the operation to perform" + }, + "create_data_info": { + "type": "object", + "properties": { + "count": { + "type": "string" + }, + "start_index": { + "type": "string" + }, + "node_type": { + "type": "string" }, + "save_nodeid": { + "$ref": "#/definitions/save_info" + }, + "attributes": { + "$ref": "#/definitions/attribute_values" + } + }, + "allOf": [ { - "required" : ["action"] + "required": [ + "node_type" + ] } ] - }, - - "attribute_value" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" + "modify_data_info": { + "type": "object", + "properties": { + "count": { + "type": "string" }, - "value" : { - "type" : "string" + "start_index": { + "type": "string" }, - "save_value" : { - "type" : "string" + "save_nodeid": { + "$ref": "#/definitions/save_info" }, - "do_not_set" : { - "type" : "boolean" + "attributes": { + "$ref": "#/definitions/attribute_values" } }, - "required" : ["name", "value"] + "allOf": [ + { + "required": [ + "attributes" + ] + } + ] }, - - "attribute_values" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/attribute_value" + "find_data_info": { + "type": "object", + "properties": { + "node_type": { + "type": "string" + }, + "save_nodeid": { + "$ref": "#/definitions/save_info" + }, + "error_if_not_found": { + "type": "boolean", + "default" : true + }, + "create": { + "type": "boolean", + "default" : false + }, + "attributes": { + "$ref": "#/definitions/attribute_refs" + } + } + }, + "delete_data_info": { + "type": "object", + "properties": { + "count": { + "type": "string" + }, + "start_index": { + "type": "string" + }, + "error_if_not_found": { + "type": "boolean", + "default" : true + } + } + }, + "attribute_value": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "save_value": { + "$ref": "#/definitions/save_info" + }, + "do_not_set": { + "type": "boolean" + } + }, + "required": [ + "name", + "value" + ] + }, + "attribute_values": { + "type": "array", + "items": { + "$ref": "#/definitions/attribute_value" } + }, + "attribute_ref" : { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "save_value": { + "$ref": "#/definitions/save_info" + } + }, + "allOf": [ + { + "required": [ + "name" + ] + } + ] + }, + "attribute_refs": { + "type": "array", + "items": { + "$ref": "#/definitions/attribute_ref" + } + }, + "save_info": { + "type": "object", + "properties": { + "save_name": { + "type": "string" + }, + "duplicate_ok": { + "type": "boolean" + } + }, + "required": [ + "save_name" + ] } } } diff --git a/testing/unittests/configmgr/ConfigMgrTemplateTests.cpp b/testing/unittests/configmgr/ConfigMgrTemplateTests.cpp index 99902f8e121..0b8e26899bb 100644 --- a/testing/unittests/configmgr/ConfigMgrTemplateTests.cpp +++ b/testing/unittests/configmgr/ConfigMgrTemplateTests.cpp @@ -28,6 +28,7 @@ // Config2 manager includes #include "jexcept.hpp" #include "mod_template_support/TemplateException.hpp" +#include "mod_template_support/TemplateExecutionException.hpp" #include "EnvironmentMgr.hpp" #include "build-config.h" #include @@ -35,7 +36,7 @@ #include #include #include "mod_template_support/EnvModTemplate.hpp" -#include "mod_template_support/IPAddressRangeInput.hpp" +#include "mod_template_support/IPAddressRangeVariable.hpp" // // This class validates that the system XSDs are wellformed and parse with no errors @@ -48,6 +49,8 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture CPPUNIT_TEST(Test_Inputs); CPPUNIT_TEST(Test_SubstitutionTests); CPPUNIT_TEST(Test_OperationsTests); + CPPUNIT_TEST(Test_FindNodeTests); + CPPUNIT_TEST(Test_CreateNodeTests); CPPUNIT_TEST_SUITE_END(); @@ -67,13 +70,22 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture bool loadEnvironment(const std::string &envFilename) { + bool rc = true; + CPPUNIT_ASSERT_MESSAGE("No envionrment manager loaded", m_pEnvMgr != nullptr); std::string xmlEnvDir = INSTALL_DIR PATHSEPSTR "testing/configmgr/schema/environments/"; m_pEnvMgr->discardEnvironment(); // in case one is currently loaded - return(m_pEnvMgr->loadEnvironment(xmlEnvDir + envFilename)); + + if (!m_pEnvMgr->loadEnvironment(xmlEnvDir + envFilename)) + { + std::string msg = "There was a problem loading environment " + envFilename + "Error: " + m_pEnvMgr->getLastEnvironmentMessage(); + CPPUNIT_ASSERT_MESSAGE(msg, false); + rc = false; + } + return rc; } - void CreateEnvironmentManager(const std::string &schemaFilename, const std::string &environmentFile) + void CreateEnvironmentManager(const std::string &schemaFilename) { // // Standard configuration for HPCC @@ -93,20 +105,13 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture std::map cfgParms; rc = m_pEnvMgr->loadSchema(schemaXSDDir, schemaFilename, cfgParms); CPPUNIT_ASSERT_MESSAGE("Unable to load configuration schema, error = " + m_pEnvMgr->getLastSchemaMessage(), rc); - - // - // Load the environment if specified - if (!environmentFile.empty()) - { - bool rc = loadEnvironment(environmentFile); - CPPUNIT_ASSERT_MESSAGE("Unable to load environment, error = " + m_pEnvMgr->getLastEnvironmentMessage(), rc); - } printf("complete."); } void LoadTemplate(const std::string &templateFilename) { + CPPUNIT_ASSERT_MESSAGE("No envionrment manager loaded", m_pEnvMgr != nullptr); printf("\n Instantiating template %s ...", templateFilename.c_str()); std::string templateSchema = INSTALL_DIR PATHSEPSTR "componentfiles/configschema/templates/schema/ModTemplateSchema.json"; if (m_pTemplate != nullptr) @@ -145,7 +150,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture } catch (TemplateException &e) { - if (e.isBadJson()) // || e.isInvalidTemplate()) + if (!e.isTemplateInvalid()) { rc = false; assertMsg += e.what(); @@ -164,17 +169,36 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture } + void executeTemplate(bool expectFailure) + { + printf("\n Executing template...");try + { + m_pTemplate->execute(); + } + catch (TemplateExecutionException &e) + { + if (!expectFailure) + { + std::string assertMsg; + assertMsg = "Failed when expected to pass, msg = "; + assertMsg += e.what(); + CPPUNIT_ASSERT_MESSAGE(assertMsg, false); + } + } + printf("complete."); + } + void Test_LoadingTemplates() { bool rc = false; printf("\n*** ConfigMgr 2.0 - Templates - Loading Only ***"); - CreateEnvironmentManager("Simple.xsd", ""); + CreateEnvironmentManager("Simple.xsd"); // // Simple template create printf("\n Instantiating a template..."); std::string templateSchema = INSTALL_DIR PATHSEPSTR "componentfiles/configschema/templates/schema/ModTemplateSchema.json"; - EnvModTemplate *pTemplate = new EnvModTemplate(m_pEnvMgr, templateSchema); + auto *pTemplate = new EnvModTemplate(m_pEnvMgr, templateSchema); printf("complete."); // @@ -184,7 +208,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture pTemplate->loadTemplateFromJson("{ \"key\" : \"value\" }"); rc = false; // the load should throw } catch (TemplateException &te) { - rc = te.isInvalidTemplate(); + rc = te.isTemplateInvalid(); } CPPUNIT_ASSERT_MESSAGE("Unable to load JSON from a string.", rc); printf("passed."); @@ -193,10 +217,10 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture // Load invalid JSON from a string and make sure it detects invalid JSON printf("\n Testing invalid JSON parse from a string..."); try { - pTemplate->loadTemplateFromJson("{ \"key : \"value\" }"); + pTemplate->loadTemplateFromJson("{ key\" : \"value\" }"); rc = false; } catch (TemplateException &te) { - rc = te.isBadJson(); + rc = true; } CPPUNIT_ASSERT_MESSAGE("Unable to detect invalid JSON from a string.", rc); printf("passed."); @@ -208,7 +232,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture pTemplate->loadTemplateFromFile(m_templateDir + "ParseFromFileTest.json"); rc = true; } catch (TemplateException &te) { - rc = te.isInvalidTemplate(); // this is the expected error + rc = te.isTemplateInvalid(); // this is the expected error } CPPUNIT_ASSERT_MESSAGE("Unable to parse valid JSON.", rc); printf("passed."); @@ -224,25 +248,25 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture std::string msg; bool rc = false; printf("\n*** ConfigMgr 2.0 - Templates - Inputs ***"); - CreateEnvironmentManager("Simple.xsd", ""); + CreateEnvironmentManager("Simple.xsd"); LoadTemplate(""); std::string json; std::vector inputTests; // Inputs testing: validate required values - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : 7 }", true, "Invalid Inputs section type (non array)")); - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [] }", false, "Valid Inputs section type (array)")); - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [\"bad\"] }", true, "Invalid Inputs array element (non-object)")); - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [{}] }", true, "Invalid Inputs array element (missing required values)")); - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [{\"name\":\"x\"}] }", true, "Invalid Inputs array element (missing required values)")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : 7 }", true, "Invalid Inputs section type (non array)")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [] }", false, "Valid Inputs section type (array)")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [\"bad\"] }", true, "Invalid Inputs array element (non-object)")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [{}] }", true, "Invalid Inputs array element (missing required values)")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [{\"name\":\"x\"}] }", true, "Invalid Inputs array element (missing required values)")); // Inputs: test types - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [{\"name\":\"x\",\"type\":\"string\"}] }", false, "Valid Inputs array element (type 'string')")); - inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [{\"name\":\"x\",\"type\":\"badtype\"}] }", true, "Invalid Input type")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [{\"name\":\"x\",\"type\":\"string\"}] }", false, "Valid Inputs array element (type 'string')")); + inputTests.emplace_back(templateInfo("{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [{\"name\":\"x\",\"type\":\"badtype\"}] }", true, "Invalid Variable type")); // Inputs: duplicate name - json = "{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"inputs\" : [{\"name\":\"x\",\"type\":\"string\"}, {\"name\":\"x\",\"type\":\"string\"}] }"; + json = "{ \"name\" : \"x\", \"type\" : \"new\", \"operations\" : [], \"variables\" : [{\"name\":\"x\",\"type\":\"string\"}, {\"name\":\"x\",\"type\":\"string\"}] }"; inputTests.emplace_back(templateInfo(json, true, "Invalid, Duplicate input name")); // @@ -253,34 +277,34 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture } // Inputs; retrieving by name with >1 inputs - json = R"({"name" : "x", "type" : "new", "operations" : [], "inputs" : [)"; - json += R"({"name":"x","type":"string", "prompt":"prompt","description":"description","tooltip":"tooltip"})"; + json = R"({"name" : "x", "type" : "new", "operations" : [], "variables" : [)"; + json += R"({"name":"x","type":"string", "prompt":"prompt","description":"description"})"; json += R"(,{"name":"y","type":"string"})"; json += R"(,{"name":"ips","type":"iprange"})"; - json += R"(,{"name":"preset","type":"string","value":"myvalue"})"; + json += R"(,{"name":"preset","type":"string","values": ["myvalue"]})"; json += "] }"; templateInfo ti1(json, false, "Valid multi-inputs"); doTemplateLoad(ti1); printf("\n Test getting inputs from template..."); - std::vector> inputs = m_pTemplate->getInputs(); + std::vector> inputs = m_pTemplate->getVariables(); CPPUNIT_ASSERT_MESSAGE("Number of inputs was not 4", inputs.size() == 4); printf("passed."); printf("\n Test retrieving first defined input 'x'..."); - auto pInput = m_pTemplate->getInput("x", false); + auto pInput = m_pTemplate->getVariable("x", false); CPPUNIT_ASSERT_MESSAGE("Unable to find input named 'x'", pInput); printf("passed."); printf("\n Test retrieving second defined input 'y'..."); - pInput = m_pTemplate->getInput("y", false); + pInput = m_pTemplate->getVariable("y", false); CPPUNIT_ASSERT_MESSAGE("Unable to find input named 'y'", pInput); printf("passed."); // // Test string type inputs (and other input attributes) printf("\n Test retrieving string input 'x'..."); - pInput = m_pTemplate->getInput("x", false); + pInput = m_pTemplate->getVariable("x", false); CPPUNIT_ASSERT_MESSAGE("Unable to find input named 'x'", pInput); printf("passed."); @@ -296,23 +320,18 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture CPPUNIT_ASSERT_MESSAGE(msg, returnedDesc == "description"); printf("passed."); - printf("\n Test retrieving 'tooltip'..."); - std::string returnedTooltip = pInput->getTooltip(); - msg = "Expected tooltip value 'tooltip' did not match returned value '" + returnedTooltip + "'"; - CPPUNIT_ASSERT_MESSAGE(msg, returnedTooltip == "tooltip"); - printf("passed."); // // test string type inputs printf("\n Test retrieving string input 'y'..."); - pInput = m_pTemplate->getInput("y", false); + pInput = m_pTemplate->getVariable("y", false); CPPUNIT_ASSERT_MESSAGE("Unable to find input named 'y'", pInput); printf("passed."); printf("\n Test setting and getting string value..."); if (pInput) { - pInput->setValue("aaaBBBccc"); + pInput->addValue("aaaBBBccc"); CPPUNIT_ASSERT_MESSAGE("Get value did not match the set value (aaaBBBccc)", pInput->getValue(0) == "aaaBBBccc"); } else @@ -325,8 +344,8 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture // IPrange inputs printf("\n Testing IPRange input..."); printf("\n Testing getting an IPRange input pointer..."); - pInput = m_pTemplate->getInput("ips"); - std::shared_ptr pIpRangeInput = std::dynamic_pointer_cast(pInput); + pInput = m_pTemplate->getVariable("ips"); + std::shared_ptr pIpRangeInput = std::dynamic_pointer_cast(pInput); if (!pIpRangeInput) { CPPUNIT_ASSERT_MESSAGE("Unable to dynamic cast input poniter to expected type of IPRange", false); @@ -336,7 +355,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture printf("\n Set single IP address..."); try { - pIpRangeInput->setValue("1.2.3.4"); + pIpRangeInput->addValue("1.2.3.4"); rc = pIpRangeInput->getValue(0) == "1.2.3.4"; CPPUNIT_ASSERT_MESSAGE("Return value did not match expected value", rc); } @@ -348,7 +367,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture printf("\n Set multiple IP addresses (';' separated IP addresses)..."); try { - pIpRangeInput->setValue("1.2.3.4;1.2.3.5"); + pIpRangeInput->addValue("1.2.3.4;1.2.3.5"); rc = pIpRangeInput->getValue(0) == "1.2.3.4"; } catch (TemplateException &te) @@ -360,7 +379,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture printf("\n Set a single range (1.2.3.1-5)..."); try { - pIpRangeInput->setValue("1.2.3.1-5"); + pIpRangeInput->addValue("1.2.3.1-5"); CPPUNIT_ASSERT_MESSAGE("Expected 5 values for variable", pIpRangeInput->getNumValues() == 5); rc = pIpRangeInput->getValue(0) == "1.2.3.1"; @@ -379,7 +398,7 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture printf("\n Set a valid and arange (3.4.5.6;1.2.3.1-5)..."); try { - pIpRangeInput->setValue("3.4.5.6;1.2.3.1-5"); + pIpRangeInput->addValue("3.4.5.6;1.2.3.1-5"); CPPUNIT_ASSERT_MESSAGE("Expected 6 values for variable", pIpRangeInput->getNumValues() == 6); rc = pIpRangeInput->getValue(0) == "3.4.5.6"; @@ -397,11 +416,20 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture printf("complete"); printf("\n Preset value..."); - pInput = m_pTemplate->getInput("preset", false); + pInput = m_pTemplate->getVariable("preset", false); CPPUNIT_ASSERT_MESSAGE("Unable to find input named 'preset'", pInput); CPPUNIT_ASSERT_MESSAGE("Variable does not have expected value", pInput->getValue(0) == "myvalue"); printf("complete"); + // + // Inputs from a file + // + // - need an InputsTest1.json that defines a number of inputs, no operations needed + // - need an InputsForInputsTest1-1.json file that has the inputs + // all defined properly + // - InputsForInputsTest1-2.json has an incorrect input name expect to fail + + // // Test complete delete m_pTemplate; @@ -414,44 +442,106 @@ class ConfigMgrTemplateTests : public CppUnit::TestFixture std::string msg; bool rc = false; printf("\n*** ConfigMgr 2.0 - Templates - Variable substitution tests ***"); - CreateEnvironmentManager("Simple.xsd", "template_test.xml"); - LoadTemplate(""); - - templateInfo si("SubstitutionTest-1.json", false, "Loading variable substitution test 1 template"); - doTemplateLoad(si); - printf("complete."); - - printf("\n Executing template..."); - m_pTemplate->execute(); - printf("complete."); + CreateEnvironmentManager("Simple.xsd"); + LoadTemplate(""); // Just loads the schema + if (loadEnvironment("template_test.xml")) + { + templateInfo si("SubstitutionTest-1.json", false, "Loading variable substitution test 1 template"); + doTemplateLoad(si); + printf("complete."); + printf("\n Executing template..."); + m_pTemplate->execute(); + printf("complete."); + } delete m_pTemplate; } + + void Test_OperationsTests() { std::string msg; bool rc = false; printf("\n*** ConfigMgr 2.0 - Templates - Execute Templates ***"); - CreateEnvironmentManager("Simple.xsd", "template_test.xml"); + CreateEnvironmentManager("Simple.xsd"); // // Test a simple add of a sub element with two attributes // load the template - // get inuts (both string) and set values (should be two) + // get inputs (both string) and set values (should be two) // execute the template // retrieve node from changed environment to validate added properly LoadTemplate("OperationsTest1.json"); + loadEnvironment("findnodes_test.xml"); + executeTemplate(false); - m_pTemplate->execute(); + // + // Test a duplicate save node id input name where it is OK for a duplicate save name + LoadTemplate("OperationsTest2.json"); + loadEnvironment("findnodes_test.xml"); + executeTemplate(false); + // + // Test a duplicate save node id input name where it is not OK for a duplicate save name + LoadTemplate("OperationsTest3.json"); + loadEnvironment("findnodes_test.xml"); + executeTemplate(true); // expect this to fail + // + // Test a duplicate save of attribute values to the same input var name + LoadTemplate("OperationsTest4.json"); + loadEnvironment("findnodes_test.xml"); + executeTemplate(false); + + // + // Test a duplicate save of attribute values to the same input var name where not allowed + LoadTemplate("OperationsTest5.json"); + loadEnvironment("findnodes_test.xml"); + executeTemplate(true); delete m_pTemplate; } + // + // Tests the action find_node + void Test_FindNodeTests() + { + std::string msg; + bool rc = false; + printf("\n*** ConfigMgr 2.0 - Templates - Find Node Tests ***"); + CreateEnvironmentManager("Simple.xsd"); + loadEnvironment("findnodes_test.xml"); + + // + // Execute the template, then query the environment to validate. Note that there + // may some modifications done in order to be able to verify + LoadTemplate("FindNodeTest1.json"); + executeTemplate(false); + } + + + // + // Tests the action find_node + void Test_CreateNodeTests() + { + std::string msg; + bool rc = false; + printf("\n*** ConfigMgr 2.0 - Templates - Create Node Tests ***"); + CreateEnvironmentManager("Simple.xsd"); + loadEnvironment("createnode_test1.xml"); + + // + // Execute the template, then query the environment to validate. Note that there + // may some modifications done in order to be able to verify + LoadTemplate("CreateNodeTest1.json"); + executeTemplate(false); + } + + + private: EnvironmentMgr *m_pEnvMgr; diff --git a/testing/unittests/configmgr/schema/environments/CMakeLists.txt b/testing/unittests/configmgr/schema/environments/CMakeLists.txt index 68654cf32b3..8ac8db1f6a7 100644 --- a/testing/unittests/configmgr/schema/environments/CMakeLists.txt +++ b/testing/unittests/configmgr/schema/environments/CMakeLists.txt @@ -15,12 +15,8 @@ # limitations under the License. ################################################################################ -if (USE_CPPUNIT) - if (USE_CPPUNIT) - FOREACH( iFILES - ${CMAKE_CURRENT_SOURCE_DIR}/template_test.xml - ) - Install ( FILES ${iFILES} DESTINATION testing/configmgr/schema/environments COMPONENT Runtime) - ENDFOREACH ( iFILES ) - endif() -endif() + +FILE(GLOB envFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.xml") +FOREACH( envFile ${envFiles} ) + Install ( FILES ${envFile} DESTINATION testing/configmgr/schema/environments COMPONENT Runtime) +ENDFOREACH () diff --git a/testing/unittests/configmgr/schema/environments/createnode_test1.xml b/testing/unittests/configmgr/schema/environments/createnode_test1.xml new file mode 100644 index 00000000000..49d7938ff0a --- /dev/null +++ b/testing/unittests/configmgr/schema/environments/createnode_test1.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/testing/unittests/configmgr/schema/environments/findnodes_test.xml b/testing/unittests/configmgr/schema/environments/findnodes_test.xml new file mode 100644 index 00000000000..ced83b2cdd5 --- /dev/null +++ b/testing/unittests/configmgr/schema/environments/findnodes_test.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/testing/unittests/configmgr/schema/environments/template_test.xml b/testing/unittests/configmgr/schema/environments/template_test.xml index fd0615102e1..c6e6afaa6ee 100644 --- a/testing/unittests/configmgr/schema/environments/template_test.xml +++ b/testing/unittests/configmgr/schema/environments/template_test.xml @@ -2,4 +2,6 @@ + + diff --git a/testing/unittests/configmgr/schema/xsd/Simple.xsd b/testing/unittests/configmgr/schema/xsd/Simple.xsd index 1cba4721592..f8f769312ac 100644 --- a/testing/unittests/configmgr/schema/xsd/Simple.xsd +++ b/testing/unittests/configmgr/schema/xsd/Simple.xsd @@ -24,10 +24,10 @@ - + - + @@ -36,9 +36,19 @@ - - - + + + + + + + + + + + + + diff --git a/testing/unittests/configmgr/templates/CreateNodeTest1.json b/testing/unittests/configmgr/templates/CreateNodeTest1.json new file mode 100644 index 00000000000..a5935f532a9 --- /dev/null +++ b/testing/unittests/configmgr/templates/CreateNodeTest1.json @@ -0,0 +1,60 @@ +{ + "name" : "", + "type" : "modification", + "inputs" : [ + ], + "operations" : [ + { + "description" : "Create another subchild in the path specified", + "action" : "create_node", + "parent_path" : "/Root/Child1", + "data" : { + "node_type" : "subchild1", + "save_nodeid" : { + "save_name" : "subchild1" + }, + "attributes" : [ + { + "name" : "attr1", + "value" : "value1" + }, + { + "name" : "attr2", + "value" : "4" + } + ] + } + }, + { + "description" : "now modify the attributes in what was added above", + "action" : "modify_node", + "parent_nodeid" : "{{subchild1}}", + "data" : { + "attributes" : [ + { + "name" : "attr1", + "value" : "new_value" + }, + { + "name" : "attr2", + "value" : "5" + } + ] + } + }, + { + "description" : "create a new non defined node (no schema)", + "action" : "find_node", + "parent_path" : "/Root/AdHocNode", + "data" : { + "create" : true, + "attributes" : [ + { + "name" : "newattr1", + "value" : "iamnew" + } + ] + } + } + ] +} diff --git a/testing/unittests/configmgr/templates/FindNodeTest1.json b/testing/unittests/configmgr/templates/FindNodeTest1.json new file mode 100644 index 00000000000..b9bc16ee1de --- /dev/null +++ b/testing/unittests/configmgr/templates/FindNodeTest1.json @@ -0,0 +1,58 @@ +{ + "name" : "Find nodes test 1", + "type" : "modification", + "description" : "Finds a node that exists", + "variables" : [ + { + "name" : "child", + "values" : ["SubChild1"], + "type" : "string" + }, + { + "name" : "ipaddresses", + "type" : "ipaddress" + } + ], + "operations" : [ + { + "action" : "find_node", + "parent_path" : "/Root/Child1/{{child}}", + "data" : { + "save_nodeid" : { + "save_name" : "parent1" + } + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child1/SubChild1[@attr1='child1_subchild1_2']" + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child2" + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child3", + "data" : { + "error_if_not_found" : false + } + }, + { + "action" : "modify_node", + "parent_nodeid" : "{{parent1}}", + "data" : { + "attributes" : [ + { + "name" : "attr2", + "value" : "4" + } + ] + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child1/SubChild1[@attr2='4']" + } + ] +} diff --git a/testing/unittests/configmgr/templates/OperationsTest1.json b/testing/unittests/configmgr/templates/OperationsTest1.json index b70b6eb36a4..1e3eeb2bd92 100644 --- a/testing/unittests/configmgr/templates/OperationsTest1.json +++ b/testing/unittests/configmgr/templates/OperationsTest1.json @@ -2,33 +2,35 @@ "name" : "test", "type" : "modification", "description" : "add a subchild to an existing node in an environment", - "inputs" : [ + "variables" : [ { "name" : "attr1", "type" : "string", - "value" : "attr1Value" + "values" : ["attr1Value"] }, { "name" : "attr2", "type" : "string", - "value" : "444" + "values" : ["444"] } ], "operations" : [ { "action" : "create_node", - "path" : "/Root/Child1", - "node_type" : "subchild1", - "attributes" : [ - { - "name" : "attr1", - "value" : "{{attr1}}" - }, - { - "name" : "attr2", - "value" : "{{attr2}}" - } - ] + "parent_path" : "/Root/Child1", + "data" : { + "node_type" : "subchild1", + "attributes" : [ + { + "name" : "attr1", + "value" : "{{attr1}}" + }, + { + "name" : "attr2", + "value" : "{{attr2}}" + } + ] + } } ] } diff --git a/testing/unittests/configmgr/templates/OperationsTest2.json b/testing/unittests/configmgr/templates/OperationsTest2.json new file mode 100644 index 00000000000..0758260d11a --- /dev/null +++ b/testing/unittests/configmgr/templates/OperationsTest2.json @@ -0,0 +1,28 @@ +{ + "name" : "", + "type" : "modification", + "description" : "Test allowing duplicate save node ID names, should execute w/o error", + "variables" : [ + ], + "operations" : [ + { + "action" : "find_node", + "parent_path" : "/Root/Child1", + "data" : { + "save_nodeid" : { + "save_name" : "nodes" + } + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child2", + "data" : { + "save_nodeid" : { + "save_name" : "nodes", + "duplicate_ok" : true + } + } + } + ] +} diff --git a/testing/unittests/configmgr/templates/OperationsTest3.json b/testing/unittests/configmgr/templates/OperationsTest3.json new file mode 100644 index 00000000000..06afa4b9776 --- /dev/null +++ b/testing/unittests/configmgr/templates/OperationsTest3.json @@ -0,0 +1,28 @@ +{ + "name" : "", + "type" : "modification", + "description" : "Test not allowing duplicate save node ID names, should execture w/o error", + "variables" : [ + ], + "operations" : [ + { + "action" : "find_node", + "parent_path" : "/Root/Child1", + "data" : { + "save_nodeid" : { + "save_name" : "nodes" + } + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child2", + "data" : { + "save_nodeid" : { + "save_name" : "nodes", + "duplicate_ok" : false + } + } + } + ] +} diff --git a/testing/unittests/configmgr/templates/OperationsTest4.json b/testing/unittests/configmgr/templates/OperationsTest4.json new file mode 100644 index 00000000000..24ee4823619 --- /dev/null +++ b/testing/unittests/configmgr/templates/OperationsTest4.json @@ -0,0 +1,38 @@ +{ + "name" : "", + "type" : "modification", + "description" : "Test allowing duplicate save of attribute values, should execute w/o error", + "variables" : [ + ], + "operations" : [ + { + "action" : "find_node", + "parent_path" : "/Root/Child1/SubChild1", + "data" : { + "attributes" : [ + { + "name" : "attr1", + "save_value" : { + "save_name" : "attrvalue" + } + } + ] + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child2/SubChild2", + "data" : { + "attributes" : [ + { + "name" : "attr1", + "save_value" : { + "save_name" : "attrvalue", + "duplicate_ok" : true + } + } + ] + } + } + ] +} diff --git a/testing/unittests/configmgr/templates/OperationsTest5.json b/testing/unittests/configmgr/templates/OperationsTest5.json new file mode 100644 index 00000000000..fdbb8f0a613 --- /dev/null +++ b/testing/unittests/configmgr/templates/OperationsTest5.json @@ -0,0 +1,38 @@ +{ + "name" : "", + "type" : "modification", + "description" : "Test not allowing duplicate save of attribute values, should cause an error", + "variables" : [ + ], + "operations" : [ + { + "action" : "find_node", + "parent_path" : "/Root/Child1/SubChild1", + "data" : { + "attributes" : [ + { + "name" : "attr1", + "save_value" : { + "save_name" : "attrvalue" + } + } + ] + } + }, + { + "action" : "find_node", + "parent_path" : "/Root/Child2/SubChild2", + "data" : { + "attributes" : [ + { + "name" : "attr1", + "save_value" : { + "save_name" : "attrvalue", + "duplicate_ok" : false + } + } + ] + } + } + ] +} diff --git a/testing/unittests/configmgr/templates/SubstitutionTest-1.json b/testing/unittests/configmgr/templates/SubstitutionTest-1.json index 35b155ada23..fdbd2cff2eb 100644 --- a/testing/unittests/configmgr/templates/SubstitutionTest-1.json +++ b/testing/unittests/configmgr/templates/SubstitutionTest-1.json @@ -2,61 +2,64 @@ "name" : "test", "type" : "modification", "description" : "add a subchild to an existing node in an environment", - "inputs" : [ + "variables" : [ { "name" : "name", "type" : "string", - "value" : "myname" + "values" : ["myname"] }, { "name" : "numsystems", "type" : "string", - "value" : "10" + "values" : ["10"] }, { "name" : "ccnum", "type" : "string", - "value" : "2" + "values" : ["2"] }, { "name" : "hwips", "type" : "iprange", - "value" : "1.2.3.1-10" + "values" : ["1.2.3.1-10"] }, { "name" : "baseip", "type" : "ipaddress", - "value" : "1.2.3.1" + "values" : ["1.2.3.1"] }, { "name" : "systemips", "type" : "iprange", + "user_input" : false, "prepared_value" : "{{baseip}}*{{numsystems}}" } ], "operations" : [ { - "action" : "noop", - "path" : "/Root/Child1", - "count" : "{{numsystems}}", - "attributes" : [ - { - "name" : "name", - "value" : "{{name}}" - }, - { - "name" : "ipaddress", - "value" : "{{hwips}}" - }, - { - "name" : "daliIp", - "value" : "{{hwips[0]}}" - }, - { - "name" : "ccIp", - "value" : "{{hwips[{{numsystems}}-{{ccnum}}]}}" - } - ] + "action" : "modify_node", + "parent_path" : "/Root/Child1", + "data" : { + "count" : "{{numsystems}}", + "attributes" : [ + { + "name" : "name", + "value" : "{{name}}" + }, + { + "name" : "ipaddress", + "value" : "{{hwips}}" + }, + { + "name" : "daliIp", + "value" : "{{hwips[0]}}" + }, + { + "name" : "ccIp", + "value" : "{{hwips[{{numsystems}}-{{ccnum}}]}}" + } + ] + } } ] }