diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html index 39c3f41c9c..0c956e915d 100644 --- a/doc/guide/bind10-guide.html +++ b/doc/guide/bind10-guide.html @@ -1,4 +1,4 @@ -BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version +BIND 10 Guide

BIND 10 Guide

Administrator Reference for BIND 10

This is the reference guide for BIND 10 version 20120712.

Abstract

BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers with development managed by Internet Systems Consortium (ISC). @@ -10,9 +10,9 @@ The most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other documents for BIND 10, can be found at http://bind10.isc.org/docs. -


Table of Contents

Preface
1. Acknowledgements
1. Introduction
1.1. Supported Platforms
1.2. Required Software at Run-time
1.3. Starting and Stopping the Server
1.4. Managing BIND 10
2. Installation
2.1. Packages
2.2. Install Hierarchy
2.3. Building Requirements
2.4. Quick start
2.5. Installation from source
2.5.1. Download Tar File
2.5.2. Retrieve from Git
2.5.3. Configure before the build
2.5.4. Build
2.5.5. Install
3. Starting BIND10 with bind10
3.1. Starting BIND 10
3.2. Configuration to start processes
4. Command channel
5. Configuration manager
6. Remote control daemon
6.1. Configuration specification for b10-cmdctl
7. Control and configure user interface
8. Authoritative Server
8.1. Server Configurations
8.2. Data Source Backends
8.2.1. Data source types
8.2.2. Examples
8.3. Loading Master Zones Files
9. Incoming Zone Transfers
9.1. Configuration for Incoming Zone Transfers
9.2. Enabling IXFR
9.3. Secondary Manager
9.4. Trigger an Incoming Zone Transfer Manually
9.5. Incoming Transfers with In-memory Datasource
10. Outbound Zone Transfers
11. Dynamic DNS Update
11.1. Enabling Dynamic Update
11.2. Access Control
11.3. Miscellaneous Operational Issues
12. Recursive Name Server
12.1. Access Control
12.2. Forwarding
13. DHCPv4 Server
13.1. DHCPv4 Server Usage
13.2. DHCPv4 Server Configuration
13.3. Supported standards
13.4. DHCPv4 Server Limitations
14. DHCPv6 Server
14.1. DHCPv6 Server Usage
14.2. DHCPv6 Server Configuration
14.3. Supported DHCPv6 Standards
14.4. DHCPv6 Server Limitations
15. libdhcp++ library
15.1. Interface detection
15.2. DHCPv4/DHCPv6 packet handling
16. Statistics
17. Logging
17.1. Logging configuration
17.1.1. Loggers
17.1.2. Output Options
17.1.3. Example session
17.2. Logging Message Format

List of Tables

3.1. Special startup components

Preface

Table of Contents

1. Acknowledgements

1. Acknowledgements

ISC would like to acknowledge generous support for +


Table of Contents

Preface
1. Acknowledgements
1. Introduction
1.1. Supported Platforms
1.2. Required Software at Run-time
1.3. Starting and Stopping the Server
1.4. Managing BIND 10
2. Installation
2.1. Packages
2.2. Install Hierarchy
2.3. Building Requirements
2.4. Quick start
2.5. Installation from source
2.5.1. Download Tar File
2.5.2. Retrieve from Git
2.5.3. Configure before the build
2.5.4. Build
2.5.5. Install
3. Starting BIND10 with bind10
3.1. Starting BIND 10
3.2. Configuration to start processes
4. Command channel
5. Configuration manager
6. Remote control daemon
6.1. Configuration specification for b10-cmdctl
7. Control and configure user interface
8. Authoritative Server
8.1. Server Configurations
8.2. Data Source Backends
8.2.1. Data source types
8.2.2. Examples
8.3. Loading Master Zones Files
9. Incoming Zone Transfers
9.1. Configuration for Incoming Zone Transfers
9.2. Enabling IXFR
9.3. Secondary Manager
9.4. Trigger an Incoming Zone Transfer Manually
9.5. Incoming Transfers with In-memory Datasource
10. Outbound Zone Transfers
11. Dynamic DNS Update
11.1. Enabling Dynamic Update
11.2. Access Control
11.3. Miscellaneous Operational Issues
12. Recursive Name Server
12.1. Access Control
12.2. Forwarding
13. DHCPv4 Server
13.1. DHCPv4 Server Usage
13.2. DHCPv4 Server Configuration
13.3. Supported standards
13.4. DHCPv4 Server Limitations
14. DHCPv6 Server
14.1. DHCPv6 Server Usage
14.2. DHCPv6 Server Configuration
14.3. Supported DHCPv6 Standards
14.4. DHCPv6 Server Limitations
15. libdhcp++ library
15.1. Interface detection
15.2. DHCPv4/DHCPv6 packet handling
16. Statistics
17. Logging
17.1. Logging configuration
17.1.1. Loggers
17.1.2. Output Options
17.1.3. Example session
17.2. Logging Message Format

List of Tables

3.1. Special startup components

Preface

Table of Contents

1. Acknowledgements

1. Acknowledgements

ISC would like to acknowledge generous support for BIND 10 development of DHCPv4 and DHCPv6 components provided - by Comcast.

Chapter 1. Introduction

BIND is the popular implementation of a DNS server, developer interfaces, and DNS tools. BIND 10 is a rewrite of BIND 9 and ISC DHCP. @@ -25,7 +25,7 @@

This guide covers the experimental prototype of BIND 10 version 20120712. -

1.1. Supported Platforms

+

1.1. Supported Platforms

BIND 10 builds have been tested on (in no particular order) Debian GNU/Linux 6 and unstable, Ubuntu 9.10, NetBSD 5, Solaris 10 and 11, FreeBSD 7 and 8, CentOS Linux 5.3, @@ -174,7 +174,7 @@ documentation and code examples. -

Chapter 2. Installation

2.1. Packages

+

Chapter 2. Installation

2.1. Packages

Some operating systems or softare package vendors may provide ready-to-use, pre-built software packages for the BIND 10 suite. @@ -283,14 +283,14 @@ downloadable tar file or via BIND 10's Git code revision control service. (It may also be available in pre-compiled ready-to-use packages from operating system vendors.) -

2.5.1. Download Tar File

+

2.5.1. Download Tar File

Downloading a release tar file is the recommended method to obtain the source code.

The BIND 10 releases are available as tar file downloads from ftp://ftp.isc.org/isc/bind10/. Periodic development snapshots may also be available. -

2.5.2. Retrieve from Git

+

2.5.2. Retrieve from Git

Downloading this "bleeding edge" code is recommended only for developers or advanced users. Using development code in a production environment is not recommended. @@ -325,7 +325,7 @@ autoheader, automake, and related commands. -

2.5.3. Configure before the build

+

2.5.3. Configure before the build

BIND 10 uses the GNU Build System to discover build environment details. To generate the makefiles using the defaults, simply run: @@ -356,12 +356,12 @@

If the configure fails, it may be due to missing or old dependencies. -

2.5.4. Build

+

2.5.4. Build

After the configure step is complete, to build the executables from the C++ code and prepare the Python scripts, run:

$ make

-

2.5.5. Install

+

2.5.5. Install

To install the BIND 10 executables, support files, and documentation, run:

$ make install

@@ -432,7 +432,7 @@ started in a special way, with the value of special used for them: -

Table 3.1. Special startup components

ComponentSpecialDescription
b10-authauthAuthoritative DNS server
b10-resolverresolverDNS resolver
b10-cmdctlcmdctlCommand control (remote control interface)


+

Table 3.1. Special startup components

ComponentSpecialDescription
b10-authauthAuthoritative DNS server
b10-resolverresolverDNS resolver
b10-cmdctlcmdctlCommand control (remote control interface)


The kind specifies how a failure of the component should be handled. If it is set to @@ -661,13 +661,13 @@ the details and relays (over a b10-msgq command channel) the configuration on to the specified module.

-

Chapter 8. Authoritative Server

The b10-auth is the authoritative DNS server. It supports EDNS0, DNSSEC, IPv6, and SQLite3 and in-memory zone data backends. Normally it is started by the bind10 master process. -

8.1. Server Configurations

+

8.1. Server Configurations

b10-auth is configured via the b10-cfgmgr configuration manager. The module name is Auth. @@ -860,8 +860,19 @@ can use various data source backends. > config set data_sources/classes/IN[1]/params { "example.org": "/path/to/example.org", "example.com": "/path/to/example.com" } > config commit

- Unfortunately, due to current technical limitations, the params must - be set as one JSON blob, it can't be edited in + Initially, a map value has to be set, but this value may be an + empty map. After that, key/value pairs can be added with 'config + add' and keys can be removed with 'config remove'. The initial + value may be an empty map, but it has to be set before zones are + added or removed. + +

+> config set data_sources/classes/IN[1]/params {}
+> config add data_sources/classes/IN[1]/params another.example.org /path/to/another.example.org
+> config add data_sources/classes/IN[1]/params another.example.com /path/to/another.example.com
+> config remove data_sources/classes/IN[1]/params another.example.org
+          

+ bindctl. To reload a zone, you the same command as above.

Note

@@ -875,7 +886,7 @@ can use various data source backends. and old configuration correspond. The defaults are consistent, so unless you tweaked either the new or the old configuration, you're good. -

8.3. Loading Master Zones Files

+

8.3. Loading Master Zones Files

RFC 1035 style DNS master zone files may imported into a BIND 10 SQLite3 data source by using the b10-loadzone utility. @@ -904,7 +915,7 @@ can use various data source backends. If you reload a zone already existing in the database, all records from that prior zone disappear and a whole new set appears. -

Chapter 9. Incoming Zone Transfers

Incoming zones are transferred using the b10-xfrin process which is started by bind10. When received, the zone is stored in the corresponding BIND 10 @@ -918,7 +929,7 @@ can use various data source backends. IXFR. Due to some implementation limitations of the current development release, however, it only tries AXFR by default, and care should be taken to enable IXFR. -

9.1. Configuration for Incoming Zone Transfers

+

9.1. Configuration for Incoming Zone Transfers

In practice, you need to specify a list of secondary zones to enable incoming zone transfers for these zones (you can still trigger a zone transfer manually, without a prior configuration @@ -934,7 +945,7 @@ can use various data source backends. > config commit

(We assume there has been no zone configuration before). -

9.2. Enabling IXFR

+

9.2. Enabling IXFR

As noted above, b10-xfrin uses AXFR for zone transfers by default. To enable IXFR for zone transfers for a particular zone, set the use_ixfr @@ -983,13 +994,13 @@ can use various data source backends. (i.e. no SOA record for it), b10-zonemgr will automatically tell b10-xfrin to transfer the zone in. -

9.4. Trigger an Incoming Zone Transfer Manually

+

9.4. Trigger an Incoming Zone Transfer Manually

To manually trigger a zone transfer to retrieve a remote zone, you may use the bindctl utility. For example, at the bindctl prompt run:

> Xfrin retransfer zone_name="foo.example.org" master=192.0.2.99

-

9.5. Incoming Transfers with In-memory Datasource

+

9.5. Incoming Transfers with In-memory Datasource

In the case of an incoming zone transfer, the received zone is first stored in the corresponding BIND 10 datasource. In case the secondary zone is served by an in-memory datasource @@ -1045,7 +1056,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)

TSIGs in the incoming messages and to sign responses.

Note

The way to specify zone specific configuration (ACLs, etc) is likely to be changed. -

Chapter 11. Dynamic DNS Update

BIND 10 supports the server side of the Dynamic DNS Update (DDNS) protocol as defined in RFC 2136. This service is provided by the b10-ddns @@ -1092,7 +1103,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)

this feature.

-

11.1. Enabling Dynamic Update

+

11.1. Enabling Dynamic Update

First off, it must be made sure that a few components on which b10-ddns depends are configured to run, which are b10-auth @@ -1153,7 +1164,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)

to shutdown gracefully this parameter should also be specified.

-

11.2. Access Control

+

11.2. Access Control

By default, b10-ddns rejects any update requests from any clients by returning a REFUSED response. To allow updates to take effect, an access control rule @@ -1251,7 +1262,7 @@ DDNS/zones[0]/update_acl[1] {"action": "ACCEPT", "from": "::1", "key": "key. arbitrary clients. There have been other troubles that could have been avoided if the ACL could be checked before the prerequisite check. -

11.3. Miscellaneous Operational Issues

+

11.3. Miscellaneous Operational Issues

Unlike BIND 9, BIND 10 currently does not support automatic re-signing of DNSSEC-signed zone when it's updated via DDNS. It could be possible to re-sign the updated zone afterwards @@ -1293,7 +1304,7 @@ DDNS/zones[0]/update_acl[1] {"action": "ACCEPT", "from": "::1", "key": "key. IXFR. This is done automatically; it does not require specific configuration to make this possible. -

Chapter 12. Recursive Name Server

+

Chapter 12. Recursive Name Server

The b10-resolver process is started by bind10. @@ -1327,7 +1338,7 @@ DDNS/zones[0]/update_acl[1] {"action": "ACCEPT", "from": "::1", "key": "key.

(Replace the 2 as needed; run config show - Resolver/listen_on if needed.)

12.1. Access Control

+ Resolver/listen_on” if needed.)

12.1. Access Control

By default, the b10-resolver daemon only accepts DNS queries from the localhost (127.0.0.1 and ::1). The Resolver/query_acl configuration may @@ -1360,7 +1371,7 @@ DDNS/zones[0]/update_acl[1] {"action": "ACCEPT", "from": "::1", "key": "key.

(Replace the 2 as needed; run config show Resolver/query_acl if needed.)

Note

This prototype access control configuration - syntax may be changed.

12.2. Forwarding

+ syntax may be changed.

12.2. Forwarding

To enable forwarding, the upstream address and port must be configured to forward queries to, such as: @@ -1639,7 +1650,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

} }

-

Chapter 17. Logging

17.1. Logging configuration

+

Chapter 17. Logging

17.1. Logging configuration

The logging system in BIND 10 is configured through the Logging module. All BIND 10 modules will look at the @@ -1648,7 +1659,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

-

17.1.1. Loggers

+

17.1.1. Loggers

Within BIND 10, a message is logged through a component called a "logger". Different parts of BIND 10 log messages @@ -1669,7 +1680,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

(what to log), and the output_options (where to log). -

17.1.1.1. name (string)

+

17.1.1.1. name (string)

Each logger in the system has a name, the name being that of the component using it to log messages. For instance, if you want to configure logging for the resolver module, @@ -1742,7 +1753,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

Auth.cache logger will appear in the output with a logger name of b10-auth.cache). -

17.1.1.2. severity (string)

+

17.1.1.2. severity (string)

This specifies the category of messages logged. Each message is logged with an associated severity which @@ -1758,7 +1769,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

-

17.1.1.3. output_options (list)

+

17.1.1.3. output_options (list)

Each logger can have zero or more output_options. These specify where log @@ -1768,7 +1779,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

The other options for a logger are: -

17.1.1.4. debuglevel (integer)

+

17.1.1.4. debuglevel (integer)

When a logger's severity is set to DEBUG, this value specifies what debug messages should be printed. It ranges @@ -1777,7 +1788,7 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

If severity for the logger is not DEBUG, this value is ignored. -

17.1.1.5. additive (true or false)

+

17.1.1.5. additive (true or false)

If this is true, the output_options from the parent will be used. For example, if there are two @@ -1791,18 +1802,18 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

-

17.1.2. Output Options

+

17.1.2. Output Options

The main settings for an output option are the destination and a value called output, the meaning of which depends on the destination that is set. -

17.1.2.1. destination (string)

+

17.1.2.1. destination (string)

The destination is the type of output. It can be one of: -

  • console
  • file
  • syslog

17.1.2.2. output (string)

+

  • console
  • file
  • syslog

17.1.2.2. output (string)

Depending on what is set as the output destination, this value is interpreted as follows: @@ -1832,12 +1843,12 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

The other options for output_options are: -

17.1.2.2.1. flush (true of false)

+

17.1.2.2.1. flush (true of false)

Flush buffers after each log message. Doing this will reduce performance but will ensure that if the program terminates abnormally, all messages up to the point of termination are output. -

17.1.2.2.2. maxsize (integer)

+

17.1.2.2.2. maxsize (integer)

Only relevant when destination is file, this is maximum file size of output files in bytes. When the maximum size is reached, the file is renamed and a new file opened. @@ -1846,11 +1857,11 @@ const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";

etc.)

If this is 0, no maximum file size is used. -

17.1.2.2.3. maxver (integer)

+

17.1.2.2.3. maxver (integer)

Maximum number of old log files to keep around when rolling the output file. Only relevant when destination is file. -

17.1.3. Example session

+

17.1.3. Example session

In this example we want to set the global logging to write to the file /var/log/my_bind10.log, @@ -2011,7 +2022,7 @@ Logging/loggers[0]/output_options[0]/maxver 8 integer (modified) And every module will now be using the values from the logger named *. -

17.2. Logging Message Format

+

17.2. Logging Message Format

Each message written by BIND 10 to the configured logging destinations comprises a number of components that identify the origin of the message and, if the message indicates diff --git a/doc/guide/bind10-guide.txt b/doc/guide/bind10-guide.txt index e53885ee97..abec8534ec 100644 --- a/doc/guide/bind10-guide.txt +++ b/doc/guide/bind10-guide.txt @@ -968,9 +968,18 @@ Chapter 8. Authoritative Server > config set data_sources/classes/IN[1]/params { "example.org": "/path/to/example.org", "example.com": "/path/to/example.com" } > config commit - Unfortunately, due to current technical limitations, the params must be - set as one JSON blob, it can't be edited in bindctl. To reload a zone, you - the same command as above. + Initially, a map value has to be set, but this value may be an empty map. + After that, key/value pairs can be added with 'config add' and keys can be + removed with 'config remove'. The initial value may be an empty map, but + it has to be set before zones are added or removed. + + > config set data_sources/classes/IN[1]/params {} + > config add data_sources/classes/IN[1]/params another.example.org /path/to/another.example.org + > config add data_sources/classes/IN[1]/params another.example.com /path/to/another.example.com + > config remove data_sources/classes/IN[1]/params another.example.org + + + bindctl. To reload a zone, you the same command as above. Note diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 90ed1f1b5d..7952d99cc5 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -1611,8 +1611,19 @@ can use various data source backends. > config set data_sources/classes/IN[1]/params { "example.org": "/path/to/example.org", "example.com": "/path/to/example.com" } > config commit - Unfortunately, due to current technical limitations, the params must - be set as one JSON blob, it can't be edited in + Initially, a map value has to be set, but this value may be an + empty map. After that, key/value pairs can be added with 'config + add' and keys can be removed with 'config remove'. The initial + value may be an empty map, but it has to be set before zones are + added or removed. + + +> config set data_sources/classes/IN[1]/params {} +> config add data_sources/classes/IN[1]/params another.example.org /path/to/another.example.org +> config add data_sources/classes/IN[1]/params another.example.com /path/to/another.example.com +> config remove data_sources/classes/IN[1]/params another.example.org + + bindctl. To reload a zone, you the same command as above. diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html index 4a4f6b2337..688027245b 100644 --- a/doc/guide/bind10-messages.html +++ b/doc/guide/bind10-messages.html @@ -1,4 +1,4 @@ -BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version +BIND 10 Messages Manual

BIND 10 Messages Manual

This is the messages manual for BIND 10 version 20120712.

Abstract

BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and diff --git a/src/lib/config/tests/testdata/spec40.spec b/src/lib/config/tests/testdata/spec40.spec index f778fc09a2..6fbec10f3f 100644 --- a/src/lib/config/tests/testdata/spec40.spec +++ b/src/lib/config/tests/testdata/spec40.spec @@ -6,6 +6,15 @@ "item_type": "any", "item_optional": false, "item_default": "asdf" + }, + { "item_name": "item2", + "item_type": "any", + "item_optional": true + }, + { "item_name": "item3", + "item_type": "any", + "item_optional": true, + "item_default": null } ] } diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py index 703d1968eb..a95316d6bd 100644 --- a/src/lib/python/isc/config/ccsession.py +++ b/src/lib/python/isc/config/ccsession.py @@ -144,7 +144,7 @@ class ModuleCCSession(ConfigData): module, and one to update the configuration run-time. These callbacks are called when 'check_command' is called on the ModuleCCSession""" - + def __init__(self, spec_file_name, config_handler, command_handler, cc_session=None, handle_logging_config=True, socket_file = None): @@ -178,9 +178,9 @@ class ModuleCCSession(ConfigData): """ module_spec = isc.config.module_spec_from_file(spec_file_name) ConfigData.__init__(self, module_spec) - + self._module_name = module_spec.get_module_name() - + self.set_config_handler(config_handler) self.set_command_handler(command_handler) @@ -248,7 +248,7 @@ class ModuleCCSession(ConfigData): returns nothing. It calls check_command_without_recvmsg() to parse the received message. - + If nonblock is True, it just checks if there's a command and does nothing if there isn't. If nonblock is False, it waits until it arrives. It temporarily sets timeout to infinity, @@ -265,7 +265,7 @@ class ModuleCCSession(ConfigData): """Parse the given message to see if there is a command or a configuration update. Calls the corresponding handler functions if present. Responds on the channel if the - handler returns a message.""" + handler returns a message.""" # should we default to an answer? success-by-default? unhandled error? if msg is not None and not 'result' in msg: answer = None @@ -314,7 +314,7 @@ class ModuleCCSession(ConfigData): answer = create_answer(1, str(exc)) if answer: self._session.group_reply(env, answer) - + def set_config_handler(self, config_handler): """Set the config handler for this module. The handler is a function that takes the full configuration and handles it. @@ -521,7 +521,7 @@ class UIModuleCCSession(MultiConfigData): if not cur_list: cur_list = [] - if value is None: + if value is None and "list_item_spec" in module_spec: if "item_default" in module_spec["list_item_spec"]: value = module_spec["list_item_spec"]["item_default"] @@ -572,8 +572,14 @@ class UIModuleCCSession(MultiConfigData): if module_spec is None: raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier)) + # for type any, we determine the 'type' by what value is set + # (which would be either list or dict) + cur_value, _ = self.get_value(identifier) + type_any = module_spec['item_type'] == 'any' + # the specified element must be a list or a named_set - if 'list_item_spec' in module_spec: + if 'list_item_spec' in module_spec or\ + (type_any and type(cur_value) == list): value = None # in lists, we might get the value with spaces, making it # the third argument. In that case we interpret both as @@ -583,11 +589,12 @@ class UIModuleCCSession(MultiConfigData): value_str += set_value_str value = isc.cc.data.parse_value_str(value_str) self._add_value_to_list(identifier, value, module_spec) - elif 'named_set_item_spec' in module_spec: + elif 'named_set_item_spec' in module_spec or\ + (type_any and type(cur_value) == dict): item_name = None item_value = None if value_str is not None: - item_name = isc.cc.data.parse_value_str(value_str) + item_name = value_str if set_value_str is not None: item_value = isc.cc.data.parse_value_str(set_value_str) else: @@ -643,12 +650,23 @@ class UIModuleCCSession(MultiConfigData): if value_str is not None: value = isc.cc.data.parse_value_str(value_str) - if 'list_item_spec' in module_spec: - if value is not None: + # for type any, we determine the 'type' by what value is set + # (which would be either list or dict) + cur_value, _ = self.get_value(identifier) + type_any = module_spec['item_type'] == 'any' + + # there's two forms of 'remove from list'; the remove-value-from-list + # form, and the 'remove-by-index' form. We can recognize the second + # case by value is None + if 'list_item_spec' in module_spec or\ + (type_any and type(cur_value) == list) or\ + value is None: + if not type_any and value is not None: isc.config.config_data.check_type(module_spec['list_item_spec'], value) self._remove_value_from_list(identifier, value) - elif 'named_set_item_spec' in module_spec: - self._remove_value_from_named_set(identifier, value) + elif 'named_set_item_spec' in module_spec or\ + (type_any and type(cur_value) == dict): + self._remove_value_from_named_set(identifier, value_str) else: raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named_set") diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py index 174e98c911..413d052b0b 100644 --- a/src/lib/python/isc/config/config_data.py +++ b/src/lib/python/isc/config/config_data.py @@ -204,6 +204,9 @@ def find_spec_part(element, identifier, strict_identifier = True): # always want the 'full' spec of the item for id_part in id_parts[:-1]: cur_el = _find_spec_part_single(cur_el, id_part) + # As soon as we find 'any', return that + if cur_el["item_type"] == "any": + return cur_el if strict_identifier and spec_part_is_list(cur_el) and\ not isc.cc.data.identifier_has_list_index(id_part): raise isc.cc.data.DataNotFoundError(id_part + @@ -553,7 +556,6 @@ class MultiConfigData: if 'item_default' in spec: # one special case, named_set if spec['item_type'] == 'named_set': - print("is " + id_part + " in named set?") return spec['item_default'] else: return spec['item_default'] @@ -582,6 +584,14 @@ class MultiConfigData: value = self.get_default_value(identifier) if value is not None: return value, self.DEFAULT + else: + # get_default_value returns None for both + # the cases where there is no default, and where + # it is set to null, so we need to catch the latter + spec_part = self.find_spec_part(identifier) + if spec_part and 'item_default' in spec_part and\ + spec_part['item_default'] is None: + return None, self.DEFAULT return None, self.NONE def _append_value_item(self, result, spec_part, identifier, all, first = False): @@ -742,6 +752,8 @@ class MultiConfigData: # list cur_list = cur_value for list_index in list_indices: + if type(cur_list) != list: + raise isc.cc.data.DataTypeError(id + " is not a list") if list_index >= len(cur_list): raise isc.cc.data.DataNotFoundError("No item " + str(list_index) + " in " + id_part) diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py index d1060bf008..0101d50fb1 100644 --- a/src/lib/python/isc/config/tests/ccsession_test.py +++ b/src/lib/python/isc/config/tests/ccsession_test.py @@ -33,7 +33,7 @@ class TestHelperFunctions(unittest.TestCase): self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [] }) self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [ 'not_an_rcode' ] }) self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [ 1, 2 ] }) - + rcode, val = parse_answer({ 'result': [ 0 ] }) self.assertEqual(0, rcode) self.assertEqual(None, val) @@ -107,7 +107,7 @@ class TestModuleCCSession(unittest.TestCase): def spec_file(self, file): return self.data_path + os.sep + file - + def create_session(self, spec_file_name, config_handler = None, command_handler = None, cc_session = None): return ModuleCCSession(self.spec_file(spec_file_name), @@ -335,7 +335,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [1, 'No config_data specification']}, fake_session.get_message('Spec1', None)) - + def test_check_command3(self): fake_session = FakeModuleCCSession() mccs = self.create_session("spec2.spec", None, None, fake_session) @@ -348,7 +348,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [0]}, fake_session.get_message('Spec2', None)) - + def test_check_command4(self): fake_session = FakeModuleCCSession() mccs = self.create_session("spec2.spec", None, None, fake_session) @@ -361,7 +361,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [1, 'aaa should be an integer']}, fake_session.get_message('Spec2', None)) - + def test_check_command5(self): fake_session = FakeModuleCCSession() mccs = self.create_session("spec2.spec", None, None, fake_session) @@ -374,7 +374,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [1, 'aaa should be an integer']}, fake_session.get_message('Spec2', None)) - + def test_check_command6(self): fake_session = FakeModuleCCSession() mccs = self.create_session("spec2.spec", None, None, fake_session) @@ -460,7 +460,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [1, 'No config_data specification']}, fake_session.get_message('Spec1', None)) - + def test_check_command_without_recvmsg2(self): "copied from test_check_command3" fake_session = FakeModuleCCSession() @@ -474,7 +474,7 @@ class TestModuleCCSession(unittest.TestCase): self.assertEqual(len(fake_session.message_queue), 1) self.assertEqual({'result': [0]}, fake_session.get_message('Spec2', None)) - + def test_check_command_without_recvmsg3(self): "copied from test_check_command7" fake_session = FakeModuleCCSession() @@ -487,7 +487,7 @@ class TestModuleCCSession(unittest.TestCase): mccs.check_command_without_recvmsg(cmd, env) self.assertEqual({'result': [0]}, fake_session.get_message('Spec2', None)) - + def test_check_command_block_timeout(self): """Check it works if session has timeout and it sets it back.""" def cmd_check(mccs, session): @@ -893,22 +893,22 @@ class fakeUIConn(): def set_get_answer(self, name, answer): self.get_answers[name] = answer - + def set_post_answer(self, name, answer): self.post_answers[name] = answer - + def send_GET(self, name, arg = None): if name in self.get_answers: return self.get_answers[name] else: return {} - + def send_POST(self, name, arg = None): if name in self.post_answers: return self.post_answers[name] else: return fakeAnswer() - + class TestUIModuleCCSession(unittest.TestCase): def setUp(self): @@ -919,9 +919,9 @@ class TestUIModuleCCSession(unittest.TestCase): def spec_file(self, file): return self.data_path + os.sep + file - - def create_uccs2(self, fake_conn): - module_spec = isc.config.module_spec_from_file(self.spec_file("spec2.spec")) + + def create_uccs(self, fake_conn, specfile="spec2.spec"): + module_spec = isc.config.module_spec_from_file(self.spec_file(specfile)) fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()}) fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION }) return UIModuleCCSession(fake_conn) @@ -989,7 +989,7 @@ class TestUIModuleCCSession(unittest.TestCase): def test_add_remove_value(self): fake_conn = fakeUIConn() - uccs = self.create_uccs2(fake_conn) + uccs = self.create_uccs(fake_conn) self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, 1, "a") self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "no_such_item", "a") @@ -1020,6 +1020,88 @@ class TestUIModuleCCSession(unittest.TestCase): self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec2/item5", None) + # Check that the difference between no default and default = null + # is recognized + def test_default_null(self): + fake_conn = fakeUIConn() + uccs = self.create_uccs(fake_conn, "spec40.spec") + (value, status) = uccs.get_value("/Spec40/item2") + self.assertIsNone(value) + self.assertEqual(uccs.NONE, status) + (value, status) = uccs.get_value("/Spec40/item3") + self.assertIsNone(value) + self.assertEqual(uccs.DEFAULT, status) + + # Test adding and removing values for type = any + def test_add_remove_value_any(self): + fake_conn = fakeUIConn() + uccs = self.create_uccs(fake_conn, "spec40.spec") + + # Test item set of basic types + items = [ 1234, "foo", True, False ] + items_as_str = [ '1234', 'foo', 'true', 'false' ] + + def test_fails(): + self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec40/item1", "foo") + self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec40/item1", "foo", "bar") + self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "Spec40/item1", "foo") + self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec40/item1[0]", None) + + # A few helper functions to perform a number of tests + # (to repeat the same test for nested data) + def check_list(identifier): + for item in items_as_str: + uccs.add_value(identifier, item) + self.assertEqual((items, 1), uccs.get_value(identifier)) + + # Removing from list should work in both ways + uccs.remove_value(identifier, "foo") + uccs.remove_value(identifier + "[1]", None) + self.assertEqual(([1234, False], 1), uccs.get_value(identifier)) + + # As should item indexing + self.assertEqual((1234, 1), uccs.get_value(identifier + "[0]")) + self.assertEqual((False, 1), uccs.get_value(identifier + "[1]")) + + def check_named_set(identifier): + for item in items_as_str: + # use string version as key as well + uccs.add_value(identifier, item, item) + + self.assertEqual((1234, 1), uccs.get_value(identifier + "/1234")) + self.assertEqual((True, 1), uccs.get_value(identifier + "/true")) + + for item in items_as_str: + # use string version as key as well + uccs.remove_value(identifier, item) + + + # should fail when set to value of primitive type + for item in items: + uccs.set_value("Spec40/item1", item) + test_fails() + + # When set to list, add and remove should work, and its elements + # should be considered of type 'any' themselves. + uccs.set_value("Spec40/item1", []) + check_list("Spec40/item1") + + # When set to dict, it should have the behaviour of a named set + uccs.set_value("Spec40/item1", {}) + check_named_set("Spec40/item1") + + # And, or course, we may need nesting. + uccs.set_value("Spec40/item1", { "foo": {}, "bar": [] }) + check_named_set("Spec40/item1/foo") + check_list("Spec40/item1/bar") + uccs.set_value("Spec40/item1", [ {}, [] ] ) + check_named_set("Spec40/item1[0]") + check_list("Spec40/item1[1]") + uccs.set_value("Spec40/item1", [[[[[[]]]]]] ) + check_list("Spec40/item1[0][0][0][0][0]") + uccs.set_value("Spec40/item1", { 'a': { 'a': { 'a': {} } } } ) + check_named_set("Spec40/item1/a/a/a") + def test_add_dup_value(self): fake_conn = fakeUIConn() uccs = self.create_uccs_listtest(fake_conn) @@ -1101,7 +1183,7 @@ class TestUIModuleCCSession(unittest.TestCase): def test_commit(self): fake_conn = fakeUIConn() - uccs = self.create_uccs2(fake_conn) + uccs = self.create_uccs(fake_conn) uccs.commit() uccs._local_changes = {'Spec2': {'item5': [ 'a' ]}} uccs.commit()