Commit ea42c6f4 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac5207'

parents 9660c647 501190d8
......@@ -150,8 +150,12 @@ public:
// Parse Host Reservations for this subnet if any.
ConstElementPtr reservations = subnet->get("reservations");
if (reservations) {
HostCollection hosts;
HostReservationsListParser<HostReservationParser4> parser;
parser.parse(subnet_->getID(), reservations);
parser.parse(subnet_->getID(), reservations, hosts);
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
}
}
return (sn4ptr);
......
......@@ -309,8 +309,12 @@ public:
// Parse Host Reservations for this subnet if any.
ConstElementPtr reservations = subnet->get("reservations");
if (reservations) {
HostCollection hosts;
HostReservationsListParser<HostReservationParser6> parser;
parser.parse(subnet_->getID(), reservations);
parser.parse(subnet_->getID(), reservations, hosts);
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(*h);
}
}
return (sn6ptr);
......
......@@ -573,12 +573,13 @@ CfgHosts::add(const HostPtr& host) {
void
CfgHosts::add4(const HostPtr& host) {
/// @todo This may need further sanity checks.
HWAddrPtr hwaddr = host->getHWAddress();
DuidPtr duid = host->getDuid();
// There should be at least one resource reserved: hostname, IPv4
// address, siaddr, sname, file or IPv6 address or prefix.
/// @todo: this check should be done in add(), not in add4()
if (host->getHostname().empty() &&
(host->getIPv4Reservation().isV4Zero()) &&
!host->hasIPv6Reservation() &&
......@@ -633,7 +634,16 @@ CfgHosts::add4(const HostPtr& host) {
<< ": There's already a reservation for this address");
}
/// @todo This may need further sanity checks.
// Check if the (identifier type, identifier) tuple is already used.
const std::vector<uint8_t>& id = host->getIdentifier();
if ((host->getIPv4SubnetID() > 0) && !id.empty()) {
if (get4(host->getIPv4SubnetID(), host->getIdentifierType(), &id[0],
id.size())) {
isc_throw(DuplicateHost, "failed to add duplicate IPv4 host using identifier: "
<< Host::getIdentifierAsText(host->getIdentifierType(),
&id[0], id.size()));
}
}
// This is a new instance - add it.
hosts_.insert(host);
......@@ -641,7 +651,12 @@ CfgHosts::add4(const HostPtr& host) {
void
CfgHosts::add6(const HostPtr& host) {
/// @todo This may need further sanity checks.
if (host->getIPv6SubnetID() == 0) {
// This is IPv4-only host. No need to add it to v6 tables.
return;
}
HWAddrPtr hwaddr = host->getHWAddress();
DuidPtr duid = host->getDuid();
......
......@@ -476,9 +476,12 @@ private:
ReturnType getHostInternal6(const asiolink::IOAddress& prefix,
const uint8_t prefix_len) const;
/// @brief Adds a new host to the v4 collection.
/// @brief Adds a new host to the collection.
///
/// This is an internal method called by public @ref add.
/// This is an internal method called by public @ref add. Contrary to its
/// name, this is useful for both IPv4 and IPv6 hosts, as this adds the
/// host to hosts_ storage that is shared by both families. Notes that
/// for IPv6 host additional steps may be required (see @ref add6).
///
/// @param host Pointer to the new @c Host object being added.
///
......@@ -486,9 +489,11 @@ private:
/// has already been added to the IPv4 subnet.
virtual void add4(const HostPtr& host);
/// @brief Adds a new host to the v6 collection.
/// @brief Adds IPv6-specific reservation to hosts collection.
///
/// This is an internal method called by public @ref add.
/// This is an internal method called by public @ref add. This method adds
/// IPv6 reservations (IPv6 addresses or prefixes reserved) to the hosts6_
/// storage. Note the host has been added to the hosts_ already (in @ref add4).
///
/// @param host Pointer to the new @c Host object being added.
///
......
......@@ -96,18 +96,19 @@ getSupportedParams6(const bool identifiers_only = false) {
namespace isc {
namespace dhcp {
void
HostPtr
HostReservationParser::parse(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
parseInternal(subnet_id, reservation_data);
return (parseInternal(subnet_id, reservation_data));
}
void
HostPtr
HostReservationParser::parseInternal(const SubnetID&,
isc::data::ConstElementPtr reservation_data) {
std::string identifier;
std::string identifier_name;
std::string hostname;
HostPtr host;
try {
// Gather those parameters that are common for both IPv4 and IPv6
......@@ -153,7 +154,7 @@ HostReservationParser::parseInternal(const SubnetID&,
}
// Create a host object from the basic parameters we already parsed.
host_.reset(new Host(identifier, identifier_name, SubnetID(0),
host.reset(new Host(identifier, identifier_name, SubnetID(0),
SubnetID(0), IOAddress("0.0.0.0"), hostname));
} catch (const std::exception& ex) {
......@@ -161,18 +162,8 @@ HostReservationParser::parseInternal(const SubnetID&,
isc_throw(DhcpConfigError, ex.what() << " ("
<< reservation_data->getPosition() << ")");
}
}
void
HostReservationParser::addHost(isc::data::ConstElementPtr reservation_data) {
try {
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host_);
} catch (const std::exception& ex) {
// Append line number to the exception string.
isc_throw(DhcpConfigError, ex.what() << " ("
<< reservation_data->getPosition() << ")");
}
return (host);
}
bool
......@@ -185,19 +176,19 @@ HostReservationParser::isSupportedParameter(const std::string& param_name) const
return (getSupportedParameters(false).count(param_name) > 0);
}
void
HostPtr
HostReservationParser4::parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
HostReservationParser::parseInternal(subnet_id, reservation_data);
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
host_->setIPv4SubnetID(subnet_id);
host->setIPv4SubnetID(subnet_id);
BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
// For 'option-data' element we will use another parser which
// already returns errors with position appended, so don't
// surround it with try-catch.
if (element.first == "option-data") {
CfgOptionPtr cfg_option = host_->getCfgOption4();
CfgOptionPtr cfg_option = host->getCfgOption4();
// This parser is converted to SimpleParser already. It
// parses the Element structure immediately, there's no need
......@@ -210,21 +201,21 @@ HostReservationParser4::parseInternal(const SubnetID& subnet_id,
} else {
try {
if (element.first == "ip-address") {
host_->setIPv4Reservation(IOAddress(element.second->
host->setIPv4Reservation(IOAddress(element.second->
stringValue()));
} else if (element.first == "next-server") {
host_->setNextServer(IOAddress(element.second->stringValue()));
host->setNextServer(IOAddress(element.second->stringValue()));
} else if (element.first == "server-hostname") {
host_->setServerHostname(element.second->stringValue());
host->setServerHostname(element.second->stringValue());
} else if (element.first == "boot-file-name") {
host_->setBootFileName(element.second->stringValue());
host->setBootFileName(element.second->stringValue());
} else if (element.first == "client-classes") {
BOOST_FOREACH(ConstElementPtr class_element,
element.second->listValue()) {
host_->addClientClass4(class_element->stringValue());
host->addClientClass4(class_element->stringValue());
}
}
......@@ -236,7 +227,7 @@ HostReservationParser4::parseInternal(const SubnetID& subnet_id,
}
}
addHost(reservation_data);
return (host);
}
const std::set<std::string>&
......@@ -244,12 +235,12 @@ HostReservationParser4::getSupportedParameters(const bool identifiers_only) cons
return (getSupportedParams4(identifiers_only));
}
void
HostPtr
HostReservationParser6::parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) {
HostReservationParser::parseInternal(subnet_id, reservation_data);
HostPtr host = HostReservationParser::parseInternal(subnet_id, reservation_data);
host_->setIPv6SubnetID(subnet_id);
host->setIPv6SubnetID(subnet_id);
BOOST_FOREACH(ConfigPair element, reservation_data->mapValue()) {
// Parse option values. Note that the configuration option parser
......@@ -257,7 +248,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
// need to surround it with try-clause (and rethrow with position
// appended).
if (element.first == "option-data") {
CfgOptionPtr cfg_option = host_->getCfgOption6();
CfgOptionPtr cfg_option = host->getCfgOption6();
// This parser is converted to SimpleParser already. It
// parses the Element structure immediately, there's no need
......@@ -318,7 +309,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
}
// Create a reservation for an address or prefix.
host_->addReservation(IPv6Resrv(resrv_type,
host->addReservation(IPv6Resrv(resrv_type,
IOAddress(prefix),
prefix_len));
......@@ -334,7 +325,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
try {
BOOST_FOREACH(ConstElementPtr class_element,
element.second->listValue()) {
host_->addClientClass6(class_element->stringValue());
host->addClientClass6(class_element->stringValue());
}
} catch (const std::exception& ex) {
// Append line number where the error occurred.
......@@ -344,8 +335,7 @@ HostReservationParser6::parseInternal(const SubnetID& subnet_id,
}
}
// This may fail, but the addHost function will handle this on its own.
addHost(reservation_data);
return (host);
}
const std::set<std::string>&
......
......@@ -28,9 +28,11 @@ public:
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
void parse(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
virtual HostPtr
parse(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data) final;
protected:
......@@ -44,20 +46,10 @@ protected:
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual void parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
/// @brief Inserts @c host_ object to the staging configuration.
///
/// This method should be called by derived classes to insert the fully
/// parsed host reservation configuration to the @c CfgMgr.
///
/// @param reservation_data Data element holding host reservation. It
/// used by this method to append the line number to the error string.
///
/// @throw DhcpConfigError When operation to add a configured host fails.
void addHost(isc::data::ConstElementPtr reservation_data);
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
/// @brief Checks if the specified parameter is a host identifier.
///
......@@ -83,11 +75,6 @@ protected:
/// @return Set of supported parameter names.
virtual const std::set<std::string>&
getSupportedParameters(const bool identifiers_only) const = 0;
/// @brief Holds a pointer to @c Host object representing a parsed
/// host reservation configuration.
HostPtr host_;
};
/// @brief Parser for a single host reservation for DHCPv4.
......@@ -101,9 +88,10 @@ protected:
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual void parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
/// @brief Returns set of the supported parameters for DHCPv4.
///
......@@ -127,9 +115,10 @@ protected:
/// @param reservation_data Data element holding map with a host
/// reservation configuration.
///
/// @return Pointer to the object representing parsed host.
/// @throw DhcpConfigError If the configuration is invalid.
virtual void parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
virtual HostPtr parseInternal(const SubnetID& subnet_id,
isc::data::ConstElementPtr reservation_data);
/// @brief Returns set of the supported parameters for DHCPv6.
///
......
......@@ -9,6 +9,7 @@
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/subnet_id.h>
#include <boost/foreach.hpp>
......@@ -30,14 +31,19 @@ public:
/// belong.
/// @param hr_list Data element holding a list of host reservations.
/// Each host reservation is described by a map object.
/// @param [out] hosts_list Hosts representing parsed reservations are stored
/// in this list.
///
/// @throw DhcpConfigError If the configuration if any of the reservations
/// is invalid.
void parse(const SubnetID& subnet_id, isc::data::ConstElementPtr hr_list) {
void parse(const SubnetID& subnet_id, isc::data::ConstElementPtr hr_list,
HostCollection& hosts_list) {
HostCollection hosts;
BOOST_FOREACH(data::ConstElementPtr reservation, hr_list->listValue()) {
HostReservationParserType parser;
parser.parse(subnet_id, reservation);
hosts.push_back(parser.parse(subnet_id, reservation));
}
hosts_list.swap(hosts);
}
};
......
......@@ -120,18 +120,15 @@ protected:
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
ParserType parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
// Retrieve a host.
HostCollection hosts;
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
ASSERT_EQ(1, hosts.size());
ASSERT_TRUE(host);
// There should be no options assigned to a host.
EXPECT_TRUE(hosts[0]->getCfgOption4()->empty());
EXPECT_TRUE(hosts[0]->getCfgOption6()->empty());
EXPECT_TRUE(host->getCfgOption4()->empty());
EXPECT_TRUE(host->getCfgOption6()->empty());
}
/// @brief This test verifies that the parser can parse a DHCPv4
......@@ -151,21 +148,15 @@ protected:
ElementPtr config_element = Element::fromJSON(config.str());
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(expected_identifier_type,
&expected_identifier[0],
expected_identifier.size()));
ASSERT_EQ(1, hosts.size());
EXPECT_EQ(10, hosts[0]->getIPv4SubnetID());
EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
EXPECT_EQ("192.0.2.112", hosts[0]->getIPv4Reservation().toText());
EXPECT_TRUE(hosts[0]->getHostname().empty());
EXPECT_EQ(10, host->getIPv4SubnetID());
EXPECT_EQ(0, host->getIPv6SubnetID());
EXPECT_EQ("192.0.2.112", host->getIPv4Reservation().toText());
EXPECT_TRUE(host->getHostname().empty());
}
/// @brief This test verifies that the parser returns an error when
......@@ -176,8 +167,12 @@ protected:
template<typename ParserType>
void testInvalidConfig(const std::string& config) const {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
ParserType parser;
EXPECT_THROW(parser.parse(SubnetID(10), config_element), DhcpConfigError);
EXPECT_THROW({
host = parser.parse(SubnetID(10), config_element);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
}, isc::Exception);
}
/// @brief HW Address object used by tests.
......@@ -361,10 +356,13 @@ TEST_F(HostReservationParserTest, dhcp4NoHostname) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
......@@ -397,10 +395,14 @@ TEST_F(HostReservationParserTest, dhcp4ClientClasses) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_));
......@@ -435,10 +437,14 @@ TEST_F(HostReservationParserTest, dhcp4MessageFields) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(Host::IDENT_HWADDR,
&hwaddr_->hwaddr_[0],
......@@ -540,10 +546,14 @@ TEST_F(HostReservationParserTest, noIPAddress) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
......@@ -649,10 +659,14 @@ TEST_F(HostReservationParserTest, dhcp6HWaddr) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser6 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_, DuidPtr()));
......@@ -714,10 +728,14 @@ TEST_F(HostReservationParserTest, dhcp6DUID) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser6 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(12), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(12), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
......@@ -790,10 +808,14 @@ TEST_F(HostReservationParserTest, dhcp6NoHostname) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser6 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(12), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(12), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
......@@ -843,10 +865,14 @@ TEST_F(HostReservationParserTest, dhcp6ClientClasses) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser6 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(Host::IDENT_DUID,
&duid_->getDuid()[0],
......@@ -968,10 +994,14 @@ TEST_F(HostReservationParserTest, options4) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser4 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_));
ASSERT_EQ(1, hosts.size());
......@@ -1051,11 +1081,15 @@ TEST_F(HostReservationParserTest, options6) {
ElementPtr config_element = Element::fromJSON(config);
HostPtr host;
HostReservationParser6 parser;
ASSERT_NO_THROW(parser.parse(SubnetID(10), config_element));
ASSERT_NO_THROW(host = parser.parse(SubnetID(10), config_element));
ASSERT_TRUE(host);
// One host should have been added to the configuration.
CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
ASSERT_NO_THROW(cfg_hosts->add(host));
HostCollection hosts;
ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
ASSERT_EQ(1, hosts.size());
......
......@@ -163,11 +163,15 @@ TEST_F(HostReservationsListParserTest, ipv4Reservations) {
ElementPtr config_element = Element::fromJSON(config);
HostCollection hosts;
HostReservationsListParser<HostReservationParser4> parser;
ASSERT_NO_THROW(parser.parse(SubnetID(1), config_element));
ASSERT_NO_THROW(parser.parse(SubnetID(1), config_element, hosts));