]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5374] Checkpoint: HA tests
authorFrancis Dupont <fdupont@isc.org>
Sat, 7 Apr 2018 16:32:01 +0000 (18:32 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 7 Apr 2018 16:32:01 +0000 (18:32 +0200)
src/bin/dhcp4/tests/classify_unittest.cc
src/bin/dhcp4/tests/dhcp4_client.cc
src/bin/dhcp4/tests/dhcp4_client.h
src/bin/dhcp6/tests/classify_unittests.cc
src/bin/dhcp6/tests/dhcp6_client.cc
src/bin/dhcp6/tests/dhcp6_client.h

index 7e9e2d8f3298151c77989994ab9267c8762e8ec0..227abd7fa5f3e37e45841b8873b8b575b7215426 100644 (file)
@@ -64,6 +64,17 @@ namespace {
 ///     option[93].hex == 0x0007, set server-hostname to deneb
 ///     option[93].hex == 0x0006, set boot-file-name to pxelinux.0
 ///     option[93].hex == 0x0001, set boot-file-name to ipxe.efi
+///
+/// - Configuration 4:
+///   - Used for complex membership (example taken from HA)
+///   - 1 subnet: 10.0.0.0/24
+///   - 4 pools: 10.0.0.10-10.0.0.49, 10.0.0.60-10.0.0.99,
+///              10.0.0.110-10.0.0.149, 10.0.0.1.60-10.0.0.199
+///   - 4 classes to compose:
+///      server1 and server2 for each HA server
+///      option[93].hex == 0x0009 aka telephones
+///      option[93].hex == 0x0007 aka computers
+///
 const char* CONFIGS[] = {
     // Configuration 0
     "{ \"interfaces-config\": {"
@@ -211,7 +222,58 @@ const char* CONFIGS[] = {
         "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
         "    \"require-client-classes\": [ \"pxe2\" ]"
         " } ]"
-    "}"
+    "}",
+
+    // Configuration 4
+    "{ \"interfaces-config\": {"
+        "   \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"client-classes\": ["
+        "{"
+        "   \"name\": \"server1\""
+        "},"
+        "{"
+        "   \"name\": \"server2\""
+        "},"
+        "{"
+        "   \"name\": \"telephones\","
+        "   \"test\": \"option[93].hex == 0x0009\""
+        "},"
+        "{"
+        "   \"name\": \"computers\","
+        "   \"test\": \"option[93].hex == 0x0007\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_telephones\","
+        "   \"test\": \"member('server1') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_computers\","
+        "   \"test\": \"member('server1') and member('computers')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_telephones\","
+        "   \"test\": \"member('server2') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_computers\","
+        "   \"test\": \"member('server2') and member('computers')\""
+        "} ],"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"id\": 1,"
+        "    \"pools\": [ "
+        "        { \"pool\": \"10.0.0.10-10.0.0.49\","
+        "          \"client-class\": \"server1_and_telephones\" },"
+        "        { \"pool\": \"10.0.0.60-10.0.0.99\","
+        "          \"client-class\": \"server1_and_computers\" },"
+        "        { \"pool\": \"10.0.0.110-10.0.0.149\","
+        "          \"client-class\": \"server2_and_telephones\" },"
+        "        { \"pool\": \"10.0.0.160-10.0.0.199\","
+        "          \"client-class\": \"server2_and_computers\" } ]"
+        " } ]"
+    "}",
 
 };
 
@@ -633,6 +695,106 @@ TEST_F(ClassifyTest, fixedFieldsInformFile32) {
     testFixedFields(CONFIGS[3], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi");
 }
 
+// This test checks the complex membership from HA with server1 telephone.
+TEST_F(ClassifyTest, server1Telephone) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[4], *client.getServer());
+
+    // Add option.
+    OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
+    client.addExtraOption(pxe);
+
+    // Add server1
+    client.addClass("server1");
+
+    // Get an address
+    client.doDORA();
+
+    // Check response.
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // The address is from the first pool.
+    EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server1 computer.
+TEST_F(ClassifyTest, server1computer) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[4], *client.getServer());
+
+    // Add option.
+    OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
+    client.addExtraOption(pxe);
+
+    // Add server1
+    client.addClass("server1");
+
+    // Get an address
+    client.doDORA();
+
+    // Check response.
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // The address is from the second pool.
+    EXPECT_EQ("10.0.0.60", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server2 telephone.
+TEST_F(ClassifyTest, server2Telephone) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[4], *client.getServer());
+
+    // Add option.
+    OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
+    client.addExtraOption(pxe);
+
+    // Add server2
+    client.addClass("server2");
+
+    // Get an address
+    client.doDORA();
+
+    // Check response.
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // The address is from the third pool.
+    EXPECT_EQ("10.0.0.110", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server2 computer.
+TEST_F(ClassifyTest, server2computer) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[4], *client.getServer());
+
+    // Add option.
+    OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
+    client.addExtraOption(pxe);
+
+    // Add server2
+    client.addClass("server2");
+
+    // Get an address
+    client.doDORA();
+
+    // Check response.
+    Pkt4Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // The address is from the forth pool.
+    EXPECT_EQ("10.0.0.160", resp->getYiaddr().toText());
+}
+
 // This test checks the precedence order in required evaluation.
 // This order is: shared-network > subnet > pools
 TEST_F(ClassifyTest, precedenceNone) {
index c117ba4320583c29510bc5e9f3b0ce56c3268395..84759891053b6b8ff34c17d16de63e50ec825280 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
@@ -238,6 +238,14 @@ Dhcp4Client::appendExtraOptions() {
     }
 }
 
+void
+Dhcp4Client::appendClasses() {
+    for (ClientClasses::const_iterator cclass = classes_.cbegin();
+         cclass != classes_.cend(); ++cclass) {
+        context_.query_->addClass(*cclass);
+    }
+}
+
 void
 Dhcp4Client::doDiscover(const boost::shared_ptr<IOAddress>& requested_addr) {
     context_.query_ = createMsg(DHCPDISCOVER);
@@ -255,6 +263,7 @@ Dhcp4Client::doDiscover(const boost::shared_ptr<IOAddress>& requested_addr) {
         context_.query_->setCiaddr(ciaddr_.get());
     }
     appendExtraOptions();
+    appendClasses();
 
     // Send the message to the server.
     sendMsg(context_.query_);
@@ -277,6 +286,8 @@ Dhcp4Client::doInform(const bool set_ciaddr) {
     appendPRL();
     // Any other options to be sent by a client.
     appendExtraOptions();
+    // Add classes.
+    appendClasses();
     // The client sending a DHCPINFORM message has an IP address obtained
     // by some other means, e.g. static configuration. The lease which we
     // are using here is most likely set by the createLease method.
@@ -396,6 +407,8 @@ Dhcp4Client::doRequest() {
     appendClientId();
     // Any other options to be sent by a client.
     appendExtraOptions();
+    // Add classes.
+    appendClasses();
     // Send the message to the server.
     sendMsg(context_.query_);
     // Expect response.
@@ -518,6 +531,12 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
     msg_copy->setRemoteAddr(msg->getLocalAddr());
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setIface(iface_name_);
+    // Copy classes
+    const ClientClasses& classes = msg->getClasses();
+    for (ClientClasses::const_iterator cclass = classes.cbegin();
+         cclass != classes.cend(); ++cclass) {
+        msg_copy->addClass(*cclass);
+    }
     srv_->fakeReceive(msg_copy);
     srv_->run();
 }
@@ -536,6 +555,13 @@ Dhcp4Client::addExtraOption(const OptionPtr& opt) {
     extra_options_.insert(std::make_pair(opt->getType(), opt));
 }
 
+void
+Dhcp4Client::addClass(const ClientClass& client_class) {
+    if (!classes_.contains(client_class)) {
+        classes_.insert(client_class);
+    }
+}
+
 } // end of namespace isc::dhcp::test
 } // end of namespace isc::dhcp
 } // end of namespace isc
index a30b7d821f15e263b6f85588cbd067a74065b863..887ddd98b1e32a780d99e6e9240ebfd8cd419124 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
@@ -369,12 +369,22 @@ public:
     /// @param opt additional option to be sent
     void addExtraOption(const OptionPtr& opt);
 
+    /// @brief Add a client class.
+    ///
+    /// @param client_class name of the class to be added.
+    void addClass(const ClientClass& client_class);
+
 private:
     /// @brief Appends extra options, previously added with addExtraOption()
     ///
     /// @brief Copies options from extra_options_ into outgoing message
     void appendExtraOptions();
 
+    /// @brief Appends extra classes, previously added with addClass()
+    ///
+    /// @brief Add client classes from classes_ to incoming message
+    void appendClasses();
+
     /// @brief Creates and adds Requested IP Address option to the client's
     /// query.
     ///
@@ -491,6 +501,9 @@ private:
 
     /// @brief Extra options the client will send.
     OptionCollection extra_options_;
+
+    /// @brief Extra classes to add to the query.
+    ClientClasses classes_;
 };
 
 } // end of namespace isc::dhcp::test
index 99611da61e1c9401f3aa54d32f798955f27e1721..6ca129a4bbb335a068df00e6f28c4b0bc5315946 100644 (file)
@@ -46,6 +46,27 @@ namespace {
 ///     the 'foo' value.
 ///   - There is one subnet specified 2001:db8:1::/48 with pool of
 ///     IPv6 addresses.
+///
+/// - Configuration 1:
+///   - Used for complex membership (example taken from HA)
+///   - 1 subnet: 2001:db8:1::/48
+///   - 4 pools: 2001:db8:1:1::/64, 2001:db8:1:2::/64,
+///              2001:db8:1:3::/64 and 2001:db8:1:4::/64
+///   - 4 classes to compose:
+///      server1 and server2 for each HA server
+///      option 1234 'foo' aka telephones
+///      option 1234 'bar' aka computers
+///
+/// - Configuration 2:
+///   - Used for complex membership (example taken from HA) and pd-pools
+///   - 1 subnet: 2001:db8::/32
+///   - 4 pd-pools: 2001:db8:1::/48, 2001:db8:2::/48,
+///                 2001:db8:3::/48 and 2001:db8:4::/48
+///   - 4 classes to compose:
+///      server1 and server2 for each HA server
+///      option 1234 'foo' aka telephones
+///      option 1234 'bar' aka computers
+///
 const char* CONFIGS[] = {
     // Configuration 0
     "{ \"interfaces-config\": {"
@@ -104,7 +125,128 @@ const char* CONFIGS[] = {
         "        \"client-classes\": [ \"reserved-class1\", \"reserved-class2\" ]"
         "    } ]"
         " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+    // Configuration 1
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"option-def\": [ "
+        "{"
+        "    \"name\": \"host-name\","
+        "    \"code\": 1234,"
+        "    \"type\": \"string\""
+        "} ],"
+        "\"client-classes\": ["
+        "{"
+        "   \"name\": \"server1\""
+        "},"
+        "{"
+        "   \"name\": \"server2\""
+        "},"
+        "{"
+        "   \"name\": \"telephones\","
+        "   \"test\": \"option[host-name].text == 'foo'\""
+        "},"
+        "{"
+        "   \"name\": \"computers\","
+        "   \"test\": \"option[host-name].text == 'bar'\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_telephones\","
+        "   \"test\": \"member('server1') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_computers\","
+        "   \"test\": \"member('server1') and member('computers')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_telephones\","
+        "   \"test\": \"member('server2') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_computers\","
+        "   \"test\": \"member('server2') and member('computers')\""
+        "}"
+        "],"
+        "\"subnet6\": [ "
+        "{   \"subnet\": \"2001:db8:1::/48\", "
+        "    \"interface\": \"eth1\","
+        "    \"pools\": [ "
+        "        { \"pool\": \"2001:db8:1:1::/64\","
+        "          \"client-class\": \"server1_and_telephones\" },"
+        "        { \"pool\": \"2001:db8:1:2::/64\","
+        "          \"client-class\": \"server1_and_computers\" },"
+        "        { \"pool\": \"2001:db8:1:3::/64\","
+        "          \"client-class\": \"server2_and_telephones\" },"
+        "        { \"pool\": \"2001:db8:1:4::/64\","
+        "          \"client-class\": \"server2_and_computers\" } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }",
+
+    // Configuration 2
+    "{ \"interfaces-config\": {"
+        "  \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"option-def\": [ "
+        "{"
+        "    \"name\": \"host-name\","
+        "    \"code\": 1234,"
+        "    \"type\": \"string\""
+        "} ],"
+        "\"client-classes\": ["
+        "{"
+        "   \"name\": \"server1\""
+        "},"
+        "{"
+        "   \"name\": \"server2\""
+        "},"
+        "{"
+        "   \"name\": \"telephones\","
+        "   \"test\": \"option[host-name].text == 'foo'\""
+        "},"
+        "{"
+        "   \"name\": \"computers\","
+        "   \"test\": \"option[host-name].text == 'bar'\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_telephones\","
+        "   \"test\": \"member('server1') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server1_and_computers\","
+        "   \"test\": \"member('server1') and member('computers')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_telephones\","
+        "   \"test\": \"member('server2') and member('telephones')\""
+        "},"
+        "{"
+        "   \"name\": \"server2_and_computers\","
+        "   \"test\": \"member('server2') and member('computers')\""
+        "}"
+        "],"
+        "\"subnet6\": [ "
+        "{   \"subnet\": \"2001:db8::/32\", "
+        "    \"interface\": \"eth1\","
+        "    \"pools\": [ "
+        "        { \"pool\": \"2001:db8:1::/48\","
+        "          \"client-class\": \"server1_and_telephones\" },"
+        "        { \"pool\": \"2001:db8:2::/48\","
+        "          \"client-class\": \"server1_and_computers\" },"
+        "        { \"pool\": \"2001:db8:3::/48\","
+        "          \"client-class\": \"server2_and_telephones\" },"
+        "        { \"pool\": \"2001:db8:4::/48\","
+        "          \"client-class\": \"server2_and_computers\" } ]"
+        " } ],"
         "\"valid-lifetime\": 4000 }"
+
 };
 
 /// @brief Test fixture class for testing client classification by the
@@ -1551,4 +1693,32 @@ TEST_F(ClassifyTest, precedenceNetwork) {
     EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
 }
 
+// This test checks the complex membership from HA with server1 telephone.
+TEST_F(ClassifyTest, server1Telephone) {
+    // Create a client.
+    Dhcp6Client client;
+    client.setInterface("eth1");
+    ASSERT_NO_THROW(client.requestAddress(0xabca0));
+
+    // Add option.
+    OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
+    client.addExtraOption(hostname);
+
+    // Add server1
+    client.addClass("server1");
+
+    // Load the config and perform a SARR
+    configure(CONFIGS[1], *client.getServer());
+    ASSERT_NO_THROW(client.doSARR());
+
+    // Check response
+    Pkt6Ptr resp = client.getContext().response_;
+    ASSERT_TRUE(resp);
+
+    // The address is from the first pool.
+    ASSERT_EQ(1, client.getLeaseNum());
+    Lease6 lease_client = client.getLease(0);
+    EXPECT_EQ("2001:db8:1:1::", lease_client.addr_.toText());
+}
+
 } // end of anonymous namespace
index f32189d294c63d549ae591f82cb3d0731d30fcec..8ab931cd6cf681ac0ec175bd0dbae884b5b350b8 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
@@ -423,6 +423,12 @@ Dhcp6Client::createMsg(const uint8_t msg_type) {
         }
     }
 
+    // Add classes.
+    for (ClientClasses::const_iterator cclass = classes_.cbegin();
+         cclass != classes_.cend(); ++cclass) {
+        msg->addClass(*cclass);
+    }
+
     return (msg);
 }
 
@@ -930,6 +936,13 @@ Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setIface(iface_name_);
 
+    // Copy classes
+    const ClientClasses& classes = msg->getClasses();
+    for (ClientClasses::const_iterator cclass = classes.cbegin();
+         cclass != classes.cend(); ++cclass) {
+        msg_copy->addClass(*cclass);
+    }
+
     srv_->fakeReceive(msg_copy);
     srv_->run();
 }
@@ -969,6 +982,18 @@ Dhcp6Client::clearExtraOptions() {
     extra_options_.clear();
 }
 
+void
+Dhcp6Client::addClass(const ClientClass& client_class) {
+    if (!classes_.contains(client_class)) {
+        classes_.insert(client_class);
+    }
+}
+
+void
+Dhcp6Client::clearClasses() {
+    classes_.clear();
+}
+
 void
 Dhcp6Client::printConfiguration() const {
 
index 3402bc6c26f96e56d3dd714508c77bd89060cb0d..80ff2c37c14248e8c00a1ee89b55fa9b2de366b7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
@@ -753,6 +753,14 @@ public:
     /// @brief Configures the client to not send any extra options.
     void clearExtraOptions();
 
+    /// @brief Add a client class.
+    ///
+    /// @param client_class name of the class to be added.
+    void addClass(const ClientClass& client_class);
+
+    /// @brief Configures the client to not add client classes.
+    void clearClasses();
+
     /// @brief Debugging method the prints currently held configuration
     ///
     /// @todo: This is mostly useful when debugging tests. This method
@@ -927,6 +935,9 @@ private:
 
     /// @brief Interface id.
     OptionPtr interface_id_;
+
+    /// @brief Extra classes to add to the query.
+    ClientClasses classes_;
 };
 
 } // end of namespace isc::dhcp::test