diff --git a/doc/sphinx/arm/classify.rst b/doc/sphinx/arm/classify.rst index e65f9804a4..8776c783ef 100644 --- a/doc/sphinx/arm/classify.rst +++ b/doc/sphinx/arm/classify.rst @@ -181,6 +181,12 @@ server uses an appropriate pool or subnet to allocate IP addresses (and/or prefixes), based on the assigned client classes. The details can be found in :ref:`hooks-high-availability`. +The ``SPAWN_`` prefix is used by template classes to generate spawn classes +names at runtime. The spawned class name is constructed by prepending the +``SPAWN_`` prefix to the template class name and the evaluated value: +"SPAWN__". +The details can be found in :ref:`classification-configuring`. + The ``BOOTP`` class is used by the BOOTP hook library to classify and respond to inbound BOOTP queries. diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index aa43959590..d89c1e8dcf 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -3215,7 +3215,12 @@ TEST_F(Dhcpv4SrvTest, matchClassification) { " \"option-data\": [" " { \"name\": \"ip-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[12].text == 'foo'\" } ] }"; + " \"test\": \"option[12].text == 'foo'\" }," + "{ \"name\": \"template-client-id\"," + " \"template-test\": \"substring(option[61].hex,0,3)\" }," + "{ \"name\": \"SPAWN_template-hostname_foo\" }," + "{ \"name\": \"template-hostname\"," + " \"template-test\": \"option[12].text\"} ] }"; ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); @@ -3266,11 +3271,35 @@ TEST_F(Dhcpv4SrvTest, matchClassification) { srv.classifyPacket(query2); srv.classifyPacket(query3); + EXPECT_EQ(query1->classes_.size(), 6); + EXPECT_EQ(query2->classes_.size(), 3); + EXPECT_EQ(query3->classes_.size(), 6); + + EXPECT_TRUE(query1->inClass("ALL")); + EXPECT_TRUE(query2->inClass("ALL")); + EXPECT_TRUE(query3->inClass("ALL")); + // Packets with the exception of the second should be in the router class EXPECT_TRUE(query1->inClass("router")); EXPECT_FALSE(query2->inClass("router")); EXPECT_TRUE(query3->inClass("router")); + EXPECT_TRUE(query1->inClass("template-hostname")); + EXPECT_FALSE(query2->inClass("template-hostname")); + EXPECT_TRUE(query3->inClass("template-hostname")); + + EXPECT_TRUE(query1->inClass("SPAWN_template-hostname_foo")); + EXPECT_FALSE(query2->inClass("SPAWN_template-hostname_foo")); + EXPECT_TRUE(query3->inClass("SPAWN_template-hostname_foo")); + + EXPECT_TRUE(query1->inClass("template-client-id")); + EXPECT_TRUE(query2->inClass("template-client-id")); + EXPECT_TRUE(query3->inClass("template-client-id")); + + EXPECT_TRUE(query1->inClass("SPAWN_template-client-id_def")); + EXPECT_TRUE(query2->inClass("SPAWN_template-client-id_def")); + EXPECT_TRUE(query3->inClass("SPAWN_template-client-id_def")); + // Process queries Pkt4Ptr response1 = srv.processDiscover(query1); Pkt4Ptr response2 = srv.processDiscover(query2); diff --git a/src/bin/dhcp6/tests/classify_unittests.cc b/src/bin/dhcp6/tests/classify_unittests.cc index ee68acfdea..d22a67f9f4 100644 --- a/src/bin/dhcp6/tests/classify_unittests.cc +++ b/src/bin/dhcp6/tests/classify_unittests.cc @@ -585,7 +585,13 @@ TEST_F(ClassifyTest, matchClassification) { " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[host-name].text == 'foo'\" } ] }"; + " \"test\": \"option[host-name].text == 'foo'\" }," + "{ \"name\": \"template-client-id\"," + " \"template-test\": \"substring(option[1].hex,0,3)\" }," + "{ \"name\": \"SPAWN_template-hostname_foo\" }," + "{ \"name\": \"template-hostname\"," + " \"template-test\": \"option[host-name].text\"} ] }"; + ASSERT_NO_THROW(configure(config)); // Create packets with enough to select the subnet @@ -611,11 +617,35 @@ TEST_F(ClassifyTest, matchClassification) { srv.classifyPacket(query2); srv.classifyPacket(query3); + EXPECT_EQ(query1->classes_.size(), 6); + EXPECT_EQ(query2->classes_.size(), 3); + EXPECT_EQ(query3->classes_.size(), 6); + + EXPECT_TRUE(query1->inClass("ALL")); + EXPECT_TRUE(query2->inClass("ALL")); + EXPECT_TRUE(query3->inClass("ALL")); + // Packets with the exception of the second should be in the router class EXPECT_TRUE(query1->inClass("router")); EXPECT_FALSE(query2->inClass("router")); EXPECT_TRUE(query3->inClass("router")); + EXPECT_TRUE(query1->inClass("template-hostname")); + EXPECT_FALSE(query2->inClass("template-hostname")); + EXPECT_TRUE(query3->inClass("template-hostname")); + + EXPECT_TRUE(query1->inClass("SPAWN_template-hostname_foo")); + EXPECT_FALSE(query2->inClass("SPAWN_template-hostname_foo")); + EXPECT_TRUE(query3->inClass("SPAWN_template-hostname_foo")); + + EXPECT_TRUE(query1->inClass("template-client-id")); + EXPECT_TRUE(query2->inClass("template-client-id")); + EXPECT_TRUE(query3->inClass("template-client-id")); + + EXPECT_TRUE(query1->inClass("SPAWN_template-client-id_def")); + EXPECT_TRUE(query2->inClass("SPAWN_template-client-id_def")); + EXPECT_TRUE(query3->inClass("SPAWN_template-client-id_def")); + // Process queries AllocEngine::ClientContext6 ctx1; bool drop = !srv.earlyGHRLookup(query1, ctx1);