Browse Source

HPCC-18324 Phase 2 ConfigMgr support

Adds ESP web service for new config manager

Signed-off-by: Ken Rowland <kenneth.rowland@lexisnexisrisk.com>
Ken Rowland 7 years ago
parent
commit
3b2f3c647c
46 changed files with 2094 additions and 1301 deletions
  1. 1 1
      configuration/config2/CMakeLists.txt
  2. 6 13
      configuration/config2/EnvironmentMgr.cpp
  3. 3 3
      configuration/config2/EnvironmentMgr.hpp
  4. 47 44
      configuration/config2/EnvironmentNode.cpp
  5. 1 1
      configuration/config2/EnvironmentNode.hpp
  6. 4 4
      configuration/config2/EnvironmentValue.cpp
  7. 1 1
      configuration/config2/Exceptions.hpp
  8. 1 0
      configuration/config2/SchemaItem.cpp
  9. 3 0
      configuration/config2/SchemaItem.hpp
  10. 2 1
      configuration/config2/SchemaTypeLimits.hpp
  11. 1 1
      configuration/config2/SchemaValue.cpp
  12. 4 4
      configuration/config2/Status.cpp
  13. 9 10
      configuration/config2/Status.hpp
  14. 0 29
      configuration/config2/ValueDef.hpp
  15. 1 1
      configuration/config2/XMLEnvironmentMgr.cpp
  16. 21 16
      configuration/config2/XSDSchemaParser.cpp
  17. 0 1143
      configuration/config2/configfiles/environment.xml
  18. 1 0
      esp/scm/espscm.cmake
  19. 384 0
      esp/scm/ws_config2.ecm
  20. 1 0
      esp/services/CMakeLists.txt
  21. 73 0
      esp/services/ws_config2/CMakeLists.txt
  22. 37 0
      esp/services/ws_config2/ws_config2Error.hpp
  23. 813 0
      esp/services/ws_config2/ws_config2Service.cpp
  24. 80 0
      esp/services/ws_config2/ws_config2Service.hpp
  25. 203 0
      esp/services/ws_config2/ws_config2Session.hpp
  26. 30 0
      esp/services/ws_config2/ws_config2_binding.hpp
  27. 73 0
      esp/services/ws_config2/ws_config2_plugin.cpp
  28. 1 0
      initfiles/componentfiles/CMakeLists.txt
  29. 31 0
      initfiles/componentfiles/config2xml/CMakeLists.txt
  30. 2 2
      configuration/config2/configfiles/buildset.xml
  31. 1 1
      configuration/config2/configfiles/dafilesrv.xsd
  32. 0 0
      initfiles/componentfiles/config2xml/dali.xsd
  33. 13 13
      configuration/config2/configfiles/eclagent.xsd
  34. 0 0
      initfiles/componentfiles/config2xml/environment.xsd
  35. 1 1
      configuration/config2/configfiles/esp.xsd
  36. 0 0
      initfiles/componentfiles/config2xml/esp_service_smc.xsd
  37. 0 0
      initfiles/componentfiles/config2xml/esp_service_wsecl2.xsd
  38. 0 0
      initfiles/componentfiles/config2xml/hardware.xsd
  39. 0 0
      initfiles/componentfiles/config2xml/types.xsd
  40. 1 0
      initfiles/etc/DIR_NAME/CMakeLists.txt
  41. 10 12
      configuration/config2/configfiles/hpcc.xsd
  42. 8 0
      initfiles/etc/DIR_NAME/config2mgr/config2mgr.conf.in
  43. 32 0
      initfiles/etc/DIR_NAME/config2mgr/esp.xml.in
  44. 1 0
      initfiles/sbin/CMakeLists.txt
  45. 191 0
      initfiles/sbin/config2mgr.in
  46. 2 0
      system/include/errorlist.h

+ 1 - 1
configuration/config2/CMakeLists.txt

@@ -48,4 +48,4 @@ ADD_DEFINITIONS( -D_USRDLL -DCONFIG2_LIB)
 HPCC_ADD_LIBRARY( config2 SHARED ${SRCS} )
 TARGET_LINK_LIBRARIES( config2 jlib )
 
-INSTALL( TARGETS config2 LIBRARY DESTINATION ${LIB_DIR} )
+INSTALL ( TARGETS config2 RUNTIME DESTINATION ${EXEC_DIR} LIBRARY DESTINATION ${LIB_DIR} )

+ 6 - 13
configuration/config2/EnvironmentMgr.cpp

@@ -32,7 +32,7 @@ EnvironmentMgr *getEnvironmentMgrInstance(const EnvironmentType envType)
 
 
 EnvironmentMgr::EnvironmentMgr() :
-    m_key(0)
+    m_key(1)  // ID 0 is reserved for the root node
 {
     m_pSchema = std::make_shared<SchemaItem>("root");  // make the root
 }
@@ -159,7 +159,7 @@ std::shared_ptr<EnvironmentNode> EnvironmentMgr::addNewEnvironmentNode(const std
     }
     else
     {
-        status.addMsg(statusMsg::error, parentNodeId, "", "", "Unable to find indicated parent node");
+        status.addMsg(statusMsg::error, parentNodeId, "", "Unable to find indicated parent node");
     }
     return pNewNode;
 }
@@ -195,7 +195,7 @@ std::shared_ptr<EnvironmentNode> EnvironmentMgr::addNewEnvironmentNode(const std
 }
 
 
-bool EnvironmentMgr::removeEnvironmentNode(const std::string &nodeId, Status &status)
+bool EnvironmentMgr::removeEnvironmentNode(const std::string &nodeId)
 {
     bool rc = false;
     std::shared_ptr<EnvironmentNode> pNode = getEnvironmentNode(nodeId);
@@ -206,15 +206,8 @@ bool EnvironmentMgr::removeEnvironmentNode(const std::string &nodeId, Status &st
         if (pParentNode->removeChild(pNode))
         {
             m_nodeIds.erase(nodeId);
+            rc = true;
         }
-        else
-        {
-            status.addMsg(statusMsg::error, nodeId, "", "", "Unable to remove the node");
-        }
-    }
-    else
-    {
-        status.addMsg(statusMsg::error, nodeId, "", "", "Unable to find indicated node");
     }
 
     return rc;
@@ -227,11 +220,11 @@ std::string EnvironmentMgr::getUniqueKey()
 }
 
 
-void EnvironmentMgr::validate(Status &status) const
+void EnvironmentMgr::validate(Status &status, bool includeHiddenNodes) const
 {
     if (m_pRootNode)
     {
-        m_pRootNode->validate(status, true);
+        m_pRootNode->validate(status, true, includeHiddenNodes);
     }
     else
     {

+ 3 - 3
configuration/config2/EnvironmentMgr.hpp

@@ -53,10 +53,10 @@ class DECL_EXPORT EnvironmentMgr
         bool loadEnvironment(const std::string &qualifiedFilename);
         std::shared_ptr<EnvironmentNode> getEnvironmentNode(const std::string &nodeId);
         std::shared_ptr<EnvironmentNode> addNewEnvironmentNode(const std::string &parentNodeId, const std::string &configType, Status &status);
-        bool removeEnvironmentNode(const std::string &nodeId, Status &status);
+        bool removeEnvironmentNode(const std::string &nodeId);
         bool saveEnvironment(const std::string &qualifiedFilename);
-        void discardEnvironment() { m_pRootNode = nullptr; }
-        void validate(Status &status) const;
+        void discardEnvironment() { m_pRootNode = nullptr; m_nodeIds.clear(); m_key=1; }
+        void validate(Status &status, bool includeHiddenNodes=false) const;
 
 
     protected:

+ 47 - 44
configuration/config2/EnvironmentNode.cpp

@@ -136,7 +136,7 @@ void EnvironmentNode::setAttributeValue(const std::string &attrName, const std::
         addAttribute(attrName, pEnvValue);
         if (!pCfgValue->isDefined())
         {
-            status.addMsg(statusMsg::warning, getId(), attrName, "", "Undefined attribute did not exist in configuration, was created");
+            status.addMsg(statusMsg::warning, getId(), attrName, "Undefined attribute did not exist in configuration, was created");
         }
     }
 
@@ -146,7 +146,7 @@ void EnvironmentNode::setAttributeValue(const std::string &attrName, const std::
     }
     else
     {
-        status.addMsg(statusMsg::error, getId(), attrName, "", "The attribute does not exist and was not created");
+        status.addMsg(statusMsg::error, getId(), attrName, "The attribute does not exist and was not created");
     }
 
 }
@@ -190,66 +190,69 @@ std::string EnvironmentNode::getLocalValue() const
 }
 
 
-void EnvironmentNode::validate(Status &status, bool includeChildren) const
+void EnvironmentNode::validate(Status &status, bool includeChildren, bool includeHiddenNodes) const
 {
-    //
-    // Check node value
-    if (m_pLocalValue)
+    if (!m_pSchemaItem->isHidden() || includeHiddenNodes)
     {
-        m_pLocalValue->validate(status, "");
-    }
-
-    //
-    // Check any attributes
-    for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
-    {
-        attrIt->second->validate(status, m_id);
+        //
+        // Check node value
+        if (m_pLocalValue)
+        {
+            m_pLocalValue->validate(status, m_id);
+        }
 
         //
-        // If this value must be unique, make sure it is
-        if (attrIt->second->getSchemaValue()->isUniqueValue())
+        // Check any attributes
+        for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
         {
-            bool found = false;
-            std::vector<std::string> allValues;
-            attrIt->second->getAllValuesForSiblings(allValues);
-            std::set<std::string> unquieValues;
-            for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
+            attrIt->second->validate(status, m_id);
+
+            //
+            // If this value must be unique, make sure it is
+            if (attrIt->second->getSchemaValue()->isUniqueValue())
             {
-                auto ret = unquieValues.insert(*it);
-                found = ret.second;
+                bool found = false;
+                std::vector<std::string> allValues;
+                attrIt->second->getAllValuesForSiblings(allValues);
+                std::set<std::string> unquieValues;
+                for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
+                {
+                    auto ret = unquieValues.insert(*it);
+                    found = ret.second;
+                }
+
+                if (found)
+                {
+                    status.addUniqueMsg(statusMsg::error, m_id, attrIt->second->getName(), "Attribute value must be unique");
+                }
             }
 
-            if (found)
+            //
+            // Does this value need to be from another set of values?
+            if (attrIt->second->getSchemaValue()->isFromUniqueValueSet())
             {
-                status.addUniqueMsg(statusMsg::error, m_id, attrIt->second->getName(), "", "Attribute value must be unique");
+                bool found = false;
+                std::vector<std::string> allValues;
+                attrIt->second->getSchemaValue()->getAllKeyRefValues(allValues);
+                for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
+                    found = *it == attrIt->second->getValue();
+                if (!found)
+                {
+                    status.addMsg(statusMsg::error, m_id, attrIt->second->getName(), "Attribute value must be from a unique set");
+                }
             }
         }
 
         //
-        // Does this value need to be from another set of values?
-        if (attrIt->second->getSchemaValue()->isFromUniqueValueSet())
+        // Now check all children
+        if (includeChildren)
         {
-            bool found = false;
-            std::vector<std::string> allValues;
-            attrIt->second->getSchemaValue()->getAllKeyRefValues(allValues);
-            for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
-                found = *it == attrIt->second->getValue();
-            if (!found)
+            for (auto childIt = m_children.begin(); childIt != m_children.end(); ++childIt)
             {
-                status.addMsg(statusMsg::error, m_id, attrIt->second->getName(), "", "Attribute value must be from a unique set");
+                childIt->second->validate(status, includeChildren);
             }
         }
     }
-
-    //
-    // Now check all children
-    if (includeChildren)
-    {
-        for (auto childIt = m_children.begin(); childIt != m_children.end(); ++childIt)
-        {
-            childIt->second->validate(status);
-        }
-    }
 }
 
 

+ 1 - 1
configuration/config2/EnvironmentNode.hpp

@@ -58,7 +58,7 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this<Environm
         bool hasAttributes() const { return m_attributes.size() != 0; }
         void setId(const std::string &id) { m_id = id; }
         const std::string &getId() const { return m_id;  }
-        void validate(Status &status, bool includeChildren=false) const;
+        void validate(Status &status, bool includeChildren=false, bool includeHiddenNodes=false) const;
         void getAttributeValueForAllSiblings(const std::string &attrName, std::vector<std::string> &result) const;
         const std::shared_ptr<SchemaItem> &getSchemaItem() const { return m_pSchemaItem; }
         void getInsertableItems(std::vector<std::shared_ptr<SchemaItem>> &items) const;

+ 4 - 4
configuration/config2/EnvironmentValue.cpp

@@ -36,7 +36,7 @@ bool EnvironmentValue::setValue(const std::string &value, Status *pStatus, bool
             if (pStatus != nullptr)
             {
                 std::string msg = "Attribute forced to invalid value (" + m_pSchemaValue->getType()->getLimitString() + ")";
-                pStatus->addMsg(statusMsg::info, m_pMyEnvNode.lock()->getId(), m_name, "", msg);
+                pStatus->addMsg(statusMsg::info, m_pMyEnvNode.lock()->getId(), m_name, msg);
             }
         }
         else
@@ -45,7 +45,7 @@ bool EnvironmentValue::setValue(const std::string &value, Status *pStatus, bool
             if (pStatus != nullptr)
             {
                 std::string msg = "Value not set. New value(" + value + ") not valid (" + m_pSchemaValue->getType()->getLimitString() + ")";
-                pStatus->addMsg(statusMsg::error, m_pMyEnvNode.lock()->getId(), m_name, "", msg);
+                pStatus->addMsg(statusMsg::error, m_pMyEnvNode.lock()->getId(), m_name, msg);
             }
         }
 
@@ -78,10 +78,10 @@ void EnvironmentValue::validate(Status &status, const std::string &myId) const
     if (isValueSet())
     {
         if (!m_pSchemaValue->isDefined())
-            status.addMsg(statusMsg::warning, myId, m_name, "", "No type information exists");
+            status.addMsg(statusMsg::warning, myId, m_name, "No type information exists");
 
         if (m_forcedSet)
-            status.addMsg(statusMsg::warning, myId, m_name, "", "Current value was force set to an invalid value");
+            status.addMsg(statusMsg::warning, myId, m_name, "Current value was force set to an invalid value");
 
         // Will generate status based on current value and type
         m_pSchemaValue->validate(status, myId, this);

+ 1 - 1
configuration/config2/Exceptions.hpp

@@ -35,7 +35,7 @@ class ParseException : public std::exception
         virtual void setMessage(const char *msg) { m_reason = msg; }
         virtual void setMessage(const std::string &msg) { m_reason = msg; }
 
-        virtual const char *what() const noexcept
+        virtual const char *what() const throw()
         {
             std::string &msg = const_cast<std::string &>(m_reason);
 

+ 1 - 0
configuration/config2/SchemaItem.cpp

@@ -27,6 +27,7 @@
 
 SchemaItem::SchemaItem(const std::string &name, const std::string &className, const std::shared_ptr<SchemaItem> &pParent) :
     m_pParent(pParent),
+    m_hidden(false),
     m_minInstances(1),
     m_maxInstances(1)
 {

+ 3 - 0
configuration/config2/SchemaItem.hpp

@@ -70,6 +70,8 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
 
         std::string getProperty(const std::string &name, const std::string &dfault = std::string("")) const;
         void setProperty(const std::string &name, const std::string &value) { m_properties[name] = value; }
+        void setHidden(bool hidden) { m_hidden = hidden; }
+        bool isHidden() const { return m_hidden; }
 
 
     protected:
@@ -79,6 +81,7 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
     protected:
 
         std::map<std::string, std::string> m_properties;
+        bool m_hidden;
         unsigned m_minInstances;
         unsigned m_maxInstances;
         std::multimap<std::string, std::shared_ptr<SchemaItem>> m_children;

+ 2 - 1
configuration/config2/SchemaTypeLimits.hpp

@@ -28,7 +28,8 @@ class EnvironmentValue;
 
 struct DECL_EXPORT AllowedValue
 {
-    AllowedValue(const std::string &value, const std::string &desc="") : m_value(value), m_description(desc) { }
+    AllowedValue(const std::string &value, const std::string &desc="") : m_value(value), m_displayName(value), m_description(desc) { }
+    std::string m_displayName;
     std::string m_value;
     std::string m_description;
 };

+ 1 - 1
configuration/config2/SchemaValue.cpp

@@ -85,7 +85,7 @@ void SchemaValue::validate(Status &status, const std::string &id, const Environm
                 msg = "Value is invalid (" + curValue + ").";
             msg += "Valid value (" + m_pType->getLimitString() + ")";
 
-            status.addMsg(pEnvValue->wasForced() ? statusMsg::warning : statusMsg::error, pEnvValue->getNodeId(), pEnvValue->getName(), "", msg);
+            status.addMsg(pEnvValue->wasForced() ? statusMsg::warning : statusMsg::error, pEnvValue->getNodeId(), pEnvValue->getName(), msg);
         }
         isValid = false;
     }

+ 4 - 4
configuration/config2/Status.cpp

@@ -18,16 +18,16 @@
 #include "Status.hpp"
 
 
-void Status::addMsg(enum statusMsg::msgLevel level, const std::string &nodeId, const std::string &name, const std::string &referNodeId, const std::string &msg)
+void Status::addMsg(enum statusMsg::msgLevel level, const std::string &nodeId, const std::string &name, const std::string &msg)
 {
-    statusMsg statusMsg(level, nodeId, name, referNodeId, msg);
+    statusMsg statusMsg(level, nodeId, name, msg);
     m_messages.insert({level, statusMsg });
     if (level > m_highestMsgLevel)
         m_highestMsgLevel = level;
 }
 
 
-void Status::addUniqueMsg(enum statusMsg::msgLevel level, const std::string &nodeId, const std::string &name, const std::string &referNodeId, const std::string &msg)
+void Status::addUniqueMsg(enum statusMsg::msgLevel level, const std::string &nodeId, const std::string &name, const std::string &msg)
 {
     bool duplicateFound = false;
     auto msgRange = m_messages.equal_range(level);
@@ -37,7 +37,7 @@ void Status::addUniqueMsg(enum statusMsg::msgLevel level, const std::string &nod
     }
 
     if (!duplicateFound)
-        addMsg(level, nodeId, name, referNodeId, msg);
+        addMsg(level, nodeId, name, msg);
 }
 
 std::vector<statusMsg> Status::getMessages() const

+ 9 - 10
configuration/config2/Status.hpp

@@ -35,13 +35,12 @@ struct DECL_EXPORT statusMsg {
         fatal
     };
 
-    statusMsg(enum msgLevel _msgLevel, const std::string &_nodeId, const std::string &_name, const std::string &_referNodeId, const std::string &_msg) :
-        msgLevel(_msgLevel), nodeId(_nodeId), attribute(_name), referNodeId(_referNodeId), msg(_msg) { }
-    msgLevel msgLevel;           // Message level
-    std::string nodeId;          // if not '', the node ID to which this status applies
-    std::string attribute;       // possible name of attribute in nodeId
-    std::string referNodeId;     // refernece node to which this status may also apply
-    std::string msg;             // message for user
+    statusMsg(enum msgLevel _msgLevel, const std::string &_nodeId, const std::string &_name, const std::string &_msg) :
+        msgLevel(_msgLevel), nodeId(_nodeId), attribute(_name), msg(_msg) { }
+    msgLevel msgLevel;                // Message level
+    std::string nodeId;               // if not '', the node ID to which this status applies
+    std::string attribute;            // possible name of attribute in nodeId
+    std::string msg;                  // message for user
 };
 
 class DECL_EXPORT Status
@@ -50,9 +49,9 @@ class DECL_EXPORT Status
 
         Status() : m_highestMsgLevel(statusMsg::info) { }
         ~Status() {}
-        void addMsg(enum statusMsg::msgLevel status, const std::string &msg) { addMsg(status, "", "", "", msg); }
-        void addMsg(enum statusMsg::msgLevel status, const std::string &nodeId, const std::string &name, const std::string &referNodeId, const std::string &msg);
-        void addUniqueMsg(enum statusMsg::msgLevel status, const std::string &nodeId, const std::string &name, const std::string &referNodeId, const std::string &msg);
+        void addMsg(enum statusMsg::msgLevel status, const std::string &msg) { addMsg(status, "", "", msg); }
+        void addMsg(enum statusMsg::msgLevel status, const std::string &nodeId, const std::string &name, const std::string &msg);
+        void addUniqueMsg(enum statusMsg::msgLevel status, const std::string &nodeId, const std::string &name, const std::string &msg);
         enum statusMsg::msgLevel getHighestMsgLevel() const { return m_highestMsgLevel; }
         bool isOk() const { return m_highestMsgLevel <= statusMsg::warning; }
         bool isError() const { return m_highestMsgLevel >= statusMsg::error; }

+ 0 - 29
configuration/config2/ValueDef.hpp

@@ -1,29 +0,0 @@
-/*/*##############################################################################
-
-    HPCC SYSTEMS software Copyright (C) 2017 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 _CONFIG2_VALUEDEF_HPP_
-#define _CONFIG2_VALUEDEF_HPP_
-
-#include <string>
-#include "platform.h"
-
-struct ValueDef {
-    std::string name;
-    std::string value;
-};
-
-#endif

+ 1 - 1
configuration/config2/XMLEnvironmentMgr.cpp

@@ -42,7 +42,7 @@ bool XMLEnvironmentMgr::doLoadEnvironment(std::istream &in)
         if (rootName == m_pSchema->getProperty("name"))
         {
             m_pRootNode = std::make_shared<EnvironmentNode>(m_pSchema, rootName);
-            m_pRootNode->setId(".");
+            m_pRootNode->setId("0");
             addPath(m_pRootNode);
             parse(rootIt->second, m_pSchema, m_pRootNode);
         }

+ 21 - 16
configuration/config2/XSDSchemaParser.cpp

@@ -245,6 +245,7 @@ void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
     std::string catName = typeTree.get("<xmlattr>.hpcc:category", "");
     std::string componentName = typeTree.get("<xmlattr>.hpcc:componentName", "");
     std::string displayName = typeTree.get("<xmlattr>.hpcc:displayName", "");
+    bool hidden = typeTree.get("<xmlattr>.hpcc:hidden", "false") == "true";
 
     if (!complexTypeName.empty())
     {
@@ -256,6 +257,7 @@ void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
                 pComponent->setProperty("category", catName);
                 pComponent->setProperty("componentName", componentName);
                 pComponent->setProperty("displayName", displayName);
+                pComponent->setHidden(hidden);
                 pt::ptree componentTree = typeTree.get_child("", pt::ptree());
                 if (!componentTree.empty())
                 {
@@ -309,16 +311,19 @@ void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
     std::string className = elemTree.get("<xmlattr>.hpcc:class", "");
     std::string category = elemTree.get("<xmlattr>.hpcc:category", "");
     std::string displayName = elemTree.get("<xmlattr>.hpcc:displayName", "");
+    std::string tooltip = elemTree.get("<xmlattr>.hpcc:tooltip", "");
     std::string typeName = elemTree.get("<xmlattr>.type", "");
     unsigned minOccurs = elemTree.get("<xmlattr>.minOccurs", 1);
     std::string maxOccursStr = elemTree.get("<xmlattr>.maxOccurs", "1");
     unsigned maxOccurs = (maxOccursStr != "unbounded") ? stoi(maxOccursStr) : UINTMAX_MAX;
 
-    std::shared_ptr<SchemaItem> pConfigElement = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
-    pConfigElement->setProperty("displayName", displayName);
-    pConfigElement->setMinInstances(minOccurs);
-    pConfigElement->setMaxInstances(maxOccurs);
-    pConfigElement->setProperty("category", category);
+    std::shared_ptr<SchemaItem> pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
+    pNewSchemaItem->setProperty("displayName", displayName);
+    pNewSchemaItem->setMinInstances(minOccurs);
+    pNewSchemaItem->setMaxInstances(maxOccurs);
+    pNewSchemaItem->setProperty("category", category);
+    pNewSchemaItem->setProperty("tooltip", tooltip);
+    pNewSchemaItem->setHidden(elemTree.get("<xmlattr>.hpcc:hidden", "false") == "true");
 
     pt::ptree childTree = elemTree.get_child("", pt::ptree());
 
@@ -339,7 +344,7 @@ void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
             {
                 std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>("");  // no name value since it's the element's value
                 pCfgValue->setType(pSimpleType);                      // will throw if type is not defined
-                pConfigElement->setItemSchemaValue(pCfgValue);
+                pNewSchemaItem->setItemSchemaValue(pCfgValue);
             }
             else
             {
@@ -348,22 +353,22 @@ void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
                 {
                     //
                     // Insert into this config element the component defined data (attributes, references, etc.)
-                    pConfigElement->insertSchemaType(pConfigType);
+                    pNewSchemaItem->insertSchemaType(pConfigType);
 
                     //
                     // Set element min/max instances to that defined by the component type def (ignore values parsed above)
-                    pConfigElement->setMinInstances(pConfigType->getMinInstances());
-                    pConfigElement->setMaxInstances(pConfigType->getMaxInstances());
+                    pNewSchemaItem->setMinInstances(pConfigType->getMinInstances());
+                    pNewSchemaItem->setMaxInstances(pConfigType->getMaxInstances());
 
                     //
                     // If a component, then set element data (allow overriding with locally parsed values)
                     if (pConfigType->getProperty("className") == "component")
                     {
-                        pConfigElement->setProperty("name", (!elementName.empty()) ? elementName : pConfigType->getProperty("name"));
-                        pConfigElement->setProperty("className", (!className.empty()) ? className : pConfigType->getProperty("className"));
-                        pConfigElement->setProperty("category", (!category.empty()) ? category : pConfigType->getProperty("category"));
-                        pConfigElement->setProperty("displayName", (!displayName.empty()) ? displayName : pConfigType->getProperty("displayName"));
-                        pConfigElement->setProperty("componentName", pConfigType->getProperty("componentName"));
+                        pNewSchemaItem->setProperty("name", (!elementName.empty()) ? elementName : pConfigType->getProperty("name"));
+                        pNewSchemaItem->setProperty("className", (!className.empty()) ? className : pConfigType->getProperty("className"));
+                        pNewSchemaItem->setProperty("category", (!category.empty()) ? category : pConfigType->getProperty("category"));
+                        pNewSchemaItem->setProperty("displayName", (!displayName.empty()) ? displayName : pConfigType->getProperty("displayName"));
+                        pNewSchemaItem->setProperty("componentName", pConfigType->getProperty("componentName"));
                     }
                 }
                 else
@@ -378,13 +383,13 @@ void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
         // Now, if there are children, create a parser and have at it
         if (!childTree.empty())
         {
-            std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pConfigElement);
+            std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pNewSchemaItem);
             pXSDParaser->parseXSD(childTree);
         }
 
         //
         // Add the element
-        m_pSchemaItem->addChild(pConfigElement);
+        m_pSchemaItem->addChild(pNewSchemaItem);
 
     }
 }

File diff suppressed because it is too large
+ 0 - 1143
configuration/config2/configfiles/environment.xml


+ 1 - 0
esp/scm/espscm.cmake

@@ -40,6 +40,7 @@ set ( ESPSCM_SRCS
       ws_esdlconfig.ecm
       ws_loggingservice.ecm
       ws_espcontrol.ecm
+      ws_config2.ecm
     )
 
 foreach ( loop_var ${ESPSCM_SRCS} )

+ 384 - 0
esp/scm/ws_config2.ecm

@@ -0,0 +1,384 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 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.
+############################################################################## */
+
+
+ESPstruct StatusMsgType
+{
+    string msgLevel;                // message level ("info", "warning", "error", "fatal")
+    string nodeId;                  // node identifier that generated this status message
+    string nodeName("");            // node name (not unique, use nodeID to find node)
+    string attribute("");           // name of node's attribute generating the message ("" for the node itself)
+    string msg;                     // the message
+    ESParray<string, parentId> parentIdList;
+};
+
+ESPstruct StatusType
+{
+    bool error(false);    // true if a message exsits in status (below) with a message level of error or higher
+    ESParray<ESPstruct StatusMsgType, StatusMsg> status;
+};
+
+
+ESPresponse [exceptions_inline] EmptyResponse
+{
+};
+
+
+ESPresponse StatusResponse
+{
+    ESPstruct StatusType status;
+};
+
+
+//
+// Session management (not to be confused with platform session management)
+
+ESPrequest OpenSessionRequest
+{
+    string username;
+    string schemaPath("");        // location of configuration schema environmentFiles
+    string masterSchemaFile("");  // name of master schema control file
+    string sourcePath("");        // path to envrionment files
+    string activePath("");        // path to active environment file ("" for none)
+};
+
+
+ESPresponse [exceptions_inline] OpenSessionResponse
+{
+    string sessionId;
+};
+
+
+ESPrequest CloseSessionRequest
+{
+    string sessionId;
+    bool forceClose(false);    // set to true for force the closure of a modified environment w/o saving
+};
+
+
+ESPrequest ListOpenSessionsRequest
+{
+};
+
+
+ESPstruct OpenSessionInfo
+{
+    string username;
+    string curEnvironmentFile("");
+    bool   locked;
+    bool   modified;
+};
+
+
+ESPresponse [exceptions_inline] ListOpenSessionsResponse
+{
+    ESParray<ESPstruct OpenSessionInfo, SessionInfo> openSessions;
+};
+
+
+//
+// Node management
+
+ESPrequest NodeRequest
+{
+    string sessionId;
+    string nodeId;
+};
+
+
+ESPrequest RemoveNodeRequest
+{
+    string sessionId;
+    string sessionLockKey;
+    string nodeId;
+};
+
+
+ESPstruct ChoiceType
+{
+    string displayName;
+    string value;
+    string desc("");
+};
+
+
+ESPstruct LimitsType
+{
+    bool   minValid(false);
+    bool   maxValid(false);
+    int    min(0);
+    int    max(0);
+    ESParray<ESPstruct ChoiceType, Choice> choiceList;
+    ESParray<string, Expr> regex;
+};
+
+
+ESPstruct TypeInfo
+{
+    string name;
+    ESPstruct LimitsType limits;
+    ESParray<string, Modifier> modifiers;
+};
+
+
+ESPstruct NodeInfoType
+{
+    string    name("");
+    string    nodeType("");
+    string    class("");
+    string    category("");
+    bool      required(false);   // only used when in the insertable list
+    bool      hidden(false);
+    string    tooltip("");
+};
+
+
+ESPstruct NodeType
+{
+    string    nodeId;
+    int       numChildren(0);
+    ESPstruct NodeInfoType nodeInfo;
+};
+
+
+ESPstruct AttributeType
+{
+    string     displayName;
+    string     name;
+    string     tooltip("");
+    ESPstruct  TypeInfo type;
+    string currentValue("");
+    string defaultValue("");
+    bool   required(true);
+    bool   readOnly(false);
+    bool   hidden(false);
+};
+
+
+ESPstruct AttributeValueType
+{
+    string    name;
+    string    value;
+};
+
+
+ESPresponse [exceptions_inline] GetNodeResponse : StatusResponse
+{
+    string nodeId;
+    ESPstruct NodeInfoType nodeInfo;
+    ESParray<ESPstruct AttributeType, Attribute> attributes;
+    ESParray<ESPstruct NodeType, Child> children;
+    ESParray<ESPstruct NodeInfoType, Item> insertable;           // list of insertable elements under this node
+    bool localValueDefined(false);                               // true if the node is configured to have a value (not usually true)
+    ESPstruct AttributeType value;                               // this would be <element>value</element> not normal, see valueDefined
+};
+
+
+ESPrequest InsertNodeRequest  // response is same as GetPathRequest
+{
+    string sessionId;
+    string sessionLockKey;
+    string parentNodeId;
+    string nodeType;  // name of new instance to create
+};
+
+
+ESPrequest SetValuesRequest
+{
+    string sessionId;
+    string sessionLockKey;
+    string nodeId;
+    ESParray<ESPstruct AttributeValueType, Attribute> attributeValues;
+    bool allowInvalid(false);  // true to allow setting an invalid value
+    bool forceCreate(false);   // force creaation of new value if not defined
+    string localValue("");
+};
+
+
+ESPresponse [exceptions_inline] GetParentsResponse
+{
+    ESParray<string, parentId> parentIdList;
+};
+
+
+//
+// Environment management
+
+ESPstruct EnvironmentFileType
+{
+    string    filename;
+    bool      isActive(false);  // True if this is the current active environment
+};
+
+
+ESPrequest CommonSessionRequest
+{
+    string sessionId;
+};
+
+
+ESPresponse [exceptions_inline] GetEnvironmentListResponse
+{
+    ESParray<ESPstruct EnvironmentFileType, EnvironmentFile> environmentFiles;
+};
+
+
+ESPrequest OpenEnvironmentFileRequest
+{
+    string sessionId;
+    string filename;
+};
+
+
+ESPresponse [exceptions_inline] OpenEnvironmentFileResponse
+{
+    string rootNodeId;
+};
+
+
+ESPrequest CloseEnvironmentFileRequest
+{
+    string sessionId;
+    string sessionLockKey;                // required if existing environment is modified and discarding changes
+    bool   discardChanges(false);         // discard modifications
+};
+
+
+ESPrequest SaveEnvironmentFileRequest
+{
+    string sessionId;
+    string sessionLockKey;          // required if saving existing environment and it has been modified
+    string filename("");            // empty string means to overwrite the existing file
+};
+
+
+ESPresponse [exceptions_inline] LockSessionResponse
+{
+    string sessionLockKey;       // filled in when a session is sucessfully locked
+};
+
+
+ESPrequest UnlockSessionRequest
+{
+    string sessionId;
+    string sessionLockKey;
+};
+
+
+ESPrequest ValidateEnvironmentRequest
+{
+    string sessionId;
+    bool   includeHiddenNodes(false);   // includes hidden nodes (hidden attributes always included)
+};
+
+
+ESPrequest GetTreeRequest
+{
+    string sessionId;
+    string nodeId;
+    bool   includeAttributes;
+    int    numLevels(1);
+};
+
+
+ESPstruct TreeElementType
+{
+    string    nodeId;
+    ESPstruct NodeInfoType nodeInfo;
+    ESParray<ESPstruct AttributeType, attribute> attributes;
+    ESParray<ESPstruct TreeElementType, Node> children;
+};
+
+
+ESPresponse [exceptions_inline] GetTreeResponse
+{
+    ESPstruct TreeElementType tree;
+};
+
+
+ESPservice [auth_feature("DEFERRED"),version("2.0"), default_client_version("2.0"), exceptions_inline("xslt/exceptions.xslt")] ws_config2
+{
+    ESPMethod
+        [
+            description("Open a session with the configuration manager service")
+        ] OpenSession(OpenSessionRequest, OpenSessionResponse);
+    ESPMethod
+        [
+            description("Close a previously open session")
+        ] CloseSession(CloseSessionRequest, EmptyResponse);
+    ESPMethod
+        [
+            description("Get a list of environment files available")
+        ] GetEnvironmentFileList(CommonSessionRequest, GetEnvironmentListResponse);
+    ESPMethod
+        [
+            description("Open an environment file")
+        ] OpenEnvironmentFile(OpenEnvironmentFileRequest, OpenEnvironmentFileResponse);
+    ESPMethod
+        [
+            description("Close an open environment file")
+        ] CloseEnvironmentFile(CloseEnvironmentFileRequest, EmptyResponse);
+    ESPMethod
+        [
+            description("Save a modifed environment file, or save to a new environment file")
+        ] SaveEnvironmentFile(SaveEnvironmentFileRequest, EmptyResponse);
+    ESPMethod
+        [
+            description("Lock a session's open environment for modification")
+        ] LockSession(CommonSessionRequest, LockSessionResponse);
+    ESPMethod
+        [
+            description("Unlock a previously locked session")
+        ] UnlockSession(UnlockSessionRequest, EmptyResponse);
+    ESPMethod
+        [
+            description("Get an environment node")
+        ] GetNode(NodeRequest, GetNodeResponse);
+    ESPMethod
+        [
+            description("Set one or more of a node's attribute values,and/or the node's local value")
+        ] SetValues(SetValuesRequest, StatusResponse);
+    ESPMethod
+        [
+            description("Returns the nodes parents all the way to the root")
+        ] GetParents(NodeRequest, GetParentsResponse);
+    ESPMethod
+        [
+            description("Insert a new node into the environment")
+        ] InsertNode(InsertNodeRequest, GetNodeResponse);
+    ESPMethod
+        [
+            description("Remove a node from the environment")
+        ] RemoveNode(RemoveNodeRequest, StatusResponse);
+    ESPMethod
+        [
+            description("Validate the environment")
+        ] ValidateEnvironment(ValidateEnvironmentRequest, StatusResponse);
+    ESPMethod
+        [
+            description("Return a list of open sessions")
+        ] GetOpenSessions(ListOpenSessionsRequest, ListOpenSessionsResponse);
+    ESPMethod
+        [
+            description("Return the tree of nodes beneath the indicated node for the indicated number of levels")
+        ] GetNodeTree(GetTreeRequest, GetTreeResponse);
+};
+
+
+SCMexportdef(ws_config2);
+
+SCMapi(ws_config2) IClientws_config2 *createws_config2Client();

+ 1 - 0
esp/services/CMakeLists.txt

@@ -19,6 +19,7 @@ IF (USE_OPENLDAP)
 ENDIF(USE_OPENLDAP)
 HPCC_ADD_SUBDIRECTORY (ws_account "PLATFORM")
 HPCC_ADD_SUBDIRECTORY (ws_config "PLATFORM")
+HPCC_ADD_SUBDIRECTORY (ws_config2 "PLATFORM")
 HPCC_ADD_SUBDIRECTORY (ws_dfu "PLATFORM")
 HPCC_ADD_SUBDIRECTORY (ws_ecl "PLATFORM")
 HPCC_ADD_SUBDIRECTORY (ws_fileio "PLATFORM")

+ 73 - 0
esp/services/ws_config2/CMakeLists.txt

@@ -0,0 +1,73 @@
+##############################################################################
+#
+#    HPCC SYSTEMS software Copyright (C) 2017 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(ws_config2)
+
+include(${HPCC_SOURCE_DIR}/esp/scm/espscm.cmake)
+
+set(SRCS
+    ${ESPSCM_GENERATED_DIR}/ws_config2_esp.cpp
+    ws_config2Service.cpp
+    ws_config2_plugin.cpp
+    )
+
+include_directories(
+    ${HPCC_SOURCE_DIR}/esp/esplib
+    ${HPCC_SOURCE_DIR}/esp/platform
+    ${HPCC_SOURCE_DIR}/system/jlib
+    ${HPCC_SOURCE_DIR}/common/environment
+    ${HPCC_SOURCE_DIR}/esp/services
+    ${HPCC_SOURCE_DIR}/common
+    ${HPCC_SOURCE_DIR}/system/security/securesocket
+    ${HPCC_SOURCE_DIR}/system/security/shared
+    ${HPCC_SOURCE_DIR}/system/include
+    ${HPCC_SOURCE_DIR}/common/remote
+    ${HPCC_SOURCE_DIR}/clients
+    ${HPCC_SOURCE_DIR}/dali/base
+    ${HPCC_SOURCE_DIR}/common/dllserver
+    ${HPCC_SOURCE_DIR}/esp/bindings
+    ${HPCC_SOURCE_DIR}/esp/bindings/SOAP/xpp
+    ${HPCC_SOURCE_DIR}/esp/bindings/http/client
+    ${HPCC_SOURCE_DIR}/esp/http/platform
+    ${HPCC_SOURCE_DIR}/system/mp
+    ${HPCC_SOURCE_DIR}/system/xmllib
+    ${HPCC_SOURCE_DIR}/configuration/config2
+    ${CMAKE_BINARY_DIR}
+    ${CMAKE_BINARY_DIR}/oss
+    ${CMAKE_BINARY_DIR}/esp/services/ws_config2
+    )
+
+add_definitions(-D_USRDLL)
+
+HPCC_ADD_LIBRARY(ws_config2 SHARED ${SRCS})
+install(
+    TARGETS ws_config2
+    RUNTIME DESTINATION ${EXEC_DIR}
+    LIBRARY DESTINATION ${LIB_DIR}
+    COMPONENT Runtime
+    )
+add_dependencies(ws_config2 espscm config2)
+target_link_libraries(ws_config2
+    jlib
+    xmllib
+    esphttp
+    environment
+    dllserver
+    securesocket
+    config2
+    )

+ 37 - 0
esp/services/ws_config2/ws_config2Error.hpp

@@ -0,0 +1,37 @@
+/*##############################################################################
+
+    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 _WSCONFIG2_ERROR_HPP_
+#define _WSCONFIG2_ERROR_HPP_
+
+#include "errorlist.h"
+
+#define CFGMGR_ERROR_INVALID_SESSION_ID      CONFIG_MGR_ERROR_START      // input session ID is not valid
+#define CFGMGR_ERROR_MISSING_SESSION_ID      CONFIG_MGR_ERROR_START+1    // input session ID is missing
+#define CFGMGR_ERROR_SESSION_NOT_LOCKED      CONFIG_MGR_ERROR_START+2    // session not locked for writing
+#define CFGMGR_ERROR_SESSION_NOT_CREATED     CONFIG_MGR_ERROR_START+3    // There was an issue creating the session (see error text)
+#define CFGMGR_ERROR_ENVIRONMENT_MODIFIED    CONFIG_MGR_ERROR_START+4    // The current environment has been modified
+#define CFGMGR_ERROR_ENVIRONMENT_NOT_LOADED  CONFIG_MGR_ERROR_START+5    // Unable to load environment
+#define CFGMGR_ERROR_LOCK_KEY_INVALID        CONFIG_MGR_ERROR_START+6    // session lock key is not valid
+#define CFGMGR_ERROR_ENVIRONMENT_LOCKED      CONFIG_MGR_ERROR_START+7    // Environment is locked
+#define CFGMGR_ERROR_SAVE_ENVIRONMENT        CONFIG_MGR_ERROR_START+8    // Error saving the environment
+#define CFGMGR_ERROR_NO_ENVIRONMENT          CONFIG_MGR_ERROR_START+9    // No environment loaded
+#define CFGMGR_ERROR_ENV_EXTERNAL_CHANGE     CONFIG_MGR_ERROR_START+10   // Environment was externally modified
+#define CFGMGR_ERROR_ENVIRONMENT_LOCKING     CONFIG_MGR_ERROR_START+11   // Error locking the enironment
+#define CFGMGR_ERROR_NODE_INVALID            CONFIG_MGR_ERROR_START+12   // Enironment node not valid
+
+#endif

+ 813 - 0
esp/services/ws_config2/ws_config2Service.cpp

@@ -0,0 +1,813 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 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 "ws_config2Service.hpp"
+#include "jfile.hpp"
+#include "SchemaItem.hpp"
+#include "jexcept.hpp"
+#include "ws_config2Error.hpp"
+
+static const std::string CFG2_MASTER_CONFIG_FILE = "environment.xsd";
+static const std::string CFG2_CONFIG_DIR = COMPONENTFILES_DIR  PATHSEPSTR "config2xml" PATHSEPSTR;
+static const std::string CFG2_SOURCE_DIR = CONFIG_SOURCE_DIR;
+static const std::string ACTIVE_ENVIRONMENT_FILE = CONFIG_DIR PATHSEPSTR ENV_XML_FILE;
+
+Cws_config2Ex::Cws_config2Ex()
+{
+    m_sessionKey = 0;
+}
+
+
+Cws_config2Ex::~Cws_config2Ex()
+{
+}
+
+
+bool Cws_config2Ex::onOpenSession(IEspContext &context, IEspOpenSessionRequest &req, IEspOpenSessionResponse &resp)
+{
+    bool loaded = false;
+    ConfigMgrSession *pNewSession = new ConfigMgrSession();
+
+    std::string inputMasterFile = req.getMasterSchemaFile();
+    std::string inputSchemaPath = req.getSchemaPath();
+    std::string inputSourcePath = req.getSourcePath();
+    std::string inputActivePath = req.getSourcePath();
+    pNewSession->masterConfigFile = (inputMasterFile != "") ? inputMasterFile : CFG2_MASTER_CONFIG_FILE;
+    pNewSession->username = req.getUsername();
+    pNewSession->schemaPath = !inputSchemaPath.empty() ? inputSchemaPath : CFG2_CONFIG_DIR;
+    pNewSession->sourcePath = !inputSourcePath.empty() ? inputSourcePath : CFG2_SOURCE_DIR;
+    pNewSession->activePath = !inputActivePath.empty() ? inputActivePath : ACTIVE_ENVIRONMENT_FILE;
+
+    //
+    // Only XML supported at this time
+    pNewSession->configType = XML;
+
+    //
+    // Open the session by loading the schema, which is done during session init
+    std::vector<std::string> cfgParms;
+    cfgParms.push_back("buildset.xml");  // Note that this is hardcoded for now, when other types suppored, must be passed in
+    if (pNewSession->initializeSession(cfgParms))
+    {
+        std::string sessionId = std::to_string(m_sessionKey);
+        resp.setSessionId(sessionId.c_str());
+        m_sessions[sessionId] = pNewSession;
+        m_sessionKey++;
+    }
+    else
+    {
+        std::string errMsg = pNewSession->getLastMsg();
+        delete pNewSession;
+        throw MakeStringException(CFGMGR_ERROR_SESSION_NOT_CREATED, "Error creating session, error: %s", errMsg.c_str());
+    }
+    return true;
+}
+
+
+bool Cws_config2Ex::onCloseSession(IEspContext &context, IEspCloseSessionRequest &req, IEspEmptyResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+
+    if (pSession->modified)
+    {
+        if (!req.getForceClose())
+        {
+            throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_MODIFIED, "Current environment is modified, either save or close it first");
+        }
+    }
+
+    deleteConfigSession(sessionId);
+    return true;
+}
+
+
+bool Cws_config2Ex::onGetOpenSessions(IEspContext &context, IEspListOpenSessionsRequest &req, IEspListOpenSessionsResponse &resp)
+{
+    IArrayOf<IEspOpenSessionInfo> openSessions;
+    for (auto sessionIt=m_sessions.begin(); sessionIt != m_sessions.end(); ++sessionIt)
+    {
+        ConfigMgrSession *pSession = sessionIt->second;
+        Owned<IEspOpenSessionInfo> pSessionInfo = createOpenSessionInfo();
+        pSessionInfo->setUsername(pSession->username.c_str());
+        pSessionInfo->setCurEnvironmentFile(pSession->curEnvironmentFile.c_str());
+        pSessionInfo->setLocked(pSession->locked);
+        pSessionInfo->setModified(pSession->modified);
+        openSessions.append(*pSessionInfo.getLink());
+    }
+    resp.setOpenSessions(openSessions);
+    return true;
+}
+
+
+bool Cws_config2Ex::onGetEnvironmentFileList(IEspContext &context, IEspCommonSessionRequest &req, IEspGetEnvironmentListResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+
+    //
+    // Calculate md5 checksum of the current active environment for use in comparing against the available list
+    // of environments so the active environment is identified in the returned list
+    StringBuffer activeConfig_md5sum;
+    md5_filesum(pSession->activePath.c_str(), activeConfig_md5sum);
+    IArrayOf<IEspEnvironmentFileType> environmentFiles;
+    Owned<IFile> pDir = createIFile(CFG2_SOURCE_DIR.c_str());
+    if (pDir->exists())
+    {
+        Owned<IDirectoryIterator> it = pDir->directoryFiles(NULL, false, true);
+        ForEach(*it)
+        {
+            StringBuffer filename;
+            it->getName(filename);
+            String str(filename);
+            str.toLowerCase();
+            if (str.endsWith(pSession->getEnvironmentFileExtension().c_str()))
+            {
+                Owned<IEspEnvironmentFileType> pEnvFile = createEnvironmentFileType();
+                pEnvFile->setFilename(filename.str());
+                //
+                // See if active
+                StringBuffer curEnvFile_md5sum;
+                std::string fullPath;
+                std::string fname = filename.str();
+                pSession->getEnvironmentFullyQualifiedPath(fname, fullPath);
+                md5_filesum(fullPath.c_str(), curEnvFile_md5sum);
+                if (strcmp(curEnvFile_md5sum.str(),activeConfig_md5sum.str()) == 0)
+                {
+                    pEnvFile->setIsActive(true);
+                }
+                environmentFiles.append(*pEnvFile.getLink());
+            }
+        }
+        resp.setEnvironmentFiles(environmentFiles);
+    }
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onOpenEnvironmentFile(IEspContext &context, IEspOpenEnvironmentFileRequest &req, IEspOpenEnvironmentFileResponse &resp)
+{
+    bool doOpen = false;
+    ConfigMgrSession *pSession = getConfigSession(req.getSessionId());
+
+    //
+    // See if modified (which can only be true if an environment is currently loaded)
+    if (pSession->modified)
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_MODIFIED, "Current environment is modified, either save or close it first");
+    }
+
+    std::string newEnvFile = req.getFilename();
+    if (!pSession->loadEnvironment(newEnvFile))
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_NOT_LOADED, "Unable to load environment, error = %s", pSession->getLastMsg().c_str());
+    }
+
+    resp.setRootNodeId("0");
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onCloseEnvironmentFile(IEspContext &context, IEspCloseEnvironmentFileRequest &req, IEspEmptyResponse &resp)
+{
+    bool doClose = false;
+    ConfigMgrSession *pSession = getConfigSession(req.getSessionId());
+
+    if (pSession->modified)
+    {
+        pSession = getConfigSessionForUpdate(req.getSessionId(), req.getSessionLockKey());  // forces the lock key check
+
+        //
+        // Since modified, only allow close if discard changes is set
+        if (!req.getDiscardChanges())
+        {
+            throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_MODIFIED, "Current environment is modified, either save, close, or set discardChanges");
+        }
+    }
+
+    pSession->closeEnvironment();
+    return true;
+}
+
+
+bool Cws_config2Ex::onSaveEnvironmentFile(IEspContext &context, IEspSaveEnvironmentFileRequest &req, IEspEmptyResponse &resp)
+{
+    // todo: If this envronment file is loaded by any other session, go mark that session that the environment has changed
+    // and don't allow a save w/o reloading first. maybe add a reload request. Add relevant errors.
+    // also make sure if filename is specified that it
+    //  1. does not match the current loaded environment if current environment is modified and lock key is missing or does not match
+    //  2. does not match the name of any other open session's loaded and locked environment.
+    std::string sessionId = req.getSessionId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);  // only verify session ID, lock key verified below
+
+    std::string saveFilename = req.getFilename();
+    bool doSave = true;
+    bool wasSaved = false;
+
+    //
+    // If a new filename is given, then search existing sessions to see if the filename matches any open
+    // environments that have locked it. If so, prevent the save.
+    if (!saveFilename.empty())
+    {
+        for (auto sessionIt = m_sessions.begin(); sessionIt != m_sessions.end(); ++sessionIt)
+        {
+            if (sessionIt->second->curEnvironmentFile == saveFilename && sessionIt->second->locked)
+            {
+                throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_LOCKED, "Target environment is currently locked by another session");
+            }
+        }
+    }
+
+    //
+    // If session if modified and saving over exiting environment, check lock key
+    if (saveFilename.empty() && pSession->modified)
+    {
+        pSession = getConfigSessionForUpdate(req.getSessionLockKey(), req.getSessionLockKey());
+    }
+
+    if (!pSession->saveEnvironment(saveFilename))
+    {
+        throw MakeStringException(CFGMGR_ERROR_SAVE_ENVIRONMENT, "There was a problem saving the environment");
+    }
+
+    //
+    // Save was completed, if a new filename was given, then search existing sessions to see if the filename
+    // matches any open environments. If so, mark the session as having the environment externally modified
+    // which prevents that session from locking the environment. Note that another session having the
+    // environment locked would have prevented this session from locking the same environment.
+    if (!saveFilename.empty())
+    {
+        for (auto sessionIt = m_sessions.begin(); sessionIt != m_sessions.end(); ++sessionIt)
+        {
+            if (sessionIt->second->curEnvironmentFile == saveFilename)
+            {
+                sessionIt->second->externallyModified = true;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onLockSession(IEspContext &context, IEspCommonSessionRequest &req, IEspLockSessionResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+    if (pSession->locked)
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_LOCKED, "Current enironment already locked");
+    }
+
+    if (pSession->curEnvironmentFile.empty())
+    {
+        throw MakeStringException(CFGMGR_ERROR_NO_ENVIRONMENT, "No environment loaded");
+    }
+
+    if (pSession->externallyModified)
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENV_EXTERNAL_CHANGE, "No environment loaded");
+    }
+
+    //
+    // Search existing sessions to see if any currently have this environment file locked
+    for (auto sessionIt = m_sessions.begin(); sessionIt != m_sessions.end(); ++sessionIt)
+    {
+        if (sessionIt->second != pSession && sessionIt->second->locked && pSession->curEnvironmentFile == sessionIt->second->curEnvironmentFile)
+        {
+            throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_LOCKED, "Environment is locked by another session");
+        }
+    }
+
+    if (pSession->lock())
+    {
+        resp.setSessionLockKey(pSession->lockKey.c_str());
+    }
+    else
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_LOCKING, "Error locking the session");
+    }
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onUnlockSession(IEspContext &context, IEspUnlockSessionRequest &req, IEspEmptyResponse &resp)
+{
+    ConfigMgrSession *pSession = getConfigSessionForUpdate(req.getSessionId(), req.getSessionLockKey());
+
+    if (!pSession->unlock(req.getSessionLockKey()))
+    {
+        throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_LOCKING, "Error unlocking the session");
+    }
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onGetNode(IEspContext &context, IEspNodeRequest &req, IEspGetNodeResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    std::string id = req.getNodeId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+    Status status;
+
+    EnvironmentMgr *pEnvMgr = pSession->m_pEnvMgr;
+    std::shared_ptr<EnvironmentNode> pNode = pEnvMgr->getEnvironmentNode(id);
+    if (pNode == nullptr)
+    {
+        throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "Environment node ID is not valid");
+    }
+
+    getNodeResponse(pNode, resp);
+    pNode->validate(status, false);  // validate this node only
+    addStatusToResponse(status, pSession, reinterpret_cast<IEspStatusResponse &>(resp));
+
+    //
+    // Finalize the response
+    resp.setNodeId(id.c_str());
+    return true;
+}
+
+
+bool Cws_config2Ex::onInsertNode(IEspContext &context, IEspInsertNodeRequest &req, IEspGetNodeResponse &resp)
+{
+    ConfigMgrSession *pSession = getConfigSessionForUpdate(req.getSessionId(), req.getSessionLockKey());
+    Status status;
+
+    std::string parentNodeId = req.getParentNodeId();
+    std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode(parentNodeId);
+
+    if (pNode)
+    {
+        std::shared_ptr<EnvironmentNode> pNewNode = pSession->m_pEnvMgr->addNewEnvironmentNode(parentNodeId, req.getNodeType(), status);
+        if (pNewNode)
+        {
+            getNodeResponse(pNewNode, resp);
+            resp.setNodeId(pNewNode->getId().c_str());
+            pSession->modified = true;
+        }
+    }
+    else
+    {
+        throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "Environment node ID is not valid");
+    }
+
+    addStatusToResponse(status, pSession, reinterpret_cast<IEspStatusResponse &>(resp));
+    return true;
+}
+
+
+bool Cws_config2Ex::onRemoveNode(IEspContext &context, IEspRemoveNodeRequest &req, IEspStatusResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    std::string key = req.getSessionLockKey();
+    ConfigMgrSession *pSession = getConfigSessionForUpdate(sessionId, key);
+    Status status;
+
+    std::string nodeId = req.getNodeId();
+
+    if (!pSession->m_pEnvMgr->removeEnvironmentNode(nodeId))
+    {
+        throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "Environment node ID is not valid");
+    }
+
+    pSession->modified = true;
+    pSession->m_pEnvMgr->validate(status, false);
+    addStatusToResponse(status, pSession, resp);
+    return true;
+}
+
+
+bool Cws_config2Ex::onValidateEnvironment(IEspContext &context, IEspValidateEnvironmentRequest &req, IEspStatusResponse &resp)
+{
+    Status status;
+    std::string sessionId = req.getSessionId();
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+    pSession->m_pEnvMgr->validate(status, req.getIncludeHiddenNodes());
+    addStatusToResponse(status, pSession, resp);
+    return true;
+}
+
+
+bool Cws_config2Ex::onSetValues(IEspContext &context, IEspSetValuesRequest &req, IEspStatusResponse &resp)
+{
+    Status status;
+    std::string sessionId = req.getSessionId();
+    std::string key = req.getSessionLockKey();
+    ConfigMgrSession *pSession = getConfigSessionForUpdate(sessionId, key);
+
+    std::string id = req.getNodeId();
+    std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode(id);
+    if (pNode == nullptr)
+    {
+        throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "Environment node ID is not valid");
+    }
+
+    bool forceCreate = req.getForceCreate();
+    bool allowInvalid = req.getAllowInvalid();
+    IArrayOf<IConstAttributeValueType> &attrbuteValues = req.getAttributeValues();
+    std::vector<NameValue> values;
+
+    ForEachItemIn(i, attrbuteValues)
+    {
+        IConstAttributeValueType& attrVal = attrbuteValues.item(i);
+        NameValue value;
+        value.name = attrVal.getName();
+        value.value = attrVal.getValue();
+        values.push_back(value);
+    }
+
+    pNode->setAttributeValues(values, status, allowInvalid, forceCreate);
+    pSession->modified = true;
+    addStatusToResponse(status, pSession, resp);
+    return true;
+}
+
+
+bool Cws_config2Ex::onGetParents(IEspContext &context, IEspNodeRequest &req, IEspGetParentsResponse &resp)
+{
+    std::string nodeId = req.getNodeId();
+    std::string sessionId = req.getSessionId();
+
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+
+    StringArray ids;
+    std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode(nodeId);
+    if (pNode == nullptr)
+    {
+        throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "Environment node ID is not valid");
+    }
+
+    while (pNode)
+    {
+        pNode = pNode->getParent();
+        if (pNode)
+        {
+            ids.append(pNode->getId().c_str());
+        }
+    }
+
+    return true;
+}
+
+
+bool Cws_config2Ex::onGetNodeTree(IEspContext &context, IEspGetTreeRequest &req, IEspGetTreeResponse &resp)
+{
+    std::string nodeId = req.getNodeId();
+    std::string sessionId = req.getSessionId();
+
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+
+    std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode(nodeId);
+    if (pNode)
+    {
+        getNodeTree(pNode, resp.updateTree(), req.getNumLevels(), req.getIncludeAttributes());
+    }
+
+    return true;
+}
+
+
+void Cws_config2Ex::addStatusToResponse(const Status &status, ConfigMgrSession *pSession, IEspStatusResponse &resp) const
+{
+    std::vector<statusMsg> statusMsgs = status.getMessages();
+
+
+    IArrayOf<IEspStatusMsgType> msgs;
+    for (auto msgIt=statusMsgs.begin(); msgIt!=statusMsgs.end(); ++msgIt)
+    {
+        Owned<IEspStatusMsgType> pStatusMsg = createStatusMsgType();
+        pStatusMsg->setNodeId((*msgIt).nodeId.c_str());
+        pStatusMsg->setMsg((*msgIt).msg.c_str());
+        pStatusMsg->setMsgLevel(status.getStatusTypeString((*msgIt).msgLevel).c_str());
+        pStatusMsg->setAttribute((*msgIt).attribute.c_str());
+
+        if (!(*msgIt).nodeId.empty() && pSession != nullptr)
+        {
+            StringArray ids;
+            getNodeParents((*msgIt).nodeId, pSession, ids);
+            std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode((*msgIt).nodeId);
+            pStatusMsg->setNodeName(pNode->getName().c_str());
+            pStatusMsg->setParentIdList(ids);
+        }
+        msgs.append(*pStatusMsg.getLink());
+    }
+
+    resp.updateStatus().setStatus(msgs);
+    resp.updateStatus().setError(status.isError());
+}
+
+
+ConfigMgrSession *Cws_config2Ex::getConfigSession(const std::string &sessionId)
+{
+    ConfigMgrSession *pSession = nullptr;
+
+    if (sessionId.empty())
+        throw MakeStringException(CFGMGR_ERROR_MISSING_SESSION_ID, "Session ID required");
+
+    auto it = m_sessions.find(sessionId);
+    if (it != m_sessions.end())
+    {
+        pSession = (it->second);
+    }
+
+    if (pSession == nullptr)
+        throw MakeStringException(CFGMGR_ERROR_INVALID_SESSION_ID, "Session ID not valid");
+
+    return pSession;
+}
+
+
+ConfigMgrSession *Cws_config2Ex::getConfigSessionForUpdate(const std::string &sessionId, const std::string &lockKey)
+{
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+    if (!pSession->doesKeyFit(lockKey))
+    {
+        throw MakeStringException(CFGMGR_ERROR_LOCK_KEY_INVALID, "Session lock key is missing or invalid");
+    }
+    return pSession;
+}
+
+
+bool Cws_config2Ex::deleteConfigSession(const std::string &sessionId)
+{
+    bool rc = false;
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+    if (pSession)
+    {
+        m_sessions.erase(sessionId);
+        delete pSession;
+        rc = true;
+    }
+    return rc;
+}
+
+
+void Cws_config2Ex::getNodeResponse(const std::shared_ptr<EnvironmentNode> &pNode, IEspGetNodeResponse &resp) const
+{
+    const std::shared_ptr<SchemaItem> &pNodeSchemaItem = pNode->getSchemaItem();
+    std::string nodeDisplayName;
+
+    resp.setNodeId(pNode->getId().c_str());
+
+    //
+    // Fill in base node info struct
+    getNodeInfo(pNode, resp.updateNodeInfo());
+
+    //
+    // Handle the attributes
+    IArrayOf<IEspAttributeType> nodeAttributes;
+    if (pNode->hasAttributes())
+    {
+        std::vector<std::shared_ptr<EnvironmentValue>> attributes;
+        pNode->getAttributes(attributes);
+        getAttributes(attributes, nodeAttributes);
+    }
+    resp.setAttributes(nodeAttributes);
+
+    //
+    // Now the children
+    IArrayOf<IEspNodeType> childNodes;
+    if (pNode->hasChildren())
+    {
+        std::vector<std::shared_ptr<EnvironmentNode>> children;
+        pNode->getChildren(children);
+        for (auto it=children.begin(); it!=children.end(); ++it)
+        {
+            std::shared_ptr<EnvironmentNode> pChildEnvNode = *it;
+            const std::shared_ptr<SchemaItem> pSchemaItem = pChildEnvNode->getSchemaItem();
+            Owned<IEspNodeType> pChildNode = createNodeType();
+            getNodeInfo(pChildEnvNode, pChildNode->updateNodeInfo());
+            pChildNode->setNodeId(pChildEnvNode->getId().c_str());
+            pChildNode->setNumChildren(pChildEnvNode->getNumChildren());
+            childNodes.append(*pChildNode.getLink());
+        }
+    }
+    resp.setChildren(childNodes);
+
+    //
+    // Build a list of items that can be inserted under this node
+    IArrayOf<IEspNodeInfoType> newNodes;
+    std::vector<std::shared_ptr<SchemaItem>> insertableList;
+    pNode->getInsertableItems(insertableList);
+    for (auto it=insertableList.begin(); it!=insertableList.end(); ++it)
+    {
+        std::shared_ptr<SchemaItem> pSchemaItem = *it;
+        Owned<IEspNodeInfoType> pNodeInfo = createNodeInfoType();
+        getNodeInfo(pSchemaItem, *pNodeInfo);
+        pNodeInfo->setRequired(pSchemaItem->isRequired());   // only filled in for insertable items
+        newNodes.append(*pNodeInfo.getLink());
+    }
+    resp.setInsertable(newNodes);
+
+    if (pNodeSchemaItem->isItemValueDefined())
+    {
+        resp.setLocalValueDefined(true);
+
+        const std::shared_ptr<SchemaValue> &pNodeSchemaValue = pNodeSchemaItem->getItemSchemaValue();
+        const std::shared_ptr<SchemaType> &pType = pNodeSchemaValue->getType();
+        resp.updateValue().updateType().setName(pType->getName().c_str());
+
+        if (pType->getLimits()->isMaxSet())
+        {
+            resp.updateValue().updateType().updateLimits().setMaxValid(true);
+            resp.updateValue().updateType().updateLimits().setMax(pType->getLimits()->getMax());
+        }
+        if (pType->getLimits()->isMinSet())
+        {
+            resp.updateValue().updateType().updateLimits().setMinValid(true);
+            resp.updateValue().updateType().updateLimits().setMin(pType->getLimits()->getMin());
+        }
+
+        if (pNode->isLocalValueSet())
+        {
+            const std::shared_ptr<EnvironmentValue> &pLocalValue = pNode->getLocalEnvValue();
+            resp.updateValue().setCurrentValue(pLocalValue->getValue().c_str());
+
+            //
+            // Type information
+            const std::shared_ptr<SchemaValue> pLocalSchemaValue = pLocalValue->getSchemaValue();
+            const std::shared_ptr<SchemaType> &pLocalType = pLocalSchemaValue->getType();
+            std::shared_ptr<SchemaTypeLimits> &pLimits = pLocalType->getLimits();
+            resp.updateValue().updateType().setName(pLocalType->getName().c_str());
+            if (pLocalType->getLimits()->isMaxSet())
+            {
+                resp.updateValue().updateType().updateLimits().setMaxValid(true);
+                resp.updateValue().updateType().updateLimits().setMax(pLocalType->getLimits()->getMax());
+            }
+            if (pLocalType->getLimits()->isMinSet())
+            {
+                resp.updateValue().updateType().updateLimits().setMinValid(true);
+                resp.updateValue().updateType().updateLimits().setMin(pLocalType->getLimits()->getMin());
+            }
+
+            resp.updateValue().setRequired(pLocalSchemaValue->isRequired());
+            resp.updateValue().setReadOnly(pLocalSchemaValue->isReadOnly());
+            resp.updateValue().setHidden(pLocalSchemaValue->isHidden());
+        }
+    }
+}
+
+
+void Cws_config2Ex::getNodeInfo(const std::shared_ptr<EnvironmentNode> &pNode, IEspNodeInfoType &nodeInfo) const
+{
+    const std::shared_ptr<SchemaItem> &pNodeSchemaItem = pNode->getSchemaItem();
+    std::string nodeDisplayName;
+    //
+    // Fill in base node info struct
+    getNodeInfo(pNodeSchemaItem, nodeInfo);      // fill it in based on schema
+    getNodeDisplayName(pNode, nodeDisplayName);  // possibly override the displayname
+    nodeInfo.setName(nodeDisplayName.c_str());
+}
+
+
+void Cws_config2Ex::getNodeInfo(const std::shared_ptr<SchemaItem> &pNodeSchemaItem, IEspNodeInfoType &nodeInfo) const
+{
+    //
+    // Fill in base node info struct
+    nodeInfo.setName(pNodeSchemaItem->getProperty("displayName").c_str());
+    nodeInfo.setNodeType(pNodeSchemaItem->getItemType().c_str());
+    nodeInfo.setClass(pNodeSchemaItem->getProperty("className").c_str());
+    nodeInfo.setCategory(pNodeSchemaItem->getProperty("category").c_str());
+    nodeInfo.setTooltip(pNodeSchemaItem->getProperty("tooltip").c_str());
+    nodeInfo.setHidden(pNodeSchemaItem->isHidden());
+}
+
+
+void Cws_config2Ex::getAttributes(const std::vector<std::shared_ptr<EnvironmentValue>> &attributes, IArrayOf<IEspAttributeType> &nodeAttributes) const
+{
+    for (auto it=attributes.begin(); it!=attributes.end(); ++it)
+    {
+        std::shared_ptr<EnvironmentValue> pAttr = *it;
+        Owned<IEspAttributeType> pAttribute = createAttributeType();
+
+        const std::shared_ptr<SchemaValue> &pSchemaValue = pAttr->getSchemaValue();
+        std::string attributeName = pAttr->getName();
+        pAttribute->setName(attributeName.c_str());
+        pAttribute->setDisplayName(pSchemaValue->getDisplayName().c_str());
+        pAttribute->setTooltip(pSchemaValue->getTooltip().c_str());
+
+        const std::shared_ptr<SchemaType> &pType = pSchemaValue->getType();
+        std::shared_ptr<SchemaTypeLimits> &pLimits = pType->getLimits();
+        pAttribute->updateType().setName(pType->getName().c_str());
+        if (pType->getLimits()->isMaxSet())
+        {
+            pAttribute->updateType().updateLimits().setMaxValid(true);
+            pAttribute->updateType().updateLimits().setMax(pType->getLimits()->getMax());
+        }
+        if (pType->getLimits()->isMinSet())
+        {
+            pAttribute->updateType().updateLimits().setMinValid(true);
+            pAttribute->updateType().updateLimits().setMin(pType->getLimits()->getMin());
+        }
+        pAttribute->setRequired(pSchemaValue->isRequired());
+        pAttribute->setReadOnly(pSchemaValue->isReadOnly());
+        pAttribute->setHidden(pSchemaValue->isHidden());
+
+        std::vector<AllowedValue> allowedValues;
+        pSchemaValue->getAllowedValues(allowedValues, pAttr.get());
+        if (!allowedValues.empty())
+        {
+            IArrayOf<IEspChoiceType> choices;
+            for (auto valueIt=allowedValues.begin(); valueIt!=allowedValues.end(); ++valueIt)
+            {
+                Owned<IEspChoiceType> pChoice = createChoiceType();
+                pChoice->setDisplayName((*valueIt).m_displayName.c_str());
+                pChoice->setValue((*valueIt).m_value.c_str());
+                pChoice->setDesc((*valueIt).m_description.c_str());
+                choices.append(*pChoice.getLink());
+            }
+            pAttribute->updateType().updateLimits().setChoiceList(choices);
+        }
+
+        pAttribute->setCurrentValue(pAttr->getValue().c_str());
+        pAttribute->setDefaultValue(pSchemaValue->getDefaultValue().c_str());
+
+        nodeAttributes.append(*pAttribute.getLink());
+    }
+}
+
+
+void Cws_config2Ex::getNodeDisplayName(const std::shared_ptr<EnvironmentNode> &pNode, std::string &nodeDisplayName) const
+{
+    const std::shared_ptr<SchemaItem> &pNodeSchemaItem = pNode->getSchemaItem();
+    nodeDisplayName = pNodeSchemaItem->getProperty("displayName");
+    if (pNode->hasAttributes())
+    {
+        std::shared_ptr<EnvironmentValue> pAttr = pNode->getAttribute("name");
+        if (pAttr && pAttr->isValueSet())
+        {
+            nodeDisplayName = pAttr->getValue();  // better usability value
+        }
+    }
+}
+
+
+void Cws_config2Ex::getNodeParents(const std::string &nodeId, ConfigMgrSession *pSession, StringArray &parentNodeIds) const
+{
+    std::shared_ptr<EnvironmentNode> pNode = pSession->m_pEnvMgr->getEnvironmentNode(nodeId);
+    if (pNode)
+    {
+        while (pNode)
+        {
+            pNode = pNode->getParent();
+            if (pNode)
+            {
+                parentNodeIds.append(pNode->getId().c_str());
+            }
+        }
+    }
+}
+
+void Cws_config2Ex::getNodeTree(const std::shared_ptr<EnvironmentNode> &pNode, IEspTreeElementType &treeElement, int levels, bool includeAttributes) const
+{
+    //
+    // Fill in this element
+    treeElement.setNodeId(pNode->getId().c_str());
+    getNodeInfo(pNode, treeElement.updateNodeInfo());
+    if (includeAttributes && pNode->hasAttributes())
+    {
+        IArrayOf<IEspAttributeType> nodeAttributes;
+        std::vector<std::shared_ptr<EnvironmentValue>> attributes;
+        pNode->getAttributes(attributes);
+        getAttributes(attributes, nodeAttributes);
+        treeElement.setAttributes(nodeAttributes);
+    }
+
+    //
+    // If we need to descend more levels, do so
+    if (levels > 0)
+    {
+        --levels;
+
+        IArrayOf<IEspTreeElementType> childNodes;
+        if (pNode->hasChildren())
+        {
+            std::vector<std::shared_ptr<EnvironmentNode>> children;
+            pNode->getChildren(children);
+            for (auto it=children.begin(); it!=children.end(); ++it)
+            {
+                Owned<IEspTreeElementType> pTreeElement = createTreeElementType();
+                getNodeTree(*it, *pTreeElement, levels, includeAttributes);
+                childNodes.append(*pTreeElement.getLink());
+            }
+        }
+        treeElement.setChildren(childNodes);
+    }
+}

+ 80 - 0
esp/services/ws_config2/ws_config2Service.hpp

@@ -0,0 +1,80 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 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 _WSCONFIG2_HPP_
+#define _WSCONFIG2_HPP_
+
+#include "ws_config2.hpp"
+#include "ws_config2_esp.ipp"
+#include <string>
+#include "XSDSchemaParser.hpp"
+#include "EnvironmentMgr.hpp"
+#include "XMLEnvironmentMgr.hpp"
+#include "ws_config2Session.hpp"
+
+
+class Status;
+
+class Cws_config2Ex : public Cws_config2
+{
+public:
+    IMPLEMENT_IINTERFACE
+
+    Cws_config2Ex();
+    virtual ~Cws_config2Ex();
+
+    virtual bool onGetNode(IEspContext &context, IEspNodeRequest &req, IEspGetNodeResponse &resp);
+    virtual bool onSetValues(IEspContext &context, IEspSetValuesRequest &req, IEspStatusResponse &resp);
+    virtual bool onGetParents(IEspContext &context, IEspNodeRequest &req, IEspGetParentsResponse &resp);
+    virtual bool onInsertNode(IEspContext &context, IEspInsertNodeRequest &req, IEspGetNodeResponse &resp);
+    virtual bool onRemoveNode(IEspContext &context, IEspRemoveNodeRequest &req, IEspStatusResponse &resp);
+
+    virtual bool onOpenSession(IEspContext &context, IEspOpenSessionRequest &req, IEspOpenSessionResponse &resp);
+    virtual bool onCloseSession(IEspContext &context, IEspCloseSessionRequest &req, IEspEmptyResponse &resp);
+    virtual bool onGetEnvironmentFileList(IEspContext &context, IEspCommonSessionRequest &req, IEspGetEnvironmentListResponse &resp);
+    virtual bool onOpenEnvironmentFile(IEspContext &context, IEspOpenEnvironmentFileRequest &req, IEspOpenEnvironmentFileResponse &resp);
+    virtual bool onCloseEnvironmentFile(IEspContext &context, IEspCloseEnvironmentFileRequest &req, IEspEmptyResponse &resp);
+    virtual bool onSaveEnvironmentFile(IEspContext &context, IEspSaveEnvironmentFileRequest &req, IEspEmptyResponse &resp);
+    virtual bool onLockSession(IEspContext &context, IEspCommonSessionRequest &req, IEspLockSessionResponse &resp);
+    virtual bool onUnlockSession(IEspContext &context, IEspUnlockSessionRequest &req, IEspEmptyResponse &resp);
+    virtual bool onValidateEnvironment(IEspContext &context, IEspValidateEnvironmentRequest &req, IEspStatusResponse &resp);
+    virtual bool onGetOpenSessions(IEspContext &context, IEspListOpenSessionsRequest &req, IEspListOpenSessionsResponse &resp);
+    virtual bool onGetNodeTree(IEspContext &context, IEspGetTreeRequest &req, IEspGetTreeResponse &resp);
+
+
+private:
+
+    void addStatusToResponse(const Status &status, ConfigMgrSession *pSession, IEspStatusResponse &resp) const;
+    ConfigMgrSession *getConfigSession(const std::string &sessionId);
+    ConfigMgrSession *getConfigSessionForUpdate(const std::string &sessionId, const std::string &lockKey);
+    bool deleteConfigSession(const std::string &sessionId);
+    void getNodeResponse(const std::shared_ptr<EnvironmentNode> &pNode, IEspGetNodeResponse &resp) const;
+    void getNodeInfo(const std::shared_ptr<EnvironmentNode> &pNode, IEspNodeInfoType &nodeInfo) const;
+    void getNodeInfo(const std::shared_ptr<SchemaItem> &pNodeSchemaItem, IEspNodeInfoType &nodeInfo) const;
+    void getAttributes(const std::vector<std::shared_ptr<EnvironmentValue>> &attributes, IArrayOf<IEspAttributeType> &nodeAttributes) const;
+    void getNodeDisplayName(const std::shared_ptr<EnvironmentNode> &pNode, std::string &nodeDisplayName) const;
+    void getNodeParents(const std::string &nodeId, ConfigMgrSession *pSession, StringArray &parentNodeIds) const;
+    void getNodeTree(const std::shared_ptr<EnvironmentNode> &pNode, IEspTreeElementType &treeElement, int levels, bool includeAttributes) const;
+
+
+private:
+
+    std::map<std::string, ConfigMgrSession *> m_sessions;
+    unsigned m_sessionKey;
+};
+
+#endif // _WSCONFIG2_HPP_

+ 203 - 0
esp/services/ws_config2/ws_config2Session.hpp

@@ -0,0 +1,203 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 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 _CONFIG2SERVICE_SESSION_HPP_
+#define _CONFIG2SERVICE_SESSION_HPP_
+
+
+#include "EnvironmentMgr.hpp"
+#include "build-config.h"
+#include <iterator>
+#include <algorithm>
+
+
+#define SESSION_KEY_LENGTH 10
+
+struct ConfigMgrSession {
+
+    // NOTE: all paths have NO trailing slash
+    std::string     username;            // owner of the session
+    std::string     schemaPath;          // path to schema files
+    std::string     sourcePath;          // path to environment files
+    std::string     activePath;          // path to active environment file
+    EnvironmentType configType;          // configuration type (XML, JSON, etc.)
+    std::string     masterConfigFile;    // master configuration file for the session
+    std::string     lockKey;             // key for write operations when session is locled
+    std::string     lastMsg;             // last error message
+    std::string     curEnvironmentFile;  // name of currentl loaded envronment file
+    bool            locked;              // true if locked
+    bool            modified;            // true if session has modified loaded environment
+    bool            externallyModified;  // true if another session modified the environment
+    EnvironmentMgr  *m_pEnvMgr;          // ptr to active environment manager for session
+
+
+    ConfigMgrSession() : locked(false), modified(false), m_pEnvMgr(nullptr), configType(UNDEFINED) { }
+    ~ConfigMgrSession()
+    {
+        if (m_pEnvMgr != nullptr)
+        {
+            delete m_pEnvMgr;
+            m_pEnvMgr = nullptr;
+        }
+    }
+
+
+    bool initializeSession(std::vector<std::string> &cfgParms)
+    {
+        bool rc = true;
+        m_pEnvMgr = getEnvironmentMgrInstance(configType);
+        if (m_pEnvMgr)
+        {
+            if (!m_pEnvMgr->loadSchema(schemaPath, masterConfigFile, cfgParms))
+            {
+                rc = false;
+                lastMsg = "Unable to load configuration schema, error = " + m_pEnvMgr->getLastSchemaMessage();
+            }
+        }
+        else
+        {
+            rc = false;
+            lastMsg = "Unrecognized configuration type";
+        }
+        return rc;
+    }
+
+
+    void getEnvironmentFullyQualifiedPath(const std::string &envFile, std::string &fullPath)
+    {
+        fullPath = sourcePath + PATHSEPSTR + envFile;
+    }
+
+
+    bool loadEnvironment(const std::string &envFile)
+    {
+        std::string fullPath;
+        getEnvironmentFullyQualifiedPath(envFile, fullPath);
+        bool rc = true;
+
+        closeEnvironment();
+
+        if (!m_pEnvMgr->loadEnvironment(fullPath))
+        {
+            rc = false;
+            lastMsg = "Unable to load environment file, error = " + m_pEnvMgr->getLastEnvironmentMessage();
+        }
+        else
+        {
+            curEnvironmentFile = envFile;
+        }
+
+        return rc;
+    }
+
+
+    void closeEnvironment()
+    {
+        m_pEnvMgr->discardEnvironment();
+        curEnvironmentFile = "";
+        locked = modified = externallyModified = false;
+        lockKey = "";
+    }
+
+
+    bool saveEnvironment(const std::string &saveAsFilename)
+    {
+        bool rc = false;
+        std::string saveFile = (saveAsFilename != "") ? saveAsFilename : curEnvironmentFile;
+        if (m_pEnvMgr->saveEnvironment(saveFile))
+        {
+            modified = false;
+            curEnvironmentFile = saveFile;
+            rc = true;
+        }
+        else
+        {
+            rc = false;
+            lastMsg = "Unable to save enivronment file, error = " + m_pEnvMgr->getLastEnvironmentMessage();
+        }
+        return rc;
+    }
+
+
+    std::string getEnvironmentFileExtension() const
+    {
+        std::string ext = ".unk";
+        if (configType == XML)
+        {
+            ext = ".xml";
+        }
+        return ext;
+    }
+
+
+    bool lock()
+    {
+        bool rc = true;
+        if (!locked)
+        {
+            lockKey = generateRandomString(SESSION_KEY_LENGTH);
+            locked = true;
+        }
+        else
+        {
+            rc = false;
+        }
+        return rc;
+    }
+
+
+    bool unlock(const std::string &key)
+    {
+        bool rc = true;
+        if (locked)
+        {
+            locked = !(lockKey == key);
+            rc = !locked;
+        }
+        return rc;
+    }
+
+
+    bool doesKeyFit(const std::string &key)
+    {
+        // must be locked and the key must match
+        return locked && (lockKey == key);
+    }
+
+
+    const std::string &getLastMsg()
+    {
+        return lastMsg;
+    }
+
+
+    std::string generateRandomString(size_t length)
+    {
+        const char* charmap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        const size_t charmapLength = strlen(charmap);
+        auto generator = [&](){ return charmap[rand()%charmapLength]; };
+        std::string result;
+        result.reserve(length);
+        std::generate_n(std::back_inserter(result), length, generator);
+        return result;
+    }
+
+};
+
+
+#endif

+ 30 - 0
esp/services/ws_config2/ws_config2_binding.hpp

@@ -0,0 +1,30 @@
+/*##############################################################################
+
+    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 "ws_config2Service.hpp"
+
+
+class Cws_config2SoapBindingEx : public Cws_config2SoapBinding
+{
+public:
+    Cws_config2SoapBindingEx(http_soap_log_level level=hsl_none) : Cws_config2SoapBinding(level)  { }
+    Cws_config2SoapBindingEx(IPropertyTree* cfg, const char *bindname, const char *procname, http_soap_log_level level=hsl_none) : Cws_config2SoapBinding(cfg, bindname, procname, level)   {  }
+    //virtual const char* getRootPage(IEspContext* ctx)  {  return "config2html";  }
+
+
+private:
+};

+ 73 - 0
esp/services/ws_config2/ws_config2_plugin.cpp

@@ -0,0 +1,73 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2017 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.
+############################################################################## */
+
+#pragma warning (disable : 4786)
+
+#include "ws_config2_esp.ipp"
+
+//ESP Bindings
+#include "http/platform/httpprot.hpp"
+
+//ESP Service
+#include "ws_config2Service.hpp"
+#include "ws_config2_binding.hpp"
+
+#include "espplugin.hpp"
+
+extern "C"
+{
+
+ESP_FACTORY IEspService * esp_service_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process)
+{
+   if (strcmp(type, "ws_config2")==0)
+   {
+      Cws_config2Ex* service = new Cws_config2Ex;
+      service->init(cfg, process, name);
+      return service;
+   }
+   return NULL;
+}
+
+ESP_FACTORY IEspRpcBinding * esp_binding_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process)
+{
+   if (strcmp(type, "ws_config2SoapBinding")==0)
+   {
+        return new Cws_config2SoapBindingEx(cfg, name, process);
+   }
+
+   return NULL;
+}
+
+ESP_FACTORY IEspProtocol * esp_protocol_factory(const char *name, const char* type, IPropertyTree *cfg, const char *process)
+{
+    if (strcmp(type, "http_protocol")==0)
+    {
+        return new CHttpProtocol;
+    }
+    else if(strcmp(type, "secure_http_protocol") == 0)
+    {
+        IPropertyTree *sslSettings;
+        sslSettings = cfg->getPropTree(StringBuffer("Software/EspProcess[@name=\"").append(process).append("\"]").append("/EspProtocol[@name=\"").append(name).append("\"]").str());
+        if(sslSettings != NULL)
+        {
+            return new CSecureHttpProtocol(sslSettings);
+        }
+    }
+    return NULL;
+}
+
+} // extern "C"

+ 1 - 0
initfiles/componentfiles/CMakeLists.txt

@@ -16,5 +16,6 @@
 
 ADD_SUBDIRECTORY(ftslave)
 ADD_SUBDIRECTORY(configxml)
+ADD_SUBDIRECTORY(config2xml)
 ADD_SUBDIRECTORY(thor)
 ADD_SUBDIRECTORY(launcher)

+ 31 - 0
initfiles/componentfiles/config2xml/CMakeLists.txt

@@ -0,0 +1,31 @@
+################################################################################
+#    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.
+################################################################################
+
+
+FOREACH( iFILES
+    ${CMAKE_CURRENT_SOURCE_DIR}/esp.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/esp_service_smc.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/esp_service_wsecl2.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/hardware.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/types.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/dafilesrv.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/dali.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/environment.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/eclagent.xsd
+    ${CMAKE_CURRENT_SOURCE_DIR}/buildset.xml
+)
+    Install ( FILES ${iFILES} DESTINATION componentfiles/config2xml COMPONENT Runtime)
+ENDFOREACH ( iFILES )

+ 2 - 2
configuration/config2/configfiles/buildset.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 ################################################################################
-#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
+#    HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License");
 #    you may not use this file except in compliance with the License.
@@ -133,4 +133,4 @@
       </BuildSet>
     </Build>
   </Programs>
-</Environment>
+</Environment>

+ 1 - 1
configuration/config2/configfiles/dafilesrv.xsd

@@ -3,7 +3,7 @@
     xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
     xmlns:hpcc="someuri">
     <xs:include schemaLocation="types.xsd"/>
-    <xs:complexType name="dafilesrv" hpcc:class="component" hpcc:category="espporocess" hpcc:displayName="Da File serve Process">
+    <xs:complexType name="dafilesrv" hpcc:class="component" hpcc:category="espporocess" hpcc:componentName="dafilesrv" hpcc:displayName="Da File serve Process">
         <xs:attributeGroup name="executionSettings">
             <xs:attribute name="parallelRequestLimit" type="percent" use="optional" default="20" hpcc:tooltip="Defines the maximum number of concurrent dafilesrv requests allowed. Requests that exceed the limit will be delayed. A value of 0 disables throttling. Overrides global settting."/>
             <xs:attribute name="throttleDelayMs" type="xs:nonNegativeInteger" use="optional" default="5000" hpcc:tooltip="Defines how many milliseconds delayed requests will be delayed by. Overrides global settting."/>

configuration/config2/configfiles/dali.xsd → initfiles/componentfiles/config2xml/dali.xsd


+ 13 - 13
configuration/config2/configfiles/eclagent.xsd

@@ -3,19 +3,19 @@
     xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
     xmlns:hpcc="someuri">
     <xs:include schemaLocation="types.xsd"/>
-    <xs:attributeGroup name="Options" hpcc:displayName="Options" hpcc:tooltip="Options" hpcc:docid="EA.t2">
-        <xs:attribute name="allowedPipePrograms" type="xs:string" use="optional" default="*" hpcc:tab="Eclagent" hpcc:tooltip="Comma separated list of allowed PIPE programs (* for allow all)"/>
-        <xs:attribute name="daliServers" type="xs:string" use="required" hpcc:autogenforwizard="true" hpcc:tooltip="Specifies the dali server to which this eclagent is attached"/>
-        <xs:attribute name="defaultMemoryLimitMB" type="xs:nonNegativeInteger" use="optional" default="300" hpcc:tooltip="Default memory limit in MB for eclagent"/>
-        <xs:attribute name="heapUseHugePages" type="xs:boolean" default="false" hpcc:tooltip="Use memory from huge pages if they have been configured"/>
-        <xs:attribute name="heapUseTransparentHugePages" type="xs:boolean" default="true" hpcc:tooltip="Use memory from transparent huge pages"/>
-        <xs:attribute name="heapRetainMemory" type="xs:boolean" default="false" hpcc:tooltip="Retain and do not return unused memory to the operating system"/>
-        <xs:attribute name="pluginDirectory" type="absolutePath" use="optional" default="/opt/HPCCSystems/plugins/" hpcc:tooltip="Directory where plugins are located"/>
-        <xs:attribute name="traceLevel" type="xs:nonNegativeInteger" use="optional" default="0" hpcc:tooltip="Trace level"/>
-        <xs:attribute name="thorConnectTimeout" type="xs:nonNegativeInteger" use="optional" default="600" hpcc:tooltip="Default connection timeout when sending query to Thor"/>
-        <xs:attribute name="wuQueueName" type="xs:string" use="optional" default="" hpcc:tab="Eclagent" hpcc:tooltip="eclAgent Workunit Execution Queue Name" hpcc:autogenforwizard="true" hpcc:autogensuffix="_queue"/>
-    </xs:attributeGroup>
-    <xs:complexType name="eclAgent" hpcc:class="component" hpcc:category="agentprocess" hpcc:displayName="ECL Agent Process">
+    <xs:complexType name="eclAgent" hpcc:class="component" hpcc:category="agentprocess" hpcc:componentName="eclagent" hpcc:displayName="ECL Agent Process">
+         <xs:attributeGroup name="Options" hpcc:displayName="Options" hpcc:tooltip="Options" hpcc:docid="EA.t2">
+            <xs:attribute name="allowedPipePrograms" type="xs:string" use="optional" default="*" hpcc:tab="Eclagent" hpcc:tooltip="Comma separated list of allowed PIPE programs (* for allow all)"/>
+            <xs:attribute name="daliServers" type="xs:string" use="required" hpcc:autogenforwizard="true" hpcc:tooltip="Specifies the dali server to which this eclagent is attached"/>
+            <xs:attribute name="defaultMemoryLimitMB" type="xs:nonNegativeInteger" use="optional" default="300" hpcc:tooltip="Default memory limit in MB for eclagent"/>
+            <xs:attribute name="heapUseHugePages" type="xs:boolean" default="false" hpcc:tooltip="Use memory from huge pages if they have been configured"/>
+            <xs:attribute name="heapUseTransparentHugePages" type="xs:boolean" default="true" hpcc:tooltip="Use memory from transparent huge pages"/>
+            <xs:attribute name="heapRetainMemory" type="xs:boolean" default="false" hpcc:tooltip="Retain and do not return unused memory to the operating system"/>
+            <xs:attribute name="pluginDirectory" type="absolutePath" use="optional" default="/opt/HPCCSystems/plugins/" hpcc:tooltip="Directory where plugins are located"/>
+            <xs:attribute name="traceLevel" type="xs:nonNegativeInteger" use="optional" default="0" hpcc:tooltip="Trace level"/>
+            <xs:attribute name="thorConnectTimeout" type="xs:nonNegativeInteger" use="optional" default="600" hpcc:tooltip="Default connection timeout when sending query to Thor"/>
+            <xs:attribute name="wuQueueName" type="xs:string" use="optional" default="" hpcc:tab="Eclagent" hpcc:tooltip="eclAgent Workunit Execution Queue Name" hpcc:autogenforwizard="true" hpcc:autogensuffix="_queue"/>
+        </xs:attributeGroup>
         <xs:sequence>
             <xs:element name="EclAgentProcess" hpcc:docid="EA.t1">
                 <xs:complexType>

configuration/config2/configfiles/newenv.xsd → initfiles/componentfiles/config2xml/environment.xsd


+ 1 - 1
configuration/config2/configfiles/esp.xsd

@@ -3,7 +3,7 @@
     xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
     xmlns:hpcc="someuri">
     <xs:include schemaLocation="types.xsd"/>
-    <xs:complexType name="espProcess" hpcc:class="component" hpcc:category="espporocess" hpcc:componentName="esp" hpcc:displayName="ESP Process">
+    <xs:complexType name="espProcess" hpcc:class="component" hpcc:category="ESP Process" hpcc:componentName="esp" hpcc:displayName="ESP Process">
         <xs:sequence>
             <xs:element name="EspProcess" hpcc:docid="ESP.t6" maxOccurs="unbounded">
                 <xs:annotation>

configuration/config2/configfiles/esp_service_smc.xsd → initfiles/componentfiles/config2xml/esp_service_smc.xsd


configuration/config2/configfiles/esp_service_wsecl2.xsd → initfiles/componentfiles/config2xml/esp_service_wsecl2.xsd


configuration/config2/configfiles/hardware.xsd → initfiles/componentfiles/config2xml/hardware.xsd


configuration/config2/configfiles/types.xsd → initfiles/componentfiles/config2xml/types.xsd


+ 1 - 0
initfiles/etc/DIR_NAME/CMakeLists.txt

@@ -29,6 +29,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/environment.conf DESTINATION etc/${DIR
 install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/genenvrules.conf DESTINATION etc/${DIR_NAME}/rpmnew COMPONENT Runtime)
 
 add_subdirectory(configmgr)
+add_subdirectory(config2mgr)
 
 # Developer build
 # target: configure

+ 10 - 12
configuration/config2/configfiles/hpcc.xsd

@@ -1,9 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
 ################################################################################
 #    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
 #
-#    Licensed under the Apache License, Version 2.0 (the "License");
+#    All rights reserved. This program is free software: you can redistribute it
+#    and/or modify
 #    you may not use this file except in compliance with the License.
 #    You may obtain a copy of the License at
 #
@@ -15,12 +14,11 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 ################################################################################
--->
-<xs:schema
-    xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
-
-    <xs:attribute name="readOnly" type="xs:boolean" default="false"/>
-    <xs:attribute name="docid" type="xs:string" default=""/>
-    <xs:attribute name="hidden" type="xs:boolean" default="false"/>
-    <xs:attribute name="tooltip" type="xs:string" default=""/>
-</xs:schema>
+configure_file("config2mgr.conf.in" "config2mgr.conf")
+configure_file("esp.xml.in" "esp.xml")
+install(
+    FILES ${CMAKE_CURRENT_BINARY_DIR}/esp.xml
+          ${CMAKE_CURRENT_BINARY_DIR}/config2mgr.conf
+    DESTINATION etc/${DIR_NAME}/config2mgr
+    COMPONENT Runtime
+    )

+ 8 - 0
initfiles/etc/DIR_NAME/config2mgr/config2mgr.conf.in

@@ -0,0 +1,8 @@
+## Default configuration file for config2
+
+[components]
+configmgr=esp;${CONFIG_DIR}/config2
+brokenconntimeout=60
+buildset=buildset.xml
+wizardalgorithm=genenvrules.conf
+autodetectipscript=${ADMIN_PATH}/get_ip_address_wiz.sh

File diff suppressed because it is too large
+ 32 - 0
initfiles/etc/DIR_NAME/config2mgr/esp.xml.in


+ 1 - 0
initfiles/sbin/CMakeLists.txt

@@ -21,6 +21,7 @@ GENERATE_BASH(processor ${bash-vars} "keygen.sh.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "add_conf_settings.sh.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "rm_conf_settings.sh.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "configmgr.in" outFiles)
+GENERATE_BASH(processor ${bash-vars} "config2mgr.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "install-cluster.sh.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "hpcc-push.sh.in" outFiles)
 GENERATE_BASH(processor ${bash-vars} "hpcc-run.sh.in" outFiles)

+ 191 - 0
initfiles/sbin/config2mgr.in

@@ -0,0 +1,191 @@
+#!/bin/bash
+################################################################################
+#    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.
+################################################################################
+
+#------------------------------------------------------------------------------
+#                       Common Function
+#------------------------------------------------------------------------------
+
+###<REPLACE>###
+
+DEBUG=${DEBUG:-NO_DEBUG}
+
+source ${INSTALL_DIR}/etc/init.d/hpcc_common
+
+createConf ()
+{
+    dir_name=$(basename ${INSTALL_DIR})
+    awk -f ${reg_path}/regex.awk -v NEW_ENVFILE=$1 -v NEW_PORT=$2 -v NEW_CONFFILE=$3< ${path}/etc/${dir_name}/config2mgr/esp.xml >${runtime}/${compName}/esp.xml
+}
+
+
+cleanup ()
+{
+    echo "Exiting config2mgr"
+    PIDPATH=${pid}/${compName}_init.pid
+    stopcmd="${START_STOP_DAEMON} -K -p $PIDPATH"
+    eval $stopcmd
+    sleep 2
+    cleanupRuntimeEnvironment
+    exit
+}
+
+
+control_c ()
+{
+    echo "Ctrl-c was hit. Exiting the process"
+    cleanup
+    exit
+}
+
+print_usage ()
+{
+    echo >&2 "Usage : $0 <full path for env file> <port> <full path for conf file>
+              or $0 (will use default files and default port number)"
+    exit 0
+}
+
+set_environmentvars
+
+exec_script_path=${path}/bin
+reg_path=${path}/sbin
+
+compName=config2mgr
+compPath=${runtime}/${compName}
+
+[[ ! -w "${CONFIG_DIR}" ]] && is_root
+
+# Trapping keyboard interrupt  control-c
+trap control_c SIGINT
+
+# Setting up Environment variables
+export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${path}/lib
+export PATH=${PATH}:${runtime}/bin:${path}/sbin
+
+# Creating runtime environment for config2mgr
+createRuntime
+
+logFile=${log}/${compName}/${compName}.log
+
+initPidFile=${pid}/${compName}_init.pid
+compPidFile=${pid}/${compName}.pid
+lockFile=${lock}/${compName}/${compName}.lock
+defaultEnv=0
+
+# Checking input arguments
+if [ $# -eq 3 ]; then
+    filename=$1
+    portnum=$2
+    conffile=$3
+    echo "<filename> = ${filename}"
+    echo "<portnumber> = ${portnum}"
+    echo "<LocalEnvConfFile> = ${conffile}"
+    #checking where files exists or not
+    if [ ! -e ${filename} ] || [ ! -e ${conffile} ]
+    then
+       echo "File ${filename} or ${conffile} does not exists"
+       exit
+    fi
+elif [ $# -eq 0 ]; then
+    defaultEnv=1
+    filename="${sourcedir}/${environment}"
+    portnum="8020"
+    conffile="${configs}/environment.conf"
+    echo "Using default filename ${sourcedir}/${environment} and default port \"8020\""
+    if [ ! -d ${sourcedir} ];
+    then
+       #creating source directory if not present
+       createDir ${sourcedir}
+       cp ${configs}/${environment} ${sourcedir} > /dev/null 2<&1
+       chmod 755 ${sourcedir}/${environment}
+       chown -R ${user}:${group} ${sourcedir}
+    else
+        if [ ! -e ${filename} ];
+        then
+            if [ ${DEBUG:-NO_DEBUG} != "NO_DEBUG" ]; then
+                echo "Default environment file does not exists, copying from ${configs}"
+            fi
+            cp ${configs}/${environment} ${sourcedir}/environment.xml > /dev/null 2<&1
+            chmod 755 ${sourcedir}/environment.xml
+            chown -R ${user}:${group} ${sourcedir}
+        fi
+    fi
+else
+    print_usage
+fi
+
+createConf "${filename}" "${portnum}" "${conffile}"
+chown ${user}:${group} ${conffile}
+
+# Sanity Check for previous instances of config2. This code also takes care if configmgr script/configesp is not running and it killed by kill command
+
+check_status ${initPidFile} ${lockFile} ${compPidFile} 0
+RCSTAT=$?
+
+if [ ${RCSTAT} -eq 0 ]; then
+    checkPid ${initPidFile}
+    echo "Config2mgr is already running with Pid $__pidValue"
+    exit 0
+else
+    cleanupRuntimeEnvironment
+fi
+
+#-----------------------------------------------------------
+# Actual Processing begins
+#-----------------------------------------------------------
+cd ${runtime}/${compName}
+
+#start configesp
+EXEC_COMMAND="${exec_script_path}/init_configesp >> $logFile 2>&1"
+startcmd="${START_STOP_DAEMON} -S -p ${initPidFile} -c ${user}:${group} -d ${runtime}/${compName} -m -x ${EXEC_COMMAND} -b"
+eval ${startcmd}
+started=$?
+
+# Creating a Lock
+lock $lockFile
+
+if [ ${DEBUG:-NO_DEBUG} != "NO_DEBUG" ]; then
+    echo $startcmd
+    echo "configesp status = $started"
+fi
+
+echo -n "Verifying config2 startup ..."
+sleep 5
+
+check_status ${initPidFile} ${lockFile} ${compPidFile} 1
+RCSTAT=$?
+
+if [ ${RCSTAT} -ne 0 ];then
+   echo "Failure"
+   cleanup
+else
+    echo " Success"
+    echo "Exit by pressing ctrl-c..."
+    while :
+        check_status ${initPidFile} ${lockFile} ${compPidFile} 1
+        if [ $? -ne 0 ] ;
+        then
+            if [ "${DEBUG}" != "NO_DEBUG" ];
+            then
+                 echo "Init_configesp/Configesp is not running, hence shutting down configmgr script"
+            fi
+            kill -2 $$ > /dev/null 2>&1
+        fi
+        trap control_c SIGINT
+    do
+        sleep 1
+    done
+fi

+ 2 - 0
system/include/errorlist.h

@@ -103,6 +103,8 @@
 #define ECLWATCH_ERROR_START    20000
 #define ECLWATCH_ERROR_END      29999
 
+#define CONFIG_MGR_ERROR_START  30000
+#define CONFIG_MGR_ERROR_END    30099
 
 #endif