errors, like adding a lease with subnet-id that does not exist in
the configuration or configuring a lease to use an address that is
outside of the subnet to which it is supposed to belong.
+ It provides a not programmatic way to manage user contexts
+ associated to leases.
</entry>
</row>
An entry is a single string with no embedded end-of-line markers,
a prepended timestamp and has the following sections:
<screen>
-timestamp address duration device-id {client-info} {relay-info}
+timestamp address duration device-id {client-info} {relay-info} {user-context}
</screen>
</para>
<para>
options: 1, 2 and 6) if present.
The circuit id and remote id are presented as hex strings
</para></listitem>
+ <listitem><para>
+ user-context - the optional user context associated to the lease.
+ </para></listitem>
</itemizedlist>
</para>
<para>
An entry is a single string with no embedded end-of-line markers,
a prepended timestamp and has the following sections:
<screen>
-timestamp address duration device-id {relay-info}*
+timestamp address duration device-id {relay-info}* {user-context}
</screen>
</para>
<para>
topology. Nevertheless, this is useful information to better scope
down the location of the device, so it is being recorded, if present.
</para></listitem>
+ <listitem><para>
+ user-context - the optional user context associated to the lease.
+ </para></listitem>
</itemizedlist>
</para>
<para>
Please refer to <xref linkend="mysql-database"/> for MySQL,
to <xref linkend="pgsql-database"/> for PostgreSQL or
to <xref linkend="cql-database"/> for Cassandra CQL.
- Scripts are in
- <filename><replaceable>path-to-kea</replaceable>/share/kea/legal_log/scripts</filename> directory, for instance the PostgreSQL create schema
- command is:
-<screen>
-$ <userinput>psql -d <replaceable>database-name</replaceable> -U <replaceable>user-name</replaceable> -f <replaceable>path-to-kea</replaceable>/share/kea/legal_log/scripts/pgsql/legldb_create.pgsql</userinput>
-Password for user <replaceable>user-name</replaceable>:
-START TRANSACTION
-CREATE TABLE
-CREATE INDEX
-CREATE TABLE
-INSERT 0 1
-COMMIT
-$
-</screen>
+ The logs table is part of the Kea database schemas.
</para>
<para>
Configuration parameters are extended by standard lease database
errors, like adding a lease with subnet-id that does not exist in the
configuration or configuring a lease to use an address that is outside
of the subnet to which it is supposed to belong.
+ It provides a not programmatic way to manage user contexts
+ associated to leases.
<note>
<para>This library may only be loaded by <command>kea-dhcp4</command>
configured for the subnet corresponding to the specified subnet-id
is used. This parameter is not used in IPv4.</para>
</listitem>
+ <listitem>
+ <para><command>user-context</command> - specifies the user
+ context to be associated with this lease. It must be a
+ JSON map.</para>
+ </listitem>
</itemizedlist>
</para>
"expire": 12345678,
"fqdn-fwd": true,
"fqdn-rev": true,
- "hostname": "urania.example.org"
+ "hostname": "urania.example.org",
+ "user-context": { "version": 1 }
}
}
</screen>
host reservations, control socket, dhcp ddns, loggers and server id.
These are supported in both DHCPv4 and DHCPv6 at the exception
of server id which is DHCPv6 only.
+ Kea 1.5 added user contexts assocated with leases.
</para>
</section>
cstr_ = base_dir + "/" + "config_file"; // config
v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
- "fqdn_fwd,fqdn_rev,hostname,state\n";
+ "fqdn_fwd,fqdn_rev,hostname,state,user_context\n";
v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
"pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
- "fqdn_rev,hostname,hwaddr,state\n";
+ "fqdn_rev,hostname,hwaddr,state,user_context\n";
// and remove any outstanding test files
removeTestFile();
// We have several entries for different leases, the naming is:
// <lease letter>_<version#>
string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
- "200,200,8,1,1,host.example.com,1\n";
+ "200,200,8,1,1,host.example.com,1,\n";
string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
- "200,500,8,1,1,host.example.com,1\n";
+ "200,500,8,1,1,host.example.com,1,\n";
string a_3 = "192.0.2.1,06:07:08:09:0a:bc,,"
- "200,800,8,1,1,host.example.com,1\n";
+ "200,800,8,1,1,host.example.com,1,{ \"foo\": true }\n";
string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
- "100,100,7,0,0,,1\n";
+ "100,100,7,0,0,,1,{ \"bar\": false }\n";
string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
- "100,135,7,0,0,,1\n";
+ "100,135,7,0,0,,1,\n";
string b_3 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
- "100,150,7,0,0,,1\n";
+ "100,150,7,0,0,,1,\n";
// This one should be invalid, no hardware address and state is not declined
string c_1 = "192.0.2.3,,a:11:01:04,"
- "200,200,8,1,1,host.example.com,0\n";
+ "200,200,8,1,1,host.example.com,0,\n";
string d_1 = "192.0.2.5,16:17:18:19:1a:bc,,"
- "200,200,8,1,1,host.example.com,1\n";
+ "200,200,8,1,1,host.example.com,1,\n";
string d_2 = "192.0.2.5,16:17:18:19:1a:bc,,"
- "0,200,8,1,1,host.example.com,1\n";
+ "0,200,8,1,1,host.example.com,1,\n";
// Subtest 1: both previous and copy available.
// Create the test previous file
// We have several entries for different leases, the naming is:
// <lease letter>_<version#>.
string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
- string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
+ "200,200,8,100,0,7,0,1,1,host.example.com,,1,\n";
+ string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,"
+ "host.example.com,,1,\n";
string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "200,400,8,100,0,7,0,1,1,host.example.com,,1\n";
+ "200,400,8,100,0,7,0,1,1,host.example.com,,1,\n";
string a_4 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "0,200,8,100,0,7,0,1,1,host.example.com,,1\n";
+ "0,200,8,100,0,7,0,1,1,host.example.com,,1,"
+ "{ \"foo\": true }\n";
string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
- "300,300,6,150,0,8,0,0,0,,,1\n";
+ "300,300,6,150,0,8,0,0,0,,,1,{ \"bar\": false }\n";
string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
- "300,800,6,150,0,8,0,0,0,,,1\n";
+ "300,800,6,150,0,8,0,0,0,,,1,\n";
string b_3 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
- "300,1000,6,150,0,8,0,0,0,,,1\n";
+ "300,1000,6,150,0,8,0,0,0,,,1,\n";
string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "100,200,8,0,2,16,64,0,0,,,1\n";
+ "100,200,8,0,2,16,64,0,0,,,1,\n";
string c_2 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "100,400,8,0,2,16,64,0,0,,,1\n";
+ "100,400,8,0,2,16,64,0,0,,,1,\n";
string d_1 = "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "200,600,8,100,0,7,0,1,1,host.example.com,,1\n";
+ "200,600,8,100,0,7,0,1,1,host.example.com,,1,\n";
// Subtest 1: bot previous and copy available
// Create the test previous file
"values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
}
+ // Handle user context.
+ ConstElementPtr ctx = lease_info->get("user-context");
+ if (ctx && (ctx->getType() != Element::map)) {
+ isc_throw(BadValue, "Invalid user context '" << ctx->str()
+ << "' is not a JSON map.");
+ }
+
// Let's fabricate some data and we're ready to go.
uint32_t t1 = subnet->getT1();
uint32_t t2 = subnet->getT2();
cltt, subnet_id,
fqdn_fwd, fqdn_rev, hostname));
l->state_ = state;
+ l->setContext(ctx);
// Retrieve the optional flag indicating if the lease must be created when it
// doesn't exist during the update.
"values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
}
+ // Handle user context.
+ ConstElementPtr ctx = lease_info->get("user-context");
+ if (ctx && (ctx->getType() != Element::map)) {
+ isc_throw(BadValue, "Invalid user context '" << ctx->str()
+ << "' is not a JSON map.");
+ }
+
// Let's fabricate some data and we're ready to go.
uint32_t t1 = subnet->getT1();
uint32_t t2 = subnet->getT2();
hwaddr_ptr, prefix_len));
l->cltt_ = cltt;
l->state_ = state;
+ l->setContext(ctx);
// Retrieve the optional flag indicating if the lease must be created when it
// doesn't exist during the update.
/// "fqdn-fwd": true,
/// "fqdn-rev": true,
/// "hostname": "myhost.example.org",
-/// "state": 0
+/// "state": 0,
+/// "user-context": { \"version\": 1 }
/// }
class Lease4Parser : public isc::data::SimpleParser {
public:
/// "fqdn-fwd": true,
/// "fqdn-rev": true,
/// "hostname": "myhost.example.org",
-/// "state": 0
+/// "state": 0,
+/// "user-context": { \"version\": 1 }
/// }
/// It expects the input data to use the following format:
#include <dhcpsrv/cfgmgr.h>
#include <cc/command_interpreter.h>
#include <cc/data.h>
+#include <testutils/user_context_utils.h>
#include <gtest/gtest.h>
-#include <cc/data.h>
#include <errno.h>
using namespace std;
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::test;
namespace {
// Check that there are no v4 specific fields.
EXPECT_FALSE(l->contains("client-id"));
}
+
+ /// @brief Checks if specified response contains user context
+ ///
+ /// @param lease Element tree that represents a lease
+ /// @param expected expected user context in textual form
+ void checkContext(ConstElementPtr lease, std::string expected) {
+ ASSERT_TRUE(lease);
+ ConstElementPtr moved = moveComments(lease);
+ ConstElementPtr ctx = moved->get("user-context");
+ if (!expected.empty()) {
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(expected, ctx->str());
+ } else {
+ EXPECT_FALSE(ctx);
+ }
+ }
};
// Simple test that checks the library really registers the commands.
exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
"(declined) and 2 (expired-reclaimed)";
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
// Check that a simple, well formed lease4 can be added.
EXPECT_FALSE(l->fqdn_fwd_);
EXPECT_FALSE(l->fqdn_rev_);
EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
// Test execution is fast. The cltt should be set to now. In some rare
// cases we could have the seconds counter to tick, so having a value off
" \"expire\": 12345678,\n"
" \"fqdn-fwd\": true,\n"
" \"fqdn-rev\": true,\n"
- " \"hostname\": \"urania.example.org\""
+ " \"hostname\": \"urania.example.org\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
" }\n"
"}";
string exp_rsp = "Lease added.";
EXPECT_EQ(true, l->fqdn_fwd_);
EXPECT_EQ(true, l->fqdn_rev_);
EXPECT_EQ("urania.example.org", l->hostname_);
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", l->getContext()->str());
}
// Check that lease6-add with missing parameters will fail.
exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
"(declined) and 2 (expired-reclaimed)";
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
// Check that a simple, well formed lease6 can be added.
testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
// Now check that the lease is really there.
- EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3")));
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
}
// Check that a simple, well formed prefix lease can be added.
ASSERT_TRUE(l);
EXPECT_EQ(Lease::TYPE_PD, l->type_);
EXPECT_EQ(48, l->prefixlen_);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
}
// Check that a well formed lease6 with tons of parameters can be added.
" \"expire\": 12345678,\n"
" \"fqdn-fwd\": true,\n"
" \"fqdn-rev\": true,\n"
- " \"hostname\": \"urania.example.org\""
+ " \"hostname\": \"urania.example.org\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
" }\n"
"}";
string exp_rsp = "Lease added.";
EXPECT_EQ(true, l->fqdn_fwd_);
EXPECT_EQ(true, l->fqdn_rev_);
EXPECT_EQ("urania.example.org", l->hostname_);
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", l->getContext()->str());
}
// Checks that lease6-get can handle a situation when the query is
"}";
exp_rsp = "Non-IPv4 address specified: 2001:db8:1::1";
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
// Check that lease4-update correctly handles case when there is
ASSERT_TRUE(l->hwaddr_);
EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
}
// Check that a lease4 is created if it doesn't exist during the update.
ASSERT_TRUE(l->hwaddr_);
EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
}
// Check that lease4-update correctly handles case when the 'force-create'
"}";
exp_rsp = "Non-IPv6 address specified: 192.0.2.1";
testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
}
// Check that a lease6 can be updated. We're changing hw-address
EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
EXPECT_EQ("newhostname.example.org", l->hostname_);
EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
}
EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
EXPECT_EQ("newhostname.example.org", l->hostname_);
EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
}
// Check that lease6-update correctly handles case when the 'force-create'
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 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
return (result.get());
}
+ConstElementPtr moveComments(ConstElementPtr element) {
+ Value<ConstElementPtr> result = moveComments1(element);
+ return (result.get());
+}
+
}; // end of isc::test namespace
}; // end of isc namespace
return (result.get());
}
+ConstElementPtr extractComments(ConstElementPtr element) {
+ Value<ConstElementPtr> result = extractComments1(element);
+ return (result.get());
+}
+
}; // end of isc::test namespace
}; // end of isc namespace
/// @return a processed copy of element or unmodified element
isc::data::ElementPtr moveComments(isc::data::ElementPtr element);
+/// @brief Move comment entries to user-context (const variant)
+///
+/// @param element
+/// @return a processed copy of element or unmodified element
+isc::data::ConstElementPtr moveComments(isc::data::ConstElementPtr element);
+
/// @brief Extract comment entries from user-context
///
/// Process an element looking for user-context entries carrying
/// @return a processed copy of element or unmodified element
isc::data::ElementPtr extractComments(isc::data::ElementPtr element);
+/// @brief Extract comment entries from user-context (const variant)
+///
+/// @param element
+/// @return a processed copy of element or unmodified element
+isc::data::ConstElementPtr extractComments(isc::data::ConstElementPtr element);
+
}; // end of isc::test namespace
}; // end of isc namespace