Browse Source

added isc::config::ConfigData class for easier client-side usage of configuration values (not completely done yet)

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@915 e5f2f494-b856-4b98-b285-d166d9295462
Jelte Jansen 15 years ago
parent
commit
b5d2ddbea1

+ 2 - 2
src/lib/config/cpp/Makefile.am

@@ -1,14 +1,14 @@
 AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/ext -Wall -Werror
 
 lib_LIBRARIES = libcfgclient.a
-libcfgclient_a_SOURCES = module_spec.h module_spec.cc ccsession.cc ccsession.h
+libcfgclient_a_SOURCES = config_data.h config_data.cc module_spec.h module_spec.cc ccsession.cc ccsession.h
 
 CLEANFILES = *.gcno *.gcda
 
 TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
-run_unittests_SOURCES = module_spec_unittests.cc run_unittests.cc
+run_unittests_SOURCES = module_spec_unittests.cc config_data_unittests.cc run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD = libcfgclient.a $(GTEST_LDADD)

+ 4 - 2
src/lib/config/cpp/ccsession.cc

@@ -135,7 +135,7 @@ ModuleCCSession::ModuleCCSession(std::string spec_file_name,
     read_module_specification(spec_file_name);
     sleep(1);
 
-    module_name_ = module_specification_.getFullSpec()->get("module_spec")->get("module_name")->stringValue();
+    module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
     config_handler_ = config_handler;
     command_handler_ = command_handler;
 
@@ -150,7 +150,9 @@ ModuleCCSession::ModuleCCSession(std::string spec_file_name,
     //session_.subscribe("Boss", "*");
     //session_.subscribe("statistics", "*");
     // send the data specification
-    session_.group_sendmsg(module_specification_.getFullSpec(), "ConfigManager");
+    ElementPtr spec_msg = Element::createFromString("{}");
+    spec_msg->set("module_spec", module_specification_.getFullSpec());
+    session_.group_sendmsg(spec_msg, "ConfigManager");
     session_.group_recvmsg(env, answer, false);
     
     // get any stored configuration from the manager

+ 5 - 1
src/lib/config/cpp/ccsession.h

@@ -37,7 +37,11 @@ public:
         isc::Exception(file, line, what) {}
 };
 
-
+///
+/// \brief This modules keeps a connection to the command channel,
+/// holds configuration information, and handles messages from
+/// the command channel
+///
 class ModuleCCSession {
 public:
     /**

+ 180 - 0
src/lib/config/cpp/config_data.cc

@@ -0,0 +1,180 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include "config_data.h"
+
+#include <boost/foreach.hpp>
+
+#include <string>
+#include <iostream>
+
+using namespace isc::data;
+
+namespace isc {
+namespace config {
+
+ElementPtr
+find_spec_part(ElementPtr spec, const std::string& identifier)
+{
+    //std::cout << "[XX] find_spec_part" << std::endl;
+    if (!spec) { return ElementPtr(); }
+    //std::cout << "in: " << spec << std::endl;
+    ElementPtr spec_part = spec;
+    if (identifier == "") {
+        //std::cout << "[XX] empty id" << std::endl;
+        return ElementPtr();
+    }
+    std::string id = identifier;
+    size_t sep = id.find('/');
+    while(sep != std::string::npos) {
+        std::string part = id.substr(0, sep);
+        //std::cout << "[XX] id part: " << part << std::endl;
+        if (spec_part->getType() == Element::list) {
+            bool found = false;
+            BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+                if (list_el->getType() == Element::map &&
+                    list_el->contains("item_name") &&
+                    list_el->get("item_name")->stringValue() == part) {
+                    spec_part = list_el->get("item_name");
+                    found = true;
+                }
+            }
+            if (!found) {
+                // raise exception?
+                return ElementPtr();
+            }
+        } else if (spec_part->getType() == Element::map) {
+            if (spec_part->contains("map_item_spec")) {
+                bool found = false;
+                BOOST_FOREACH(ElementPtr list_el, spec_part->get("map_item_spec")->listValue()) {
+                    if (list_el->getType() == Element::map &&
+                        list_el->contains("item_name") &&
+                        list_el->get("item_name")->stringValue() == part) {
+                        spec_part = list_el->get("item_name");
+                        found = true;
+                    }
+                }
+                if (!found) {
+                    // raise exception?
+                    return ElementPtr();
+                }
+            }
+        }
+        id = id.substr(sep + 1);
+    }
+    if (id != "" && id != "/") {
+        if (spec_part->getType() == Element::list) {
+            bool found = false;
+            BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+                if (list_el->getType() == Element::map &&
+                    list_el->contains("item_name") &&
+                    list_el->get("item_name")->stringValue() == id) {
+                    spec_part = list_el;
+                    found = true;
+                }
+            }
+            if (!found) {
+                // raise exception?
+                return ElementPtr();
+            }
+        } else if (spec_part->getType() == Element::map) {
+            if (spec_part->contains("map_item_spec")) {
+                bool found = false;
+                BOOST_FOREACH(ElementPtr list_el, spec_part->get("map_item_spec")->listValue()) {
+                    if (list_el->getType() == Element::map &&
+                        list_el->contains("item_name") &&
+                        list_el->get("item_name")->stringValue() == id) {
+                        spec_part = list_el;
+                        found = true;
+                    }
+                }
+                if (!found) {
+                    // raise exception?
+                    return ElementPtr();
+                }
+            }
+        }
+    }
+    return spec_part;
+}
+
+ElementPtr
+ConfigData::getValue(const std::string& identifier)
+{
+    bool fake;
+    return getValue(fake, identifier);
+}
+
+ElementPtr
+ConfigData::getValue(bool& is_default, const std::string& identifier)
+{
+    ElementPtr value = _config->find(identifier);
+    if (!value) {
+        ElementPtr spec_part = find_spec_part(_module_spec.getConfigSpec(), identifier);
+        if (spec_part) {
+            value = spec_part->get("item_default");
+            is_default = true;
+        } else {
+            // we should raise an error here
+            dns_throw(DataNotFoundError, "identifier not found");
+        }
+    } else {
+        is_default = false;
+    }
+    return value;
+}
+
+void
+spec_name_list(ElementPtr result, ElementPtr spec_part, std::string prefix, bool recurse = false)
+{
+    if (spec_part->getType() == Element::list) {
+        BOOST_FOREACH(ElementPtr list_el, spec_part->listValue()) {
+            if (list_el->getType() == Element::map &&
+                list_el->contains("item_name")) {
+                    result->add(Element::create(prefix + "/" + list_el->get("item_name")->stringValue()));
+            }
+        }
+    } else if (spec_part->getType() == Element::map &&
+               spec_part->contains("map_item_spec")
+    ) {
+        if (recurse) {
+            spec_name_list(result, spec_part->get("map_item_spec"), prefix + "/" + spec_part->get("item_name")->stringValue(), recurse);
+        } else {
+            result->add(Element::create(prefix + "/" + spec_part->get("item_name")->stringValue()));
+        }
+    }
+}
+
+ElementPtr
+ConfigData::getItemList(const std::string& identifier, bool recurse)
+{
+    ElementPtr result = Element::createFromString("[]");
+    ElementPtr spec_part = getModuleSpec().getConfigSpec();
+    if (identifier != "" && identifier != "/") {
+        spec_part = find_spec_part(spec_part, identifier);
+    }
+    spec_name_list(result, spec_part, identifier, recurse);
+    return result;
+}
+
+ElementPtr
+ConfigData::getFullConfig()
+{
+    return ElementPtr();
+}
+
+}
+}

+ 56 - 0
src/lib/config/cpp/config_data.h

@@ -0,0 +1,56 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __CONFIG_DATA_H
+#define __CONFIG_DATA_H 1
+
+#include <string>
+#include <vector>
+
+#include <config/module_spec.h>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace config {
+
+class DataNotFoundError : public isc::Exception {
+public:
+    DataNotFoundError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+    
+class ConfigData {
+public:
+    ConfigData() { _config = Element::createFromString("{}"); };
+    ConfigData(const ModuleSpec& module_spec) : _module_spec(module_spec) { _config = Element::createFromString("{}"); }
+
+    ElementPtr getValue(const std::string& identifier);
+    ElementPtr getValue(bool &is_default, const std::string& identifier);
+    const ModuleSpec getModuleSpec() { return _module_spec; };
+    void setModuleSpec(ModuleSpec module_spec) { _module_spec = module_spec; };
+    void setLocalConfig(ElementPtr config) { _config = config; }
+    ElementPtr getLocalConfig() { return _config; }
+    ElementPtr getItemList(const std::string& identifier, bool recurse = false);
+    ElementPtr getFullConfig();
+
+private:
+    ElementPtr _config;
+    ModuleSpec _module_spec;
+};
+
+}
+}
+#endif

+ 103 - 0
src/lib/config/cpp/config_data_unittests.cc

@@ -0,0 +1,103 @@
+
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <gtest/gtest.h>
+
+#include "config_data.h"
+#include "data_def_unittests_config.h"
+
+#include <iostream>
+
+using namespace isc::data;
+using namespace isc::config;
+
+ConfigData
+setupSpec2()
+{
+    ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
+    return ConfigData(spec2);
+}
+
+TEST(ConfigData, Creation) {
+    ConfigData cd = setupSpec2();
+    EXPECT_TRUE(true);
+}
+
+TEST(ConfigData, getValue) {
+    ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
+    ConfigData cd = ConfigData(spec2);
+    //std::cout << "[XX] SPEC2: " << cd.getModuleSpec().getFullSpec() << std::endl;
+    bool is_default;
+    //ElementPtr value = cd.getValue(is_default, "item1");
+    EXPECT_EQ(1, cd.getValue(is_default, "item1")->intValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_EQ(1.1, cd.getValue(is_default, "item2")->doubleValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_TRUE(cd.getValue(is_default, "item3")->boolValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_EQ("test", cd.getValue(is_default, "item4")->stringValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_EQ("a", cd.getValue(is_default, "item5")->get(0)->stringValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_EQ("b", cd.getValue(is_default, "item5")->get(1)->stringValue());
+    EXPECT_TRUE(is_default);
+    EXPECT_EQ("{}", cd.getValue(is_default, "item6")->str());
+    EXPECT_TRUE(is_default);
+}
+
+TEST(ConfigData, setLocalConfig) {
+    ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
+    ConfigData cd = ConfigData(spec2);
+    bool is_default;
+
+    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+
+    EXPECT_EQ("{}", cd.getValue("item6")->str());
+
+    cd.setLocalConfig(my_config);
+    EXPECT_EQ(2, cd.getValue(is_default, "item1")->intValue());
+    EXPECT_FALSE(is_default);
+    EXPECT_EQ("{}", cd.getValue("item6")->str());
+    EXPECT_EQ(1.1, cd.getValue(is_default, "item2")->doubleValue());
+    EXPECT_TRUE(is_default);
+
+    cd.setLocalConfig(my_config2);
+    EXPECT_EQ("{\"value1\": \"a\"}", cd.getValue("item6")->str());
+}
+
+TEST(ConfigData, getLocalConfig) {
+    ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
+    ConfigData cd = ConfigData(spec2);
+    EXPECT_EQ("{}", cd.getLocalConfig()->str());
+    
+    ElementPtr my_config = Element::createFromString("{\"item1\": 2}");
+    cd.setLocalConfig(my_config);
+    EXPECT_EQ("{\"item1\": 2}", cd.getLocalConfig()->str());
+
+    ElementPtr my_config2 = Element::createFromString("{\"item6\": { \"value1\": \"a\" } }");
+    cd.setLocalConfig(my_config2);
+    EXPECT_EQ("{\"item6\": {\"value1\": \"a\"}}", cd.getLocalConfig()->str());
+}
+
+TEST(ConfigData, getItemList) {
+    ModuleSpec spec2 = moduleSpecFromFile(std::string(TEST_DATA_PATH) + "/spec2.spec");
+    ConfigData cd = ConfigData(spec2);
+
+    //EXPECT_EQ("", cd.getItemList("")->str());
+}
+

+ 16 - 12
src/lib/config/cpp/module_spec.cc

@@ -160,11 +160,7 @@ check_data_specification(const ElementPtr& spec) {
 static void
 check_module_specification(const ElementPtr& def)
 {
-    if (!def->contains("module_spec")) {
-        throw ModuleSpecError("Data specification does not contain module_spec element");
-    } else {
-        check_data_specification(def->get("module_spec"));
-    }
+    check_data_specification(def);
 }
 
 //
@@ -183,7 +179,7 @@ ModuleSpec::ModuleSpec(ElementPtr module_spec_element,
 }
 
 const ElementPtr
-ModuleSpec::getCommandsSpec()
+ModuleSpec::getCommandsSpec() const
 {
     if (module_specification->contains("commands")) {
         return module_specification->get("commands");
@@ -193,7 +189,7 @@ ModuleSpec::getCommandsSpec()
 }
 
 const ElementPtr
-ModuleSpec::getConfigSpec()
+ModuleSpec::getConfigSpec() const
 {
     if (module_specification->contains("config_data")) {
         return module_specification->get("config_data");
@@ -203,7 +199,7 @@ ModuleSpec::getConfigSpec()
 }
 
 const std::string
-ModuleSpec::getModuleName()
+ModuleSpec::getModuleName() const
 {
     return module_specification->get("module_name")->stringValue();
 }
@@ -211,14 +207,14 @@ ModuleSpec::getModuleName()
 bool
 ModuleSpec::validate_config(const ElementPtr data, const bool full)
 {
-    ElementPtr spec = module_specification->find("module_spec/config_data");
+    ElementPtr spec = module_specification->find("config_data");
     return validate_spec_list(spec, data, full, ElementPtr());
 }
 
 bool
 ModuleSpec::validate_config(const ElementPtr data, const bool full, ElementPtr errors)
 {
-    ElementPtr spec = module_specification->find("module_spec/config_data");
+    ElementPtr spec = module_specification->find("config_data");
     return validate_spec_list(spec, data, full, errors);
 }
 
@@ -236,14 +232,22 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
     }
 
     ElementPtr module_spec_element = Element::createFromString(file, file_name);
-    return ModuleSpec(module_spec_element, check);
+    if (module_spec_element->contains("module_spec")) {
+        return ModuleSpec(module_spec_element->get("module_spec"), check);
+    } else {
+        throw ModuleSpecError("No module_spec in specification");
+    }
 }
 
 ModuleSpec
 moduleSpecFromFile(std::ifstream& in, const bool check)
                    throw(ParseError, ModuleSpecError) {
     ElementPtr module_spec_element = Element::createFromString(in);
-    return ModuleSpec(module_spec_element, check);
+    if (module_spec_element->contains("module_spec")) {
+        return ModuleSpec(module_spec_element->get("module_spec"), check);
+    } else {
+        throw ModuleSpecError("No module_spec in specification");
+    }
 }
 
 

+ 4 - 4
src/lib/config/cpp/module_spec.h

@@ -62,20 +62,20 @@ namespace isc { namespace config {
         /// ElementPtr, returns an empty ElementPtr if there is none
         /// \return ElementPtr Shared pointer to the commands
         ///                    part of the specification
-        const ElementPtr getCommandsSpec();
+        const ElementPtr getCommandsSpec() const;
 
         /// Returns the configuration part of the specification as an
         /// ElementPtr
         /// \return ElementPtr Shared pointer to the configuration
         ///                    part of the specification
-        const ElementPtr getConfigSpec();
+        const ElementPtr getConfigSpec() const;
 
         /// Returns the full module specification as an ElementPtr
         /// \return ElementPtr Shared pointer to the specification
-        const ElementPtr getFullSpec() { return module_specification; };
+        const ElementPtr getFullSpec() const { return module_specification; };
 
         /// Returns the module name as specified by the specification
-        const std::string getModuleName();
+        const std::string getModuleName() const;
         
         // returns true if the given element conforms to this data
         // configuration specification

+ 6 - 9
src/lib/config/cpp/module_spec_unittests.cc

@@ -48,12 +48,10 @@ TEST(ModuleSpec, ReadingSpecfiles) {
     // Tests whether we can open specfiles and if we get the
     // right parse errors
     ModuleSpec dd = moduleSpecFromFile(specfile("spec1.spec"));
-    EXPECT_EQ(dd.getFullSpec()->get("module_spec")
-                                ->get("module_name")
-                                ->stringValue(), "Spec1");
+    EXPECT_EQ(dd.getFullSpec()->get("module_name")
+                              ->stringValue(), "Spec1");
     dd = moduleSpecFromFile(specfile("spec2.spec"));
-    EXPECT_EQ(dd.getFullSpec()->get("module_spec")
-                                ->get("config_data")->size(), 6);
+    EXPECT_EQ(dd.getFullSpec()->get("config_data")->size(), 6);
     module_spec_error("doesnotexist",
                    "Error opening ",
                    specfile("doesnotexist"),
@@ -62,9 +60,8 @@ TEST(ModuleSpec, ReadingSpecfiles) {
     std::ifstream file;
     file.open(specfile("spec1.spec").c_str());
     dd = moduleSpecFromFile(file);
-    EXPECT_EQ(dd.getFullSpec()->get("module_spec")
-                                ->get("module_name")
-                                ->stringValue(), "Spec1");
+    EXPECT_EQ(dd.getFullSpec()->get("module_name")
+                              ->stringValue(), "Spec1");
 }
 
 TEST(ModuleSpec, SpecfileItems) {
@@ -97,7 +94,7 @@ TEST(ModuleSpec, SpecfileConfigData)
     module_spec_error("spec7.spec",
                    "module_name missing in {}");
     module_spec_error("spec8.spec",
-                   "Data specification does not contain module_spec element");
+                   "No module_spec in specification");
     module_spec_error("spec16.spec",
                    "config_data is not a list of elements");
     module_spec_error("spec21.spec",