From: Tomek Mrugalski Date: Fri, 19 Apr 2019 10:22:40 +0000 (+0200) Subject: [#494, !273] Unit-test written for authoritative flag. X-Git-Tag: Kea-1.6.0-beta~196 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72fcc3dbed3a44f3645b61e4232885173fc59689;p=thirdparty%2Fkea.git [#494, !273] Unit-test written for authoritative flag. --- diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index 2083ad9270..0f952eb98d 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -26,6 +26,7 @@ #include #include #include +#include using namespace std; using namespace isc::asiolink; @@ -638,6 +639,54 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv, } } +std::pair +Dhcpv4SrvTest::configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv, + const bool commit, const int exp_rcode) { + ConstElementPtr json; + try { + json = parseJSON(config); + } catch (const std::exception& ex){ + // Fatal falure on parsing error + + std::stringstream tmp; + tmp << "parsing failure:" + << "config:" << config << std::endl + << "error: " << ex.what(); + ADD_FAILURE() << tmp.str(); + return (std::make_pair(-1, tmp.str())); + } + + ConstElementPtr status; + + // Disable the re-detect flag + disableIfacesReDetect(json); + + // Configure the server and make sure the config is accepted + EXPECT_NO_THROW(status = configureDhcp4Server(srv, json)); + EXPECT_TRUE(status); + if (!status) { + return (make_pair(-1, "configureDhcp4Server didn't return anythin")); + } + + int rcode; + ConstElementPtr comment = config::parseAnswer(rcode, status); + EXPECT_EQ(exp_rcode, rcode) << comment->stringValue(); + + // Use specified lease database backend. + if (rcode == 0) { + EXPECT_NO_THROW( { + CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); + cfg_db->setAppendedParameters("universe=4"); + cfg_db->createManagers(); + } ); + if (commit) { + CfgMgr::instance().commit(); + } + } + + return (std::make_pair(rcode, comment->stringValue())); + } + Dhcpv4Exchange Dhcpv4SrvTest::createExchange(const Pkt4Ptr& query) { bool drop = false; diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index 0ec1171f28..c286d97b56 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -446,6 +446,18 @@ public: void configure(const std::string& config, NakedDhcpv4Srv& srv, const bool commit = true); + /// @brief Configure specified DHCP server using JSON string. + /// + /// @param config String holding server configuration in JSON format. + /// @param srv Instance of the server to be configured. + /// @param commit A boolean flag indicating if the new configuration + /// should be committed (if true), or not (if false). + /// @param exp_rcode expected status code (default = 0 (success)) + /// @return (a pair of status code and a string with result) + std::pair + configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv, + const bool commit = true, const int exp_rcode = 0); + /// @brief Pretends a packet of specified type was received. /// /// Instantiates fake network interfaces, configures passed Dhcpv4Srv, diff --git a/src/bin/dhcp4/tests/shared_network_unittest.cc b/src/bin/dhcp4/tests/shared_network_unittest.cc index 7afcbe2077..019e9e4f70 100644 --- a/src/bin/dhcp4/tests/shared_network_unittest.cc +++ b/src/bin/dhcp4/tests/shared_network_unittest.cc @@ -1029,6 +1029,15 @@ public: StatsMgr::instance().removeAll(); } + /// @brief Specifies authoritative flag value + /// + /// Used to generate authoritative configs + typedef enum AuthoritativeFlag { + AUTH_DEFAULT, // explicit value not specified (use default) + AUTH_YES, // defined explicitly as yes + AUTH_NO // defined explciitly as no + } AuthoritativeFlag; + /// @brief Returns subnet having specified address in range. /// /// @param address Address for which subnet is being searched. @@ -1242,6 +1251,76 @@ public: EXPECT_EQ(ns_address, addrs[0].toText()); } + /// @brief returns authoritative flag as JSON string + /// @param f value to be specified (default, true or false) + string auth(AuthoritativeFlag f) { + switch (f) { + case AUTH_DEFAULT: + return (""); + case AUTH_YES: + return (" \"authoritative\": true,"); + break; + case AUTH_NO: + return (" \"authoritative\": false,"); + } + return (""); + } + + /// @brief generates Config file with specified authoritative flag values + /// + /// The config file has the same structure: + /// - 1 shared network with 2 subnets (global authoritative flag) + /// - first subnet: authoritative (subnet1 flag here) + /// - second subnet: authoritative (subnet2 flag here) + /// + /// @param global coverns presence/value of global authoritative flag + /// @param subnet1 coverns presence/value of authoritative flag in subnet1 + /// @param subnet2 coverns presence/value of authoritative flag in subnet2 + string generateAuthConfig(AuthoritativeFlag global, AuthoritativeFlag subnet1, + AuthoritativeFlag subnet2) { + string cfg = "{" + " \"interfaces-config\": {" + " \"interfaces\": [ \"*\" ]" + " }," + " \"valid-lifetime\": 600," + " \"shared-networks\": [" + " {" + " \"name\": \"frog\"," + " \"comment\": \"example\","; + cfg += auth(global); + cfg += + " \"subnet4\": [" + " {" + " \"subnet\": \"192.0.2.0/26\"," + " \"id\": 10,"; + cfg += auth(subnet1); + + cfg += + " \"pools\": [" + " {" + " \"pool\": \"192.0.2.63 - 192.0.2.63\"" + " }" + " ]" + " }," + " {" + " \"subnet\": \"10.0.0.0/24\"," + " \"id\": 100,"; + cfg += auth(subnet2); + cfg += + " \"pools\": [" + " {" + " \"pool\": \"10.0.0.16 - 10.0.0.16\"" + " }" + " ]" + " }" + " ]" + " }" + " ]" + "}"; + + return (cfg); + } + /// @brief Destructor. virtual ~Dhcpv4SharedNetworkTest() { StatsMgr::instance().removeAll(); @@ -2564,4 +2643,66 @@ TEST_F(Dhcpv4SharedNetworkTest, precedenceReservation) { testPrecedence(config, "192.0.2.6"); } +// Verify authoritative sanitization. +// This test generates many similar configs. They all have similar +// structure: +// - 1 shared-network (with possibly authoritative flag in it) +// 2 subnets (with each possibly having its own authoritative flag) +// +// If the flag is specified on subnet-level, the value must match those +// specified on subnet level. +TEST_F(Dhcpv4SharedNetworkTest, authoritative) { + + // Each scenario will be defined using those parameters. + typedef struct scenario { + bool exp_success; + AuthoritativeFlag global; + AuthoritativeFlag subnet1; + AuthoritativeFlag subnet2; + } scenario; + + // We have the following scenarios. The default is no. + // The only allowed combinations are those that end up with + // having all three values (global, subnet1, subnet2) agree. + scenario scenarios[] = { + //result, global, subnet1, subnet2 + { true, AUTH_DEFAULT, AUTH_DEFAULT, AUTH_DEFAULT }, + { true, AUTH_YES, AUTH_DEFAULT, AUTH_DEFAULT }, + { true, AUTH_YES, AUTH_YES, AUTH_YES }, + { true, AUTH_NO, AUTH_DEFAULT, AUTH_DEFAULT }, + { true, AUTH_NO, AUTH_NO, AUTH_NO }, + { false, AUTH_DEFAULT, AUTH_YES, AUTH_NO }, + { false, AUTH_DEFAULT, AUTH_NO, AUTH_YES }, + { false, AUTH_DEFAULT, AUTH_YES, AUTH_YES }, + { false, AUTH_YES, AUTH_NO, AUTH_NO }, + { false, AUTH_YES, AUTH_DEFAULT, AUTH_NO }, + { false, AUTH_YES, AUTH_NO, AUTH_DEFAULT }, + { false, AUTH_YES, AUTH_NO, AUTH_NO }, + { false, AUTH_YES, AUTH_NO, AUTH_YES }, + { false, AUTH_YES, AUTH_YES, AUTH_NO } + }; + + // Let's test them one by one + int cnt = 0; + for ( auto s : scenarios) { + cnt++; + + string cfg = generateAuthConfig(s.global, s.subnet1, s.subnet2); + + // Create client and set MAC address to the one that has a reservation. + Dhcp4Client client(Dhcp4Client::SELECTING); + + stringstream tmp; + tmp << "Testing scenario " << cnt; + SCOPED_TRACE(tmp.str()); + // Create server configuration. + auto result = configureWithStatus(cfg, *client.getServer(), true, s.exp_success? 0 : 1); + if (s.exp_success) { + EXPECT_EQ(result.first, 0) << result.second; + } else { + EXPECT_EQ(result.first, 1) << "Configuration expected to fail, but succeeded"; + } + } +} + } // end of anonymous namespace