Browse Source

[trac2355] Addressed interrim review comments, and completed remainder
of the common parser refactoring. Replaced individual global variables with
instance of new ParserContext class. Created new base classes PoolParser
and SubnetConfigParser.

Thomas Markwalder 12 years ago
parent
commit
702512a6bb

+ 206 - 493
src/bin/dhcp4/config_parser.cc

@@ -41,314 +41,212 @@ using namespace isc::asiolink;
 
 namespace {
 
-// Pointers to various parser objects.
-typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
-typedef boost::shared_ptr<StringParser> StringParserPtr;
-typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
+/// @brief Create the global parser context which stores global
+/// parameters, options, and option definitions.
+ParserContextPtr global_context_ptr(new ParserContext(Option::V4));
 
-/// @brief a collection of pools
+/// @brief Parser for DHCP4 option data value.
 ///
-/// That type is used as intermediate storage, when pools are parsed, but there is
-/// no subnet object created yet to store them.
-typedef std::vector<Pool4Ptr> PoolStorage;
-
-
-/// @brief Global uint32 parameters that will be used as defaults.
-Uint32Storage uint32_defaults;
-
-/// @brief global string parameters that will be used as defaults.
-StringStorage string_defaults;
-
-/// @brief Global storage for options that will be used as defaults.
-OptionStorage option_defaults;
-
-/// @brief Global storage for option definitions.
-OptionDefStorage option_def_intermediate;
-
-/// @brief parser for pool definition
-///
-/// This parser handles pool definitions, i.e. a list of entries of one
-/// of two syntaxes: min-max and prefix/len. Pool4 objects are created
-/// and stored in chosen PoolStorage container.
-///
-/// As there are no default values for pool, setStorage() must be called
-/// before build(). Otherwise exception will be thrown.
-///
-/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
-class PoolParser : public DhcpConfigParser {
+/// This parser parses configuration entries that specify value of
+/// a single option specific to DHCP4.  It provides the DHCP4-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp4OptionDataParser : public OptionDataParser {
 public:
-
-    /// @brief constructor.
-    PoolParser(const std::string& /*param_name*/)
-        :pools_(NULL) {
-        // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
-    }
-
-    /// @brief constructor.
-    PoolParser(const std::string& /*param_name*/,  PoolStorage* pools)
-        :pools_(pools) {
-        // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
-    }
-
-    /// @brief parses the actual list
+    /// @brief Constructor.
     ///
-    /// This method parses the actual list of interfaces.
-    /// No validation is done at this stage, everything is interpreted as
-    /// interface name.
-    /// @param pools_list list of pools defined for a subnet
-    /// @throw InvalidOperation if storage was not specified (setStorage() not called)
-    /// @throw DhcpConfigError when pool parsing fails
-    void build(ConstElementPtr pools_list) {
-        // setStorage() should have been called before build
-        if (!pools_) {
-            isc_throw(InvalidOperation, "Parser logic error. No pool storage set,"
-                      " but pool parser asked to parse pools");
-        }
-
-        BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
-
-            // That should be a single pool representation. It should contain
-            // text is form prefix/len or first - last. Note that spaces
-            // are allowed
-            string txt = text_pool->stringValue();
-
-            // first let's remove any whitespaces
-            boost::erase_all(txt, " "); // space
-            boost::erase_all(txt, "\t"); // tabulation
-
-            // Is this prefix/len notation?
-            size_t pos = txt.find("/");
-            if (pos != string::npos) {
-                IOAddress addr("::");
-                uint8_t len = 0;
-                try {
-                    addr = IOAddress(txt.substr(0, pos));
-
-                    // start with the first character after /
-                    string prefix_len = txt.substr(pos + 1);
-
-                    // It is lexical cast to int and then downcast to uint8_t.
-                    // Direct cast to uint8_t (which is really an unsigned char)
-                    // will result in interpreting the first digit as output
-                    // value and throwing exception if length is written on two
-                    // digits (because there are extra characters left over).
-
-                    // No checks for values over 128. Range correctness will
-                    // be checked in Pool4 constructor.
-                    len = boost::lexical_cast<int>(prefix_len);
-                } catch (...)  {
-                    isc_throw(DhcpConfigError, "Failed to parse pool "
-                              "definition: " << text_pool->stringValue());
-                }
-
-                Pool4Ptr pool(new Pool4(addr, len));
-                local_pools_.push_back(pool);
-                continue;
-            }
-
-            // Is this min-max notation?
-            pos = txt.find("-");
-            if (pos != string::npos) {
-                // using min-max notation
-                IOAddress min(txt.substr(0,pos));
-                IOAddress max(txt.substr(pos + 1));
-
-                Pool4Ptr pool(new Pool4(min, max));
-
-                local_pools_.push_back(pool);
-                continue;
-            }
-
-            isc_throw(DhcpConfigError, "Failed to parse pool definition:"
-                      << text_pool->stringValue() <<
-                      ". Does not contain - (for min-max) nor / (prefix/len)");
-        }
+    /// @param dummy first param, option names are always "Dhcp4/option-data[n]"
+    /// @param options is the option storage in which to store the parsed option
+    /// upon "commit".
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    Dhcp4OptionDataParser(const std::string&, 
+        OptionStoragePtr options, ParserContextPtr global_context) 
+        :OptionDataParser("", options, global_context) {
     }
 
-    /// @brief sets storage for value of this parameter
+    /// @brief static factory method for instantiating Dhcp4OptionDataParsers
     ///
-    /// See \ref dhcpv4ConfigInherit for details.
-    ///
-    /// @param storage pointer to the storage container
-    void setStorage(PoolStorage* storage) {
-        pools_ = storage;
+    /// @param param_name name fo the parameter to be parsed.
+    /// @param options storage where the parameter value is to be stored.
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    /// @return returns a pointer to a new OptionDataParser. Caller is
+    /// is responsible for deleting it when it is no longer needed.
+    static OptionDataParser* factory(const std::string& param_name,
+        OptionStoragePtr options, ParserContextPtr global_context) {
+        return (new Dhcp4OptionDataParser(param_name, options, global_context));
     }
 
-    /// @brief Stores the parsed values in a storage provided
-    ///        by an upper level parser.
-    virtual void commit() {
-        if (pools_) {
-            // local_pools_ holds the values produced by the build function.
-            // At this point parsing should have completed successfuly so
-            // we can append new data to the supplied storage.
-            pools_->insert(pools_->end(), local_pools_.begin(),
-                           local_pools_.end());
+protected:
+    /// @brief Finds an option definition within the server's option space
+    /// 
+    /// Given an option space and an option code, find the correpsonding 
+    /// option defintion within the server's option defintion storage.
+    ///
+    /// @param option_space name of the parameter option space 
+    /// @param option_code numeric value of the parameter to find 
+    /// @return OptionDefintionPtr of the option defintion or an 
+    /// empty OptionDefinitionPtr if not found.
+    /// @throw DhcpConfigError if the option space requested is not valid 
+    /// for this server. 
+    virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+                std::string& option_space, uint32_t option_code) {
+        OptionDefinitionPtr def;
+        if (option_space == "dhcp4" &&
+            LibDHCP::isStandardOption(Option::V4, option_code)) {
+            def = LibDHCP::getOptionDef(Option::V4, option_code);
+        } else if (option_space == "dhcp6") {
+            isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
+                     << " for DHCPv6 server");
         }
-    }
 
-    /// @brief factory that constructs PoolParser objects
-    ///
-    /// @param param_name name of the parameter to be parsed
-    static DhcpConfigParser* factory(const std::string& param_name) {
-        return (new PoolParser(param_name));
+        return (def);
     }
-
-private:
-    /// @brief pointer to the actual Pools storage
-    ///
-    /// That is typically a storage somewhere in Subnet parser
-    /// (an upper level parser).
-    PoolStorage* pools_;
-    /// A temporary storage for pools configuration. It is a
-    /// storage where pools are stored by build function.
-    PoolStorage local_pools_;
 };
 
-/// @brief this class parses a single subnet
+/// @brief Parser for IPv4 pool definitions.  
+///
+/// This is the IPv4 derivation of the PoolParser class and handles pool 
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and 
+/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen 
+/// PoolStorage container.
 ///
-/// This class parses the whole subnet definition. It creates parsers
-/// for received configuration parameters as needed.
-class Subnet4ConfigParser : public DhcpConfigParser {
+/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
+class Pool4Parser : public PoolParser {
 public:
 
-    /// @brief constructor
-    Subnet4ConfigParser(const std::string& ) {
-        // The parameter should always be "subnet", but we don't check here
-        // against it in case someone wants to reuse this parser somewhere.
+    /// @brief Constructor.
+    ///
+    /// @param param_name name of the parameter. Note, it is passed through
+    /// but unused, parameter is currently always "Dhcp4/subnet4[X]/pool"
+    /// @param pools storage container in which to store the parsed pool
+    /// upon "commit"
+    Pool4Parser(const std::string& param_name,  PoolStoragePtr pools)
+        :PoolParser(param_name, pools) {
     }
 
-    /// @brief parses parameter value
+protected:
+    /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
     ///
-    /// @param subnet pointer to the content of subnet definition
-    void build(ConstElementPtr subnet) {
-
-        BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
-            ParserPtr parser(createSubnet4ConfigParser(param.first));
-            parser->build(param.second);
-            parsers_.push_back(parser);
-        }
-
-        // In order to create new subnet we need to get the data out
-        // of the child parsers first. The only way to do it is to
-        // invoke commit on them because it will make them write
-        // parsed data into storages we have supplied.
-        // Note that triggering commits on child parsers does not
-        // affect global data because we supplied pointers to storages
-        // local to this object. Thus, even if this method fails
-        // later on, the configuration remains consistent.
-        BOOST_FOREACH(ParserPtr parser, parsers_) {
-            parser->commit();
-        }
+    /// @param addr is the IPv4 prefix of the pool.
+    /// @param len is the prefix length.
+    /// @param ignored dummy parameter to provide symmetry between 
+    /// @return returns a PoolPtr to the new Pool4 object. 
+    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t)
+    {
+        return (PoolPtr(new Pool4(addr, len)));
+    }
 
-        // Create a subnet.
-        createSubnet();
+    /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
+    ///
+    /// @param min is the first IPv4 address in the pool.
+    /// @param max is the last IPv4 address in the pool.
+    /// @param ignored dummy parameter to provide symmetry between the 
+    /// PoolParser derivations. The V6 derivation requires a third value.
+    /// @return returns a PoolPtr to the new Pool4 object. 
+    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t)
+    {
+        return (PoolPtr(new Pool4(min, max)));
     }
+};
 
-    /// @brief commits received configuration.
+/// @brief This class parses a single IPv4 subnet.
+///
+/// This is the IPv4 derivation of the SubnetConfigParser class and it parses 
+/// the whole subnet definition. It creates parsersfor received configuration 
+/// parameters as needed.
+class Subnet4ConfigParser : public SubnetConfigParser {
+public:
+    /// @brief Constructor
     ///
-    /// This method does most of the configuration. Many other parsers are just
-    /// storing the values that are actually consumed here. Pool definitions
-    /// created in other parsers are used here and added to newly created Subnet4
-    /// objects. Subnet4 are then added to DHCP CfgMgr.
-    /// @throw DhcpConfigError if there are any issues encountered during commit
+    /// @param ignored first parameter
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    Subnet4ConfigParser(const std::string&, ParserContextPtr global_context)
+        :SubnetConfigParser("", global_context) {
+    } 
+
+    /// @brief Adds the created subnet to a server's configuration.
     void commit() {
         if (subnet_) {
-            CfgMgr::instance().addSubnet4(subnet_);
+            Subnet4Ptr bs = boost::dynamic_pointer_cast<Subnet4>(subnet_);
+            isc::dhcp::CfgMgr::instance().addSubnet4(bs);
         }
     }
 
-private:
+protected:
 
-    /// @brief Append sub-options to an option.
+    /// @brief Creates parsers for entries in subnet definition.
     ///
-    /// @param option_space a name of the encapsulated option space.
-    /// @param option option instance to append sub-options to.
-    void appendSubOptions(const std::string& option_space, OptionPtr& option) {
-        // Only non-NULL options are stored in option container.
-        // If this option pointer is NULL this is a serious error.
-        assert(option);
-
-        OptionDefinitionPtr def;
-        if (option_space == "dhcp4" &&
-            LibDHCP::isStandardOption(Option::V4, option->getType())) {
-            def = LibDHCP::getOptionDef(Option::V4, option->getType());
-            // Definitions for some of the standard options hasn't been
-            // implemented so it is ok to leave here.
-            if (!def) {
-                return;
-            }
+    /// @param config_id name of the entry
+    ///
+    /// @return parser object for specified entry name. Note the caller is
+    /// responsible for deleting the parser created.
+    /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+    /// for unknown config element
+    DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
+        DhcpConfigParser* parser = NULL;
+        if ((config_id.compare("valid-lifetime") == 0)  ||
+            (config_id.compare("renew-timer") == 0)  ||
+            (config_id.compare("rebind-timer") == 0))  {
+            parser = new Uint32Parser(config_id, uint32_values_);
+        } else if ((config_id.compare("subnet") == 0) || 
+                 (config_id.compare("interface") == 0)) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("pool") == 0) {
+            parser = new Pool4Parser(config_id, pools_);
+        } else if (config_id.compare("option-data") == 0) {
+           parser = new OptionDataListParser(config_id, options_, 
+                                            global_context_,
+                                            Dhcp4OptionDataParser::factory);
         } else {
-            const OptionDefContainerPtr defs =
-                option_def_intermediate.getItems(option_space);
-            const OptionDefContainerTypeIndex& idx = defs->get<1>();
-            const OptionDefContainerTypeRange& range =
-                idx.equal_range(option->getType());
-            // There is no definition so we have to leave.
-            if (std::distance(range.first, range.second) == 0) {
-                return;
-            }
-
-            def = *range.first;
-
-            // If the definition exists, it must be non-NULL.
-            // Otherwise it is a programming error.
-            assert(def);
+            isc_throw(NotImplemented,
+                "parser error: Subnet4 parameter not supported: " << config_id);
         }
 
-        // We need to get option definition for the particular option space
-        // and code. This definition holds the information whether our
-        // option encapsulates any option space.
-        // Get the encapsulated option space name.
-        std::string encapsulated_space = def->getEncapsulatedSpace();
-        // If option space name is empty it means that our option does not
-        // encapsulate any option space (does not include sub-options).
-        if (!encapsulated_space.empty()) {
-            // Get the sub-options that belong to the encapsulated
-            // option space.
-            const Subnet::OptionContainerPtr sub_opts =
-                option_defaults.getItems(encapsulated_space);
-            // Append sub-options to the option.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
-                if (desc.option) {
-                    option->addOption(desc.option);
-                }
-            }
-        }
+        return (parser);
     }
 
-    /// @brief Create a new subnet using a data from child parsers.
+
+    /// @brief Determines if the given option space name and code describe
+    /// a standard option for the DCHP4 server. 
     ///
-    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
-    void createSubnet() {
-        std::string subnet_txt;
-        try {
-            subnet_txt = string_values_.getParam("subnet"); 
-        } catch (DhcpConfigError) {
-            // rethrow with precise error
-            isc_throw(DhcpConfigError,
-                      "Mandatory subnet definition in subnet missing");
-        }
+    /// @param option_space is the name of the option space to consider
+    /// @param code is the numeric option code to consider
+    /// @return returns true if the space and code are part of the server's
+    /// standard options.
+    bool isServerStdOption(std::string option_space, uint32_t code) {
+        return ((option_space.compare("dhcp4") == 0)
+                && LibDHCP::isStandardOption(Option::V4, code));
+    }
 
-        // Remove any spaces or tabs.
-        boost::erase_all(subnet_txt, " ");
-        boost::erase_all(subnet_txt, "\t");
-
-        // The subnet format is prefix/len. We are going to extract
-        // the prefix portion of a subnet string to create IOAddress
-        // object from it. IOAddress will be passed to the Subnet's
-        // constructor later on. In order to extract the prefix we
-        // need to get all characters preceding "/".
-        size_t pos = subnet_txt.find("/");
-        if (pos == string::npos) {
-            isc_throw(DhcpConfigError,
-                      "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
-        }
+    /// @brief Returns the option definition for a given option code from
+    /// the DHCP4 server's standard set of options.
+    /// @param code is the numeric option code of the desired option definition.
+    /// @return returns a pointer the option definition
+    OptionDefinitionPtr getServerStdOptionDefinition (uint32_t code) {
+        return (LibDHCP::getOptionDef(Option::V4, code));
+    }
 
-        // Try to create the address object. It also validates that
-        // the address syntax is ok.
-        IOAddress addr(subnet_txt.substr(0, pos));
-        uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+    /// @brief Issues a DHCP4 server specific warning regarding duplicate subnet
+    /// options. 
+    /// 
+    /// @param code is the numeric option code of the duplicate option
+    /// @param addr is the subnet address 
+    /// @todo a means to know the correct logger and perhaps a common
+    /// message would allow this method to be emitted by the base class. 
+    virtual void duplicate_option_warning(uint32_t code,
+                                         isc::asiolink::IOAddress& addr) {
+        LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
+            .arg(code).arg(addr.toText());
+    }
 
+    /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
+    /// and prefix length.  
+    /// 
+    /// @param addr is IPv4 address of the subnet.
+    /// @param len is the prefix length 
+    void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
         // Get all 'time' parameters using inheritance.
         // If the subnet-specific value is defined then use it, else
         // use the global value. The global value must always be
@@ -366,151 +264,10 @@ private:
         LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());
 
         subnet_.reset(new Subnet4(addr, len, t1, t2, valid));
-
-        for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
-            subnet_->addPool(*it);
-        }
-
-        // We are going to move configured options to the Subnet object.
-        // Configured options reside in the container where options
-        // are grouped by space names. Thus we need to get all space names
-        // and iterate over all options that belong to them.
-        std::list<std::string> space_names = options_.getOptionSpaceNames();
-        BOOST_FOREACH(std::string option_space, space_names) {
-            // Get all options within a particular option space.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc,
-                          *options_.getItems(option_space)) {
-                // The pointer should be non-NULL. The validation is expected
-                // to be performed by the OptionDataParser before adding an
-                // option descriptor to the container.
-                assert(desc.option);
-                // We want to check whether an option with the particular
-                // option code has been already added. If so, we want
-                // to issue a warning.
-                Subnet::OptionDescriptor existing_desc =
-                    subnet_->getOptionDescriptor("option_space",
-                                                 desc.option->getType());
-                if (existing_desc.option) {
-                    LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
-                        .arg(desc.option->getType()).arg(addr.toText());
-                }
-                // Add sub-options (if any).
-                appendSubOptions(option_space, desc.option);
-                // In any case, we add the option to the subnet.
-                subnet_->addOption(desc.option, false, option_space);
-            }
-        }
-
-        // Check all global options and add them to the subnet object if
-        // they have been configured in the global scope. If they have been
-        // configured in the subnet scope we don't add global option because
-        // the one configured in the subnet scope always takes precedence.
-        space_names = option_defaults.getOptionSpaceNames();
-        BOOST_FOREACH(std::string option_space, space_names) {
-            // Get all global options for the particular option space.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc,
-                          *option_defaults.getItems(option_space)) {
-                // The pointer should be non-NULL. The validation is expected
-                // to be performed by the OptionDataParser before adding an
-                // option descriptor to the container.
-                assert(desc.option);
-                // Check if the particular option has been already added.
-                // This would mean that it has been configured in the
-                // subnet scope. Since option values configured in the
-                // subnet scope take precedence over globally configured
-                // values we don't add option from the global storage
-                // if there is one already.
-                Subnet::OptionDescriptor existing_desc =
-                    subnet_->getOptionDescriptor(option_space, desc.option->getType());
-                if (!existing_desc.option) {
-                    // Add sub-options (if any).
-                    appendSubOptions(option_space, desc.option);
-
-                    subnet_->addOption(desc.option, false, option_space);
-                }
-            }
-        }
-    }
-
-    /// @brief creates parsers for entries in subnet definition
-    ///
-    /// @todo Add subnet-specific things here (e.g. subnet-specific options)
-    ///
-    /// @param config_id name od the entry
-    /// @return parser object for specified entry name
-    /// @throw NotImplemented if trying to create a parser for unknown config element
-    DhcpConfigParser* createSubnet4ConfigParser(const std::string& config_id) {
-        DhcpConfigParser *parser = NULL;
-        if ((config_id.compare("valid-lifetime") == 0)  ||
-            (config_id.compare("renew-timer") == 0)  ||
-            (config_id.compare("rebind-timer") == 0))  {
-            parser = new Uint32Parser(config_id, &uint32_values_);
-        }
-        else if (config_id.compare("subnet") == 0) {
-            parser = new StringParser(config_id, &string_values_);
-        }
-        else if (config_id.compare("pool") == 0) {
-            parser = new PoolParser(config_id, &pools_);
-        }
-        else if (config_id.compare("option-data") == 0) {
-           parser = new OptionDataListParser(config_id, &options_, &option_def_intermediate,
-                                            Dhcp4OptionDataParser::factory);
-        } else {
-            isc_throw(NotImplemented,
-                "parser error: Subnet4 parameter not supported: " << config_id);
-        }
-
-        return (parser);
-    }
-
-    /// @brief Returns value for a given parameter (after using inheritance)
-    ///
-    /// This method implements inheritance. For a given parameter name, it first
-    /// checks if there is a global value for it and overwrites it with specific
-    /// value if such value was defined in subnet.
-    ///
-    /// @param name name of the parameter
-    /// @return triplet with the parameter name
-    /// @throw DhcpConfigError when requested parameter is not present
-    Triplet<uint32_t> getParam(const std::string& name) {
-        uint32_t value = 0;
-        try {
-            // look for local value 
-            value = uint32_values_.getParam(name);
-        } catch (DhcpConfigError) {
-            try {
-                // no local, use global value 
-                value = uint32_defaults.getParam(name);
-            } catch (DhcpConfigError) {
-                isc_throw(DhcpConfigError, "Mandatory parameter " << name
-                      << " missing (no global default and no subnet-"
-                      << "specific value)");
-            }
-        }
-
-        return (Triplet<uint32_t>(value));
     }
-
-    /// storage for subnet-specific uint32 values
-    Uint32Storage uint32_values_;
-
-    /// storage for subnet-specific integer values
-    StringStorage string_values_;
-
-    /// storage for pools belonging to this subnet
-    PoolStorage pools_;
-
-    /// storage for options belonging to this subnet
-    OptionStorage options_;
-
-    /// parsers are stored here
-    ParserCollection parsers_;
-
-    /// @brief Pointer to the created subnet object.
-    isc::dhcp::Subnet4Ptr subnet_;
 };
 
-/// @brief this class parses list of subnets
+/// @brief this class parses list of DHCP4 subnets
 ///
 /// This is a wrapper parser that handles the whole list of Subnet4
 /// definitions. It iterates over all entries and creates Subnet4ConfigParser
@@ -520,8 +277,9 @@ public:
 
     /// @brief constructor
     ///
+    /// @param dummy first argument, always ingored. All parsers accept a
+    /// string parameter "name" as their first argument.
     Subnets4ListConfigParser(const std::string&) {
-        /// parameter name is ignored
     }
 
     /// @brief parses contents of the list
@@ -532,7 +290,8 @@ public:
     /// @param subnets_list pointer to a list of IPv4 subnets
     void build(ConstElementPtr subnets_list) {
         BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
-            ParserPtr parser(new Subnet4ConfigParser("subnet"));
+            ParserPtr parser(new Subnet4ConfigParser("subnet", 
+                                                    global_context_ptr));
             parser->build(subnet);
             subnets_.push_back(parser);
         }
@@ -540,8 +299,8 @@ public:
 
     /// @brief commits subnets definitions.
     ///
-    /// Iterates over all Subnet4 parsers. Each parser contains definitions
-    /// of a single subnet and its parameters and commits each subnet separately.
+    /// Iterates over all Subnet4 parsers. Each parser contains definitions of
+    /// a single subnet and its parameters and commits each subnet separately.
     void commit() {
         // @todo: Implement more subtle reconfiguration than toss
         // the old one and replace with the new one.
@@ -572,34 +331,6 @@ public:
 namespace isc {
 namespace dhcp {
 
-//************** Dhcp4OptionDataParser methods *******************************
-
-Dhcp4OptionDataParser::Dhcp4OptionDataParser(const std::string& param_name, 
-    OptionStorage *options, OptionDefStorage *option_defs)
-    :OptionDataParser(param_name, options, option_defs, Option::V4) {
-}
-
-OptionDataParser* Dhcp4OptionDataParser::factory(const std::string& param_name,
-    OptionStorage *options, OptionDefStorage *option_defs) {
-    return new Dhcp4OptionDataParser(param_name, options, option_defs);
-}
-
-OptionDefinitionPtr Dhcp4OptionDataParser::findServerSpaceOptionDefinition (
-    std::string& option_space, uint32_t option_code) {
-    OptionDefinitionPtr def;
-
-    if (option_space == "dhcp4" &&
-        LibDHCP::isStandardOption(Option::V4, option_code)) {
-        def = LibDHCP::getOptionDef(Option::V4, option_code);
-
-    } else if (option_space == "dhcp6") {
-        isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
-                << " for DHCPv6 server");
-    }
-
-    return def;
-}
-
 /// @brief creates global parsers
 ///
 /// This method creates global parsers that parse global parameters, i.e.
@@ -607,35 +338,33 @@ OptionDefinitionPtr Dhcp4OptionDataParser::findServerSpaceOptionDefinition (
 ///
 /// @param config_id pointer to received global configuration entry
 /// @return parser for specified global DHCPv4 parameter
-/// @throw NotImplemented if trying to create a parser for unknown config element
+/// @throw NotImplemented if trying to create a parser for unknown 
+/// config element
 DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
-    DhcpConfigParser *parser = NULL;
+    DhcpConfigParser* parser = NULL;
     if ((config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0))  {
-        parser = new Uint32Parser(config_id, &uint32_defaults);
-    }
-    else if (config_id.compare("interface") == 0) {
+        parser = new Uint32Parser(config_id, 
+                                 global_context_ptr->uint32_values_);
+    } else if (config_id.compare("interface") == 0) {
         parser = new InterfaceListConfigParser(config_id);
-    }
-    else if (config_id.compare("subnet4") == 0) {
+    } else if (config_id.compare("subnet4") == 0) {
         parser = new Subnets4ListConfigParser(config_id);
-    }
-    else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, &option_defaults, 
-                                          &option_def_intermediate,
+    } else if (config_id.compare("option-data") == 0) {
+        parser = new OptionDataListParser(config_id, 
+                                          global_context_ptr->options_, 
+                                          global_context_ptr,
                                           Dhcp4OptionDataParser::factory);
-    }
-    else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, &option_def_intermediate);
-    }
-    else if (config_id.compare("version") == 0) {
-        parser  = new StringParser(config_id, &string_defaults);
-    }
-    else if (config_id.compare("lease-database") == 0) {
+    } else if (config_id.compare("option-def") == 0) {
+        parser  = new OptionDefListParser(config_id, 
+                                          global_context_ptr->option_defs_);
+    } else if (config_id.compare("version") == 0) {
+        parser  = new StringParser(config_id, 
+                                    global_context_ptr->string_values_);
+    } else if (config_id.compare("lease-database") == 0) {
         parser = new DbAccessParser(config_id); 
-    }
-    else {
+    } else {
         isc_throw(NotImplemented,
                 "Parser error: Global configuration parameter not supported: "
                 << config_id);
@@ -645,17 +374,18 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
 }
 
 isc::data::ConstElementPtr
-configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
+configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     if (!config_set) {
         ConstElementPtr answer = isc::config::createAnswer(1,
                                  string("Can't parse NULL config"));
         return (answer);
     }
 
-    /// @todo: append most essential info here (like "2 new subnets configured")
+    /// @todo: Append most essential info here (like "2 new subnets configured")
     string config_details;
 
-    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START).arg(config_set->str());
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, 
+              DHCP4_CONFIG_START).arg(config_set->str());
 
     // Some of the values specified in the configuration depend on
     // other values. Typically, the values in the subnet4 structure
@@ -675,14 +405,11 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
     // parsing operation fails after the global storage has been
     // modified. We need to preserve the original global data here
     // so as we can rollback changes when an error occurs.
-    Uint32Storage uint32_local(uint32_defaults);
-    StringStorage string_local(string_defaults);
-    OptionStorage option_local(option_defaults);
-    OptionDefStorage option_def_local(option_def_intermediate);
+    ParserContext original_context(*global_context_ptr);
 
-    // answer will hold the result.
+    // Answer will hold the result.
     ConstElementPtr answer;
-    // rollback informs whether error occured and original data
+    // Rollback informs whether error occured and original data
     // have to be restored to global storages.
     bool rollback = false;
     // config_pair holds the details of the current parser when iterating over
@@ -692,17 +419,15 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
     try {
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
-            config_set->mapValue();
+                                                        config_set->mapValue();
         BOOST_FOREACH(config_pair, values_map) {
             ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
                       .arg(config_pair.first);
             if (config_pair.first == "subnet4") {
                 subnet_parser = parser;
-
             } else if (config_pair.first == "option-data") {
                 option_parser = parser;
-
             } else {
                 // Those parsers should be started before other
                 // parsers so we can call build straight away.
@@ -733,7 +458,6 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
     } catch (const isc::Exception& ex) {
         LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
                   .arg(config_pair.first).arg(ex.what());
-        std::cout << "parse failed on:" << config_pair.first << std::endl;
         answer = isc::config::createAnswer(1,
                      string("Configuration parsing failed: ") + ex.what());
 
@@ -741,7 +465,7 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
         rollback = true;
 
     } catch (...) {
-        // for things like bad_cast in boost::lexical_cast
+        // For things like bad_cast in boost::lexical_cast
         LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
         answer = isc::config::createAnswer(1,
                      string("Configuration parsing failed"));
@@ -765,30 +489,18 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
             answer = isc::config::createAnswer(2,
                          string("Configuration commit failed: ") + ex.what());
             rollback = true;
-
         } catch (...) {
-            // for things like bad_cast in boost::lexical_cast
+            // For things like bad_cast in boost::lexical_cast
             LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
             answer = isc::config::createAnswer(2,
                          string("Configuration commit failed"));
             rollback = true;
-
         }
     }
 
     // Rollback changes as the configuration parsing failed.
     if (rollback) {
-
-    // TKM - take this out, its just here for diagnostics
-    std::cout << "***************" <<  std::endl;
-    std::cout << "answer is:" <<  std::endl;
-    answer->toJSON(std::cout);
-    std::cout << std::endl << "***************" <<  std::endl;
-
-        std::swap(uint32_defaults, uint32_local);
-        std::swap(string_defaults, string_local);
-        std::swap(option_defaults, option_local);
-        std::swap(option_def_intermediate, option_def_local);
+        global_context_ptr.reset(new ParserContext(original_context));
         return (answer);
     }
 
@@ -799,8 +511,9 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
     return (answer);
 }
 
-const Uint32Storage& getUint32Defaults() {
-    return (uint32_defaults);
+// Makes global context accessible for unit tests.
+const ParserContext& getGlobalParserContext() {
+    return (*global_context_ptr);
 }
 
 }; // end of isc::dhcp namespace

+ 9 - 53
src/bin/dhcp4/config_parser.h

@@ -30,7 +30,8 @@ namespace dhcp {
 
 class Dhcpv4Srv;
 
-/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration values.
+/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration 
+/// values.
 ///
 /// This function parses configuration information stored in @c config_set
 /// and configures the @c server by applying the configuration to it.
@@ -43,9 +44,9 @@ class Dhcpv4Srv;
 /// (such as malformed configuration or invalid configuration parameter),
 /// this function returns appropriate error code.
 ///
-/// This function is called every time a new configuration is received. The extra
-/// parameter is a reference to DHCPv4 server component. It is currently not used
-/// and CfgMgr::instance() is accessed instead.
+/// This function is called every time a new configuration is received. The 
+/// extra parameter is a reference to DHCPv4 server component. It is currently
+/// not used and CfgMgr::instance() is accessed instead.
 ///
 /// This method does not throw. It catches all exceptions and returns them as
 /// reconfiguration statuses. It may return the following response codes:
@@ -60,58 +61,13 @@ isc::data::ConstElementPtr
 configureDhcp4Server(Dhcpv4Srv&,
                      isc::data::ConstElementPtr config_set);
 
-
-/// @brief Returns the global uint32_t values storage.
+/// @brief Returns the global context
 ///
 /// This function must be only used by unit tests that need
-/// to access uint32_t global storage to verify that the
-/// Uint32Parser works as expected.
-///
-/// @return a reference to a global uint32 values storage.
-const Uint32Storage& getUint32Defaults();
-
-
-/// @brief Parser for DHCP4 option data value.
+/// to access global context.
 ///
-/// This parser parses configuration entries that specify value of
-/// a single option specific to DHCP4.  It provides the DHCP4-specific
-/// implementation of the abstract class OptionDataParser.
-class Dhcp4OptionDataParser : public OptionDataParser {
-public:
-    /// @brief Constructor.
-    ///
-    /// Class constructor.
-    Dhcp4OptionDataParser(const std::string&, OptionStorage *options,
-        OptionDefStorage *option_defs);
-
-    /// @brief static factory method for instantiating Dhcp4OptionDataParsers
-    ///
-    /// @param param_name name fo the parameter to be parsed.
-    /// @param options storage where the parameter value is to be stored.
-    /// @param global_option_defs global option definitions storage 
-    static OptionDataParser* factory(const std::string& param_name, OptionStorage *options,
-        OptionDefStorage *global_option_defs);
-
-protected:
-    /// @brief Finds an option definition within the server's option space
-    /// 
-    /// Given an option space and an option code, find the correpsonding 
-    /// option defintion within the server's option defintion storage.
-    ///
-    /// @param option_space name of the parameter option space 
-    /// @param option_code numeric value of the parameter to find 
-    /// @return OptionDefintionPtr of the option defintion or an 
-    /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid 
-    /// for this server. 
-    virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
-        std::string& option_space, uint32_t option_code);
-
-private:
-    // Private default Constructor declared for safety.
-    Dhcp4OptionDataParser() :OptionDataParser("",NULL,NULL,Option::V4) {}
-};
-
+/// @returns a const reference to the global context
+const ParserContext& getGlobalParserContext();
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 3 - 2
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -52,9 +52,10 @@ public:
 
     // Checks if global parameter of name have expected_value
     void checkGlobalUint32(string name, uint32_t expected_value) {
-        const Uint32Storage& uint32_defaults = getUint32Defaults();
+        const Uint32StoragePtr uint32_defaults = 
+                                        getGlobalParserContext().uint32_values_;
         try {
-            uint32_t actual_value = uint32_defaults.getParam(name);
+            uint32_t actual_value = uint32_defaults->getParam(name);
             EXPECT_EQ(expected_value, actual_value);
         } catch (DhcpConfigError) {
             ADD_FAILURE() << "Expected uint32 with name " << name

+ 213 - 497
src/bin/dhcp6/config_parser.cc

@@ -55,305 +55,219 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
 typedef boost::shared_ptr<StringParser> StringParserPtr;
 typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
 
-/// @brief Collection of address pools.
-///
-/// This type is used as intermediate storage, when pools are parsed, but there is
-/// no subnet object created yet to store them.
-typedef std::vector<isc::dhcp::Pool6Ptr> PoolStorage;
-
-/// @brief Global uint32 parameters that will be used as defaults.
-Uint32Storage uint32_defaults;
-
-/// @brief global string parameters that will be used as defaults.
-StringStorage string_defaults;
-
-/// @brief Global storage for options that will be used as defaults.
-OptionStorage option_defaults;
-
-/// @brief Global storage for option definitions.
-OptionDefStorage option_def_intermediate;
+// TKM - declare a global parser context
+ParserContextPtr global_context_ptr(new ParserContext(Option::V6));
 
-/// @brief parser for pool definition
+/// @brief Parser for DHCP6 option data value.
 ///
-/// This parser handles pool definitions, i.e. a list of entries of one
-/// of two syntaxes: min-max and prefix/len. Pool6 objects are created
-/// and stored in chosen PoolStorage container.
-///
-/// As there are no default values for pool, setStorage() must be called
-/// before build(). Otherwise an exception will be thrown.
-///
-/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
-class PoolParser : public DhcpConfigParser {
+/// This parser parses configuration entries that specify value of
+/// a single option specific to DHCP6.  It provides the DHCP6-specific
+/// implementation of the abstract class OptionDataParser.
+class Dhcp6OptionDataParser : public OptionDataParser {
 public:
-
-    /// @brief constructor.
-    PoolParser(const std::string& /*param_name*/)
-        : pools_(NULL) {
-        // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
+    /// @brief Constructor.
+    ///
+    /// @param dummy first param, option names are always "Dhcp6/option-data[n]"
+    /// @param options is the option storage in which to store the parsed option
+    /// upon "commit".
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    Dhcp6OptionDataParser(const std::string&, OptionStoragePtr options, 
+                         ParserContextPtr global_context) 
+        :OptionDataParser("", options, global_context) {
     }
 
-    /// @brief constructor.
-    PoolParser(const std::string& /*param_name*/,  PoolStorage* pools)
-        :pools_(pools) {
-        // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
+    /// @brief static factory method for instantiating Dhcp4OptionDataParsers
+    ///
+    /// @param param_name name of the parameter to be parsed.
+    /// @param options storage where the parameter value is to be stored.
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    /// @return returns a pointer to a new OptionDataParser. Caller is
+    /// is responsible for deleting it when it is no longer needed.
+    static OptionDataParser* factory(const std::string& param_name,
+    OptionStoragePtr options, ParserContextPtr global_context) {
+        return (new Dhcp6OptionDataParser(param_name, options, global_context));
     }
 
-    /// @brief parses the actual list
+
+protected:
+    /// @brief Finds an option definition within the server's option space
+    /// 
+    /// Given an option space and an option code, find the correpsonding 
+    /// option defintion within the server's option defintion storage.
     ///
-    /// This method parses the actual list of interfaces.
-    /// No validation is done at this stage, everything is interpreted as
-    /// interface name.
-    /// @param pools_list list of pools defined for a subnet
-    /// @throw isc::InvalidOperation if storage was not specified
-    ///        (setStorage() not called)
-    void build(ConstElementPtr pools_list) {
-
-        // setStorage() should have been called before build
-        if (!pools_) {
-            isc_throw(isc::InvalidOperation, "parser logic error: no pool storage set,"
-                      " but pool parser asked to parse pools");
+    /// @param option_space name of the parameter option space 
+    /// @param option_code numeric value of the parameter to find 
+    /// @return OptionDefintionPtr of the option defintion or an 
+    /// empty OptionDefinitionPtr if not found.
+    /// @throw DhcpConfigError if the option space requested is not valid 
+    /// for this server. 
+    virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
+                            std::string& option_space, uint32_t option_code) {
+        OptionDefinitionPtr def;
+        if (option_space == "dhcp6" &&
+            LibDHCP::isStandardOption(Option::V6, option_code)) {
+            def = LibDHCP::getOptionDef(Option::V6, option_code);
+        } else if (option_space == "dhcp4") {
+            isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
+                     << " for DHCPv4 server");
         }
 
-        BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
-
-            // That should be a single pool representation. It should contain
-            // text in the form prefix/len or first - last. Note that spaces
-            // are allowed
-            string txt = text_pool->stringValue();
-
-            // first let's remove any whitespaces
-            boost::erase_all(txt, " "); // space
-            boost::erase_all(txt, "\t"); // tabulation
-
-            // Is this prefix/len notation?
-            size_t pos = txt.find("/");
-            if (pos != string::npos) {
-                IOAddress addr("::");
-                uint8_t len = 0;
-                try {
-                    addr = IOAddress(txt.substr(0, pos));
-
-                    // start with the first character after /
-                    string prefix_len = txt.substr(pos + 1);
-
-                    // It is lexically cast to int and then downcast to uint8_t.
-                    // Direct cast to uint8_t (which is really an unsigned char)
-                    // will result in interpreting the first digit as output
-                    // value and throwing exception if length is written on two
-                    // digits (because there are extra characters left over).
-
-                    // No checks for values over 128. Range correctness will
-                    // be checked in Pool6 constructor.
-                    len = boost::lexical_cast<int>(prefix_len);
-                } catch (...)  {
-                    isc_throw(DhcpConfigError, "failed to parse pool "
-                              "definition: " << text_pool->stringValue());
-                }
-
-                Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
-                local_pools_.push_back(pool);
-                continue;
-            }
-
-            // Is this min-max notation?
-            pos = txt.find("-");
-            if (pos != string::npos) {
-                // using min-max notation
-                IOAddress min(txt.substr(0, pos));
-                IOAddress max(txt.substr(pos + 1));
-
-                Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
-
-                local_pools_.push_back(pool);
-                continue;
-            }
-
-            isc_throw(DhcpConfigError, "failed to parse pool definition:"
-                      << text_pool->stringValue() <<
-                      ". Does not contain - (for min-max) nor / (prefix/len)");
-        }
+        return def;
     }
+};
 
-    /// @brief sets storage for value of this parameter
-    ///
-    /// See @ref dhcpv6ConfigInherit for details.
-    ///
-    /// @param storage pointer to the storage container
-    void setStorage(PoolStorage* storage) {
-        pools_ = storage;
-    }
+/// @brief Parser for IPv4 pool definitions.  
+///
+/// This is the IPv6 derivation of the PoolParser class and handles pool 
+/// definitions, i.e. a list of entries of one of two syntaxes: min-max and 
+/// prefix/len for IPv6 pools. Pool6 objects are created and stored in chosen 
+/// PoolStorage container.
+///
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class Pool6Parser : public PoolParser {
+public:
 
-    /// @brief Stores the parsed values in a storage provided
-    ///        by an upper level parser.
-    virtual void commit() {
-        if (pools_) {
-            // local_pools_ holds the values produced by the build function.
-            // At this point parsing should have completed successfuly so
-            // we can append new data to the supplied storage.
-            pools_->insert(pools_->end(), local_pools_.begin(),
-                           local_pools_.end());
-        }
+    /// @brief Constructor.
+    ///
+    /// @param param_name name of the parameter. Note, it is passed through
+    /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
+    /// @param pools storage container in which to store the parsed pool
+    /// upon "commit"
+    Pool6Parser(const std::string& param_name,  PoolStoragePtr pools)
+        :PoolParser(param_name, pools) {
     }
 
-    /// @brief factory that constructs PoolParser objects
+protected:
+    /// @brief Creates a Pool6 object given a IPv6 prefix and the prefix length.
     ///
-    /// @param param_name name of the parameter to be parsed
-    static DhcpConfigParser* factory(const std::string& param_name) {
-        return (new PoolParser(param_name));
+    /// @param addr is the IPv6 prefix of the pool.
+    /// @param len is the prefix length.
+    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
+    /// passed in as an int32_t and cast to Pool6Type to accommodate a 
+    /// polymorphic interface.
+    /// @return returns a PoolPtr to the new Pool4 object. 
+    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t ptype)
+    {
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+                                  (ptype), addr, len)));
     }
 
-private:
-    /// @brief pointer to the actual Pools storage
+    /// @brief Creates a Pool6 object given starting and ending IPv6 addresses.
     ///
-    /// This is typically a storage somewhere in Subnet parser
-    /// (an upper level parser).
-    PoolStorage* pools_;
-    /// A temporary storage for pools configuration. It is a
-    /// storage where pools are stored by build function.
-    PoolStorage local_pools_;
+    /// @param min is the first IPv6 address in the pool.
+    /// @param max is the last IPv6 address in the pool.
+    /// @param ptype is the type of IPv6 pool (Pool6::Pool6Type). Note this is
+    /// passed in as an int32_t and cast to Pool6Type to accommodate a 
+    /// polymorphic interface.
+    /// @return returns a PoolPtr to the new Pool4 object. 
+    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t ptype)
+    {
+        return (PoolPtr(new Pool6(static_cast<isc::dhcp::Pool6::Pool6Type>
+                                  (ptype), min, max)));
+    }
 };
 
-/// @brief this class parses a single subnet
+/// @brief This class parses a single IPv6 subnet.
 ///
-/// This class parses the whole subnet definition. It creates parsers
-/// for received configuration parameters as needed.
-class Subnet6ConfigParser : public DhcpConfigParser {
+/// This is the IPv6 derivation of the SubnetConfigParser class and it parses 
+/// the whole subnet definition. It creates parsersfor received configuration 
+/// parameters as needed.
+class Subnet6ConfigParser : public SubnetConfigParser {
 public:
 
-    /// @brief constructor
-    Subnet6ConfigParser(const std::string& ) {
-        // The parameter should always be "subnet", but we don't check
-        // against that here in case some wants to reuse this parser somewhere.
-    }
-
-    /// @brief parses parameter value
-    ///
-    /// @param subnet pointer to the content of subnet definition
+    /// @brief Constructor
     ///
-    /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
-    void build(ConstElementPtr subnet) {
-
-        BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
-            ParserPtr parser(createSubnet6ConfigParser(param.first));
-            parser->build(param.second);
-            parsers_.push_back(parser);
-        }
-
-        // In order to create new subnet we need to get the data out
-        // of the child parsers first. The only way to do it is to
-        // invoke commit on them because it will make them write
-        // parsed data into storages we have supplied.
-        // Note that triggering commits on child parsers does not
-        // affect global data because we supplied pointers to storages
-        // local to this object. Thus, even if this method fails
-        // later on, the configuration remains consistent.
-        BOOST_FOREACH(ParserPtr parser, parsers_) {
-            parser->commit();
-        }
-
-        // Create a subnet.
-        createSubnet();
+    /// @param ignored first parameter
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions.
+    Subnet6ConfigParser(const std::string&, ParserContextPtr global_context) 
+        :SubnetConfigParser("", global_context) {
     }
 
     /// @brief Adds the created subnet to a server's configuration.
     void commit() {
         if (subnet_) {
-            isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
+            Subnet6Ptr bs = boost::dynamic_pointer_cast<Subnet6>(subnet_);
+            isc::dhcp::CfgMgr::instance().addSubnet6(bs);
         }
     }
 
-private:
+protected:
 
-    /// @brief Append sub-options to an option.
+    /// @brief creates parsers for entries in subnet definition
     ///
-    /// @param option_space a name of the encapsulated option space.
-    /// @param option option instance to append sub-options to.
-    void appendSubOptions(const std::string& option_space, OptionPtr& option) {
-        // Only non-NULL options are stored in option container.
-        // If this option pointer is NULL this is a serious error.
-        assert(option);
-
-        OptionDefinitionPtr def;
-        if (option_space == "dhcp6" &&
-            LibDHCP::isStandardOption(Option::V6, option->getType())) {
-            def = LibDHCP::getOptionDef(Option::V6, option->getType());
-            // Definitions for some of the standard options hasn't been
-            // implemented so it is ok to leave here.
-            if (!def) {
-                return;
-            }
+    /// @param config_id name of the entry
+    ///
+    /// @return parser object for specified entry name. Note the caller is
+    /// responsible for deleting the parser created.
+    /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+    /// for unknown config element
+    DhcpConfigParser* createSubnetConfigParser(const std::string& config_id) {
+        DhcpConfigParser* parser = NULL;
+        if ((config_id.compare("preferred-lifetime") == 0)  ||
+            (config_id.compare("valid-lifetime") == 0)  ||
+            (config_id.compare("renew-timer") == 0)  ||
+            (config_id.compare("rebind-timer") == 0))  {
+            parser = new Uint32Parser(config_id, uint32_values_);
+        } else if ((config_id.compare("subnet") == 0) ||
+                 (config_id.compare("interface") == 0)) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("pool") == 0) {
+            parser = new Pool6Parser(config_id, pools_);
+        } else if (config_id.compare("option-data") == 0) {
+           parser = new OptionDataListParser(config_id, options_, 
+                                             global_context_,
+                                             Dhcp6OptionDataParser::factory);
         } else {
-            const OptionDefContainerPtr defs =
-                option_def_intermediate.getItems(option_space);
-            const OptionDefContainerTypeIndex& idx = defs->get<1>();
-            const OptionDefContainerTypeRange& range =
-                idx.equal_range(option->getType());
-            // There is no definition so we have to leave.
-            if (std::distance(range.first, range.second) == 0) {
-                return;
-            }
-
-            def = *range.first;
-
-            // If the definition exists, it must be non-NULL.
-            // Otherwise it is a programming error.
-            assert(def);
+            isc_throw(NotImplemented,
+                "parser error: Subnet6 parameter not supported: " << config_id);
         }
 
-        // We need to get option definition for the particular option space
-        // and code. This definition holds the information whether our
-        // option encapsulates any option space.
-        // Get the encapsulated option space name.
-        std::string encapsulated_space = def->getEncapsulatedSpace();
-        // If option space name is empty it means that our option does not
-        // encapsulate any option space (does not include sub-options).
-        if (!encapsulated_space.empty()) {
-            // Get the sub-options that belong to the encapsulated
-            // option space.
-            const Subnet::OptionContainerPtr sub_opts =
-                option_defaults.getItems(encapsulated_space);
-            // Append sub-options to the option.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
-                if (desc.option) {
-                    option->addOption(desc.option);
-                }
-            }
-        }
+        return (parser);
     }
 
-    /// @brief Create a new subnet using a data from child parsers.
+
+    /// @brief Determines if the given option space name and code describe
+    /// a standard option for the DHCP6 server. 
     ///
-    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
-    void createSubnet() {
-        std::string subnet_txt;
-        try {
-            subnet_txt = string_values_.getParam("subnet");
-        } catch (DhcpConfigError) {
-            // rethrow with precise error
-            isc_throw(DhcpConfigError,
-                      "Mandatory subnet definition in subnet missing");
-        }
+    /// @param option_space is the name of the option space to consider
+    /// @param code is the numeric option code to consider
+    /// @return returns true if the space and code are part of the server's
+    /// standard options.
+    bool isServerStdOption(std::string option_space, uint32_t code) {
+        return ((option_space.compare("dhcp6") == 0) 
+                && LibDHCP::isStandardOption(Option::V6, code));
+    }
 
-        // Remove any spaces or tabs.
-        boost::erase_all(subnet_txt, " ");
-        boost::erase_all(subnet_txt, "\t");
-
-        // The subnet format is prefix/len. We are going to extract
-        // the prefix portion of a subnet string to create IOAddress
-        // object from it. IOAddress will be passed to the Subnet's
-        // constructor later on. In order to extract the prefix we
-        // need to get all characters preceding "/".
-        size_t pos = subnet_txt.find("/");
-        if (pos == string::npos) {
-            isc_throw(DhcpConfigError,
-                      "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
-        }
+    /// @brief Returns the option definition for a given option code from
+    /// the DHCP6 server's standard set of options.
+    /// @param code is the numeric option code of the desired option definition.
+    /// @return returns a pointer the option definition
+    OptionDefinitionPtr getServerStdOptionDefinition (uint32_t code) {
+        return (LibDHCP::getOptionDef(Option::V6, code));
+    }
 
-        // Try to create the address object. It also validates that
-        // the address syntax is ok.
-        IOAddress addr(subnet_txt.substr(0, pos));
-        uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+    /// @brief Issues a DHCP6 server specific warning regarding duplicate subnet
+    /// options. 
+    /// 
+    /// @param code is the numeric option code of the duplicate option
+    /// @param addr is the subnet address
+    /// @todo A means to know the correct logger and perhaps a common
+    /// message would allow this message to be emitted by the base class.
+    virtual void duplicate_option_warning(uint32_t code, 
+                                         isc::asiolink::IOAddress& addr) {
+        LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
+            .arg(code).arg(addr.toText());
+    }
 
+    /// @brief Instantiates the IPv6 Subnet based on a given IPv6 address
+    /// and prefix length.  
+    /// 
+    /// @param addr is IPv6 prefix of the subnet.
+    /// @param len is the prefix length 
+    void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) {
         // Get all 'time' parameters using inheritance.
         // If the subnet-specific value is defined then use it, else
         // use the global value. The global value must always be
@@ -364,15 +278,6 @@ private:
         Triplet<uint32_t> pref = getParam("preferred-lifetime");
         Triplet<uint32_t> valid = getParam("valid-lifetime");
 
-        // Get interface name. If it is defined, then the subnet is available
-        // directly over specified network interface.
-        std::string iface;
-        try {
-            iface = string_values_.getParam("interface");
-        } catch (DhcpConfigError) {
-            // iface not mandatory so swallow the exception
-        }
-
         /// @todo: Convert this to logger once the parser is working reliably
         stringstream tmp;
         tmp << addr.toText() << "/" << (int)len
@@ -383,166 +288,12 @@ private:
 
         // Create a new subnet.
         subnet_.reset(new Subnet6(addr, len, t1, t2, pref, valid));
-
-        // Add pools to it.
-        for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
-            subnet_->addPool(*it);
-        }
-
-        // Configure interface, if defined
-        if (!iface.empty()) {
-            if (!IfaceMgr::instance().getIface(iface)) {
-                isc_throw(DhcpConfigError, "Specified interface name " << iface
-                          << " for subnet " << subnet_->toText() << " is not present"
-                          << " in the system.");
-            }
-
-            subnet_->setIface(iface);
-        }
-
-        // We are going to move configured options to the Subnet object.
-        // Configured options reside in the container where options
-        // are grouped by space names. Thus we need to get all space names
-        // and iterate over all options that belong to them.
-        std::list<std::string> space_names = options_.getOptionSpaceNames();
-        BOOST_FOREACH(std::string option_space, space_names) {
-            // Get all options within a particular option space.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc,
-                          *options_.getItems(option_space)) {
-                // The pointer should be non-NULL. The validation is expected
-                // to be performed by the OptionDataParser before adding an
-                // option descriptor to the container.
-                assert(desc.option);
-                // We want to check whether an option with the particular
-                // option code has been already added. If so, we want
-                // to issue a warning.
-                Subnet::OptionDescriptor existing_desc =
-                    subnet_->getOptionDescriptor("option_space",
-                                                 desc.option->getType());
-                if (existing_desc.option) {
-                    LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
-                        .arg(desc.option->getType()).arg(addr.toText());
-                }
-                // Add sub-options (if any).
-                appendSubOptions(option_space, desc.option);
-                // In any case, we add the option to the subnet.
-                subnet_->addOption(desc.option, false, option_space);
-            }
-        }
-
-        // Check all global options and add them to the subnet object if
-        // they have been configured in the global scope. If they have been
-        // configured in the subnet scope we don't add global option because
-        // the one configured in the subnet scope always takes precedence.
-        space_names = option_defaults.getOptionSpaceNames();
-        BOOST_FOREACH(std::string option_space, space_names) {
-            // Get all global options for the particular option space.
-            BOOST_FOREACH(Subnet::OptionDescriptor desc,
-                          *option_defaults.getItems(option_space)) {
-                // The pointer should be non-NULL. The validation is expected
-                // to be performed by the OptionDataParser before adding an
-                // option descriptor to the container.
-                assert(desc.option);
-                // Check if the particular option has been already added.
-                // This would mean that it has been configured in the
-                // subnet scope. Since option values configured in the
-                // subnet scope take precedence over globally configured
-                // values we don't add option from the global storage
-                // if there is one already.
-                Subnet::OptionDescriptor existing_desc =
-                    subnet_->getOptionDescriptor(option_space, desc.option->getType());
-                if (!existing_desc.option) {
-                    // Add sub-options (if any).
-                    appendSubOptions(option_space, desc.option);
-
-                    subnet_->addOption(desc.option, false, option_space);
-                }
-            }
-        }
-    }
-
-    /// @brief creates parsers for entries in subnet definition
-    ///
-    /// @param config_id name od the entry
-    ///
-    /// @return parser object for specified entry name
-    /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
-    ///        for unknown config element
-    DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
-        DhcpConfigParser *parser = NULL;
-        if ((config_id.compare("preferred-lifetime") == 0)  ||
-            (config_id.compare("valid-lifetime") == 0)  ||
-            (config_id.compare("renew-timer") == 0)  ||
-            (config_id.compare("rebind-timer") == 0))  {
-            parser = new Uint32Parser(config_id, &uint32_values_);
-        }
-        else if ((config_id.compare("subnet") == 0) ||
-                 (config_id.compare("interface") == 0)) {
-            parser = new StringParser(config_id, &string_values_);
-        }
-        else if (config_id.compare("pool") == 0) {
-            parser = new PoolParser(config_id, &pools_);
-        }
-        else if (config_id.compare("option-data") == 0) {
-           parser = new OptionDataListParser(config_id, &options_, 
-                                             &option_def_intermediate,
-                                             Dhcp6OptionDataParser::factory);
-        } else {
-            isc_throw(NotImplemented,
-                "parser error: Subnet6 parameter not supported: " << config_id);
-        }
-
-        return (parser);
-    }
-
-    /// @brief Returns value for a given parameter (after using inheritance)
-    ///
-    /// This method implements inheritance.  For a given parameter name, it first
-    /// checks if there is a global value for it and overwrites it with specific
-    /// value if such value was defined in subnet.
-    ///
-    /// @param name name of the parameter
-    /// @return triplet with the parameter name
-    /// @throw DhcpConfigError when requested parameter is not present
-    isc::dhcp::Triplet<uint32_t> getParam(const std::string& name) {
-        uint32_t value = 0;
-        try {
-            // look for local value 
-            value = uint32_values_.getParam(name);
-        } catch (DhcpConfigError) {
-            try {
-                // no local, use global value 
-                value = uint32_defaults.getParam(name);
-            } catch (DhcpConfigError) {
-                isc_throw(DhcpConfigError, "Mandatory parameter " << name
-                      << " missing (no global default and no subnet-"
-                      << "specific value)");
-            }
-        }
-
-        return (Triplet<uint32_t>(value));
     }
 
-    /// storage for subnet-specific uint32 values
-    Uint32Storage uint32_values_;
-
-    /// storage for subnet-specific integer values
-    StringStorage string_values_;
-
-    /// storage for pools belonging to this subnet
-    PoolStorage pools_;
-
-    /// storage for options belonging to this subnet
-    OptionStorage options_;
-
-    /// parsers are stored here
-    ParserCollection parsers_;
-
-    /// Pointer to the created subnet object.
-    isc::dhcp::Subnet6Ptr subnet_;
 };
 
-/// @brief this class parses a list of subnets
+
+/// @brief this class parses a list of DHCP6 subnets
 ///
 /// This is a wrapper parser that handles the whole list of Subnet6
 /// definitions. It iterates over all entries and creates Subnet6ConfigParser
@@ -552,8 +303,9 @@ public:
 
     /// @brief constructor
     ///
+    /// @param dummy first argument, always ingored. All parsers accept a
+    /// string parameter "name" as their first argument.
     Subnets6ListConfigParser(const std::string&) {
-        /// parameter name is ignored
     }
 
     /// @brief parses contents of the list
@@ -563,13 +315,9 @@ public:
     ///
     /// @param subnets_list pointer to a list of IPv6 subnets
     void build(ConstElementPtr subnets_list) {
-
-        // No need to define FactoryMap here. There's only one type
-        // used: Subnet6ConfigParser
-
         BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
-
-            ParserPtr parser(new Subnet6ConfigParser("subnet"));
+            ParserPtr parser(new Subnet6ConfigParser("subnet", 
+                                                    global_context_ptr));
             parser->build(subnet);
             subnets_.push_back(parser);
         }
@@ -578,8 +326,8 @@ public:
 
     /// @brief commits subnets definitions.
     ///
-    /// Iterates over all Subnet6 parsers. Each parser contains definitions
-    /// of a single subnet and its parameters and commits each subnet separately.
+    /// Iterates over all Subnet6 parsers. Each parser contains definitions of
+    /// a single subnet and its parameters and commits each subnet separately.
     void commit() {
         // @todo: Implement more subtle reconfiguration than toss
         // the old one and replace with the new one.
@@ -609,34 +357,6 @@ public:
 namespace isc {
 namespace dhcp {
 
-//************** Dhcp6OptionDataParser methods ****************************
-
-Dhcp6OptionDataParser::Dhcp6OptionDataParser(const std::string& param_name, 
-    OptionStorage *options, OptionDefStorage *option_defs)
-    :OptionDataParser(param_name, options, option_defs, Option::V6) {
-}
-
-OptionDataParser* Dhcp6OptionDataParser::factory(const std::string& param_name,
-    OptionStorage *options, OptionDefStorage *option_defs) {
-    return new Dhcp6OptionDataParser(param_name, options, option_defs);
-}
-
-OptionDefinitionPtr Dhcp6OptionDataParser::findServerSpaceOptionDefinition (
-    std::string& option_space, uint32_t option_code) {
-    OptionDefinitionPtr def;
-
-    if (option_space == "dhcp6" &&
-        LibDHCP::isStandardOption(Option::V6, option_code)) {
-        def = LibDHCP::getOptionDef(Option::V6, option_code);
-
-    } else if (option_space == "dhcp4") {
-        isc_throw(DhcpConfigError, "'dhcp4' option space name is reserved"
-                << " for DHCPv4 server");
-    }
-
-    return def;
-}
-
 /// @brief creates global parsers
 ///
 /// This method creates global parsers that parse global parameters, i.e.
@@ -644,36 +364,34 @@ OptionDefinitionPtr Dhcp6OptionDataParser::findServerSpaceOptionDefinition (
 ///
 /// @param config_id pointer to received global configuration entry
 /// @return parser for specified global DHCPv6 parameter
-/// @throw NotImplemented if trying to create a parser for unknown config element
-DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
-    DhcpConfigParser *parser = NULL;
+/// @throw NotImplemented if trying to create a parser for unknown config 
+/// element
+DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
     if ((config_id.compare("preferred-lifetime") == 0)  ||
         (config_id.compare("valid-lifetime") == 0)  ||
         (config_id.compare("renew-timer") == 0)  ||
         (config_id.compare("rebind-timer") == 0))  {
-        parser = new Uint32Parser(config_id, &uint32_defaults);
-    }
-    else if (config_id.compare("interface") == 0) {
+        parser = new Uint32Parser(config_id, 
+                                 global_context_ptr->uint32_values_);
+    } else if (config_id.compare("interface") == 0) {
         parser = new InterfaceListConfigParser(config_id);
-    }
-    else if (config_id.compare("subnet6") == 0) {
+    } else if (config_id.compare("subnet6") == 0) {
         parser = new Subnets6ListConfigParser(config_id);
-    }
-    else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, &option_defaults, 
-                                          &option_def_intermediate,
+    } else if (config_id.compare("option-data") == 0) {
+        parser = new OptionDataListParser(config_id, 
+                                          global_context_ptr->options_, 
+                                          global_context_ptr,
                                           Dhcp6OptionDataParser::factory);
-    }
-    else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, &option_def_intermediate);
-    }
-    else if (config_id.compare("version") == 0) {
-        parser  = new StringParser(config_id, &string_defaults);
-    }
-    else if (config_id.compare("lease-database") == 0) {
+    } else if (config_id.compare("option-def") == 0) {
+        parser  = new OptionDefListParser(config_id, 
+                                          global_context_ptr->option_defs_);
+    } else if (config_id.compare("version") == 0) {
+        parser  = new StringParser(config_id, 
+                                   global_context_ptr->string_values_);
+    } else if (config_id.compare("lease-database") == 0) {
         parser = new DbAccessParser(config_id);
-    }
-    else {
+    } else {
         isc_throw(NotImplemented,
                 "Parser error: Global configuration parameter not supported: "
                 << config_id);
@@ -682,26 +400,27 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
     return (parser);
 }
 
-ConstElementPtr
-configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
+isc::data::ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     if (!config_set) {
         ConstElementPtr answer = isc::config::createAnswer(1,
                                  string("Can't parse NULL config"));
         return (answer);
     }
 
-    /// @todo: append most essential info here (like "2 new subnets configured")
+    /// @todo: Append most essential info here (like "2 new subnets configured")
     string config_details;
 
-    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str());
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, 
+              DHCP6_CONFIG_START).arg(config_set->str());
 
     // Some of the values specified in the configuration depend on
-    // other values. Typically, the values in the subnet4 structure
+    // other values. Typically, the values in the subnet6 structure
     // depend on the global values. Also, option values configuration
     // must be performed after the option definitions configurations.
     // Thus we group parsers and will fire them in the right order:
-    // all parsers other than subnet4 and option-data parser,
-    // option-data parser, subnet4 parser.
+    // all parsers other than subnet6 and option-data parser,
+    // option-data parser, subnet6 parser.
     ParserCollection independent_parsers;
     ParserPtr subnet_parser;
     ParserPtr option_parser;
@@ -713,10 +432,7 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
     // parsing operation fails after the global storage has been
     // modified. We need to preserve the original global data here
     // so as we can rollback changes when an error occurs.
-    Uint32Storage uint32_local(uint32_defaults);
-    StringStorage string_local(string_defaults);
-    OptionStorage option_local(option_defaults);
-    OptionDefStorage option_def_local(option_def_intermediate);
+    ParserContext original_context(*global_context_ptr);
 
     // answer will hold the result.
     ConstElementPtr answer;
@@ -733,15 +449,13 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
         const std::map<std::string, ConstElementPtr>& values_map =
             config_set->mapValue();
         BOOST_FOREACH(config_pair, values_map) {
-            ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
+            ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first));
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
                       .arg(config_pair.first);
             if (config_pair.first == "subnet6") {
                 subnet_parser = parser;
-
             } else if (config_pair.first == "option-data") {
                 option_parser = parser;
-
             } else {
                 // Those parsers should be started before other
                 // parsers so we can call build straight away.
@@ -814,10 +528,7 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
 
     // Rollback changes as the configuration parsing failed.
     if (rollback) {
-        std::swap(uint32_defaults, uint32_local);
-        std::swap(string_defaults, string_local);
-        std::swap(option_defaults, option_local);
-        std::swap(option_def_intermediate, option_def_local);
+        global_context_ptr.reset(new ParserContext(original_context));
         return (answer);
     }
 
@@ -828,5 +539,10 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
     return (answer);
 }
 
+// Makes global context accessible for unit tests.
+const ParserContext& getGlobalParserContext() {
+    return (*global_context_ptr);
+}
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 10 - 44
src/bin/dhcp6/config_parser.h

@@ -31,9 +31,9 @@ class Dhcpv6Srv;
 
 /// @brief Configures DHCPv6 server
 ///
-/// This function is called every time a new configuration is received. The extra
-/// parameter is a reference to DHCPv6 server component. It is currently not used
-/// and CfgMgr::instance() is accessed instead.
+/// This function is called every time a new configuration is received. The 
+/// extra parameter is a reference to DHCPv6 server component. It is currently 
+/// not used and CfgMgr::instance() is accessed instead.
 ///
 /// This method does not throw. It catches all exceptions and returns them as
 /// reconfiguration statuses. It may return the following response codes:
@@ -49,48 +49,14 @@ class Dhcpv6Srv;
 isc::data::ConstElementPtr
 configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
 
-/// @brief Parser for DHCP6 option data value.
+/// @brief Returns the global context
 ///
-/// This parser parses configuration entries that specify value of
-/// a single option specific to DHCP6.  It provides the DHCP6-specific
-/// implementation of the abstract class OptionDataParser.
-class Dhcp6OptionDataParser : public OptionDataParser {
-public:
-    /// @brief Constructor.
-    ///
-    /// Class constructor.
-    Dhcp6OptionDataParser(const std::string&, OptionStorage *options,
-        OptionDefStorage *option_defs);
-
-    /// @brief static factory method for instantiating Dhcp4OptionDataParsers
-    ///
-    /// @param param_name name fo the parameter to be parsed.
-    /// @param options storage where the parameter value is to be stored.
-    /// @param global_option_defs global option definitions storage 
-    static OptionDataParser* factory(const std::string& param_name, OptionStorage *options,
-        OptionDefStorage *global_option_defs);
-
-protected:
-    /// @brief Finds an option definition within the server's option space
-    /// 
-    /// Given an option space and an option code, find the correpsonding 
-    /// option defintion within the server's option defintion storage.
-    ///
-    /// @param option_space name of the parameter option space 
-    /// @param option_code numeric value of the parameter to find 
-    /// @return OptionDefintionPtr of the option defintion or an 
-    /// empty OptionDefinitionPtr if not found.
-    /// @throw DhcpConfigError if the option space requested is not valid 
-    /// for this server. 
-    virtual OptionDefinitionPtr findServerSpaceOptionDefinition (
-        std::string& option_space, uint32_t option_code);
-
-private:
-    // Private default Constructor declared for safety.
-    Dhcp6OptionDataParser() :OptionDataParser("",NULL,NULL,Option::V6) {}
-};
-
-
+/// This function must be only used by unit tests that need
+/// to access global context.
+///
+/// @returns a const reference to the global context
+const ParserContext& getGlobalParserContext();
+ 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 

+ 4 - 1
src/lib/dhcpsrv/dhcp_config_parser.h

@@ -147,7 +147,7 @@ class ValueStorage {
         ///
         /// @param name is the name of the paramater to store.
         /// @param value is the data value to store.
-        void setParam(const std::string name, const ValueType& value) {
+        void setParam(const std::string& name, const ValueType& value) {
             values_[name] = value;
         }
 
@@ -196,12 +196,15 @@ class ValueStorage {
 
 /// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
 typedef ValueStorage<uint32_t> Uint32Storage;
+typedef boost::shared_ptr<Uint32Storage> Uint32StoragePtr;
 
 /// @brief a collection of elements that store string values
 typedef ValueStorage<std::string> StringStorage;
+typedef boost::shared_ptr<StringStorage> StringStoragePtr;
 
 /// @brief Storage for parsed boolean values.
 typedef ValueStorage<bool> BooleanStorage;
+typedef boost::shared_ptr<BooleanStorage> BooleanStoragePtr;
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 410 - 64
src/lib/dhcpsrv/dhcp_parsers.cc

@@ -12,9 +12,10 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <dhcpsrv/dhcp_parsers.h>
+#include <dhcp/iface_mgr.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 
@@ -32,10 +33,25 @@ using namespace isc::data;
 namespace isc {
 namespace dhcp {
 
-// Pointers to various parser objects.
-typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
-typedef boost::shared_ptr<StringParser> StringParserPtr;
-typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
+// *********************** ParserContext  *************************
+
+ParserContext::ParserContext(Option::Universe universe):
+        boolean_values_(new BooleanStorage()),
+        uint32_values_(new Uint32Storage()),
+        string_values_(new StringStorage()),
+        options_(new OptionStorage()),
+        option_defs_(new OptionDefStorage()),
+        universe_(universe) {
+    }
+
+ParserContext::ParserContext(ParserContext& rhs):
+        boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
+        uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
+        string_values_(new StringStorage(*(rhs.string_values_))),
+        options_(new OptionStorage(*(rhs.options_))),
+        option_defs_(new OptionDefStorage(*(rhs.option_defs_))),
+        universe_(rhs.universe_) {
+    }
 
 // **************************** DebugParser *************************
 
@@ -58,8 +74,10 @@ void DebugParser::commit() {
 }
 
 // **************************** BooleanParser  *************************
-BooleanParser::BooleanParser(const std::string& param_name, BooleanStorage *storage)
-    : storage_(storage), param_name_(param_name), value_(false) {
+
+BooleanParser::BooleanParser(const std::string& param_name, 
+                            BooleanStoragePtr storage_)
+    : storage_(storage_), param_name_(param_name), value_(false) {
     // Empty parameter name is invalid.
     if (param_name_.empty()) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
@@ -89,7 +107,7 @@ void BooleanParser::commit() {
 
 // **************************** Uin32Parser  *************************
 
-Uint32Parser::Uint32Parser(const std::string& param_name, Uint32Storage *storage)
+Uint32Parser::Uint32Parser(const std::string& param_name, Uint32StoragePtr storage)
     : storage_(storage), param_name_(param_name) {
     // Empty parameter name is invalid.
     if (param_name_.empty()) {
@@ -134,7 +152,7 @@ void Uint32Parser::commit() {
 
 // **************************** StringParser  *************************
 
-StringParser::StringParser(const std::string& param_name, StringStorage *storage)
+StringParser::StringParser(const std::string& param_name, StringStoragePtr storage)
     :storage_(storage), param_name_(param_name) {
     // Empty parameter name is invalid.
     if (param_name_.empty()) {
@@ -181,17 +199,21 @@ void InterfaceListConfigParser::commit() {
 }
 
 // **************************** OptionDataParser *************************
-
-OptionDataParser::OptionDataParser(const std::string&, OptionStorage *options,
-    OptionDefStorage *option_defs, Option::Universe universe)
-        : options_(options),
-          // initialize option to NULL ptr
-          option_descriptor_(false), global_option_defs_(option_defs),
-          universe_(universe) {
+OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
+                                  ParserContextPtr global_context)
+    : boolean_values_(new BooleanStorage()), 
+    string_values_(new StringStorage()), uint32_values_(new Uint32Storage()), 
+    options_(options), option_descriptor_(false), 
+    global_context_(global_context) {
     if (!options_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
              << "options storage may not be NULL");
     }
+
+    if (!global_context_) {
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+             << "context may may not be NULL");
+    }
 }
 
 void OptionDataParser::build(ConstElementPtr option_data_entries) {
@@ -199,13 +221,16 @@ void OptionDataParser::build(ConstElementPtr option_data_entries) {
         ParserPtr parser;
         if (param.first == "name" || param.first == "data" ||
             param.first == "space") {
-            StringParserPtr name_parser(new StringParser(param.first, &string_values_)); 
+            StringParserPtr name_parser(new StringParser(param.first, 
+                                        string_values_)); 
             parser = name_parser;
         } else if (param.first == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(param.first, &uint32_values_)); 
+            Uint32ParserPtr code_parser(new Uint32Parser(param.first, 
+                                       uint32_values_)); 
             parser = code_parser;
         } else if (param.first == "csv-format") {
-            BooleanParserPtr value_parser(new BooleanParser(param.first, &boolean_values_)); 
+            BooleanParserPtr value_parser(new BooleanParser(param.first, 
+                                         boolean_values_)); 
             parser = value_parser;
         } else {
             isc_throw(DhcpConfigError,
@@ -229,8 +254,8 @@ void OptionDataParser::build(ConstElementPtr option_data_entries) {
 
 void OptionDataParser::commit() {
     if (!option_descriptor_.option) {
-        // Before we can commit the new option should be configured. If it is not
-        // than somebody must have called commit() before build().
+        // Before we can commit the new option should be configured. If it is 
+        // not than somebody must have called commit() before build().
         isc_throw(isc::InvalidOperation, 
             "parser logic error: no option has been configured and"
             " thus there is nothing to commit. Has build() been called?");
@@ -259,7 +284,7 @@ void OptionDataParser::createOption() {
     // Option code is held in the uint32_t storage but is supposed to
     // be uint16_t value. We need to check that value in the configuration
     // does not exceed range of uint8_t and is not zero.
-    uint32_t option_code = uint32_values_.getParam("code");
+    uint32_t option_code = uint32_values_->getParam("code");
     if (option_code == 0) {
         isc_throw(DhcpConfigError, "option code must not be zero."
                 << " Option code '0' is reserved in DHCPv4.");
@@ -271,7 +296,7 @@ void OptionDataParser::createOption() {
 
     // Check that the option name has been specified, is non-empty and does not
     // contain spaces
-    std::string option_name = string_values_.getParam("name"); 
+    std::string option_name = string_values_->getParam("name"); 
     if (option_name.empty()) {
         isc_throw(DhcpConfigError, "name of the option with code '"
                 << option_code << "' is empty");
@@ -280,7 +305,7 @@ void OptionDataParser::createOption() {
                 << "', space character is not allowed");
     }
 
-    std::string option_space = string_values_.getParam("space"); 
+    std::string option_space = string_values_->getParam("space"); 
     if (!OptionSpace::validateName(option_space)) {
         isc_throw(DhcpConfigError, "invalid option space name '"
                 << option_space << "' specified for option '"
@@ -296,7 +321,8 @@ void OptionDataParser::createOption() {
         // need to search for its definition among user-configured
         // options. They are expected to be in the global storage
         // already.
-        OptionDefContainerPtr defs = global_option_defs_->getItems(option_space);
+        OptionDefContainerPtr defs = 
+            global_context_->option_defs_->getItems(option_space);
 
         // The getItems() should never return the NULL pointer. If there are
         // no option definitions for the particular option space a pointer
@@ -316,8 +342,8 @@ void OptionDataParser::createOption() {
     }
 
     // Get option data from the configuration database ('data' field).
-    const std::string option_data = string_values_.getParam("data");
-    const bool csv_format = boolean_values_.getParam("csv-format");
+    const std::string option_data = string_values_->getParam("data");
+    const bool csv_format = boolean_values_->getParam("csv-format");
 
     // Transform string of hexadecimal digits into binary format.
     std::vector<uint8_t> binary;
@@ -349,16 +375,17 @@ void OptionDataParser::createOption() {
                       << " does not have a definition.");
         }
 
-        // @todo We have a limited set of option definitions intiialized at the moment.
-        // In the future we want to initialize option definitions for all options.
-        // Consequently an error will be issued if an option definition does not exist
-        // for a particular option code. For now it is ok to create generic option
-        // if definition does not exist.
-        OptionPtr option(new Option(universe_, static_cast<uint16_t>(option_code),
-                                        binary));
-        // The created option is stored in option_descriptor_ class member until the
-        // commit stage when it is inserted into the main storage. If an option with the
-        // same code exists in main storage already the old option is replaced.
+        // @todo We have a limited set of option definitions intiialized at 
+        // the moment.  In the future we want to initialize option definitions 
+        // for all options.  Consequently an error will be issued if an option 
+        // definition does not exist for a particular option code. For now it is
+        // ok to create generic option if definition does not exist.
+        OptionPtr option(new Option(global_context_->universe_, 
+                        static_cast<uint16_t>(option_code), binary));
+        // The created option is stored in option_descriptor_ class member 
+        // until the commit stage when it is inserted into the main storage. 
+        // If an option with the same code exists in main storage already the 
+        // old option is replaced.
         option_descriptor_.option = option;
         option_descriptor_.persistent = false;
     } else {
@@ -379,8 +406,10 @@ void OptionDataParser::createOption() {
         // an instance of our option.
         try {
             OptionPtr option = csv_format ?
-                def->optionFactory(universe_, option_code, data_tokens) :
-                def->optionFactory(universe_, option_code, binary);
+                def->optionFactory(global_context_->universe_, 
+                                  option_code, data_tokens) :
+                def->optionFactory(global_context_->universe_, 
+                                  option_code, binary);
             Subnet::OptionDescriptor desc(option, false);
             option_descriptor_.option = option;
             option_descriptor_.persistent = false;
@@ -397,29 +426,33 @@ void OptionDataParser::createOption() {
 }
 
 // **************************** OptionDataListParser *************************
-
 OptionDataListParser::OptionDataListParser(const std::string&, 
-    OptionStorage *storage, OptionDefStorage *global_option_defs,
-    OptionDataParserFactory *optionDataParserFactory)
-    : options_(storage), local_options_(), 
-    global_option_defs_(global_option_defs),
+    OptionStoragePtr options, ParserContextPtr global_context,
+    OptionDataParserFactory* optionDataParserFactory)
+    : options_(options), local_options_(new OptionStorage()), 
+    global_context_(global_context),
     optionDataParserFactory_(optionDataParserFactory) {
     if (!options_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
              << "options storage may not be NULL");
     }
 
+    if (!options_) {
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+             << "context may not be NULL");
+    }
+
     if (!optionDataParserFactory_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
              << "option data parser factory may not be NULL");
     }
 }
 
-
 void OptionDataListParser::build(ConstElementPtr option_data_list) {
     BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
         boost::shared_ptr<OptionDataParser> 
-            parser((*optionDataParserFactory_)("option-data", &local_options_, global_option_defs_));
+            parser((*optionDataParserFactory_)("option-data", 
+                    local_options_, global_context_));
 
         // options_ member will hold instances of all options thus
         // each OptionDataParser takes it as a storage.
@@ -438,13 +471,14 @@ void OptionDataListParser::commit() {
     // Parsing was successful and we have all configured
     // options in local storage. We can now replace old values
     // with new values.
-    std::swap(local_options_, *options_);
+    std::swap(*local_options_, *options_);
 }
 
 // ******************************** OptionDefParser ****************************
-
-OptionDefParser::OptionDefParser(const std::string&, OptionDefStorage *storage)
-    : storage_(storage) {
+OptionDefParser::OptionDefParser(const std::string&, 
+                                OptionDefStoragePtr storage)
+    : storage_(storage), boolean_values_(new BooleanStorage()),
+    string_values_(new StringStorage()), uint32_values_(new Uint32Storage()) {
     if (!storage_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
              << "options storage may not be NULL");
@@ -458,13 +492,16 @@ void OptionDefParser::build(ConstElementPtr option_def) {
         ParserPtr parser;
         if (entry == "name" || entry == "type" || entry == "record-types" 
             || entry == "space" || entry == "encapsulate") {
-            StringParserPtr str_parser(new StringParser(entry, &string_values_));
+            StringParserPtr str_parser(new StringParser(entry, 
+                                       string_values_));
             parser = str_parser;
         } else if (entry == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(entry, &uint32_values_));
+            Uint32ParserPtr code_parser(new Uint32Parser(entry, 
+                                        uint32_values_));
             parser = code_parser;
         } else if (entry == "array") {
-            BooleanParserPtr array_parser(new BooleanParser(entry, &boolean_values_));
+            BooleanParserPtr array_parser(new BooleanParser(entry, 
+                                         boolean_values_));
             parser = array_parser;
         } else {
             isc_throw(DhcpConfigError, "invalid parameter: " << entry);
@@ -504,7 +541,7 @@ void OptionDefParser::commit() {
 
 void OptionDefParser::createOptionDef() {
     // Get the option space name and validate it.
-    std::string space = string_values_.getParam("space");
+    std::string space = string_values_->getParam("space");
     if (!OptionSpace::validateName(space)) {
         isc_throw(DhcpConfigError, "invalid option space name '"
                   << space << "'");
@@ -512,11 +549,11 @@ void OptionDefParser::createOptionDef() {
 
     // Get other parameters that are needed to create the
     // option definition.
-    std::string name = string_values_.getParam("name");
-    uint32_t code = uint32_values_.getParam("code");
-    std::string type = string_values_.getParam("type");
-    bool array_type = boolean_values_.getParam("array");
-    std::string encapsulates = string_values_.getParam("encapsulate");
+    std::string name = string_values_->getParam("name");
+    uint32_t code = uint32_values_->getParam("code");
+    std::string type = string_values_->getParam("type");
+    bool array_type = boolean_values_->getParam("array");
+    std::string encapsulates = string_values_->getParam("encapsulate");
 
     // Create option definition.
     OptionDefinitionPtr def;
@@ -547,7 +584,7 @@ void OptionDefParser::createOptionDef() {
 
     // The record-types field may carry a list of comma separated names
     // of data types that form a record.
-    std::string record_types = string_values_.getParam("record-types");
+    std::string record_types = string_values_->getParam("record-types");
 
     // Split the list of record types into tokens.
     std::vector<std::string> record_tokens =
@@ -581,9 +618,8 @@ void OptionDefParser::createOptionDef() {
 }
 
 // ******************************** OptionDefListParser ************************
-
 OptionDefListParser::OptionDefListParser(const std::string&, 
-    OptionDefStorage *storage) :storage_(storage) {
+    OptionDefStoragePtr storage) :storage_(storage) {
     if (!storage_) {
         isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
              << "storage may not be NULL");
@@ -591,7 +627,7 @@ OptionDefListParser::OptionDefListParser(const std::string&,
 }
 
 void OptionDefListParser::build(ConstElementPtr option_def_list) {
-    // Clear existing items in the global storage.
+    // Clear existing items in the storage.
     // We are going to replace all of them.
     storage_->clearItems();
 
@@ -613,7 +649,7 @@ void OptionDefListParser::commit() {
     cfg_mgr.deleteOptionDefs();
 
     // We need to move option definitions from the temporary
-    // storage to the global storage.
+    // storage to the storage.
     std::list<std::string> space_names =
     storage_->getOptionSpaceNames();
 
@@ -629,5 +665,315 @@ void OptionDefListParser::commit() {
     }
 }
 
+//****************************** PoolParser ********************************
+PoolParser::PoolParser(const std::string&,  PoolStoragePtr pools)
+        :pools_(pools) {
+
+    if (!pools_) {
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+                  << "storage may not be NULL");
+    }
+}
+
+void PoolParser::build(ConstElementPtr pools_list) {
+    BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
+        // That should be a single pool representation. It should contain
+        // text is form prefix/len or first - last. Note that spaces
+        // are allowed
+        string txt = text_pool->stringValue();
+
+        // first let's remove any whitespaces
+        boost::erase_all(txt, " "); // space
+        boost::erase_all(txt, "\t"); // tabulation
+
+        // Is this prefix/len notation?
+        size_t pos = txt.find("/");
+        if (pos != string::npos) {
+            isc::asiolink::IOAddress addr("::");
+            uint8_t len = 0;
+            try {
+                addr = isc::asiolink::IOAddress(txt.substr(0, pos));
+
+                // start with the first character after /
+                string prefix_len = txt.substr(pos + 1);
+
+                // It is lexical cast to int and then downcast to uint8_t.
+                // Direct cast to uint8_t (which is really an unsigned char)
+                // will result in interpreting the first digit as output
+                // value and throwing exception if length is written on two
+                // digits (because there are extra characters left over).
+    
+                // No checks for values over 128. Range correctness will
+                // be checked in Pool4 constructor.
+                len = boost::lexical_cast<int>(prefix_len);
+            } catch (...)  {
+                isc_throw(DhcpConfigError, "Failed to parse pool "
+                          "definition: " << text_pool->stringValue());
+            }
+
+            PoolPtr pool(poolMaker(addr, len));
+            local_pools_.push_back(pool);
+            continue;
+        }
+
+        // Is this min-max notation?
+        pos = txt.find("-");
+        if (pos != string::npos) {
+            // using min-max notation
+            isc::asiolink::IOAddress min(txt.substr(0,pos));
+            isc::asiolink::IOAddress max(txt.substr(pos + 1));
+
+            PoolPtr pool(poolMaker(min, max));
+            local_pools_.push_back(pool);
+            continue;
+        }
+
+        isc_throw(DhcpConfigError, "Failed to parse pool definition:"
+                  << text_pool->stringValue() <<
+                  ". Does not contain - (for min-max) nor / (prefix/len)");
+        }
+    }
+
+void PoolParser::commit() {
+    if (pools_) {
+        // local_pools_ holds the values produced by the build function.
+        // At this point parsing should have completed successfuly so
+        // we can append new data to the supplied storage.
+        pools_->insert(pools_->end(), local_pools_.begin(), local_pools_.end());
+    }
+}
+
+//****************************** SubnetConfigParser *************************
+
+SubnetConfigParser::SubnetConfigParser(const std::string&, 
+                                       ParserContextPtr global_context) 
+    : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()), 
+    pools_(new PoolStorage()), options_(new OptionStorage()),
+    global_context_(global_context) {
+    // The first parameter should always be "subnet", but we don't check
+    // against that here in case some wants to reuse this parser somewhere.
+    if (!global_context_) {
+        isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+                 << "context storage may not be NULL");
+    }
+}
+
+void SubnetConfigParser::build(ConstElementPtr subnet) {
+    BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
+        ParserPtr parser(createSubnetConfigParser(param.first));
+        parser->build(param.second);
+        parsers_.push_back(parser);
+    }
+
+    // In order to create new subnet we need to get the data out
+    // of the child parsers first. The only way to do it is to
+    // invoke commit on them because it will make them write
+    // parsed data into storages we have supplied.
+    // Note that triggering commits on child parsers does not
+    // affect global data because we supplied pointers to storages
+    // local to this object. Thus, even if this method fails
+    // later on, the configuration remains consistent.
+    BOOST_FOREACH(ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+
+    // Create a subnet.
+    createSubnet();
+}
+
+void SubnetConfigParser::appendSubOptions(const std::string& option_space, 
+                                         OptionPtr& option) {
+    // Only non-NULL options are stored in option container.
+    // If this option pointer is NULL this is a serious error.
+    assert(option);
+
+    OptionDefinitionPtr def;
+    if (isServerStdOption(option_space, option->getType())) {
+        def = getServerStdOptionDefinition(option->getType());
+        // Definitions for some of the standard options hasn't been
+        // implemented so it is ok to leave here.
+        if (!def) {
+            return;
+        }
+    } else {
+        const OptionDefContainerPtr defs =
+                global_context_->option_defs_->getItems(option_space);
+
+        const OptionDefContainerTypeIndex& idx = defs->get<1>();
+        const OptionDefContainerTypeRange& range =
+        idx.equal_range(option->getType());
+        // There is no definition so we have to leave.
+        if (std::distance(range.first, range.second) == 0) {
+            return;
+        }
+
+        def = *range.first;
+
+        // If the definition exists, it must be non-NULL.
+        // Otherwise it is a programming error.
+        assert(def);
+    }
+
+    // We need to get option definition for the particular option space
+    // and code. This definition holds the information whether our
+    // option encapsulates any option space.
+    // Get the encapsulated option space name.
+    std::string encapsulated_space = def->getEncapsulatedSpace();
+    // If option space name is empty it means that our option does not
+    // encapsulate any option space (does not include sub-options).
+    if (!encapsulated_space.empty()) {
+        // Get the sub-options that belong to the encapsulated
+        // option space.
+        const Subnet::OptionContainerPtr sub_opts =
+                global_context_->options_->getItems(encapsulated_space);
+        // Append sub-options to the option.
+        BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
+            if (desc.option) {
+                option->addOption(desc.option);
+            }
+        }
+    }
+}
+
+void SubnetConfigParser::createSubnet() {
+    std::string subnet_txt;
+    try {
+        subnet_txt = string_values_->getParam("subnet");
+    } catch (DhcpConfigError) {
+        // rethrow with precise error
+        isc_throw(DhcpConfigError,
+                 "Mandatory subnet definition in subnet missing");
+    }
+
+    // Remove any spaces or tabs.
+    boost::erase_all(subnet_txt, " ");
+    boost::erase_all(subnet_txt, "\t");
+
+    // The subnet format is prefix/len. We are going to extract
+    // the prefix portion of a subnet string to create IOAddress
+    // object from it. IOAddress will be passed to the Subnet's
+    // constructor later on. In order to extract the prefix we
+    // need to get all characters preceding "/".
+    size_t pos = subnet_txt.find("/");
+    if (pos == string::npos) {
+        isc_throw(DhcpConfigError,
+                  "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
+    }
+
+    // Try to create the address object. It also validates that
+    // the address syntax is ok.
+    isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
+    uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+    // Call the subclass's method to instantiate the subnet 
+    initSubnet(addr, len);
+
+    // Add pools to it.
+    for (PoolStorage::iterator it = pools_->begin(); it != pools_->end(); 
+         ++it) {
+        subnet_->addPool(*it);
+    }
+
+    // Configure interface, if defined
+
+    // Get interface name. If it is defined, then the subnet is available
+    // directly over specified network interface.
+    std::string iface;
+    try {
+        iface = string_values_->getParam("interface");
+    } catch (DhcpConfigError) {
+        // iface not mandatory so swallow the exception
+    }
+
+    if (!iface.empty()) {
+        if (!IfaceMgr::instance().getIface(iface)) {
+            isc_throw(DhcpConfigError, "Specified interface name " << iface
+                     << " for subnet " << subnet_->toText()
+                     << " is not present" << " in the system.");
+        }
+
+        subnet_->setIface(iface);
+    }
+
+    // We are going to move configured options to the Subnet object.
+    // Configured options reside in the container where options
+    // are grouped by space names. Thus we need to get all space names
+    // and iterate over all options that belong to them.
+    std::list<std::string> space_names = options_->getOptionSpaceNames();
+    BOOST_FOREACH(std::string option_space, space_names) {
+        // Get all options within a particular option space.
+        BOOST_FOREACH(Subnet::OptionDescriptor desc,
+                      *options_->getItems(option_space)) {
+            // The pointer should be non-NULL. The validation is expected
+            // to be performed by the OptionDataParser before adding an
+            // option descriptor to the container.
+            assert(desc.option);
+            // We want to check whether an option with the particular
+            // option code has been already added. If so, we want
+            // to issue a warning.
+            Subnet::OptionDescriptor existing_desc =
+                            subnet_->getOptionDescriptor("option_space",
+                                                 desc.option->getType());
+            if (existing_desc.option) {
+                duplicate_option_warning(desc.option->getType(), addr);
+            }
+            // Add sub-options (if any).
+            appendSubOptions(option_space, desc.option);
+            // In any case, we add the option to the subnet.
+            subnet_->addOption(desc.option, false, option_space);
+        }
+    }
+
+    // Check all global options and add them to the subnet object if
+    // they have been configured in the global scope. If they have been
+    // configured in the subnet scope we don't add global option because
+    // the one configured in the subnet scope always takes precedence.
+    space_names = global_context_->options_->getOptionSpaceNames();
+    BOOST_FOREACH(std::string option_space, space_names) {
+        // Get all global options for the particular option space.
+        BOOST_FOREACH(Subnet::OptionDescriptor desc,
+                *(global_context_->options_->getItems(option_space))) {
+            // The pointer should be non-NULL. The validation is expected
+            // to be performed by the OptionDataParser before adding an
+            // option descriptor to the container.
+            assert(desc.option);
+            // Check if the particular option has been already added.
+            // This would mean that it has been configured in the
+            // subnet scope. Since option values configured in the
+            // subnet scope take precedence over globally configured
+            // values we don't add option from the global storage
+            // if there is one already.
+            Subnet::OptionDescriptor existing_desc =
+                    subnet_->getOptionDescriptor(option_space, 
+                                                desc.option->getType());
+            if (!existing_desc.option) {
+                // Add sub-options (if any).
+                appendSubOptions(option_space, desc.option);
+                subnet_->addOption(desc.option, false, option_space);
+            }
+        }
+    }
+}
+
+isc::dhcp::Triplet<uint32_t> SubnetConfigParser::getParam(const 
+                                                          std::string& name) {
+    uint32_t value = 0;
+    try {
+        // look for local value 
+        value = uint32_values_->getParam(name);
+    } catch (DhcpConfigError) {
+        try {
+            // no local, use global value 
+            value = global_context_->uint32_values_->getParam(name);
+        } catch (DhcpConfigError) {
+            isc_throw(DhcpConfigError, "Mandatory parameter " << name
+                      << " missing (no global default and no subnet-"
+                      << "specific value)");
+        }
+    }
+
+    return (Triplet<uint32_t>(value));
+}
+
 };  // namespace dhcp
 };  // namespace isc

+ 325 - 84
src/lib/dhcpsrv/dhcp_parsers.h

@@ -15,6 +15,7 @@
 #ifndef DHCP_PARSERS_H
 #define DHCP_PARSERS_H
 
+#include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <dhcp/option_definition.h>
 #include <dhcpsrv/dhcp_config_parser.h>
@@ -33,12 +34,56 @@ namespace dhcp {
 typedef OptionSpaceContainer<OptionDefContainer,
                              OptionDefinitionPtr> OptionDefStorage;
 
+/// @brief Shared pointer to  option definitions storage.
+typedef boost::shared_ptr<OptionDefStorage> OptionDefStoragePtr;
+
 /// Collection of containers holding option spaces. Each container within
 /// a particular option space holds so-called option descriptors.
 typedef OptionSpaceContainer<Subnet::OptionContainer,
                              Subnet::OptionDescriptor> OptionStorage;
+/// @brief Shared pointer to  option storage.
+typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
+
+
+/// @brief Container for the current parsing context. It provides a
+/// single enclosure for the storage of configuration paramaters,
+/// options, option definitions, and other context specific information
+/// that needs to be accessible throughout the parsing and parsing
+/// constructs.
+class ParserContext {
+public:
+    /// @brief Constructor
+    /// 
+    /// @param universe is the Option::Universe value of this
+    /// context. 
+    ParserContext(Option::Universe universe);
+
+    /// @brief Copy constructor
+    ParserContext(ParserContext& rhs);
+
+    /// @brief Storage for boolean parameters.
+    BooleanStoragePtr boolean_values_;
+
+    /// @brief Storage for uint32 parameters.
+    Uint32StoragePtr uint32_values_;
+
+    /// @brief Storage for string parameters.
+    StringStoragePtr string_values_;
+
+    /// @brief Storage for options.
+    OptionStoragePtr options_;
+
+    /// @brief Storage for option definitions.
+    OptionDefStoragePtr option_defs_;
 
-/// @brief a dummy configuration parser
+    /// @brief The parsing universe of this context.
+    Option::Universe universe_;
+};
+
+// Pointers to various parser objects.
+typedef boost::shared_ptr<ParserContext> ParserContextPtr;
+
+//brief a dummy configuration parser
 ///
 /// It is a debugging parser. It does not configure anything,
 /// will accept any configuration and will just print it out
@@ -89,7 +134,10 @@ public:
     /// @brief Constructor.
     ///
     /// @param param_name name of the parameter.
-    BooleanParser(const std::string& param_name, BooleanStorage *storage);
+    /// @param storage is a pointer to the storage container where the parsed
+    /// value be stored upon commit.
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    BooleanParser(const std::string& param_name, BooleanStoragePtr storage);
 
     /// @brief Parse a boolean value.
     ///
@@ -106,23 +154,21 @@ public:
 
 private:
     /// Pointer to the storage where parsed value is stored.
-    BooleanStorage* storage_;
+    BooleanStoragePtr storage_;
+
     /// Name of the parameter which value is parsed with this parser.
     std::string param_name_;
     /// Parsed value.
     bool value_;
-
-    /// Default constructor is private for safety.
-    BooleanParser(){};
 };
 
 /// @brief Configuration parser for uint32 parameters
 ///
 /// This class is a generic parser that is able to handle any uint32 integer
-/// type. By default it stores the value in external global container
-/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
+/// type. 
+/// Upon commit it stores the value in the external storage passed in
+/// during construction.  
+/// This class follows the parser interface, laid out
 /// in its base class, @ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
@@ -132,7 +178,10 @@ public:
 
     /// @brief constructor for Uint32Parser
     /// @param param_name name of the configuration parameter being parsed
-    Uint32Parser(const std::string& param_name, Uint32Storage *storage);
+    /// @param storage is a pointer to the storage container where the parsed
+    /// value be stored upon commit.
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    Uint32Parser(const std::string& param_name, Uint32StoragePtr storage);
 
     /// @brief Parses configuration configuration parameter as uint32_t.
     ///
@@ -146,25 +195,23 @@ public:
 
 private:
     /// pointer to the storage, where parsed value will be stored
-    Uint32Storage* storage_;
+    Uint32StoragePtr storage_;
 
     /// name of the parameter to be parsed
     std::string param_name_;
 
     /// the actual parsed value
     uint32_t value_;
-
-    /// Default constructor is private for safety.
-    Uint32Parser(){};
 };
 
 /// @brief Configuration parser for string parameters
 ///
 /// This class is a generic parser that is able to handle any string
-/// parameter. By default it stores the value in external global container
-/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
-/// in subnet config), it can be pointed to a different storage, using
-/// setStorage() method. This class follows the parser interface, laid out
+/// parameter.  
+/// Upon commit it stores the value in the external storage passed in
+/// during construction.  
+///
+/// This class follows the parser interface, laid out
 /// in its base class, @ref DhcpConfigParser.
 ///
 /// For overview of usability of this generic purpose parser, see
@@ -173,7 +220,10 @@ class StringParser : public DhcpConfigParser {
 public:
     /// @brief constructor for StringParser
     /// @param param_name name of the configuration parameter being parsed
-    StringParser(const std::string& param_name, StringStorage *storage);
+    /// @param storage is a pointer to the storage container where the parsed
+    /// value be stored upon commit.
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    StringParser(const std::string& param_name, StringStoragePtr storage);
 
     /// @brief parses parameter value
     ///
@@ -188,16 +238,13 @@ public:
 
 private:
     /// pointer to the storage, where parsed value will be stored
-    StringStorage* storage_;
+    StringStoragePtr storage_;
 
     /// name of the parameter to be parsed
     std::string param_name_;
 
     /// the actual parsed value
     std::string value_;
-
-    /// Default constructor is private for safety.
-    StringParser(){};
 };
 
 /// @brief parser for interface list definition
@@ -234,9 +281,6 @@ public:
 private:
     /// contains list of network interfaces
     std::vector<std::string> interfaces_;
-
-    /// Default constructor is private for safety.
-    InterfaceListConfigParser(){};
 };
 
 
@@ -262,9 +306,15 @@ class OptionDataParser : public DhcpConfigParser {
 public:
     /// @brief Constructor.
     ///
-    /// Class constructor.
-    OptionDataParser(const std::string&, OptionStorage *options, 
-        OptionDefStorage *option_defs, Option::Universe universe);
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param options is the option storage in which to store the parsed option
+    /// upon "commit". 
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions. 
+    /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
+    OptionDataParser(const std::string&, OptionStoragePtr options, 
+                    ParserContextPtr global_context);
 
     /// @brief Parses the single option data.
     ///
@@ -273,9 +323,6 @@ public:
     /// carried by this option. Eventually it creates the instance of the
     /// option.
     ///
-    /// @warning setStorage must be called with valid storage pointer prior
-    /// to calling this method.
-    ///
     /// @param option_data_entries collection of entries that define value
     /// for a particular option.
     /// @throw DhcpConfigError if invalid parameter specified in
@@ -286,10 +333,11 @@ public:
 
     /// @brief Commits option value.
     ///
-    /// This function adds a new option to the storage or replaces an existing option
-    /// with the same code.
+    /// This function adds a new option to the storage or replaces an existing 
+    /// option with the same code.
     ///
-    /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
+    /// @throw isc::InvalidOperation if failed to set pointer to storage or 
+    /// failed
     /// to call build() prior to commit. If that happens data in the storage
     /// remain un-modified.
     virtual void commit();
@@ -330,33 +378,33 @@ private:
     /// are invalid.
     void createOption();
 
-    /// Storage for uint32 values (e.g. option code).
-    Uint32Storage uint32_values_;
-    /// Storage for string values (e.g. option name or data).
-    StringStorage string_values_;
     /// Storage for boolean values.
-    BooleanStorage boolean_values_;
+    BooleanStoragePtr boolean_values_;
+
+    /// Storage for string values (e.g. option name or data).
+    StringStoragePtr string_values_;
+
+    /// Storage for uint32 values (e.g. option code).
+    Uint32StoragePtr uint32_values_;
+
     /// Pointer to options storage. This storage is provided by
     /// the calling class and is shared by all OptionDataParser objects.
-    OptionStorage* options_;
+    OptionStoragePtr options_;
+
     /// Option descriptor holds newly configured option.
     Subnet::OptionDescriptor option_descriptor_;
+
     /// Option space name where the option belongs to.
     std::string option_space_;
 
-    /// Option definition storage
-    OptionDefStorage *global_option_defs_;
-
-    /// Option::Universe for this parser's option
-    Option::Universe universe_;
-
-    /// Default constructor is private for safety.
-    OptionDataParser():option_descriptor_(false){};
+    /// Parsing context which contains global values, options and option 
+    /// definitions.
+    ParserContextPtr global_context_;
 };
 
 ///@brief Function pointer for OptionDataParser factory methods
-typedef OptionDataParser *OptionDataParserFactory(const std::string&, OptionStorage *options, 
-        OptionDefStorage *option_defs);
+typedef OptionDataParser *OptionDataParserFactory(const std::string&, 
+                     OptionStoragePtr options, ParserContextPtr global_context);
 
 /// @brief Parser for option data values within a subnet.
 ///
@@ -369,13 +417,15 @@ public:
     /// @brief Constructor.
     ///
     /// @param string& nominally would be param name, this is always ignored.
-    /// @param storage parsed option storage for options in this list
-    /// @param global_option_defs global set of option definitions to reference
-    /// @param optionDataParserFactory factory method for creating individual option
-    /// parsers 
-    OptionDataListParser(const std::string&, OptionStorage *storage, 
-        OptionDefStorage *global_option_defs,
-        OptionDataParserFactory *optionDataParserFactory);
+    /// @param options parsed option storage for options in this list
+    /// @param global_context is a pointer to the global context which 
+    /// stores global scope parameters, options, option defintions. 
+    /// @param optionDataParserFactory factory method for creating individual 
+    /// option parsers 
+    /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
+    OptionDataListParser(const std::string&, OptionStoragePtr options, 
+                        ParserContextPtr global_context, 
+                        OptionDataParserFactory *optionDataParserFactory);
 
     /// @brief Parses entries that define options' data for a subnet.
     ///
@@ -393,23 +443,23 @@ public:
 
 private:
     /// Pointer to options instances storage.
-    OptionStorage* options_;
+    OptionStoragePtr options_;
+
     /// Intermediate option storage. This storage is used by
     /// lower level parsers to add new options.  Values held
     /// in this storage are assigned to main storage (options_)
     /// if overall parsing was successful.
-    OptionStorage local_options_;
+    OptionStoragePtr local_options_;
+
     /// Collection of parsers;
     ParserCollection parsers_;
 
-    /// Global option definitions
-    OptionDefStorage *global_option_defs_;
+    /// Parsing context which contains global values, options and option 
+    /// definitions.
+    ParserContextPtr global_context_;
 
     /// Factory to create server-specific option data parsers
     OptionDataParserFactory *optionDataParserFactory_;
-
-    /// Default constructor is private for safety.
-    OptionDataListParser(){};
 };
 
 
@@ -420,10 +470,12 @@ class OptionDefParser : public DhcpConfigParser {
 public:
     /// @brief Constructor.
     ///
-    /// This constructor sets the pointer to the option definitions
-    /// storage to NULL. It must be set to point to the actual storage
-    /// before \ref build is called.
-    OptionDefParser(const std::string&, OptionDefStorage *storage);
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param storage is the definition storage in which to store the parsed 
+    /// definition upon "commit". 
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    OptionDefParser(const std::string&, OptionDefStoragePtr storage);
 
     /// @brief Parses an entry that describes single option definition.
     ///
@@ -447,17 +499,16 @@ private:
 
     /// Pointer to a storage where the option definition will be
     /// added when \ref commit is called.
-    OptionDefStorage* storage_;
+    OptionDefStoragePtr storage_;
 
     /// Storage for boolean values.
-    BooleanStorage boolean_values_;
+    BooleanStoragePtr boolean_values_;
+
     /// Storage for string values.
-    StringStorage string_values_;
-    /// Storage for uint32 values.
-    Uint32Storage uint32_values_;
+    StringStoragePtr string_values_;
 
-    // Default constructor is private for safety.
-    OptionDefParser(){};
+    /// Storage for uint32 values.
+    Uint32StoragePtr uint32_values_;
 };
 
 /// @brief Parser for a list of option definitions.
@@ -470,10 +521,12 @@ class OptionDefListParser : public DhcpConfigParser {
 public:
     /// @brief Constructor.
     ///
-    /// This constructor initializes the pointer to option definitions
-    /// storage to NULL value. This pointer has to be set to point to
-    /// the actual storage before the \ref build function is called.
-    OptionDefListParser(const std::string&, OptionDefStorage *storage);
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param storage is the definition storage in which to store the parsed 
+    /// definitions in this list 
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    OptionDefListParser(const std::string&, OptionDefStoragePtr storage);
 
     /// @brief Parse configuration entries.
     ///
@@ -490,13 +543,201 @@ public:
 
 private:
     /// @brief storage for option definitions.
-    OptionDefStorage *storage_; 
+    OptionDefStoragePtr storage_; 
+};
+
+/// @brief a collection of pools
+///
+/// That type is used as intermediate storage, when pools are parsed, but there is
+/// no subnet object created yet to store them.
+typedef std::vector<PoolPtr> PoolStorage;
+typedef boost::shared_ptr<PoolStorage> PoolStoragePtr;
+
+/// @brief parser for pool definition
+///
+/// This abstract parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// As there are no default values for pool, setStorage() must be called
+/// before build(). Otherwise exception will be thrown.
+///
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[X]/pool parameters.
+class PoolParser : public DhcpConfigParser {
+public:
+
+    /// @brief constructor.
+   
+
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param pools is the storage in which to store the parsed pool 
+    /// upon "commit". 
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    PoolParser(const std::string&,  PoolStoragePtr pools);
+
+    /// @brief parses the actual list
+    ///
+    /// This method parses the actual list of interfaces.
+    /// No validation is done at this stage, everything is interpreted as
+    /// interface name.
+    /// @param pools_list list of pools defined for a subnet
+    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
+    virtual void build(isc::data::ConstElementPtr pools_list);
+
+    /// @brief Stores the parsed values in a storage provided
+    ///        by an upper level parser.
+    virtual void commit();
+
+protected:
+    /// @brief Creates a Pool object given a IPv4 prefix and the prefix length.
+    ///
+    /// @param addr is the IP  prefix of the pool.
+    /// @param len is the prefix length.
+    /// @param ignored dummy parameter to provide symmetry between 
+    /// @return returns a PoolPtr to the new Pool object. 
+    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &addr, uint32_t len, 
+                           int32_t ptype=0) = 0;
+
+    /// @brief Creates a Pool object given starting and ending IP addresses.
+    ///
+    /// @param min is the first IP address in the pool.
+    /// @param max is the last IP address in the pool.
+    /// @param ptype is the type of pool to create (not used by all derivations)
+    /// @return returns a PoolPtr to the new Pool object.
+    virtual PoolPtr poolMaker(isc::asiolink::IOAddress &min, 
+                           isc::asiolink::IOAddress &max, int32_t ptype=0) = 0;
+
+    /// @brief pointer to the actual Pools storage
+    ///
+    /// That is typically a storage somewhere in Subnet parser
+    /// (an upper level parser).
+    PoolStoragePtr pools_;
+
+    /// A temporary storage for pools configuration. It is a
+    /// storage where pools are stored by build function.
+    PoolStorage local_pools_;
+};
+
+/// @brief this class parses a single subnet
+///
+/// This class parses the whole subnet definition. It creates parsers
+/// for received configuration parameters as needed.
+class SubnetConfigParser : public DhcpConfigParser {
+public:
+
+    /// @brief constructor
+    SubnetConfigParser(const std::string&, ParserContextPtr global_context);
+
+    /// @brief parses parameter value
+    ///
+    /// @param subnet pointer to the content of subnet definition
+    ///
+    /// @throw isc::DhcpConfigError if subnet configuration parsing failed.
+    virtual void build(isc::data::ConstElementPtr subnet); 
+
+    /// @brief Adds the created subnet to a server's configuration.
+    virtual void commit() = 0;
+
+protected:
+    /// @brief creates parsers for entries in subnet definition
+    ///
+    /// @param config_id name od the entry
+    ///
+    /// @return parser object for specified entry name
+    /// @throw isc::dhcp::DhcpConfigError if trying to create a parser
+    ///        for unknown config element
+    virtual DhcpConfigParser* createSubnetConfigParser(
+                                            const std::string& config_id) = 0;
+
+    /// @brief Determines if the given option space name and code describe
+    /// a standard option for the  server. 
+    ///
+    /// @param option_space is the name of the option space to consider
+    /// @param code is the numeric option code to consider
+    /// @return returns true if the space and code are part of the server's
+    /// standard options.
+    virtual bool isServerStdOption(std::string option_space, uint32_t code) = 0;
+
+    /// @brief Returns the option definition for a given option code from
+    /// the server's standard set of options.
+    /// @param code is the numeric option code of the desired option definition.
+    /// @return returns a pointer the option definition
+    virtual OptionDefinitionPtr getServerStdOptionDefinition (
+                                                             uint32_t code) = 0;
+
+    /// @brief Issues a server specific warning regarding duplicate subnet
+    /// options. 
+    /// 
+    /// @param code is the numeric option code of the duplicate option
+    /// @param addr is the subnet address 
+    /// @todo a means to know the correct logger and perhaps a common
+    /// message would allow this method to be emitted by the base class.
+    virtual void duplicate_option_warning(uint32_t code, 
+        isc::asiolink::IOAddress& addr) = 0;
+
+    /// @brief Instantiates the subnet based on a given IP prefix and prefix 
+    /// length.  
+    /// 
+    /// @param addr is the IP prefix of the subnet.
+    /// @param len is the prefix length 
+    virtual void initSubnet(isc::asiolink::IOAddress addr, uint8_t len) = 0;
+
+    /// @brief Returns value for a given parameter (after using inheritance)
+    ///
+    /// This method implements inheritance. For a given parameter name, it first
+    /// checks if there is a global value for it and overwrites it with specific
+    /// value if such value was defined in subnet.
+    ///
+    /// @param name name of the parameter
+    /// @return triplet with the parameter name
+    /// @throw DhcpConfigError when requested parameter is not present
+    isc::dhcp::Triplet<uint32_t> getParam(const std::string& name);
+
+private:
+
+    /// @brief Append sub-options to an option.
+    ///
+    /// @param option_space a name of the encapsulated option space.
+    /// @param option option instance to append sub-options to.
+    void appendSubOptions(const std::string& option_space, OptionPtr& option);
+
+    /// @brief Create a new subnet using a data from child parsers.
+    ///
+    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing 
+    /// failed.
+    void createSubnet();
+
+protected:
+
+    /// Storage for subnet-specific integer values.
+    Uint32StoragePtr uint32_values_;
 
-    // Default constructor is private for safety.
-    OptionDefListParser(){};
+    /// Storage for subnet-specific string values.
+    StringStoragePtr string_values_;
 
+    /// Storage for pools belonging to this subnet.
+    PoolStoragePtr pools_;
+
+    /// Storage for options belonging to this subnet.
+    OptionStoragePtr options_;
+
+    /// Parsers are stored here.
+    ParserCollection parsers_;
+
+    /// Pointer to the created subnet object.
+    isc::dhcp::SubnetPtr subnet_;
+
+    /// Parsing context which contains global values, options and option 
+    /// definitions.
+    ParserContextPtr global_context_;
 };
 
+// Pointers to various parser objects.
+typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
+typedef boost::shared_ptr<StringParser> StringParserPtr;
+typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
 

+ 11 - 2
src/lib/dhcpsrv/subnet.cc

@@ -135,6 +135,15 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
     return (candidate);
 }
 
+void Subnet::setIface(const std::string& iface_name) {
+    iface_ = iface_name;
+}
+
+std::string Subnet::getIface() const {
+    return (iface_);
+}
+
+
 
 void
 Subnet4::validateOption(const OptionPtr& option) const {
@@ -183,7 +192,7 @@ Subnet6::validateOption(const OptionPtr& option) const {
     }
 }
 
-
+#if 0
 void Subnet6::setIface(const std::string& iface_name) {
     iface_ = iface_name;
 }
@@ -191,7 +200,7 @@ void Subnet6::setIface(const std::string& iface_name) {
 std::string Subnet6::getIface() const {
     return (iface_);
 }
-
+#endif
 
 } // end of isc::dhcp namespace
 } // end of isc namespace

+ 12 - 13
src/lib/dhcpsrv/subnet.h

@@ -299,7 +299,18 @@ public:
         return pools_;
     }
 
-    /// @brief returns textual representation of the subnet (e.g. "2001:db8::/64")
+    /// @brief sets name of the network interface for directly attached networks
+    ///
+    /// @param iface_name name of the interface
+    void setIface(const std::string& iface_name);
+
+    /// @brief network interface name used to reach subnet (or "" for remote 
+    /// subnets)
+    /// @return network interface name for directly attached subnets or ""
+    std::string getIface() const;
+
+    /// @brief returns textual representation of the subnet (e.g. 
+    /// "2001:db8::/64")
     ///
     /// @return textual representation
     virtual std::string toText() const;
@@ -451,18 +462,6 @@ public:
         return (preferred_);
     }
 
-    /// @brief sets name of the network interface for directly attached networks
-    ///
-    /// A subnet may be reachable directly (not via relays). In DHCPv6 it is not
-    /// possible to decide that based on addresses assigned to network interfaces,
-    /// as DHCPv6 operates on link-local (and site local) addresses.
-    /// @param iface_name name of the interface
-    void setIface(const std::string& iface_name);
-
-    /// @brief network interface name used to reach subnet (or "" for remote subnets)
-    /// @return network interface name for directly attached subnets or ""
-    std::string getIface() const;
-
 protected:
 
     /// @brief Check if option is valid and can be added to a subnet.