diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/doc/examples/kea6/iPXE.json b/doc/examples/kea6/iPXE.json index b9c3cceca1..9d38c4c4e0 100644 --- a/doc/examples/kea6/iPXE.json +++ b/doc/examples/kea6/iPXE.json @@ -1,13 +1,11 @@ -// This is and example configuration for iPXE boot in Kea6. +// This is an example configuration for iPXE boot in Kea6. { "Dhcp6": { - // mandatory part of the config that list interfaces on which - // kea will listen to incoming traffic + // Mandatory part of the config that list interfaces on which + // Kea will listen for incoming traffic. "interfaces-config": { - "interfaces": [ - "ethX" - ] + "interfaces": [ "ethX" ] }, // Two classes are migrated form ISC-DHCP example: diff --git a/doc/guide/ctrl-channel.xml b/doc/guide/ctrl-channel.xml index 18f9b542a7..04907a0573 100644 --- a/doc/guide/ctrl-channel.xml +++ b/doc/guide/ctrl-channel.xml @@ -175,7 +175,7 @@ the form: { - "result": 0|1, + "result": 0|1|2|3, "text": "textual description", "arguments": { "argument1": "value1", @@ -185,14 +185,26 @@ } result indicates the outcome of the command. A value of 0 - means success while any non-zero value designates an error. Currently 1 is - used as a generic error, but additional error codes may be added in the - future. The text field typically appears when result is - non-zero and contains a description of the error encountered, but it may - also appear for successful results (that is command specific). - arguments is a map of additional data values returned by - the server which is specific to the command issued. The map is always present, even - if it contains no data values. + means success while any non-zero value designates an error or at least a + failure to complete the requested action. Currently 1 is used as a generic + error, 2 means that a command is not supported and 3 means that the + requested operation was completed, but the requested object was not + found. Additional error codes may be added in the future. For example a well + formed command that requests a subnet that exists in server's configuration + would return result 0. If the server encounters an error condition, it would + return 1. If the command was asking for IPv6 subnet, but was sent to DHCPv4 + server, it would return 2. If the query was asking for a subnet-id and there + is no subnet with such id, the result would be set to 3. + + + The text field typically appears when result is non-zero + and contains a description of the error encountered, but it often also appears + for successful outcomes. The exact text is command specific, but in general + uses plain English to describe the outcome of the command. + arguments is a map of additional data values + returned by the server which is specific to the command issued. The map is + may be present, but that depends on specific command. + @@ -211,7 +223,7 @@ Kea development team is actively working on providing client applications which can be used to control the servers. These applications are, however, in the early stages of development and as of Kea 1.2.0 release have certain limitations. - The easiest way to start playing with the control API is to use common Unix/Linux tools + The easiest way to start interacting with the control API is to use common Unix/Linux tools such as socat and curl. In order to control the given Kea service via unix domain socket, use @@ -222,14 +234,16 @@ $ socat UNIX:/path/to/the/kea/socket - where /path/to/the/kea/socket is the path specified in the Dhcp4/control-socket/socket-name parameter in the Kea configuration file. Text passed to socat -will be sent to Kea and the responses received from Kea printed to standard output. +will be sent to Kea and the responses received from Kea printed to standard + output. This approach communicates with the specific server directly and + bypasses Control Agent. It is also easy to open UNIX socket programmatically. An example of such a simplistic client written in C is available in the Kea Developer's Guide, chapter Control Channel Overview, section Using Control Channel. - In order to use Kea's RESTful API with curl try the - following: + In order to use Kea's RESTful API with curl you may + use the following: $ curl -X POST -H "Content-Type: application/json" -d '{ "command": "config-get", "service": [ "dhcp4" ] }' http://ca.example.org:8000/ diff --git a/doc/guide/hooks.xml b/doc/guide/hooks.xml index f95795e2d7..3be72d337b 100644 --- a/doc/guide/hooks.xml +++ b/doc/guide/hooks.xml @@ -1721,12 +1721,15 @@ An example IPv4 lease deletion by "hw-address" looks as follows: subnet_cmds: Subnet Commands This section describes a hook application that offers a number of new - commands used to query and manipulate subnet configurations in Kea. - This application is very useful in deployments with a large number of - subnets being managed by the DHCP servers and when the subnets are - frequently updated. The commands offer lightweight approach for - manipulating subnets without a need to fully reconfigure the server - and without affecting existing servers' configurations. + commands used to query and manipulate subnet and shared network + configurations in Kea. This application is very useful in deployments + with a large number of subnets being managed by the DHCP servers and + when the subnets are frequently updated. The commands offer + lightweight approach for manipulating subnets without a need to fully + reconfigure the server and without affecting existing servers' + configurations. An ability to manage shared networks (listing, + retrieving details, adding new ones, removing existing ones, adding + subnets to and removing from shared networks) is also provided. Currently this library is only available to ISC customers with a @@ -1740,7 +1743,7 @@ An example IPv4 lease deletion by "hw-address" looks as follows: - subnet4-get/subnet6-get: retrieves detailed information about a selected subnet + subnet4-get/subnet6-get: retrieves detailed information about a specified subnet @@ -1753,6 +1756,45 @@ An example IPv4 lease deletion by "hw-address" looks as follows: subnet4-del/subnet6-del: removes a subnet from the server's configuration + + + + network4-list/network6-list: lists all configured + shared networks + + + + + network4-get/network6-get: retrieves detailed + information about specified shared network + + + + + network4-add/network6-add: adds a new shared + network to the server's configuration + + + + + network4-del/network6-del: removes a shared + network from the server's configuration + + + + + network4-subnet-add/network6-subnet-add: adds + existing subnet to existing shared network + + + + + network4-subnet-del/network6-subnet-del: removes + a subnet from existing shared network and demotes it to a plain + subnet. + + + @@ -2094,6 +2136,12 @@ If the subnet exists the response will be similar to this: new subnet. Thus, we recommend that this command is used with extreme caution. + + This command can also be used to completely delete an IPv4 subnet that + is part of a shared network. If you want to simply remove the subnet + from a shared network and keep the subnet configuration, use + network4-subnet-del command instead. + The command has the following structure: @@ -2148,6 +2196,12 @@ If the subnet exists the response will be similar to this: new subnet. Thus, we recommend that this command is used with extreme caution. + + This command can also be used to completely delete an IPv6 subnet that + is part of a shared network. If you want to simply remove the subnet + from a shared network and keep the subnet configuration, use + network6-subnet-del command instead. + The command has the following structure: @@ -2176,8 +2230,305 @@ If the subnet exists the response will be similar to this: +
+ network4-list, network6-list commands + + These commands are used to retrieve full list of currently configured + shared networks. The list contains only very basic information about + each shared network. If more details are needed, please use + network4-get or network6-get to + retrieve all information available. This command does not require any + parameters and its invocation is very simple: + +{ + "command": "network4-list" +} + +An example response for network4-list looks as follows: + +{ + "arguments": { + "shared-networks": [ + { "name": "floor1" }, + { "name": "office" } + ] + }, + "result": 0, + "text": "2 IPv4 network(s) found" +} +network6-list follows exactly the same syntax for +both the query and the response. +
- + +
+ network4-get, network6-get commands + + These commands are used to retrieve detailed information + about shared networks, including subnets currently + being part of a given network. Both commands take one + mandatory parameter name, which specify + the name of shared network. An example command to retrieve + details about IPv4 shared network with a name "floor13" + looks as follows: + +{ + "command": "network4-get", + "arguments": { + "name": "floor13" + } +} +An example response could look as follows: + +{ + "result": 0, + "text": "Info about IPv4 shared network 'floor13' returned", + "arguments": { + "shared-networks": [ + { + "match-client-id": true, + "name": "floor13", + "option-data": [ ], + "rebind-timer": 90, + "relay": { + "ip-address": "0.0.0.0" + }, + "renew-timer": 60, + "reservation-mode": "all", + "subnet4": [ + { + "subnet": "192.0.2.0/24", + "id": 5, + // many other subnet specific details here + }, + { + "id": 6, + "subnet": "192.0.3.0/31", + // many other subnet specific details here + } + ], + "valid-lifetime": 120 + } + ] + } +} + +Note that actual response contains many additional fields that are +omitted here for clarity. The response format is exactly the same as +used in config-get, just is limited to returning +shared networks information. + +
+ +
+ network4-add, network6-add commands + + These commands are used to add a new shared network. New + network has to have unique name. This command requires one parameter + shared-networks, which is a list and + should contain exactly one entry that defines the + network. The only mandatory element for a network is its + name. Although it does not make operational sense, it is + allowed to add an empty shared network that does not have + any subnets in it. That is allowed for testing purposes, but + having empty networks (or with only one subnet) is + discouraged in production environments. For details regarding + syntax, see and . + + As opposed to parameter inheritance during full + new configuration processing, this command does not fully handle + parameter inheritance and any missing parameters will be + filled with default values, rather than inherited from + global scope. + + An example that showcases how to add a new IPv4 shared network looks + as follows: + +{ + "command": "network4-add", + "arguments": { + "shared-networks": [ { + "name": "floor13", + "subnet4": [ + { + "id": 100, + "pools": [ { "pool": "192.0.2.2-192.0.2.99" } ], + "subnet": "192.0.2.0/24", + "option-data": [ + { + "name": "routers", + "data": "192.0.2.1" + } + ] + }, + { + "id": 101, + "pools": [ { "pool": "192.0.3.2-192.0.3.99" } ], + "subnet": "192.0.3.0/24", + "option-data": [ + { + "name": "routers", + "data": "192.0.3.1" + } + ] + } ] + } ] + } +} + +Assuming there was no shared network with a name floor13 and no subnets with id +100 and 101 previously configured, the command will be successful and will +return the following response: + +{ + "arguments": { + "shared-networks": [ { "name": "floor13" } ] + }, + "result": 0, + "text": "A new IPv4 shared network 'floor13' added" +} + +The network6-add uses the same syntax for both the query and +the response. However, there are some parameters that are IPv4-only +(e.g. match-client-id) and some are IPv6-only (e.g. interface-id). The same +applies to subnets within the network. + +
+
+ network4-del, network6-del commands + + These commands are used to delete existing shared networks. Each + subnet within the network being removed will be demoted to a plain + subnet. If you want to completely remove the subnets, please use + subnet4-del or subnet6-del + commands. Both commands take exactly one parameter 'name' that + specifies the name of the network to be removed. An example invocation + of network4-del command looks as follows: + +{ + "command": "network4-del", + "arguments": { + "name": "floor13" + } +} + +Assuming there was such a network configured, the response will look similar to +the following: + +{ + "arguments": { + "shared-networks": [ + { + "name": "floor1" + } + ] + }, + "result": 0, + "text": "IPv4 shared network 'floor13' deleted" +} +The network6-del command uses exactly the same syntax for +both the command and the response. + + +
+
+ network4-subnet-add, network6-subnet-add commands + + These commands are used to add existing subnets to existing shared + networks. There are several ways to add new shared network. System + administrator can add the whole shared network at once, either by + editing a configuration file or by calling + network4-add or network6-add + commands with desired subnets in it. This approach works better for completely + new shared subnets. However, there may be cases when an existing + subnet is running out of addresses and needs to be extended with + additional address space. In other words another subnet has to be + added on top of it. For this scenario, a system administrator can use + network4-add or network6-add and + then add existing subnet to this newly created shared network using + network4-subnet-add or + network6-subnet-add. + + + The network4-subnet-add and + network6-subnet-add commands take two parameters: + id, which is an integer and specifies subnet-id of existing subnet to + be added to a shared network; and name, which + specifies name of the shared network the subnet will be added to. The + subnet must not belong to any existing network. In case you want to + reassign a subnet from one shared network to another, please use + network4-subnet-del or + network6-subnet-del commands first. + + + An example invocation of network4-subnet-add + command looks as follows: + +{ + "command": "network4-subnet-add", + "arguments": { + "name": "floor13", + "id": 5 + } +} +Assuming there is a network named 'floor13', there is a subnet with subnet-id 5 +and it is not a part of existing network, the command will return a response +similar to the following: + +{ + "result": 0, + "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now part of shared network 'floor1'" +} + The network6-subnet-add command uses exactly the same syntax for +both the command and the response. + + + As opposed to parameter inheritance during full + new configuration processing or when adding a new shared network with + new subnets, this command does not fully handle + parameter inheritance and any missing parameters will be + filled with default values, rather than inherited from + global scope or from the shared network. +
+
+ network4-subnet-del, network6-subnet-del commands + + These commands are used to remove a subnet that is part of existing shared + network and demote it to a plain, stand-alone subnet. If you want to + remove a subnet completely, use subnet4-del or + subnet6-del commands instead. + The network4-subnet-del and + network6-subnet-del commands take two parameters: + id, which is an integer and specifies subnet-id of + existing subnet to be removed from a shared network; and + name, which specifies name of the shared network + the subnet will be removed from. + + An example invocation of the + network4-subnet-del command looks as follows: + + { + "command": "network4-subnet-del", + "arguments": { + "name": "floor13", + "id": 5 + } + } + Assuming there was a subnet with subnet-id equal to 5 that was part of a shared + network named 'floor13', the response would look similar to the following: + +{ + "result": 0, + "text": "IPv4 subnet 10.0.0.0/8 (id 5) is now removed from shared network 'floor13'" +} +The network6-subnet-del command uses exactly the same syntax for +both the command and the response. + +
+ + +
User contexts diff --git a/premium b/premium new file mode 160000 index 0000000000..133a3165f2 --- /dev/null +++ b/premium @@ -0,0 +1 @@ +Subproject commit 133a3165f244adb0ed50cc3a59e3e4a86f1e10bc diff --git a/src/lib/dhcpsrv/cfg_shared_networks.h b/src/lib/dhcpsrv/cfg_shared_networks.h index 252e5e4a22..291d2bb722 100644 --- a/src/lib/dhcpsrv/cfg_shared_networks.h +++ b/src/lib/dhcpsrv/cfg_shared_networks.h @@ -58,8 +58,11 @@ public: auto& index = networks_.template get(); auto shared_network = index.find(name); if (shared_network != index.end()) { - index.erase(shared_network); + // Delete all subnets from the network + (*shared_network)->delAll(); + // Then delete the network from the networks list. + index.erase(shared_network); } else { isc_throw(BadValue, "unable to delete non-existing network '" << name << "' from shared networks configuration"); diff --git a/src/lib/dhcpsrv/shared_network.cc b/src/lib/dhcpsrv/shared_network.cc index fba654a8c3..54e4e55acd 100644 --- a/src/lib/dhcpsrv/shared_network.cc +++ b/src/lib/dhcpsrv/shared_network.cc @@ -219,6 +219,14 @@ SharedNetwork4::del(const SubnetID& subnet_id) { clearSharedNetwork(subnet); } +void +SharedNetwork4::delAll() { + for (auto subnet = subnets_.cbegin(); subnet != subnets_.cend(); ++subnet) { + clearSharedNetwork(*subnet); + } + subnets_.clear(); +} + Subnet4Ptr SharedNetwork4::getSubnet(const SubnetID& subnet_id) const { return (Impl::getSubnet(subnets_, subnet_id)); @@ -267,6 +275,13 @@ SharedNetwork6::del(const SubnetID& subnet_id) { clearSharedNetwork(subnet); } +void +SharedNetwork6::delAll() { + for (auto subnet = subnets_.cbegin(); subnet != subnets_.cend(); ++subnet) { + clearSharedNetwork(*subnet); + } + subnets_.clear(); +} Subnet6Ptr SharedNetwork6::getSubnet(const SubnetID& subnet_id) const { return (Impl::getSubnet(subnets_, subnet_id)); diff --git a/src/lib/dhcpsrv/shared_network.h b/src/lib/dhcpsrv/shared_network.h index 838fca73f5..31d0d43d97 100644 --- a/src/lib/dhcpsrv/shared_network.h +++ b/src/lib/dhcpsrv/shared_network.h @@ -86,6 +86,9 @@ public: /// @throw BadValue When specified subnet doesn't exist. void del(const SubnetID& subnet_id); + /// @brief Removes all subnets from a shared network. + void delAll(); + /// @brief Returns a pointer to the collection of subnets within this /// shared network. const Subnet4Collection* getAllSubnets() const { @@ -217,6 +220,9 @@ public: /// @throw BadValue When specified subnet doesn't exist. void del(const SubnetID& subnet_id); + /// @brief Removes all subnets from a shared network. + void delAll(); + /// @brief Returns a pointer to the collection of subnets within this /// shared network. const Subnet6Collection* getAllSubnets() const { diff --git a/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc index c90d21f0e8..cd82ab3797 100644 --- a/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc @@ -8,10 +8,12 @@ #include #include #include +#include #include using namespace isc; using namespace isc::dhcp; +using namespace asiolink; namespace { @@ -62,6 +64,37 @@ TEST(CfgSharedNetworks4Test, deleteByName) { ASSERT_THROW(cfg.del(network2->getName()), BadValue); } +// Checks that subnets have their shared network pointers updated when +// the network is deleted. This is used when the shared network is deleted +// by admin commands. +TEST(CfgSharedNetworks4Test, deleteNetworkWithSubnets) { + CfgSharedNetworks4 cfg; + SharedNetwork4Ptr network(new SharedNetwork4("frog")); + SubnetID id1(100); + SubnetID id2(101); + Subnet4Ptr sub1(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, id1)); + Subnet4Ptr sub2(new Subnet4(IOAddress("192.0.3.0"), 24, 1, 2, 3, id2)); + network->add(sub1); + network->add(sub2); + cfg.add(network); + + // Make sure the subnets are part of the network. + SharedNetwork4Ptr test; + sub1->getSharedNetwork(test); + EXPECT_TRUE(test); + EXPECT_EQ(network->toElement()->str(), test->toElement()->str()); + sub2->getSharedNetwork(test); + EXPECT_TRUE(test); + EXPECT_EQ(network->toElement()->str(), test->toElement()->str()); + + // Now remove the network. Subnets should be disassociated with the network. + cfg.del("frog"); + sub1->getSharedNetwork(test); + EXPECT_FALSE(test); + sub2->getSharedNetwork(test); + EXPECT_FALSE(test); +} + // This test verifies that shared networks must have unique names. TEST(CfgSharedNetworks4Test, duplicateName) { SharedNetwork4Ptr network1(new SharedNetwork4("frog")); diff --git a/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc index 5763cd6bd9..92e84ad3d9 100644 --- a/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc @@ -7,11 +7,13 @@ #include #include #include +#include #include #include using namespace isc; using namespace isc::dhcp; +using namespace asiolink; namespace { @@ -62,6 +64,37 @@ TEST(CfgSharedNetworks6Test, deleteByName) { ASSERT_THROW(cfg.del(network2->getName()), BadValue); } +// Checks that subnets have their shared network pointers updated when +// the network is deleted. This is used when the shared network is deleted +// by admin commands. +TEST(CfgSharedNetworks6Test, deleteNetworkWithSubnets) { + CfgSharedNetworks6 cfg; + SharedNetwork6Ptr network(new SharedNetwork6("frog")); + SubnetID id1(100); + SubnetID id2(101); + Subnet6Ptr sub1(new Subnet6(IOAddress("2001:db8::"), 48, 1, 2, 3, 4, id1)); + Subnet6Ptr sub2(new Subnet6(IOAddress("fec0::"), 12, 1, 2, 3, 4, id2)); + network->add(sub1); + network->add(sub2); + cfg.add(network); + + // Make sure the subnets are part of the network. + SharedNetwork6Ptr test; + sub1->getSharedNetwork(test); + EXPECT_TRUE(test); + EXPECT_EQ(network->toElement()->str(), test->toElement()->str()); + sub2->getSharedNetwork(test); + EXPECT_TRUE(test); + EXPECT_EQ(network->toElement()->str(), test->toElement()->str()); + + // Now remove the network. Subnets should be disassociated with the network. + cfg.del("frog"); + sub1->getSharedNetwork(test); + EXPECT_FALSE(test); + sub2->getSharedNetwork(test); + EXPECT_FALSE(test); +} + // This test verifies that shared networks must have unique names. TEST(CfgSharedNetworks6Test, duplicateName) { SharedNetwork6Ptr network1(new SharedNetwork6("frog")); diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc index 30aedd054f..63508ec44e 100644 --- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc @@ -288,6 +288,27 @@ TEST(SharedNetwork4Test, destructSharedNetwork) { ASSERT_FALSE(subnet_to_network); } +// This test verifies that it is possible to remove all subnets. +TEST(SharedNetwork4Test, delAll) { + // Create two subnets and add them to the shared network. + Subnet4Ptr subnet1(new Subnet4(IOAddress("10.0.0.0"), 8, 10, 20, 30, + SubnetID(1))); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.0"), 24, 10, 20, 30, + SubnetID(2))); + + SharedNetwork4Ptr network(new SharedNetwork4("frog")); + ASSERT_NO_THROW(network->add(subnet1)); + ASSERT_NO_THROW(network->add(subnet2)); + + // Make sure they have been added successfully. + ASSERT_EQ(2, network->getAllSubnets()->size()); + + ASSERT_NO_THROW(network->delAll()); + + // Now check that there are no subnets. + ASSERT_EQ(0, network->getAllSubnets()->size()); +} + // This test verifies that shared network can be given a name and that // this name can be retrieved. TEST(SharedNetwork6Test, getName) { @@ -545,5 +566,26 @@ TEST(SharedNetwork6Test, destructSharedNetwork) { ASSERT_FALSE(subnet_to_network); } +// This test verifies that it is possible to remove all subnets. +TEST(SharedNetwork6Test, delAll) { + // Create two subnets and add them to the shared network. + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 64, 10, 20, 30, + 40, SubnetID(1))); + Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 16, 10, 20, 30, 40, + SubnetID(2))); + + SharedNetwork6Ptr network(new SharedNetwork6("frog")); + ASSERT_NO_THROW(network->add(subnet1)); + ASSERT_NO_THROW(network->add(subnet2)); + + // Make sure they have been added successfully. + ASSERT_EQ(2, network->getAllSubnets()->size()); + + ASSERT_NO_THROW(network->delAll()); + + // Now check that there are no subnets. + ASSERT_EQ(0, network->getAllSubnets()->size()); +} + } // end of anonymous namespace