From 29a504c82f3b776d8dc2f2942721cae5c29f025c Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Tue, 28 Nov 2017 12:45:51 +0100 Subject: [PATCH] [5351] checkpoint: added comment --- doc/examples/kea4/advanced.json | 6 +++ doc/examples/kea6/advanced.json | 6 +++ doc/guide/dhcp4-srv.xml | 3 ++ doc/guide/dhcp6-srv.xml | 3 ++ doc/guide/hooks.xml | 15 ++++++++ src/lib/dhcpsrv/pool.cc | 14 ++++++- src/lib/dhcpsrv/subnet.cc | 37 +++++++++++++++++-- .../dhcpsrv/tests/cfg_subnets4_unittest.cc | 20 +++++++++- .../dhcpsrv/tests/cfg_subnets6_unittest.cc | 20 ++++++++++ 9 files changed, 117 insertions(+), 7 deletions(-) diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json index 327047bcd2..03ff4dc98c 100644 --- a/doc/examples/kea4/advanced.json +++ b/doc/examples/kea4/advanced.json @@ -107,6 +107,10 @@ // It is intended to keep anything you may want to put there - comments, // extra designations, floor or department names etc. These structures // will be made available to Kea hooks. + // You can define multiple user-contexts in the same scope without + // the last one replacing previous values. A comment entry is + // translated into a user-context with a "comment" property so + // you can include comments inside the configuration itself. "subnet4": [ { "pools": [ { @@ -117,6 +121,8 @@ "user-context": { "comment": "Our first subnet!" } + // Equivalent using smart parser + // "comment": "Our first subnet!" }, { // This particular subnet has match-client-id value changed. diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json index a8e67ee973..8e0fb0faae 100644 --- a/doc/examples/kea6/advanced.json +++ b/doc/examples/kea6/advanced.json @@ -86,6 +86,10 @@ // structures. You can put anything you want in the user-context // as long as it is a valid JSON and it starts with a map (i.e. // is enclosed by curly brackets). + // You can define multiple user-contexts in the same scope without + // the last one replacing previous values. A comment entry is + // translated into a user-context with a "comment" property so + // you can include comments inside the configuration itself. "subnet6": [ { "pools": [ @@ -102,6 +106,8 @@ // Here's the user-context for the whole subnet. "user-context": { "comment": "Floor one, west wing" }, + // Equivalent using smart parser + // "comment": "Floor one, west wing", // This defines PD (prefix delegation) pools. In this case // we have only one pool. That consists of /64 prefixes diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index b0070184be..bc6ee58d2f 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -4683,6 +4683,9 @@ autogenerated IDs are not stable across configuration changes. It should be noted that Kea will not use that information, but will simply store and make it available to hook libraries. It is up to the hook library to extract that information and make use of it. + The parser translates "comment" entries into a user-context + with the entry, this allows to attach comments inside the + configuration itself. For more background information, see . diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index 06d5717aed..befbca5b3e 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -4650,6 +4650,9 @@ autogenerated IDs are not stable across configuration changes. it just stores it, making it available to the hook libraries. It is up to each hook library to extract the information and make use of it. + The parser translates "comment" entries into a user-context + with the entry, this allows to attach comments inside the + configuration itself. For more background information, see . diff --git a/doc/guide/hooks.xml b/doc/guide/hooks.xml index 3be72d337b..e1b8630fd0 100644 --- a/doc/guide/hooks.xml +++ b/doc/guide/hooks.xml @@ -2543,6 +2543,14 @@ both the command and the response. arbitrary complexity. Kea does not use that data on its own, simply stores and makes it available for the hook libraries. + + Usually when an entry is defined multiple times in the same scope + the last value is used overwriting previous values (silently: + this behavior should be fixed soon). With user contexts values + are combined at the first level, for instance multiple user context + with a "comment" entry gives an entry with the list of accumulated + values. + Another use case for user contexts may be storing comments and other information that will be retained by Kea. Regular comments are discarded @@ -2550,6 +2558,13 @@ both the command and the response. useful if you want your comments to survive config-set, config-get operations for example. + + The parser translates "comment" entries at locations user context + are valid into a user context with a "comment" entry. The pretty + print of a configuration does the opposite operation and puts + "comment" entries at the beginning of maps as it seems to be the + common usage. + As of Kea 1.3, the structures that allow user contexts are pools of all types (addresses and prefixes) and subnets. These are supported in both diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc index 77cdc46121..b57b0fb06a 100644 --- a/src/lib/dhcpsrv/pool.cc +++ b/src/lib/dhcpsrv/pool.cc @@ -80,10 +80,20 @@ Pool::toElement() const { // Prepare the map ElementPtr map = Element::createMap(); - // Set user-context + // Set user-context extracting comment ConstElementPtr context = getContext(); if (!isNull(context)) { - map->set("user-context", context); + if ((context->getType() == Element::map) && + context->contains("comment")) { + ElementPtr copied = isc::data::copy(context); + map->set("comment", copied->get("comment")); + copied->remove("comment"); + if (copied->size() > 0) { + map->set("user-context", copied); + } + } else { + map->set("user-context", context); + } } // Set pool options diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index 837faed171..d8482dd3b3 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -566,9 +566,20 @@ Subnet::toElement() const { map->set("subnet", Element::create(toText())); // Add user-context, but only if defined. Omit if it was not. + // Extract comment so it will be printed first. ConstElementPtr ctx = getContext(); if (ctx) { - map->set("user-context", ctx); + if ((ctx->getType() == Element::map) && + ctx->contains("comment")) { + ElementPtr copied = isc::data::copy(ctx); + map->set("comment",copied->get("comment")); + copied->remove("comment"); + if (copied->size() > 0) { + map->set("user-context", copied); + } + } else { + map->set("user-context", ctx); + } } return (map); @@ -638,7 +649,17 @@ Subnet6::toElement() const { // Set user-context ConstElementPtr context = (*pool)->getContext(); if (!isNull(context)) { - pool_map->set("user-context", context); + if ((context->getType() == Element::map) && + context->contains("comment")) { + ElementPtr copied = isc::data::copy(context); + pool_map->set("comment", copied->get("comment")); + copied->remove("comment"); + if (copied->size() > 0) { + pool_map->set("user-context", copied); + } + } else { + pool_map->set("user-context", context); + } } // Set pool options ConstCfgOptionPtr opts = (*pool)->getCfgOption(); @@ -692,7 +713,17 @@ Subnet6::toElement() const { // Set user-context ConstElementPtr context = pdpool->getContext(); if (!isNull(context)) { - pool_map->set("user-context", context); + if ((context->getType() == Element::map) && + context->contains("comment")) { + ElementPtr copied = isc::data::copy(context); + pool_map->set("comment", copied->get("comment")); + copied->remove("comment"); + if (copied->size() > 0) { + pool_map->set("user-context", copied); + } + } else { + pool_map->set("user-context", context); + } } // Set pool options ConstCfgOptionPtr opts = pdpool->getCfgOption(); diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index c8083d0c84..cfe0363bda 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -740,6 +740,11 @@ TEST(CfgSubnets4Test, unparseSubnet) { subnet2->setRelayInfo(IOAddress("10.0.0.1")); subnet3->setIface("eth1"); + data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }"); + subnet1->setContext(ctx1); + data::ElementPtr ctx2 = data::Element::createMap(); + subnet2->setContext(ctx2); + cfg.add(subnet1); cfg.add(subnet2); cfg.add(subnet3); @@ -747,6 +752,7 @@ TEST(CfgSubnets4Test, unparseSubnet) { // Unparse std::string expected = "[\n" "{\n" + " \"comment\": \"foo\",\n" " \"id\": 123,\n" " \"subnet\": \"192.0.2.0/26\",\n" " \"relay\": { \"ip-address\": \"0.0.0.0\" },\n" @@ -780,6 +786,7 @@ TEST(CfgSubnets4Test, unparseSubnet) { " \"4o6-interface-id\": \"\",\n" " \"4o6-subnet\": \"\",\n" " \"reservation-mode\": \"all\",\n" + " \"user-context\": {},\n" " \"option-data\": [ ],\n" " \"pools\": [ ]\n" "},{\n" @@ -813,6 +820,12 @@ TEST(CfgSubnets4Test, unparsePool) { Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.1"), IOAddress("192.0.2.10"))); Pool4Ptr pool2(new Pool4(IOAddress("192.0.2.64"), 26)); + std::string json1 = "{ \"comment\": \"foo\", \"version\": 1 }"; + data::ElementPtr ctx1 = data::Element::fromJSON(json1); + pool1->setContext(ctx1); + data::ElementPtr ctx2 = data::Element::fromJSON("{ \"foo\": \"bar\" }"); + pool2->setContext(ctx2); + subnet->addPool(pool1); subnet->addPool(pool2); cfg.add(subnet); @@ -837,11 +850,14 @@ TEST(CfgSubnets4Test, unparsePool) { " \"option-data\": [],\n" " \"pools\": [\n" " {\n" + " \"comment\": \"foo\",\n" " \"option-data\": [ ],\n" - " \"pool\": \"192.0.2.1-192.0.2.10\"\n" + " \"pool\": \"192.0.2.1-192.0.2.10\",\n" + " \"user-context\": { \"version\": 1 }\n" " },{\n" " \"option-data\": [ ],\n" - " \"pool\": \"192.0.2.64/26\"\n" + " \"pool\": \"192.0.2.64/26\"\n," + " \"user-context\": { \"foo\": \"bar\" }\n" " }\n" " ]\n" "} ]\n"; diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 575a0b8e90..f8200fad06 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -439,6 +439,11 @@ TEST(CfgSubnets6Test, unparseSubnet) { subnet2->setRelayInfo(IOAddress("2001:db8:ff::2")); subnet3->setIface("eth1"); + data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }"); + subnet1->setContext(ctx1); + data::ElementPtr ctx2 = data::Element::createMap(); + subnet2->setContext(ctx2); + cfg.add(subnet1); cfg.add(subnet2); cfg.add(subnet3); @@ -446,6 +451,7 @@ TEST(CfgSubnets6Test, unparseSubnet) { // Unparse std::string expected = "[\n" "{\n" + " \"comment\": \"foo\",\n" " \"id\": 123,\n" " \"subnet\": \"2001:db8:1::/48\",\n" " \"relay\": { \"ip-address\": \"::\" },\n" @@ -471,6 +477,7 @@ TEST(CfgSubnets6Test, unparseSubnet) { " \"valid-lifetime\": 4,\n" " \"rapid-commit\": false,\n" " \"reservation-mode\": \"all\",\n" + " \"user-context\": { },\n" " \"pools\": [ ],\n" " \"pd-pools\": [ ],\n" " \"option-data\": [ ]\n" @@ -504,6 +511,12 @@ TEST(CfgSubnets6Test, unparsePool) { IOAddress("2001:db8:1::199"))); Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64)); + std::string json1 = "{ \"comment\": \"foo\", \"version\": 1 }"; + data::ElementPtr ctx1 = data::Element::fromJSON(json1); + pool1->setContext(ctx1); + data::ElementPtr ctx2 = data::Element::fromJSON("{ \"foo\": \"bar\" }"); + pool2->setContext(ctx2); + subnet->addPool(pool1); subnet->addPool(pool2); cfg.add(subnet); @@ -522,10 +535,13 @@ TEST(CfgSubnets6Test, unparsePool) { " \"reservation-mode\": \"all\",\n" " \"pools\": [\n" " {\n" + " \"comment\": \"foo\",\n" " \"pool\": \"2001:db8:1::100-2001:db8:1::199\",\n" + " \"user-context\": { \"version\": 1 },\n" " \"option-data\": [ ]\n" " },{\n" " \"pool\": \"2001:db8:1:1::/64\",\n" + " \"user-context\": { \"foo\": \"bar\" },\n" " \"option-data\": [ ]\n" " }\n" " ],\n" @@ -548,6 +564,9 @@ TEST(CfgSubnets6Test, unparsePdPool) { Pool6Ptr pdpool2(new Pool6(IOAddress("2001:db8:3::"), 48, 56, IOAddress("2001:db8:3::"), 64)); + data::ElementPtr ctx1 = data::Element::fromJSON("{ \"foo\": [ \"bar\" ] }"); + pdpool1->setContext(ctx1); + subnet->addPool(pdpool1); subnet->addPool(pdpool2); cfg.add(subnet); @@ -570,6 +589,7 @@ TEST(CfgSubnets6Test, unparsePdPool) { " \"prefix\": \"2001:db8:2::\",\n" " \"prefix-len\": 48,\n" " \"delegated-len\": 64,\n" + " \"user-context\": { \"foo\": [ \"bar\" ] },\n" " \"option-data\": [ ]\n" " },{\n" " \"prefix\": \"2001:db8:3::\",\n"