From: Francis Dupont Date: Sat, 11 Mar 2017 11:52:56 +0000 (+0100) Subject: [5152] Done (at the exception of unit tests) X-Git-Tag: trac102b_base~3^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=85918740ffcefe718ed7295c779e9d2455d120f6;p=thirdparty%2Fkea.git [5152] Done (at the exception of unit tests) --- diff --git a/doc/guide/ddns.xml b/doc/guide/ddns.xml index 1479feddb1..c32d244409 100644 --- a/doc/guide/ddns.xml +++ b/doc/guide/ddns.xml @@ -97,9 +97,14 @@ - -W - prints out Kea configuration report - and exits. - + -t file + 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. @@ -152,9 +157,9 @@ strings path/kea-dhcp-ddns | sed -n 's/;;;; //p'
Configuring the DHCP-DDNS Server - Before starting kea-dhcp-ddns 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 kea-dhcp-ddns 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. "DhcpDdns": { "ip-address": "127.0.0.1", @@ -164,10 +169,10 @@ strings path/kea-dhcp-ddns | sed -n 's/;;;; //p' "ncr-format": "JSON", "tsig-keys": [ ], "forward-ddns": { - "ddns-domains": [ ] + "ddns-domains": [ ] }, "reverse-ddns": { - "ddns-domains": [ ] + "ddns-domains": [ ] } } @@ -176,30 +181,30 @@ strings path/kea-dhcp-ddns | sed -n 's/;;;; //p' The configuration can be divided as follows, each of which is described in its own section: - - - + + + Global Server Parameters - values which control connectivity and global server behavior - - - - - TSIG Key Info - defines the TSIG keys used for secure traffic with DNS servers - - - - - Forward DDNS - defines the catalog of Forward DDNS Domains - - - - - Reverse DDNS - defines the catalog of Forward DDNS Domains - - - + + + + + TSIG Key Info - defines the TSIG keys used for secure traffic with DNS servers + + + + + Forward DDNS - defines the catalog of Forward DDNS Domains + + + + + Reverse DDNS - defines the catalog of Forward DDNS Domains + + +
- Global Server Parameters + Global Server Parameters @@ -231,11 +236,11 @@ strings path/kea-dhcp-ddns | sed -n 's/;;;; //p' - - 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: + + 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: "DhcpDdns": { "ip-address": "192.168.1.10", @@ -243,19 +248,19 @@ strings path/kea-dhcp-ddns | sed -n 's/;;;; //p' ... } } - - - - 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. - + + + + 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. + - + 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.
- TSIG Key List - - A DDNS protocol exchange can be conducted with or without TSIG - (defined in RFC - 2845). This configuration section allows the administrator - to define the set of TSIG keys that may be used in such - exchanges. - - 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. - - - 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: - - - - name - - 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. - - - - - algorithm - - 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: - - - HMAC-MD5 - - - HMAC-SHA1 - - - HMAC-SHA224 - - - HMAC-SHA256 - - - HMAC-SHA384 - - - HMAC-SHA512 - - - This value is not case sensitive. - - - - - digest-bits - - 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. - - - - - secret - - 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. - - - - - - 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: + TSIG Key List + + A DDNS protocol exchange can be conducted with or without TSIG + (defined in RFC + 2845). This configuration section allows the administrator + to define the set of TSIG keys that may be used in such + exchanges. + + 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. + + + 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: + + + + name - + 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. + + + + + algorithm - + 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: + + + HMAC-MD5 + + + HMAC-SHA1 + + + HMAC-SHA224 + + + HMAC-SHA256 + + + HMAC-SHA384 + + + HMAC-SHA512 + + + This value is not case sensitive. + + + + + digest-bits - + 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. + + + + + secret - + 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. + + + + + + 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: : key "key.four.example.com." { @@ -363,7 +368,7 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section. }; : - By default, the TSIG Key list is empty: + By default, the TSIG Key list is empty: "DhcpDdns": { "tsig-keys": [ ], @@ -371,333 +376,333 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section. } - We must extend the list with a new key: + We must extend the list with a new key: "DhcpDdns": { "tsig-keys": [ { - "name": "key.four.example.com.", - "algorithm": "HMAC-SHA224", - "secret": "bZEG7Ow8OgAUPfLWV3aAUQ==" - } + "name": "key.four.example.com.", + "algorithm": "HMAC-SHA224", + "secret": "bZEG7Ow8OgAUPfLWV3aAUQ==" + } ], ... } - + - These steps would be repeated for each TSIG key needed. Note that - the same TSIG key can be used with more than one domain. + These steps would be repeated for each TSIG key needed. Note that + the same TSIG key can be used with more than one domain.
- +
- Forward DDNS - - 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. + Forward DDNS + + 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. "DhcpDdns": { "forward-ddns": { - "ddns-domains": [ ] + "ddns-domains": [ ] }, ... } - By default, this list is empty, which will cause the server to ignore - the forward update portions of requests. - -
- Adding Forward DDNS Domains - - 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. - - - 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: - - - - name - - 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. - - - - - key-name - - 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. - - - - - dns-servers - - 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. - - - - 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. + +
+ Adding Forward DDNS Domains + + 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. + + + 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: + + + + name - + 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. + + + + + key-name - + 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. + + + + + dns-servers - + 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. + + + + To create a new forward DDNS Domain, one must add a new domain + element and set its parameters: "DhcpDdns": { "forward-ddns": { - "ddns-domains": [ - { - "name": "other.example.com.", - "key-name": "", - "dns-servers": [ - ] - } - ] + "ddns-domains": [ + { + "name": "other.example.com.", + "key-name": "", + "dns-servers": [ + ] + } + ] } } - 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. - - -
- Adding Forward DNS Servers - - 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. - - - 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: - - - - hostname - - The resolvable host name of the DNS server. This value is not - yet implemented. - - - - - ip-address - - The IP address at which the server listens for DDNS requests. - This may be either an IPv4 or an IPv6 address. - - - - - port - - The port on which the server listens for DDNS requests. It - defaults to the standard DNS service port of 53. - - - - 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. + + +
+ Adding Forward DNS Servers + + 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. + + + 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: + + + + hostname - + The resolvable host name of the DNS server. This value is not + yet implemented. + + + + + ip-address - + The IP address at which the server listens for DDNS requests. + This may be either an IPv4 or an IPv6 address. + + + + + port - + The port on which the server listens for DDNS requests. It + defaults to the standard DNS service port of 53. + + + + 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: "DhcpDdns": { "forward-ddns": { - "ddns-domains": [ - { - "name": "other.example.com.", - "key-name": "", - "dns-servers": [ - { - "hostname": "", - "ip-address": "172.88.99.10", - "port": 53 - } - ] - } - ] + "ddns-domains": [ + { + "name": "other.example.com.", + "key-name": "", + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.88.99.10", + "port": 53 + } + ] + } + ] } } - + - 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. -
+
- Reverse DDNS - - 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. + Reverse DDNS + + 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. "DhcpDdns": { "reverse-ddns": { - "ddns-domains": [ ] + "ddns-domains": [ ] } ... } - By default, this list is empty, which will cause the server to ignore - the reverse update portions of requests. - -
- Adding Reverse DDNS Domains - - 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. - - - 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: - - - - name - - 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. - - - - - key-name - - 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. - - - - - dns-servers - - 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. - - - - 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. + +
+ Adding Reverse DDNS Domains + + 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. + + + 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: + + + + name - + 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. + + + + + key-name - + 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. + + + + + dns-servers - + 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. + + + + 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: "DhcpDdns": { "reverse-ddns": { - "ddns-domains": [ - { - "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", - "key-name": "", - "dns-servers": [ - ] - } - ] + "ddns-domains": [ + { + "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", + "key-name": "", + "dns-servers": [ + ] + } + ] } } - 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. - - -
- Adding Reverse DNS Servers - - This section describes how to add DNS servers to a Reverse DDNS Domain. - Repeat them for as many servers as desired for each domain. - - - 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: - - - - hostname - - The resolvable host name of the DNS server. This value is - currently ignored. - - - - - ip-address - - The IP address at which the server listens for DDNS requests. - - - - - port - - The port on which the server listens for DDNS requests. It - defaults to the standard DNS service port of 53. - - - - 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. + + +
+ Adding Reverse DNS Servers + + This section describes how to add DNS servers to a Reverse DDNS Domain. + Repeat them for as many servers as desired for each domain. + + + 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: + + + + hostname - + The resolvable host name of the DNS server. This value is + currently ignored. + + + + + ip-address - + The IP address at which the server listens for DDNS requests. + + + + + port - + The port on which the server listens for DDNS requests. It + defaults to the standard DNS service port of 53. + + + + 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: "DhcpDdns": { "reverse-ddns": { - "ddns-domains": [ - { - "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", - "key-name": "", - "dns-servers": [ - { - "hostname": "", - "ip-address": "172.88.99.10", - "port": 53 - } - ] - } - ] + "ddns-domains": [ + { + "name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.", + "key-name": "", + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.88.99.10", + "port": 53 + } + ] + } + ] } } @@ -710,219 +715,219 @@ corresponding values in the DHCP servers' "dhcp-ddns" configuration section. -
+
- Example DHCP-DDNS Server Configuration - - 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. - - - Our example network - - - - - - - - Domain - Subnet - Forward DNS Servers - Reverse DNS Servers - - - - - four.example.com - 192.0.2.0/24 - 172.16.1.5, 172.16.2.5 - 172.16.1.5, 172.16.2.5 - - - six.example.com - 2001:db8:1::/64 - 3001:1::50 - 3001:1::51 - - - example.com - 192.0.0.0/16 - 172.16.2.5 - 172.16.2.5 - - - -
-
- - We need to construct three forward DDNS Domains: - - Forward DDNS Domains Needed - - - - - - - # - DDNS Domain Name - DNS Servers - - - - - 1. - four.example.com. - 172.16.1.5, 172.16.2.5 - - - 2. - six.example.com. - 3001:1::50 - - - 3. - example.com. - 172.16.2.5 - - - -
- 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. -
- - The following example configuration specified the Forward DDNS Domains. + Example DHCP-DDNS Server Configuration + + 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. + + + Our example network + + + + + + + + Domain + Subnet + Forward DNS Servers + Reverse DNS Servers + + + + + four.example.com + 192.0.2.0/24 + 172.16.1.5, 172.16.2.5 + 172.16.1.5, 172.16.2.5 + + + six.example.com + 2001:db8:1::/64 + 3001:1::50 + 3001:1::51 + + + example.com + 192.0.0.0/16 + 172.16.2.5 + 172.16.2.5 + + + +
+
+ + We need to construct three forward DDNS Domains: + + Forward DDNS Domains Needed + + + + + + + # + DDNS Domain Name + DNS Servers + + + + + 1. + four.example.com. + 172.16.1.5, 172.16.2.5 + + + 2. + six.example.com. + 3001:1::50 + + + 3. + example.com. + 172.16.2.5 + + + +
+ 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. +
+ + The following example configuration specified the Forward DDNS Domains. "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" } + ] + }, + + ] } } - - - Similarly, we need to construct the three reverse DDNS Domains: - - Reverse DDNS Domains Needed - - - - - - - # - DDNS Domain Name - DNS Servers - - - - - 1. - 2.0.192.in-addr.arpa. - 172.16.1.5, 172.16.2.5 - - - 2. - 1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa. - 3001:1::50 - - - 3. - 0.182.in-addr.arpa. - 172.16.2.5 - - - -
- 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. -
- - These Reverse DDNS Domains are specified as follows: + + + Similarly, we need to construct the three reverse DDNS Domains: + + Reverse DDNS Domains Needed + + + + + + + # + DDNS Domain Name + DNS Servers + + + + + 1. + 2.0.192.in-addr.arpa. + 172.16.1.5, 172.16.2.5 + + + 2. + 1.0.0.0.8.d.b.0.1.0.0.2.ip6.arpa. + 3001:1::50 + + + 3. + 0.182.in-addr.arpa. + 172.16.2.5 + + + +
+ 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. +
+ + These Reverse DDNS Domains are specified as follows: "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" } + ] + } + ] } } - -
+
+
DHCP-DDNS Server Limitations The following are the current limitations of the DHCP-DDNS Server. - - - 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. - - + + + 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. + +
diff --git a/src/bin/agent/kea-ctrl-agent.xml b/src/bin/agent/kea-ctrl-agent.xml index 492a6e693f..6fff18e0f9 100644 --- a/src/bin/agent/kea-ctrl-agent.xml +++ b/src/bin/agent/kea-ctrl-agent.xml @@ -52,7 +52,8 @@ - + + @@ -115,6 +116,16 @@ + + + + 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. + + + diff --git a/src/bin/agent/main.cc b/src/bin/agent/main.cc index c74329ba12..be43065b29 100644 --- a/src/bin/agent/main.cc +++ b/src/bin/agent/main.cc @@ -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; } diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc index 05cd038df7..a786cd3d9f 100644 --- a/src/bin/d2/d2_process.cc +++ b/src/bin/d2/d2_process.cc @@ -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) { diff --git a/src/bin/d2/kea-dhcp-ddns.xml b/src/bin/d2/kea-dhcp-ddns.xml index 9974227256..4eaa2d70b4 100644 --- a/src/bin/d2/kea-dhcp-ddns.xml +++ b/src/bin/d2/kea-dhcp-ddns.xml @@ -2,7 +2,7 @@ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - + + @@ -105,21 +105,21 @@ - - + - 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. diff --git a/src/bin/d2/main.cc b/src/bin/d2/main.cc index a1299bdc41..7eb704467b 100644 --- a/src/bin/d2/main.cc +++ b/src/bin/d2/main.cc @@ -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; } diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc index 05f1e60caa..495994c7a8 100644 --- a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc +++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc @@ -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)); } diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc index 244a84f9e0..c8ee639c69 100644 --- a/src/bin/d2/tests/d2_controller_unittests.cc +++ b/src/bin/d2/tests/d2_controller_unittests.cc @@ -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); } diff --git a/src/bin/d2/tests/get_config_unittest.cc b/src/bin/d2/tests/get_config_unittest.cc index 478ab76743..0cded96a08 100644 --- a/src/bin/d2/tests/get_config_unittest.cc +++ b/src/bin/d2/tests/get_config_unittest.cc @@ -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() diff --git a/src/lib/process/d_cfg_mgr.cc b/src/lib/process/d_cfg_mgr.cc index 3b295a30c4..1bc3e0d59a 100644 --- a/src/lib/process/d_cfg_mgr.cc +++ b/src/lib/process/d_cfg_mgr.cc @@ -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); } diff --git a/src/lib/process/d_cfg_mgr.h b/src/lib/process/d_cfg_mgr.h index 340a8df683..c3d95d6380 100644 --- a/src/lib/process/d_cfg_mgr.h +++ b/src/lib/process/d_cfg_mgr.h @@ -246,7 +246,7 @@ typedef std::vector 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 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. diff --git a/src/lib/process/d_controller.cc b/src/lib/process/d_controller.cc index 2d63197351..5097ff3473 100644 --- a/src/lib/process/d_controller.cc +++ b/src/lib/process/d_controller.cc @@ -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)); } diff --git a/src/lib/process/d_controller.h b/src/lib/process/d_controller.h index 6ba258a25a..80eb098ada 100644 --- a/src/lib/process/d_controller.h +++ b/src/lib/process/d_controller.h @@ -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_; diff --git a/src/lib/process/tests/d_cfg_mgr_unittests.cc b/src/lib/process/tests/d_cfg_mgr_unittests.cc index b060b6a517..fca4741ee5 100644 --- a/src/lib/process/tests/d_cfg_mgr_unittests.cc +++ b/src/lib/process/tests/d_cfg_mgr_unittests.cc @@ -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); diff --git a/src/lib/process/tests/d_controller_unittests.cc b/src/lib/process/tests/d_controller_unittests.cc index 68eb1acf00..c65828f00f 100644 --- a/src/lib/process/tests/d_controller_unittests.cc +++ b/src/lib/process/tests/d_controller_unittests.cc @@ -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); } diff --git a/src/lib/process/testutils/d_test_stubs.cc b/src/lib/process/testutils/d_test_stubs.cc index b013ffbf6f..8bff0f2acc 100644 --- a/src/lib/process/testutils/d_test_stubs.cc +++ b/src/lib/process/testutils/d_test_stubs.cc @@ -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 diff --git a/src/lib/process/testutils/d_test_stubs.h b/src/lib/process/testutils/d_test_stubs.h index 79ac19ca30..a7811a36a6 100644 --- a/src/lib/process/testutils/d_test_stubs.h +++ b/src/lib/process/testutils/d_test_stubs.h @@ -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