]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5152] Done (at the exception of unit tests)
authorFrancis Dupont <fdupont@isc.org>
Sat, 11 Mar 2017 11:52:56 +0000 (12:52 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sat, 11 Mar 2017 11:52:56 +0000 (12:52 +0100)
17 files changed:
doc/guide/ddns.xml
src/bin/agent/kea-ctrl-agent.xml
src/bin/agent/main.cc
src/bin/d2/d2_process.cc
src/bin/d2/kea-dhcp-ddns.xml
src/bin/d2/main.cc
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/d2_controller_unittests.cc
src/bin/d2/tests/get_config_unittest.cc
src/lib/process/d_cfg_mgr.cc
src/lib/process/d_cfg_mgr.h
src/lib/process/d_controller.cc
src/lib/process/d_controller.h
src/lib/process/tests/d_cfg_mgr_unittests.cc
src/lib/process/tests/d_controller_unittests.cc
src/lib/process/testutils/d_test_stubs.cc
src/lib/process/testutils/d_test_stubs.h

index 1479feddb1f0f55900f91f89f6b3cca46228fe9b..c32d2444096de6101fc2e9369baba33c520fac2c 100644 (file)
           </listitem>
           <listitem>
             <simpara>
-              <command>-W</command> - prints out Kea configuration report
-              and exits.
-            </simpara>
+            <command>-t <replaceable>file</replaceable></command>
+            specifies the configuration file to be tested. Kea-dhcp-ddns
+            will attempt to load it, and will conduct sanity checks.
+            Note that certain checks are possible only while running
+            the actual server. The actual status is reported with exit
+            code (0 = configuration looks ok, 1 = error encountered).
+            Kea will print out log messages to standard output and error
+            to standard error when testing configuration.</simpara>
           </listitem>
       </itemizedlist>
 
@@ -152,9 +157,9 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
     <section id="d2-configuration">
       <title>Configuring the DHCP-DDNS Server</title>
       <para>
-       Before starting <command>kea-dhcp-ddns</command> module for the
-       first time, a configuration file needs to be created. The following default
-       configuration is a template that can be customised to your requirements.
+        Before starting <command>kea-dhcp-ddns</command> module for the
+        first time, a configuration file needs to be created. The following default
+        configuration is a template that can be customised to your requirements.
 <screen>
 <userinput>"DhcpDdns": {
     "ip-address": "127.0.0.1",
@@ -164,10 +169,10 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
     "ncr-format": "JSON",
     "tsig-keys": [ ],
     "forward-ddns": {
-       "ddns-domains": [ ]
+        "ddns-domains": [ ]
     },
     "reverse-ddns": {
-       "ddns-domains": [ ]
+        "ddns-domains": [ ]
     }
 }</userinput>
 </screen>
@@ -176,30 +181,30 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
       The configuration can be divided as follows, each of which is described
       in its own section:
       </para>
-       <itemizedlist>
-         <listitem>
-           <simpara>
+        <itemizedlist>
+          <listitem>
+            <simpara>
         <emphasis>Global Server Parameters</emphasis> - values which control connectivity and global server behavior
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <emphasis>TSIG Key Info</emphasis> - defines the TSIG keys used for secure traffic with DNS servers
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <emphasis>Forward DDNS</emphasis> - defines the catalog of Forward DDNS Domains
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <emphasis>Reverse DDNS</emphasis> - defines the catalog of Forward DDNS Domains
-           </simpara>
-         </listitem>
-       </itemizedlist>
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <emphasis>TSIG Key Info</emphasis> - defines the TSIG keys used for secure traffic with DNS servers
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <emphasis>Forward DDNS</emphasis> - defines the catalog of Forward DDNS Domains
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <emphasis>Reverse DDNS</emphasis> - defines the catalog of Forward DDNS Domains
+            </simpara>
+          </listitem>
+        </itemizedlist>
       <section id="d2-server-parameter-config">
-       <title>Global Server Parameters</title>
+        <title>Global Server Parameters</title>
       <itemizedlist>
 
       <listitem><simpara>
@@ -231,11 +236,11 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
       </simpara></listitem>
 
       </itemizedlist>
-       <para>
-       D2 must listen for change requests on a known address and port.  By
-       default it listens at 127.0.0.1 on port 53001. The following example
-       illustrates how to change D2's global parameters so it will listen
-       at 192.168.1.10 port 900:
+        <para>
+        D2 must listen for change requests on a known address and port.  By
+        default it listens at 127.0.0.1 on port 53001. The following example
+        illustrates how to change D2's global parameters so it will listen
+        at 192.168.1.10 port 900:
 <screen>
 "DhcpDdns": {
     <userinput>"ip-address": "192.168.1.10",
@@ -243,19 +248,19 @@ strings <userinput>path</userinput>/kea-dhcp-ddns | sed -n 's/;;;; //p'
     ...
     }
 }</screen>
-       </para>
-       <warning>
-         <simpara>
-           It is possible for a malicious attacker to send bogus
-           NameChangeRequests to the DHCP-DDNS server.  Addresses
-           other than the IPv4 or IPv6 loopback addresses (127.0.0.1
-           or ::1) should only be used for testing purposes, but
-           note that local users may still communicate with the
-           DHCP-DDNS server.  A future version of Kea will implement
-           authentication to guard against such attacks.
-         </simpara>
+        </para>
+        <warning>
+          <simpara>
+            It is possible for a malicious attacker to send bogus
+            NameChangeRequests to the DHCP-DDNS server.  Addresses
+            other than the IPv4 or IPv6 loopback addresses (127.0.0.1
+            or ::1) should only be used for testing purposes, but
+            note that local users may still communicate with the
+            DHCP-DDNS server.  A future version of Kea will implement
+            authentication to guard against such attacks.
+          </simpara>
 <!-- see ticket #3514 -->
-       </warning>
+        </warning>
 <note>
 <simpara>
 If the ip-address and port are changed, it will be necessary to change the
@@ -265,96 +270,96 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section.
       </section> <!-- "d2-server-parameter-config" -->
 
       <section id="d2-tsig-key-list-config">
-       <title>TSIG Key List</title>
-       <para>
-       A DDNS protocol exchange can be conducted with or without TSIG
-       (defined in <ulink url="http://tools.ietf/org/html/rfc2845">RFC
-       2845</ulink>). This configuration section allows the administrator
-       to define the set of TSIG keys that may be used in such
-       exchanges.</para>
-
-       <para>To use TSIG when updating entries in a DNS Domain,
-       a key must be defined in the TSIG Key List and referenced by
-       name in that domain's configuration entry.  When D2 matches a
-       change request to a domain, it checks whether the domain has
-       a TSIG key associated with it.  If so, D2 will use that key to
-       sign DNS update messages sent to and verify responses received
-       from the domain's DNS server(s). For each TSIG key required by
-       the DNS servers that D2 will be working with there must be a
-       corresponding TSIG key in the TSIG Key list.</para>
-
-       <para>
-       As one might gather from the name, the tsig-key section of the
-       D2 configuration lists the TSIG keys.  Each entry describes a
-       TSIG key used by one or more DNS servers to authenticate requests
-       and sign responses.  Every entry in the list has three parameters:
-       <itemizedlist>
-         <listitem>
-           <simpara>
-             <command>name</command> -
-             a unique text label used to identify this key within the
-             list.  This value is used to specify which key (if any) should be
-             used when updating a specific domain. So long as it is unique its
-             content is arbitrary, although for clarity and ease of maintenance
-             it is recommended that it match the name used on the DNS server(s).
-             It cannot be blank.
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <command>algorithm</command> -
-             specifies which hashing algorithm should be used with this
-             key.  This value must specify the same algorithm used for the
-             key on the DNS server(s). The supported algorithms are listed below:
-             <itemizedlist>
-               <listitem>
-                  <command>HMAC-MD5</command>
-               </listitem>
-               <listitem>
-                   <command>HMAC-SHA1</command>
-               </listitem>
-               <listitem>
-                 <command>HMAC-SHA224</command>
-             </listitem>
-             <listitem>
-                 <command>HMAC-SHA256</command>
-             </listitem>
-             <listitem>
-                 <command>HMAC-SHA384</command>
-                 </listitem>
-             <listitem>
-                 <command>HMAC-SHA512</command>
-             </listitem>
-             </itemizedlist>
-             This value is not case sensitive.
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <command>digest-bits</command> -
-             is used to specify the minimum truncated length in bits.
-             The default value 0 means truncation is forbidden, non-zero
-             values must be an integral number of octets, be greater
-             than 80 and the half of the full length. Note in BIND9
-             this parameter is appended after a dash to the algorithm
-             name.
-           </simpara>
-         </listitem>
-         <listitem>
-           <simpara>
-             <command>secret</command> -
-             is used to specify the shared secret key code for this key.  This value is
-             case sensitive and must exactly match the value specified on the DNS server(s).
-             It is a base64-encoded text value.
-           </simpara>
-         </listitem>
-       </itemizedlist>
-       </para>
-       <para>
-       As an example, suppose that a domain D2 will be updating is
-       maintained by a BIND9 DNS server which requires dynamic updates
-       to be secured with TSIG.  Suppose further that the entry for
-       the TSIG key in BIND9's named.conf file looks like this:
+        <title>TSIG Key List</title>
+        <para>
+        A DDNS protocol exchange can be conducted with or without TSIG
+        (defined in <ulink url="http://tools.ietf/org/html/rfc2845">RFC
+        2845</ulink>). This configuration section allows the administrator
+        to define the set of TSIG keys that may be used in such
+        exchanges.</para>
+
+        <para>To use TSIG when updating entries in a DNS Domain,
+        a key must be defined in the TSIG Key List and referenced by
+        name in that domain's configuration entry.  When D2 matches a
+        change request to a domain, it checks whether the domain has
+        a TSIG key associated with it.  If so, D2 will use that key to
+        sign DNS update messages sent to and verify responses received
+        from the domain's DNS server(s). For each TSIG key required by
+        the DNS servers that D2 will be working with there must be a
+        corresponding TSIG key in the TSIG Key list.</para>
+
+        <para>
+        As one might gather from the name, the tsig-key section of the
+        D2 configuration lists the TSIG keys.  Each entry describes a
+        TSIG key used by one or more DNS servers to authenticate requests
+        and sign responses.  Every entry in the list has three parameters:
+        <itemizedlist>
+          <listitem>
+            <simpara>
+              <command>name</command> -
+              a unique text label used to identify this key within the
+              list.  This value is used to specify which key (if any) should be
+              used when updating a specific domain. So long as it is unique its
+              content is arbitrary, although for clarity and ease of maintenance
+              it is recommended that it match the name used on the DNS server(s).
+              It cannot be blank.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <command>algorithm</command> -
+              specifies which hashing algorithm should be used with this
+              key.  This value must specify the same algorithm used for the
+              key on the DNS server(s). The supported algorithms are listed below:
+              <itemizedlist>
+                <listitem>
+                   <command>HMAC-MD5</command>
+                </listitem>
+                <listitem>
+                    <command>HMAC-SHA1</command>
+                </listitem>
+                <listitem>
+                  <command>HMAC-SHA224</command>
+              </listitem>
+              <listitem>
+                  <command>HMAC-SHA256</command>
+              </listitem>
+              <listitem>
+                  <command>HMAC-SHA384</command>
+                  </listitem>
+              <listitem>
+                  <command>HMAC-SHA512</command>
+              </listitem>
+              </itemizedlist>
+              This value is not case sensitive.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <command>digest-bits</command> -
+              is used to specify the minimum truncated length in bits.
+              The default value 0 means truncation is forbidden, non-zero
+              values must be an integral number of octets, be greater
+              than 80 and the half of the full length. Note in BIND9
+              this parameter is appended after a dash to the algorithm
+              name.
+            </simpara>
+          </listitem>
+          <listitem>
+            <simpara>
+              <command>secret</command> -
+              is used to specify the shared secret key code for this key.  This value is
+              case sensitive and must exactly match the value specified on the DNS server(s).
+              It is a base64-encoded text value.
+            </simpara>
+          </listitem>
+        </itemizedlist>
+        </para>
+        <para>
+        As an example, suppose that a domain D2 will be updating is
+        maintained by a BIND9 DNS server which requires dynamic updates
+        to be secured with TSIG.  Suppose further that the entry for
+        the TSIG key in BIND9's named.conf file looks like this:
 <screen>
    :
    key "key.four.example.com." {
@@ -363,7 +368,7 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section.
    };
    :
 </screen>
-       By default, the TSIG Key list is empty:
+        By default, the TSIG Key list is empty:
 <screen>
 "DhcpDdns": {
    <userinput>"tsig-keys": [ ]</userinput>,
@@ -371,333 +376,333 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section.
 }
 </screen>
 
-       We must extend the list with a new key:
+        We must extend the list with a new key:
 <screen>
 "DhcpDdns": {
     "tsig-keys": [
     <userinput>    {
-           "name": "key.four.example.com.",
-           "algorithm": "HMAC-SHA224",
-           "secret": "bZEG7Ow8OgAUPfLWV3aAUQ=="
-       }</userinput>
+            "name": "key.four.example.com.",
+            "algorithm": "HMAC-SHA224",
+            "secret": "bZEG7Ow8OgAUPfLWV3aAUQ=="
+        }</userinput>
     ],
     ...
 }
 </screen>
-       </para>
+        </para>
 
-       <para>These steps would be repeated for each TSIG key needed.  Note that
-       the same TSIG key can be used with more than one domain.</para>
+        <para>These steps would be repeated for each TSIG key needed.  Note that
+        the same TSIG key can be used with more than one domain.</para>
       </section>
-       <!-- "d2-tsig-key-list-config" -->
+        <!-- "d2-tsig-key-list-config" -->
 
       <section id="d2-forward-ddns-config">
-       <title>Forward DDNS</title>
-       <para>
-       The Forward DDNS section is used to configure D2's forward update
-       behavior. Currently it contains a single parameter, the catalog of
-       forward DDNS Domains, which is a list of structures.
+        <title>Forward DDNS</title>
+        <para>
+        The Forward DDNS section is used to configure D2's forward update
+        behavior. Currently it contains a single parameter, the catalog of
+        forward DDNS Domains, which is a list of structures.
 <screen>
 "DhcpDdns": {
     <userinput>"forward-ddns": {
-       "ddns-domains": [ ]
+        "ddns-domains": [ ]
     }</userinput>,
     ...
 }
 </screen>
 
-       By default, this list is empty, which will cause the server to ignore
-       the forward update portions of requests.
-       </para>
-       <section id="add-forward-ddns-domain">
-         <title>Adding Forward DDNS Domains</title>
-         <para>
-         A forward DDNS Domain maps a forward DNS zone to a set of
-         DNS servers which maintain the forward DNS data (i.e. name to
-         address mapping) for that zone.  You will need one forward DDNS
-         Domain for each zone you wish to service.  It may very well
-         be that some or all of your zones are maintained by the same
-         servers. You will still need one DDNS Domain per zone. Remember
-         that matching a request to the appropriate server(s) is done
-         by zone and a DDNS Domain only defines a single zone.
-         </para>
-         <para>
-         This section describes how to add Forward DDNS Domains. Repeat these
-         steps for each Forward DDNS Domain desired.  Each Forward DDNS Domain
-         has the following parameters:
-         <itemizedlist>
-           <listitem>
-             <simpara>
-             <command>name</command> -
-             The fully qualified domain name (or zone) that this DDNS Domain
-             can update.  This is value used to compare against the request
-             FQDN during forward matching.  It must be unique within the
-             catalog.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>key-name</command> -
-             If TSIG is used with this domain's servers, this
-             value should be the name of the key from within the TSIG Key List
-             to use.  If the value is blank (the default), TSIG will not be
-             used in DDNS conversations with this domain's servers.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>dns-servers</command> -
-             A list of one or more DNS servers which can conduct the server
-             side of the DDNS protocol for this domain.  The servers
-             are used in a first to last preference. In other words, when D2
-             begins to process a request for this domain it will pick the
-             first server in this list and attempt to communicate with it.
-             If that attempt fails, it will move to next one in the list and
-             so on until the it achieves success or the list is exhausted.
-             </simpara>
-           </listitem>
-         </itemizedlist>
-       To create a new forward DDNS Domain, one must add a new domain
-       element and set its parameters:
+        By default, this list is empty, which will cause the server to ignore
+        the forward update portions of requests.
+        </para>
+        <section id="add-forward-ddns-domain">
+          <title>Adding Forward DDNS Domains</title>
+          <para>
+          A forward DDNS Domain maps a forward DNS zone to a set of
+          DNS servers which maintain the forward DNS data (i.e. name to
+          address mapping) for that zone.  You will need one forward DDNS
+          Domain for each zone you wish to service.  It may very well
+          be that some or all of your zones are maintained by the same
+          servers. You will still need one DDNS Domain per zone. Remember
+          that matching a request to the appropriate server(s) is done
+          by zone and a DDNS Domain only defines a single zone.
+          </para>
+          <para>
+          This section describes how to add Forward DDNS Domains. Repeat these
+          steps for each Forward DDNS Domain desired.  Each Forward DDNS Domain
+          has the following parameters:
+          <itemizedlist>
+            <listitem>
+              <simpara>
+              <command>name</command> -
+              The fully qualified domain name (or zone) that this DDNS Domain
+              can update.  This is value used to compare against the request
+              FQDN during forward matching.  It must be unique within the
+              catalog.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>key-name</command> -
+              If TSIG is used with this domain's servers, this
+              value should be the name of the key from within the TSIG Key List
+              to use.  If the value is blank (the default), TSIG will not be
+              used in DDNS conversations with this domain's servers.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>dns-servers</command> -
+              A list of one or more DNS servers which can conduct the server
+              side of the DDNS protocol for this domain.  The servers
+              are used in a first to last preference. In other words, when D2
+              begins to process a request for this domain it will pick the
+              first server in this list and attempt to communicate with it.
+              If that attempt fails, it will move to next one in the list and
+              so on until the it achieves success or the list is exhausted.
+              </simpara>
+            </listitem>
+          </itemizedlist>
+        To create a new forward DDNS Domain, one must add a new domain
+        element and set its parameters:
 <screen>
 "DhcpDdns": {
     "forward-ddns": {
-       "ddns-domains": [
-           <userinput>{
-               "name": "other.example.com.",
-               "key-name": "",
-               "dns-servers": [
-               ]
-           }</userinput>
-       ]
+        "ddns-domains": [
+            <userinput>{
+                "name": "other.example.com.",
+                "key-name": "",
+                "dns-servers": [
+                ]
+            }</userinput>
+        ]
     }
 }
 </screen>
 
-       It is permissible to add a domain without any servers. If that domain
-       should be matched to a request, however, the request will fail.  In
-       order to make the domain useful though, we must add at least one DNS
-       server to it.
-       </para>
-
-       <section id="add-forward-dns-servers">
-         <title>Adding Forward DNS Servers</title>
-         <para>
-         This section describes how to add DNS servers to a Forward DDNS Domain.
-         Repeat them for as many servers as desired for a each domain.
-         </para>
-         <para>
-         Forward DNS Server entries represent actual DNS servers which
-         support the server side of the DDNS protocol. Each Forward DNS Server
-         has the following parameters:
-         <itemizedlist>
-           <listitem>
-             <simpara>
-             <command>hostname</command> -
-             The resolvable host name of the DNS server. This value is not
-             yet implemented.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>ip-address</command> -
-             The IP address at which the server listens for DDNS requests.
-             This may be either an IPv4 or an IPv6 address.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>port</command> -
-             The port on which the server listens for DDNS requests. It
-             defaults to the standard DNS service port of 53.
-             </simpara>
-           </listitem>
-         </itemizedlist>
-         To create a new forward DNS Server, one must add a new server
-         element to the domain and fill in its parameters.  If for
-       example the service is running at "172.88.99.10", then set it as
-       follows:
+        It is permissible to add a domain without any servers. If that domain
+        should be matched to a request, however, the request will fail.  In
+        order to make the domain useful though, we must add at least one DNS
+        server to it.
+        </para>
+
+        <section id="add-forward-dns-servers">
+          <title>Adding Forward DNS Servers</title>
+          <para>
+          This section describes how to add DNS servers to a Forward DDNS Domain.
+          Repeat them for as many servers as desired for a each domain.
+          </para>
+          <para>
+          Forward DNS Server entries represent actual DNS servers which
+          support the server side of the DDNS protocol. Each Forward DNS Server
+          has the following parameters:
+          <itemizedlist>
+            <listitem>
+              <simpara>
+              <command>hostname</command> -
+              The resolvable host name of the DNS server. This value is not
+              yet implemented.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>ip-address</command> -
+              The IP address at which the server listens for DDNS requests.
+              This may be either an IPv4 or an IPv6 address.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>port</command> -
+              The port on which the server listens for DDNS requests. It
+              defaults to the standard DNS service port of 53.
+              </simpara>
+            </listitem>
+          </itemizedlist>
+          To create a new forward DNS Server, one must add a new server
+          element to the domain and fill in its parameters.  If for
+        example the service is running at "172.88.99.10", then set it as
+        follows:
 <screen>
 "DhcpDdns": {
     "forward-ddns": {
-       "ddns-domains": [
-           {
-               "name": "other.example.com.",
-               "key-name": "",
-               "dns-servers": [
-                   <userinput>{
-                       "hostname": "",
-                       "ip-address": "172.88.99.10",
-                       "port": 53
-                   }</userinput>
-               ]
-           }
-       ]
+        "ddns-domains": [
+            {
+                "name": "other.example.com.",
+                "key-name": "",
+                "dns-servers": [
+                    <userinput>{
+                        "hostname": "",
+                        "ip-address": "172.88.99.10",
+                        "port": 53
+                    }</userinput>
+                ]
+            }
+        ]
     }
 }
 </screen>
-         </para>
+          </para>
 
     <note><simpara>
-       As stated earlier, "hostname" is not yet supported so, the parameter
-       "ip-address" must be set to the address of the DNS server.
+        As stated earlier, "hostname" is not yet supported so, the parameter
+        "ip-address" must be set to the address of the DNS server.
     </simpara></note>
 
-       </section> <!-- "add-forward-dns-servers" -->
+        </section> <!-- "add-forward-dns-servers" -->
 
       </section> <!-- "add-forward-ddns-domains" -->
 
       </section> <!-- "d2-forward-ddns-config" -->
 
       <section id="d2-reverse-ddns-config">
-       <title>Reverse DDNS</title>
-       <para>
-       The Reverse DDNS section is used to configure D2's reverse update
-       behavior, and the concepts are the same as for the forward DDNS
-       section. Currently it contains a single parameter, the catalog of
-       reverse DDNS Domains, which is a list of structures.
+        <title>Reverse DDNS</title>
+        <para>
+        The Reverse DDNS section is used to configure D2's reverse update
+        behavior, and the concepts are the same as for the forward DDNS
+        section. Currently it contains a single parameter, the catalog of
+        reverse DDNS Domains, which is a list of structures.
 <screen>
 "DhcpDdns": {
     <userinput>"reverse-ddns": {
-       "ddns-domains": [ ]
+        "ddns-domains": [ ]
     }</userinput>
     ...
 }
 </screen>
-       By default, this list is empty, which will cause the server to ignore
-       the reverse update portions of requests.
-       </para>
-       <section id="add-reverse-ddns-domain">
-         <title>Adding Reverse DDNS Domains</title>
-         <para>
-         A reverse DDNS Domain maps a reverse DNS zone to a set of DNS
-         servers which maintain the reverse DNS data (address to name
-         mapping) for that zone.  You will need one reverse DDNS Domain
-         for each zone you wish to service.  It may very well be that
-         some or all of your zones are maintained by the same servers;
-         even then, you will still need one DDNS Domain entry for each
-         zone. Remember that matching a request to the appropriate
-         server(s) is done by zone and a DDNS Domain only defines a
-         single zone.
-         </para>
-         <para>
-         This section describes how to add Reverse DDNS Domains. Repeat these
-         steps for each Reverse DDNS Domain desired.  Each Reverse DDNS Domain
-         has the following parameters:
-         <itemizedlist>
-           <listitem>
-             <simpara>
-             <command>name</command> -
-             The fully qualified reverse zone that this DDNS Domain
-             can update.  This is the value used during reverse matching
-             which will compare it with a reversed version of the request's
-             lease address. The zone name should follow the appropriate
-             standards: for example, to to support the IPv4 subnet 172.16.1,
-             the name should be. "1.16.172.in-addr.arpa.".  Similarly,
-             to support an IPv6 subnet of 2001:db8:1, the name should be
-             "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa."
-             Whatever the name, it must be unique within the catalog.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>key-name</command> -
-             If TSIG should be used with this domain's servers, then this
-             value should be the name of that key from the TSIG Key List.
-             If the value is blank (the default), TSIG will not be
-             used in DDNS conversations with this domain's servers.  Currently
-             this value is not used as TSIG has not been implemented.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>dns-servers</command> -
-             a list of one or more DNS servers which can conduct the server
-             side of the DDNS protocol for this domain.  Currently the servers
-             are used in a first to last preference. In other words, when D2
-             begins to process a request for this domain it will pick the
-             first server in this list and attempt to communicate with it.
-             If that attempt fails, it will move to next one in the list and
-             so on until the it achieves success or the list is exhausted.
-             </simpara>
-           </listitem>
-         </itemizedlist>
-       To create a new reverse DDNS Domain, one must add a new domain element
-       and set its parameters. For example, to support subnet 2001:db8:1::,
-       the following configuration could be used:
+        By default, this list is empty, which will cause the server to ignore
+        the reverse update portions of requests.
+        </para>
+        <section id="add-reverse-ddns-domain">
+          <title>Adding Reverse DDNS Domains</title>
+          <para>
+          A reverse DDNS Domain maps a reverse DNS zone to a set of DNS
+          servers which maintain the reverse DNS data (address to name
+          mapping) for that zone.  You will need one reverse DDNS Domain
+          for each zone you wish to service.  It may very well be that
+          some or all of your zones are maintained by the same servers;
+          even then, you will still need one DDNS Domain entry for each
+          zone. Remember that matching a request to the appropriate
+          server(s) is done by zone and a DDNS Domain only defines a
+          single zone.
+          </para>
+          <para>
+          This section describes how to add Reverse DDNS Domains. Repeat these
+          steps for each Reverse DDNS Domain desired.  Each Reverse DDNS Domain
+          has the following parameters:
+          <itemizedlist>
+            <listitem>
+              <simpara>
+              <command>name</command> -
+              The fully qualified reverse zone that this DDNS Domain
+              can update.  This is the value used during reverse matching
+              which will compare it with a reversed version of the request's
+              lease address. The zone name should follow the appropriate
+              standards: for example, to to support the IPv4 subnet 172.16.1,
+              the name should be. "1.16.172.in-addr.arpa.".  Similarly,
+              to support an IPv6 subnet of 2001:db8:1, the name should be
+              "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa."
+              Whatever the name, it must be unique within the catalog.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>key-name</command> -
+              If TSIG should be used with this domain's servers, then this
+              value should be the name of that key from the TSIG Key List.
+              If the value is blank (the default), TSIG will not be
+              used in DDNS conversations with this domain's servers.  Currently
+              this value is not used as TSIG has not been implemented.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>dns-servers</command> -
+              a list of one or more DNS servers which can conduct the server
+              side of the DDNS protocol for this domain.  Currently the servers
+              are used in a first to last preference. In other words, when D2
+              begins to process a request for this domain it will pick the
+              first server in this list and attempt to communicate with it.
+              If that attempt fails, it will move to next one in the list and
+              so on until the it achieves success or the list is exhausted.
+              </simpara>
+            </listitem>
+          </itemizedlist>
+        To create a new reverse DDNS Domain, one must add a new domain element
+        and set its parameters. For example, to support subnet 2001:db8:1::,
+        the following configuration could be used:
 <screen>
 "DhcpDdns": {
     "reverse-ddns": {
-       "ddns-domains": [
-           <userinput>{
-               "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
-               "key-name": "",
-               "dns-servers": [
-               ]
-           }</userinput>
-       ]
+        "ddns-domains": [
+            <userinput>{
+                "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+                "key-name": "",
+                "dns-servers": [
+                ]
+            }</userinput>
+        ]
     }
 }
 </screen>
 
-       It is permissible to add a domain without any servers. If that domain
-       should be matched to a request, however, the request will fail.  In
-       order to make the domain useful though, we must add at least one DNS
-       server to it.
-       </para>
-
-       <section id="add-reverse-dns-servers">
-         <title>Adding Reverse DNS Servers</title>
-         <para>
-         This section describes how to add DNS servers to a Reverse DDNS Domain.
-         Repeat them for as many servers as desired for each domain.
-         </para>
-         <para>
-         Reverse DNS Server entries represents a actual DNS servers which
-         support the server side of the DDNS protocol. Each Reverse DNS Server
-         has the following parameters:
-         <itemizedlist>
-           <listitem>
-             <simpara>
-             <command>hostname</command> -
-             The resolvable host name of the DNS server. This value is
-             currently ignored.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>ip-address</command> -
-             The IP address at which the server listens for DDNS requests.
-             </simpara>
-           </listitem>
-           <listitem>
-             <simpara>
-             <command>port</command> -
-             The port on which the server listens for DDNS requests. It
-             defaults to the standard DNS service port of 53.
-             </simpara>
-           </listitem>
-         </itemizedlist>
-         To create a new reverse DNS Server, one must first add a new server
-         element to the domain and fill in its parameters.  If for
-       example the service is running at "172.88.99.10", then set it as
-       follows:
+        It is permissible to add a domain without any servers. If that domain
+        should be matched to a request, however, the request will fail.  In
+        order to make the domain useful though, we must add at least one DNS
+        server to it.
+        </para>
+
+        <section id="add-reverse-dns-servers">
+          <title>Adding Reverse DNS Servers</title>
+          <para>
+          This section describes how to add DNS servers to a Reverse DDNS Domain.
+          Repeat them for as many servers as desired for each domain.
+          </para>
+          <para>
+          Reverse DNS Server entries represents a actual DNS servers which
+          support the server side of the DDNS protocol. Each Reverse DNS Server
+          has the following parameters:
+          <itemizedlist>
+            <listitem>
+              <simpara>
+              <command>hostname</command> -
+              The resolvable host name of the DNS server. This value is
+              currently ignored.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>ip-address</command> -
+              The IP address at which the server listens for DDNS requests.
+              </simpara>
+            </listitem>
+            <listitem>
+              <simpara>
+              <command>port</command> -
+              The port on which the server listens for DDNS requests. It
+              defaults to the standard DNS service port of 53.
+              </simpara>
+            </listitem>
+          </itemizedlist>
+          To create a new reverse DNS Server, one must first add a new server
+          element to the domain and fill in its parameters.  If for
+        example the service is running at "172.88.99.10", then set it as
+        follows:
 <screen>
 "DhcpDdns": {
     "reverse-ddns": {
-       "ddns-domains": [
-           {
-               "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
-               "key-name": "",
-               "dns-servers": [
-                   <userinput>{
-                       "hostname": "",
-                       "ip-address": "172.88.99.10",
-                       "port": 53
-                   }</userinput>
-               ]
-           }
-       ]
+        "ddns-domains": [
+            {
+                "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+                "key-name": "",
+                "dns-servers": [
+                    <userinput>{
+                        "hostname": "",
+                        "ip-address": "172.88.99.10",
+                        "port": 53
+                    }</userinput>
+                ]
+            }
+        ]
     }
 }
 </screen>
@@ -710,219 +715,219 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section.
     </simpara>
     </note>
 
-       </section> <!-- "add-reverse-dns-servers" -->
+        </section> <!-- "add-reverse-dns-servers" -->
 
       </section> <!-- "add-reverse-ddns-domains" -->
 
       </section> <!-- "d2-reverse-ddns-config" -->
 
       <section id="d2-example-config">
-       <title>Example DHCP-DDNS Server Configuration</title>
-       <para>
-       This section provides an example DHCP-DDNS server configuration based
-       on a small example network.  Let's suppose our example network has
-       three domains, each with their own subnet.
-
-       <table>
-         <title>Our example network</title>
-         <tgroup cols='4' align='left'>
-         <colspec colname='domain'/>
-         <colspec colname='subnet'/>
-         <colspec colname='fservers'/>
-         <colspec colname='rservers'/>
-         <thead>
-           <row>
-             <entry>Domain</entry>
-             <entry>Subnet</entry>
-             <entry>Forward DNS Servers</entry>
-             <entry>Reverse DNS Servers</entry>
-           </row>
-         </thead>
-         <tbody>
-           <row>
-             <entry>four.example.com</entry>
-             <entry>192.0.2.0/24</entry>
-             <entry>172.16.1.5, 172.16.2.5</entry>
-             <entry>172.16.1.5, 172.16.2.5</entry>
-           </row>
-           <row>
-             <entry>six.example.com</entry>
-             <entry>2001:db8:1::/64</entry>
-             <entry>3001:1::50</entry>
-             <entry>3001:1::51</entry>
-           </row>
-           <row>
-             <entry>example.com</entry>
-             <entry>192.0.0.0/16</entry>
-             <entry>172.16.2.5</entry>
-             <entry>172.16.2.5</entry>
-           </row>
-         </tbody>
-         </tgroup>
-       </table>
-       </para>
-       <para>
-       We need to construct three forward DDNS Domains:
-       <table>
-         <title>Forward DDNS Domains Needed</title>
-         <tgroup cols='3' align='left'>
-         <colspec colname='num'/>
-         <colspec colname='name'/>
-         <colspec colname='servers'/>
-         <thead>
-           <row>
-             <entry>#</entry>
-             <entry>DDNS Domain Name</entry>
-             <entry>DNS Servers</entry>
-           </row>
-         </thead>
-         <tbody>
-           <row>
-             <entry>1.</entry>
-             <entry>four.example.com.</entry>
-             <entry>172.16.1.5, 172.16.2.5</entry>
-           </row>
-           <row>
-             <entry>2.</entry>
-             <entry>six.example.com.</entry>
-             <entry>3001:1::50</entry>
-           </row>
-           <row>
-             <entry>3.</entry>
-             <entry>example.com.</entry>
-             <entry>172.16.2.5</entry>
-           </row>
-         </tbody>
-         </tgroup>
-       </table>
-       As discussed earlier, FQDN to domain matching is based on the longest
-       match. The FQDN, "myhost.four.example.com.", will match the first
-       domain ("four.example.com") while "admin.example.com." will match the
-       third domain ("example.com"). The
-       FQDN, "other.example.net." will fail to match any domain and would
-       be rejected.
-       </para>
-       <para>
-       The following example configuration specified the Forward DDNS Domains.
+        <title>Example DHCP-DDNS Server Configuration</title>
+        <para>
+        This section provides an example DHCP-DDNS server configuration based
+        on a small example network.  Let's suppose our example network has
+        three domains, each with their own subnet.
+
+        <table>
+          <title>Our example network</title>
+          <tgroup cols='4' align='left'>
+          <colspec colname='domain'/>
+          <colspec colname='subnet'/>
+          <colspec colname='fservers'/>
+          <colspec colname='rservers'/>
+          <thead>
+            <row>
+              <entry>Domain</entry>
+              <entry>Subnet</entry>
+              <entry>Forward DNS Servers</entry>
+              <entry>Reverse DNS Servers</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>four.example.com</entry>
+              <entry>192.0.2.0/24</entry>
+              <entry>172.16.1.5, 172.16.2.5</entry>
+              <entry>172.16.1.5, 172.16.2.5</entry>
+            </row>
+            <row>
+              <entry>six.example.com</entry>
+              <entry>2001:db8:1::/64</entry>
+              <entry>3001:1::50</entry>
+              <entry>3001:1::51</entry>
+            </row>
+            <row>
+              <entry>example.com</entry>
+              <entry>192.0.0.0/16</entry>
+              <entry>172.16.2.5</entry>
+              <entry>172.16.2.5</entry>
+            </row>
+          </tbody>
+          </tgroup>
+        </table>
+        </para>
+        <para>
+        We need to construct three forward DDNS Domains:
+        <table>
+          <title>Forward DDNS Domains Needed</title>
+          <tgroup cols='3' align='left'>
+          <colspec colname='num'/>
+          <colspec colname='name'/>
+          <colspec colname='servers'/>
+          <thead>
+            <row>
+              <entry>#</entry>
+              <entry>DDNS Domain Name</entry>
+              <entry>DNS Servers</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>1.</entry>
+              <entry>four.example.com.</entry>
+              <entry>172.16.1.5, 172.16.2.5</entry>
+            </row>
+            <row>
+              <entry>2.</entry>
+              <entry>six.example.com.</entry>
+              <entry>3001:1::50</entry>
+            </row>
+            <row>
+              <entry>3.</entry>
+              <entry>example.com.</entry>
+              <entry>172.16.2.5</entry>
+            </row>
+          </tbody>
+          </tgroup>
+        </table>
+        As discussed earlier, FQDN to domain matching is based on the longest
+        match. The FQDN, "myhost.four.example.com.", will match the first
+        domain ("four.example.com") while "admin.example.com." will match the
+        third domain ("example.com"). The
+        FQDN, "other.example.net." will fail to match any domain and would
+        be rejected.
+        </para>
+        <para>
+        The following example configuration specified the Forward DDNS Domains.
 <screen><userinput>
 "DhcpDdns": {
     "forward-ddns": {
-       "ddns-domains": [
-           {
-               "name": "four.example.com.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "172.16.1.5" },
-                   { "ip-address": "172.16.2.5" }
-               ]
-           },
-           {
-               "name": "six.example.com.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "2001:db8::1" }
-               ]
-           },
-           {
-               "name": "example.com.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "172.16.2.5" }
-               ]
-           },
-
-       ]
+        "ddns-domains": [
+            {
+                "name": "four.example.com.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "172.16.1.5" },
+                    { "ip-address": "172.16.2.5" }
+                ]
+            },
+            {
+                "name": "six.example.com.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "2001:db8::1" }
+                ]
+            },
+            {
+                "name": "example.com.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "172.16.2.5" }
+                ]
+            },
+
+        ]
     }
 }</userinput>
 </screen>
 
-       </para>
-       <para>
-       Similarly, we need to construct the three reverse DDNS Domains:
-       <table>
-         <title>Reverse DDNS Domains Needed</title>
-         <tgroup cols='3' align='left'>
-         <colspec colname='num'/>
-         <colspec colname='DDNS Domain name'/>
-         <colspec colname='DDNS Domain DNS Servers'/>
-         <thead>
-           <row>
-             <entry>#</entry>
-             <entry>DDNS Domain Name</entry>
-             <entry>DNS Servers</entry>
-           </row>
-         </thead>
-         <tbody>
-           <row>
-             <entry>1.</entry>
-             <entry>2.0.192.in-addr.arpa.</entry>
-             <entry>172.16.1.5, 172.16.2.5</entry>
-           </row>
-           <row>
-             <entry>2.</entry>
-             <entry>1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa.</entry>
-             <entry>3001:1::50</entry>
-           </row>
-           <row>
-             <entry>3.</entry>
-             <entry>0.182.in-addr.arpa.</entry>
-             <entry>172.16.2.5</entry>
-           </row>
-         </tbody>
-         </tgroup>
-       </table>
-       An address of "192.0.2.150" will match the first domain,
-       "2001:db8:1::10" will match the second domain, and "192.0.50.77"
-       the third domain.
-       </para>
-       <para>
-       These Reverse DDNS Domains are specified as follows:
+        </para>
+        <para>
+        Similarly, we need to construct the three reverse DDNS Domains:
+        <table>
+          <title>Reverse DDNS Domains Needed</title>
+          <tgroup cols='3' align='left'>
+          <colspec colname='num'/>
+          <colspec colname='DDNS Domain name'/>
+          <colspec colname='DDNS Domain DNS Servers'/>
+          <thead>
+            <row>
+              <entry>#</entry>
+              <entry>DDNS Domain Name</entry>
+              <entry>DNS Servers</entry>
+            </row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>1.</entry>
+              <entry>2.0.192.in-addr.arpa.</entry>
+              <entry>172.16.1.5, 172.16.2.5</entry>
+            </row>
+            <row>
+              <entry>2.</entry>
+              <entry>1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa.</entry>
+              <entry>3001:1::50</entry>
+            </row>
+            <row>
+              <entry>3.</entry>
+              <entry>0.182.in-addr.arpa.</entry>
+              <entry>172.16.2.5</entry>
+            </row>
+          </tbody>
+          </tgroup>
+        </table>
+        An address of "192.0.2.150" will match the first domain,
+        "2001:db8:1::10" will match the second domain, and "192.0.50.77"
+        the third domain.
+        </para>
+        <para>
+        These Reverse DDNS Domains are specified as follows:
 
 <screen><userinput>
 "DhcpDdns": {
     "reverse-ddns": {
-       "ddns-domains": [
-           {
-               "name": "2.0.192.in-addr.arpa.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "172.16.1.5" },
-                   { "ip-address": "172.16.2.5" }
-               ]
-           }
-           {
-               "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "2001:db8::1" }
-               ]
-           }
-           {
-               "name": "0.192.in-addr.arpa.",
-               "key-name": "",
-               "dns-servers": [
-                   { "ip-address": "172.16.2.5" }
-               ]
-           }
-       ]
+        "ddns-domains": [
+            {
+                "name": "2.0.192.in-addr.arpa.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "172.16.1.5" },
+                    { "ip-address": "172.16.2.5" }
+                ]
+            }
+            {
+                "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "2001:db8::1" }
+                ]
+            }
+            {
+                "name": "0.192.in-addr.arpa.",
+                "key-name": "",
+                "dns-servers": [
+                    { "ip-address": "172.16.2.5" }
+                ]
+            }
+        ]
     }
 }</userinput>
 </screen>
 
-       </para>
-       </section> <!-- end of "d2-example" -->
+        </para>
+        </section> <!-- end of "d2-example" -->
     </section> <!-- end of section "d2-configuration" -->
     <section>
       <title>DHCP-DDNS Server Limitations</title>
       <para>The following are the current limitations of the DHCP-DDNS Server.</para>
       <itemizedlist>
-       <listitem>
-         <simpara>
-           Requests received from the DHCP servers are placed in a
-           queue until they are processed.  Currently all queued requests
-           are lost when the server shuts down.
-         </simpara>
-       </listitem>
+        <listitem>
+          <simpara>
+            Requests received from the DHCP servers are placed in a
+            queue until they are processed.  Currently all queued requests
+            are lost when the server shuts down.
+          </simpara>
+        </listitem>
       </itemizedlist>
     </section>
   </chapter> <!-- DHCP-DDNS Server -->
index 492a6e693f04c9b80a9e8ada2177db09a63389ac..6fff18e0f986d022be0d1a41b5490fce77ab414d 100644 (file)
@@ -52,7 +52,8 @@
       <arg><option>-V</option></arg>
       <arg><option>-W</option></arg>
       <arg><option>-d</option></arg>
-      <arg><option>-s</option></arg>
+      <arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-t</option></term>
+        <listitem><para>
+          Check the syntax of the configuration file and report the
+          first error if any. Note that not all parameters are
+          completely checked, in particular, service and client
+          sockets are not opened, and hook libraries are not loaded.
+        </para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index c74329ba128a6385809494499ff365e969fbcd8d..be43065b2914200f1dd434276aa233b43b453e24 100644 (file)
@@ -25,9 +25,18 @@ int main(int argc, char* argv[]) {
         // 'false' value disables test mode.
         controller->launch(argc, argv, false);
     } catch (const VersionMessage& ex) {
-        std::cout << ex.what() << std::endl;
+        std::string msg(ex.what());
+        if (!msg.empty()) {
+            std::cout << msg << std::endl;
+        }
+    } catch (const InvalidUsage& ex) {
+        std::string msg(ex.what());
+        if (!msg.empty()) {
+            std::cerr << msg << std::endl;
+        }
+        ret = EXIT_FAILURE;
     } catch (const isc::Exception& ex) {
-        std::cerr << "Service failed:" << ex.what() << std::endl;
+        std::cerr << "Service failed: " << ex.what() << std::endl;
         ret = EXIT_FAILURE;
     }
 
index 05cd038df762d89b8f7aa63ffcd14237aaa54840..a786cd3d9fac81603fa42a6e22c67b503770591f 100644 (file)
@@ -195,14 +195,14 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
     LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
               DHCP_DDNS_CONFIGURE).arg(config_set->str());
 
-    /// @todo: Implement this eventually.
+    isc::data::ConstElementPtr answer;
+    answer = getCfgMgr()->parseConfig(config_set, check_only);;
     if (check_only) {
-        return (isc::config::createAnswer(0, "Configuration check is not supported by D2."));
+        return (answer);
     }
 
     int rcode = 0;
     isc::data::ConstElementPtr comment;
-    isc::data::ConstElementPtr answer = getCfgMgr()->parseConfig(config_set);;
     comment = isc::config::parseAnswer(rcode, answer);
 
     if (rcode) {
index 9974227256e035204a6300d96e6e77009a4ff25b..4eaa2d70b4ec5cf5933c7978ee8c437240a08a25 100644 (file)
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
                [<!ENTITY mdash "&#8212;">]>
 <!--
- - Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
  -
  - This Source Code Form is subject to the terms of the Mozilla Public
  - License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -52,8 +52,8 @@
       <arg><option>-V</option></arg>
       <arg><option>-W</option></arg>
       <arg><option>-d</option></arg>
-      <!-- not yet <arg><option>-t</option></arg> -->
-      <arg><option>-c</option></arg>
+      <arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
         </para></listitem>
       </varlistentry>
 
-<!-- not yet
       <varlistentry>
-        <term><option>-t</option></term>
+        <term><option>-c</option></term>
         <listitem><para>
-          Check the syntax of the configuration file and report the first
-          error if any.
+          Configuration file including the configuration for DHCP-DDNS server.
+          It may also contain configuration entries for other Kea services.
         </para></listitem>
       </varlistentry>
--->
 
       <varlistentry>
-        <term><option>-c</option></term>
+        <term><option>-t</option></term>
         <listitem><para>
-          Configuration file including the configuration for DHCP-DDNS server.
-          It may also contain configuration entries for other Kea services.
+          Check the syntax of the configuration file and report the
+          first error if any. Note that not all parameters are
+          completely checked, in particular, service socket is
+          not opened.
         </para></listitem>
       </varlistentry>
 
index a1299bdc41aab463efdba160c616be4a363c2e03..7eb704467b4d0e72c7f5bb2a8726aaa3eb33429f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -34,9 +34,18 @@ int main(int argc, char* argv[]) {
         // 'false' value disables test mode.
         controller->launch(argc, argv, false);
     } catch (const VersionMessage& ex) {
-        std::cout << ex.what() << std::endl;
+        std::string msg(ex.what());
+        if (!msg.empty()) {
+            std::cout << msg << std::endl;
+        }
+    } catch (const InvalidUsage& ex) {
+        std::string msg(ex.what());
+        if (!msg.empty()) {
+            std::cerr << msg << std::endl;
+        }
+        ret = EXIT_FAILURE;
     } catch (const isc::Exception& ex) {
-        std::cerr << "Service failed:" << ex.what() << std::endl;
+        std::cerr << "Service failed: " << ex.what() << std::endl;
         ret = EXIT_FAILURE;
     }
 
index 05f1e60caa76ad9be1a9758457b7e374cf16ba76..495994c7a8ed2766349d9f6f16e861a3541ceff2 100644 (file)
@@ -168,7 +168,8 @@ public:
 
         // The JSON parsed ok and we've added the defaults, pass the config
         // into the Element parser and check for the expected outcome.
-        data::ConstElementPtr answer = cfg_mgr_->parseConfig(config_set_);
+        data::ConstElementPtr answer;
+        answer = cfg_mgr_->parseConfig(config_set_, false);
 
         // Extract the result and error text from the answer.
         int rcode = 0;
@@ -601,7 +602,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
 
     // Verify that parsing the exact same configuration a second time
     // does not cause a duplicate value errors.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     ASSERT_TRUE(checkAnswer(0));
 }
 
index 244a84f9e0bad981abc9007c1ea137441e117d1d..c8ee639c697a38192fd79a8c0b982cde3c43fd31 100644 (file)
@@ -177,14 +177,24 @@ TEST_F(D2ControllerTest, configUpdateTests) {
                                 isc::data::Element::fromJSON(valid_d2_config);
 
     // Verify that given a valid config we get a successful update result.
-    answer = updateConfig(config_set);
+    answer = updateConfig(config_set, false);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(0, rcode);
+
+    // Verify that given a valid config we get a successful check result.
+    answer = updateConfig(config_set, true);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
 
     // Use an invalid configuration to verify parsing error return.
     std::string config = "{ \"bogus\": 1000 } ";
     config_set = isc::data::Element::fromJSON(config);
-    answer = updateConfig(config_set);
+    answer = updateConfig(config_set, false);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
+
+    // Use an invalid configuration to verify checking error return.
+    answer = updateConfig(config_set, true);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(1, rcode);
 }
index 478ab767437b7bc23424ec1ad3c51ed9143a77ab..0cded96a08569c098ac1e7f52defa69aa4a0487b 100644 (file)
@@ -152,7 +152,7 @@ public:
         // try DHCPDDNS configure
         ConstElementPtr status;
         try {
-            status = srv_->parseConfig(d2);
+            status = srv_->parseConfig(d2, false);
         } catch (const std::exception& ex) {
             ADD_FAILURE() << "configure for " << operation
                           << " failed with " << ex.what()
index 3b295a30c44cf04bb960a33330b9bb3e949e61cd..1bc3e0d59a1a317b6cb36f4de082cbcfa3e945ca 100644 (file)
@@ -122,7 +122,8 @@ DCfgMgrBase::setContext(DCfgContextBasePtr& context) {
 }
 
 isc::data::ConstElementPtr
-DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
+DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set,
+                         bool check_only) {
     LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
                 DCTL_CONFIG_START).arg(config_set->str());
 
@@ -245,9 +246,15 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
         }
 
         // Everything was fine. Configuration set processed successfully.
-        LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
-        answer = isc::config::createAnswer(0, "Configuration committed.");
-
+        if (!check_only) {
+            LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
+            answer = isc::config::createAnswer(0, "Configuration committed.");
+        } else {
+            answer = isc::config::createAnswer(0, "Configuration seems sane.");
+            LOG_INFO(dctl_logger, DCTL_CONFIG_CHECK_COMPLETE)
+                .arg(getConfigSummary(0))
+                .arg(config::answerToText(answer));
+        }
     } catch (const std::exception& ex) {
         LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
         answer = isc::config::createAnswer(1, ex.what());
@@ -257,6 +264,12 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
         return (answer);
     }
 
+    if (check_only) {
+        // If this is a configuration check only, then don't actually apply
+        // the configuration and reverse to the previous one.
+        context_ = original_context;
+    }
+
     return (answer);
 }
 
index 340a8df6831bc4d3ad77a4fff83e00eba476344e..c3d95d63807fed614af085c7a99c502126de9c0d 100644 (file)
@@ -246,7 +246,7 @@ typedef std::vector<std::string> ElementIdList;
 ///        update context with parsed results
 ///        break on error
 ///
-///    if an error occurred
+///    if an error occurred or this is only a check
 ///        restore configuration context from backup
 /// @endcode
 ///
@@ -281,7 +281,7 @@ typedef std::vector<std::string> ElementIdList;
 /// 1. implementation calls simpleParseConfig from its configure method.
 /// 2. simpleParseConfig makes a configuration context
 /// 3. parse method from the derived class is called
-/// 4. if the configuration was unsuccessful of this is only a check, the
+/// 4. if the configuration was unsuccessful or this is only a check, the
 ///    old context is reinstantiated. If not, the configuration is kept.
 ///
 /// See @ref isc::agent::CtrlAgentCfgMgr and @ref isc::agent::CtrlAgentProcess
@@ -303,12 +303,14 @@ public:
     /// the parsing as described in the class brief.
     ///
     /// @param config_set is a set of configuration elements to be parsed.
+    /// @param check_only true if the config is to be checked only, but not applied
     ///
     /// @return an Element that contains the results of configuration composed
     /// of an integer status value (0 means successful, non-zero means failure),
     /// and a string explanation of the outcome.
-    isc::data::ConstElementPtr parseConfig(isc::data::ConstElementPtr
-                                           config_set);
+    isc::data::ConstElementPtr
+    parseConfig(isc::data::ConstElementPtr config_set,
+                bool check_only = false);
 
 
     /// @brief Acts as the receiver of new configurations.
index 2d63197351453bd441f9397d0a4e18f001b85eb9..5097ff3473fbc85f420f59ba246af202045d6d0a 100644 (file)
@@ -68,11 +68,67 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
         parseArgs(argc, argv);
     } catch (const InvalidUsage& ex) {
         usage(ex.what());
-        throw; // rethrow it
+        // rethrow it with an empty message
+        isc_throw(InvalidUsage, "");
     }
 
     setProcName(bin_name_);
 
+    if (!test_mode && check_only_) {
+        try {
+            // We need to initialize logging, in case any error
+            // messages are to be printed.
+            // This is just a test, so we don't care about lockfile.
+            setenv("KEA_LOCKFILE_DIR", "none", 0);
+            isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_);
+            isc::dhcp::CfgMgr::instance().setVerbose(verbose_);
+            Daemon::loggerInit(bin_name_.c_str(), verbose_);
+
+            // Check the syntax first.
+            std::string config_file = getConfigFile();
+            if (config_file.empty()) {
+                // Basic sanity check: file name must not be empty.
+                isc_throw(InvalidUsage,
+                          "JSON configuration file not specified");
+            }
+            isc::data::ConstElementPtr whole_config = parseFile(config_file);
+            if (!whole_config) {
+                // No fallback to fromJSONFile
+                isc_throw(InvalidUsage, "No configuration found");
+            }
+            if (verbose_) {
+                std::cerr << "Syntax check OK" << std::endl;
+            }
+
+            // Check the logic next.
+            isc::data::ConstElementPtr module_config;
+            module_config = whole_config->get(getAppName());
+            if (!module_config) {
+                isc_throw(InvalidUsage, "Config file " << config_file <<
+                          " does not include '" << getAppName() << "' entry");
+            }
+
+            // Get an application process object.
+            initProcess();
+
+            isc::data::ConstElementPtr answer;
+            answer = updateConfig(module_config, true);
+            int rcode = 0;
+            answer = isc::config::parseAnswer(rcode, answer);
+            if (rcode != 0) {
+                isc_throw(InvalidUsage, "Error encountered: "
+                          << answer->stringValue());
+            }
+        } catch (const VersionMessage&) {
+            throw;
+        } catch (const InvalidUsage&) {
+            throw;
+        } catch (const std::exception& ex) {
+            isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what());
+        }
+        return;
+    }   
+
     // It is important that we set a default logger name because this name
     // will be used when the user doesn't provide the logging configuration
     // in the Kea configuration file.
@@ -150,12 +206,12 @@ void
 DControllerBase::parseArgs(int argc, char* argv[])
 {
     // Iterate over the given command line options. If its a stock option
-    // ("s" or "v") handle it here.  If its a valid custom option, then
+    // ("c" or "d") handle it here.  If its a valid custom option, then
     // invoke customOption.
     int ch;
     opterr = 0;
     optind = 1;
-    std::string opts("dvVWc:" + getCustomOpts());
+    std::string opts("dvVWc:t:" + getCustomOpts());
     while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
         switch (ch) {
         case 'd':
@@ -182,12 +238,17 @@ DControllerBase::parseArgs(int argc, char* argv[])
             break;
 
         case 'c':
+        case 't':
             // config file name
             if (optarg == NULL) {
                 isc_throw(InvalidUsage, "configuration file name missing");
             }
 
             setConfigFile(optarg);
+
+            if (ch == 't') {
+                check_only_ = true;
+            }
             break;
 
         case '?': {
@@ -290,7 +351,7 @@ DControllerBase::configFromFile() {
                                  getAppName() << "' entry.");
         }
 
-        answer = updateConfig(module_config);
+        answer = updateConfig(module_config, false);
         int rcode = 0;
         isc::config::parseAnswer(rcode, answer);
         if (!rcode) {
@@ -329,8 +390,9 @@ DControllerBase::runProcess() {
 
 // Instance method for handling new config
 isc::data::ConstElementPtr
-DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
-    return (process_->configure(new_config, false));
+DControllerBase::updateConfig(isc::data::ConstElementPtr new_config,
+                              bool check_only) {
+    return (process_->configure(new_config, check_only));
 }
 
 
index 6ba258a25a19661f2f61b1fb34302cfe2dbfba05..80eb098ada5d691fefd1f7c549b2fc23de83af01 100644 (file)
@@ -24,6 +24,7 @@ namespace isc {
 namespace process {
 
 /// @brief Exception thrown when the command line is invalid.
+/// Can be used to transmit negative messages too.
 class InvalidUsage : public isc::Exception {
 public:
     InvalidUsage(const char* file, size_t line, const char* what) :
@@ -34,7 +35,7 @@ public:
 /// Since command line argument parsing is done as part of
 /// DControllerBase::launch(), it uses this exception to propagate
 /// version information up to main(), when command line argument
-/// -v or -V is given.
+/// -v, -V or -W is given. Can be used to transmit positive messages too.
 class VersionMessage : public isc::Exception {
 public:
     VersionMessage(const char* file, size_t line, const char* what) :
@@ -128,7 +129,7 @@ public:
     /// arguments.
     ///
     /// This function can be run in "test mode". It prevents initialization
-    /// of D2 module logger. This is used in unit tests which initialize logger
+    /// of module logger. This is used in unit tests which initialize logger
     /// in their main function. Such a logger uses environmental variables to
     /// control severity, verbosity etc.
     ///
@@ -154,12 +155,14 @@ public:
     /// configuration and then invoke the application process' configure method.
     ///
     /// @param  new_config is the new configuration
+    /// @param check_only false for normal configuration, true when verifying only
     ///
     /// @return returns an Element that contains the results of configuration
     /// update composed of an integer status value (0 means successful,
     /// non-zero means failure), and a string explanation of the outcome.
-    virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
-                                                    new_config);
+    virtual isc::data::ConstElementPtr
+    updateConfig(isc::data::ConstElementPtr new_config,
+                 bool check_only = false);
 
     /// @brief Reconfigures the process from a configuration file
     ///
@@ -223,10 +226,10 @@ public:
     /// @return an Element that contains the results of command composed
     /// of an integer status value and a string explanation of the outcome.
     /// The status value is one of the following:
-    ///   D2::COMMAND_SUCCESS - Command executed successfully
-    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
+    ///   COMMAND_SUCCESS - Command executed successfully
+    ///   COMMAND_ERROR - Command is valid but suffered an operational
     ///   failure.
-    ///   D2::COMMAND_INVALID - Command is not recognized as valid be either
+    ///   COMMAND_INVALID - Command is not recognized as valid be either
     ///   the controller or the application process.
     virtual isc::data::ConstElementPtr executeCommand(const std::string&
                                                       command,
@@ -283,10 +286,10 @@ protected:
     /// @return an Element that contains the results of command composed
     /// of an integer status value and a string explanation of the outcome.
     /// The status value is one of the following:
-    ///   D2::COMMAND_SUCCESS - Command executed successfully
-    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
+    ///   COMMAND_SUCCESS - Command executed successfully
+    ///   COMMAND_ERROR - Command is valid but suffered an operational
     ///   failure.
-    ///   D2::COMMAND_INVALID - Command is not recognized as a valid custom
+    ///   COMMAND_INVALID - Command is not recognized as a valid custom
     ///   controller command.
     virtual isc::data::ConstElementPtr customControllerCommand(
             const std::string& command, isc::data::ConstElementPtr args);
@@ -302,7 +305,7 @@ protected:
 
     /// @brief Virtual method which returns a string containing the option
     /// letters for any custom command line options supported by the derivation.
-    /// These are added to the stock options of "c" and "v" during command
+    /// These are added to the stock options of "c", "d", ..., during command
     /// line interpretation.
     ///
     /// @return returns a string containing the custom option letters.
@@ -342,6 +345,20 @@ protected:
         verbose_ = value;
     }
 
+    /// @brief Supplies whether or not check only mode is enabled.
+    ///
+    /// @return returns true if check only is enabled.
+    bool isCheckOnly() const {
+        return (check_only_);
+    }
+
+    /// @brief Method for enabling or disabling check only mode.
+    ///
+    /// @param value is the new value to assign the flag.
+    void setCheckOnly(bool value) {
+        check_only_ = value;
+    }
+
     /// @brief Getter for fetching the controller's IOService
     ///
     /// @return returns a pointer reference to the IOService.
@@ -385,14 +402,15 @@ protected:
     /// list of options with those returned by getCustomOpts(), and uses
     /// cstdlib's getopt to loop through the command line.
     /// It handles stock options directly, and passes any custom options into
-    /// the customOption method.  Currently there are only two stock options
-    /// -c for specifying the configuration file, and -v for verbose logging.
+    /// the customOption method.  Currently there are only some stock options
+    /// -c/t for specifying the configuration file, -d for verbose logging,
+    /// and -v/V/W for version reports.
     ///
     /// @param argc  is the number of command line arguments supplied
     /// @param argv  is the array of string (char *) command line arguments
     ///
     /// @throw InvalidUsage when there are usage errors.
-    /// @throw VersionMessage if the -v or -V arguments is given.
+    /// @throw VersionMessage if the -v, -V or -W arguments is given.
     void parseArgs(int argc, char* argv[]);
 
 
@@ -536,6 +554,9 @@ private:
     /// @brief Indicates if the verbose logging mode is enabled.
     bool verbose_;
 
+    /// @brief Indicates if the check only mode is enabled.
+    bool check_only_;
+
     /// @brief The absolute file name of the JSON spec file.
     std::string spec_file_name_;
 
index b060b6a51735c884720ec71ed7c008c4e228e9b8..fca4741ee5183f3197c645e416daa32c975234e7 100644 (file)
@@ -115,13 +115,22 @@ TEST_F(DStubCfgMgrTest, basicParseTest) {
     ASSERT_TRUE(fromJSON(config));
 
     // Verify that we can parse a simple configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that we can check a simple configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_, true);
     EXPECT_TRUE(checkAnswer(0));
 
     // Verify that an unknown element error is caught and returns a failed
     // parse result.
     SimFailure::set(SimFailure::ftElementUnknown);
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that an error is caught too when the config is checked for.
+    SimFailure::set(SimFailure::ftElementUnknown);
+    answer_ = cfg_mgr_->parseConfig(config_set_, true);
     EXPECT_TRUE(checkAnswer(1));
 }
 
@@ -181,7 +190,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
     EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
 
     // Parse the configuration, verify it parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(0));
 
     // Verify that the parsed order matches what we expected.
@@ -197,7 +206,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
     EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
 
     // Verify the configuration fails.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(1));
 
     // Verify that the configuration parses correctly, when the parse order
@@ -212,7 +221,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
     cfg_mgr_->parsed_order_.clear();
 
     // Verify the configuration parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(0));
 
     // Build expected order
@@ -238,7 +247,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
     EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
 
     // Verify the configuration fails.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(1));
 }
 
@@ -262,7 +271,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
     ASSERT_TRUE(fromJSON(config));
 
     // Verify that the configuration parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     ASSERT_TRUE(checkAnswer(0));
     DStubContextPtr context = getStubContext();
     ASSERT_TRUE(context);
@@ -301,7 +310,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
     ASSERT_TRUE(fromJSON(config2));
 
     // Verify that the configuration parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(0));
     context = getStubContext();
     ASSERT_TRUE(context);
@@ -352,7 +361,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
     ASSERT_TRUE(fromJSON(config));
 
     // Verify that the configuration parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(0));
     DStubContextPtr context = getStubContext();
     ASSERT_TRUE(context);
@@ -389,7 +398,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
 
     // Force a failure on the last element
     SimFailure::set(SimFailure::ftElementUnknown);
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     EXPECT_TRUE(checkAnswer(1));
     context = getStubContext();
     ASSERT_TRUE(context);
@@ -414,6 +423,76 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
     EXPECT_TRUE(object);
 }
 
+/// @brief Tests that the configuration context is preserved during
+/// check only  parsing.
+TEST_F(DStubCfgMgrTest, checkOnly) {
+    // Create a configuration with all of the parameters.
+    string config = "{ \"bool_test\": true , "
+                    "  \"uint32_test\": 77 , "
+                    "  \"string_test\": \"hmmm chewy\" , "
+                    "  \"map_test\" : {} , "
+                    "  \"list_test\": [] }";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
+    EXPECT_TRUE(checkAnswer(0));
+    DStubContextPtr context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Verify that all of parameters have the expected values.
+    bool actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    uint32_t actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    std::string actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    isc::data::ConstElementPtr object;
+    EXPECT_NO_THROW(context->getObjectParam("map_test", object));
+    EXPECT_TRUE(object);
+
+    EXPECT_NO_THROW(context->getObjectParam("list_test", object));
+    EXPECT_TRUE(object);
+
+    // Create a configuration which "updates" all of the parameter values.
+    string config2 = "{ \"bool_test\": false , "
+                    "  \"uint32_test\": 88 , "
+                    "  \"string_test\": \"ewww yuk!\" , "
+                    "  \"map_test2\" : {} , "
+                    "  \"list_test2\": [] }";
+    ASSERT_TRUE(fromJSON(config2));
+
+    answer_ = cfg_mgr_->parseConfig(config_set_, true);
+    EXPECT_TRUE(checkAnswer(0));
+    context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Verify that all of parameters have the original values.
+    actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    EXPECT_NO_THROW(context->getObjectParam("map_test", object));
+    EXPECT_TRUE(object);
+
+    EXPECT_NO_THROW(context->getObjectParam("list_test", object));
+    EXPECT_TRUE(object);
+}
+
 // Tests that configuration element position is returned by getParam variants.
 TEST_F(DStubCfgMgrTest, paramPosition) {
     // Create a configuration with one of each scalar types.  We end them
@@ -424,7 +503,7 @@ TEST_F(DStubCfgMgrTest, paramPosition) {
     ASSERT_TRUE(fromJSON(config));
 
     // Verify that the configuration parses without error.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
+    answer_ = cfg_mgr_->parseConfig(config_set_, false);
     ASSERT_TRUE(checkAnswer(0));
     DStubContextPtr context = getStubContext();
     ASSERT_TRUE(context);
index 68eb1acf0014dec23dcfe11f351b1b2c0f93a87d..c65828f00ff5ccedd3f864483c67a204dce9a35f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -268,13 +268,24 @@ TEST_F(DStubControllerTest, configUpdateTests) {
     isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
 
     // Verify that a valid config gets a successful update result.
-    answer = updateConfig(config_set);
+    answer = updateConfig(config_set, false);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(0, rcode);
+
+    // Verify that a valid config gets a successful check result.
+    answer = updateConfig(config_set, true);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
 
     // Verify that an error in process configure method is handled.
     SimFailure::set(SimFailure::ftProcessConfigure);
-    answer = updateConfig(config_set);
+    answer = updateConfig(config_set, false);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
+
+    // Verify that an error is handled too when the config is checked for.
+    SimFailure::set(SimFailure::ftProcessConfigure);
+    answer = updateConfig(config_set, true);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(1, rcode);
 }
index b013ffbf6f3d53686ae9a9fed97ae5ec9f8da9b3..8bff0f2accc1ea3f39050fab60b5dd0fd6083b40 100644 (file)
@@ -68,18 +68,13 @@ DStubProcess::shutdown(isc::data::ConstElementPtr /* args */) {
 
 isc::data::ConstElementPtr
 DStubProcess::configure(isc::data::ConstElementPtr config_set, bool check_only) {
-    if (check_only) {
-        return (isc::config::createAnswer(1,
-                "Configuration checking is not supported."));
-    }
-
     if (SimFailure::shouldFailOn(SimFailure::ftProcessConfigure)) {
         // Simulates a process configure failure.
         return (isc::config::createAnswer(1,
                 "Simulated process configuration error."));
     }
 
-    return (getCfgMgr()->parseConfig(config_set));
+    return (getCfgMgr()->parseConfig(config_set, check_only));
 }
 
 isc::data::ConstElementPtr
index 79ac19ca30e19f9e77d5b3f4b03dcf3f370c8176..a7811a36a6bb0dbd9bde144d0f2665c4c5550801 100644 (file)
@@ -487,9 +487,10 @@ public:
 
     /// @Wrapper to invoke the Controller's updateConfig method.  Please
     /// refer to DControllerBase::updateConfig for details.
-    isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
-                                            new_config) {
-        return (getController()->updateConfig(new_config));
+    isc::data::ConstElementPtr
+    updateConfig(isc::data::ConstElementPtr new_config,
+                 bool check_only) {
+        return (getController()->updateConfig(new_config, check_only));
     }
 
     /// @Wrapper to invoke the Controller's executeCommand method.  Please