Commit 8a34ddb9 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[3222] Added D2 unit test files to dhpc6

parent 9a3ec071
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/iface_mgr.h>
#include <dhcp6/config_parser.h>
#include <dhcp6/tests/d2_unittest.h>
#include <dhcpsrv/cfgmgr.h>
#include <gtest/gtest.h>
#include <string>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::data;
namespace isc {
namespace dhcp {
namespace test {
/// @todo
void
D2Dhcpv6Srv::d2ClientErrorHandler(const
dhcp_ddns::NameChangeSender::Result result,
dhcp_ddns::NameChangeRequestPtr& ncr) {
++error_count_;
// call base class error handler
Dhcpv6Srv::d2ClientErrorHandler(result, ncr);
}
const bool Dhcp6SrvD2Test::SHOULD_PASS;
const bool Dhcp6SrvD2Test::SHOULD_FAIL;
Dhcp6SrvD2Test::Dhcp6SrvD2Test() : rcode_(-1) {
}
Dhcp6SrvD2Test::~Dhcp6SrvD2Test() {
reset();
}
dhcp_ddns::NameChangeRequestPtr
Dhcp6SrvD2Test::buildTestNcr(uint32_t dhcid_id_num) {
// Build an NCR from json string.
std::ostringstream stream;
stream <<
"{"
" \"change_type\" : 0 , "
" \"forward_change\" : true , "
" \"reverse_change\" : false , "
" \"fqdn\" : \"myhost.example.com.\" , "
" \"ip_address\" : \"192.168.2.1\" , "
" \"dhcid\" : \""
<< std::hex << std::setfill('0') << std::setw(16)
<< dhcid_id_num << "\" , "
" \"lease_expires_on\" : \"20140121132405\" , "
" \"lease_length\" : 1300 "
"}";
return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
}
void
Dhcp6SrvD2Test::reset() {
std::string config = "{ \"interfaces\": [ \"all\" ],"
"\"hooks-libraries\": [ ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"subnet6\": [ ], "
"\"dhcp-ddns\": { \"enable-updates\" : false }, "
"\"option-def\": [ ], "
"\"option-data\": [ ] }";
configure(config, SHOULD_PASS);
}
void
Dhcp6SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
const std::string& ip_address,
const uint32_t port) {
std::ostringstream config;
config <<
"{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
" \"subnet\": \"2001:db8:1::/64\" } ],"
" \"dhcp-ddns\" : {"
" \"enable-updates\" : " << (enable_d2 ? "true" : "false") << ", "
" \"server-ip\" : \"" << ip_address << "\", "
" \"server-port\" : " << port << ", "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
" \"always-include-fqdn\" : true, "
" \"allow-client-update\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
" \"replace-client-name\" : true, "
" \"generated-prefix\" : \"test.prefix\", "
" \"qualifying-suffix\" : \"test.suffix.\" },"
"\"valid-lifetime\": 4000 }";
configure(config.str(), exp_result);
}
void
Dhcp6SrvD2Test::configure(const std::string& config, bool exp_result) {
ElementPtr json = Element::fromJSON(config);
ConstElementPtr status;
// Configure the server and make sure the config is accepted
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
ASSERT_TRUE(status);
int rcode;
ConstElementPtr comment = config::parseAnswer(rcode, status);
if (exp_result == SHOULD_PASS) {
ASSERT_EQ(0, rcode);
} else {
ASSERT_EQ(1, rcode);
}
}
// Tests ability to turn on and off ddns updates by submitting
// by submitting the appropriate configuration to Dhcp6 server
// and then invoking its startD2() method.
TEST_F(Dhcp6SrvD2Test, enableDisable) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
// Verify a valid config with ddns enabled configures ddns properly,
// but does not start the sender.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
// Verify that calling start does not throw and starts the sender.
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Verify a valid config with ddns disabled configures ddns properly.
// Sender should not have been started.
ASSERT_NO_FATAL_FAILURE(configureD2(false));
ASSERT_FALSE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
// Verify that the sender does NOT get started when ddns is disabled.
srv_.startD2();
ASSERT_FALSE(mgr.amSending());
}
// Tests Dhcp6 server's ability to correctly handle a flawed dhcp-ddns configuration.
// It does so by first enabling updates by submitting a valid configuration and then
// ensuring they remain on after submitting a flawed configuration.
// and then invoking its startD2() method.
TEST_F(Dhcp6SrvD2Test, badConfig) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
// Configure it enabled and start it.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Now attempt to give it an invalid configuration.
// Result should indicate failure.
ASSERT_NO_FATAL_FAILURE(configureD2(false, SHOULD_FAIL, "bogus_ip"));
// Configure was not altered, so ddns should be enabled and still sending.
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_TRUE(mgr.amSending());
// Verify that calling start does not throw or stop the sender.
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
}
// Checks that submitting an identical dhcp-ddns configuration
// is handled properly. Not effect should be no change in
// status for ddns updating. Updates should still enabled and
// in send mode. This indicates that the sender was not stopped.
TEST_F(Dhcp6SrvD2Test, sameConfig) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
// Configure it enabled and start it.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Now submit an identical configuration.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
// Configuration was not altered, so ddns should still enabled and sending.
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_TRUE(mgr.amSending());
// Verify that calling start does not throw or stop the sender.
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
}
// Checks that submitting an different, but valid dhcp-ddns configuration
// is handled properly. Updates should be enabled, however they should
// not yet be running. This indicates that the sender was stopped and
// replaced, but not yet started.
TEST_F(Dhcp6SrvD2Test, differentConfig) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
// Configure it enabled and start it.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Now enable it on a different port.
ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "127.0.0.1", 54001));
// Configuration was altered, so ddns should still enabled but not sending.
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
// Verify that calling start starts the sender.
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
}
// Checks that given a valid, enabled configuration and placing
// sender in send mode, permits NCR requests to be sent via UPD
// socket. Note this test does not employ any sort of receiving
// client to verify actual transmission. These types of tests
// are including under dhcp_ddns and d2 unit testing.
TEST_F(Dhcp6SrvD2Test, simpleUDPSend) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
// Configure it enabled and start it.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Verify that we can queue up a message.
dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
ASSERT_NO_THROW(mgr.sendRequest(ncr));
EXPECT_EQ(1, mgr.getQueueSize());
// Calling receive should detect the ready IO on the sender's select-fd,
// and invoke callback, which should complete the send.
ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
// Verify the queue is now empty.
EXPECT_EQ(0, mgr.getQueueSize());
}
// Checks that an IO error in sending a request to D2, results in ddns updates
// being suspended. This indicates that Dhcp6Srv's error handler has been
// invoked as expected. Note that this unit test relies on an attempt to send
// to a server address of 0.0.0.0 port 0 fails under all OSs.
TEST_F(Dhcp6SrvD2Test, forceUDPSendFailure) {
// Grab the manager and verify that be default ddns is off
// and a sender was not started.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_FALSE(mgr.ddnsEnabled());
// Configure it enabled and start it.
// Using server address of 0.0.0.0/0 should induce failure on send.
ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "0.0.0.0", 0));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Queue up 3 messages.
for (int i = 0; i < 3; i++) {
dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
ASSERT_NO_THROW(mgr.sendRequest(ncr));
}
EXPECT_EQ(3, mgr.getQueueSize());
// Calling receive should detect the ready IO on the sender's select-fd,
// and invoke callback, which should complete the send, which should
// fail.
ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
// Verify the error handler was invoked.
EXPECT_EQ(1, srv_.error_count_);
// Verify that updates are disabled and we are no longer sending.
ASSERT_FALSE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
// Verify message is still in the queue.
EXPECT_EQ(3, mgr.getQueueSize());
// Verify that we can't just restart it.
/// @todo This may change if we add ability to resume.
ASSERT_NO_THROW(srv_.startD2());
ASSERT_FALSE(mgr.amSending());
// Configure it enabled and start it.
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Verify message is still in the queue.
EXPECT_EQ(3, mgr.getQueueSize());
// This will finish sending the 1st message in queue
// and initiate send of 2nd message.
ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
EXPECT_EQ(1, srv_.error_count_);
// First message is off the queue.
EXPECT_EQ(2, mgr.getQueueSize());
}
// Tests error handling of D2ClientMgr::sendRequest() failure
// by attempting to queue maximum number of messages.
TEST_F(Dhcp6SrvD2Test, queueMaxError) {
// Configure it enabled and start it.
dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
ASSERT_NO_FATAL_FAILURE(configureD2(true));
ASSERT_TRUE(mgr.ddnsEnabled());
ASSERT_NO_THROW(srv_.startD2());
ASSERT_TRUE(mgr.amSending());
// Attempt to queue more then the maximum allowed.
int max_msgs = mgr.getQueueMaxSize();
for (int i = 0; i < max_msgs + 1; i++) {
dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
ASSERT_NO_THROW(mgr.sendRequest(ncr));
}
// Stopping sender will complete the first message so there
// should be max less one.
EXPECT_EQ(max_msgs - 1, mgr.getQueueSize());
// Verify the error handler was invoked.
EXPECT_EQ(1, srv_.error_count_);
// Verify that updates are disabled and we are no longer sending.
ASSERT_FALSE(mgr.ddnsEnabled());
ASSERT_FALSE(mgr.amSending());
}
} // namespace test
} // namespace dhcp
} // namespace isc
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
/// @file d2_unittest.h Defines classes for testing Dhcpv6srv with D2ClientMgr
#ifndef D2_UNITTEST_H
#define D2_UNITTEST_H
#include <dhcp6/dhcp6_srv.h>
#include <config/ccsession.h>
#include <gtest/gtest.h>
namespace isc {
namespace dhcp {
namespace test {
/// @brief Test derivation of Dhcpv6Srv class used in D2 testing.
/// Use of this class allows the intervention at strategic points in testing
/// by permitting overridden methods and access to scope protected members.
class D2Dhcpv6Srv : public Dhcpv6Srv {
public:
/// @brief Counts the number of times the client error handler is called.
int error_count_;
/// @brief Constructor
D2Dhcpv6Srv()
: Dhcpv6Srv(0), error_count_(0) {
}
/// @brief virtual Destructor.
virtual ~D2Dhcpv6Srv() {
}
/// @brief Override the error handler.
virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::
Result result,
dhcp_ddns::NameChangeRequestPtr& ncr);
};
/// @brief Test fixture which permits testing the interaction between the
/// D2ClientMgr and Dhcpv6Srv.
class Dhcp6SrvD2Test : public ::testing::Test {
public:
/// @brief Mnemonic constants for calls to configuration methods.
static const bool SHOULD_PASS = true;
static const bool SHOULD_FAIL = false;
/// @brief Constructor
Dhcp6SrvD2Test();
/// @brief virtual Destructor
virtual ~Dhcp6SrvD2Test();
/// @brief Resets the CfgMgr singleton to defaults.
/// Primarily used in the test destructor as gtest doesn't exit between
/// tests.
/// @todo CfgMgr should provide a method to reset everything or maybe
/// reconstruct the singleton.
void reset();
/// @brief Configures the server with D2 enabled or disabled
///
/// Constructs a configuration string including dhcp-ddns with the
/// parameters given and passes it into the server's configuration handler.
///
/// @param enable_updates value to assign to the enable-updates parameter
/// @param exp_result indicates if configuration should pass or fail
/// @param ip_address IP address for the D2 server
/// @param port port for the D2 server
void configureD2(bool enable_updates, bool exp_result = SHOULD_PASS,
const std::string& ip_address = "127.0.0.1",
const uint32_t port = 53001);
/// @brief Configures the server with the given configuration
///
/// Passes the given configuration string into the server's configuration
/// handler. It accepts a flag indicating whether or not the configuration
/// is expected to succeed or fail. This permits testing the server's
/// response to both valid and invalid configurations.
///
/// @param config JSON string containing the configuration
/// @param exp_result indicates if configuration should pass or fail
void configure(const std::string& config, bool exp_result = SHOULD_PASS);
/// @brief Contructs a NameChangeRequest message from a fixed JSON string.
///
/// @param dhcid_id_num Integer value to use as the DHCID.
dhcp_ddns::NameChangeRequestPtr buildTestNcr(uint32_t
dhcid_id_num = 0xdeadbeef);
/// @brief Stores the return code of the last configuration attempt.
int rcode_;
/// @brief Stores the message component of the last configuration tattempt.
isc::data::ConstElementPtr comment_;
/// @brief Server object under test.
D2Dhcpv6Srv srv_;
};
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif // D2_UNITTEST_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment