Browse Source

[5132] parser updated, unit-tests implemented

Tomek 8 years ago
parent
commit
b35c56666d

+ 10 - 0
src/bin/dhcp4/dhcp4_lexer.ll

@@ -718,6 +718,16 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     }
 }
 
+\"flex-id\" {
+    switch(driver.ctx_) {
+    case isc::dhcp::Parser4Context::HOST_RESERVATION_IDENTIFIERS:
+    case isc::dhcp::Parser4Context::RESERVATIONS:
+        return isc::dhcp::Dhcp4Parser::make_FLEX_ID(driver.loc_);
+    default:
+        return isc::dhcp::Dhcp4Parser::make_STRING("flex-id", driver.loc_);
+    }
+}
+
 \"hostname\" {
     switch(driver.ctx_) {
     case isc::dhcp::Parser4Context::RESERVATIONS:

+ 15 - 0
src/bin/dhcp4/dhcp4_parser.yy

@@ -122,6 +122,7 @@ using namespace std;
   CIRCUIT_ID "circuit-id"
   CLIENT_ID "client-id"
   HOSTNAME "hostname"
+  FLEX_ID "flex-id"
 
   RELAY "relay"
   IP_ADDRESS "ip-address"
@@ -631,6 +632,7 @@ host_reservation_identifier: duid_id
                            | hw_address_id
                            | circuit_id
                            | client_id
+                           | flex_id
                            ;
 
 duid_id : DUID {
@@ -653,6 +655,11 @@ client_id : CLIENT_ID {
     ctx.stack_.back()->add(client);
 };
 
+flex_id: FLEX_ID {
+    ElementPtr flex_id(new StringElement("flex-id", ctx.loc2pos(@1)));
+    ctx.stack_.back()->add(flex_id);
+}
+
 hooks_libraries: HOOKS_LIBRARIES {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("hooks-libraries", l);
@@ -1241,6 +1248,7 @@ reservation_param: duid
                  | reservation_client_classes
                  | client_id_value
                  | circuit_id_value
+                 | flex_id_value
                  | ip_address
                  | hw_address
                  | hostname
@@ -1315,6 +1323,13 @@ circuit_id_value: CIRCUIT_ID {
     ctx.leave();
 };
 
+flex_id_value: FLEX_ID {
+    ctx.enter(ctx.NO_KEYWORD);
+} COLON STRING {
+    ElementPtr hw(new StringElement($4, ctx.loc2pos(@4)));
+    ctx.stack_.back()->set("flex-id", hw);
+    ctx.leave();
+};
 
 hostname: HOSTNAME {
     ctx.enter(ctx.NO_KEYWORD);

+ 91 - 0
src/bin/dhcp4/tests/hooks_unittest.cc

@@ -42,6 +42,7 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
     int hook_index_lease4_release  = -1;
     int hook_index_pkt4_send       = -1;
     int hook_index_buffer4_send    = -1;
+    int hook_index_host4_identifier= -1;
 
     // check if appropriate indexes are set
     EXPECT_NO_THROW(hook_index_buffer4_receive = ServerHooks::getServerHooks()
@@ -56,6 +57,8 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
                     .getIndex("pkt4_send"));
     EXPECT_NO_THROW(hook_index_buffer4_send = ServerHooks::getServerHooks()
                     .getIndex("buffer4_send"));
+    EXPECT_NO_THROW(hook_index_host4_identifier = ServerHooks::getServerHooks()
+                    .getIndex("host4_identifier"));
 
     EXPECT_TRUE(hook_index_buffer4_receive > 0);
     EXPECT_TRUE(hook_index_pkt4_receive > 0);
@@ -63,6 +66,7 @@ TEST_F(Dhcpv4SrvTest, Hooks) {
     EXPECT_TRUE(hook_index_lease4_release > 0);
     EXPECT_TRUE(hook_index_pkt4_send > 0);
     EXPECT_TRUE(hook_index_buffer4_send > 0);
+    EXPECT_TRUE(hook_index_host4_identifier > 0);
 }
 
 // A dummy MAC address, padded with 0s
@@ -111,6 +115,7 @@ public:
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_renew");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_release");
         HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_decline");
+        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("host4_identifier");
 
         HooksManager::getSharedCalloutManager().reset();
         delete srv_;
@@ -542,6 +547,24 @@ public:
         return (lease4_decline_callout(callout_handle));
     }
 
+    /// @brief Test host4_identifier by setting identifier to "foo"
+    ///
+    /// @param callout_handle handle passed by the hooks framework
+    /// @return always 0
+    static int
+    host4_identifier_foo_callout(CalloutHandle& handle) {
+        callback_name_ = string("host4_identifier");
+
+        std::vector<uint8_t> id(3);
+        id[0] = 0x66; // f
+        id[1] = 0x6f; // o
+        id[2] = 0x6f; // o
+        handle.setArgument("id_value", id);
+        handle.setArgument("id_type", Host::IDENT_FLEX);
+
+        return (0);
+    }
+
     /// resets buffers used to store data received by callouts
     void resetCalloutBuffers() {
         callback_name_ = string("");
@@ -1702,6 +1725,74 @@ TEST_F(HooksDhcpv4SrvTest, HooksDeclineDrop) {
 }
 
 
+// Checks if callout installed on host4_identifier can generate an
+// identifier and whether that identifier is actually used.
+TEST_F(HooksDhcpv4SrvTest, host4_identifier) {
+    IfaceMgrTestConfig test_config(true);
+    IfaceMgr::instance().openSockets4();
+
+    // Configure a subnet with host reservation. The reservation is based on
+    // flexible identifier value of 'foo'. That's exactly what the
+    // host4_identifier_foo_callout sets.
+    string config = "{ \"interfaces-config\": {"
+        "    \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"host-reservation-identifiers\": [ \"flex-id\" ], "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.0/25\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"interface\": \"eth0\", "
+        "    \"reservations\": ["
+        "        {"
+        "            \"flex-id\": \"'foo'\","
+        "            \"ip-address\": \"192.0.2.201\""
+        "        }"
+        "    ]"
+        "} ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ConstElementPtr json;
+    EXPECT_NO_THROW(json = parseDHCP4(config));
+    ASSERT_TRUE(json);
+    ConstElementPtr status;
+
+    // Configure the server and make sure the config is accepted
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    comment_ = parseAnswer(rcode_, status);
+    ASSERT_EQ(0, rcode_);
+
+    CfgMgr::instance().commit();
+
+    // Install host4_identifier_foo_callout
+    EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+                        "host4_identifier", host4_identifier_foo_callout));
+
+    // Let's create a simple DISCOVER
+    Pkt4Ptr sol = generateSimpleDiscover();
+
+    // Simulate that we have received that traffic
+    srv_->fakeReceive(sol);
+
+    // Server will now process to run its normal loop, but instead of calling
+    // IfaceMgr::receive4(), it will read all packets from the list set by
+    // fakeReceive()
+    // In particular, it should call registered pkt4_receive callback.
+    srv_->run();
+
+    // check that the server did send a response
+    ASSERT_EQ(1, srv_->fake_sent_.size());
+
+    // Make sure that we received a response
+    Pkt4Ptr adv = srv_->fake_sent_.front();
+    ASSERT_TRUE(adv);
+
+    // Make sure the address offered is the one that was reserved.
+    EXPECT_EQ("192.0.2.201", adv->getYiaddr().toText());
+}
+
 // Verifies that libraries are unloaded by server destruction
 // The callout libraries write their library index number to a marker
 // file upon load and unload, making it simple to test whether or not

+ 5 - 1
src/lib/dhcpsrv/cfg_hosts.cc

@@ -713,8 +713,12 @@ CfgHosts::toElement4() const {
             const std::vector<uint8_t>& bin = (*host)->getIdentifier();
             std::string client_id = util::encode::encodeHex(bin);
             map->set("client-id", Element::create(client_id));
+        } else if (id_type == Host::IDENT_FLEX) {
+            const std::vector<uint8_t>& bin = (*host)->getIdentifier();
+            std::string flex = util::encode::encodeHex(bin);
+            map->set("flex-id", Element::create(flex));
         } else {
-            isc_throw(ToElementError, "invalid DUID type: " << id_type);
+            isc_throw(ToElementError, "invalid identifier type: " << id_type);
         }
         // Set the reservation
         const IOAddress& address = (*host)->getIPv4Reservation();

+ 6 - 2
src/lib/dhcpsrv/host.cc

@@ -160,7 +160,8 @@ Host::getIdentifierType(const std::string& identifier_name) {
 
     } else if (identifier_name == "client-id") {
         return (IDENT_CLIENT_ID);
-
+    } else if (identifier_name == "flex-id") {
+        return (IDENT_FLEX);
     } else {
         isc_throw(isc::BadValue, "invalid client identifier type '"
                   << identifier_name << "'");
@@ -205,7 +206,7 @@ Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
         s << "client-id";
         break;
     case IDENT_FLEX:
-        s << "flex";
+        s << "flex-id";
     default:
         // This should never happen actually, unless we add new identifier
         // and forget to add a case for it above.
@@ -231,6 +232,9 @@ Host::getIdentifierName(const IdentifierType& type) {
     case Host::IDENT_CLIENT_ID:
         return ("client-id");
 
+    case Host::IDENT_FLEX:
+        return ("flex-id");
+
     default:
         ;
     }

+ 1 - 0
src/lib/dhcpsrv/parsers/host_reservation_parser.cc

@@ -42,6 +42,7 @@ getSupportedParams4(const bool identifiers_only = false) {
         identifiers_set.insert("duid");
         identifiers_set.insert("circuit-id");
         identifiers_set.insert("client-id");
+        identifiers_set.insert("flex-id");
     }
     // Copy identifiers and add all other parameters.
     if (params_set.empty()) {