From a49d8187d7b89c1f40b520b4af324096eb69fa5d Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 22 Aug 2012 18:46:38 +0200 Subject: [PATCH 001/194] [2140] Initial skeleton for Lease manager. --- src/lib/dhcp/Makefile.am | 1 + src/lib/dhcp/lease_mgr.h | 326 +++++++++++++++++++++++++++++++++ src/lib/dhcp/tests/Makefile.am | 4 +- 3 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 src/lib/dhcp/lease_mgr.h diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index fbd4eb53b5..9f52690e40 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -16,6 +16,7 @@ CLEANFILES = *.gcno *.gcda lib_LTLIBRARIES = libb10-dhcp++.la libb10_dhcp___la_SOURCES = libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h +libb10_dhcp___la_SOURCES += lease_mgr.cc lease_mgr.h libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h libb10_dhcp___la_SOURCES += iface_mgr_linux.cc libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h new file mode 100644 index 0000000000..e6cfa8e548 --- /dev/null +++ b/src/lib/dhcp/lease_mgr.h @@ -0,0 +1,326 @@ +// Copyright (C) 2012 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 +#include +#include +#include +#include "benchmark.h" + +/// @brief Holds DUID (DHCPv6 Unique Identifier) +/// +/// This class holds DUID, that is used in client-id, server-id and +/// several other options. It is used to identify DHCPv6 entity. +class DUID { + + /// @brief specifies DUID type + typedef enum { + DUID_UNKNOWN = 0, // invalid/unknown type + DUID_LLT = 1, // link-layer + time, see RFC3315, section 9.2 + DUID_EN = 2, // enterprise-id, see RFC3315, section 9.3 + DUID_LL = 3, // link-layer, see RFC3315, section 9.4 + DUID_UUID = 4, // UUID, see RFC6355 + } DUIDType; + + public: + DUID(const std::vector& duid); + DUID(const char *duid, size_t len); + const std::vector getDuid() const; + DUIDType getType(); + bool operator == (const DUID& other); + protected: + std::vector duid_; + +} + +/// @brief Structure that holds a lease for IPv4 address +/// +/// For performance reasons it is a simple structure, not a class. If we chose +/// make it a class, all fields would have to made private and getters/setters +/// would be required. As this is a critical part of the code that will be used +/// extensively, direct access is warranted. +struct Lease4 { + uint32_t addr_; /// IPv4 address + + /// @brief Address extension + /// + /// It is envisaged that in some cases IPv4 address will be accompanied with some + /// additional data. One example of such use are Address + Port solutions (or + /// Port-restricted Addresses), where several clients may get the same address, but + /// different port ranges. This feature is not expected to be widely used. + /// Under normal circumstances, the value should be 0. + uint32_t ext_; /// address extension + + /// @brief hardware address + std::vector hwaddr; + + /// @brief client identifier + std::vector client_id; + + /// valid lifetime timestamp + uint32_t valid_lft; + + /// @brief Recycle timer + /// + /// Typically, the lease return to free pool immediately after it is released + /// or expired. This timer specifies number of seconds that it should be kept + /// after release or expiration, in case the client returns. This feature is not + /// currently used and this value is set to 0. + uint32_t recycle_time; + + /// @brief client last transmission time + /// + /// Specifies a timestamp, when last transmission from a client was received. + time_t cltt; + + /// @brief Pool identifier + /// + /// Specifies pool-id of the pool that the lease belongs to + uint32_t pool_id; + + /// @brief Is this a fixed lease? + /// + /// Fixed leases are kept after they are released/expired. + bool fixed; + + /// @brief client hostname + /// + /// This field may be empty + std::string hostname; + + /// @brief did we update AAAA record for this lease? + bool fqdn_fwd; + + /// @brief did we update PTR record for this lease? + bool fqdn_rev; + + /// @brief additional options stored with this lease + /// + /// Currently not used. + std::string options; + + /// @brief Lease comments. + /// + /// Currently not used. It may be used for keeping comments made by the + /// system administrator. + std::string comments; +}; + +/// @brief Pointer to a Lease4 structure. +typedef boost::shared_ptr Lease4Ptr; + + +/// @brief Structure that holds a lease for IPv6 address and/or prefix +/// +/// For performance reasons it is a simple structure, not a class. Had we chose to +/// make it a class, all fields would have to be made private and getters/setters +/// would be required. As this is a critical part of the code that will be used +/// extensively, direct access rather than through getters/setters is warranted. +struct Lease6 { + typedef enum { + LEASE_IA_NA, /// the lease contains non-temporary IPv6 address + LEASE_IA_TA, /// the lease contains temporary IPv6 address + LEASE_IA_PD /// the lease contains IPv6 prefix (for prefix delegation) + } LeaseType; + + /// @brief specifies lease type (normal addr, temporary addr, prefix) + LeaseType type_; + + /// IPv6 address + isc::asiolink::IOAddress addr_; + + /// IPv6 prefix length (used only for PD) + uint8_t prefixlen_; + + /// @brief IAID + /// + /// Identity Association IDentifier. DHCPv6 stores all addresses and prefixes + /// in IA containers (IA_NA, IA_TA, IA_PD). Most containers may appear more + /// than once in a message. To differentiate between them, IAID field is present + uint32_t iaid_; + + /// @brief hardware address + /// + /// This field is not really used and is optional at best. The concept of identifying + /// clients by their hardware address was replaced in DHCPv6 by DUID concept. Each + /// client has its own unique DUID (DHCP Unique IDentifier). Furthermore, client's + /// HW address is not always available, because client may be behind a relay (relay + /// stores only link-local address). + std::vector hwaddr; + + /// @brief client identifier + boost::shared_ptr duid_; + + /// @brief preferred lifetime + /// + /// This parameter specifies preferred lifetime since the lease was assigned/renewed + /// (cltt), expressed in seconds. + uint32_t preferred_lft; + + /// @brief valid lifetime + uint32_t valid_lft; + + /// @brief T1 timer + /// + /// Specifies renewal time. Although technically it is a property of IA container, + /// not the address itself, since our data model does not define separate IA + /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes + /// for the same IA, each must have consistent T1 and T2 values. + uint32_t t1_; + + /// @brief T2 timer + /// + /// Specifies rebinding time. Although technically it is a property of IA container, + /// not the address itself, since our data model does not define separate IA + /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes + /// for the same IA, each must have consistent T1 and T2 values. + uint32_t t2_; + + /// @brief Recycle timer + /// + /// Typically, the lease return to free pool immediately after it is released + /// or expired. This timer specifies number of seconds that it should be kept + /// after release or expiration, in case the client returns. This feature is not + /// currently used and this value is set to 0. + uint32_t recycle_time; + + /// @brief client last transmission time + /// + /// Specifies a timestamp, when last transmission from a client was received. + time_t cltt; + + /// @brief Pool identifier + /// + /// Specifies pool-id of the pool that the lease belongs to + uint32_t pool_id; + + /// @brief Is this a fixed lease? + /// + /// Fixed leases are kept after they are released/expired. + bool fixed; + + /// @brief client hostname + /// + /// This field may be empty + std::string hostname; + + /// @brief did we update AAAA record for this lease? + bool fqdn_fwd; + + /// @brief did we update PTR record for this lease? + bool fqdn_rev; + + /// @brief additional options stored with this lease + /// + /// That field is currently not used. We may keep extra options assigned + /// for leasequery and possibly other purposes. + /// @todo: Define this as a container of options + std::string options; + + /// @brief /// comments on that lease + /// + /// (currently not used) + std::string comments; +}; + +/// @brief Pointer to a Lease6 structure. +typedef boost::shared_ptr Lease6Ptr; + +/// @brief Const pointer to a Lease6 structure. +typedef boost::shared_ptr ConstLease6Ptr; + + +/// @brief In-memory + lease file database implementation +/// +/// This is a simplified in-memory database that mimics ISC DHCP4 implementation. +/// It uses STL and boost: std::map for storage, boost::shared ptr for memory +/// management. It does use C file operations (fopen, fwrite, etc.), because +/// C++ streams does not offer any easy way to flush their contents, like +/// fflush() and fsync() does. +/// +/// IPv4 address is used as a key in the hash. +class LeaseMgr { +public: + + /// @brief The sole lease manager constructor + /// + /// @param filename name of the lease file (will be overwritten) + /// @param sync should operations be + LeaseMgr(const std::string& filename, bool sync); + + /// @brief Destructor (closes file) + ~LeaseMgr(); + + /// @brief adds an IPv4 lease + /// + /// @param lease lease to be added + bool addLease(Lease4Ptr lease); + + /// @brief adds a IPv6 lease + /// + /// @param lease lease to be added + bool addLease(Lease6Ptr lease); + + /// @brief returns existing IPv4 lease + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if lease is not found) + Lease4Ptr getLease4(isc::asiolink::IOAddress addr); + +#error "Implement this" + Lease4Ptr getLease4(hwaddress ); + + Lease4Ptr getLease4(client-id); + + /// @brief returns existing IPv4 lease + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if lease is not found) + Lease6Ptr getLease6(isc::asiolink::IOAddress addr); + +#error "Implement this" + Lease6Ptr getLease6(DUID); + + Lease6Ptr getLease6(DUID, iaid); + + +#error "Implement this" + bool updateLease4(Lease4Ptr lease4); + + bool updateLease6(Lease6Ptr lease6); + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists + bool deleteLease4(uint32_t addr); + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists + bool deleteLease6(isc::asiolink::IOAddress addr); + +protected: + +}; + + + + +}; diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index eba7e81f14..05faee8704 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -26,8 +26,8 @@ TESTS = if HAVE_GTEST TESTS += libdhcp++_unittests libdhcp___unittests_SOURCES = run_unittests.cc -libdhcp___unittests_SOURCES += ../libdhcp++.h ../libdhcp++.cc -libdhcp___unittests_SOURCES += libdhcp++_unittest.cc +libdhcp___unittests_SOURCES += ../libdhcp++.cc ../libdhcp++.h libdhcp++_unittest.cc +libdhcp___unittests_SOURCES += ../lease_mgr.cc ../lease_mgr.h lease_mgr_unittest.cc libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc From 2c7936f05e6e2055016cdf7f4c915a80f465cde5 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 23 Aug 2012 18:39:49 +0200 Subject: [PATCH 002/194] [2140] LeaseMgr improvements. --- src/lib/dhcp/lease_mgr.cc | 55 +++++ src/lib/dhcp/lease_mgr.h | 135 ++++++++---- src/lib/dhcp/tests/lease_mgr_unittest.cc | 248 +++++++++++++++++++++++ 3 files changed, 398 insertions(+), 40 deletions(-) create mode 100644 src/lib/dhcp/lease_mgr.cc create mode 100644 src/lib/dhcp/tests/lease_mgr_unittest.cc diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc new file mode 100644 index 0000000000..6e349bdf2a --- /dev/null +++ b/src/lib/dhcp/lease_mgr.cc @@ -0,0 +1,55 @@ +// Copyright (C) 2012 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lease_mgr.h" + +using namespace std; + +using namespace isc::dhcp; + +LeaseMgr::LeaseMgr(const std::string& dbconfig) { + + vector tokens; + + boost::split(tokens, dbconfig, boost::is_any_of("\t ")); + + BOOST_FOREACH(std::string token, tokens) { + size_t pos = token.find("="); + if (pos != string::npos) { + string name = token.substr(0, pos); + string value = token.substr(pos + 1, -1); + parameters_.insert(pair(name, value)); + } else { + isc_throw(InvalidParameter, "Cannot parse " << token + << ", expected format is name=value"); + } + + } + + /// @todo: Parse dbconfig string +} + +LeaseMgr::~LeaseMgr() { +} diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index e6cfa8e548..63c166ce4b 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -15,8 +15,25 @@ #include #include #include +#include +#include #include -#include "benchmark.h" + +namespace isc { +namespace dhcp { + +/// @brief Holds Client identifier +class ClientId { + public: + ClientId(const std::vector& duid); + ClientId(const char *duid, size_t len); + ClientId(uint32_t id); + ClientId(const isc::asiolink::IOAddress& addr); + const std::vector getClientId() const; + bool operator == (const ClientId& other); + protected: + std::vector clientid_; +}; /// @brief Holds DUID (DHCPv6 Unique Identifier) /// @@ -41,8 +58,7 @@ class DUID { bool operator == (const DUID& other); protected: std::vector duid_; - -} +}; /// @brief Structure that holds a lease for IPv4 address /// @@ -241,86 +257,125 @@ typedef boost::shared_ptr Lease6Ptr; typedef boost::shared_ptr ConstLease6Ptr; -/// @brief In-memory + lease file database implementation +/// @brief Abstract Lease Manager /// -/// This is a simplified in-memory database that mimics ISC DHCP4 implementation. -/// It uses STL and boost: std::map for storage, boost::shared ptr for memory -/// management. It does use C file operations (fopen, fwrite, etc.), because -/// C++ streams does not offer any easy way to flush their contents, like -/// fflush() and fsync() does. -/// -/// IPv4 address is used as a key in the hash. +/// This is an abstract API for lease database backends. It provides unified +/// interface to all backends. As this is an abstract class, it should not +/// be used directly, but rather specialized derived class should be used +/// instead. class LeaseMgr { public: + /// Client Hardware address + typedef std::vector HWAddr; + /// @brief The sole lease manager constructor /// - /// @param filename name of the lease file (will be overwritten) - /// @param sync should operations be - LeaseMgr(const std::string& filename, bool sync); + /// dbconfig is a generic way of passing parameters. Parameters + /// are passed in the "name=value" format, separated by spaces. + /// Values may be enclosed in double quotes, if needed. + /// + /// @param dbconfig database configuration + LeaseMgr(const std::string& dbconfig); /// @brief Destructor (closes file) - ~LeaseMgr(); + virtual ~LeaseMgr(); - /// @brief adds an IPv4 lease + /// @brief Adds an IPv4 lease. /// /// @param lease lease to be added - bool addLease(Lease4Ptr lease); + virtual bool addLease(Lease4Ptr lease) = 0; - /// @brief adds a IPv6 lease + /// @brief Adds an IPv6 lease. /// /// @param lease lease to be added - bool addLease(Lease6Ptr lease); + virtual bool addLease(Lease6Ptr lease) = 0; - /// @brief returns existing IPv4 lease + /// @brief Returns existing IPv4 lease for specified IPv4 address. /// /// @param addr address of the searched lease /// - /// @return smart pointer to the lease (or NULL if lease is not found) - Lease4Ptr getLease4(isc::asiolink::IOAddress addr); + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0; -#error "Implement this" - Lease4Ptr getLease4(hwaddress ); + /// @brief Returns existing IPv4 lease for specified hardware address. + /// + /// @param hwaddr hardware address of the client + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const HWAddr& hwaddr) const = 0; - Lease4Ptr getLease4(client-id); + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// @param clientid client identifier + virtual Lease4Ptr getLease4(const ClientId& clientid) const = 0; - /// @brief returns existing IPv4 lease + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// @param addr address of the searched lease /// - /// @return smart pointer to the lease (or NULL if lease is not found) - Lease6Ptr getLease6(isc::asiolink::IOAddress addr); + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const = 0; -#error "Implement this" - Lease6Ptr getLease6(DUID); + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid) const = 0; - Lease6Ptr getLease6(DUID, iaid); + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + virtual void updateLease4(Lease4Ptr lease4) = 0; - -#error "Implement this" - bool updateLease4(Lease4Ptr lease4); - - bool updateLease6(Lease6Ptr lease6); + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + virtual void updateLease6(Lease6Ptr lease6) = 0; /// @brief Deletes a lease. /// /// @param addr IPv4 address of the lease to be deleted. /// /// @return true if deletion was successful, false if no such lease exists - bool deleteLease4(uint32_t addr); + virtual bool deleteLease4(uint32_t addr) = 0; /// @brief Deletes a lease. /// /// @param addr IPv4 address of the lease to be deleted. /// /// @return true if deletion was successful, false if no such lease exists - bool deleteLease6(isc::asiolink::IOAddress addr); + virtual bool deleteLease6(isc::asiolink::IOAddress addr) = 0; + /// @brief Returns backend name. + /// + /// Each backend have specific name, e.g. "mysql" or "sqlite". + virtual std::string getName() const = 0; + + /// @brief Returns description of the backend. + /// + /// This description may be multiline text that describes the backend. + virtual std::string getDescription() const = 0; + + /// @brief Returns backend version. + virtual std::string getVersion() const = 0; + + /// @todo: Add pool management here + + /// @todo: Add host management here protected: + /// @brief list of parameters passed in dbconfig + std::map parameters_; }; +}; // end of isc::dhcp namespace - -}; +}; // end of isc namespace diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc new file mode 100644 index 0000000000..601c38eb4c --- /dev/null +++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc @@ -0,0 +1,248 @@ +// Copyright (C) 2011-2012 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 +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::asiolink; +using namespace isc::dhcp; + +class Memfile_LeaseMgr : public LeaseMgr { +public: + + /// @brief The sole lease manager constructor + /// + /// dbconfig is a generic way of passing parameters. Parameters + /// are passed in the "name=value" format, separated by spaces. + /// Values may be enclosed in double quotes, if needed. + /// + /// @param dbconfig database configuration + Memfile_LeaseMgr(const std::string& dbconfig); + + /// @brief Destructor (closes file) + virtual ~Memfile_LeaseMgr(); + + /// @brief Adds an IPv4 lease. + /// + /// @param lease lease to be added + virtual bool addLease(Lease4Ptr lease); + + /// @brief Adds an IPv6 lease. + /// + /// @param lease lease to be added + virtual bool addLease(Lease6Ptr lease); + + /// @brief Returns existing IPv4 lease for specified IPv4 address. + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const; + + /// @brief Returns existing IPv4 lease for specified hardware address. + /// + /// @param hwaddr hardware address of the client + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const HWAddr& hwaddr) const; + + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// @param clientid client identifier + virtual Lease4Ptr getLease4(const ClientId& clientid) const; + + /// @brief Returns existing IPv6 lease for a given IPv6 address. + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const; + + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + Lease6Ptr getLease6(const DUID& duid, uint32_t iaid) const; + + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + void updateLease4(Lease4Ptr lease4); + + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + void updateLease6(Lease6Ptr lease6); + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists + bool deleteLease4(uint32_t addr); + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists + bool deleteLease6(isc::asiolink::IOAddress addr); + + /// @brief Returns backend name. + /// + /// Each backend have specific name, e.g. "mysql" or "sqlite". + std::string getName() const { return "memfile"; } + + /// @brief Returns description of the backend. + /// + /// This description may be multiline text that describes the backend. + std::string getDescription() const; + + /// @brief Returns backend version. + std::string getVersion() const { return "test-version"; } +protected: + + +}; + +Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig) + : LeaseMgr(dbconfig) { +} + +Memfile_LeaseMgr::~Memfile_LeaseMgr() { +} + +bool Memfile_LeaseMgr::addLease(boost::shared_ptr) { + return (false); +} + +bool Memfile_LeaseMgr::addLease(boost::shared_ptr) { + return (false); +} + +Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const { + return (Lease4Ptr()); +} + +Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr& ) const { + return (Lease4Ptr()); +} + +Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId& ) const { + return (Lease4Ptr()); +} + +Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const { + return (Lease6Ptr()); +} + +Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const { + return (Lease6Ptr()); +} + +void Memfile_LeaseMgr::updateLease4(Lease4Ptr ) { +} + +void Memfile_LeaseMgr::updateLease6(Lease6Ptr ) { + +} + +bool Memfile_LeaseMgr::deleteLease4(uint32_t ) { + return (false); +} + +bool Memfile_LeaseMgr::deleteLease6(isc::asiolink::IOAddress ) { + return (false); +} + +std::string Memfile_LeaseMgr::getDescription() const { + return (string("This is a dummy memfile backend implementation.\n" + "It does not offer any useful lease management and its only\n" + "purpose is to test abstract lease manager API.")); +} + +#if 0 +bool Memfile_LeaseMgr::addLease(Lease4Ptr lease) { + if (ip4Hash_.find(lease->addr) != ip4Hash_.end()) { + // there is such an address already in the hash + return false; + } + ip4Hash_.insert(pair(lease->addr, lease)); + lease->hostname = "add"; + writeLease(lease); + return (true); +} + +Lease4Ptr Memfile_LeaseMgr::getLease(uint32_t addr) { + leaseIt x = ip4Hash_.find(addr); + if (x != ip4Hash_.end()) { + return x->second; // found + } + + // not found + return Lease4Ptr(); +} + +Lease4Ptr Memfile_LeaseMgr::updateLease(uint32_t addr, uint32_t new_cltt) { + leaseIt x = ip4Hash_.find(addr); + if (x != ip4Hash_.end()) { + x->second->cltt = new_cltt; + x->second->hostname = "update"; + writeLease(x->second); + return x->second; + } + return Lease4Ptr(); +} + +bool Memfile_LeaseMgr::deleteLease(uint32_t addr) { + leaseIt x = ip4Hash_.find(addr); + if (x != ip4Hash_.end()) { + x->second->hostname = "delete"; + writeLease(x->second); + ip4Hash_.erase(x); + return true; + } + return false; +} +#endif + +namespace { +// empty class for now, but may be extended once Addr6 becomes bigger +class LeaseMgrTest : public ::testing::Test { +public: + LeaseMgrTest() { + } +}; + +TEST_F(LeaseMgrTest, constructor) { + + LeaseMgr * leaseMgr = new Memfile_LeaseMgr("param1=value1"); + + delete leaseMgr; +} + +}; // end of anonymous namespace From 5ff43d80fb8eaae5ac2621a5a754cf94204149de Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 12 Sep 2012 17:09:40 +0200 Subject: [PATCH 003/194] [2140] DUID is now a separate class in separate files --- src/lib/dhcp/Makefile.am | 1 + src/lib/dhcp/duid.cc | 63 +++++++++++++++ src/lib/dhcp/duid.h | 69 ++++++++++++++++ src/lib/dhcp/lease_mgr.h | 68 ++++++---------- src/lib/dhcp/tests/Makefile.am | 1 + src/lib/dhcp/tests/duid_unittest.cc | 121 ++++++++++++++++++++++++++++ 6 files changed, 279 insertions(+), 44 deletions(-) create mode 100644 src/lib/dhcp/duid.cc create mode 100644 src/lib/dhcp/duid.h create mode 100644 src/lib/dhcp/tests/duid_unittest.cc diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 9f52690e40..b656e831b7 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -29,6 +29,7 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h +libb10_dhcp___la_SOURCES += duid.cc duid.h EXTRA_DIST = README #EXTRA_DIST += log_messages.mes diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc new file mode 100644 index 0000000000..950e32d0de --- /dev/null +++ b/src/lib/dhcp/duid.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2012 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 +#include +#include + +namespace isc { +namespace dhcp { + +DUID::DUID(const std::vector& duid) { + if (duid.size() > MAX_DUID_LEN) { + isc_throw(OutOfRange, "DUID too large"); + } else { + duid_ = duid; + } +} + +DUID::DUID(const uint8_t * data, size_t len) { + if (len > MAX_DUID_LEN) { + isc_throw(OutOfRange, "DUID too large"); + } + + duid_ = std::vector(data, data + len); +} + +const std::vector& DUID::getDuid() const { + return duid_; +} + +DUID::DUIDType DUID::getType() const { + if (duid_.size() < 2) { + return (DUID_UNKNOWN); + } + uint16_t type = (duid_[0] << 8) + duid_[1]; + if (type < DUID_MAX) { + return (static_cast(type)); + } else { + return (DUID_UNKNOWN); + } +} + +bool DUID::operator == (const DUID& other) const { + return (this->duid_ == other.duid_); +} + +bool DUID::operator != (const DUID& other) const { + return (this->duid_ != other.duid_); +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h new file mode 100644 index 0000000000..d980561a91 --- /dev/null +++ b/src/lib/dhcp/duid.h @@ -0,0 +1,69 @@ +// Copyright (C) 2012 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 +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Holds DUID (DHCPv6 Unique Identifier) +/// +/// This class holds DUID, that is used in client-id, server-id and +/// several other options. It is used to identify DHCPv6 entity. +class DUID { + public: + /// @brief maximum duid size + /// As defined in RFC3315, section 9.1 + static const size_t MAX_DUID_LEN = 128; + + /// @brief specifies DUID type + typedef enum { + DUID_UNKNOWN = 0, ///< invalid/unknown type + DUID_LLT = 1, ///< link-layer + time, see RFC3315, section 9.2 + DUID_EN = 2, ///< enterprise-id, see RFC3315, section 9.3 + DUID_LL = 3, ///< link-layer, see RFC3315, section 9.4 + DUID_UUID = 4, ///< UUID, see RFC6355 + DUID_MAX ///< not a real type, just maximum defined value + 1 + } DUIDType; + + /// @brief creates a DUID + DUID(const std::vector& duid); + + /// @brief creates a DUID + DUID(const uint8_t *duid, size_t len); + + /// @brief returns a const reference to the actual DUID value + /// + /// Note: This reference is only valid as long as the DUID + /// that returned it. + const std::vector& getDuid() const; + + /// @brief returns DUID type + DUIDType getType() const; + + // compares two DUIDs + bool operator == (const DUID& other) const; + + // compares two DUIDs + bool operator != (const DUID& other) const; + + protected: + /// the actual content of the DUID + std::vector duid_; +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index 63c166ce4b..4e6ef53995 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include namespace isc { namespace dhcp { @@ -35,31 +37,6 @@ class ClientId { std::vector clientid_; }; -/// @brief Holds DUID (DHCPv6 Unique Identifier) -/// -/// This class holds DUID, that is used in client-id, server-id and -/// several other options. It is used to identify DHCPv6 entity. -class DUID { - - /// @brief specifies DUID type - typedef enum { - DUID_UNKNOWN = 0, // invalid/unknown type - DUID_LLT = 1, // link-layer + time, see RFC3315, section 9.2 - DUID_EN = 2, // enterprise-id, see RFC3315, section 9.3 - DUID_LL = 3, // link-layer, see RFC3315, section 9.4 - DUID_UUID = 4, // UUID, see RFC6355 - } DUIDType; - - public: - DUID(const std::vector& duid); - DUID(const char *duid, size_t len); - const std::vector getDuid() const; - DUIDType getType(); - bool operator == (const DUID& other); - protected: - std::vector duid_; -}; - /// @brief Structure that holds a lease for IPv4 address /// /// For performance reasons it is a simple structure, not a class. If we chose @@ -79,13 +56,15 @@ struct Lease4 { uint32_t ext_; /// address extension /// @brief hardware address - std::vector hwaddr; + std::vector hwaddr_; /// @brief client identifier - std::vector client_id; + std::vector client_id_; - /// valid lifetime timestamp - uint32_t valid_lft; + /// @brief valid lifetime + /// + /// Expressed as number of seconds since cltt + uint32_t valid_lft_; /// @brief Recycle timer /// @@ -93,44 +72,45 @@ struct Lease4 { /// or expired. This timer specifies number of seconds that it should be kept /// after release or expiration, in case the client returns. This feature is not /// currently used and this value is set to 0. - uint32_t recycle_time; + uint32_t recycle_time_; /// @brief client last transmission time /// /// Specifies a timestamp, when last transmission from a client was received. - time_t cltt; + time_t cltt_; /// @brief Pool identifier /// /// Specifies pool-id of the pool that the lease belongs to - uint32_t pool_id; + uint32_t pool_id_; /// @brief Is this a fixed lease? /// /// Fixed leases are kept after they are released/expired. - bool fixed; + bool fixed_; /// @brief client hostname /// /// This field may be empty - std::string hostname; + std::string hostname_; /// @brief did we update AAAA record for this lease? - bool fqdn_fwd; + bool fqdn_fwd_; - /// @brief did we update PTR record for this lease? - bool fqdn_rev; + /// @brief did we update PTR record for this lease? + bool fqdn_rev_; /// @brief additional options stored with this lease /// - /// Currently not used. - std::string options; + /// This field is currently not used. + /// @todo We need a way to store options in the databased. + Option::OptionCollection options_; /// @brief Lease comments. /// /// Currently not used. It may be used for keeping comments made by the /// system administrator. - std::string comments; + std::string comments_; }; /// @brief Pointer to a Lease4 structure. @@ -209,7 +189,7 @@ struct Lease6 { /// or expired. This timer specifies number of seconds that it should be kept /// after release or expiration, in case the client returns. This feature is not /// currently used and this value is set to 0. - uint32_t recycle_time; + uint32_t recycle_time; /// @brief client last transmission time /// @@ -234,7 +214,7 @@ struct Lease6 { /// @brief did we update AAAA record for this lease? bool fqdn_fwd; - /// @brief did we update PTR record for this lease? + /// @brief did we update PTR record for this lease? bool fqdn_rev; /// @brief additional options stored with this lease @@ -242,12 +222,12 @@ struct Lease6 { /// That field is currently not used. We may keep extra options assigned /// for leasequery and possibly other purposes. /// @todo: Define this as a container of options - std::string options; + std::string options; /// @brief /// comments on that lease /// /// (currently not used) - std::string comments; + std::string comments; }; /// @brief Pointer to a Lease6 structure. diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 05faee8704..68e2e00bcc 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -39,6 +39,7 @@ libdhcp___unittests_SOURCES += ../option4_addrlst.cc ../option4_addrlst.h option libdhcp___unittests_SOURCES += ../option.h ../option.cc option_unittest.cc libdhcp___unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc libdhcp___unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc +libdhcp___unittests_SOURCES += ../duid.h ../duid.cc duid_unittest.cc libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc new file mode 100644 index 0000000000..4c1c422b75 --- /dev/null +++ b/src/lib/dhcp/tests/duid_unittest.cc @@ -0,0 +1,121 @@ +// Copyright (C) 2011 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 +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; + +// don't import the entire boost namespace. It will unexpectedly hide uint8_t +// for some systems. +using boost::scoped_ptr; + +namespace { + +TEST(DuidTest, constructor) { + + uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; + + vector data2(data1, data1 + sizeof(data1)); + + scoped_ptr duid1(new DUID(data1, sizeof(data1))); + scoped_ptr duid2(new DUID(data2)); + + vector vecdata = duid1->getDuid(); + EXPECT_EQ(data2, vecdata); + EXPECT_EQ(DUID::DUID_LLT, duid1->getType()); + + vecdata = duid2->getDuid(); + EXPECT_EQ(data2, vecdata); + + EXPECT_EQ(DUID::DUID_LLT, duid2->getType()); +} + +TEST(DuidTest, size) { + const int MAX_DUID_SIZE = 128; + uint8_t data[MAX_DUID_SIZE + 1]; + vector data2; + for (uint8_t i = 0; i < MAX_DUID_SIZE + 1; ++i) { + data[i] = i; + if (i < MAX_DUID_SIZE) + data2.push_back(i); + } + ASSERT_EQ(data2.size(), MAX_DUID_SIZE); + + scoped_ptr duidmaxsize1(new DUID(data, MAX_DUID_SIZE)); + scoped_ptr duidmaxsize2(new DUID(data2)); + + EXPECT_THROW( + scoped_ptr toolarge1(new DUID(data, MAX_DUID_SIZE + 1)), + OutOfRange); + + // that's one too much + data2.push_back(128); + + EXPECT_THROW( + scoped_ptr toolarge2(new DUID(data2)), + OutOfRange); +} + +TEST(DuidTest, getType) { + uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6}; + uint8_t en[] = {0, 2, 2, 3, 4, 5, 6}; + uint8_t ll[] = {0, 3, 2, 3, 4, 5, 6}; + uint8_t uuid[] = {0, 4, 2, 3, 4, 5, 6}; + uint8_t invalid[] = {0,55, 2, 3, 4, 5, 6}; + + scoped_ptr duid_llt(new DUID(llt, sizeof(llt))); + scoped_ptr duid_en(new DUID(en, sizeof(en))); + scoped_ptr duid_ll(new DUID(ll, sizeof(ll))); + scoped_ptr duid_uuid(new DUID(uuid, sizeof(uuid))); + scoped_ptr duid_invalid(new DUID(invalid, sizeof(invalid))); + + EXPECT_EQ(DUID::DUID_LLT, duid_llt->getType()); + EXPECT_EQ(DUID::DUID_EN, duid_en->getType()); + EXPECT_EQ(DUID::DUID_LL, duid_ll->getType()); + EXPECT_EQ(DUID::DUID_UUID, duid_uuid->getType()); + EXPECT_EQ(DUID::DUID_UNKNOWN, duid_invalid->getType()); +} + +TEST(DuidTest, operators) { + uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; + uint8_t data2[] = {0, 1, 2, 3, 4}; + uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different + uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1 + + scoped_ptr duid1(new DUID(data1, sizeof(data1))); + scoped_ptr duid2(new DUID(data2, sizeof(data2))); + scoped_ptr duid3(new DUID(data3, sizeof(data3))); + scoped_ptr duid4(new DUID(data4, sizeof(data4))); + + EXPECT_TRUE(*duid1 == *duid4); + EXPECT_FALSE(*duid1 == *duid2); + EXPECT_FALSE(*duid1 == *duid3); + + EXPECT_FALSE(*duid1 != *duid4); + EXPECT_TRUE(*duid1 != *duid2); + EXPECT_TRUE(*duid1 != *duid3); +} + + + +} // end of anonymous namespace From 01eb4277b933cb0537204d98b9200550ee3eb641 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 12 Sep 2012 20:39:07 +0200 Subject: [PATCH 004/194] [2140] ClientId class implemented and tested. --- src/lib/dhcp/duid.cc | 48 ++++++++++++++++++++++++++ src/lib/dhcp/duid.h | 41 +++++++++++++++++++++++ src/lib/dhcp/lease_mgr.h | 13 -------- src/lib/dhcp/tests/duid_unittest.cc | 52 +++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 13 deletions(-) diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc index 950e32d0de..912fc4835a 100644 --- a/src/lib/dhcp/duid.cc +++ b/src/lib/dhcp/duid.cc @@ -14,6 +14,8 @@ #include #include +#include +#include #include namespace isc { @@ -59,5 +61,51 @@ bool DUID::operator != (const DUID& other) const { return (this->duid_ != other.duid_); } +/// constructor based on vector +ClientId::ClientId(const std::vector& clientid) + :DUID(clientid) { +} + +/// constructor based on C-style data +ClientId::ClientId(const uint8_t *clientid, size_t len) + :DUID(clientid, len) { +} + +/// constructor based on IOAddress +ClientId::ClientId(const isc::asiolink::IOAddress& addr) + :DUID(std::vector(4, 0)) { + if (addr.getFamily() != AF_INET) { + isc_throw(BadValue, "Client-id supports only IPv4 addresses"); + } + isc::util::writeUint32(addr, &duid_[0]); +} + +/// @brief returns reference to the client-id data +const std::vector ClientId::getClientId() const { + return duid_; +} + +isc::asiolink::IOAddress ClientId::getAddress() const { + if (duid_.size() != sizeof(uint32_t)) { + isc_throw(BadValue, "This client-id is not an IPv4 address"); + } + + return isc::asiolink::IOAddress( isc::util::readUint32(&duid_[0]) ); +} + +bool ClientId::isAddress() const { + return (duid_.size() == sizeof(uint32_t)); +} + +// compares two client-ids +bool ClientId::operator == (const ClientId& other) const { + return (this->duid_ == other.duid_); +} + +// compares two client-ids +bool ClientId::operator != (const ClientId& other) const { + return (this->duid_ != other.duid_); +} + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h index d980561a91..132d1d7e88 100644 --- a/src/lib/dhcp/duid.h +++ b/src/lib/dhcp/duid.h @@ -15,6 +15,8 @@ #include #include #include +#include + namespace isc { namespace dhcp { @@ -65,5 +67,44 @@ class DUID { std::vector duid_; }; +/// @brief Holds Client identifier or client IPv4 address +/// +/// This class is intended to be a generic IPv4 client identifier. It can hold +/// a client-id +class ClientId : DUID { + public: + + /// constructor based on vector + ClientId(const std::vector& clientid); + + /// constructor based on C-style data + ClientId(const uint8_t *clientid, size_t len); + + /// constructor based on IOAddress + /// + /// @throw BadValue if specified address is not IPv4 + ClientId(const isc::asiolink::IOAddress& addr); + + /// @brief returns reference to the client-id data + /// + /// This reference is only valid as long as the object + /// that returned it. + const std::vector getClientId() const; + + /// @brief return an IPv4 address represented by this client-id + /// + /// @throw BadValue if this client-id is not an IPv4 address + isc::asiolink::IOAddress getAddress() const; + + /// @brief returns if client-id is an address + bool isAddress() const; + + // compares two client-ids + bool operator == (const ClientId& other) const; + + // compares two client-ids + bool operator != (const ClientId& other) const; +}; + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index 4e6ef53995..1f33af9a88 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -24,19 +24,6 @@ namespace isc { namespace dhcp { -/// @brief Holds Client identifier -class ClientId { - public: - ClientId(const std::vector& duid); - ClientId(const char *duid, size_t len); - ClientId(uint32_t id); - ClientId(const isc::asiolink::IOAddress& addr); - const std::vector getClientId() const; - bool operator == (const ClientId& other); - protected: - std::vector clientid_; -}; - /// @brief Structure that holds a lease for IPv4 address /// /// For performance reasons it is a simple structure, not a class. If we chose diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc index 4c1c422b75..d3da6e8997 100644 --- a/src/lib/dhcp/tests/duid_unittest.cc +++ b/src/lib/dhcp/tests/duid_unittest.cc @@ -19,11 +19,13 @@ #include #include #include +#include #include using namespace std; using namespace isc; using namespace isc::dhcp; +using namespace isc::asiolink; // don't import the entire boost namespace. It will unexpectedly hide uint8_t // for some systems. @@ -116,6 +118,56 @@ TEST(DuidTest, operators) { EXPECT_TRUE(*duid1 != *duid3); } +TEST(ClientIdTest, constructor) { + IOAddress addr2("192.0.2.1"); + IOAddress addr3("2001:db8:1::1"); + uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; + vector data2(data1, data1 + sizeof(data1)); + uint8_t data3[] = {192, 0 , 2, 1 }; + + // checks for C-style construtor (uint8_t * + len) + scoped_ptr id1(new ClientId(data1, sizeof(data1))); + vector vecdata = id1->getClientId(); + EXPECT_EQ(data2, vecdata); + EXPECT_FALSE(id1->isAddress()); + EXPECT_THROW(id1->getAddress(), BadValue); + + // checks for vector-based constructor + scoped_ptr id2(new ClientId(data2)); + vecdata = id2->getClientId(); + EXPECT_EQ(data2, vecdata); + EXPECT_FALSE(id1->isAddress()); + EXPECT_THROW(id1->getAddress(), BadValue); + + // checks for IOAddress based constructor + scoped_ptr id3(new ClientId(addr2)); + vecdata = id3->getClientId(); + EXPECT_TRUE(vecdata == vector(data3, data3 + 4)); + EXPECT_EQ("192.0.2.1", id3->getAddress().toText()); + + // should support v4 address only, v6 is a wrong address here + EXPECT_THROW(new ClientId(addr3), BadValue); +} + +TEST(ClientIdTest, operators) { + uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; + uint8_t data2[] = {0, 1, 2, 3, 4}; + uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different + uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1 + + scoped_ptr id1(new ClientId(data1, sizeof(data1))); + scoped_ptr id2(new ClientId(data2, sizeof(data2))); + scoped_ptr id3(new ClientId(data3, sizeof(data3))); + scoped_ptr id4(new ClientId(data4, sizeof(data4))); + + EXPECT_TRUE(*id1 == *id4); + EXPECT_FALSE(*id1 == *id2); + EXPECT_FALSE(*id1 == *id3); + + EXPECT_FALSE(*id1 != *id4); + EXPECT_TRUE(*id1 != *id2); + EXPECT_TRUE(*id1 != *id3); +} } // end of anonymous namespace From 0b7e9646307a6e58d3e1df60c68cbac5dbcbfecb Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 12 Sep 2012 21:27:30 +0200 Subject: [PATCH 005/194] [2140] LeaseMgr tweaks. --- src/lib/dhcp/lease_mgr.cc | 12 ++++- src/lib/dhcp/lease_mgr.h | 67 ++++++++++++++++-------- src/lib/dhcp/tests/lease_mgr_unittest.cc | 66 +++++++---------------- 3 files changed, 76 insertions(+), 69 deletions(-) diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc index 6e349bdf2a..927c328526 100644 --- a/src/lib/dhcp/lease_mgr.cc +++ b/src/lib/dhcp/lease_mgr.cc @@ -31,6 +31,10 @@ using namespace isc::dhcp; LeaseMgr::LeaseMgr(const std::string& dbconfig) { + if (dbconfig.length() == 0) { + return; + } + vector tokens; boost::split(tokens, dbconfig, boost::is_any_of("\t ")); @@ -47,8 +51,14 @@ LeaseMgr::LeaseMgr(const std::string& dbconfig) { } } +} - /// @todo: Parse dbconfig string +std::string LeaseMgr::getParameter(const std::string& name) const { + map::const_iterator param = parameters_.find(name); + if (param == parameters_.end()) { + isc_throw(BadValue, "Parameter not found"); + } + return (param->second); } LeaseMgr::~LeaseMgr() { diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index 1f33af9a88..3f90ee0ffc 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -31,7 +31,8 @@ namespace dhcp { /// would be required. As this is a critical part of the code that will be used /// extensively, direct access is warranted. struct Lease4 { - uint32_t addr_; /// IPv4 address + /// IPv4 address + isc::asiolink::IOAddress addr_; /// @brief Address extension /// @@ -40,13 +41,13 @@ struct Lease4 { /// Port-restricted Addresses), where several clients may get the same address, but /// different port ranges. This feature is not expected to be widely used. /// Under normal circumstances, the value should be 0. - uint32_t ext_; /// address extension + uint32_t ext_; /// @brief hardware address std::vector hwaddr_; /// @brief client identifier - std::vector client_id_; + boost::shared_ptr client_id_; /// @brief valid lifetime /// @@ -140,7 +141,7 @@ struct Lease6 { /// client has its own unique DUID (DHCP Unique IDentifier). Furthermore, client's /// HW address is not always available, because client may be behind a relay (relay /// stores only link-local address). - std::vector hwaddr; + std::vector hwaddr_; /// @brief client identifier boost::shared_ptr duid_; @@ -149,17 +150,21 @@ struct Lease6 { /// /// This parameter specifies preferred lifetime since the lease was assigned/renewed /// (cltt), expressed in seconds. - uint32_t preferred_lft; + uint32_t preferred_lft_; /// @brief valid lifetime - uint32_t valid_lft; + /// + /// This parameter specified valid lifetime since the lease was assigned/renewed + /// (cltt), expressed in seconds. + uint32_t valid_lft_; /// @brief T1 timer /// /// Specifies renewal time. Although technically it is a property of IA container, /// not the address itself, since our data model does not define separate IA /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes - /// for the same IA, each must have consistent T1 and T2 values. + /// for the same IA, each must have consistent T1 and T2 values. Specified in + /// seconds since cltt. uint32_t t1_; /// @brief T2 timer @@ -167,7 +172,8 @@ struct Lease6 { /// Specifies rebinding time. Although technically it is a property of IA container, /// not the address itself, since our data model does not define separate IA /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes - /// for the same IA, each must have consistent T1 and T2 values. + /// for the same IA, each must have consistent T1 and T2 values. Specified in + /// seconds since cltt. uint32_t t2_; /// @brief Recycle timer @@ -176,45 +182,45 @@ struct Lease6 { /// or expired. This timer specifies number of seconds that it should be kept /// after release or expiration, in case the client returns. This feature is not /// currently used and this value is set to 0. - uint32_t recycle_time; + uint32_t recycle_time_; /// @brief client last transmission time /// /// Specifies a timestamp, when last transmission from a client was received. - time_t cltt; + time_t cltt_; /// @brief Pool identifier /// /// Specifies pool-id of the pool that the lease belongs to - uint32_t pool_id; + uint32_t pool_id_; /// @brief Is this a fixed lease? /// /// Fixed leases are kept after they are released/expired. - bool fixed; + bool fixed_; /// @brief client hostname /// /// This field may be empty - std::string hostname; + std::string hostname_; /// @brief did we update AAAA record for this lease? - bool fqdn_fwd; + bool fqdn_fwd_; /// @brief did we update PTR record for this lease? - bool fqdn_rev; + bool fqdn_rev_; /// @brief additional options stored with this lease /// /// That field is currently not used. We may keep extra options assigned /// for leasequery and possibly other purposes. - /// @todo: Define this as a container of options - std::string options; + /// @todo We need a way to store options in the databased. + Option::OptionCollection options_; - /// @brief /// comments on that lease + /// @brief Lease comments /// - /// (currently not used) - std::string comments; + /// This field is currently not used. + std::string comments_; }; /// @brief Pointer to a Lease6 structure. @@ -331,14 +337,31 @@ public: virtual std::string getDescription() const = 0; /// @brief Returns backend version. + /// + /// @todo: We will need to implement 3 version functions eventually: + /// A. abstract API version + /// B. backend version + /// C. database version (stored in the database scheme) + /// + /// and then check that: + /// B>=A and B=C (it is ok to have newer backend, as it should be backward + /// compatible) + /// Also if B>C, some database upgrade procedure may happen virtual std::string getVersion() const = 0; - /// @todo: Add pool management here - /// @todo: Add host management here + /// As host reservation is outside of scope for 2012, support for hosts + /// is currently postponed. + protected: + /// @brief returns value of the parameter + std::string getParameter(const std::string& name) const; /// @brief list of parameters passed in dbconfig + /// + /// That will be mostly used for storing database name, username, + /// password and other parameters required for DB access. It is not + /// intended to keep any DHCP-related parameters. std::map parameters_; }; diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc index 601c38eb4c..39102bb533 100644 --- a/src/lib/dhcp/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc @@ -25,6 +25,11 @@ using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; +// This is a concrete implementation of a Lease database. +// It does not do anything useful now, and is used for abstract LeaseMgr +// class testing. It may later evolve into more useful backend if the +// need arises. We can reuse code from memfile benchmark. See code in +// tests/tools/dhcp-ubench/memfile_bench.{cc|h} class Memfile_LeaseMgr : public LeaseMgr { public: @@ -124,6 +129,9 @@ public: /// @brief Returns backend version. std::string getVersion() const { return "test-version"; } + + using LeaseMgr::getParameter; + protected: @@ -185,51 +193,6 @@ std::string Memfile_LeaseMgr::getDescription() const { "purpose is to test abstract lease manager API.")); } -#if 0 -bool Memfile_LeaseMgr::addLease(Lease4Ptr lease) { - if (ip4Hash_.find(lease->addr) != ip4Hash_.end()) { - // there is such an address already in the hash - return false; - } - ip4Hash_.insert(pair(lease->addr, lease)); - lease->hostname = "add"; - writeLease(lease); - return (true); -} - -Lease4Ptr Memfile_LeaseMgr::getLease(uint32_t addr) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - return x->second; // found - } - - // not found - return Lease4Ptr(); -} - -Lease4Ptr Memfile_LeaseMgr::updateLease(uint32_t addr, uint32_t new_cltt) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - x->second->cltt = new_cltt; - x->second->hostname = "update"; - writeLease(x->second); - return x->second; - } - return Lease4Ptr(); -} - -bool Memfile_LeaseMgr::deleteLease(uint32_t addr) { - leaseIt x = ip4Hash_.find(addr); - if (x != ip4Hash_.end()) { - x->second->hostname = "delete"; - writeLease(x->second); - ip4Hash_.erase(x); - return true; - } - return false; -} -#endif - namespace { // empty class for now, but may be extended once Addr6 becomes bigger class LeaseMgrTest : public ::testing::Test { @@ -240,9 +203,20 @@ public: TEST_F(LeaseMgrTest, constructor) { - LeaseMgr * leaseMgr = new Memfile_LeaseMgr("param1=value1"); + // should not throw any exceptions here + Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr(""); + delete leaseMgr; + + leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2"); + + EXPECT_EQ("value1", leaseMgr->getParameter("param1")); + EXPECT_EQ("value2", leaseMgr->getParameter("param2")); + EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue); delete leaseMgr; } +// There's no point in calling any other methods in LeaseMgr, as they +// are purely virtual, so we would only call Memfile_LeaseMgr methods. + }; // end of anonymous namespace From 5ee024c32a3ce4afd33d0d5d4df59fcde90d739d Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 20 Sep 2012 13:55:42 +0200 Subject: [PATCH 006/194] [2269] Initial subnet6 definitions for DHCPv6 --- src/bin/dhcp6/dhcp6.spec | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index 2a82a2d3a0..572689da68 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -7,6 +7,82 @@ "item_type": "string", "item_optional": false, "item_default": "eth0" + } , + + + { "item_name": "renew-timer", + "item_type": "integer", + "item_optional": false, + "item_default": 1000 + }, + + { "item_name": "rebind-timer", + "item_type": "integer", + "item_optional": false, + "item_default": 2000 + }, + + { "item_name": "preferred-lifetime", + "item_type": "integer", + "item_optional": false, + "item_default": 3000 + }, + + { "item_name": "valid-lifetime", + "item_type": "integer", + "item_optional": false, + "item_default": 4000 + }, + + { "item_name": "subnet6", + "item_type": "list", + "item_optional": false, + "item_default": [], + "list_item_spec": + { + "item_name": "single-subnet6", + "item_type": "map", + "item_optional": false, + "item_default": {}, + "map_item_spec": [ + + { "item_name": "renew-timer", + "item_type": "integer", + "item_optional": false, + "item_default": 1000 + }, + + { "item_name": "rebind-timer", + "item_type": "integer", + "item_optional": false, + "item_default": 2000 + }, + + { "item_name": "preferred-lifetime", + "item_type": "integer", + "item_optional": false, + "item_default": 3000 + }, + + { "item_name": "valid-lifetime", + "item_type": "integer", + "item_optional": false, + "item_default": 7200 + }, + { "item_name": "pool6", + "item_type": "list", + "item_optional": false, + "item_default": [], + "list_item_spec": + { + "item_name": "type", + "item_type": "string", + "item_optional": false, + "item_default": "" + } + } + ] + } } ], "commands": [ From fb86b2f215ca1a08440c5c200f22af7747c9de2a Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 21 Sep 2012 14:21:58 +0200 Subject: [PATCH 007/194] [2269] dhcp6.spec update, documentation written --- doc/guide/bind10-guide.xml | 84 ++++++++++++++++++++++++++++++++++---- src/bin/dhcp6/dhcp6.spec | 11 +++-- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index a95b0f540e..7b6c5fa01d 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -2872,16 +2872,84 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";
DHCPv6 Server Configuration - The DHCPv6 server does not have lease database implemented yet - or any support for configuration, so every time the same set - of configuration options (including the same fixed address) - will be assigned every time. + Once the server is started, its configuration becomes possible. To view + existing configuration, use the following command in bindctl: + + > config show Dhcp6 + When starting Dhcp6 daemon for the first time, the initial configuration + will be available. It will look similar to this: + +> config show Dhcp6 +Dhcp6/interface "eth0" string (default) +Dhcp6/renew-timer 1000 integer (default) +Dhcp6/rebind-timer 2000 integer (default) +Dhcp6/preferred-lifetime 3000 integer (default) +Dhcp6/valid-lifetime 4000 integer (default) +Dhcp6/subnet6 [] list (default) + + + + To change one of the existing configurations, simply follow + the usual bindctl procedure. For example, to make the + leases longer, change their valid-lifetime parameter: + +> config set Dhcp6/valid-lifetime 7200 +> config commit + Please note that most parameters specified in Dhcp6 scope are global + and apply to all defined subnets, unless they are overwritten in a more + specific scope. + + + + The essential role of DHCPv6 server is address assignment. The server + has to be configured with at least a subnet and pool of dynamic + addresses that it can manage. For example, let's assume that the server + is connected to a network segment that uses 2001:db8:1::/64 + prefix. Administrator of that network decided that addresses from range + 2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6 + server. Such configuration can be achieved in the following way: + +> config add Dhcp6/subnet6 +> config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64" +> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff" ] +> config commit + Please note that subnet is defined as a simple string, but the pool is + an actual list of pools, therefore must be defined with square + brackets. It is possible to define more than one pool in a + subnet. Continuing the previous example, let's further assume that also + 2001:db8:1:0:5::/80 should be used. It could be written as + 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many Fs + is cumbersome. It can be expressed simply as 2001:db8:1:0:5::/80. Both + formats are supported by Dhcp6. For example, one could define the following pools: + +> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] +> config commit + Number of subnets is not limited, but for performance reasons it is recommended to keep + them as low as possible. - At this stage of development, the only way to alter server - configuration is to tweak its source code. To do so, please - edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following - parameters and recompile: + The server may be configured to serve more than one subnet. To add a second subnet, + use command similar to the following: + +> config add Dhcp6/subnet6 +> config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48" +> config set Dhcp6/subnet6[1]/pool6 [ "2001:db8:beef::/48" ] +> config commit + Arrays are counted from 0. subnet[0] refers to the subnet defined in the + previous example. The config add Dhcp6/subnet6 adds + another (second) subnet. It can be referred to as + Dhcp6/subnet6[1]. In this example, we allow server to + dynamically assign all addresses available in the whole subnet. Although + very wasteful, it is certainly a valid configuration to dedicate the + whole /48 subnet for that purpose. + + + + Note: Although configuration is now accepted, it is not internally used + by they server yet. At this stage of development, the only way to alter + server configuration is to tweak its source code. To do so, please edit + src/bin/dhcp6/dhcp6_srv.cc file, modify the following parameters and + recompile: const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd"; const uint32_t HARDCODED_T1 = 1500; // in seconds diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index 572689da68..279049a754 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -6,10 +6,9 @@ { "item_name": "interface", "item_type": "string", "item_optional": false, - "item_default": "eth0" + "item_default": "all" } , - { "item_name": "renew-timer", "item_type": "integer", "item_optional": false, @@ -46,6 +45,12 @@ "item_default": {}, "map_item_spec": [ + { "item_name": "subnet", + "item_type": "string", + "item_optional": false, + "item_default": "" + }, + { "item_name": "renew-timer", "item_type": "integer", "item_optional": false, @@ -69,7 +74,7 @@ "item_optional": false, "item_default": 7200 }, - { "item_name": "pool6", + { "item_name": "pool", "item_type": "list", "item_optional": false, "item_default": [], From e2a552fcaa6fcc1f0d39defd990bd5a35a4c73a3 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 25 Sep 2012 18:26:37 +0200 Subject: [PATCH 008/194] [2269] Configuration parser for DHCPv6 implemented. --- doc/guide/bind10-guide.xml | 6 +- src/bin/dhcp6/Makefile.am | 2 + src/bin/dhcp6/config_parser.cc | 507 +++++++++++++++++++++++++++++++ src/bin/dhcp6/config_parser.h | 169 +++++++++++ src/bin/dhcp6/ctrl_dhcp6_srv.cc | 14 + src/bin/dhcp6/dhcp6.spec | 11 +- src/bin/dhcp6/dhcp6_messages.mes | 6 + src/bin/dhcp6/tests/Makefile.am | 2 + src/lib/dhcp/triplet.h | 5 + 9 files changed, 717 insertions(+), 5 deletions(-) create mode 100644 src/bin/dhcp6/config_parser.cc create mode 100644 src/bin/dhcp6/config_parser.h diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 7b6c5fa01d..061f7f3bb9 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -2911,7 +2911,7 @@ Dhcp6/subnet6 [] list (default) > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64" -> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff" ] +> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ] > config commit Please note that subnet is defined as a simple string, but the pool is an actual list of pools, therefore must be defined with square @@ -2922,7 +2922,7 @@ Dhcp6/subnet6 [] list (default) is cumbersome. It can be expressed simply as 2001:db8:1:0:5::/80. Both formats are supported by Dhcp6. For example, one could define the following pools: -> config set Dhcp6/subnet6[0]/pool6 [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] +> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] > config commit Number of subnets is not limited, but for performance reasons it is recommended to keep them as low as possible. @@ -2933,7 +2933,7 @@ Dhcp6/subnet6 [] list (default) > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48" -> config set Dhcp6/subnet6[1]/pool6 [ "2001:db8:beef::/48" ] +> config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ] > config commit Arrays are counted from 0. subnet[0] refers to the subnet defined in the previous example. The config add Dhcp6/subnet6 adds diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 4dec4e7da6..68aadea5ad 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6 b10_dhcp6_SOURCES = main.cc b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h +b10_dhcp6_SOURCES += config_parser.cc config_parser.h b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h @@ -62,6 +63,7 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc new file mode 100644 index 0000000000..377c3d48be --- /dev/null +++ b/src/bin/dhcp6/config_parser.cc @@ -0,0 +1,507 @@ +// Copyright (C) 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc::data; +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +typedef boost::shared_ptr ParserPtr; +typedef pair ConfigPair; +typedef std::vector ParserCollection; +typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id); + +typedef std::map FactoryMap; + +typedef std::map Uint32Storage; +/// @brief That is a map with global parameters that will be used as defaults +Uint32Storage uint32_defaults; + +typedef std::map StringStorage; +StringStorage string_defaults; + +typedef std::vector PoolStorage; +PoolStorage pool_defaults; + +/// @brief a dummy configuration parser +/// +/// It is a debugging parser. It does not configure anything, +/// will accept any configuration and will just print it out +/// on commit. +class DummyParser : public Dhcp6ConfigParser { +public: + DummyParser(const std::string& param_name) + :param_name_(param_name) { + } + virtual void build(ConstElementPtr new_config) { + value_ = new_config; + } + virtual void commit() { + // debug message. The whole DummyParser class is used only for parser + // debugging, and is not used in production code. It is very convenient + // to keep it around. Please do not turn this cout into logger calls + std::cout << "Commit for token: [" << param_name_ << "] = [" + << value_->str() << "]" << std::endl; + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new DummyParser(param_name)); + } + +protected: + std::string param_name_; + ConstElementPtr value_; +}; + +class Uint32Parser : public Dhcp6ConfigParser { +public: + Uint32Parser(const std::string& param_name) + :storage_(&uint32_defaults), param_name_(param_name) { + } + + virtual void build(ConstElementPtr value) { + try { + value_ = boost::lexical_cast(value->str()); + } catch (const boost::bad_lexical_cast &) { + isc_throw(BadValue, "Failed to parse value " << value->str() + << " as unsigned 32-bit integer."); + } + cout << "### storing " << param_name_ << "=" << value_ << + " in " << storage_ << endl; + storage_->insert(pair(param_name_, value_)); + } + + virtual void commit() { + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new Uint32Parser(param_name)); + } + + void setStorage(Uint32Storage* storage) { + storage_ = storage; + } + +protected: + Uint32Storage * storage_; + std::string param_name_; + uint32_t value_; +}; + +class StringParser : public Dhcp6ConfigParser { +public: + StringParser(const std::string& param_name) + :storage_(&string_defaults), param_name_(param_name) { + } + + virtual void build(ConstElementPtr value) { + value_ = value->str(); + boost::erase_all(value_, "\""); + storage_->insert(pair(param_name_, value_)); + } + + virtual void commit() { + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new StringParser(param_name)); + } + + void setStorage(StringStorage * storage) { + storage_ = storage; + } + +protected: + StringStorage * storage_; + std::string param_name_; + std::string value_; +}; + +class InterfaceListConfigParser : public Dhcp6ConfigParser { +public: + InterfaceListConfigParser(const std::string& param_name) { + if (param_name != "interface") { + isc_throw(NotImplemented, "Internal error. Interface configuration " + "parser called for the wrong parameter: " << param_name); + } + } + virtual void build(ConstElementPtr value) { + BOOST_FOREACH(ConstElementPtr iface, value->listValue()) { + interfaces_.push_back(iface->str()); + cout << "#### Configured to listen on interface " << iface->str() << endl; + } + } + + virtual void commit() { + /// @todo: Implement per interface listening. Currently always listening on all + /// interfaces. + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new InterfaceListConfigParser(param_name)); + } + +protected: + vector interfaces_; +}; + +class PoolParser : public Dhcp6ConfigParser { +public: + PoolParser(const std::string& /*param_name*/) + :pools_(NULL) { + // ignore parameter name, it is always Dhcp6/subnet6[X]/pool + } + void build(ConstElementPtr pools_list) { + // setStorage() should have been called before build + if (!pools_) { + isc_throw(NotImplemented, "Parser logic error. No pool storage set," + " but pool parser asked to parse pools"); + } + + BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) { + + + + // That should be a single pool representation. It should contain + // text is form prefix/len or first - last. Note that spaces + // are allowed + string txt = text_pool->stringValue(); + + // first let's remove any spaces or tabs + boost::erase_all(txt, " "); + boost::erase_all(txt, "\t"); + + size_t pos = txt.find("/"); + if (pos != string::npos) { + IOAddress addr("::"); + uint8_t len = 0; + try { + addr = IOAddress(txt.substr(0, pos)); + string num = txt.substr(pos+1); + + // it is lexical cast to int and then downcast to uint8_t + // direct cast to uint8_t (which is really an unsigned char) + // will result in interpreting the first digit as output + // value and throwing exception if length written on two + // digits (because there are extra characters left over) + len = boost::lexical_cast(num); + } catch (...) { + isc_throw(Dhcp6ConfigError, "Failed to parse pool " + "definition: " << text_pool->stringValue()); + } + + cout << "#### Creating Pool6(TYPE_IA, " << addr.toText() << "/" + << (int)len << ")" << endl; + // using prefix/len notation + Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len)); + pools_->push_back(pool); + continue; + } + + pos = txt.find("-"); + if (pos != string::npos) { + IOAddress min(txt.substr(0,pos-1)); + IOAddress max(txt.substr(pos+1)); + + cout << "#### Creating Pool6(TYPE_IA, " << min.toText() << "," + << max.toText() << ")" << endl; + + Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max)); + + pools_->push_back(pool); + continue; + } + + isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:" + << text_pool->stringValue() << + ". Does not contain - (for min-max) nor / (prefix/len)"); + } + } + void setStorage(PoolStorage* storage) { + pools_ = storage; + } + + void commit() {} + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new PoolParser(param_name)); + } + +protected: + PoolStorage * pools_; + +}; + +/// @brief this class parses a single subnet +class Subnet6ConfigParser : public Dhcp6ConfigParser { +public: + Subnet6ConfigParser(const std::string& param_name) { + } + + void build(ConstElementPtr subnet) { + + cout << "#### Subnet6ConfigParser::build(): parsing: [" << subnet->str() << "]" << endl; + BOOST_FOREACH(ConfigPair param, subnet->mapValue()) { + + ParserPtr parser(createSubnet6ConfigParser(param.first)); + + // if this is an Uint32 parser, tell it to store the values + // in values_, rather than in global storage + boost::shared_ptr uintParser = + boost::dynamic_pointer_cast(parser); + if (uintParser) { + uintParser->setStorage(&uint32_values_); + } + + boost::shared_ptr stringParser = + boost::dynamic_pointer_cast(parser); + if (stringParser) { + stringParser->setStorage(&string_values_); + } + + boost::shared_ptr poolParser = + boost::dynamic_pointer_cast(parser); + if (poolParser) { + poolParser->setStorage(&pools_); + } + + parser->build(param.second); + parsers_.push_back(parser); + } + + // Ok, we now have subnet parsed + } + + void commit() { + + StringStorage::const_iterator it = string_values_.find("subnet"); + cout << "#### Subnet6ConfigParser::commit() string_values_.size()=" + << string_values_.size() << endl; + if (it == string_values_.end()) { + isc_throw(Dhcp6ConfigError, + "Mandatory subnet definition in subnet missing"); + } + string subnet_txt = it->second; + boost::erase_all(subnet_txt, " "); + boost::erase_all(subnet_txt, "\t"); + + size_t pos = subnet_txt.find("/"); + if (pos == string::npos) { + isc_throw(Dhcp6ConfigError, + "Invalid subnet syntax (prefix/len expected):" << it->second); + } + IOAddress addr(subnet_txt.substr(0, pos)); + uint8_t len = boost::lexical_cast(subnet_txt.substr(pos + 1)); + + Triplet t1 = getParam("renew-timer"); + Triplet t2 = getParam("rebind-timer"); + Triplet pref = getParam("preferred-lifetime"); + Triplet valid = getParam("valid-lifetime"); + + cout << "#### Adding subnet " << addr.toText() << "/" << (int)len + << " with params t1=" << t1 << ", t2=" << t2 << ", pref=" + << pref << ", valid=" << valid << endl; + + Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid)); + + + + CfgMgr::instance().addSubnet6(subnet); + } + +protected: + Dhcp6ConfigParser* createSubnet6ConfigParser(const std::string& config_id) { + FactoryMap factories; + + factories.insert(pair( + "preferred-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "valid-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "renew-timer", Uint32Parser::Factory)); + factories.insert(pair( + "rebind-timer", Uint32Parser::Factory)); + + factories.insert(pair( + "subnet", StringParser::Factory)); + + factories.insert(pair( + "pool", PoolParser::Factory)); + + FactoryMap::iterator f = factories.find(config_id); + if (f == factories.end()) { + // Used for debugging only. + // return new DummyParser(config_id); + + isc_throw(NotImplemented, + "Parser error: Subnet6 parameter not supported: " + << config_id); + } + return (f->second(config_id)); + } + + Triplet getParam(const std::string& name) { + uint32_t value = 0; + bool found = false; + Uint32Storage::iterator global = uint32_defaults.find(name); + if (global != uint32_defaults.end()) { + value = global->second; + found = true; + } + + Uint32Storage::iterator local = uint32_values_.find(name); + if (local != uint32_values_.end()) { + value = local->second; + found = true; + } + + if (found) { + return (Triplet(value)); + } else { + isc_throw(Dhcp6ConfigError, "Mandatory parameter " << name + << " missing (no global default and no subnet-" + << "specific value)"); + } + } + + Uint32Storage uint32_values_; + StringStorage string_values_; + PoolStorage pools_; + ParserCollection parsers_; +}; + +/// @brief this class parses list of subnets +class Subnets6ListConfigParser : public Dhcp6ConfigParser { +public: + Subnets6ListConfigParser(const std::string& param_name) { + } + + void build(ConstElementPtr subnets_list) { + + // No need to define FactoryMap here. There's only one type + // used: Subnet6ConfigParser + + BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) { + + ParserPtr parser(new Subnet6ConfigParser("subnet")); + parser->build(subnet); + subnets_.push_back(parser); + } + + } + + void commit() { + BOOST_FOREACH(ParserPtr subnet, subnets_) { + subnet->commit(); + } + + } + + static Dhcp6ConfigParser* Factory(const std::string& param_name) { + return (new Subnets6ListConfigParser(param_name)); + } + + ParserCollection subnets_; +}; + +/// @brief creates global parsers +/// +/// This method creates global parsers that parse global parameters, i.e. +/// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth. +Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { + FactoryMap factories; + + // + factories.insert(pair( + "preferred-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "valid-lifetime", Uint32Parser::Factory)); + factories.insert(pair( + "renew-timer", Uint32Parser::Factory)); + factories.insert(pair( + "rebind-timer", Uint32Parser::Factory)); + + factories.insert(pair( + "interface", InterfaceListConfigParser::Factory)); + factories.insert(pair( + "subnet6", Subnets6ListConfigParser::Factory)); + + factories.insert(pair( + "version", StringParser::Factory)); + + FactoryMap::iterator f = factories.find(config_id); + if (f == factories.end()) { + // Used for debugging only. + // return new DummyParser(config_id); + + isc_throw(NotImplemented, + "Parser error: Global configuration parameter not supported: " + << config_id); + } + return (f->second(config_id)); +} + +void +configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { + if (!config_set) { + isc_throw(Dhcp6ConfigError, + "Null pointer is passed to configuration parser"); + } + + ParserCollection parsers; + try { + BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) { + + ParserPtr parser(createGlobalDhcp6ConfigParser(config_pair.first)); + parser->build(config_pair.second); + parsers.push_back(parser); + } + } catch (const Dhcp6ConfigError& ex) { + throw; // simply rethrowing it + } catch (const isc::Exception& ex) { + isc_throw(Dhcp6ConfigError, "Server configuration failed: " << + ex.what()); + } + + try { + BOOST_FOREACH(ParserPtr parser, parsers) { + parser->commit(); + } + } catch (...) { + isc_throw(Dhcp6ConfigError, "Unrecoverable error: " + "a configuration parser threw in commit"); + } +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h new file mode 100644 index 0000000000..497a89c15c --- /dev/null +++ b/src/bin/dhcp6/config_parser.h @@ -0,0 +1,169 @@ +// Copyright (C) 2012 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 +#include +#include + +#ifndef DHCP6_CONFIG_PARSER_H +#define DHCP6_CONFIG_PARSER_H + +namespace isc { +namespace dhcp { + +class Dhcpv6Srv; + +/// An exception that is thrown if an error occurs while configuring an +/// \c Dhcpv6Srv object. +class Dhcp6ConfigError : public isc::Exception { +public: +Dhcp6ConfigError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +class Dhcp6ConfigParser { + /// + /// \name Constructors and Destructor + /// + /// Note: The copy constructor and the assignment operator are + /// intentionally defined as private to make it explicit that this is a + /// pure base class. + //@{ +private: + Dhcp6ConfigParser(const Dhcp6ConfigParser& source); + Dhcp6ConfigParser& operator=(const Dhcp6ConfigParser& source); +protected: + /// \brief The default constructor. + /// + /// This is intentionally defined as \c protected as this base class should + /// never be instantiated (except as part of a derived class). + Dhcp6ConfigParser() {} +public: + /// The destructor. + virtual ~Dhcp6ConfigParser() {} + //@} + + /// Prepare configuration value. + /// + /// This method parses the "value part" of the configuration identifier + /// that corresponds to this derived class and prepares a new value to + /// apply to the server. + /// In the above example, the derived class for the identifier "param1" + /// would be passed an data \c Element storing an integer whose value + /// is 10, and would record that value internally; + /// the derived class for the identifier "param2" would be passed a + /// map element and (after parsing) convert it into some internal + /// data structure. + /// + /// This method must validate the given value both in terms of syntax + /// and semantics of the configuration, so that the server will be + /// validly configured at the time of \c commit(). Note: the given + /// configuration value is normally syntactically validated, but the + /// \c build() implementation must also expect invalid input. If it + /// detects an error it may throw an exception of a derived class + /// of \c isc::Exception. + /// + /// Preparing a configuration value will often require resource + /// allocation. If it fails, it may throw a corresponding standard + /// exception. + /// + /// This method is not expected to be called more than once. Although + /// multiple calls are not prohibited by the interface, the behavior + /// is undefined. + /// + /// \param config_value The configuration value for the identifier + /// corresponding to the derived class. + virtual void build(isc::data::ConstElementPtr config_value) = 0; + + /// Apply the prepared configuration value to the server. + /// + /// This method is expected to be exception free, and, as a consequence, + /// it should normally not involve resource allocation. + /// Typically it would simply perform exception free assignment or swap + /// operation on the value prepared in \c build(). + /// In some cases, however, it may be very difficult to meet this + /// condition in a realistic way, while the failure case should really + /// be very rare. In such a case it may throw, and, if the parser is + /// called via \c configureDhcp6Server(), the caller will convert the + /// exception as a fatal error. + /// + /// This method is expected to be called after \c build(), and only once. + /// The result is undefined otherwise. + virtual void commit() = 0; +}; + +/// Configure an \c Dhcpv6Srv object with a set of configuration values. +/// +/// This function parses configuration information stored in \c config_set +/// and configures the \c server by applying the configuration to it. +/// It provides the strong exception guarantee as long as the underlying +/// derived class implementations of \c Dhcp6ConfigParser meet the assumption, +/// that is, it ensures that either configuration is fully applied or the +/// state of the server is intact. +/// +/// If a syntax or semantics level error happens during the configuration +/// (such as malformed configuration or invalid configuration parameter), +/// this function throws an exception of class \c Dhcp6ConfigError. +/// If the given configuration requires resource allocation and it fails, +/// a corresponding standard exception will be thrown. +/// Other exceptions may also be thrown, depending on the implementation of +/// the underlying derived class of \c Dhcp6ConfigError. +/// In any case the strong guarantee is provided as described above except +/// in the very rare cases where the \c commit() method of a parser throws +/// an exception. If that happens this function converts the exception +/// into a \c FatalError exception and rethrows it. This exception is +/// expected to be caught at the highest level of the application to terminate +/// the program gracefully. +/// +/// \param server The \c Dhcpv6Srv object to be configured. +/// \param config_set A JSON style configuration to apply to \c server. +void configureDhcp6Server(Dhcpv6Srv& server, + isc::data::ConstElementPtr config_set); + + +/// Create a new \c Dhcp6ConfigParser object for a given configuration +/// identifier. +/// +/// It internally identifies an appropriate derived class for the given +/// identifier and creates a new instance of that class. The caller can +/// then configure the \c server regarding the identifier by calling +/// the \c build() and \c commit() methods of the returned object. +/// +/// In practice, this function is only expected to be used as a backend of +/// \c configureDhcp6Server() and is not supposed to be called directly +/// by applications. It is publicly available mainly for testing purposes. +/// When called directly, the created object must be deleted by the caller. +/// Note: this means if this module and the caller use incompatible sets of +/// new/delete, it may cause unexpected strange failure. We could avoid that +/// by providing a separate deallocation function or by using a smart pointer, +/// but since the expected usage of this function is very limited (i.e. for +/// our own testing purposes) it would be an overkilling. We therefore prefer +/// simplicity and keeping the interface intuitive. +/// +/// If the resource allocation for the new object fails, a corresponding +/// standard exception will be thrown. Otherwise this function is not +/// expected to throw an exception, unless the constructor of the underlying +/// derived class implementation (unexpectedly) throws. +/// +/// \param server The \c Dhcpv6Srv object to be configured. +/// \param config_id The configuration identifier for which a parser object +/// is to be created. +/// \return A pointer to an \c Dhcp6ConfigParser object. +Dhcp6ConfigParser* createDhcp6ConfigParser(Dhcpv6Srv& server, + const std::string& config_id); + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // DHCP6_CONFIG_PARSER_H diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 4afb2039fd..6e2f0d8087 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,13 @@ ConstElementPtr ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE) .arg(new_config->str()); + + /// @todo: commented out as server is not able to understand partial + /// configuration (only the diff and not the whole configuration) + /* if (server_) { + configureDhcp6Server(*server_, new_config); + } */ + ConstElementPtr answer = isc::config::createAnswer(0, "Thank you for sending config."); return (answer); @@ -105,6 +113,12 @@ void ControlledDhcpv6Srv::establishSession() { dhcp6CommandHandler, false); config_session_->start(); + try { + configureDhcp6Server(*this, config_session_->getFullConfig()); + } catch (const Dhcp6ConfigError& ex) { + LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what()); + } + /// Integrate the asynchronous I/O model of BIND 10 configuration /// control with the "select" model of the DHCP server. This is /// fully explained in \ref dhcpv6Session. diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index 279049a754..b61ef2a8b2 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -4,9 +4,16 @@ "module_description": "DHCPv6 server daemon", "config_data": [ { "item_name": "interface", - "item_type": "string", + "item_type": "list", "item_optional": false, - "item_default": "all" + "item_default": [ "all" ], + "list_item_spec": + { + "item_name": "interface_name", + "item_type": "string", + "item_optional": false, + "item_default": "all" + } } , { "item_name": "renew-timer", diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index a3615319a7..dcfb2caad6 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -99,3 +99,9 @@ processed any command-line switches and is starting. This is a debug message issued during the IPv6 DHCP server startup. It lists some information about the parameters with which the server is running. + +% DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1 +This critical error message indicates that the initial DHCPv6 +configuration has failed. The server will continue to run, but +administrator's intervention is required. The server's configuration +must be fixed before it can become usable. diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 1d9308fcaa..30d2c384cd 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -48,6 +48,7 @@ dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc +dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc if USE_CLANGPP @@ -61,6 +62,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) dhcp6_unittests_LDADD = $(GTEST_LDADD) dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h index 549698babd..f5fa20b238 100644 --- a/src/lib/dhcp/triplet.h +++ b/src/lib/dhcp/triplet.h @@ -12,6 +12,9 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#ifndef TRIPLET_H +#define TRIPLET_H + namespace isc { namespace dhcp { @@ -106,3 +109,5 @@ protected: } // namespace isc::dhcp } // namespace isc + +#endif // ifdef TRIPLET_H From 4a6553ba36f68f141e7420b070ad6d621f9a8465 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 27 Sep 2012 19:33:20 +0200 Subject: [PATCH 009/194] [2269] DHCPv6 startup phase is now handled properly by the config parser. --- src/bin/dhcp6/config_parser.cc | 39 +++++++++++++++++++------------- src/bin/dhcp6/config_parser.h | 5 ++-- src/bin/dhcp6/ctrl_dhcp6_srv.cc | 25 ++++++++++++-------- src/bin/dhcp6/dhcp6_messages.mes | 11 +++++++++ 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index 377c3d48be..79ec28ee7b 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -22,12 +22,15 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include using namespace std; using namespace isc::data; @@ -64,12 +67,14 @@ public: :param_name_(param_name) { } virtual void build(ConstElementPtr new_config) { + std::cout << "Build for token: [" << param_name_ << "] = [" + << value_->str() << "]" << std::endl; value_ = new_config; } virtual void commit() { - // debug message. The whole DummyParser class is used only for parser + // Debug message. The whole DummyParser class is used only for parser // debugging, and is not used in production code. It is very convenient - // to keep it around. Please do not turn this cout into logger calls + // to keep it around. Please do not turn this cout into logger calls. std::cout << "Commit for token: [" << param_name_ << "] = [" << value_->str() << "]" << std::endl; } @@ -96,8 +101,6 @@ public: isc_throw(BadValue, "Failed to parse value " << value->str() << " as unsigned 32-bit integer."); } - cout << "### storing " << param_name_ << "=" << value_ << - " in " << storage_ << endl; storage_->insert(pair(param_name_, value_)); } @@ -158,7 +161,6 @@ public: virtual void build(ConstElementPtr value) { BOOST_FOREACH(ConstElementPtr iface, value->listValue()) { interfaces_.push_back(iface->str()); - cout << "#### Configured to listen on interface " << iface->str() << endl; } } @@ -201,6 +203,7 @@ public: boost::erase_all(txt, " "); boost::erase_all(txt, "\t"); + // Is this prefix/len notation? size_t pos = txt.find("/"); if (pos != string::npos) { IOAddress addr("::"); @@ -220,22 +223,18 @@ public: "definition: " << text_pool->stringValue()); } - cout << "#### Creating Pool6(TYPE_IA, " << addr.toText() << "/" - << (int)len << ")" << endl; - // using prefix/len notation Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len)); pools_->push_back(pool); continue; } + // Is this min-max notation? pos = txt.find("-"); if (pos != string::npos) { + // using min-max notation IOAddress min(txt.substr(0,pos-1)); IOAddress max(txt.substr(pos+1)); - cout << "#### Creating Pool6(TYPE_IA, " << min.toText() << "," - << max.toText() << ")" << endl; - Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max)); pools_->push_back(pool); @@ -270,7 +269,6 @@ public: void build(ConstElementPtr subnet) { - cout << "#### Subnet6ConfigParser::build(): parsing: [" << subnet->str() << "]" << endl; BOOST_FOREACH(ConfigPair param, subnet->mapValue()) { ParserPtr parser(createSubnet6ConfigParser(param.first)); @@ -305,8 +303,6 @@ public: void commit() { StringStorage::const_iterator it = string_values_.find("subnet"); - cout << "#### Subnet6ConfigParser::commit() string_values_.size()=" - << string_values_.size() << endl; if (it == string_values_.end()) { isc_throw(Dhcp6ConfigError, "Mandatory subnet definition in subnet missing"); @@ -328,7 +324,8 @@ public: Triplet pref = getParam("preferred-lifetime"); Triplet valid = getParam("valid-lifetime"); - cout << "#### Adding subnet " << addr.toText() << "/" << (int)len + /// @todo: Convert this to logger once the parser is working reliably + cout << "Adding subnet " << addr.toText() << "/" << (int)len << " with params t1=" << t1 << ", t2=" << t2 << ", pref=" << pref << ", valid=" << valid << endl; @@ -471,13 +468,18 @@ Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { return (f->second(config_id)); } -void +ConstElementPtr configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { if (!config_set) { isc_throw(Dhcp6ConfigError, "Null pointer is passed to configuration parser"); } + /// @todo: append most essential info here (like "2 new subnets configured") + string config_details; + + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str()); + ParserCollection parsers; try { BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) { @@ -501,6 +503,11 @@ configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { isc_throw(Dhcp6ConfigError, "Unrecoverable error: " "a configuration parser threw in commit"); } + + LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details); + + ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited."); + return (answer); } }; // end of isc::dhcp namespace diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h index 497a89c15c..6b71c351be 100644 --- a/src/bin/dhcp6/config_parser.h +++ b/src/bin/dhcp6/config_parser.h @@ -128,8 +128,9 @@ public: /// /// \param server The \c Dhcpv6Srv object to be configured. /// \param config_set A JSON style configuration to apply to \c server. -void configureDhcp6Server(Dhcpv6Srv& server, - isc::data::ConstElementPtr config_set); +isc::data::ConstElementPtr +configureDhcp6Server(Dhcpv6Srv& server, + isc::data::ConstElementPtr config_set); /// Create a new \c Dhcp6ConfigParser object for a given configuration diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 6e2f0d8087..4f402dd153 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -49,14 +49,13 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE) .arg(new_config->str()); - /// @todo: commented out as server is not able to understand partial - /// configuration (only the diff and not the whole configuration) - /* if (server_) { - configureDhcp6Server(*server_, new_config); - } */ + if (server_) { + return (configureDhcp6Server(*server_, new_config)); + } - ConstElementPtr answer = isc::config::createAnswer(0, - "Thank you for sending config."); + // that should never happen as we install config_handler after we + ConstElementPtr answer = isc::config::createAnswer(1, + "Configuration rejected, server is during startup/shutdown phase."); return (answer); } @@ -94,7 +93,7 @@ void ControlledDhcpv6Srv::sessionReader(void) { } void ControlledDhcpv6Srv::establishSession() { - + string specfile; if (getenv("B10_FROM_BUILD")) { specfile = string(getenv("B10_FROM_BUILD")) + @@ -104,15 +103,21 @@ void ControlledDhcpv6Srv::establishSession() { } /// @todo: Check if session is not established already. Throw, if it is. - + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING) .arg(specfile); cc_session_ = new Session(io_service_.get_io_service()); config_session_ = new ModuleCCSession(specfile, *cc_session_, - dhcp6ConfigHandler, + NULL, dhcp6CommandHandler, false); config_session_->start(); + // We initially create ModuleCCSession() without configHandler, as + // the session module is too eager to send partial configuration. + // We want to get the full configuration, so we explicitly call + // getFullConfig() and then pass it to our configHandler. + config_session_->setConfigHandler(dhcp6ConfigHandler); + try { configureDhcp6Server(*this, config_session_->getFullConfig()); } catch (const Dhcp6ConfigError& ex) { diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index dcfb2caad6..b2de63afd9 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -105,3 +105,14 @@ This critical error message indicates that the initial DHCPv6 configuration has failed. The server will continue to run, but administrator's intervention is required. The server's configuration must be fixed before it can become usable. + +% DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1 +This is a debug message that is issued every time the server receives +configuration. That happens during every start up and also when server +configuration change is committed by the administrator. + +% DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1 +This is an informational message that announces successful processing +of a new configuration. That may happen as a result of one of two +events: server startup or commit of configuration change, done by the +administrator. Additional information may be provided. From 7e7171deec8d2a31147a9d5700f4315e2ea3e87b Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 28 Sep 2012 20:39:13 +0200 Subject: [PATCH 010/194] [2269] dhcp6 config parser now returns status codes and does not throw. --- src/bin/dhcp6/config_parser.cc | 106 +++++++++++++++++++++++++------ src/bin/dhcp6/config_parser.h | 2 +- src/bin/dhcp6/dhcp6_messages.mes | 4 ++ 3 files changed, 93 insertions(+), 19 deletions(-) diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index 79ec28ee7b..efe8d84d26 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -21,16 +21,16 @@ #include #include #include +#include #include #include -#include -#include +#include #include #include #include #include +#include #include -#include using namespace std; using namespace isc::data; @@ -43,9 +43,7 @@ typedef boost::shared_ptr ParserPtr; typedef pair ConfigPair; typedef std::vector ParserCollection; typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id); - typedef std::map FactoryMap; - typedef std::map Uint32Storage; /// @brief That is a map with global parameters that will be used as defaults Uint32Storage uint32_defaults; @@ -54,23 +52,40 @@ typedef std::map StringStorage; StringStorage string_defaults; typedef std::vector PoolStorage; -PoolStorage pool_defaults; /// @brief a dummy configuration parser /// /// It is a debugging parser. It does not configure anything, /// will accept any configuration and will just print it out -/// on commit. +/// on commit. Useful for debugging existing configurations and +/// adding new ones. class DummyParser : public Dhcp6ConfigParser { public: + + /// @brief Constructor + /// + /// See \ref Dhcp6ConfigParser class for details. + /// + /// @param param_name name of the parsed parameter DummyParser(const std::string& param_name) :param_name_(param_name) { } + + /// @brief builds parameter value + /// + /// See \ref Dhcp6ConfigParser class for details. virtual void build(ConstElementPtr new_config) { std::cout << "Build for token: [" << param_name_ << "] = [" << value_->str() << "]" << std::endl; value_ = new_config; } + + /// @brief pretends to apply the configuration + /// + /// This is a method required by base class. It pretends to apply the + /// configuration, but in fact it only prints the parameter out. + /// + /// See \ref Dhcp6ConfigParser class for details. virtual void commit() { // Debug message. The whole DummyParser class is used only for parser // debugging, and is not used in production code. It is very convenient @@ -79,21 +94,45 @@ public: << value_->str() << "]" << std::endl; } + /// @brief factory that constructs DummyParser objects + /// + /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new DummyParser(param_name)); } protected: + /// name of the parsed parameter std::string param_name_; + + /// pointer to the actual value of the parameter ConstElementPtr value_; }; +/// @brief Configuration parser for uint32 types +/// +/// This class is a generic parser that is able to handle any uint32 integer +/// type. By default it stores the value in external global container +/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters +/// in subnet config), it can be pointed to a different storage, using +/// setStorage() method. This class follows the parser interface, laid out +/// in its base class, \ref Dhcp6ConfigParser. + class Uint32Parser : public Dhcp6ConfigParser { public: + + /// @brief constructor for Uint32Parser + /// @param param_name name of the parameter that is going to be parsed Uint32Parser(const std::string& param_name) :storage_(&uint32_defaults), param_name_(param_name) { } + /// @brief builds parameter value + /// + /// Parses configuration entry and stored it in storage. See + /// \ref setStorage() for details. + /// + /// @param value pointer to the content of parsed values virtual void build(ConstElementPtr value) { try { value_ = boost::lexical_cast(value->str()); @@ -104,9 +143,20 @@ public: storage_->insert(pair(param_name_, value_)); } + /// @brief does nothing + /// + /// This method is required for all parser. The value itself + /// is not commited anywhere. Higher level parsers are expected to + /// use values stored in the storage, e.g. renew-timer for a given + /// subnet is stored in subnet-specific storage. It is not commited + /// here, but is rather used by \ref Subnet6Parser when constructing + /// the subnet. virtual void commit() { } + /// @brief factory that constructs DummyParser objects + /// + /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new Uint32Parser(param_name)); } @@ -325,13 +375,18 @@ public: Triplet valid = getParam("valid-lifetime"); /// @todo: Convert this to logger once the parser is working reliably - cout << "Adding subnet " << addr.toText() << "/" << (int)len - << " with params t1=" << t1 << ", t2=" << t2 << ", pref=" - << pref << ", valid=" << valid << endl; + stringstream tmp; + tmp << addr.toText() << "/" << (int)len + << " with params t1=" << t1 << ", t2=" << t2 << ", pref=" + << pref << ", valid=" << valid; + + LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str()); Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid)); - + for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) { + subnet->addPool6(*it); + } CfgMgr::instance().addSubnet6(subnet); } @@ -418,6 +473,12 @@ public: } void commit() { + // @todo: Implement more subtle reconfiguration than toss + // the old one and replace with the new one. + + // remove old subnets + CfgMgr::instance().deleteSubnets6(); + BOOST_FOREACH(ParserPtr subnet, subnets_) { subnet->commit(); } @@ -469,7 +530,7 @@ Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { } ConstElementPtr -configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { +configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) { if (!config_set) { isc_throw(Dhcp6ConfigError, "Null pointer is passed to configuration parser"); @@ -488,20 +549,29 @@ configureDhcp6Server(Dhcpv6Srv& server, ConstElementPtr config_set) { parser->build(config_pair.second); parsers.push_back(parser); } - } catch (const Dhcp6ConfigError& ex) { - throw; // simply rethrowing it } catch (const isc::Exception& ex) { - isc_throw(Dhcp6ConfigError, "Server configuration failed: " << - ex.what()); + ConstElementPtr answer = isc::config::createAnswer(1, + string("Configuration parsing failed:") + ex.what()); + return (answer); + } catch (...) { + // for things like bad_cast in boost::lexical_cast + ConstElementPtr answer = isc::config::createAnswer(1, + string("Configuration parsing failed")); } try { BOOST_FOREACH(ParserPtr parser, parsers) { parser->commit(); } + } + catch (const isc::Exception& ex) { + ConstElementPtr answer = isc::config::createAnswer(2, + string("Configuration commit failed:") + ex.what()); + return (answer); } catch (...) { - isc_throw(Dhcp6ConfigError, "Unrecoverable error: " - "a configuration parser threw in commit"); + // for things like bad_cast in boost::lexical_cast + ConstElementPtr answer = isc::config::createAnswer(2, + string("Configuration commit failed")); } LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details); diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h index 6b71c351be..5c941d5bdd 100644 --- a/src/bin/dhcp6/config_parser.h +++ b/src/bin/dhcp6/config_parser.h @@ -162,7 +162,7 @@ configureDhcp6Server(Dhcpv6Srv& server, /// is to be created. /// \return A pointer to an \c Dhcp6ConfigParser object. Dhcp6ConfigParser* createDhcp6ConfigParser(Dhcpv6Srv& server, - const std::string& config_id); + const std::string& config_id); }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index b2de63afd9..56f4b1c970 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -111,6 +111,10 @@ This is a debug message that is issued every time the server receives configuration. That happens during every start up and also when server configuration change is committed by the administrator. +% DHCP6_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1 +This is an informational message that the configuration has extended +and specified new subnet is now supported. + % DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1 This is an informational message that announces successful processing of a new configuration. That may happen as a result of one of two From ff837bb1fc512920556a796c40cacc20407b165c Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Fri, 28 Sep 2012 20:40:45 +0200 Subject: [PATCH 011/194] [2269] Tests for dhcp6 config parser implemented. --- src/bin/dhcp6/dhcp6_srv.cc | 7 + src/bin/dhcp6/tests/Makefile.am | 1 + src/bin/dhcp6/tests/config_parser_unittest.cc | 225 ++++++++++++++++++ src/lib/dhcp/cfgmgr.h | 15 ++ src/lib/dhcp/tests/cfgmgr_unittest.cc | 7 + 5 files changed, 255 insertions(+) create mode 100644 src/bin/dhcp6/tests/config_parser_unittest.cc diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 7c21941d4d..f9457d5b77 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -42,6 +42,13 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1"; Dhcpv6Srv::Dhcpv6Srv(uint16_t port) { + if (port == 0) { + // used for testing purposes. Some tests, e.g. configuration parser, + // require Dhcpv6Srv object, but they don't really need it to do + // anything. This speed up and simplifies the tests. + return; + } + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port); // First call to instance() will create IfaceMgr (it's a singleton) diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 30d2c384cd..14797335b3 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -45,6 +45,7 @@ TESTS += dhcp6_unittests dhcp6_unittests_SOURCES = dhcp6_unittests.cc dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += config_parser_unittest.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc new file mode 100644 index 0000000000..8c38762bdd --- /dev/null +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -0,0 +1,225 @@ +// Copyright (C) 2012 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::config; + +namespace { + +class Dhcp6ParserTest : public ::testing::Test { +public: + Dhcp6ParserTest() + :rcode_(-1) { + // open port 0 means to not do anything at all + srv_ = new Dhcpv6Srv(0); + } + + ~Dhcp6ParserTest() { + delete srv_; + }; + + Dhcpv6Srv * srv_; + + int rcode_; + ConstElementPtr comment_; +}; + + +TEST_F(Dhcp6ParserTest, version) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{\"version\": 0}"))); + + // returned value must be 0 (configuration accepted) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); +} + +TEST_F(Dhcp6ParserTest, bogus_command) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{\"bogus\": 5}"))); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(1, rcode_); +} + +TEST_F(Dhcp6ParserTest, empty_subnet) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"))); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); +} + +TEST_F(Dhcp6ParserTest, subnet_global_defaults) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1000, subnet->getT1()); + EXPECT_EQ(2000, subnet->getT2()); + EXPECT_EQ(3000, subnet->getPreferred()); + EXPECT_EQ(4000, subnet->getValid()); +} + +// +TEST_F(Dhcp6ParserTest, subnet_local) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ]," + " \"renew-timer\": 1, " + " \"rebind-timer\": 2, " + " \"preferred-lifetime\": 3," + " \"valid-lifetime\": 4," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1, subnet->getT1()); + EXPECT_EQ(2, subnet->getT2()); + EXPECT_EQ(3, subnet->getPreferred()); + EXPECT_EQ(4, subnet->getValid()); +} + +TEST_F(Dhcp6ParserTest, pool_out_of_subnet) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"4001:db8:1::/80\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 2 (values error) + // as the pool does not belong to that subnet + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(2, rcode_); + +} + +TEST_F(Dhcp6ParserTest, subnet_prefix_len) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::/80\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1000, subnet->getT1()); + EXPECT_EQ(2000, subnet->getT2()); + EXPECT_EQ(3000, subnet->getPreferred()); + EXPECT_EQ(4000, subnet->getValid()); +} + +}; diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h index 5b73f2b966..f57ef994d6 100644 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@ -98,6 +98,21 @@ public: /// to remove subnets. The only case where subnet6 removal would be /// needed is a dynamic server reconfiguration - a use case that is not /// planned to be supported any time soon. + + /// @brief removes all subnets + /// + /// This method removes all existing subnets. It is used during + /// reconfiguration - old configuration is wiped and new definitions + /// are used to recreate subnets. + /// + /// @todo Implement more intelligent approach. Note that comparison + /// between old and new configuration is tricky. For example: is + /// 2000::/64 and 2000::/48 the same subnet or is it something + /// completely new? + void deleteSubnets6() { + subnets6_.clear(); + } + protected: /// @brief Protected constructor. diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc index 7f287eaf80..bdd9a37541 100644 --- a/src/lib/dhcp/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc @@ -266,6 +266,9 @@ TEST(Subnet6Test, Subnet6_Pool6_checks) { Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16)); EXPECT_THROW(subnet->addPool6(pool3), BadValue); + + Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80)); + EXPECT_THROW(subnet->addPool6(pool4), BadValue); } // This test verifies if the configuration manager is able to hold and return @@ -294,6 +297,10 @@ TEST(CfgMgrTest, subnet6) { EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef"))); EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1"))); + cfg_mgr.deleteSubnets6(); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("200::123"))); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("3000::123"))); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("4000::123"))); } } // end of anonymous namespace From 938d8b3145666d6f7d7084668b0ee665b3458e4a Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Mon, 1 Oct 2012 16:47:31 +0200 Subject: [PATCH 012/194] [2269] Documentation update in progress --- doc/devel/02-dhcp.dox | 23 ------- src/bin/dhcp6/config_parser.cc | 120 +++++++++++++++++++++++++++++---- src/bin/dhcp6/config_parser.h | 15 +++++ src/bin/dhcp6/dhcp6.dox | 80 ++++++++++++++++++++++ 4 files changed, 202 insertions(+), 36 deletions(-) create mode 100644 src/bin/dhcp6/dhcp6.dox diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox index 5217f73971..a4bbff9955 100644 --- a/doc/devel/02-dhcp.dox +++ b/doc/devel/02-dhcp.dox @@ -57,29 +57,6 @@ * that does not support msgq. That is useful for embedded environments. * It may also be useful in validation. * - * @page dhcpv6 DHCPv6 Server Component - * - * BIND10 offers DHCPv6 server implementation. It is implemented as - * b10-dhcp6 component. Its primary code is located in - * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively, - * especially lib::dhcp::Pkt6, isc::dhcp::Option and - * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton - * functionality, i.e. it is able to receive and process incoming - * requests and trasmit responses. However, it does not have database - * management, so it returns only one, hardcoded lease to whoever asks - * for it. - * - * DHCPv6 server component does not support relayed traffic yet, as - * support for relay decapsulation is not implemented yet. - * - * DHCPv6 server component does not use BIND10 logging yet. - * - * @section dhcpv6Session BIND10 message queue integration - * - * DHCPv4 server component is now integrated with BIND10 message queue. - * It follows the same principle as DHCPv4. See \ref dhcpv4Session for - * details. - * * @page libdhcp libdhcp++ * * @section libdhcpIntro Libdhcp++ Library Introduction diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index efe8d84d26..271c3b1716 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -39,20 +39,33 @@ using namespace isc::asiolink; namespace isc { namespace dhcp { -typedef boost::shared_ptr ParserPtr; +/// @brief auxiliary type used for storing element name and its parser typedef pair ConfigPair; -typedef std::vector ParserCollection; + +/// @brief a factory method that will create a parser for a given element name typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id); + +/// @brief a collection of factories that creates parsers for specified element names typedef std::map FactoryMap; + +/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900) typedef std::map Uint32Storage; -/// @brief That is a map with global parameters that will be used as defaults + +/// @brief a collection of elements that store string values +typedef std::map StringStorage; + +/// @brief a collection of pools +/// +/// That type is used as intermediate storage, when pools are parsed, but there is +/// no subnet object created yet to store them. +typedef std::vector PoolStorage; + +/// @brief Global uint32 parameters that will be used as defaults. Uint32Storage uint32_defaults; -typedef std::map StringStorage; +/// @brief global string parameters that will be used as defaults. StringStorage string_defaults; -typedef std::vector PoolStorage; - /// @brief a dummy configuration parser /// /// It is a debugging parser. It does not configure anything, @@ -74,6 +87,8 @@ public: /// @brief builds parameter value /// /// See \ref Dhcp6ConfigParser class for details. + /// + /// @param new_config pointer to the new configuration virtual void build(ConstElementPtr new_config) { std::cout << "Build for token: [" << param_name_ << "] = [" << value_->str() << "]" << std::endl; @@ -109,7 +124,7 @@ protected: ConstElementPtr value_; }; -/// @brief Configuration parser for uint32 types +/// @brief Configuration parser for uint32 parameters /// /// This class is a generic parser that is able to handle any uint32 integer /// type. By default it stores the value in external global container @@ -117,19 +132,21 @@ protected: /// in subnet config), it can be pointed to a different storage, using /// setStorage() method. This class follows the parser interface, laid out /// in its base class, \ref Dhcp6ConfigParser. - +/// +/// For overview of usability of this generic purpose parser, see +/// \ref dhcpv6-config-inherit page. class Uint32Parser : public Dhcp6ConfigParser { public: /// @brief constructor for Uint32Parser - /// @param param_name name of the parameter that is going to be parsed + /// @param param_name name of the configuration parameter being parsed Uint32Parser(const std::string& param_name) :storage_(&uint32_defaults), param_name_(param_name) { } /// @brief builds parameter value /// - /// Parses configuration entry and stored it in storage. See + /// Parses configuration entry and stores it in a storage. See /// \ref setStorage() for details. /// /// @param value pointer to the content of parsed values @@ -154,76 +171,153 @@ public: virtual void commit() { } - /// @brief factory that constructs DummyParser objects + /// @brief factory that constructs Uint32Parser objects /// /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new Uint32Parser(param_name)); } + /// @brief sets storage for value of this parameter + /// + /// See \ref dhcpv6-config-inherit for details. + /// + /// @param storage pointer to the storage container void setStorage(Uint32Storage* storage) { storage_ = storage; } protected: + /// pointer to the storage, where parsed value will be stored Uint32Storage * storage_; + + /// name of the parameter to be parsed std::string param_name_; + + /// the actual parsed value uint32_t value_; }; +/// @brief Configuration parser for string parameters +/// +/// This class is a generic parser that is able to handle any string +/// parameter. By default it stores the value in external global container +/// (string_defaults). If used in smaller scopes (e.g. to parse parameters +/// in subnet config), it can be pointed to a different storage, using +/// setStorage() method. This class follows the parser interface, laid out +/// in its base class, \ref Dhcp6ConfigParser. +/// +/// For overview of usability of this generic purpose parser, see +/// \ref dhcpv6-config-inherit page. class StringParser : public Dhcp6ConfigParser { public: + + /// @brief constructor for StringParser + /// @param param_name name of the configuration parameter being parsed StringParser(const std::string& param_name) :storage_(&string_defaults), param_name_(param_name) { } + /// @brief builds parameter value + /// + /// Parses configuration entry and stored it in storage. See + /// \ref setStorage() for details. + /// + /// @param value pointer to the content of parsed values virtual void build(ConstElementPtr value) { value_ = value->str(); boost::erase_all(value_, "\""); storage_->insert(pair(param_name_, value_)); } + /// @brief does nothing + /// + /// This method is required for all parser. The value itself + /// is not commited anywhere. Higher level parsers are expected to + /// use values stored in the storage, e.g. renew-timer for a given + /// subnet is stored in subnet-specific storage. It is not commited + /// here, but is rather used by its parent parser when constructing + /// an object, e.g. the subnet. virtual void commit() { } + /// @brief factory that constructs StringParser objects + /// + /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new StringParser(param_name)); } + /// @brief sets storage for value of this parameter + /// + /// See \ref dhcpv6-config-inherit for details. + /// + /// @param storage pointer to the storage container void setStorage(StringStorage * storage) { storage_ = storage; } protected: + /// pointer to the storage, where parsed value will be stored StringStorage * storage_; + + /// name of the parameter to be parsed std::string param_name_; + + /// the actual parsed value std::string value_; }; + +/// @brief parser for interface list definition +/// +/// This parser handles Dhcp6/interface entry. +/// It contains a list of network interfaces that the server listens on. +/// In particular, it can contain an entry called "all" or "any" that +/// designates all interfaces. class InterfaceListConfigParser : public Dhcp6ConfigParser { public: + + /// @brief constructor + /// + /// As this is a dedicated parser, it must be used to parse + /// "interface" parameter only. All other types will throw exception. + /// + /// @param param_name name of the configuration parameter being parsed InterfaceListConfigParser(const std::string& param_name) { if (param_name != "interface") { isc_throw(NotImplemented, "Internal error. Interface configuration " "parser called for the wrong parameter: " << param_name); } } + + /// @brief parses parameters value + /// + /// Parses configuration entry (list of parameters) and stores it in + /// storage. See \ref setStorage() for details. + /// + /// @param value pointer to the content of parsed values virtual void build(ConstElementPtr value) { BOOST_FOREACH(ConstElementPtr iface, value->listValue()) { interfaces_.push_back(iface->str()); } } + /// @brief commits interfaces list configuration virtual void commit() { - /// @todo: Implement per interface listening. Currently always listening on all - /// interfaces. + /// @todo: Implement per interface listening. Currently always listening + /// on all interfaces. } + /// @brief factory that constructs InterfaceListConfigParser objects + /// + /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new InterfaceListConfigParser(param_name)); } protected: + /// contains list of network interfaces vector interfaces_; }; diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h index 5c941d5bdd..79dae8d6eb 100644 --- a/src/bin/dhcp6/config_parser.h +++ b/src/bin/dhcp6/config_parser.h @@ -28,6 +28,12 @@ class Dhcpv6Srv; /// \c Dhcpv6Srv object. class Dhcp6ConfigError : public isc::Exception { public: + +/// @brief constructor +/// +/// @param file name of the file, where exception occurred +/// @param line line of the file, where exception occurred +/// @param what text description of the issue that caused exception Dhcp6ConfigError(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) {} }; @@ -103,6 +109,15 @@ public: virtual void commit() = 0; }; +/// @brief a pointer to configuration parser +typedef boost::shared_ptr ParserPtr; + +/// @brief a collection of parsers +/// +/// This container is used to store pointer to parsers for a given scope. +typedef std::vector ParserCollection; + + /// Configure an \c Dhcpv6Srv object with a set of configuration values. /// /// This function parses configuration information stored in \c config_set diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox new file mode 100644 index 0000000000..aafd4d6dea --- /dev/null +++ b/src/bin/dhcp6/dhcp6.dox @@ -0,0 +1,80 @@ +/** + * @page dhcpv6 DHCPv6 Server Component + * + * BIND10 offers DHCPv6 server implementation. It is implemented as + * b10-dhcp6 component. Its primary code is located in + * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively, + * especially lib::dhcp::Pkt6, isc::dhcp::Option and + * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton + * functionality, i.e. it is able to receive and process incoming + * requests and trasmit responses. However, it does not have database + * management, so it returns only one, hardcoded lease to whoever asks + * for it. + * + * DHCPv6 server component does not support relayed traffic yet, as + * support for relay decapsulation is not implemented yet. + * + * DHCPv6 server component does not use BIND10 logging yet. + * + * @section dhcpv6Session BIND10 message queue integration + * + * DHCPv4 server component is now integrated with BIND10 message queue. + * It follows the same principle as DHCPv4. See \ref dhcpv4Session for + * details. + + @section dhcpv6-config-parser Configuration Parser in DHCPv6 + + b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During + initial configuration (See \ref ControlledDhcpv6Srv::establishSession()), + the configuration handler callback is installed + (see ControlledDhcpv6Srv::dhcp6ConfigHandler(). It is called every time there + is a new configuration. In particular, it is called every time during daemon + start process. It contains a ConstElementPtr to a new configuration. This + simple handler calls \ref isc::dhcp::configureDhcp6Server() method that + processes received configuration. + + This method iterates over list of received configuration elements and creates + a list of parsers for each received entry. Parser is an object that is derived + from a \ref Dhcp6ConfigParser class. Once a parser is created (constructor), + its value is set (using build() method). Once all parsers are build, the + configuration is then applied ("commited") and commit() method is called. + + All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some + of them are generic (e.g. \ref Uint32Parser that is able to handle + any unsigned 32 bit integer), but some are very specialized + (e.g. \ref Subnets6ListConfigParser parses definitions of Subnet6 lists). In + some cases, e.g. subnet6 definitions, the configuration entry is not a simple + value, but a map or a list itself. In such case, the parser iterates over + all elements and creates parsers for a given scope. This process may be + repeated (sort of) recursively. + + @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance + + One notable useful features of DHCP configuration is its parameter inheritance. + For example, renew-timer value may be specified at a global scope and it + then applies to all subnets. However, some subnets may have it overwritten + with more specific values that takes precedence over global values that are + considered defaults. Some parsers (e.g. \ref Uint32ConfigParser and + \StringParser) implement that inheritance. By default, they store values in + global uint32_defaults and string_defaults storages. However, it is possible + to instruct them to store parsed values in more specific storages. That + capability is used, e.g. in \ref Subnet6ConfigParser that has its own storage + that is unique for each subnet. Finally, during commit phase (commit() method), + appropriate parsers can use apply parameter inheritance. + + Debugging configuration parser may be confusing. Therefore there is a special + class called \ref DummyParser. It does not configure anything, but just accepts + any parameter of any type. If requested to commit configuration, it will print + out received parameter name and its value. This class is not currently used, + but it is convenient to have it every time a new parameter is added to DHCP + configuration. For that purpose it should be left in the code. + + Parameter inheritance is done during reconfiguration phase, as reconfigurations + are rare, so extra logic here is not a problem. On the other hand, values of + those parameters may be used thousands times per second, so its use must be as + simple as possible. In fact, currently the code has to call Subnet6->getT1() and + do not implement any fancy inheritance logic. + + */ + + From 43636d3f31d1684984eb5337b91a418dd1725fbe Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Mon, 1 Oct 2012 18:56:55 +0200 Subject: [PATCH 013/194] [2269] Remaining DHCPv6 config parsers documented. --- src/bin/dhcp6/config_parser.cc | 126 +++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 7 deletions(-) diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index 271c3b1716..6c6c937dca 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -218,7 +218,7 @@ public: :storage_(&string_defaults), param_name_(param_name) { } - /// @brief builds parameter value + /// @brief parses parameter value /// /// Parses configuration entry and stored it in storage. See /// \ref setStorage() for details. @@ -275,6 +275,8 @@ protected: /// It contains a list of network interfaces that the server listens on. /// In particular, it can contain an entry called "all" or "any" that /// designates all interfaces. +/// +/// It is useful for parsing Dhcp6/interface parameter. class InterfaceListConfigParser : public Dhcp6ConfigParser { public: @@ -321,12 +323,30 @@ protected: vector interfaces_; }; +/// @brief parser for pool definition +/// +/// This parser handles pool definitions, i.e. a list of entries of one +/// of two syntaxes: min-max and prefix/len. Pool6 objects are created +/// and stored in chosen PoolStorage container. +/// +/// As there are no default values for pool, setStorage() must be called +/// before build(). Otherwise exception will be thrown. +/// +/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters. class PoolParser : public Dhcp6ConfigParser { public: + + /// @brief constructor. PoolParser(const std::string& /*param_name*/) :pools_(NULL) { // ignore parameter name, it is always Dhcp6/subnet6[X]/pool } + + /// @brief parses the actual list + /// + /// This method parses the actual list of interfaces. + /// No validation is done at this stage, everything is interpreted as + /// interface name. void build(ConstElementPtr pools_list) { // setStorage() should have been called before build if (!pools_) { @@ -336,8 +356,6 @@ public: BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) { - - // That should be a single pool representation. It should contain // text is form prefix/len or first - last. Note that spaces // are allowed @@ -390,27 +408,54 @@ public: ". Does not contain - (for min-max) nor / (prefix/len)"); } } + + /// @brief sets storage for value of this parameter + /// + /// See \ref dhcpv6-config-inherit for details. + /// + /// @param storage pointer to the storage container void setStorage(PoolStorage* storage) { pools_ = storage; } - void commit() {} + /// @brief does nothing. + /// + /// This method is required for all parser. The value itself + /// is not commited anywhere. Higher level parsers (for subnet) are expected + /// to use values stored in the storage, + virtual void commit() {} + /// @brief factory that constructs PoolParser objects + /// + /// @param param_name name of the parameter to be parsed static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new PoolParser(param_name)); } protected: + /// @brief pointer to the actual Pools storage + /// + /// That is typically a storage somewhere in Subnet parser + /// (an upper level parser). PoolStorage * pools_; - }; /// @brief this class parses a single subnet +/// +/// This class parses the whole subnet definition. It creates parsers +/// for received configuration parameters as needed. class Subnet6ConfigParser : public Dhcp6ConfigParser { public: - Subnet6ConfigParser(const std::string& param_name) { + + /// @brief constructor + Subnet6ConfigParser(const std::string& ) { + // The parameter should always be "subnet", but we don't check here + // against it in case some wants to reuse this parser somewhere. } + /// @brief parses parameter value + /// + /// @param subnet pointer to the content of subnet definition void build(ConstElementPtr subnet) { BOOST_FOREACH(ConfigPair param, subnet->mapValue()) { @@ -444,6 +489,12 @@ public: // Ok, we now have subnet parsed } + /// @brief commits received configuration. + /// + /// This method does most of the configuration. Many other parsers are just + /// storing the values that are actually consumed here. Pool definitions + /// created in other parsers are used here and added to newly created Subnet6 + /// objects. Subnet6 are then added to DHCP CfgMgr. void commit() { StringStorage::const_iterator it = string_values_.find("subnet"); @@ -486,6 +537,13 @@ public: } protected: + + /// @brief creates parsers for entries in subnet definition + /// + /// @todo Add subnet-specific things here (e.g. subnet-specific options) + /// + /// @param config_id name od the entry + /// @return parser object for specified entry name Dhcp6ConfigParser* createSubnet6ConfigParser(const std::string& config_id) { FactoryMap factories; @@ -516,6 +574,14 @@ protected: return (f->second(config_id)); } + /// @brief returns value for a given parameter (after using inheritance) + /// + /// This method implements inheritance. For a given parameter name, it first + /// checks if there is a global value for it and overwrites it with specific + /// value if such value was defined in subnet. + /// + /// @param name name of the parameter + /// @return triplet with the parameter name Triplet getParam(const std::string& name) { uint32_t value = 0; bool found = false; @@ -540,18 +606,39 @@ protected: } } + /// storage for subnet-specific uint32 values Uint32Storage uint32_values_; + + /// storage for subnet-specific integer values StringStorage string_values_; + + /// storage for pools belonging to this subnet PoolStorage pools_; + + /// parsers are stored here ParserCollection parsers_; }; /// @brief this class parses list of subnets +/// +/// This is a wrapper parser that handles the whole list of Subnet6 +/// definitions. It iterates over all entries and creates Subnet6ConfigParser +/// for each entry. class Subnets6ListConfigParser : public Dhcp6ConfigParser { public: - Subnets6ListConfigParser(const std::string& param_name) { + + /// @brief constructor + /// + Subnets6ListConfigParser(const std::string&) { + /// parameter name is ignored } + /// @brief parses contents of the list + /// + /// Iterates over all entries on the list and creates Subnet6ConfigParser + /// for each entry. + /// + /// @param subnets_list pointer to a list of IPv6 subnets void build(ConstElementPtr subnets_list) { // No need to define FactoryMap here. There's only one type @@ -566,6 +653,10 @@ public: } + /// @brief commits subnets definitions. + /// + /// Iterates over all Subnet6 parsers. Each parser contains definitions + /// of a single subnet and its parameters and commits each subnet separately. void commit() { // @todo: Implement more subtle reconfiguration than toss // the old one and replace with the new one. @@ -579,10 +670,14 @@ public: } + /// @brief Returns Subnet6ListConfigParser object + /// @param param_name name of the parameter + /// @return Subnets6ListConfigParser object static Dhcp6ConfigParser* Factory(const std::string& param_name) { return (new Subnets6ListConfigParser(param_name)); } + /// @brief collection of subnet parsers. ParserCollection subnets_; }; @@ -590,6 +685,9 @@ public: /// /// This method creates global parsers that parse global parameters, i.e. /// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth. +/// +/// @param config_id pointer to received global configuration entry +/// @return parser for specified global DHCPv6 parameter Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { FactoryMap factories; @@ -623,6 +721,20 @@ Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { return (f->second(config_id)); } +/// @brief configures DHCPv6 server +/// +/// This function is called every time a new configuration is received. The extra +/// parameter is a reference to DHCPv6 server component. It is currently not used +/// and CfgMgr::instance() is accessed instead. +/// +/// This method does not throw. It catches all exceptions and returns them as +/// reconfiguration statuses. It may return the following response codes: +/// 0 - configuration successful +/// 1 - malformed configuration (parsing failed) +/// 2 - logical error (parsing was successful, but the values are invalid) +/// +/// @param config_set a new configuration for DHCPv6 server +/// @return answer that contains result of reconfiguration ConstElementPtr configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) { if (!config_set) { From 5b9ab4bc5c038d3fe6e12a0cf90c33e5f54d0f45 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Mon, 1 Oct 2012 19:19:49 +0200 Subject: [PATCH 014/194] [2269] Developer's guide updated. --- doc/devel/mainpage.dox | 5 ++ src/bin/dhcp6/dhcp6.dox | 115 ++++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox index ca9d8815fa..0da52875bb 100644 --- a/doc/devel/mainpage.dox +++ b/doc/devel/mainpage.dox @@ -15,12 +15,17 @@ * BIND10 webpage (http://bind10.isc.org) * * @section DNS + * - Authoritative DNS (todo) + * - Recursive resolver (todo) * - @subpage DataScrubbing * * @section DHCP * - @subpage dhcpv4 * - @subpage dhcpv4Session * - @subpage dhcpv6 + * - @subpage dhcpv6-session + * - @subpage dhcpv6-config-parser + * - @subpage dhcpv6-config-inherit * - @subpage libdhcp * - @subpage libdhcpIntro * - @subpage libdhcpIfaceMgr diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox index aafd4d6dea..fe842de44b 100644 --- a/src/bin/dhcp6/dhcp6.dox +++ b/src/bin/dhcp6/dhcp6.dox @@ -1,71 +1,72 @@ /** - * @page dhcpv6 DHCPv6 Server Component - * - * BIND10 offers DHCPv6 server implementation. It is implemented as - * b10-dhcp6 component. Its primary code is located in - * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively, - * especially lib::dhcp::Pkt6, isc::dhcp::Option and - * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton - * functionality, i.e. it is able to receive and process incoming - * requests and trasmit responses. However, it does not have database - * management, so it returns only one, hardcoded lease to whoever asks - * for it. - * - * DHCPv6 server component does not support relayed traffic yet, as - * support for relay decapsulation is not implemented yet. - * - * DHCPv6 server component does not use BIND10 logging yet. - * - * @section dhcpv6Session BIND10 message queue integration - * - * DHCPv4 server component is now integrated with BIND10 message queue. - * It follows the same principle as DHCPv4. See \ref dhcpv4Session for - * details. - + @page dhcpv6 DHCPv6 Server Component + + BIND10 offers DHCPv6 server implementation. It is implemented as + b10-dhcp6 component. Its primary code is located in + isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively, + especially lib::dhcp::Pkt6, isc::dhcp::Option and + isc::dhcp::IfaceMgr classes. Currently this code offers skeleton + functionality, i.e. it is able to receive and process incoming + requests and trasmit responses. However, it does not have database + management, so it returns only one, hardcoded lease to whoever asks + for it. + + DHCPv6 server component does not support relayed traffic yet, as + support for relay decapsulation is not implemented yet. + + DHCPv6 server component does not use BIND10 logging yet. + + @section dhcpv6-session BIND10 message queue integration + + DHCPv4 server component is now integrated with BIND10 message queue. + It follows the same principle as DHCPv4. See \ref dhcpv4Session for + details. + @section dhcpv6-config-parser Configuration Parser in DHCPv6 b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During - initial configuration (See \ref ControlledDhcpv6Srv::establishSession()), - the configuration handler callback is installed - (see ControlledDhcpv6Srv::dhcp6ConfigHandler(). It is called every time there - is a new configuration. In particular, it is called every time during daemon - start process. It contains a ConstElementPtr to a new configuration. This - simple handler calls \ref isc::dhcp::configureDhcp6Server() method that - processes received configuration. + initial configuration (See \ref + isc::dhcp::ControlledDhcpv6Srv::establishSession()), the configuration handler + callback is installed (see isc::dhcp::ControlledDhcpv6Srv::dhcp6ConfigHandler(). + It is called every time there is a new configuration. In particular, it is + called every time during daemon start process. It contains a + isc::data::ConstElementPtr to a new configuration. This simple handler calls + \ref isc::dhcp::configureDhcp6Server() method that processes received configuration. - This method iterates over list of received configuration elements and creates - a list of parsers for each received entry. Parser is an object that is derived - from a \ref Dhcp6ConfigParser class. Once a parser is created (constructor), - its value is set (using build() method). Once all parsers are build, the - configuration is then applied ("commited") and commit() method is called. + This method iterates over list of received configuration elements and creates a + list of parsers for each received entry. Parser is an object that is derived + from a \ref isc::dhcp::Dhcp6ConfigParser class. Once a parser is created + (constructor), its value is set (using build() method). Once all parsers are + build, the configuration is then applied ("commited") and commit() method is + called. - All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some - of them are generic (e.g. \ref Uint32Parser that is able to handle - any unsigned 32 bit integer), but some are very specialized - (e.g. \ref Subnets6ListConfigParser parses definitions of Subnet6 lists). In + All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them + are generic (e.g. \ref isc::dhcp::Uint32Parser that is able to handle any + unsigned 32 bit integer), but some are very specialized (e.g. \ref + isc::dhcp::Subnets6ListConfigParser parses definitions of Subnet6 lists). In some cases, e.g. subnet6 definitions, the configuration entry is not a simple - value, but a map or a list itself. In such case, the parser iterates over - all elements and creates parsers for a given scope. This process may be - repeated (sort of) recursively. - + value, but a map or a list itself. In such case, the parser iterates over all + elements and creates parsers for a given scope. This process may be repeated + (sort of) recursively. + @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance One notable useful features of DHCP configuration is its parameter inheritance. - For example, renew-timer value may be specified at a global scope and it - then applies to all subnets. However, some subnets may have it overwritten - with more specific values that takes precedence over global values that are - considered defaults. Some parsers (e.g. \ref Uint32ConfigParser and - \StringParser) implement that inheritance. By default, they store values in - global uint32_defaults and string_defaults storages. However, it is possible - to instruct them to store parsed values in more specific storages. That - capability is used, e.g. in \ref Subnet6ConfigParser that has its own storage - that is unique for each subnet. Finally, during commit phase (commit() method), - appropriate parsers can use apply parameter inheritance. + For example, renew-timer value may be specified at a global scope and it then + applies to all subnets. However, some subnets may have it overwritten with more + specific values that takes precedence over global values that are considered + defaults. Some parsers (e.g. \ref isc::dhcp::Uint32Parser and \ref + isc::dhcp::StringParser) implement that inheritance. By default, they store + values in global uint32_defaults and string_defaults storages. However, it is + possible to instruct them to store parsed values in more specific + storages. That capability is used, e.g. in \ref isc::dhcp::Subnet6ConfigParser + that has its own storage that is unique for each subnet. Finally, during commit + phase (commit() method), appropriate parsers can use apply parameter inheritance. Debugging configuration parser may be confusing. Therefore there is a special - class called \ref DummyParser. It does not configure anything, but just accepts - any parameter of any type. If requested to commit configuration, it will print - out received parameter name and its value. This class is not currently used, + class called \ref isc::dhcp::DummyParser. It does not configure anything, but just + accepts any parameter of any type. If requested to commit configuration, it will + print out received parameter name and its value. This class is not currently used, but it is convenient to have it every time a new parameter is added to DHCP configuration. For that purpose it should be left in the code. @@ -76,5 +77,3 @@ do not implement any fancy inheritance logic. */ - - From 574a6f8834ffa1ef3faddc637257efcde4eae445 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 2 Oct 2012 17:18:49 +0200 Subject: [PATCH 015/194] [2237] Pool4 and tests implemented. --- src/lib/dhcp/addr_utilities.cc | 57 ++++++++++++++++++++-- src/lib/dhcp/pool.cc | 33 ++++++++++++- src/lib/dhcp/pool.h | 27 +++++++++++ src/lib/dhcp/tests/pool_unittest.cc | 73 +++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 4 deletions(-) diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc index ad728333d2..0396d1a0b7 100644 --- a/src/lib/dhcp/addr_utilities.cc +++ b/src/lib/dhcp/addr_utilities.cc @@ -13,17 +13,30 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include #include using namespace isc::asiolink; +const static uint32_t masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, + 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, + 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, + 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff }; + +const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + + namespace isc { namespace dhcp { -isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, +isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix, uint8_t len) { - const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; uint8_t packed[V6ADDRESS_LEN]; // First we copy the whole address as 16 bytes. @@ -55,7 +68,36 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } -isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix, +isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix, + uint8_t len) { + uint32_t addr = prefix; + if (len>32) { + isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + } + + return (IOAddress(addr & (~masks[32-len]))); +} + +isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, + uint8_t len) { + if (prefix.getFamily() == AF_INET) { + return firstAddrInPrefix4(prefix, len); + } else { + return firstAddrInPrefix6(prefix, len); + } +} + +isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix, + uint8_t len) { + uint32_t addr = prefix; + if (len>32) { + isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + } + + return (IOAddress(addr | masks[32-len])); +} + +isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix, uint8_t len) { const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; @@ -92,5 +134,14 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } +isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix, + uint8_t len) { + if (prefix.getFamily() == AF_INET) { + return lastAddrInPrefix4(prefix, len); + } else { + return lastAddrInPrefix6(prefix, len); + } +} + }; }; diff --git a/src/lib/dhcp/pool.cc b/src/lib/dhcp/pool.cc index da8a2e3389..1cf47a31a7 100644 --- a/src/lib/dhcp/pool.cc +++ b/src/lib/dhcp/pool.cc @@ -30,6 +30,38 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const { return (first_.smallerEqual(addr) && addr.smallerEqual(last_)); } +Pool4::Pool4(const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last) + :Pool(first, last) { + // check if specified address boundaries are sane + if (first.getFamily() != AF_INET || last.getFamily() != AF_INET) { + isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4"); + } + + if (last < first) { + isc_throw(BadValue, "Upper boundary is smaller than lower boundary."); + } +} + +Pool4::Pool4(const isc::asiolink::IOAddress& prefix, + uint8_t prefix_len) + :Pool(prefix, IOAddress("0.0.0.0")) { + + // check if the prefix is sane + if (prefix.getFamily() != AF_INET) { + isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4"); + } + + // check if the prefix length is sane + if (prefix_len == 0 || prefix_len > 32) { + isc_throw(BadValue, "Invalid prefix length"); + } + + // Let's now calculate the last address in defined pool + last_ = lastAddrInPrefix(prefix, prefix_len); +} + + Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, const isc::asiolink::IOAddress& last) :Pool(first, last), type_(type), prefix_len_(0) { @@ -52,7 +84,6 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first, // last_ = first; } - // TYPE_PD is not supported by this constructor. first-last style // parameters are for IA and TA only. There is another dedicated // constructor for that (it uses prefix/length) diff --git a/src/lib/dhcp/pool.h b/src/lib/dhcp/pool.h index 8f6fd8675f..46f6578d3c 100644 --- a/src/lib/dhcp/pool.h +++ b/src/lib/dhcp/pool.h @@ -91,6 +91,33 @@ protected: std::string comments_; }; +/// @brief Pool information for IPv4 addresses +/// +/// It holds information about pool4, i.e. a range of IPv4 address space that +/// is configured for DHCP allocation. +class Pool4 : public Pool { +public: + /// @brief the constructor for Pool4 "min-max" style definition + /// + /// @param first the first address in a pool + /// @param last the last address in a pool + Pool4(const isc::asiolink::IOAddress& first, + const isc::asiolink::IOAddress& last); + + /// @brief the constructor for Pool4 "prefix/len" style definition + /// + /// @param prefix specifies prefix of the pool + /// @param prefix_len specifies length of the prefix of the pool + Pool4(const isc::asiolink::IOAddress& prefix, + uint8_t prefix_len); +}; + +/// @brief a pointer an IPv4 Pool +typedef boost::shared_ptr Pool4Ptr; + +/// @brief a container for IPv4 Pools +typedef std::vector Pool4Collection; + /// @brief Pool information for IPv6 addresses and prefixes /// /// It holds information about pool6, i.e. a range of IPv6 address space that diff --git a/src/lib/dhcp/tests/pool_unittest.cc b/src/lib/dhcp/tests/pool_unittest.cc index 61d4c4acf3..63d4289794 100644 --- a/src/lib/dhcp/tests/pool_unittest.cc +++ b/src/lib/dhcp/tests/pool_unittest.cc @@ -27,6 +27,79 @@ using namespace isc::asiolink; namespace { +TEST(Pool4Test, constructor_first_last) { + + // let's construct 192.0.2.1-192.0.2.255 pool + Pool4 pool1(IOAddress("192.0.2.1"), IOAddress("192.0.2.255")); + + EXPECT_EQ(IOAddress("192.0.2.1"), pool1.getFirstAddress()); + EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress()); + + // This is Pool4, IPv6 addresses do not belong here + EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"), + IOAddress("192.168.0.5")), BadValue); + EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), + IOAddress("2001:db8::1")), BadValue); + + // Should throw. Range should be 192.0.2.1-192.0.2.2, not + // the other way around. + EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.0.2.2"), + IOAddress("192.0.2.1")), BadValue); +} + +TEST(Pool4Test, constructor_prefix_len) { + + // let's construct 2001:db8:1::/96 pool + Pool4 pool1(IOAddress("192.0.2.0"), 25); + + EXPECT_EQ("192.0.2.0", pool1.getFirstAddress().toText()); + EXPECT_EQ("192.0.2.127", pool1.getLastAddress().toText()); + + // No such thing as /33 prefix + EXPECT_THROW(Pool4(IOAddress("192.0.2.1"), 33), BadValue); + + // /0 prefix does not make sense + EXPECT_THROW(Pool4(IOAddress("192.0.2.0"), 0), BadValue); + + // This is Pool6, IPv4 addresses do not belong here + EXPECT_THROW(Pool4(IOAddress("2001:db8::1"), 20), BadValue); +} + +TEST(Pool4Test, in_range) { + Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20")); + + EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.0"))); + EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.10"))); + EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.17"))); + EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.20"))); + EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.21"))); + EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.255"))); + EXPECT_FALSE(pool1.inRange(IOAddress("255.255.255.255"))); + EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0"))); +} + +// This test creates 100 pools and verifies that their IDs are unique. +TEST(Pool4Test, unique_id) { + + const int num_pools = 100; + std::vector pools; + + for (int i = 0; i < num_pools; ++i) { + pools.push_back(Pool4Ptr(new Pool4(IOAddress("192.0.2.0"), + IOAddress("192.0.2.255")))); + } + + for (int i = 0; i < num_pools; ++i) { + for (int j = i + 1; j < num_pools; ++j) { + if (pools[i]->getId() == pools[j]->getId()) { + FAIL() << "Pool-ids must be unique"; + } + } + } + +} + + TEST(Pool6Test, constructor_first_last) { // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool From 4babe763de5b20437e32829024f25a1b902df82f Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 2 Oct 2012 20:22:23 +0200 Subject: [PATCH 016/194] [2292] Parametrize constness of the chain --- src/lib/datasrc/memory/domaintree.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 20f46936e7..4f1d94aca7 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -684,7 +684,7 @@ DomainTreeNode::predecessor() const { /// DomainTree. /// This is the reason why manipulation methods such as \c push() and \c pop() /// are private (and not shown in the doxygen document). -template +template > class DomainTreeNodeChain { /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has /// knowledge to manipulate it. @@ -817,7 +817,7 @@ private: /// root node of DomainTree /// /// \exception None - const DomainTreeNode* top() const { + NodeType* top() const { assert(!isEmpty()); return (nodes_[level_count_ - 1]); } @@ -840,7 +840,7 @@ private: /// otherwise the node should be the root node of DomainTree. /// /// \exception None - void push(const DomainTreeNode* node) { + void push(NodeType* node) { assert(level_count_ < RBT_MAX_LEVEL); nodes_[level_count_++] = node; } @@ -852,7 +852,7 @@ private: const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS; size_t level_count_; - const DomainTreeNode* nodes_[RBT_MAX_LEVEL]; + NodeType* nodes_[RBT_MAX_LEVEL]; const DomainTreeNode* last_compared_; isc::dns::NameComparisonResult last_comparison_; }; From 5c74085e2f4c16655e77e1bcbc308d4fb0a694f8 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 2 Oct 2012 21:05:33 +0200 Subject: [PATCH 017/194] [2292] Get rid of the const_cast It was needed when extracting data from a domain tree chain. The chain now can hold mutable pointers too, so we use that (and some amount of template bureaucracy) to avoid the cast. While the interface changed (on the core find function, it is not possible to pass const node chain and have a mutable node get out), it doesn't seem to influence the current code. Also, it is a private interface anyway, so it should be safe. --- src/lib/datasrc/memory/domaintree.h | 46 ++++++++++++++++----------- src/lib/datasrc/memory/zone_finder.cc | 2 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 4f1d94aca7..e54d8eaaed 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1085,9 +1085,10 @@ public: /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, DomainTreeNode** node) const { - DomainTreeNodeChain node_path; + DomainTreeNodeChain > node_path; const isc::dns::LabelSequence ls(name); - return (find(ls, node, node_path, NULL, NULL)); + return (find >(ls, node, node_path, NULL, + NULL)); } /// \brief Simple find returning immutable node. @@ -1097,9 +1098,11 @@ public: Result find(const isc::dns::Name& name, const DomainTreeNode** node) const { DomainTreeNodeChain node_path; - DomainTreeNode *target_node = NULL; + const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find(ls, &target_node, node_path, NULL, NULL)); + Result ret = (find >(ls, &target_node, + node_path, NULL, + NULL)); if (ret != NOTFOUND) { *node = target_node; } @@ -1113,7 +1116,8 @@ public: DomainTreeNodeChain& node_path) const { const isc::dns::LabelSequence ls(name); - return (find(ls, node, node_path, NULL, NULL)); + return (find >(ls, node, node_path, + NULL, NULL)); } /// \brief Simple find returning immutable node, with node_path tracking @@ -1123,9 +1127,11 @@ public: Result find(const isc::dns::Name& name, const DomainTreeNode** node, DomainTreeNodeChain& node_path) const { - DomainTreeNode *target_node = NULL; + const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find(ls, &target_node, node_path, NULL, NULL)); + Result ret = (find >(ls, &target_node, + node_path, NULL, + NULL)); if (ret != NOTFOUND) { *node = target_node; } @@ -1143,7 +1149,7 @@ public: bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { - DomainTreeNode* target_node = NULL; + const DomainTreeNode* target_node = NULL; const isc::dns::LabelSequence ls(name); Result ret = find(ls, &target_node, node_path, callback, callback_arg); @@ -1227,10 +1233,10 @@ public: /// /// \return As in the description, but in case of callback returning /// \c true, it returns immediately with the current node. - template + template Result find(const isc::dns::LabelSequence& target_labels_orig, - DomainTreeNode** node, - DomainTreeNodeChain& node_path, + NodeType** node, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const; @@ -1245,9 +1251,11 @@ public: bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { - DomainTreeNode* target_node = NULL; - Result ret = find(target_labels, &target_node, node_path, - callback, callback_arg); + const DomainTreeNode* target_node = NULL; + Result ret = find >(target_labels, + &target_node, + node_path, callback, + callback_arg); if (ret != NOTFOUND) { *node = target_node; } @@ -1512,11 +1520,11 @@ DomainTree::deleteHelper(util::MemorySegment& mem_sgmt, } template -template +template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - DomainTreeNode** target, - DomainTreeNodeChain& node_path, + NodeType** target, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { @@ -1526,11 +1534,11 @@ DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, " and label sequence"); } - DomainTreeNode* node; + NodeType* node; if (!node_path.isEmpty()) { // Get the top node in the node chain - node = const_cast*>(node_path.top()); + node = node_path.top(); // Start searching from its down pointer node = node->getDown(); } else { diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index 9403c4fec8..31a58d930e 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -417,7 +417,7 @@ FindNodeResult findNode(const ZoneData& zone_data, ZoneFinder::FindOptions options, bool out_of_zone_ok = false) { - ZoneNode* node = NULL; + const ZoneNode* node = NULL; FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0); const ZoneTree& tree(zone_data.getZoneTree()); From d5a5e815fc96116ee205256b9cba7c11752f0692 Mon Sep 17 00:00:00 2001 From: Stephen Morris Date: Wed, 3 Oct 2012 14:32:37 +0100 Subject: [PATCH 018/194] [2269] Minor documentation changes. --- doc/guide/bind10-guide.xml | 53 +++++++++++++++++--------------- src/bin/dhcp6/dhcp6.spec | 30 +++++++++--------- src/bin/dhcp6/dhcp6_messages.mes | 23 +++++++------- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 061f7f3bb9..2e79952b56 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -2679,13 +2679,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address DHCPv4 Server Configuration The DHCPv4 server does not have a lease database implemented yet - nor any support for configuration, so every time the same set + nor any support for configuration, so the same set of configuration options (including the same fixed address) will be assigned every time. At this stage of development, the only way to alter the server - configuration is to tweak its source code. To do so, please + configuration is to modify its source code. To do so, please edit src/bin/dhcp4/dhcp4_srv.cc file and modify following parameters and recompile: @@ -2872,11 +2872,11 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";
DHCPv6 Server Configuration - Once the server is started, its configuration becomes possible. To view - existing configuration, use the following command in bindctl: + Once the server is started, it can be configured. To view the + current configuration, use the following command in bindctl: > config show Dhcp6 - When starting Dhcp6 daemon for the first time, the initial configuration + When starting Dhcp6 daemon for the first time, the default configuration will be available. It will look similar to this: > config show Dhcp6 @@ -2889,47 +2889,50 @@ Dhcp6/subnet6 [] list (default) - To change one of the existing configurations, simply follow + To change one of the parameters, simply follow the usual bindctl procedure. For example, to make the leases longer, change their valid-lifetime parameter: > config set Dhcp6/valid-lifetime 7200 > config commit - Please note that most parameters specified in Dhcp6 scope are global - and apply to all defined subnets, unless they are overwritten in a more - specific scope. + Please note that most Dhcp6 parameters are of global scope + and apply to all defined subnets, unless they are overridden on a + per-subnet basis. The essential role of DHCPv6 server is address assignment. The server - has to be configured with at least a subnet and pool of dynamic - addresses that it can manage. For example, let's assume that the server - is connected to a network segment that uses 2001:db8:1::/64 - prefix. Administrator of that network decided that addresses from range + has to be configured with at least one subnet and one pool of dynamic + addresses to be managed. For example, assume that the server + is connected to a network segment that uses the 2001:db8:1::/64 + prefix. The Administrator of that network has decided that addresses from range 2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6 - server. Such configuration can be achieved in the following way: + server. Such a configuration can be achieved in the following way: > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64" > config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ] > config commit - Please note that subnet is defined as a simple string, but the pool is - an actual list of pools, therefore must be defined with square - brackets. It is possible to define more than one pool in a - subnet. Continuing the previous example, let's further assume that also - 2001:db8:1:0:5::/80 should be used. It could be written as - 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many Fs - is cumbersome. It can be expressed simply as 2001:db8:1:0:5::/80. Both - formats are supported by Dhcp6. For example, one could define the following pools: + Note that subnet is defined as a simple string, but the pool parameter + is actually a list of pools: for this reason, the pool definition is + enclosed in square brackets, even though only one range of addresses + is specified. + It is possible to define more than one pool in a + subnet: continuing the previous example, further assume that + 2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as + 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many 'f's + is cumbersome. It can be expressed more simply as 2001:db8:1:0:5::/80. Both + formats are supported by Dhcp6 and can be mixed in the pool list. + For example, one could define the following pools: > config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ] > config commit - Number of subnets is not limited, but for performance reasons it is recommended to keep - them as low as possible. + The number of pools is not limited, but for performance reasons it is recommended to + use as few as possible. The server may be configured to serve more than one subnet. To add a second subnet, - use command similar to the following: + use a command similar to the following: > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48" diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec index b61ef2a8b2..f35f606213 100644 --- a/src/bin/dhcp6/dhcp6.spec +++ b/src/bin/dhcp6/dhcp6.spec @@ -59,32 +59,32 @@ }, { "item_name": "renew-timer", - "item_type": "integer", - "item_optional": false, - "item_default": 1000 + "item_type": "integer", + "item_optional": false, + "item_default": 1000 }, { "item_name": "rebind-timer", - "item_type": "integer", - "item_optional": false, - "item_default": 2000 + "item_type": "integer", + "item_optional": false, + "item_default": 2000 }, { "item_name": "preferred-lifetime", - "item_type": "integer", - "item_optional": false, - "item_default": 3000 + "item_type": "integer", + "item_optional": false, + "item_default": 3000 }, { "item_name": "valid-lifetime", - "item_type": "integer", - "item_optional": false, - "item_default": 7200 + "item_type": "integer", + "item_optional": false, + "item_default": 7200 }, { "item_name": "pool", - "item_type": "list", - "item_optional": false, - "item_default": [], + "item_type": "list", + "item_optional": false, + "item_default": [], "list_item_spec": { "item_name": "type", diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index 56f4b1c970..a4f24d0b78 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -102,21 +102,20 @@ is running. % DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1 This critical error message indicates that the initial DHCPv6 -configuration has failed. The server will continue to run, but -administrator's intervention is required. The server's configuration -must be fixed before it can become usable. +configuration has failed. The server will start, but nothing will be +served until the configuration has been corrected. % DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1 -This is a debug message that is issued every time the server receives -configuration. That happens during every start up and also when server -configuration change is committed by the administrator. +This is a debug message that is issued every time the server receives a +configuration. That happens start up and also when a server configuration +change is committed by the administrator. % DHCP6_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1 -This is an informational message that the configuration has extended -and specified new subnet is now supported. +This is an informational message reporting that the configuration has +been extended to include the specified subnet. % DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1 -This is an informational message that announces successful processing -of a new configuration. That may happen as a result of one of two -events: server startup or commit of configuration change, done by the -administrator. Additional information may be provided. +This is an informational message announcing the successful processing of a +new configuration. it is output during server startup, and when an updated +configuration is committed by the administrator. Additional information +may be provided. From 46c03d26e27417bdce132b90eef18686443bd5a7 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 4 Oct 2012 14:43:43 +0200 Subject: [PATCH 019/194] [2237] Support for Subnet4 implemented. --- ChangeLog | 6 +++ src/lib/dhcp/cfgmgr.cc | 33 ++++++++++++ src/lib/dhcp/cfgmgr.h | 33 ++++++++++-- src/lib/dhcp/subnet.cc | 44 +++++++++++++++ src/lib/dhcp/subnet.h | 51 ++++++++++++++++++ src/lib/dhcp/tests/cfgmgr_unittest.cc | 28 +++++++++- src/lib/dhcp/tests/subnet_unittest.cc | 78 ++++++++++++++++++++++++++- 7 files changed, 267 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 53a79e1fa8..8e6d84ed82 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +4XX. [func] tomek + libdhcpsrv: The DHCP Configuration Manager is now able to store + information about IPv4 subnets and pool. It is still not possible + to configure that information. This will be implemented in a near + future. + 484. [func] tomek A new library (libb10-dhcpsrv) has been created. At present, it only holds the code for the DHCP Configuration Manager. Currently diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc index 15e3ad97d6..d06544c559 100644 --- a/src/lib/dhcp/cfgmgr.cc +++ b/src/lib/dhcp/cfgmgr.cc @@ -68,6 +68,39 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) { subnets6_.push_back(subnet); } +Subnet4Ptr +CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) { + + // If there's only one subnet configured, let's just use it + // The idea is to keep small deployments easy. In a small network - one + // router that also runs DHCPv6 server. Users specifies a single pool and + // expects it to just work. Without this, the server would complain that it + // doesn't have IP address on its interfaces that matches that + // configuration. Such requirement makes sense in IPv4, but not in IPv6. + // The server does not need to have a global address (using just link-local + // is ok for DHCPv6 server) from the pool it serves. + if (subnets4_.size() == 1) { + return (subnets4_[0]); + } + + // If there is more than one, we need to choose the proper one + for (Subnet4Collection::iterator subnet = subnets4_.begin(); + subnet != subnets4_.end(); ++subnet) { + if ((*subnet)->inRange(hint)) { + return (*subnet); + } + } + + // sorry, we don't support that subnet + return (Subnet4Ptr()); +} + +void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) { + /// @todo: Check that this new subnet does not cross boundaries of any + /// other already defined subnet. + subnets4_.push_back(subnet); +} + CfgMgr::CfgMgr() { } diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h index 5b73f2b966..2e127c088c 100644 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@ -72,7 +72,7 @@ public: /// accessing it. static CfgMgr& instance(); - /// @brief get subnet by address + /// @brief get IPv6 subnet by address /// /// Finds a matching subnet, based on an address. This can be used /// in two cases: when trying to find an appropriate lease based on @@ -83,7 +83,7 @@ public: /// @param hint an address that belongs to a searched subnet Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint); - /// @brief get subnet by interface-id + /// @brief get IPv6 subnet by interface-id /// /// Another possibility to find a subnet is based on interface-id. /// @@ -91,13 +91,30 @@ public: /// @todo This method is not currently supported. Subnet6Ptr getSubnet6(OptionPtr interface_id); - /// @brief adds a subnet6 + /// @brief adds an IPv6 subnet void addSubnet6(const Subnet6Ptr& subnet); /// @todo: Add subnet6 removal routines. Currently it is not possible /// to remove subnets. The only case where subnet6 removal would be /// needed is a dynamic server reconfiguration - a use case that is not /// planned to be supported any time soon. + + /// @brief get IPv4 subnet by address + /// + /// Finds a matching subnet, based on an address. This can be used + /// in two cases: when trying to find an appropriate lease based on + /// a) relay link address (that must be the address that is on link) + /// b) our global address on the interface the message was received on + /// (for directly connected clients) + /// + /// @param hint an address that belongs to a searched subnet + Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint); + + /// @brief adds a subnet4 + void addSubnet4(const Subnet4Ptr& subnet); + + /// @brief removes all IPv4 subnets + void removeSubnets4(); protected: /// @brief Protected constructor. @@ -111,13 +128,21 @@ protected: /// @brief virtual desctructor virtual ~CfgMgr(); - /// @brief a container for Subnet6 + /// @brief a container for IPv6 subnets. /// /// That is a simple vector of pointers. It does not make much sense to /// optimize access time (e.g. using a map), because typical search /// pattern will use calling inRange() method on each subnet until /// a match is found. Subnet6Collection subnets6_; + + /// @brief a container for IPv4 subnets. + /// + /// That is a simple vector of pointers. It does not make much sense to + /// optimize access time (e.g. using a map), because typical search + /// pattern will use calling inRange() method on each subnet until + /// a match is found. + Subnet4Collection subnets4_; }; } // namespace isc::dhcp diff --git a/src/lib/dhcp/subnet.cc b/src/lib/dhcp/subnet.cc index a999295690..d0c4cb35fe 100644 --- a/src/lib/dhcp/subnet.cc +++ b/src/lib/dhcp/subnet.cc @@ -41,6 +41,50 @@ bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const { return ((first <= addr) && (addr <= last)); } +Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length, + const Triplet& t1, + const Triplet& t2, + const Triplet& valid_lifetime) + :Subnet(prefix, length, t1, t2, valid_lifetime) { + if (prefix.getFamily() != AF_INET) { + isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText() + << " specified in subnet4"); + } +} + +void Subnet4::addPool4(const Pool4Ptr& pool) { + IOAddress first_addr = pool->getFirstAddress(); + IOAddress last_addr = pool->getLastAddress(); + + if (!inRange(first_addr) || !inRange(last_addr)) { + isc_throw(BadValue, "Pool4 (" << first_addr.toText() << "-" << last_addr.toText() + << " does not belong in this (" << prefix_ << "/" << prefix_len_ + << ") subnet4"); + } + + /// @todo: Check that pools do not overlap + + pools_.push_back(pool); +} + +Pool4Ptr Subnet4::getPool4(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) { + Pool4Ptr candidate; + for (Pool4Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) { + + // if we won't find anything better, then let's just use the first pool + if (!candidate) { + candidate = *pool; + } + + // if the client provided a pool and there's a pool that hint is valid in, + // then let's use that pool + if ((*pool)->inRange(hint)) { + return (*pool); + } + } + return (candidate); +} + Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length, const Triplet& t1, const Triplet& t2, diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h index aa59010ee6..7e4e0b7f07 100644 --- a/src/lib/dhcp/subnet.h +++ b/src/lib/dhcp/subnet.h @@ -93,6 +93,57 @@ protected: Triplet valid_; }; +/// @brief A configuration holder for IPv4 subnet. +/// +/// This class represents an IPv4 subnet. +class Subnet4 : public Subnet { +public: + + /// @brief Constructor with all parameters + /// + /// @param prefix Subnet4 prefix + /// @param length prefix length + /// @param t1 renewal timer (in seconds) + /// @param t2 rebind timer (in seconds) + /// @param valid_lifetime preferred lifetime of leases (in seconds) + Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length, + const Triplet& t1, + const Triplet& t2, + const Triplet& valid_lifetime); + + /// @brief Returns a pool that specified address belongs to + /// + /// @param hint address that the returned pool should cover (optional) + /// @return Pointer to found pool4 (or NULL) + Pool4Ptr getPool4(const isc::asiolink::IOAddress& hint = + isc::asiolink::IOAddress("0.0.0.0")); + + /// @brief Adds a new pool. + /// @param pool pool to be added + void addPool4(const Pool4Ptr& pool); + + /// @brief returns all pools + /// + /// The reference is only valid as long as the object that + /// returned it. + /// + /// @return a collection of all pools + const Pool4Collection& getPools() const { + return pools_; + } + +protected: + /// @brief collection of pools in that list + Pool4Collection pools_; +}; + +/// @brief A pointer to a Subnet4 object +typedef boost::shared_ptr Subnet4Ptr; + +/// @brief A collection of Subnet6 objects +typedef std::vector Subnet4Collection; + + /// @brief A configuration holder for IPv6 subnet. /// /// This class represents an IPv6 subnet. diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc index a11acbfdd7..75e60010aa 100644 --- a/src/lib/dhcp/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc @@ -32,6 +32,33 @@ using boost::scoped_ptr; namespace { +// This test verifies if the configuration manager is able to hold and return +// valid leases +TEST(CfgMgrTest, subnet4) { + CfgMgr& cfg_mgr = CfgMgr::instance(); + + ASSERT_TRUE(&cfg_mgr != 0); + + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3)); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3)); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3)); + + // there shouldn't be any subnet configured at this stage + EXPECT_EQ( Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.0"))); + + cfg_mgr.addSubnet4(subnet1); + + // Now we have only one subnet, any request will be served from it + EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63"))); + + cfg_mgr.addSubnet4(subnet2); + cfg_mgr.addSubnet4(subnet3); + + EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191"))); + EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85"))); + EXPECT_EQ(Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.192"))); +} + // This test verifies if the configuration manager is able to hold and return // valid leases TEST(CfgMgrTest, subnet6) { @@ -57,7 +84,6 @@ TEST(CfgMgrTest, subnet6) { EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123"))); EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef"))); EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1"))); - } } // end of anonymous namespace diff --git a/src/lib/dhcp/tests/subnet_unittest.cc b/src/lib/dhcp/tests/subnet_unittest.cc index 6afebb8e60..34982b595d 100644 --- a/src/lib/dhcp/tests/subnet_unittest.cc +++ b/src/lib/dhcp/tests/subnet_unittest.cc @@ -29,6 +29,83 @@ using namespace isc::asiolink; namespace { +TEST(Subnet4Test, constructor) { + EXPECT_NO_THROW(Subnet4 subnet1(IOAddress("192.0.2.2"), 16, + 1, 2, 3)); + + EXPECT_THROW(Subnet4 subnet2(IOAddress("192.0.2.0"), 33, 1, 2, 3), + BadValue); // invalid prefix length + EXPECT_THROW(Subnet4 subnet3(IOAddress("2001:db8::1"), 24, 1, 2, 3), + BadValue); // IPv6 addresses are not allowed in Subnet4 +} + +TEST(Subnet4Test, in_range) { + Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000); + + EXPECT_EQ(1000, subnet.getT1()); + EXPECT_EQ(2000, subnet.getT2()); + EXPECT_EQ(3000, subnet.getValid()); + + EXPECT_FALSE(subnet.inRange(IOAddress("192.0.0.0"))); + EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.0"))); + EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.1"))); + EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.255"))); + EXPECT_FALSE(subnet.inRange(IOAddress("192.0.3.0"))); + EXPECT_FALSE(subnet.inRange(IOAddress("0.0.0.0"))); + EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255"))); +} + +TEST(Subnet4Test, Pool4InSubnet4) { + + Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3)); + + Pool4Ptr pool1(new Pool4(IOAddress("192.1.2.0"), 25)); + Pool4Ptr pool2(new Pool4(IOAddress("192.1.2.128"), 26)); + Pool4Ptr pool3(new Pool4(IOAddress("192.1.2.192"), 30)); + + subnet->addPool4(pool1); + + // If there's only one pool, get that pool + Pool4Ptr mypool = subnet->getPool4(); + EXPECT_EQ(mypool, pool1); + + + subnet->addPool4(pool2); + subnet->addPool4(pool3); + + // If there are more than one pool and we didn't provide hint, we + // should get the first pool + mypool = subnet->getPool4(); + + EXPECT_EQ(mypool, pool1); + + // If we provide a hint, we should get a pool that this hint belongs to + mypool = subnet->getPool4(IOAddress("192.1.2.195")); + + EXPECT_EQ(mypool, pool3); + +} + +TEST(Subnet4Test, Subnet4_Pool4_checks) { + + Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3)); + + // this one is in subnet + Pool4Ptr pool1(new Pool4(IOAddress("192.255.0.0"), 16)); + subnet->addPool4(pool1); + + // this one is larger than the subnet! + Pool4Ptr pool2(new Pool4(IOAddress("193.0.0.0"), 24)); + + EXPECT_THROW(subnet->addPool4(pool2), BadValue); + + // this one is totally out of blue + Pool4Ptr pool3(new Pool4(IOAddress("1.2.3.4"), 16)); + EXPECT_THROW(subnet->addPool4(pool3), BadValue); +} + +// Tests for Subnet6 + TEST(Subnet6Test, constructor) { EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64, @@ -48,7 +125,6 @@ TEST(Subnet6Test, in_range) { EXPECT_EQ(3000, subnet.getPreferred()); EXPECT_EQ(4000, subnet.getValid()); - EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff"))); EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0"))); EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1"))); From a1d9278fd64f74a4a0657a2292851157ad3bb43a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:27:15 -0700 Subject: [PATCH 020/194] [2244] added a test case that reproduces one aspect of the problem. the configurator should be able to delete a component to be removed even if the it's failing (unexpectedly). The current implementation doesn't handle that condition correctly. --- .../python/isc/bind10/tests/component_test.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 3f26870452..db7707e21b 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -872,12 +872,13 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): 'priority': 6, 'special': 'test', 'process': 'additional', - 'kind': 'needed' + 'kind': 'dispensable' # need to be dispensable so it could restart } self.log = [] plan = configurator._build_plan(self.__build_components(self.__core), bigger) - self.assertEqual([('additional', 'init'), ('additional', 'needed')], + self.assertEqual([('additional', 'init'), + ('additional', 'dispensable')], self.log) self.assertEqual(1, len(plan)) self.assertTrue('component' in plan[0]) @@ -888,15 +889,27 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): # Now remove the one component again # We run the plan so the component is wired into internal structures configurator._run_plan(plan) - self.log = [] - plan = configurator._build_plan(self.__build_components(bigger), - self.__core) - self.assertEqual([], self.log) - self.assertEqual([{ - 'command': 'stop', - 'name': 'additional', - 'component': component - }], plan) + # We should have the 'additional' component in the configurator. + self.assertTrue('additional' in configurator._components) + for count in [0, 1]: # repeat two times, see below + self.log = [] + plan = configurator._build_plan(self.__build_components(bigger), + self.__core) + self.assertEqual([], self.log) + self.assertEqual([{ + 'command': 'stop', + 'name': 'additional', + 'component': component + }], plan) + + if count is 0: + # We then emulate unexpected failure of the component (but + # before it restarts). It shouldn't confuse the + # configurator in the second phase of the test + component.failed(0) + # Run the plan, confirm the specified component is gone. + configurator._run_plan(plan) + self.assertFalse('additional' in configurator._components) # We want to switch a component. So, prepare the configurator so it # holds one From bff3846230660040790debeacf8d9432ac302fb9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:37:09 -0700 Subject: [PATCH 021/194] [2244] introduced a new "Failed" state of Component. --- src/lib/python/isc/bind10/component.py | 17 ++++++++++++++--- .../python/isc/bind10/tests/component_test.py | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 9c29ace589..77196b02e9 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -45,6 +45,7 @@ COMPONENT_RESTART_DELAY = 10 STATE_DEAD = 'dead' STATE_STOPPED = 'stopped' +STATE_FAILED = 'failed' STATE_RUNNING = 'running' def get_signame(signal_number): @@ -68,6 +69,7 @@ class BaseComponent: explicitly). - Running - after start() was called, it started successfully and is now running. + - Failed - the component failed (crashed) and is waiting for a restart - Dead - it failed and can not be resurrected. Init @@ -79,11 +81,11 @@ class BaseComponent: | | | |failure | failed() | | | | - v | | + v | | start()/restart() +<-----------+ | | | | kind == dispensable or kind|== needed and failed late - +-----------------------------+ + +-----------------------> Failed | | kind == core or kind == needed and it failed too soon v @@ -236,7 +238,7 @@ class BaseComponent: exit_str) if not self.running(): raise ValueError("Can't fail component that isn't running") - self.__state = STATE_STOPPED + self.__state = STATE_FAILED self._failed_internal() # If it is a core component or the needed component failed to start # (including it stopped really soon) @@ -296,6 +298,15 @@ class BaseComponent: """ return self.__state == STATE_RUNNING + def is_failed(self): + """Informs if the component has failed and is waiting for a restart. + + Unlike the case of running(), if this returns True it always means + the corresponding process has died and not yet restarted. + + """ + return self.__state == STATE_FAILED + def _start_internal(self): """ This method does the actual starting of a process. You need to override diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index db7707e21b..1cfc30f8b4 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -192,6 +192,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) self.assertRaises(ValueError, component.failed, 1) @@ -205,6 +206,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertTrue(component.running()) + self.assertFalse(component.is_failed()) def __check_dead(self, component): """ @@ -216,6 +218,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) # Nor started @@ -235,6 +238,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertTrue(component.running()) + self.assertFalse(component.is_failed()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -247,6 +251,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertFalse(component.running()) + self.assertTrue(component.is_failed()) def __do_start_stop(self, kind): """ @@ -271,6 +276,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) # Or failed From 3584fbbffa6a633df2988ef8e52a4f367cdc814c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:38:43 -0700 Subject: [PATCH 022/194] [2244] make sure configurtor's build_plan() handles 'failed' component. this fixes the test case introduced at the beginning of the branch. --- src/lib/python/isc/bind10/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 77196b02e9..0b822a83e2 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -602,7 +602,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.running(): + if component.running() or component.is_failed(): plan.append({ 'command': STOP_CMD, 'component': component, From 0ff7d113ebb54889bbcd9be91c37a0e5ffcc2fc4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:55:57 -0700 Subject: [PATCH 023/194] [2244] added a simple has_component() method to configurtor. should be pretty trivial, so added a couple of test cases as part of other tests. --- src/lib/python/isc/bind10/component.py | 4 ++++ src/lib/python/isc/bind10/tests/component_test.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 0b822a83e2..57f9428586 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -571,6 +571,10 @@ class Configurator: self._running = False self.__reconfigure_internal(self._components, {}) + def has_component(self, component): + '''Return if a specified component is configured.''' + return component in map(lambda x: x[1], self._components.values()) + def reconfigure(self, configuration): """ Changes configuration from the current one to the provided. It diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 1cfc30f8b4..d8a89d578a 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -896,7 +896,7 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): # We run the plan so the component is wired into internal structures configurator._run_plan(plan) # We should have the 'additional' component in the configurator. - self.assertTrue('additional' in configurator._components) + self.assertTrue(configurator.has_component(component)) for count in [0, 1]: # repeat two times, see below self.log = [] plan = configurator._build_plan(self.__build_components(bigger), @@ -915,6 +915,8 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): component.failed(0) # Run the plan, confirm the specified component is gone. configurator._run_plan(plan) + self.assertFalse(configurator.has_component(component)) + # There shouldn't be any other object that has the same name self.assertFalse('additional' in configurator._components) # We want to switch a component. So, prepare the configurator so it From 17bffd083824af840b8af99a043bcf55e4c0f555 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 20:53:37 -0700 Subject: [PATCH 024/194] [2244] added comment about has_comonent as it may not be very obvious. --- src/lib/python/isc/bind10/component.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 57f9428586..aad3e694df 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -573,6 +573,9 @@ class Configurator: def has_component(self, component): '''Return if a specified component is configured.''' + # Values of self._components are tuples of (name, component). + # Extract the components of the tuples and see if the given one + # is included. return component in map(lambda x: x[1], self._components.values()) def reconfigure(self, configuration): From fa1d1161f76487ceea122baf4dac79c407a07ba8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 21:08:55 -0700 Subject: [PATCH 025/194] [2244] a minor editorial fix to the previous comment update --- src/lib/python/isc/bind10/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index aad3e694df..fbb9e0e1bb 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -573,7 +573,7 @@ class Configurator: def has_component(self, component): '''Return if a specified component is configured.''' - # Values of self._components are tuples of (name, component). + # Values of self._components are tuples of (config, component). # Extract the components of the tuples and see if the given one # is included. return component in map(lambda x: x[1], self._components.values()) From 74cc637858d0879f33f1828aabbb1f78e9636d4f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:10:40 -0700 Subject: [PATCH 026/194] [2244] make sure restarting components only when they are in config. this should fix the main problem of this ticket. --- src/bin/bind10/bind10_messages.mes | 10 ++++++++ src/bin/bind10/bind10_src.py.in | 7 +++++- src/bin/bind10/tests/bind10_test.py.in | 35 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index c7515833e8..d18df9e926 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -295,3 +295,13 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. + +% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 +The boss module tried to restart a component after it failed (crashed) +unexpectedly, but the boss then found that the component had been removed +from its local configuration of components to run. This is an unusal +situation but can happen if the administrator removes the component from +the configuration after the component's crash and before the restart time. +The boss module simply skipped restarting that module, and the whole syste +went back to the expected state (except that the crash itself is likely +to be a bug). diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index bb2edaffaa..4567229ece 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -771,7 +771,12 @@ class BoB: next_restart_time = None now = time.time() for component in self.components_to_restart: - if not component.restart(now): + # If the component was removed from the configurator between since + # scheduled to restart, just ignore it. The object will just be + # dropped here. + if not self._component_configurator.has_component(component): + logger.info(BIND10_RESTART_COMPONENT_SKIPPED, component.name()) + elif not component.restart(now): still_dead.append(component) if next_restart_time is None or\ next_restart_time > component.get_restart_time(): diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 9a40e42a84..0b119606e0 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -929,7 +929,14 @@ class MockComponent: self.name = lambda: name self.pid = lambda: pid self.address = lambda: address + self.restarted = False + def get_restart_time(self): + return 0 # arbitrary dummy value + + def restart(self, now): + self.restarted = True + return True class TestBossCmd(unittest.TestCase): def test_ping(self): @@ -1266,6 +1273,34 @@ class TestBossComponents(unittest.TestCase): bob.start_all_components() self.__check_extended(self.__param) + def __setup_restart(self, bob, component): + '''Common procedure for restarting a component used below.''' + bob.components_to_restart = { component } + component.restarted = False + bob.restart_processes() + + def test_restart_processes(self): + '''Check some behavior on restarting processes.''' + bob = MockBob() + bob.runnable = True + component = MockComponent('test', 53) + + # A component to be restarted will actually be restarted iff it's + # in the configurator's configuration. + # We bruteforce the configurator internal below; ugly, but the easiest + # way for the test. + bob._component_configurator._components['test'] = (None, component) + self.__setup_restart(bob, component) + self.assertTrue(component.restarted) + self.assertFalse(component in bob.components_to_restart) + + # Remove the component from the configuration. It won't be restarted + # even if scheduled, nor will remain in the to-be-restarted list. + del bob._component_configurator._components['test'] + self.__setup_restart(bob, component) + self.assertFalse(component.restarted) + self.assertFalse(component in bob.components_to_restart) + class SocketSrvTest(unittest.TestCase): """ This tests some methods of boss related to the unix domain sockets used From f8a03432904899e1874f2696ff036322be539022 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:11:45 -0700 Subject: [PATCH 027/194] [2244] cleanup: reordered log message file --- src/bin/bind10/bind10_messages.mes | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index d18df9e926..dd36ef96e3 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -141,6 +141,16 @@ it now. The new configuration is printed. % BIND10_RECEIVED_SIGNAL received signal %1 The boss module received the given signal. +% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 +The boss module tried to restart a component after it failed (crashed) +unexpectedly, but the boss then found that the component had been removed +from its local configuration of components to run. This is an unusal +situation but can happen if the administrator removes the component from +the configuration after the component's crash and before the restart time. +The boss module simply skipped restarting that module, and the whole syste +went back to the expected state (except that the crash itself is likely +to be a bug). + % BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2) The given process has been restarted successfully, and is now running with the given process id. @@ -295,13 +305,3 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. - -% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 -The boss module tried to restart a component after it failed (crashed) -unexpectedly, but the boss then found that the component had been removed -from its local configuration of components to run. This is an unusal -situation but can happen if the administrator removes the component from -the configuration after the component's crash and before the restart time. -The boss module simply skipped restarting that module, and the whole syste -went back to the expected state (except that the crash itself is likely -to be a bug). From 7a40926af1fd90e0fd685a8db05795f699560245 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:19:27 -0700 Subject: [PATCH 028/194] [2244] rename Componet.running() is_running() for consistency. this branch introduced is_failed() (and failed() was already defined for a different a purpose), so for consistency it would be better to name the running version is_xxx too. --- src/lib/python/isc/bind10/component.py | 14 ++++++------ .../python/isc/bind10/tests/component_test.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index fbb9e0e1bb..6b96b943e9 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -165,7 +165,7 @@ class BaseComponent: """ if self.__state == STATE_DEAD: raise ValueError("Can't resurrect already dead component") - if self.running(): + if self.is_running(): raise ValueError("Can't start already running component") logger.info(BIND10_COMPONENT_START, self.name()) self.__state = STATE_RUNNING @@ -190,7 +190,7 @@ class BaseComponent: """ # This is not tested. It talks with the outher world, which is out # of scope of unittests. - if not self.running(): + if not self.is_running(): raise ValueError("Can't stop a component which is not running") logger.info(BIND10_COMPONENT_STOP, self.name()) self.__state = STATE_STOPPED @@ -236,7 +236,7 @@ class BaseComponent: logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(), exit_str) - if not self.running(): + if not self.is_running(): raise ValueError("Can't fail component that isn't running") self.__state = STATE_FAILED self._failed_internal() @@ -286,7 +286,7 @@ class BaseComponent: else: return False - def running(self): + def is_running(self): """ Informs if the component is currently running. It assumes the failed is called whenever the component really fails and there might be some @@ -301,7 +301,7 @@ class BaseComponent: def is_failed(self): """Informs if the component has failed and is waiting for a restart. - Unlike the case of running(), if this returns True it always means + Unlike the case of is_running(), if this returns True it always means the corresponding process has died and not yet restarted. """ @@ -609,7 +609,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.running() or component.is_failed(): + if component.is_running() or component.is_failed(): plan.append({ 'command': STOP_CMD, 'component': component, @@ -692,7 +692,7 @@ class Configurator: self._components[task['name']] = (task['config'], component) elif command == STOP_CMD: - if component.running(): + if component.is_running(): component.stop() del self._components[task['name']] else: diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index d8a89d578a..339a9399ca 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -191,7 +191,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__start_called) self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) @@ -205,7 +205,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertFalse(component.is_failed()) def __check_dead(self, component): @@ -217,7 +217,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) @@ -237,7 +237,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertFalse(component.is_failed()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -250,7 +250,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertTrue(component.is_failed()) def __do_start_stop(self, kind): @@ -275,7 +275,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) @@ -559,10 +559,10 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertIsNone(component.pid()) self.assertEqual(['hello'], component._params) self.assertEqual('Address', component._address) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertEqual({}, self.__registered_processes) component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) # Some versions of unittest miss assertIsInstance self.assertTrue(isinstance(component._procinfo, TestProcInfo)) self.assertEqual(42, component.pid()) @@ -586,11 +586,11 @@ class ComponentTests(BossUtils, unittest.TestCase): """ component = Component('component', self, 'needed', 'Address') component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertEqual('component', self.__start_simple_params) component.pid = lambda: 42 component.stop() - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertEqual(('component', 'Address', 42), self.__stop_process_params) @@ -615,7 +615,7 @@ class ComponentTests(BossUtils, unittest.TestCase): component = Component('component', self, 'needed', 'Address', [], ProcInfo) component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) component.kill() self.assertTrue(process.terminated) self.assertFalse(process.killed) From a0a5e207d4a05738f278df6124172f415a43d4ad Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 14:03:52 -0700 Subject: [PATCH 029/194] [2204] changed internal representation of auth_srv's "client_lists_" to ptrs. this will be necessary later in this branch. also renamed the member variable "datasrc_client_lists_" as the mere "client" can be ambiguous. --- src/bin/auth/auth_srv.cc | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index b870ae2cb9..880a4ac1e8 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -69,6 +69,8 @@ using namespace std; +using boost::shared_ptr; + using namespace isc; using namespace isc::cc; using namespace isc::datasrc; @@ -264,23 +266,21 @@ public: AddressList listen_addresses_; /// The TSIG keyring - const boost::shared_ptr* keyring_; + const shared_ptr* keyring_; - /// The client list - std::map > - client_lists_; + /// The data source client list + shared_ptr > > + datasrc_client_lists_; - boost::shared_ptr getClientList(const RRClass& - rrclass) - { + shared_ptr getClientList(const RRClass& rrclass) { // TODO: Debug-build only check if (!mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked!"); } - const std::map >:: - const_iterator it(client_lists_.find(rrclass)); - if (it == client_lists_.end()) { - return (boost::shared_ptr()); + const std::map >:: + const_iterator it(datasrc_client_lists_->find(rrclass)); + if (it == datasrc_client_lists_->end()) { + return (shared_ptr()); } else { return (it->second); } @@ -335,6 +335,8 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client, xfrin_session_(NULL), counters_(), keyring_(NULL), + datasrc_client_lists_(new std::map >()), ddns_base_forwarder_(ddns_forwarder), ddns_forwarder_(NULL), xfrout_connected_(false), @@ -650,7 +652,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message, try { const ConstQuestionPtr question = *message.beginQuestion(); - const boost::shared_ptr + const shared_ptr list(getClientList(question->getClass())); if (list) { const RRType& qtype = question->getType(); @@ -911,7 +913,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) { } void -AuthSrv::setTSIGKeyRing(const boost::shared_ptr* keyring) { +AuthSrv::setTSIGKeyRing(const shared_ptr* keyring) { impl_->keyring_ = keyring; } @@ -932,19 +934,21 @@ AuthSrv::destroyDDNSForwarder() { void AuthSrv::setClientList(const RRClass& rrclass, - const boost::shared_ptr& list) { + const shared_ptr& list) +{ // TODO: Debug-build only check if (!impl_->mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked"); } if (list) { - impl_->client_lists_[rrclass] = list; + (*impl_->datasrc_client_lists_)[rrclass] = list; } else { - impl_->client_lists_.erase(rrclass); + impl_->datasrc_client_lists_->erase(rrclass); } } -boost::shared_ptr + +shared_ptr AuthSrv::getClientList(const RRClass& rrclass) { return (impl_->getClientList(rrclass)); } @@ -957,9 +961,9 @@ AuthSrv::getClientListClasses() const { } vector result; - for (std::map >:: - const_iterator it(impl_->client_lists_.begin()); - it != impl_->client_lists_.end(); ++it) { + for (std::map >:: + const_iterator it(impl_->datasrc_client_lists_->begin()); + it != impl_->datasrc_client_lists_->end(); ++it) { result.push_back(it->first); } return (result); From d664fca786643e2345d9722da6d8fd35996bbea1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 15:15:18 -0700 Subject: [PATCH 030/194] [2204] simplify configureDataSource by always creating a new lists and swap. so we don't have to worry about what are in the current lists or rollback operations. swapDataSrcClientLists() is newly introduced for AuthSrv. No direc tests yet (technically bad in terms TDD but the definition is very simple), which will be provided in the next step. the lock is now moved inside swapDataSrcClientLists(). note: even though this version builds everything, the amount of work should be mostly the same because the only save is to create the empty ClientList when the new and old have the same class of client. The expensive part is ClientList::configure(). This version doesn't need any more call to configure() than the old version. --- src/bin/auth/auth_srv.cc | 13 +++- src/bin/auth/auth_srv.h | 18 ++++- src/bin/auth/datasrc_config.h | 76 +++++-------------- src/bin/auth/tests/command_unittest.cc | 2 + src/bin/auth/tests/datasrc_config_unittest.cc | 68 ++++++++--------- 5 files changed, 78 insertions(+), 99 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 880a4ac1e8..391416b82e 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -269,8 +269,7 @@ public: const shared_ptr* keyring_; /// The data source client list - shared_ptr > > - datasrc_client_lists_; + AuthSrv::DataSrcClientListsPtr datasrc_client_lists_; shared_ptr getClientList(const RRClass& rrclass) { // TODO: Debug-build only check @@ -948,6 +947,16 @@ AuthSrv::setClientList(const RRClass& rrclass, } } +AuthSrv::DataSrcClientListsPtr +AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { + { + thread::Mutex::Locker locker(impl_->mutex_); + std::swap(new_lists, impl_->datasrc_client_lists_); + } + + return (new_lists); +} + shared_ptr AuthSrv::getClientList(const RRClass& rrclass) { return (impl_->getClientList(rrclass)); diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index ee7bd52bb2..48aac067b9 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -15,10 +15,11 @@ #ifndef __AUTH_SRV_H #define __AUTH_SRV_H 1 -#include - #include + #include +#include + #include #include #include @@ -35,6 +36,11 @@ #include #include +#include + +#include +#include + namespace isc { namespace util { namespace io { @@ -309,6 +315,14 @@ public: boost::shared_ptr& list); + typedef boost::shared_ptr > > + DataSrcClientListsPtr; + + DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr + new_lists); + /// \brief Returns the currently used client list for the class. /// /// \param rrclass The class for which to get the list. diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 79ace2811a..02354ffa26 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -19,10 +19,10 @@ #include #include -#include #include +#include #include /// \brief Configure the authoritative server's data source lists @@ -45,67 +45,25 @@ configureDataSourceGeneric(Server& server, { typedef boost::shared_ptr ListPtr; typedef std::map Map; - typedef std::pair RollbackPair; - typedef std::pair - RollbackConfiguration; + typedef std::map ListMap; - // Lock the client lists, we're going to manipulate them. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + boost::shared_ptr new_lists(new ListMap); - // Some structures to be able to perform a rollback - std::vector rollback_sets; - std::vector rollback_configurations; - try { - // Get the configuration and current state. - const Map& map(config->mapValue()); - const std::vector - activeVector(server.getClientListClasses()); - std::set active(activeVector.begin(), - activeVector.end()); - // Go through the configuration and change everything. - for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { - const isc::dns::RRClass rrclass(it->first); - active.erase(rrclass); - ListPtr list(server.getClientList(rrclass)); - bool need_set(false); - if (list) { - rollback_configurations. - push_back(RollbackConfiguration(rrclass, - list->getConfiguration())); - } else { - list.reset(new List(rrclass)); - need_set = true; - rollback_sets.push_back(RollbackPair(rrclass, ListPtr())); - } - list->configure(it->second, true); - if (need_set) { - server.setClientList(rrclass, list); - } - } - // Remove the ones that are not in the configuration. - for (std::set::iterator it(active.begin()); - it != active.end(); ++it) { - // There seems to be no way the setClientList could throw. - // But this is just to make sure in case it did to restore - // the original. - rollback_sets.push_back( - RollbackPair(*it, server.getClientList(*it))); - server.setClientList(*it, ListPtr()); - } - } catch (...) { - // Perform a rollback of the changes. The old configuration should - // work. - for (typename std::vector::const_iterator - it(rollback_sets.begin()); it != rollback_sets.end(); ++it) { - server.setClientList(it->first, it->second); - } - for (typename std::vector::const_iterator - it(rollback_configurations.begin()); - it != rollback_configurations.end(); ++it) { - server.getClientList(it->first)->configure(it->second, true); - } - throw; + // Get the configuration and current state. + const Map& map(config->mapValue()); + + // Go through the configuration and create corresponding list. + for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { + const isc::dns::RRClass rrclass(it->first); + ListPtr list(new List(rrclass)); + list->configure(it->second, true); + new_lists->insert(std::pair(rrclass, + list)); } + + // Replace the server's lists. By ignoring the return value we let the + // old lists be destroyed. + server.swapDataSrcClientLists(new_lists); } /// \brief Concrete version of configureDataSource() for the diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index c7994831a5..b5e43edda2 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -16,6 +16,8 @@ #include "datasrc_util.h" +#include + #include #include #include diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 3d3aa58b91..c0c9b5ce99 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -81,27 +81,27 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: - // These pretend to be the server - ListPtr getClientList(const RRClass& rrclass) { - log_ += "get " + rrclass.toText() + "\n"; - return (lists_[rrclass]); - } - void setClientList(const RRClass& rrclass, const ListPtr& list) { - log_ += "set " + rrclass.toText() + " " + - (list ? list->getConf() : "") + "\n"; - lists_[rrclass] = list; - } - vector getClientListClasses() const { - vector result; - for (std::map::const_iterator it(lists_.begin()); - it != lists_.end(); ++it) { - result.push_back(it->first); + // To pretend to be the server: + void swapDataSrcClientLists(shared_ptr > + new_lists) + { + lists_.clear(); // first empty it + + // Record the operation and results. Note that map elements are + // sorted by RRClass, so the ordering should be predictable. + for (std::map::const_iterator it = + new_lists->begin(); + it != new_lists->end(); + ++it) + { + const RRClass rrclass = it->first; + ListPtr list = it->second; + log_ += "set " + rrclass.toText() + " " + + (list ? list->getConf() : "") + "\n"; + lists_[rrclass] = list; } - return (result); - } - isc::util::thread::Mutex& getClientListMutex() const { - return (mutex_); } + protected: DatasrcConfigTest() : session(ElementPtr(new ListElement), ElementPtr(new ListElement), @@ -147,9 +147,8 @@ protected: session.addMessage(createCommand("config_update", config), "data_sources", "*"); mccs->checkCommand(); - // Check it called the correct things (check that there's no IN yet and - // set a new one. - EXPECT_EQ("get IN\nset IN xxx\n", log_); + // Check that the passed config is stored. + EXPECT_EQ("set IN xxx\n", log_); EXPECT_EQ(1, lists_.size()); } FakeSession session; @@ -166,8 +165,10 @@ TEST_F(DatasrcConfigTest, createList) { } TEST_F(DatasrcConfigTest, modifyList) { - // First, initialize the list + // First, initialize the list, and confirm the current config initializeINList(); + EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf()); + // And now change the configuration of the list const ElementPtr config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}")); @@ -175,9 +176,7 @@ TEST_F(DatasrcConfigTest, modifyList) { "*"); log_ = ""; mccs->checkCommand(); - // This one does not set - EXPECT_EQ("get IN\n", log_); - // But this should contain the yyy configuration + // Now the new one should be installed. EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ(1, lists_.size()); } @@ -191,7 +190,7 @@ TEST_F(DatasrcConfigTest, multiple) { "*"); mccs->checkCommand(); // We have set commands for both classes. - EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_); + EXPECT_EQ("set IN yyy\nset CH xxx\n", log_); // We should have both there EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); @@ -212,9 +211,7 @@ TEST_F(DatasrcConfigTest, updateAdd) { "*"); log_ = ""; mccs->checkCommand(); - // The CH is set, IN not - EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_); - // But this should contain the yyy configuration + EXPECT_EQ("set IN yyy\nset CH xxx\n", log_); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ(2, lists_.size()); @@ -229,12 +226,11 @@ TEST_F(DatasrcConfigTest, updateDelete) { "*"); log_ = ""; mccs->checkCommand(); - EXPECT_EQ("get IN\nset IN \n", log_); - EXPECT_FALSE(lists_[RRClass::IN()]); - // In real auth server, the NULL one would be removed. However, we just - // store it, so the IN bucket is still in there. This checks there's nothing - // else. - EXPECT_EQ(1, lists_.size()); + + // No operation takes place in the configuration, and the old one is + // just dropped + EXPECT_EQ("", log_); + EXPECT_TRUE(lists_.empty()); } // Check that we can rollback an addition if something else fails From c286e0dec766c3d9b2fff6a8bd55539ec976aae9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:02:01 -0700 Subject: [PATCH 031/194] [2204] completely replaced setClientList with swapDataSrcClientLists. the test cases using setClientList were updated so they use swapDataSrcClientLists (some of them work as a test for the "swap" itself). now we don't need setClientList, so it was removed. --- src/bin/auth/auth_srv.cc | 24 ++------ src/bin/auth/auth_srv.h | 38 ++++++++---- src/bin/auth/datasrc_config.h | 10 +++- src/bin/auth/tests/auth_srv_unittest.cc | 59 +++++++++++-------- src/bin/auth/tests/datasrc_config_unittest.cc | 5 +- 5 files changed, 76 insertions(+), 60 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 391416b82e..1102ae3ed3 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -931,29 +931,13 @@ AuthSrv::destroyDDNSForwarder() { } } -void -AuthSrv::setClientList(const RRClass& rrclass, - const shared_ptr& list) -{ - // TODO: Debug-build only check - if (!impl_->mutex_.locked()) { - isc_throw(isc::Unexpected, "Not locked"); - } - - if (list) { - (*impl_->datasrc_client_lists_)[rrclass] = list; - } else { - impl_->datasrc_client_lists_->erase(rrclass); - } -} - AuthSrv::DataSrcClientListsPtr AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { - { - thread::Mutex::Locker locker(impl_->mutex_); - std::swap(new_lists, impl_->datasrc_client_lists_); + // TODO: Debug-build only check + if (!impl_->mutex_.locked()) { + isc_throw(isc::Unexpected, "Not locked!"); } - + std::swap(new_lists, impl_->datasrc_client_lists_); return (new_lists); } diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index 48aac067b9..f1e73c1158 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -302,24 +302,36 @@ public: /// If there was no forwarder yet, this method does nothing. void destroyDDNSForwarder(); - /// \brief Sets the currently used list for data sources of given - /// class. - /// - /// Replaces the internally used client list with a new one. Other - /// classes are not changed. - /// - /// \param rrclass The class to modify. - /// \param list Shared pointer to the client list. If it is NULL, - /// the list is removed instead. - void setClientList(const isc::dns::RRClass& rrclass, const - boost::shared_ptr& - list); - typedef boost::shared_ptr > > DataSrcClientListsPtr; + /// \brief Swap the currently used set of data source client lists with + /// given one. + /// + /// The "set" of lists is actually given in the form of map from + /// RRClasses to shared pointers to isc::datasrc::ConfigurableClientList. + /// + /// This method returns the swapped set of lists, which was previously + /// used by the server. + /// + /// This method is intended to be used by a separate method to update + /// the data source configuration "at once". The caller must hold + /// a lock for the mutex object returned by \c getClientListMutex() + /// before calling this method. + /// + /// The ownership of the returned pointer is transferred to the caller. + /// The caller is generally expected to release the resources used in + /// the old lists. Note that it could take longer time if some of the + /// data source clients contain a large size of in-memory data. + /// + /// The caller can pass a NULL pointer. This effectively disables + /// any data source for the server. + /// + /// \param new_lists Shared pointer to a new set of data source client + /// lists. + /// \return The previous set of lists. It can be NULL. DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr new_lists); diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 02354ffa26..532aa34a2f 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -17,6 +17,8 @@ #include "auth_srv.h" +#include + #include #include @@ -62,8 +64,12 @@ configureDataSourceGeneric(Server& server, } // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. - server.swapDataSrcClientLists(new_lists); + // old lists be destroyed. Lock will be released immediately after the + // swap. + { + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(new_lists); + } } /// \brief Concrete version of configureDataSource() for the diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 60a9a2ac38..dd60d896ad 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -63,6 +63,7 @@ using namespace std; using namespace isc::cc; using namespace isc::dns; +using namespace isc::datasrc; using namespace isc::util; using namespace isc::util::io::internal; using namespace isc::util::unittests; @@ -90,6 +91,9 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone"; // a signed example zone. const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed"; +// shortcut commonly used in tests +typedef boost::shared_ptr ListPtr; + class AuthSrvTest : public SrvTestBase { protected: AuthSrvTest() : @@ -1431,7 +1435,9 @@ TEST_F(AuthSrvTest, boost::shared_ptr list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER, false)); - server.setClientList(RRClass::IN(), list); + AuthSrv::DataSrcClientListsPtr lists(new std::map); + lists->insert(pair(RRClass::IN(), list)); + server.swapDataSrcClientLists(lists); } createDataFromFile("nsec3query_nodnssec_fromWire.wire"); @@ -1459,7 +1465,9 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception, boost::shared_ptr list(new FakeList(server.getClientList(RRClass::IN()), throw_when, isc_exception, rrset)); - server.setClientList(RRClass::IN(), list); + AuthSrv::DataSrcClientListsPtr lists(new std::map); + lists->insert(pair(RRClass::IN(), list)); + server.swapDataSrcClientLists(lists); } TEST_F(AuthSrvTest, @@ -1772,34 +1780,37 @@ TEST_F(AuthSrvTest, clientList) { // There's a debug-build only check in them to make sure everything // locks them and we call them directly here. isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + + AuthSrv::DataSrcClientListsPtr lists; // initially empty + // The lists don't exist. Therefore, the list of RRClasses is empty. - // We also have no IN list. - EXPECT_TRUE(server.getClientListClasses().empty()); - EXPECT_EQ(boost::shared_ptr(), - server.getClientList(RRClass::IN())); + EXPECT_TRUE(server.swapDataSrcClientLists(lists)->empty()); + // Put something in. - const boost::shared_ptr - list(new isc::datasrc::ConfigurableClientList(RRClass::IN())); - const boost::shared_ptr - list2(new isc::datasrc::ConfigurableClientList(RRClass::CH())); - server.setClientList(RRClass::IN(), list); - server.setClientList(RRClass::CH(), list2); - // There are two things in the list and they are IN and CH - vector classes(server.getClientListClasses()); - ASSERT_EQ(2, classes.size()); - EXPECT_EQ(RRClass::IN(), classes[0]); - EXPECT_EQ(RRClass::CH(), classes[1]); + const ListPtr list(new ConfigurableClientList(RRClass::IN())); + const ListPtr list2(new ConfigurableClientList(RRClass::CH())); + + lists.reset(new std::map); + lists->insert(pair(RRClass::IN(), list)); + lists->insert(pair(RRClass::CH(), list2)); + server.swapDataSrcClientLists(lists); + // And the lists can be retrieved. EXPECT_EQ(list, server.getClientList(RRClass::IN())); EXPECT_EQ(list2, server.getClientList(RRClass::CH())); - // Remove one of them - server.setClientList(RRClass::CH(), - boost::shared_ptr()); - // This really got deleted, including the class. - classes = server.getClientListClasses(); - ASSERT_EQ(1, classes.size()); - EXPECT_EQ(RRClass::IN(), classes[0]); + + // Replace the lists with new lists containing only one list. + lists.reset(new std::map); + lists->insert(pair(RRClass::IN(), list)); + lists = server.swapDataSrcClientLists(lists); + + // Old one had two lists. That confirms our swap for IN and CH classes + // (i.e., no other entries were there). + EXPECT_EQ(2, lists->size()); + + // The CH list really got deleted. EXPECT_EQ(list, server.getClientList(RRClass::IN())); + EXPECT_FALSE(server.getClientList(RRClass::CH())); } // We just test the mutex can be locked (exactly once). diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index c0c9b5ce99..329c5d11be 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -81,7 +81,10 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: - // To pretend to be the server: + // These pretend to be the server + isc::util::thread::Mutex& getClientListMutex() const { + return (mutex_); + } void swapDataSrcClientLists(shared_ptr > new_lists) { From 5d61dba1dc3e51d66ba40d82b1bc378577783dc0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:40:10 -0700 Subject: [PATCH 032/194] [2204] extracted swapDataSrcClientLists() from configureDataSource(). now configureDataSource() can take time. --- src/bin/auth/datasrc_config.cc | 2 +- src/bin/auth/datasrc_config.h | 17 ++++--------- src/bin/auth/main.cc | 24 +++++++++++++++---- src/bin/auth/tests/auth_srv_unittest.cc | 16 +++++++++---- src/bin/auth/tests/command_unittest.cc | 12 ++++++++-- src/bin/auth/tests/datasrc_config_unittest.cc | 20 +++++++++++----- 6 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/bin/auth/datasrc_config.cc b/src/bin/auth/datasrc_config.cc index 73fb5190c6..bef6e57de7 100644 --- a/src/bin/auth/datasrc_config.cc +++ b/src/bin/auth/datasrc_config.cc @@ -18,7 +18,7 @@ // This is a trivial specialization for the commonly used version. // Defined in .cc to avoid accidental creation of multiple copies. -void +AuthSrv::DataSrcClientListsPtr configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config) { return (configureDataSourceGeneric - #include #include @@ -41,8 +39,9 @@ /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. template -void -configureDataSourceGeneric(Server& server, +boost::shared_ptr > > // = ListMap below +configureDataSourceGeneric(Server& /*server*/, const isc::data::ConstElementPtr& config) { typedef boost::shared_ptr ListPtr; @@ -63,18 +62,12 @@ configureDataSourceGeneric(Server& server, list)); } - // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. Lock will be released immediately after the - // swap. - { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - server.swapDataSrcClientLists(new_lists); - } + return (new_lists); } /// \brief Concrete version of configureDataSource() for the /// use with authoritative server implementation. -void +AuthSrv::DataSrcClientListsPtr configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); #endif // DATASRC_CONFIG_H diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index b425813006..2a17112ba4 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -93,18 +94,33 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, { assert(server != NULL); if (config->contains("classes")) { + AuthSrv::DataSrcClientListsPtr lists; + if (*first_time) { // HACK: The default is not passed to the handler in the first // callback. This one will get the default (or, current value). // Further updates will work the usual way. assert(config_session != NULL); *first_time = false; - configureDataSource(*auth_server, - config_session->getRemoteConfigValue( - "data_sources", "classes")); + lists = configureDataSource( + *auth_server, + config_session->getRemoteConfigValue("data_sources", + "classes")); } else { - configureDataSource(*server, config->get("classes")); + lists = configureDataSource(*server, config->get("classes")); } + + // Replace the server's lists. By ignoring the return value we let the + // old lists be destroyed. Lock will be released immediately after the + // swap. + { + isc::util::thread::Mutex::Locker locker( + server->getClientListMutex()); + lists = server->swapDataSrcClientLists(lists); + } + // The previous lists are destroyed here. Note that it's outside + // of the critical section protected by the locker. So this can + // take time if running on a separate thread. } } diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index dd60d896ad..f250d70f05 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -725,6 +725,14 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) { EXPECT_FALSE(dnsserv.hasAnswer()); } +void +installDataSrcClientLists(AuthSrv& server, + AuthSrv::DataSrcClientListsPtr lists) +{ + thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(lists); +} + void updateDatabase(AuthSrv& server, const char* params) { const ConstElementPtr config(Element::fromJSON("{" @@ -732,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) { " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } void @@ -749,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } void @@ -759,7 +767,7 @@ updateBuiltin(AuthSrv& server) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -957,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index b5e43edda2..36d13906b4 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -191,6 +191,14 @@ zoneChecks(AuthSrv& server) { find(Name("ns.test2.example"), RRType::AAAA())->code); } +void +installDataSrcClientLists(AuthSrv& server, + AuthSrv::DataSrcClientListsPtr lists) +{ + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(lists); +} + void configureZones(AuthSrv& server) { ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in " @@ -210,7 +218,7 @@ configureZones(AuthSrv& server) { " \"cache-enable\": true" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); zoneChecks(server); } @@ -273,7 +281,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - configureDataSource(server_, config); + installDataSrcClientLists(server_, configureDataSource(server_, config)); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 329c5d11be..82067d30d8 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -60,14 +60,11 @@ private: typedef shared_ptr ListPtr; +// Forward declaration. We need precise definition of DatasrcConfigTest +// to complete this function. void testConfigureDataSource(DatasrcConfigTest& test, - const isc::data::ConstElementPtr& config) -{ - // We use the test fixture for the Server type. This makes it possible - // to easily fake all needed methods and look that they were called. - configureDataSourceGeneric(test, config); -} + const isc::data::ConstElementPtr& config); void datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, @@ -162,6 +159,17 @@ protected: mutable isc::util::thread::Mutex mutex_; }; +void +testConfigureDataSource(DatasrcConfigTest& test, + const isc::data::ConstElementPtr& config) +{ + // We use the test fixture for the Server type. This makes it possible + // to easily fake all needed methods and look that they were called. + shared_ptr > lists = + configureDataSourceGeneric(test, config); + test.swapDataSrcClientLists(lists); +} + // Push there a configuration with a single list. TEST_F(DatasrcConfigTest, createList) { initializeINList(); From d7846ea4a9d811c35641f77384d9825f12488e71 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:48:03 -0700 Subject: [PATCH 033/194] [2204] removed server function/template param from configureDataSourceGeneric. This is pure cleanup. This function already doesn't use the server. --- src/bin/auth/benchmarks/query_bench.cc | 2 -- src/bin/auth/datasrc_config.cc | 7 +++---- src/bin/auth/datasrc_config.h | 18 ++++++++---------- src/bin/auth/main.cc | 3 +-- src/bin/auth/tests/auth_srv_unittest.cc | 8 ++++---- src/bin/auth/tests/command_unittest.cc | 6 +++--- src/bin/auth/tests/datasrc_config_unittest.cc | 6 +++--- 7 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index 73f702baff..d732f945be 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -126,7 +126,6 @@ public: QueryBenchMark(queries, query_message, buffer) { configureDataSource( - *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"sqlite3\"," " \"params\": {" @@ -145,7 +144,6 @@ public: QueryBenchMark(queries, query_message, buffer) { configureDataSource( - *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"MasterFiles\"," " \"cache-enable\": true, " diff --git a/src/bin/auth/datasrc_config.cc b/src/bin/auth/datasrc_config.cc index bef6e57de7..62c3c7a6ba 100644 --- a/src/bin/auth/datasrc_config.cc +++ b/src/bin/auth/datasrc_config.cc @@ -19,8 +19,7 @@ // This is a trivial specialization for the commonly used version. // Defined in .cc to avoid accidental creation of multiple copies. AuthSrv::DataSrcClientListsPtr -configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config) -{ - return (configureDataSourceGeneric(server, config)); +configureDataSource(const isc::data::ConstElementPtr& config) { + return (configureDataSourceGeneric< + isc::datasrc::ConfigurableClientList>(config)); } diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 9a6f7ac64a..842d2ca927 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -25,25 +25,23 @@ #include #include -/// \brief Configure the authoritative server's data source lists +/// \brief Configure data source client lists /// -/// This will hook into the data_sources module configuration and it will -/// keep the local copy of data source clients in the list in the authoritative -/// server. +/// This will hook into the data_sources module configuration and it return +/// a new set (in the form of a shared pointer to map) of data source client +/// lists corresponding to the configuration. /// /// This function is templated. This is simply because of easier testing. /// You don't need to pay attention to it, use the configureDataSource /// specialization instead. /// -/// \param server It is the server to configure. /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. -template +/// \return A map from RR classes to configured lists. +template boost::shared_ptr > > // = ListMap below -configureDataSourceGeneric(Server& /*server*/, - const isc::data::ConstElementPtr& config) -{ +configureDataSourceGeneric(const isc::data::ConstElementPtr& config) { typedef boost::shared_ptr ListPtr; typedef std::map Map; typedef std::map ListMap; @@ -68,7 +66,7 @@ configureDataSourceGeneric(Server& /*server*/, /// \brief Concrete version of configureDataSource() for the /// use with authoritative server implementation. AuthSrv::DataSrcClientListsPtr -configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); +configureDataSource(const isc::data::ConstElementPtr& config); #endif // DATASRC_CONFIG_H diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 2a17112ba4..d9b01314a2 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -103,11 +103,10 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, assert(config_session != NULL); *first_time = false; lists = configureDataSource( - *auth_server, config_session->getRemoteConfigValue("data_sources", "classes")); } else { - lists = configureDataSource(*server, config->get("classes")); + lists = configureDataSource(config->get("classes")); } // Replace the server's lists. By ignoring the return value we let the diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index f250d70f05..2668ecc79a 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -740,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) { " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } void @@ -757,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } void @@ -767,7 +767,7 @@ updateBuiltin(AuthSrv& server) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -965,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 36d13906b4..48b0040067 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -218,7 +218,7 @@ configureZones(AuthSrv& server) { " \"cache-enable\": true" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); zoneChecks(server); } @@ -281,7 +281,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - installDataSrcClientLists(server_, configureDataSource(server_, config)); + installDataSrcClientLists(server_, configureDataSource(config)); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); @@ -345,7 +345,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.com\"]" "}]}")); - EXPECT_THROW(configureDataSource(server_, config2), + EXPECT_THROW(configureDataSource(config2), ConfigurableClientList::ConfigurationError); result_ = execAuthServerCommand(server_, "loadzone", diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 82067d30d8..ba554fe859 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -163,10 +163,10 @@ void testConfigureDataSource(DatasrcConfigTest& test, const isc::data::ConstElementPtr& config) { - // We use the test fixture for the Server type. This makes it possible - // to easily fake all needed methods and look that they were called. + // We use customized (faked lists) for the List type. This makes it + // possible to easily look that they were called. shared_ptr > lists = - configureDataSourceGeneric(test, config); + configureDataSourceGeneric(config); test.swapDataSrcClientLists(lists); } From 0b6e74f544b522943436bfdff79c2fa06273b49d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:52:37 -0700 Subject: [PATCH 034/194] [2204] regression fix: make sure the datasrc config is installed to server. --- src/bin/auth/benchmarks/query_bench.cc | 34 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index d732f945be..a319555140 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -18,6 +18,8 @@ #include #include +#include + #include #include #include @@ -125,12 +127,15 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - configureDataSource( - Element::fromJSON("{\"IN\":" - " [{\"type\": \"sqlite3\"," - " \"params\": {" - " \"database_file\": \"" + - string(datasrc_file) + "\"}}]}")); + isc::util::thread::Mutex::Locker locker( + server_->getClientListMutex()); + server_->swapDataSrcClientLists( + configureDataSource( + Element::fromJSON("{\"IN\":" + " [{\"type\": \"sqlite3\"," + " \"params\": {" + " \"database_file\": \"" + + string(datasrc_file) + "\"}}]}"))); } }; @@ -143,13 +148,16 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - configureDataSource( - Element::fromJSON("{\"IN\":" - " [{\"type\": \"MasterFiles\"," - " \"cache-enable\": true, " - " \"params\": {\"" + - string(zone_origin) + "\": \"" + - string(zone_file) + "\"}}]}")); + isc::util::thread::Mutex::Locker locker( + server_->getClientListMutex()); + server_->swapDataSrcClientLists( + configureDataSource( + Element::fromJSON("{\"IN\":" + " [{\"type\": \"MasterFiles\"," + " \"cache-enable\": true, " + " \"params\": {\"" + + string(zone_origin) + "\": \"" + + string(zone_file) + "\"}}]}"))); } }; From 86a4bae1ffa68eb38921d6cb3f16dad43b616009 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 17:04:28 -0700 Subject: [PATCH 035/194] [2204] cleanup: removed unused func, and renamed "ClientList" with "DataSrc". getClientListClasses() was removed. It's not used anymore. a few methods named with "ClientList" were renamed with adding "DataSrc". The resulting names are a bit long, but since there's no namespace qualification, simply saying "client" can be confusing (it could be interpreted to mean DNS clients, for example). --- src/bin/auth/auth_srv.cc | 30 +++-------- src/bin/auth/auth_srv.h | 21 +++----- src/bin/auth/benchmarks/query_bench.cc | 4 +- src/bin/auth/command.cc | 5 +- src/bin/auth/main.cc | 2 +- src/bin/auth/tests/auth_srv_unittest.cc | 32 ++++++----- src/bin/auth/tests/command_unittest.cc | 53 ++++++++++++------- src/bin/auth/tests/datasrc_config_unittest.cc | 2 +- 8 files changed, 74 insertions(+), 75 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 1102ae3ed3..e73eb528db 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -271,7 +271,9 @@ public: /// The data source client list AuthSrv::DataSrcClientListsPtr datasrc_client_lists_; - shared_ptr getClientList(const RRClass& rrclass) { + shared_ptr getDataSrcClientList( + const RRClass& rrclass) + { // TODO: Debug-build only check if (!mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked!"); @@ -646,13 +648,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message, } // Lock the client lists and keep them under the lock until the processing // and rendering is done (this is the same mutex as from - // AuthSrv::getClientListMutex()). + // AuthSrv::getDataSrcClientListMutex()). isc::util::thread::Mutex::Locker locker(mutex_); try { const ConstQuestionPtr question = *message.beginQuestion(); const shared_ptr - list(getClientList(question->getClass())); + list(getDataSrcClientList(question->getClass())); if (list) { const RRType& qtype = question->getType(); const Name& qname = question->getName(); @@ -942,28 +944,12 @@ AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { } shared_ptr -AuthSrv::getClientList(const RRClass& rrclass) { - return (impl_->getClientList(rrclass)); -} - -vector -AuthSrv::getClientListClasses() const { - // TODO: Debug-build only check - if (!impl_->mutex_.locked()) { - isc_throw(isc::Unexpected, "Not locked"); - } - - vector result; - for (std::map >:: - const_iterator it(impl_->datasrc_client_lists_->begin()); - it != impl_->datasrc_client_lists_->end(); ++it) { - result.push_back(it->first); - } - return (result); +AuthSrv::getDataSrcClientList(const RRClass& rrclass) { + return (impl_->getDataSrcClientList(rrclass)); } util::thread::Mutex& -AuthSrv::getClientListMutex() const { +AuthSrv::getDataSrcClientListMutex() const { return (impl_->mutex_); } diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index f1e73c1158..0849bdd1d0 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -302,6 +302,7 @@ public: /// If there was no forwarder yet, this method does nothing. void destroyDDNSForwarder(); + /// \brief Shortcut typedef used for swapDataSrcClientLists(). typedef boost::shared_ptr > > @@ -318,7 +319,7 @@ public: /// /// This method is intended to be used by a separate method to update /// the data source configuration "at once". The caller must hold - /// a lock for the mutex object returned by \c getClientListMutex() + /// a lock for the mutex object returned by \c getDataSrcClientListMutex() /// before calling this method. /// /// The ownership of the returned pointer is transferred to the caller. @@ -340,13 +341,7 @@ public: /// \param rrclass The class for which to get the list. /// \return The list, or NULL if no list is set for the class. boost::shared_ptr - getClientList(const isc::dns::RRClass& rrclass); - - /// \brief Returns a list of classes that have a client list. - /// - /// \return List of classes for which a non-NULL client list - /// has been set by setClientList. - std::vector getClientListClasses() const; + getDataSrcClientList(const isc::dns::RRClass& rrclass); /// \brief Return a mutex for the client lists. /// @@ -357,9 +352,9 @@ public: /// is correct: /// \code /// { - /// Mutex::Locker locker(auth->getClientListMutex()); + /// Mutex::Locker locker(auth->getDataSrcClientListMutex()); /// boost::shared_ptr - /// list(auth->getClientList(RRClass::IN())); + /// list(auth->getDataSrcClientList(RRClass::IN())); /// // Do some processing here /// } /// \endcode @@ -368,8 +363,8 @@ public: /// \code /// boost::shared_ptr list; /// { - /// Mutex::Locker locker(auth->getClientListMutex()); - /// list = auth->getClientList(RRClass::IN())); + /// Mutex::Locker locker(auth->getDataSrcClientListMutex()); + /// list = auth->getDataSrcClientList(RRClass::IN())); /// } /// // Do some processing here /// \endcode @@ -378,7 +373,7 @@ public: /// (lock) the mutex. It's because locking of the mutex is not really /// a modification of the server object and it is needed to protect the /// lists even on read-only operations. - isc::util::thread::Mutex& getClientListMutex() const; + isc::util::thread::Mutex& getDataSrcClientListMutex() const; /// \brief Sets the timeout for incoming TCP connections /// diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index a319555140..37976a3358 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -128,7 +128,7 @@ public: QueryBenchMark(queries, query_message, buffer) { isc::util::thread::Mutex::Locker locker( - server_->getClientListMutex()); + server_->getDataSrcClientListMutex()); server_->swapDataSrcClientLists( configureDataSource( Element::fromJSON("{\"IN\":" @@ -149,7 +149,7 @@ public: QueryBenchMark(queries, query_message, buffer) { isc::util::thread::Mutex::Locker locker( - server_->getClientListMutex()); + server_->getDataSrcClientListMutex()); server_->swapDataSrcClientLists( configureDataSource( Element::fromJSON("{\"IN\":" diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index 448a31bc8e..d26cf091de 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -192,9 +192,10 @@ public: // We're going to work with the client lists. They may be used // from a different thread too, protect them. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); const boost::shared_ptr - list(server.getClientList(zone_class)); + list(server.getDataSrcClientList(zone_class)); if (!list) { isc_throw(AuthCommandError, "There's no client list for " diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index d9b01314a2..1e3ca9c3de 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -114,7 +114,7 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, // swap. { isc::util::thread::Mutex::Locker locker( - server->getClientListMutex()); + server->getDataSrcClientListMutex()); lists = server->swapDataSrcClientLists(lists); } // The previous lists are destroyed here. Note that it's outside diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 2668ecc79a..e248e81483 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -729,7 +729,7 @@ void installDataSrcClientLists(AuthSrv& server, AuthSrv::DataSrcClientListsPtr lists) { - thread::Mutex::Locker locker(server.getClientListMutex()); + thread::Mutex::Locker locker(server.getDataSrcClientListMutex()); server.swapDataSrcClientLists(lists); } @@ -1439,10 +1439,11 @@ TEST_F(AuthSrvTest, // Set real inmem client to proxy updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); boost::shared_ptr - list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER, - false)); + list(new FakeList(server.getDataSrcClientList(RRClass::IN()), + THROW_NEVER, false)); AuthSrv::DataSrcClientListsPtr lists(new std::map); lists->insert(pair(RRClass::IN(), list)); server.swapDataSrcClientLists(lists); @@ -1469,10 +1470,11 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception, { updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); boost::shared_ptr - list(new FakeList(server.getClientList(RRClass::IN()), throw_when, - isc_exception, rrset)); + list(new FakeList(server.getDataSrcClientList(RRClass::IN()), + throw_when, isc_exception, rrset)); AuthSrv::DataSrcClientListsPtr lists(new std::map); lists->insert(pair(RRClass::IN(), list)); server.swapDataSrcClientLists(lists); @@ -1787,7 +1789,8 @@ TEST_F(AuthSrvTest, clientList) { // We need to lock the mutex to make the (get|set)ClientList happy. // There's a debug-build only check in them to make sure everything // locks them and we call them directly here. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); AuthSrv::DataSrcClientListsPtr lists; // initially empty @@ -1804,8 +1807,8 @@ TEST_F(AuthSrvTest, clientList) { server.swapDataSrcClientLists(lists); // And the lists can be retrieved. - EXPECT_EQ(list, server.getClientList(RRClass::IN())); - EXPECT_EQ(list2, server.getClientList(RRClass::CH())); + EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN())); + EXPECT_EQ(list2, server.getDataSrcClientList(RRClass::CH())); // Replace the lists with new lists containing only one list. lists.reset(new std::map); @@ -1817,19 +1820,20 @@ TEST_F(AuthSrvTest, clientList) { EXPECT_EQ(2, lists->size()); // The CH list really got deleted. - EXPECT_EQ(list, server.getClientList(RRClass::IN())); - EXPECT_FALSE(server.getClientList(RRClass::CH())); + EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN())); + EXPECT_FALSE(server.getDataSrcClientList(RRClass::CH())); } // We just test the mutex can be locked (exactly once). TEST_F(AuthSrvTest, mutex) { - isc::util::thread::Mutex::Locker l1(server.getClientListMutex()); + isc::util::thread::Mutex::Locker l1(server.getDataSrcClientListMutex()); // TODO: Once we have non-debug build, this one will not work, since // we currently use the fact that we can't lock twice from the same // thread. In the non-debug mode, this would deadlock. // Skip then. EXPECT_THROW({ - isc::util::thread::Mutex::Locker l2(server.getClientListMutex()); + isc::util::thread::Mutex::Locker l2( + server.getDataSrcClientListMutex()); }, isc::InvalidOperation); } diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 48b0040067..8aa2322151 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -176,17 +176,18 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) { // zones, and checks the zones are correctly loaded. void zoneChecks(AuthSrv& server) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::AAAA())->code); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::AAAA())->code); } @@ -195,7 +196,8 @@ void installDataSrcClientLists(AuthSrv& server, AuthSrv::DataSrcClientListsPtr lists) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); server.swapDataSrcClientLists(lists); } @@ -225,20 +227,22 @@ configureZones(AuthSrv& server) { void newZoneChecks(AuthSrv& server) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::A())->code); // now test1.example should have ns/AAAA - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::AAAA())->code); // test2.example shouldn't change - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, + server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::AAAA())->code); } @@ -284,9 +288,11 @@ TEST_F(AuthCommandTest, installDataSrcClientLists(server_, configureDataSource(config)); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // Check that the A record at www.example.org does not exist - EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXDOMAIN, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); @@ -306,7 +312,8 @@ TEST_F(AuthCommandTest, sql_updater->addRRset(*rrset); sql_updater->commit(); - EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXDOMAIN, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); } @@ -318,9 +325,11 @@ TEST_F(AuthCommandTest, checkAnswer(0, "Successful load"); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // And now it should be present too. - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); } @@ -331,9 +340,11 @@ TEST_F(AuthCommandTest, checkAnswer(1, "example.com"); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // The previous zone is not hurt in any way - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("example.org"), RRType::SOA())->code); } @@ -352,9 +363,11 @@ TEST_F(AuthCommandTest, Element::fromJSON("{\"origin\": \"example.com\"}")); checkAnswer(1, "Unreadable"); - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // The previous zone is not hurt in any way - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("example.org"), RRType::SOA())->code); } diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index ba554fe859..97d89bf2bb 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -79,7 +79,7 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: // These pretend to be the server - isc::util::thread::Mutex& getClientListMutex() const { + isc::util::thread::Mutex& getDataSrcClientListMutex() const { return (mutex_); } void swapDataSrcClientLists(shared_ptr > From 76243c3ccd7a30484da8478f7457e18fce485492 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:13:56 +0200 Subject: [PATCH 036/194] [2292] Remove the setZoneData method Currently, the content of zone is passed to the addZone method. No way to change it later, but the addZone overwrites the old value if called again. This should hopefully allow for removal of mutable find methods from the tree. --- src/lib/datasrc/memory/memory_client.cc | 15 ++--- src/lib/datasrc/memory/zone_table.cc | 41 +++--------- src/lib/datasrc/memory/zone_table.h | 54 ++++++--------- .../tests/memory/zone_table_unittest.cc | 67 +++++++++++++------ 4 files changed, 80 insertions(+), 97 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5f6f510306..5eaf58b547 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -630,22 +630,15 @@ InMemoryClient::InMemoryClientImpl::load( std::string* tstr = node->setData(new std::string(filename)); delete tstr; - ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name)); - if (result.code == result::SUCCESS) { + result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, holder)); + if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. ++zone_count_; } - ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name, - holder.release())); - assert(fr.code == result::SUCCESS); - if (fr.zone_data != NULL) { - ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_); - } - - return (result.code); + return (result); } namespace { diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index a74a61dd27..68c73ad712 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -67,19 +67,11 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, mem_sgmt.deallocate(ztable, sizeof(ZoneTable)); } -ZoneTable::AddResult +result::Result ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, - const Name& zone_name) + const Name& zone_name, + SegmentObjectHolder& content) { - // Create a new ZoneData instance first. If the specified name already - // exists in the table, the new data will soon be destroyed, but we want - // to make sure if this allocation fails the tree won't be changed to - // provide as strong guarantee as possible. In practice, we generally - // expect the caller tries to add a zone only when it's a new one, so - // this should be a minor concern. - SegmentObjectHolder holder( - mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class); - // Get the node where we put the zone ZoneTableNode* node(NULL); switch (zones_->insert(mem_sgmt, zone_name, &node)) { @@ -94,12 +86,13 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, // Can Not Happen assert(node != NULL); - // Is it empty? We either just created it or it might be nonterminal - if (node->isEmpty()) { - node->setData(holder.get()); - return (AddResult(result::SUCCESS, holder.release())); - } else { // There's something there already - return (AddResult(result::EXIST, node->getData())); + // We can release now, setData never throws + ZoneData* old = node->setData(content.release()); + if (old != NULL) { + ZoneData::destroy(mem_sgmt, old, zone_class); + return (result::EXIST); + } else { + return (result::SUCCESS); } } @@ -132,20 +125,6 @@ ZoneTable::findZone(const Name& name) const { return (FindResult(my_result, node->getData())); } -ZoneTable::FindResult -ZoneTable::setZoneData(const Name& name, ZoneData* data) -{ - ZoneTableNode* node(NULL); - - ZoneTableTree::Result result(zones_->find(name, &node)); - - if (result != ZoneTableTree::EXACTMATCH) { - return (FindResult(result::NOTFOUND, NULL)); - } else { - return (FindResult(result::SUCCESS, node->setData(data))); - } -} - } // end of namespace memory } // end of namespace datasrc } // end of namespace isc diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 8ad6213724..aebd4d0d0f 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -36,6 +36,10 @@ namespace memory { // forward declaration: in this header it's mostly an opaque type. class ZoneData; +namespace detail { +template class SegmentObjectHolder; +} + /// \brief A conceptual table of authoritative zones. /// /// This class is actually a simple wrapper for a \c DomainTree whose data is @@ -74,14 +78,6 @@ private: typedef DomainTreeNode ZoneTableNode; public: - /// \brief Result data of addZone() method. - struct AddResult { - AddResult(result::Result param_code, ZoneData* param_zone_data) : - code(param_code), zone_data(param_zone_data) - {} - const result::Result code; - ZoneData* const zone_data; - }; /// \brief Result data of findZone() method. struct FindResult { @@ -140,30 +136,28 @@ public: /// Add a new zone to the \c ZoneTable. /// - /// This method creates a new \c ZoneData for the given zone name and - /// holds it in the internal table. The newly created zone data will be - /// returned via the \c zone_data member of the return value. If the given - /// zone name already exists in the table, a new data object won't be - /// created; instead, the existing corresponding data will be returned. - /// - /// The zone table keeps the ownership of the created zone data; the - /// caller must not try to destroy it directly. (We'll eventually - /// add an interface to delete specific zone data from the table). + /// This method adds a given zone data to the internal table. /// /// \throw std::bad_alloc Internal resource allocation fails. /// /// \param mem_sgmt The \c MemorySegment to allocate zone data to be - /// created. It must be the same segment that was used to create - /// the zone table at the time of create(). + /// created. It must be the same segment that was used to create + /// the zone table at the time of create(). /// \param zone_name The name of the zone to be added. /// \param zone_class The RR class of the zone. It must be the RR class - /// that is supposed to be associated to the zone table. + /// that is supposed to be associated to the zone table. + /// \param content This one should hold the zone content (the ZoneData). + /// When it is added successfully, it is released from the holder. /// \return \c result::SUCCESS If the zone is successfully - /// added to the zone table. - /// \return \c result::EXIST The zone table already contains - /// zone of the same origin. - AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class, - const dns::Name& zone_name); + /// added to the zone table. + /// \return \c result::EXIST The zone table already contained + /// zone of the same origin. The old data is released and replaced + /// by the new one. + result::Result addZone(util::MemorySegment& mem_sgmt, + dns::RRClass zone_class, + const dns::Name& zone_name, + detail::SegmentObjectHolder& content); /// Find a zone that best matches the given name in the \c ZoneTable. /// @@ -185,16 +179,6 @@ public: /// \return A \c FindResult object enclosing the search result (see above). FindResult findZone(const isc::dns::Name& name) const; - /// Override the ZoneData for a node (zone) in the zone tree. - /// - /// \throw none - /// - /// \param name A domain name for which the zone data is set. - /// \param data The new zone data to set. - /// \return A \c FindResult object containing the old data if the - /// zone was found. - FindResult setZoneData(const isc::dns::Name& name, ZoneData* data); - private: boost::interprocess::offset_ptr zones_; }; diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 359df4923f..b9ee3d9441 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -30,6 +31,7 @@ using namespace isc::dns; using namespace isc::datasrc; using namespace isc::datasrc::memory; +using namespace isc::datasrc::memory::detail; namespace { // Memory segment specified for tests. It normally behaves like a "local" @@ -87,46 +89,69 @@ TEST_F(ZoneTableTest, create) { } TEST_F(ZoneTableTest, addZone) { + SegmentObjectHolder holder1( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); // Normal successful case. - const ZoneTable::AddResult result1 = - zone_table->addZone(mem_sgmt_, zclass_, zname1); - EXPECT_EQ(result::SUCCESS, result1.code); + EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, + zname1, holder1)); + // It got released by it + EXPECT_EQ(NULL, holder1.get()); // Duplicate add doesn't replace the existing data. + SegmentObjectHolder holder2( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1).code); - EXPECT_EQ(result1.zone_data, - zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data); + zname1, holder2)); + // It releases this one even when we replace the old zone + EXPECT_EQ(NULL, holder2.get()); + + SegmentObjectHolder holder3( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")), + zclass_); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - Name("EXAMPLE.COM")).code); + Name("EXAMPLE.COM"), + holder3)); // Add some more different ones. Should just succeed. + SegmentObjectHolder holder4( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2).code); + zone_table->addZone(mem_sgmt_, zclass_, zname2, holder4)); + SegmentObjectHolder holder5( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3).code); + zone_table->addZone(mem_sgmt_, zclass_, zname3, holder5)); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected // in TearDown()). - mem_sgmt_.setThrowCount(2); - EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")), + SegmentObjectHolder holder6( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_); + mem_sgmt_.setThrowCount(1); + EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"), + holder6), std::bad_alloc); } TEST_F(ZoneTableTest, findZone) { - const ZoneTable::AddResult add_result1 = - zone_table->addZone(mem_sgmt_, zclass_, zname1); - EXPECT_EQ(result::SUCCESS, add_result1.code); + SegmentObjectHolder holder1( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); + ZoneData* zone_data = holder1.get(); + EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, + holder1)); + SegmentObjectHolder holder2( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2).code); + zone_table->addZone(mem_sgmt_, zclass_, zname2, holder2)); + SegmentObjectHolder holder3( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3).code); + zone_table->addZone(mem_sgmt_, zclass_, zname3, holder3)); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); EXPECT_EQ(result::SUCCESS, find_result1.code); - EXPECT_EQ(add_result1.zone_data, find_result1.zone_data); + EXPECT_EQ(zone_data, find_result1.zone_data); EXPECT_EQ(result::NOTFOUND, zone_table->findZone(Name("example.org")).code); @@ -137,14 +162,16 @@ TEST_F(ZoneTableTest, findZone) { // and the code should be PARTIALMATCH. EXPECT_EQ(result::PARTIALMATCH, zone_table->findZone(Name("www.example.com")).code); - EXPECT_EQ(add_result1.zone_data, + EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); // make sure the partial match is indeed the longest match by adding // a zone with a shorter origin and query again. + SegmentObjectHolder holder4( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - Name("com")).code); - EXPECT_EQ(add_result1.zone_data, + Name("com"), holder4)); + EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } } From e1b4bf1cb50160c436f2e5440fd774680b91b7a2 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:38:23 +0200 Subject: [PATCH 037/194] [2292] Remove mutable find methods They are currently used in test code only (so we'll need to update the tests to compile). The real code seams clean now. --- src/lib/datasrc/memory/domaintree.h | 47 ++----------------------- src/lib/datasrc/memory/memory_client.cc | 20 +---------- src/lib/datasrc/memory/memory_client.h | 29 --------------- src/lib/datasrc/memory/zone_table.cc | 2 +- src/lib/datasrc/memory/zone_table.h | 4 +-- 5 files changed, 6 insertions(+), 96 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index e54d8eaaed..50f5d40cf6 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1080,17 +1080,6 @@ public: /// of it. In that case, node parameter is left intact. //@{ - /// \brief Simple find. - /// - /// Acts as described in the \ref find section. - Result find(const isc::dns::Name& name, - DomainTreeNode** node) const { - DomainTreeNodeChain > node_path; - const isc::dns::LabelSequence ls(name); - return (find >(ls, node, node_path, NULL, - NULL)); - } - /// \brief Simple find returning immutable node. /// /// Acts as described in the \ref find section, but returns immutable node @@ -1109,17 +1098,6 @@ public: return (ret); } - /// \brief Simple find, with node_path tracking - /// - /// Acts as described in the \ref find section. - Result find(const isc::dns::Name& name, DomainTreeNode** node, - DomainTreeNodeChain& node_path) const - { - const isc::dns::LabelSequence ls(name); - return (find >(ls, node, node_path, - NULL, NULL)); - } - /// \brief Simple find returning immutable node, with node_path tracking /// /// Acts as described in the \ref find section, but returns immutable node @@ -1138,27 +1116,6 @@ public: return (ret); } - /// \brief Simple find returning immutable node. - /// - /// Acts as described in the \ref find section, but returns immutable - /// node pointer. - template - Result find(const isc::dns::Name& name, - const DomainTreeNode** node, - DomainTreeNodeChain& node_path, - bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const - { - const DomainTreeNode* target_node = NULL; - const isc::dns::LabelSequence ls(name); - Result ret = find(ls, &target_node, node_path, callback, - callback_arg); - if (ret != NOTFOUND) { - *node = target_node; - } - return (ret); - } - /// \brief Find with callback and node chain /// \anchor callback /// @@ -1235,7 +1192,7 @@ public: /// \c true, it returns immediately with the current node. template Result find(const isc::dns::LabelSequence& target_labels_orig, - NodeType** node, + const NodeType** node, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const; @@ -1523,7 +1480,7 @@ template template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - NodeType** target, + const NodeType** target, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5eaf58b547..de953ca107 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -725,7 +725,7 @@ InMemoryClient::load(const isc::dns::Name& zone_name, const std::string InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { - FileNameNode* node(NULL); + const FileNameNode* node(NULL); FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, &node); if (result == FileNameTree::EXACTMATCH) { @@ -735,24 +735,6 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { } } -result::Result -InMemoryClient::add(const isc::dns::Name& zone_name, - const ConstRRsetPtr& rrset) -{ - const ZoneTable::FindResult result = - impl_->zone_table_->findZone(zone_name); - if (result.code != result::SUCCESS) { - isc_throw(DataSourceError, "No such zone: " + zone_name.toText()); - } - - const ConstRRsetPtr sig_rrset = - rrset ? rrset->getRRsig() : ConstRRsetPtr(); - impl_->add(rrset, sig_rrset, zone_name, *result.zone_data); - - // add() doesn't allow duplicate add, so we always return SUCCESS. - return (result::SUCCESS); -} - namespace { class MemoryIterator : public ZoneIterator { diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index 330d62e15f..c37ad53c0b 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -139,35 +139,6 @@ public: /// zone from a file before. const std::string getFileName(const isc::dns::Name& zone_name) const; - /// \brief Inserts an rrset into the zone. - /// - /// It puts another RRset into the zone. - /// - /// In the current implementation, this method doesn't allow an existing - /// RRset to be updated or overridden. So the caller must make sure that - /// all RRs of the same type and name must be given in the form of a - /// single RRset. The current implementation will also require that - /// when an RRSIG is added, the RRset to be covered has already been - /// added. These restrictions are probably too strict when this data - /// source accepts various forms of input, so they should be revisited - /// later. - /// - /// Except for NullRRset and OutOfZone, this method does not guarantee - /// strong exception safety (it is currently not needed, if it is needed - /// in future, it should be implemented). - /// - /// \throw NullRRset \c rrset is a NULL pointer. - /// \throw OutOfZone The owner name of \c rrset is outside of the - /// origin of the zone. - /// \throw AddError Other general errors. - /// \throw Others This method might throw standard allocation exceptions. - /// - /// \param rrset The set to add. - /// \return SUCCESS or EXIST (if an rrset for given name and type already - /// exists). - result::Result add(const isc::dns::Name& zone_name, - const isc::dns::ConstRRsetPtr& rrset); - /// \brief RRset is NULL exception. /// /// This is thrown if the provided RRset parameter is NULL. diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 68c73ad712..0e6ed3c4ed 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -98,7 +98,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, ZoneTable::FindResult ZoneTable::findZone(const Name& name) const { - ZoneTableNode* node(NULL); + const ZoneTableNode* node(NULL); result::Result my_result; // Translate the return codes diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index aebd4d0d0f..4f309b7c2e 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -82,11 +82,11 @@ public: /// \brief Result data of findZone() method. struct FindResult { FindResult(result::Result param_code, - ZoneData* param_zone_data) : + const ZoneData* param_zone_data) : code(param_code), zone_data(param_zone_data) {} const result::Result code; - ZoneData* const zone_data; + const ZoneData* const zone_data; }; private: From 915576b6aa27d020faadf7fbfc4e4d9bb1df129e Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:50:50 +0200 Subject: [PATCH 038/194] [2292] Remove unused template parameters The domain tree no longer supports the mutable find versions, so we don't need the template parameters allowing for them. --- src/lib/datasrc/memory/domaintree.h | 68 +++++++---------------------- 1 file changed, 16 insertions(+), 52 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 50f5d40cf6..5101c65635 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -684,7 +684,7 @@ DomainTreeNode::predecessor() const { /// DomainTree. /// This is the reason why manipulation methods such as \c push() and \c pop() /// are private (and not shown in the doxygen document). -template > +template class DomainTreeNodeChain { /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has /// knowledge to manipulate it. @@ -817,7 +817,7 @@ private: /// root node of DomainTree /// /// \exception None - NodeType* top() const { + const DomainTreeNode* top() const { assert(!isEmpty()); return (nodes_[level_count_ - 1]); } @@ -840,7 +840,7 @@ private: /// otherwise the node should be the root node of DomainTree. /// /// \exception None - void push(NodeType* node) { + void push(const DomainTreeNode* node) { assert(level_count_ < RBT_MAX_LEVEL); nodes_[level_count_++] = node; } @@ -852,7 +852,7 @@ private: const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS; size_t level_count_; - NodeType* nodes_[RBT_MAX_LEVEL]; + const DomainTreeNode* nodes_[RBT_MAX_LEVEL]; const DomainTreeNode* last_compared_; isc::dns::NameComparisonResult last_comparison_; }; @@ -1080,39 +1080,25 @@ public: /// of it. In that case, node parameter is left intact. //@{ - /// \brief Simple find returning immutable node. + /// \brief Simple find /// - /// Acts as described in the \ref find section, but returns immutable node - /// pointer. + /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, const DomainTreeNode** node) const { DomainTreeNodeChain node_path; - const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find >(ls, &target_node, - node_path, NULL, - NULL)); - if (ret != NOTFOUND) { - *node = target_node; - } + Result ret = (find(ls, node, node_path, NULL, NULL)); return (ret); } - /// \brief Simple find returning immutable node, with node_path tracking + /// \brief Simple find, with node_path tracking /// - /// Acts as described in the \ref find section, but returns immutable node - /// pointer. + /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, const DomainTreeNode** node, DomainTreeNodeChain& node_path) const { - const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find >(ls, &target_node, - node_path, NULL, - NULL)); - if (ret != NOTFOUND) { - *node = target_node; - } + Result ret = (find(ls, node, node_path, NULL, NULL)); return (ret); } @@ -1190,34 +1176,12 @@ public: /// /// \return As in the description, but in case of callback returning /// \c true, it returns immediately with the current node. - template - Result find(const isc::dns::LabelSequence& target_labels_orig, - const NodeType** node, - DomainTreeNodeChain& node_path, - bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const; - - /// \brief Simple find returning immutable node. - /// - /// Acts as described in the \ref find section, but returns immutable - /// node pointer. template - Result find(const isc::dns::LabelSequence& target_labels, + Result find(const isc::dns::LabelSequence& target_labels_orig, const DomainTreeNode** node, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const - { - const DomainTreeNode* target_node = NULL; - Result ret = find >(target_labels, - &target_node, - node_path, callback, - callback_arg); - if (ret != NOTFOUND) { - *node = target_node; - } - return (ret); - } + CBARG callback_arg) const; //@} /// \brief return the next bigger node in DNSSEC order from a given node @@ -1477,11 +1441,11 @@ DomainTree::deleteHelper(util::MemorySegment& mem_sgmt, } template -template +template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - const NodeType** target, - DomainTreeNodeChain& node_path, + const DomainTreeNode** target, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { @@ -1491,7 +1455,7 @@ DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, " and label sequence"); } - NodeType* node; + const DomainTreeNode* node; if (!node_path.isEmpty()) { // Get the top node in the node chain From 7a628baa1a158b5837d6f383e10b30542d2ac59b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:03:18 +0200 Subject: [PATCH 039/194] [2292] Drop tests for InMemoryClient::add As the method was removed. --- .../tests/memory/memory_client_unittest.cc | 92 ------------------- 1 file changed, 92 deletions(-) diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc index 58979a4351..6d3f05505c 100644 --- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc +++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc @@ -543,29 +543,6 @@ TEST_F(MemoryClientTest, loadRRSIGs) { EXPECT_EQ(1, client_->getZoneCount()); } -TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - RRsetPtr rrset(new RRset(Name("example.org"), - RRClass::IN(), RRType::A(), RRTTL(3600))); - rrset->addRdata(in::A("192.0.2.1")); - rrset->addRdata(in::A("192.0.2.2")); - - RRsetPtr rrsig(new RRset(Name("example.org"), zclass_, - RRType::RRSIG(), RRTTL(300))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 " - "12345 example.org. FAKEFAKEFAKE")); - rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 " - "54321 example.org. FAKEFAKEFAKEFAKE")); - rrset->addRRsig(rrsig); - - EXPECT_THROW(client_->add(Name("example.org"), rrset), - InMemoryClient::AddError); - - // Teardown checks for memory segment leaks -} - TEST_F(MemoryClientTest, getZoneCount) { EXPECT_EQ(0, client_->getZoneCount()); client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone"); @@ -655,75 +632,6 @@ TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) { EXPECT_THROW(iterator->getSOA(), isc::NotImplemented); } -TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) { - // The zone "example.org" doesn't exist, so we can't add an RRset to - // it. - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError); -} - -TEST_F(MemoryClientTest, addOutOfZoneThrows) { - // Out of zone names should throw. - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-empty.zone"); - - RRsetPtr rrset_a(new RRset(Name("a.example.com"), - RRClass::IN(), RRType::A(), RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), - OutOfZone); - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, addNullRRsetThrows) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()), - InMemoryClient::NullRRset); - - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, addEmptyRRsetThrows) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), - InMemoryClient::AddError); - - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, add) { - client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone"); - - // Add another RRset - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - client_->add(Name("example.org"), rrset_a); - - ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"))); - - // First we have the SOA - ConstRRsetPtr rrset(iterator->getNextRRset()); - EXPECT_TRUE(rrset); - EXPECT_EQ(RRType::A(), rrset->getType()); - - rrset = iterator->getNextRRset(); - EXPECT_TRUE(rrset); - EXPECT_EQ(RRType::SOA(), rrset->getType()); - - // There's nothing else in this zone - EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); -} - TEST_F(MemoryClientTest, findZoneData) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-rrsigs.zone"); From 73900f62a74bf769b5f6ea7b3cf43428baa19842 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:18:33 +0200 Subject: [PATCH 040/194] [2292] Re-add version of find removed by accident This one was not mutable, it just took Name as an argument. --- src/lib/datasrc/memory/domaintree.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 5101c65635..7e68f53bf0 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1102,6 +1102,23 @@ public: return (ret); } + /// \brief Simple find returning immutable node. + /// + /// Acts as described in the \ref find section, but returns immutable + /// node pointer. + template + Result find(const isc::dns::Name& name, + const DomainTreeNode** node, + DomainTreeNodeChain& node_path, + bool (*callback)(const DomainTreeNode&, CBARG), + CBARG callback_arg) const + { + const isc::dns::LabelSequence ls(name); + Result ret = find(ls, node, node_path, callback, + callback_arg); + return (ret); + } + /// \brief Find with callback and node chain /// \anchor callback /// From 00d3de1af86efe6b4df1310a039adad8f6a719ae Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:19:12 +0200 Subject: [PATCH 041/194] [2292] Update tests They don't need the mutable versions to work, but they used them anyway. --- .../tests/memory/domaintree_unittest.cc | 28 +++++++++---------- .../tests/memory/zone_data_unittest.cc | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index cb16e02c79..3754139620 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -256,8 +256,8 @@ TEST_F(DomainTreeTest, subTreeRoot) { // "g.h" is not a subtree root EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("g.h"), &dtnode)); - EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("g.h"), &cdtnode)); + EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); // fission the node "g.h" EXPECT_EQ(TestDomainTree::ALREADYEXISTS, @@ -270,8 +270,8 @@ TEST_F(DomainTreeTest, subTreeRoot) { // "g.h" should be a subtree root now. EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("g.h"), &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("g.h"), &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); } TEST_F(DomainTreeTest, additionalNodeFission) { @@ -286,8 +286,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) { // "t.0" is not a subtree root EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("t.0"), &dtnode)); - EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("t.0"), &cdtnode)); + EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); // fission the node "t.0" EXPECT_EQ(TestDomainTree::ALREADYEXISTS, @@ -300,8 +300,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) { // "t.0" should be a subtree root now. EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("t.0"), &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("t.0"), &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); } TEST_F(DomainTreeTest, findName) { @@ -328,10 +328,10 @@ TEST_F(DomainTreeTest, findName) { EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode)); - // find dtnode + // find cdtnode EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"), - &dtnode)); - EXPECT_EQ(Name("q"), dtnode->getName()); + &cdtnode)); + EXPECT_EQ(Name("q"), cdtnode->getName()); } TEST_F(DomainTreeTest, findError) { @@ -411,11 +411,11 @@ performCallbackTest(TestDomainTree& dtree, Name("example"), &parentdtnode)); // the child/parent nodes shouldn't "inherit" the callback flag. - // "dtnode" may be invalid due to the insertion, so we need to re-find + // "cdtnode" may be invalid due to the insertion, so we need to re-find // it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), - &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); + &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc index d15fe8beb2..3c28cec229 100644 --- a/src/lib/datasrc/tests/memory/zone_data_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc @@ -108,7 +108,7 @@ void checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type, const RdataSet* expected_set) { - ZoneNode* node = NULL; + const ZoneNode* node = NULL; tree.find(name, &node); ASSERT_NE(static_cast(NULL), node); EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type)); From 23da2f078af486c381649611d2987c24a538739a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 10:13:58 -0700 Subject: [PATCH 042/194] [clang-build] explictly define destructor of mostly empty template class. without this new versions of clang++ seem to complain that instantiation of the class results in an unused variable. --- src/lib/util/locks.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h index da9e9cd89c..3b51a5ad44 100644 --- a/src/lib/util/locks.h +++ b/src/lib/util/locks.h @@ -42,13 +42,14 @@ class upgradable_mutex { template class sharable_lock { public: - sharable_lock(T) { } + sharable_lock(T) {} }; template class scoped_lock { public: - scoped_lock(T) { } + scoped_lock(T) {} + ~scoped_lock() {} void lock() {} void unlock() {} From daf81c8846bf0b4e4308068a019c96beb09c5199 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 12:20:34 -0700 Subject: [PATCH 043/194] [clang-build] specify thread flag as part CPPFLAGS, not LDFLAGS. newer version of clang treats the latter as a fatal error. --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c6a2f01ffb..8495dc97a6 100644 --- a/configure.ac +++ b/configure.ac @@ -743,7 +743,8 @@ else done fi -LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus $MULTITHREADING_FLAG" +LOG4CPLUS_INCLUDES="$LOG4CPLUS_INCLUDES $MULTITHREADING_FLAG" +LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus" AC_SUBST(LOG4CPLUS_LIBS) AC_SUBST(LOG4CPLUS_INCLUDES) From 9ea5133b172ccde43427f9658f5be2ba5d497b34 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 12:34:55 -0700 Subject: [PATCH 044/194] [clang-build] removed an effectively unused variable. clang would complain. --- src/lib/datasrc/memory_datasrc_link.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc index cbbc6db4c1..857223f699 100644 --- a/src/lib/datasrc/memory_datasrc_link.cc +++ b/src/lib/datasrc/memory_datasrc_link.cc @@ -129,7 +129,7 @@ checkConfig(ConstElementPtr config, ElementPtr errors) { result = false; } else { try { - RRClass rrc(config->get("class")->stringValue()); + RRClass(config->get("class")->stringValue()); } catch (const isc::Exception& rrce) { addError(errors, "Error parsing class config for memory backend: " + From d62a1e4250c88e765b3cd7f77fe9a948db0a565d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 14:12:02 -0700 Subject: [PATCH 045/194] [clang-build] specify -pthread in CXXFLAGS and via system-indepedent variable. specifying it in LDFLAGS would trigger build error with some versions of clang. hardcoding "-pthread" is less portable. --- src/bin/resolver/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index 47e242c1b4..c56289c08f 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -62,6 +62,7 @@ b10_resolver_SOURCES += common.cc common.h nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h +b10_resolver_CXXFLAGS = $(AM_CPPFLAGS) $(MULTITHREADING_FLAG) b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la @@ -77,7 +78,6 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la -b10_resolver_LDFLAGS = -pthread # TODO: config.h.in is wrong because doesn't honor pkgdatadir # and can't use @datadir@ because doesn't expand default ${prefix} From b211a060119605ebbded0a94f070009b7a8bb5ce Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 21:19:23 -0700 Subject: [PATCH 046/194] [clang-build] Revert "[clang-build] specify -pthread in CXXFLAGS and via system-indepedent variable." This reverts commit d62a1e4250c88e765b3cd7f77fe9a948db0a565d. I believe the sense of the change is correct, but it has effects on other OSes, so it's probably better to use clang specific workaround (will commit it later). --- src/bin/resolver/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index c56289c08f..47e242c1b4 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -62,7 +62,6 @@ b10_resolver_SOURCES += common.cc common.h nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h -b10_resolver_CXXFLAGS = $(AM_CPPFLAGS) $(MULTITHREADING_FLAG) b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la @@ -78,6 +77,7 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la +b10_resolver_LDFLAGS = -pthread # TODO: config.h.in is wrong because doesn't honor pkgdatadir # and can't use @datadir@ because doesn't expand default ${prefix} From e4bd572168b357f9803ce9c5d96ff3a95d935b81 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 21:20:52 -0700 Subject: [PATCH 047/194] [clang-build] Revert "[clang-build] specify thread flag as part CPPFLAGS, not LDFLAGS." This reverts commit daf81c8846bf0b4e4308068a019c96beb09c5199. I believe the sense of the change is correct, but it has effects on other OSes, so it's probably better to use clang specific workaround (will commit it later). --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8495dc97a6..c6a2f01ffb 100644 --- a/configure.ac +++ b/configure.ac @@ -743,8 +743,7 @@ else done fi -LOG4CPLUS_INCLUDES="$LOG4CPLUS_INCLUDES $MULTITHREADING_FLAG" -LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus" +LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus $MULTITHREADING_FLAG" AC_SUBST(LOG4CPLUS_LIBS) AC_SUBST(LOG4CPLUS_INCLUDES) From c629dec43601cd76158bef6aa8b76cea84181fc2 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 23:53:26 -0700 Subject: [PATCH 048/194] [clang-build] added workaround for the clang++ "unused arguments" warn/error. by specifying -Qunused-arguments. Also, as a cleanup for better organization I moved compiler specific settings to earlier part of configure.ac, where this workaround was added. --- configure.ac | 185 +++++++++++++++++++++++++++------------------------ 1 file changed, 98 insertions(+), 87 deletions(-) diff --git a/configure.ac b/configure.ac index c6a2f01ffb..900ff371be 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,104 @@ AC_TRY_LINK([],[], ]) LDFLAGS=$LDFLAGS_SAVED +# Compiler dependent settings: define some mandatory CXXFLAGS here. +# We also use a separate variable B10_CXXFLAGS. This will (and should) be +# used as the default value for each specific AM_CXXFLAGS: +# AM_CXXFLAGS = $(B10_CXXFLAGS) +# AM_CXXFLAGS += ... # add module specific flags +# We need this so that we can disable some specific compiler warnings per +# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since +# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot +# specify the default warning flags in CXXFLAGS and let specific modules +# "override" the default. + +# This may be used to try linker flags. +AC_DEFUN([BIND10_CXX_TRY_FLAG], [ + AC_MSG_CHECKING([whether $CXX supports $1]) + + bind10_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $1" + + AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])], + [bind10_cxx_flag=yes], [bind10_cxx_flag=no]) + CXXFLAGS="$bind10_save_CXXFLAGS" + + if test "x$bind10_cxx_flag" = "xyes"; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , :, [$3]) + fi + + AC_MSG_RESULT([$bind10_cxx_flag]) +]) + +# SunStudio compiler requires special compiler options for boost +# (http://blogs.sun.com/sga/entry/boost_mini_howto) +if test "$SUNCXX" = "yes"; then +CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic" +MULTITHREADING_FLAG="-mt" +fi + +# Newer versions of clang++ promotes "unused driver arguments" warnings to +# a fatal error with -Werror, causing build failure. Since we use multiple +# compilers on multiple systems, this can easily happen due to settings for +# non clang++ environments that could be just ignored otherwise. It can also +# happen if clang++ is used via ccache. So, although probably suboptimal, +# we suppress this particular warning. Note that it doesn't weaken checks +# on the source code. +if test "$CLANGPP" = "yes"; then + B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments" +fi + +BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers], + [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"]) +AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) + +# gcc specific settings: +if test "X$GXX" = "Xyes"; then +B10_CXXFLAGS="$B10_CXXFLAGS -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" +case "$host" in +*-solaris*) + MULTITHREADING_FLAG=-pthreads + # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces + B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces" + ;; +*) + MULTITHREADING_FLAG=-pthread + ;; +esac + +# Don't use -Werror if configured not to +AC_ARG_WITH(werror, + AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]), + [ + case "${withval}" in + yes) with_werror=1 ;; + no) with_werror=0 ;; + *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;; + esac], + [with_werror=1]) + +werror_ok=0 + +# Certain versions of gcc (g++) have a bug that incorrectly warns about +# the use of anonymous name spaces even if they're closed in a single +# translation unit. For these versions we have to disable -Werror. +if test $with_werror = 1; then + CXXFLAGS_SAVED="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror" + AC_MSG_CHECKING(for in-TU anonymous namespace breakage) + AC_TRY_COMPILE([namespace { class Foo {}; } + namespace isc {class Bar {Foo foo_;};} ],, + [AC_MSG_RESULT(no) + werror_ok=1 + B10_CXXFLAGS="$B10_CXXFLAGS -Werror"], + [AC_MSG_RESULT(yes)]) + CXXFLAGS="$CXXFLAGS_SAVED" +fi + +AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) + # allow building programs with static link. we need to make it selective # because loadable modules cannot be statically linked. AC_ARG_ENABLE([static-link], @@ -256,91 +354,6 @@ fi # TODO: check for _sqlite3.py module -# Compiler dependent settings: define some mandatory CXXFLAGS here. -# We also use a separate variable B10_CXXFLAGS. This will (and should) be -# used as the default value for each specific AM_CXXFLAGS: -# AM_CXXFLAGS = $(B10_CXXFLAGS) -# AM_CXXFLAGS += ... # add module specific flags -# We need this so that we can disable some specific compiler warnings per -# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since -# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot -# specify the default warning flags in CXXFLAGS and let specific modules -# "override" the default. - -# This may be used to try linker flags. -AC_DEFUN([BIND10_CXX_TRY_FLAG], [ - AC_MSG_CHECKING([whether $CXX supports $1]) - - bind10_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $1" - - AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])], - [bind10_cxx_flag=yes], [bind10_cxx_flag=no]) - CXXFLAGS="$bind10_save_CXXFLAGS" - - if test "x$bind10_cxx_flag" = "xyes"; then - ifelse([$2], , :, [$2]) - else - ifelse([$3], , :, [$3]) - fi - - AC_MSG_RESULT([$bind10_cxx_flag]) -]) - -# SunStudio compiler requires special compiler options for boost -# (http://blogs.sun.com/sga/entry/boost_mini_howto) -if test "$SUNCXX" = "yes"; then -CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic" -MULTITHREADING_FLAG="-mt" -fi - -BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers], - [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"]) -AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) - -# gcc specific settings: -if test "X$GXX" = "Xyes"; then -B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" -case "$host" in -*-solaris*) - MULTITHREADING_FLAG=-pthreads - # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces - B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces" - ;; -*) - MULTITHREADING_FLAG=-pthread - ;; -esac - -# Don't use -Werror if configured not to -AC_ARG_WITH(werror, - AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]), - [ - case "${withval}" in - yes) with_werror=1 ;; - no) with_werror=0 ;; - *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;; - esac], - [with_werror=1]) - -werror_ok=0 - -# Certain versions of gcc (g++) have a bug that incorrectly warns about -# the use of anonymous name spaces even if they're closed in a single -# translation unit. For these versions we have to disable -Werror. -if test $with_werror = 1; then - CXXFLAGS_SAVED="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror" - AC_MSG_CHECKING(for in-TU anonymous namespace breakage) - AC_TRY_COMPILE([namespace { class Foo {}; } - namespace isc {class Bar {Foo foo_;};} ],, - [AC_MSG_RESULT(no) - werror_ok=1 - B10_CXXFLAGS="$B10_CXXFLAGS -Werror"], - [AC_MSG_RESULT(yes)]) - CXXFLAGS="$CXXFLAGS_SAVED" -fi - # Python 3.2 has an unused parameter in one of its headers. This # has been reported, but not fixed as of yet, so we check if we need # to set -Wno-unused-parameter. @@ -372,8 +385,6 @@ fi fi dnl GXX = yes -AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) - # produce PIC unless we disable shared libraries. need this for python bindings. if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then B10_CXXFLAGS="$B10_CXXFLAGS -fPIC" From 9a9260c2589a8468b73f2ecc16929acd66aeb81c Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 8 Oct 2012 23:40:11 +0530 Subject: [PATCH 049/194] [2266] Remove all traces of RRsetList It is no longer used anywhere. --- src/lib/datasrc/memory/memory_client.cc | 1 - src/lib/datasrc/memory_datasrc.cc | 1 - .../tests/memory/memory_client_unittest.cc | 1 - .../datasrc/tests/memory_datasrc_unittest.cc | 1 - src/lib/dns/Makefile.am | 2 - src/lib/dns/rrsetlist.cc | 60 ------ src/lib/dns/rrsetlist.h | 132 ------------ src/lib/dns/tests/Makefile.am | 2 +- src/lib/dns/tests/rrsetlist_unittest.cc | 188 ------------------ 9 files changed, 1 insertion(+), 387 deletions(-) delete mode 100644 src/lib/dns/rrsetlist.cc delete mode 100644 src/lib/dns/rrsetlist.h delete mode 100644 src/lib/dns/tests/rrsetlist_unittest.cc diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5f6f510306..436f3596d0 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 53cf077d43..686dd94898 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc index 58979a4351..a24941bf17 100644 --- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc +++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index 958c9e12c1..5223d83a1d 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 38809e0921..c6af73d2c2 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -107,7 +107,6 @@ libb10_dns___la_SOURCES += rdatafields.h rdatafields.cc libb10_dns___la_SOURCES += rrclass.cc libb10_dns___la_SOURCES += rrparamregistry.h libb10_dns___la_SOURCES += rrset.h rrset.cc -libb10_dns___la_SOURCES += rrsetlist.h rrsetlist.cc libb10_dns___la_SOURCES += rrttl.h rrttl.cc libb10_dns___la_SOURCES += rrtype.cc libb10_dns___la_SOURCES += question.h question.cc @@ -153,7 +152,6 @@ libdns___include_HEADERS = \ rdata.h \ rrparamregistry.h \ rrset.h \ - rrsetlist.h \ rrttl.h \ tsigkey.h # Purposely not installing these headers: diff --git a/src/lib/dns/rrsetlist.cc b/src/lib/dns/rrsetlist.cc deleted file mode 100644 index fcdcfbb605..0000000000 --- a/src/lib/dns/rrsetlist.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2010 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 - -#include - -#include - -#include -#include -#include -#include - -namespace isc { -namespace dns { - -void -RRsetList::addRRset(RRsetPtr rrsetptr) { - ConstRRsetPtr rrset_found = findRRset(rrsetptr->getType(), - rrsetptr->getClass()); - if (rrset_found != NULL) { - isc_throw(DuplicateRRset, "RRset is being doubly added to RRsetList: " - "type=" << rrsetptr->getType() << ", class=" << - rrsetptr->getClass()); - } - rrsets_.push_back(rrsetptr); -} - -void -RRsetList::append(RRsetList& source) { - BOOST_FOREACH(RRsetPtr rrset, source) { - addRRset(rrset); - } -} - -RRsetPtr -RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) { - BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) { - if ((rrsetptr->getClass() == rrclass) && - (rrsetptr->getType() == rrtype)) { - return (rrsetptr); - } - } - return (RRsetPtr()); -} - -} -} diff --git a/src/lib/dns/rrsetlist.h b/src/lib/dns/rrsetlist.h deleted file mode 100644 index 0e05b5be9c..0000000000 --- a/src/lib/dns/rrsetlist.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) 2010 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. - -#ifndef __RRSETLIST_H -#define __RRSETLIST_H 1 - -#include -#include -#include - -#include - -#include -#include -#include - -namespace isc { -namespace dns { - -class DuplicateRRset : public Exception { -public: - DuplicateRRset(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) {} -}; - -template -class RRsetListIterator : - public std::iterator { -public: - RRsetListIterator() {} - explicit RRsetListIterator(const T& it) : - it_(it) {} - RRsetListIterator& operator++() - { - ++it_; - return (*this); - } - RRsetListIterator operator++(int) - { - RRsetListIterator tmp(*this); - ++it_; - return (tmp); - } - R operator*() const - { - return (*it_); - } - P operator->() const - { - return (&(operator*())); - } - bool operator==(const RRsetListIterator& other) - { - return (it_ == other.it_); - } - bool operator!=(const RRsetListIterator& other) - { - return (it_ != other.it_); - } - -private: - T it_; -}; - -/// A set of RRsets. -/// -/// \note Do not use this class unless you really understand what -/// you're doing and you're 100% sure that this class is the best choice -/// for your purpose. -/// -/// Counter intuitively, this class is not a "list" of RRsets but a -/// "set" of them; it doesn't allow multiple RRsets of the same RR -/// type and RR class to be added at the same time. And, for that -/// reason, adding an RRset is more expensive than you'd expect. The -/// class name is confusing, but was named so as a result of -/// compromise: "RRsetset" would look awkward; RRsets would be -/// confusing (with RRset). -/// -/// In any case, if you want a list like container of RRsets, your best choice -/// would be \c std::vector or \c std::list, not this class. -/// In fact, in many cases \c RRsetList will be a suboptimal choice. -/// This class is defined publicly as part of libdns++ for a historical -/// reason and is actually quite specific to a particular need for libdatasrc. -/// If you are tempted to use it, think twice to assess if this class -/// is really what you want. Again, in many cases the answer will be no. -class RRsetList { -private: - RRsetList(const RRsetList& source); - RRsetList& operator=(const RRsetList& source); -public: - RRsetList() {} - void addRRset(RRsetPtr new_rrsetptr); - void append(RRsetList& source); - RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass); - - typedef RRsetListIterator::iterator, - RRsetPtr*, - RRsetPtr&> iterator; - typedef RRsetListIterator::const_iterator, - const RRsetPtr*, - const RRsetPtr&> const_iterator; - - const_iterator begin() const { return (const_iterator(rrsets_.begin())); } - const_iterator end() const { return (const_iterator(rrsets_.end())); } - - iterator begin() { return (iterator(rrsets_.begin())); } - iterator end() { return (iterator(rrsets_.end())); } - - size_t size() const { return (rrsets_.size()); } - -private: - std::vector rrsets_; -}; - -} // end of namespace dns -} // end of namespace isc -#endif // __RRSETLIST_H - -// Local Variables: -// mode: c++ -// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 0abb3896f1..e8cbe101f5 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -56,7 +56,7 @@ run_unittests_SOURCES += rdata_minfo_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc run_unittests_SOURCES += rdata_naptr_unittest.cc run_unittests_SOURCES += rdata_hinfo_unittest.cc -run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc +run_unittests_SOURCES += rrset_unittest.cc run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc run_unittests_SOURCES += masterload_unittest.cc diff --git a/src/lib/dns/tests/rrsetlist_unittest.cc b/src/lib/dns/tests/rrsetlist_unittest.cc deleted file mode 100644 index 080f88825b..0000000000 --- a/src/lib/dns/tests/rrsetlist_unittest.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (C) 2010 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; -using namespace isc::dns; -using namespace isc::dns::rdata; - -namespace { -class RRsetListTest : public ::testing::Test { -protected: - RRsetListTest() : example_name(Name("example.com")), - example_ttl(RRTTL(3600)) - {} - void setupList(RRsetList& list); - Name example_name; - RRTTL example_ttl; -}; - -const in::A rdata_in_a("192.0.2.1"); -const in::AAAA rdata_in_aaaa("2001:db8::1234"); -const generic::NS rdata_ns("ns.example.com"); -const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"), - 2010012601, 3600, 300, 3600000, 1200); -const generic::CNAME rdata_cname("target.example.com"); -const generic::DNAME rdata_dname("dtarget.example.com"); - -void -RRsetListTest::setupList(RRsetList& list) { - RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(), - RRType::A(), example_ttl)); - RRsetPtr aaaa(new RRset(Name("example.com"), RRClass::IN(), - RRType::AAAA(), example_ttl)); - RRsetPtr ns(new RRset(Name("example.com"), RRClass::IN(), - RRType::NS(), example_ttl)); - RRsetPtr soa(new RRset(Name("example.com"), RRClass::IN(), - RRType::SOA(), example_ttl)); - RRsetPtr cname(new RRset(Name("example.com"), RRClass::IN(), - RRType::CNAME(), example_ttl)); - - a->addRdata(rdata_in_a); - aaaa->addRdata(rdata_in_aaaa); - ns->addRdata(rdata_ns); - soa->addRdata(rdata_soa); - cname->addRdata(rdata_cname); - - list.addRRset(a); - list.addRRset(aaaa); - list.addRRset(ns); - list.addRRset(soa); - list.addRRset(cname); -} - -TEST_F(RRsetListTest, emptyOnInitialCreate) { - RRsetList list; - EXPECT_EQ(list.size(), 0); -} - -TEST_F(RRsetListTest, addRRsets) { - RRsetList list; - setupList(list); - EXPECT_EQ(list.size(), 5); -} - -TEST_F(RRsetListTest, append) { - RRsetList list1; - setupList(list1); - RRsetList list2; - RRsetPtr dname(new RRset(Name("example.com"), RRClass::IN(), - RRType::DNAME(), example_ttl)); - dname->addRdata(rdata_dname); - list2.addRRset(dname); - list1.append(list2); - EXPECT_EQ(list2.size(), 1); - EXPECT_EQ(list1.size(), 6); - - RRsetPtr rrset = list1.findRRset(RRType::DNAME(), RRClass::IN()); - EXPECT_EQ(RRType::DNAME(), rrset->getType()); - - EXPECT_THROW(list1.append(list2), DuplicateRRset); -} - -TEST_F(RRsetListTest, extraRRset) { - RRsetList list; - setupList(list); - RRsetPtr cname(new RRset(Name("another.example.com"), RRClass::IN(), - RRType::CNAME(), example_ttl)); - EXPECT_THROW(list.addRRset(cname), DuplicateRRset); -} - -void -checkFindResult(RRsetList& list, const Name& name, - const RRType& rrtype, const RRClass& rrclass, - const RRTTL& rrttl) -{ - RRsetPtr rrset = list.findRRset(rrtype, rrclass);; - EXPECT_EQ(name, rrset->getName()); - EXPECT_EQ(rrtype, rrset->getType()); - EXPECT_EQ(rrclass, rrset->getClass()); - EXPECT_EQ(rrttl, rrset->getTTL()); -} - -TEST_F(RRsetListTest, findRRset) { - RRsetList list; - setupList(list); - - checkFindResult(list, example_name, RRType::A(), RRClass::IN(), - example_ttl); - checkFindResult(list, example_name, RRType::CNAME(), RRClass::IN(), - example_ttl); - checkFindResult(list, example_name, RRType::AAAA(), RRClass::IN(), - example_ttl); - checkFindResult(list, example_name, RRType::NS(), RRClass::IN(), - example_ttl); - checkFindResult(list, example_name, RRType::SOA(), RRClass::IN(), - example_ttl); -} - -TEST_F(RRsetListTest, checkData) { - RRsetList list; - RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(), - RRType::A(), example_ttl)); - a->addRdata(rdata_in_a); - list.addRRset(a); - - RdataIteratorPtr it = - list.findRRset(RRType::A(), RRClass::IN())->getRdataIterator(); - EXPECT_FALSE(it->isLast()); - EXPECT_EQ("192.0.2.1", it->getCurrent().toText()); -} - -TEST_F(RRsetListTest, iterate) { - RRsetList list; - setupList(list); - - bool has_a = false, has_aaaa = false, has_ns = false, has_soa = false, - has_cname = false; - int i = 0; - BOOST_FOREACH(RRsetPtr rrset, list) { - if (rrset->getType() == RRType::A()) { - has_a = true; - } - if (rrset->getType() == RRType::AAAA()) { - has_aaaa = true; - } - if (rrset->getType() == RRType::NS()) { - has_ns = true; - } - if (rrset->getType() == RRType::SOA()) { - has_soa = true; - } - if (rrset->getType() == RRType::CNAME()) { - has_cname = true; - } - ++i; - } - EXPECT_TRUE(has_a); - EXPECT_TRUE(has_aaaa); - EXPECT_TRUE(has_ns); - EXPECT_TRUE(has_soa); - EXPECT_TRUE(has_cname); - EXPECT_TRUE(i == 5); -} - -} From 869d14d7056ee7b0c3cf1dd0db1b375be755bba7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 11:32:18 -0700 Subject: [PATCH 050/194] [2292] minor cleanups: constify, indentation --- src/lib/datasrc/memory/memory_client.cc | 10 +++++----- src/lib/datasrc/tests/memory/domaintree_unittest.cc | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index de953ca107..a5824fa63b 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -627,11 +627,11 @@ InMemoryClient::InMemoryClientImpl::load( // node must point to a valid node now assert(node != NULL); - std::string* tstr = node->setData(new std::string(filename)); + const std::string* tstr = node->setData(new std::string(filename)); delete tstr; - result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, holder)); + const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, holder)); if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. @@ -726,8 +726,8 @@ InMemoryClient::load(const isc::dns::Name& zone_name, const std::string InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { const FileNameNode* node(NULL); - FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, - &node); + const FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, + &node); if (result == FileNameTree::EXACTMATCH) { return (*node->getData()); } else { diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index 3754139620..81fa576b21 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -414,7 +414,7 @@ performCallbackTest(TestDomainTree& dtree, // "cdtnode" may be invalid due to the insertion, so we need to re-find // it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), - &cdtnode)); + &cdtnode)); EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); From 04db154ec2fdd1d768fe716b0ccabaa1660e872b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 15:49:24 -0700 Subject: [PATCH 051/194] [2244] fixed a typo in a log message. --- src/bin/bind10/bind10_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index dd36ef96e3..2f48325498 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -147,7 +147,7 @@ unexpectedly, but the boss then found that the component had been removed from its local configuration of components to run. This is an unusal situation but can happen if the administrator removes the component from the configuration after the component's crash and before the restart time. -The boss module simply skipped restarting that module, and the whole syste +The boss module simply skipped restarting that module, and the whole system went back to the expected state (except that the crash itself is likely to be a bug). From 7b053fd99e6503d3a0a8fb7dbb9d3139df942565 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 16:14:16 -0700 Subject: [PATCH 052/194] [2244] renamed BaseComponent.is_failed() to is_restarting(). so it sounds more natural as an English term. --- src/lib/python/isc/bind10/component.py | 14 +++++++------- src/lib/python/isc/bind10/tests/component_test.py | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 6b96b943e9..1f7006cb57 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -45,7 +45,7 @@ COMPONENT_RESTART_DELAY = 10 STATE_DEAD = 'dead' STATE_STOPPED = 'stopped' -STATE_FAILED = 'failed' +STATE_RESTARTING = 'restarting' STATE_RUNNING = 'running' def get_signame(signal_number): @@ -69,7 +69,7 @@ class BaseComponent: explicitly). - Running - after start() was called, it started successfully and is now running. - - Failed - the component failed (crashed) and is waiting for a restart + - Restarting - the component failed (crashed) and is waiting for a restart - Dead - it failed and can not be resurrected. Init @@ -85,7 +85,7 @@ class BaseComponent: +<-----------+ | | | | kind == dispensable or kind|== needed and failed late - +-----------------------> Failed + +-----------------------> Restarting | | kind == core or kind == needed and it failed too soon v @@ -238,7 +238,7 @@ class BaseComponent: exit_str) if not self.is_running(): raise ValueError("Can't fail component that isn't running") - self.__state = STATE_FAILED + self.__state = STATE_RESTARTING # tentatively set, maybe changed to DEAD self._failed_internal() # If it is a core component or the needed component failed to start # (including it stopped really soon) @@ -298,14 +298,14 @@ class BaseComponent: """ return self.__state == STATE_RUNNING - def is_failed(self): + def is_restarting(self): """Informs if the component has failed and is waiting for a restart. Unlike the case of is_running(), if this returns True it always means the corresponding process has died and not yet restarted. """ - return self.__state == STATE_FAILED + return self.__state == STATE_RESTARTING def _start_internal(self): """ @@ -609,7 +609,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.is_running() or component.is_failed(): + if component.is_running() or component.is_restarting(): plan.append({ 'command': STOP_CMD, 'component': component, diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 339a9399ca..18efea7451 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -192,7 +192,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) self.assertRaises(ValueError, component.failed, 1) @@ -206,7 +206,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertTrue(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) def __check_dead(self, component): """ @@ -218,7 +218,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) # Nor started @@ -238,7 +238,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertTrue(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -251,7 +251,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertFalse(component.is_running()) - self.assertTrue(component.is_failed()) + self.assertTrue(component.is_restarting()) def __do_start_stop(self, kind): """ @@ -276,7 +276,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) # Or failed From 4e159ffca0b633c817472101087be4a235a42af0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 16:56:10 -0700 Subject: [PATCH 053/194] [2204] updated comments about releasing old data source client lists the previous one was not really accurate and was confusing. --- src/bin/auth/main.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 1e3ca9c3de..99080662ff 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -109,17 +109,16 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, lists = configureDataSource(config->get("classes")); } - // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. Lock will be released immediately after the - // swap. + // Replace the server's lists. The returned lists will be stored + // in a local variable 'lists', and will be destroyed outside of + // the temporary block for the lock scope. That way we can minimize + // the range of the critical section. { isc::util::thread::Mutex::Locker locker( server->getDataSrcClientListMutex()); lists = server->swapDataSrcClientLists(lists); } - // The previous lists are destroyed here. Note that it's outside - // of the critical section protected by the locker. So this can - // take time if running on a separate thread. + // The previous lists are destroyed here. } } From 3a68ea2e8de8d6df97d95c077ff9e2874ca0d9c3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 17:27:23 -0700 Subject: [PATCH 054/194] [2204] updated test comments without using "rollback". as the new code actually doesn't do rollback operation internally, even though the observable effect is the same. --- src/bin/auth/tests/datasrc_config_unittest.cc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 97d89bf2bb..877f92166f 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -244,10 +244,11 @@ TEST_F(DatasrcConfigTest, updateDelete) { EXPECT_TRUE(lists_.empty()); } -// Check that we can rollback an addition if something else fails -TEST_F(DatasrcConfigTest, rollbackAddition) { +// Check that broken new configuration doesn't break the running configuration. +TEST_F(DatasrcConfigTest, brokenConfigForAdd) { initializeINList(); - // The configuration is wrong. However, the CH one will get done first. + // The configuration is wrong. However, the CH one will be handled + // without an error first. const ElementPtr config(buildConfig("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"xxx\"}]}")); @@ -263,8 +264,9 @@ TEST_F(DatasrcConfigTest, rollbackAddition) { EXPECT_FALSE(lists_[RRClass::CH()]); } -// Check that we can rollback a deletion if something else fails -TEST_F(DatasrcConfigTest, rollbackDeletion) { +// Similar to the previous one, but the broken config would delete part of +// the running config. +TEST_F(DatasrcConfigTest, brokenConfigForDelete) { initializeINList(); // Put the CH there const ElementPtr @@ -273,27 +275,25 @@ TEST_F(DatasrcConfigTest, rollbackDeletion) { testConfigureDataSource(*this, config1); const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}")); - // This would delete CH. However, the IN one fails. - // As the deletions happen after the additions/settings - // and there's no known way to cause an exception during the - // deletions, it is not a true rollback, but the result should - // be the same. + // This would delete CH. However, the new config is broken, so it won't + // actually apply. EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } -// Check that we can roll back configuration change if something -// fails later on. -TEST_F(DatasrcConfigTest, rollbackConfiguration) { +// Similar to the previous cases, but the broken config would modify the +// running config of one of the classes. +TEST_F(DatasrcConfigTest, brokenConfigForModify) { initializeINList(); // Put the CH there const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); testConfigureDataSource(*this, config1); - // Now, the CH happens first. But nevertheless, it should be - // restored to the previoeus version. + // Now, the CH change will be handled first without an error, then + // the change to the IN class will fail, and the none of the changes + // will apply. const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"yyy\"}]}")); From 1e93f1db983cc35b118853b88ab2919decafe2c5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 17:29:15 -0700 Subject: [PATCH 055/194] [2204] grammar fix in documentation --- src/bin/auth/datasrc_config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 842d2ca927..02e4e5c45e 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -27,9 +27,9 @@ /// \brief Configure data source client lists /// -/// This will hook into the data_sources module configuration and it return -/// a new set (in the form of a shared pointer to map) of data source client -/// lists corresponding to the configuration. +/// This will hook into the data_sources module configuration and it will +/// return a new set (in the form of a shared pointer to map) of data source +/// client lists corresponding to the configuration. /// /// This function is templated. This is simply because of easier testing. /// You don't need to pay attention to it, use the configureDataSource From 7888a2127db48e004cb1cce24552db230cb3d685 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 13:39:30 +0200 Subject: [PATCH 056/194] [2292] Don't pass SegmentObjectHolder It is supposed to be mostly private. Use it internally only. --- src/lib/datasrc/memory/memory_client.cc | 3 +- src/lib/datasrc/memory/zone_table.cc | 10 +++++-- src/lib/datasrc/memory/zone_table.h | 11 +++---- .../tests/memory/zone_table_unittest.cc | 29 ++++++++++++------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index a5824fa63b..e33a0f32e9 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -631,7 +631,8 @@ InMemoryClient::InMemoryClientImpl::load( delete tstr; const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, holder)); + zone_name, + holder.release())); if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 0e6ed3c4ed..4a1f184974 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -69,9 +69,13 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, result::Result ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, - const Name& zone_name, - SegmentObjectHolder& content) + const Name& zone_name, ZoneData* content) { + if (content == NULL) { + isc_throw(isc::BadValue, "Zone content must not be NULL"); + } + SegmentObjectHolder holder(mem_sgmt, content, + zone_class); // Get the node where we put the zone ZoneTableNode* node(NULL); switch (zones_->insert(mem_sgmt, zone_name, &node)) { @@ -87,7 +91,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, assert(node != NULL); // We can release now, setData never throws - ZoneData* old = node->setData(content.release()); + ZoneData* old = node->setData(holder.release()); if (old != NULL) { ZoneData::destroy(mem_sgmt, old, zone_class); return (result::EXIST); diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 4f309b7c2e..5ade3ac963 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -36,10 +36,6 @@ namespace memory { // forward declaration: in this header it's mostly an opaque type. class ZoneData; -namespace detail { -template class SegmentObjectHolder; -} - /// \brief A conceptual table of authoritative zones. /// /// This class is actually a simple wrapper for a \c DomainTree whose data is @@ -147,7 +143,9 @@ public: /// \param zone_class The RR class of the zone. It must be the RR class /// that is supposed to be associated to the zone table. /// \param content This one should hold the zone content (the ZoneData). - /// When it is added successfully, it is released from the holder. + /// The ownership is passed onto the zone table. Must not be null. + /// Must correspond to the name and class and must be allocated from + /// mem_sgmt. /// \return \c result::SUCCESS If the zone is successfully /// added to the zone table. /// \return \c result::EXIST The zone table already contained @@ -156,8 +154,7 @@ public: result::Result addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class, const dns::Name& zone_name, - detail::SegmentObjectHolder& content); + ZoneData* content); /// Find a zone that best matches the given name in the \c ZoneTable. /// diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index b9ee3d9441..64ec312c02 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -89,11 +89,15 @@ TEST_F(ZoneTableTest, create) { } TEST_F(ZoneTableTest, addZone) { + // It doesn't accept empty (NULL) zones + EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, zname1, NULL), + isc::BadValue); + SegmentObjectHolder holder1( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); // Normal successful case. EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder1)); + zname1, holder1.release())); // It got released by it EXPECT_EQ(NULL, holder1.get()); @@ -101,7 +105,7 @@ TEST_F(ZoneTableTest, addZone) { SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder2)); + zname1, holder2.release())); // It releases this one even when we replace the old zone EXPECT_EQ(NULL, holder2.get()); @@ -111,16 +115,18 @@ TEST_F(ZoneTableTest, addZone) { // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, Name("EXAMPLE.COM"), - holder3)); + holder3.release())); // Add some more different ones. Should just succeed. SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2, holder4)); + zone_table->addZone(mem_sgmt_, zclass_, zname2, + holder4.release())); SegmentObjectHolder holder5( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3, holder5)); + zone_table->addZone(mem_sgmt_, zclass_, zname3, + holder5.release())); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected @@ -129,7 +135,7 @@ TEST_F(ZoneTableTest, addZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_); mem_sgmt_.setThrowCount(1); EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"), - holder6), + holder6.release()), std::bad_alloc); } @@ -138,15 +144,17 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); ZoneData* zone_data = holder1.get(); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, - holder1)); + holder1.release())); SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2, holder2)); + zone_table->addZone(mem_sgmt_, zclass_, zname2, + holder2.release())); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3, holder3)); + zone_table->addZone(mem_sgmt_, zclass_, zname3, + holder3.release())); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); @@ -170,7 +178,8 @@ TEST_F(ZoneTableTest, findZone) { SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - Name("com"), holder4)); + Name("com"), + holder4.release())); EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } From 3a6ee0d12d06c1f9e5b9f261b3fcaab444a9ae80 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 14:07:13 +0200 Subject: [PATCH 057/194] [2292] Pass the old zone back Instead of releasing it directly. While the internal release was more convenient, it didn't allow for swapping things fast under a mutex and then spending the time releasing it unlocked. --- src/lib/datasrc/memory/memory_client.cc | 14 ++++--- src/lib/datasrc/memory/zone_table.cc | 7 ++-- src/lib/datasrc/memory/zone_table.h | 20 +++++++--- .../tests/memory/zone_table_unittest.cc | 38 ++++++++++++------- 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index e33a0f32e9..16096114fc 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -630,16 +630,20 @@ InMemoryClient::InMemoryClientImpl::load( const std::string* tstr = node->setData(new std::string(filename)); delete tstr; - const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, - holder.release())); - if (result == result::SUCCESS) { + const ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, + holder.release())); + if (result.code == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. ++zone_count_; } + // Destroy the old instance of the zone if there was any + if (result.zone_data != NULL) { + ZoneData::destroy(mem_sgmt_, result.zone_data, rrclass_); + } - return (result); + return (result.code); } namespace { diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 4a1f184974..836b020088 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -67,7 +67,7 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, mem_sgmt.deallocate(ztable, sizeof(ZoneTable)); } -result::Result +ZoneTable::AddResult ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, const Name& zone_name, ZoneData* content) { @@ -93,10 +93,9 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, // We can release now, setData never throws ZoneData* old = node->setData(holder.release()); if (old != NULL) { - ZoneData::destroy(mem_sgmt, old, zone_class); - return (result::EXIST); + return (AddResult(result::EXIST, old)); } else { - return (result::SUCCESS); + return (AddResult(result::SUCCESS, NULL)); } } diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 5ade3ac963..024558eb30 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -74,6 +74,14 @@ private: typedef DomainTreeNode ZoneTableNode; public: + /// \brief Result data of addZone() method. + struct AddResult { + AddResult(result::Result param_code, ZoneData* param_zone_data) : + code(param_code), zone_data(param_zone_data) + {} + const result::Result code; + ZoneData* const zone_data; + }; /// \brief Result data of findZone() method. struct FindResult { @@ -149,12 +157,12 @@ public: /// \return \c result::SUCCESS If the zone is successfully /// added to the zone table. /// \return \c result::EXIST The zone table already contained - /// zone of the same origin. The old data is released and replaced - /// by the new one. - result::Result addZone(util::MemorySegment& mem_sgmt, - dns::RRClass zone_class, - const dns::Name& zone_name, - ZoneData* content); + /// zone of the same origin. The old data is replaced and returned + /// inside the result. + AddResult addZone(util::MemorySegment& mem_sgmt, + dns::RRClass zone_class, + const dns::Name& zone_name, + ZoneData* content); /// Find a zone that best matches the given name in the \c ZoneTable. /// diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 64ec312c02..9cf1b3420c 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -95,38 +95,50 @@ TEST_F(ZoneTableTest, addZone) { SegmentObjectHolder holder1( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); + const ZoneData* data1(holder1.get()); // Normal successful case. - EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder1.release())); + const ZoneTable::AddResult result1(zone_table->addZone(mem_sgmt_, zclass_, + zname1, + holder1.release())); + EXPECT_EQ(result::SUCCESS, result1.code); + EXPECT_EQ(NULL, result1.zone_data); // It got released by it EXPECT_EQ(NULL, holder1.get()); // Duplicate add doesn't replace the existing data. SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder2.release())); + const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_, + zname1, + holder2.release())); + EXPECT_EQ(result::EXIST, result2.code); + // The old one gets out + EXPECT_EQ(data1, result2.zone_data); // It releases this one even when we replace the old zone EXPECT_EQ(NULL, holder2.get()); + // We need to release the old one manually + ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")), zclass_); // names are compared in a case insensitive manner. - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - Name("EXAMPLE.COM"), - holder3.release())); + const ZoneTable::AddResult result3(zone_table->addZone(mem_sgmt_, zclass_, + Name("EXAMPLE.COM"), + holder3.release())); + EXPECT_EQ(result::EXIST, result3.code); + ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_); // Add some more different ones. Should just succeed. SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname2, - holder4.release())); + holder4.release()).code); SegmentObjectHolder holder5( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname3, - holder5.release())); + holder5.release()).code); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected @@ -144,17 +156,17 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); ZoneData* zone_data = holder1.get(); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, - holder1.release())); + holder1.release()).code); SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname2, - holder2.release())); + holder2.release()).code); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname3, - holder3.release())); + holder3.release()).code); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); @@ -179,7 +191,7 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, Name("com"), - holder4.release())); + holder4.release()).code); EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } From d78dd497ecb846ab2545ce1cb7b6e41341ff5995 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 14:11:25 +0200 Subject: [PATCH 058/194] [2292] Clarify test And make it slightly stronger (it now checks something that should be mostly obvious too, but who knows, with software). --- src/lib/datasrc/tests/memory/domaintree_unittest.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index 81fa576b21..45e256a0b4 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -411,10 +411,11 @@ performCallbackTest(TestDomainTree& dtree, Name("example"), &parentdtnode)); // the child/parent nodes shouldn't "inherit" the callback flag. - // "cdtnode" may be invalid due to the insertion, so we need to re-find - // it. + // "dtnode" should still validly point to "callback.example", but we + // explicitly confirm it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), &cdtnode)); + ASSERT_EQ(dtnode, cdtnode); EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); From ebfe8a5ab083e3ec1ead648709bfba801fb617a5 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 9 Oct 2012 14:54:09 +0200 Subject: [PATCH 059/194] [2237] Changes after review. --- src/lib/dhcp/addr_utilities.cc | 43 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc index 0396d1a0b7..1abc825841 100644 --- a/src/lib/dhcp/addr_utilities.cc +++ b/src/lib/dhcp/addr_utilities.cc @@ -18,18 +18,24 @@ using namespace isc::asiolink; -const static uint32_t masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, - 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, - 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, - 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, - 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, - 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, - 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, - 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, - 0xffffffff }; +namespace { -const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; +/// @brief mask used for first/last address calculation in a IPv4 prefix +/// +/// Using a static mask is faster than calculating it dynamically every time. +const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, + 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, + 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, + 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, + 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, + 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, + 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, + 0x0000000f, 0x00000007, 0x00000003, 0x00000001, + 0x00000000 }; +/// @brief mask used for first/last address calculation in a IPv6 prefix +const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; +} namespace isc { namespace dhcp { @@ -49,7 +55,7 @@ isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& pref // Get the appropriate mask. It has relevant bits (those that should // stay) set and irrelevant (those that should be wiped) cleared. - uint8_t mask = bitMask[len % 8]; + uint8_t mask = bitMask6[len % 8]; // Let's leave only whatever the mask says should not be cleared. packed[len / 8] = packed[len / 8] & mask; @@ -68,14 +74,18 @@ isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& pref return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } +/// @brief IPv4 version of firstAddrInPrefix4 +/// +/// @param prefix IPv4 prefix that we calculate first address for +/// @param len netmask length (0-32) isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix, uint8_t len) { uint32_t addr = prefix; - if (len>32) { + if (len > 32) { isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); } - return (IOAddress(addr & (~masks[32-len]))); + return (IOAddress(addr & (~bitMask4[len]))); } isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, @@ -94,13 +104,12 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); } - return (IOAddress(addr | masks[32-len])); + return (IOAddress(addr | bitMask4[len])); } isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix, uint8_t len) { - const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; uint8_t packed[V6ADDRESS_LEN]; // First we copy the whole address as 16 bytes. @@ -112,10 +121,10 @@ isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefi if (len % 8 != 0) { // Get the appropriate mask. It has relevant bits (those that should // stay) set and irrelevant (those that should be set to 1) cleared. - uint8_t mask = bitMask[len % 8]; + uint8_t mask = bitMask6[len % 8]; // Let's set those irrelevant bits with 1. It would be perhaps - // easier to not use negation here and invert bitMask content. However, + // easier to not use negation here and invert bitMask6 content. However, // with this approach, we can use the same mask in first and last // address calculations. packed[len / 8] = packed[len / 8] | ~mask; From 5e39872cc6dc260c91596ee31f1601dbf53de503 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 9 Oct 2012 16:08:25 +0200 Subject: [PATCH 060/194] [2237] Tests for addr utilities added, other minor fixes after review. --- src/lib/dhcp/addr_utilities.cc | 28 +++++++- src/lib/dhcp/tests/addr_utilities_unittest.cc | 65 ++++++++++++++++++- src/lib/dhcp/tests/cfgmgr_unittest.cc | 5 ++ 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc index 1abc825841..bf9889a827 100644 --- a/src/lib/dhcp/addr_utilities.cc +++ b/src/lib/dhcp/addr_utilities.cc @@ -40,6 +40,13 @@ const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; namespace isc { namespace dhcp { +/// @brief calculates the first IPv6 address in a IPv6 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv6 prefix +/// @param len prefix length isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix, uint8_t len) { @@ -74,9 +81,12 @@ isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& pref return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } -/// @brief IPv4 version of firstAddrInPrefix4 +/// @brief calculates the first IPv4 address in a IPv4 prefix /// -/// @param prefix IPv4 prefix that we calculate first address for +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv4 prefix /// @param len netmask length (0-32) isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix, uint8_t len) { @@ -97,6 +107,13 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi } } +/// @brief calculates the last IPv4 address in a IPv4 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use firstAddrInPrefix() instead. +/// +/// @param prefix IPv4 prefix that we calculate first address for +/// @param len netmask length (0-32) isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix, uint8_t len) { uint32_t addr = prefix; @@ -107,6 +124,13 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi return (IOAddress(addr | bitMask4[len])); } +/// @brief calculates the last IPv6 address in a IPv6 prefix +/// +/// Note: This is a private function. Do not use it directly. +/// Please use lastAddrInPrefix() instead. +/// +/// @param prefix IPv6 prefix that we calculate first address for +/// @param len netmask length (0-128) isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix, uint8_t len) { diff --git a/src/lib/dhcp/tests/addr_utilities_unittest.cc b/src/lib/dhcp/tests/addr_utilities_unittest.cc index 838282739f..2ea4e2adfa 100644 --- a/src/lib/dhcp/tests/addr_utilities_unittest.cc +++ b/src/lib/dhcp/tests/addr_utilities_unittest.cc @@ -26,7 +26,67 @@ using namespace std; using namespace isc::dhcp; using namespace isc::asiolink; -TEST(Pool6Test, lastAddrInPrefix) { +// This test verifies that lastAddrInPrefix is able to handle IPv4 operations. +TEST(AddrUtilitiesTest, lastAddrInPrefix4) { + IOAddress addr1("192.0.2.1"); + + // Prefixes rounded to addresses are easy... + EXPECT_EQ("192.255.255.255", lastAddrInPrefix(addr1, 8).toText()); + EXPECT_EQ("192.0.255.255", lastAddrInPrefix(addr1, 16).toText()); + EXPECT_EQ("192.0.2.255", lastAddrInPrefix(addr1, 24).toText()); + + // these are trickier + EXPECT_EQ("192.0.2.127", lastAddrInPrefix(addr1, 25).toText()); + EXPECT_EQ("192.0.2.63", lastAddrInPrefix(addr1, 26).toText()); + EXPECT_EQ("192.0.2.31", lastAddrInPrefix(addr1, 27).toText()); + EXPECT_EQ("192.0.2.15", lastAddrInPrefix(addr1, 28).toText()); + EXPECT_EQ("192.0.2.7", lastAddrInPrefix(addr1, 29).toText()); + EXPECT_EQ("192.0.2.3", lastAddrInPrefix(addr1, 30).toText()); + + // that doesn't make much sense as /31 subnet consists of network address + // and a broadcast address, with 0 usable addresses. + EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 31).toText()); + EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 32).toText()); + + // Let's check extreme cases + IOAddress anyAddr("0.0.0.0"); + EXPECT_EQ("127.255.255.255", lastAddrInPrefix(anyAddr, 1).toText()); + EXPECT_EQ("255.255.255.255", lastAddrInPrefix(anyAddr, 0).toText()); + EXPECT_EQ("0.0.0.0", lastAddrInPrefix(anyAddr, 32).toText()); +} + +// This test checks if firstAddrInPrefix is able to handle IPv4 operations. +TEST(AddrUtilitiesTest, firstAddrInPrefix4) { + IOAddress addr1("192.223.2.255"); + + // Prefixes rounded to addresses are easy... + EXPECT_EQ("192.0.0.0", firstAddrInPrefix(addr1, 8).toText()); + EXPECT_EQ("192.223.0.0", firstAddrInPrefix(addr1, 16).toText()); + EXPECT_EQ("192.223.2.0", firstAddrInPrefix(addr1, 24).toText()); + + // these are trickier + EXPECT_EQ("192.223.2.128", firstAddrInPrefix(addr1, 25).toText()); + EXPECT_EQ("192.223.2.192", firstAddrInPrefix(addr1, 26).toText()); + EXPECT_EQ("192.223.2.224", firstAddrInPrefix(addr1, 27).toText()); + EXPECT_EQ("192.223.2.240", firstAddrInPrefix(addr1, 28).toText()); + EXPECT_EQ("192.223.2.248", firstAddrInPrefix(addr1, 29).toText()); + EXPECT_EQ("192.223.2.252", firstAddrInPrefix(addr1, 30).toText()); + + // that doesn't make much sense as /31 subnet consists of network address + // and a broadcast address, with 0 usable addresses. + EXPECT_EQ("192.223.2.254", firstAddrInPrefix(addr1, 31).toText()); + EXPECT_EQ("192.223.2.255", firstAddrInPrefix(addr1, 32).toText()); + + // Let's check extreme cases. + IOAddress bcast("255.255.255.255"); + EXPECT_EQ("128.0.0.0", firstAddrInPrefix(bcast, 1).toText()); + EXPECT_EQ("0.0.0.0", firstAddrInPrefix(bcast, 0).toText()); + EXPECT_EQ("255.255.255.255", firstAddrInPrefix(bcast, 32).toText()); + +} + +/// This test checks if lastAddrInPrefix properly supports IPv6 operations +TEST(AddrUtilitiesTest, lastAddrInPrefix6) { IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef"); // Prefixes rounded to nibbles are easy... @@ -63,7 +123,8 @@ TEST(Pool6Test, lastAddrInPrefix) { EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText()); } -TEST(Pool6Test, firstAddrInPrefix) { +/// This test checks if firstAddrInPrefix properly supports IPv6 operations +TEST(AddrUtilitiesTest, firstAddrInPrefix6) { IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef"); // Prefixes rounded to nibbles are easy... diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc index 75e60010aa..dbe0fc606d 100644 --- a/src/lib/dhcp/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc @@ -51,11 +51,16 @@ TEST(CfgMgrTest, subnet4) { // Now we have only one subnet, any request will be served from it EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63"))); + // Now we add more subnets and check that both old and new subnets + // are accessible. cfg_mgr.addSubnet4(subnet2); cfg_mgr.addSubnet4(subnet3); EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191"))); + EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15"))); EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85"))); + + // Try to find an address that does not belong to any subnet EXPECT_EQ(Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.192"))); } From 1517f32c9e68baacfc68c2aab412b29045d846b9 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 9 Oct 2012 19:13:29 +0200 Subject: [PATCH 061/194] [2269] Changes in Dhcp6 config parser after review. --- doc/guide/bind10-guide.xml | 12 +- src/bin/auth/auth_config.h | 2 +- src/bin/dhcp6/config_parser.cc | 113 ++++++++++-------- src/bin/dhcp6/config_parser.h | 64 ++-------- src/bin/dhcp6/ctrl_dhcp6_srv.cc | 3 +- src/bin/dhcp6/tests/config_parser_unittest.cc | 68 +++++++---- 6 files changed, 129 insertions(+), 133 deletions(-) diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml index 2e79952b56..8b62e2ea44 100644 --- a/doc/guide/bind10-guide.xml +++ b/doc/guide/bind10-guide.xml @@ -2911,7 +2911,7 @@ Dhcp6/subnet6 [] list (default) > config add Dhcp6/subnet6 > config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64" -> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff" ] +> config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::0 - 2001:db8:1::ffff" ] > config commit Note that subnet is defined as a simple string, but the pool parameter is actually a list of pools: for this reason, the pool definition is @@ -2946,11 +2946,19 @@ Dhcp6/subnet6 [] list (default) very wasteful, it is certainly a valid configuration to dedicate the whole /48 subnet for that purpose. + + When configuring a DHCPv6 server using prefix/length notation, please pay + attention to the boundary values. When specifying that the server should use + a given pool, it will be able to allocate also first (typically network + address) address from that pool. For example for pool 2001:db8::/64 the + 2001:db8:: address may be assigned as well. If you want to avoid this, + please use min-max notation. + Note: Although configuration is now accepted, it is not internally used by they server yet. At this stage of development, the only way to alter - server configuration is to tweak its source code. To do so, please edit + server configuration is to modify its source code. To do so, please edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following parameters and recompile: diff --git a/src/bin/auth/auth_config.h b/src/bin/auth/auth_config.h index 6f18810f7f..8e816a3fb4 100644 --- a/src/bin/auth/auth_config.h +++ b/src/bin/auth/auth_config.h @@ -93,7 +93,7 @@ public: /// that corresponds to this derived class and prepares a new value to /// apply to the server. /// In the above example, the derived class for the identifier "param1" - /// would be passed an data \c Element storing an integer whose value + /// would be passed a data \c Element storing an integer whose value /// is 10, and would record that value internally; /// the derived class for the identifier "param2" would be passed a /// map element and (after parsing) convert it into some internal diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index 6c6c937dca..9f75cd72f5 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -43,7 +43,7 @@ namespace dhcp { typedef pair ConfigPair; /// @brief a factory method that will create a parser for a given element name -typedef Dhcp6ConfigParser* ParserFactory(const std::string& config_id); +typedef DhcpConfigParser* ParserFactory(const std::string& config_id); /// @brief a collection of factories that creates parsers for specified element names typedef std::map FactoryMap; @@ -72,21 +72,21 @@ StringStorage string_defaults; /// will accept any configuration and will just print it out /// on commit. Useful for debugging existing configurations and /// adding new ones. -class DummyParser : public Dhcp6ConfigParser { +class DebugParser : public DhcpConfigParser { public: /// @brief Constructor /// - /// See \ref Dhcp6ConfigParser class for details. + /// See \ref DhcpConfigParser class for details. /// /// @param param_name name of the parsed parameter - DummyParser(const std::string& param_name) + DebugParser(const std::string& param_name) :param_name_(param_name) { } /// @brief builds parameter value /// - /// See \ref Dhcp6ConfigParser class for details. + /// See \ref DhcpConfigParser class for details. /// /// @param new_config pointer to the new configuration virtual void build(ConstElementPtr new_config) { @@ -100,20 +100,20 @@ public: /// This is a method required by base class. It pretends to apply the /// configuration, but in fact it only prints the parameter out. /// - /// See \ref Dhcp6ConfigParser class for details. + /// See \ref DhcpConfigParser class for details. virtual void commit() { - // Debug message. The whole DummyParser class is used only for parser + // Debug message. The whole DebugParser class is used only for parser // debugging, and is not used in production code. It is very convenient // to keep it around. Please do not turn this cout into logger calls. std::cout << "Commit for token: [" << param_name_ << "] = [" << value_->str() << "]" << std::endl; } - /// @brief factory that constructs DummyParser objects + /// @brief factory that constructs DebugParser objects /// /// @param param_name name of the parameter to be parsed - static Dhcp6ConfigParser* Factory(const std::string& param_name) { - return (new DummyParser(param_name)); + static DhcpConfigParser* Factory(const std::string& param_name) { + return (new DebugParser(param_name)); } protected: @@ -131,11 +131,11 @@ protected: /// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters /// in subnet config), it can be pointed to a different storage, using /// setStorage() method. This class follows the parser interface, laid out -/// in its base class, \ref Dhcp6ConfigParser. +/// in its base class, \ref DhcpConfigParser. /// /// For overview of usability of this generic purpose parser, see /// \ref dhcpv6-config-inherit page. -class Uint32Parser : public Dhcp6ConfigParser { +class Uint32Parser : public DhcpConfigParser { public: /// @brief constructor for Uint32Parser @@ -174,7 +174,7 @@ public: /// @brief factory that constructs Uint32Parser objects /// /// @param param_name name of the parameter to be parsed - static Dhcp6ConfigParser* Factory(const std::string& param_name) { + static DhcpConfigParser* Factory(const std::string& param_name) { return (new Uint32Parser(param_name)); } @@ -189,7 +189,7 @@ public: protected: /// pointer to the storage, where parsed value will be stored - Uint32Storage * storage_; + Uint32Storage* storage_; /// name of the parameter to be parsed std::string param_name_; @@ -205,11 +205,11 @@ protected: /// (string_defaults). If used in smaller scopes (e.g. to parse parameters /// in subnet config), it can be pointed to a different storage, using /// setStorage() method. This class follows the parser interface, laid out -/// in its base class, \ref Dhcp6ConfigParser. +/// in its base class, \ref DhcpConfigParser. /// /// For overview of usability of this generic purpose parser, see /// \ref dhcpv6-config-inherit page. -class StringParser : public Dhcp6ConfigParser { +class StringParser : public DhcpConfigParser { public: /// @brief constructor for StringParser @@ -244,7 +244,7 @@ public: /// @brief factory that constructs StringParser objects /// /// @param param_name name of the parameter to be parsed - static Dhcp6ConfigParser* Factory(const std::string& param_name) { + static DhcpConfigParser* Factory(const std::string& param_name) { return (new StringParser(param_name)); } @@ -253,13 +253,13 @@ public: /// See \ref dhcpv6-config-inherit for details. /// /// @param storage pointer to the storage container - void setStorage(StringStorage * storage) { + void setStorage(StringStorage* storage) { storage_ = storage; } protected: /// pointer to the storage, where parsed value will be stored - StringStorage * storage_; + StringStorage* storage_; /// name of the parameter to be parsed std::string param_name_; @@ -277,7 +277,7 @@ protected: /// designates all interfaces. /// /// It is useful for parsing Dhcp6/interface parameter. -class InterfaceListConfigParser : public Dhcp6ConfigParser { +class InterfaceListConfigParser : public DhcpConfigParser { public: /// @brief constructor @@ -314,7 +314,7 @@ public: /// @brief factory that constructs InterfaceListConfigParser objects /// /// @param param_name name of the parameter to be parsed - static Dhcp6ConfigParser* Factory(const std::string& param_name) { + static DhcpConfigParser* Factory(const std::string& param_name) { return (new InterfaceListConfigParser(param_name)); } @@ -333,7 +333,7 @@ protected: /// before build(). Otherwise exception will be thrown. /// /// It is useful for parsing Dhcp6/subnet6[X]/pool parameters. -class PoolParser : public Dhcp6ConfigParser { +class PoolParser : public DhcpConfigParser { public: /// @brief constructor. @@ -350,8 +350,8 @@ public: void build(ConstElementPtr pools_list) { // setStorage() should have been called before build if (!pools_) { - isc_throw(NotImplemented, "Parser logic error. No pool storage set," - " but pool parser asked to parse pools"); + isc_throw(NotImplemented, "Parser logic error. No pool storage set," + " but pool parser asked to parse pools"); } BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) { @@ -372,14 +372,19 @@ public: uint8_t len = 0; try { addr = IOAddress(txt.substr(0, pos)); - string num = txt.substr(pos+1); - // it is lexical cast to int and then downcast to uint8_t - // direct cast to uint8_t (which is really an unsigned char) + // start with the first charater after / + string prefix_len = txt.substr(pos + 1); + + // It is lexical cast to int and then downcast to uint8_t. + // Direct cast to uint8_t (which is really an unsigned char) // will result in interpreting the first digit as output - // value and throwing exception if length written on two - // digits (because there are extra characters left over) - len = boost::lexical_cast(num); + // value and throwing exception if length is written on two + // digits (because there are extra characters left over). + + // No checks for values over 128. Range correctness will + // be checked in Pool6 constructor. + len = boost::lexical_cast(prefix_len); } catch (...) { isc_throw(Dhcp6ConfigError, "Failed to parse pool " "definition: " << text_pool->stringValue()); @@ -394,8 +399,8 @@ public: pos = txt.find("-"); if (pos != string::npos) { // using min-max notation - IOAddress min(txt.substr(0,pos-1)); - IOAddress max(txt.substr(pos+1)); + IOAddress min(txt.substr(0,pos - 1)); + IOAddress max(txt.substr(pos + 1)); Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max)); @@ -422,13 +427,13 @@ public: /// /// This method is required for all parser. The value itself /// is not commited anywhere. Higher level parsers (for subnet) are expected - /// to use values stored in the storage, + /// to use values stored in the storage. virtual void commit() {} /// @brief factory that constructs PoolParser objects /// /// @param param_name name of the parameter to be parsed - static Dhcp6ConfigParser* Factory(const std::string& param_name) { + static DhcpConfigParser* Factory(const std::string& param_name) { return (new PoolParser(param_name)); } @@ -437,14 +442,14 @@ protected: /// /// That is typically a storage somewhere in Subnet parser /// (an upper level parser). - PoolStorage * pools_; + PoolStorage* pools_; }; /// @brief this class parses a single subnet /// /// This class parses the whole subnet definition. It creates parsers /// for received configuration parameters as needed. -class Subnet6ConfigParser : public Dhcp6ConfigParser { +class Subnet6ConfigParser : public DhcpConfigParser { public: /// @brief constructor @@ -468,18 +473,20 @@ public: boost::dynamic_pointer_cast(parser); if (uintParser) { uintParser->setStorage(&uint32_values_); - } + } else { - boost::shared_ptr stringParser = - boost::dynamic_pointer_cast(parser); - if (stringParser) { - stringParser->setStorage(&string_values_); - } + boost::shared_ptr stringParser = + boost::dynamic_pointer_cast(parser); + if (stringParser) { + stringParser->setStorage(&string_values_); + } else { - boost::shared_ptr poolParser = - boost::dynamic_pointer_cast(parser); - if (poolParser) { - poolParser->setStorage(&pools_); + boost::shared_ptr poolParser = + boost::dynamic_pointer_cast(parser); + if (poolParser) { + poolParser->setStorage(&pools_); + } + } } parser->build(param.second); @@ -544,7 +551,7 @@ protected: /// /// @param config_id name od the entry /// @return parser object for specified entry name - Dhcp6ConfigParser* createSubnet6ConfigParser(const std::string& config_id) { + DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) { FactoryMap factories; factories.insert(pair( @@ -565,7 +572,7 @@ protected: FactoryMap::iterator f = factories.find(config_id); if (f == factories.end()) { // Used for debugging only. - // return new DummyParser(config_id); + // return new DebugParser(config_id); isc_throw(NotImplemented, "Parser error: Subnet6 parameter not supported: " @@ -624,7 +631,7 @@ protected: /// This is a wrapper parser that handles the whole list of Subnet6 /// definitions. It iterates over all entries and creates Subnet6ConfigParser /// for each entry. -class Subnets6ListConfigParser : public Dhcp6ConfigParser { +class Subnets6ListConfigParser : public DhcpConfigParser { public: /// @brief constructor @@ -673,7 +680,7 @@ public: /// @brief Returns Subnet6ListConfigParser object /// @param param_name name of the parameter /// @return Subnets6ListConfigParser object - static Dhcp6ConfigParser* Factory(const std::string& param_name) { + static DhcpConfigParser* Factory(const std::string& param_name) { return (new Subnets6ListConfigParser(param_name)); } @@ -688,7 +695,7 @@ public: /// /// @param config_id pointer to received global configuration entry /// @return parser for specified global DHCPv6 parameter -Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { +DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) { FactoryMap factories; // @@ -712,7 +719,7 @@ Dhcp6ConfigParser* createGlobalDhcp6ConfigParser(const std::string& config_id) { FactoryMap::iterator f = factories.find(config_id); if (f == factories.end()) { // Used for debugging only. - // return new DummyParser(config_id); + // return new DebugParser(config_id); isc_throw(NotImplemented, "Parser error: Global configuration parameter not supported: " @@ -751,7 +758,7 @@ configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) { try { BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) { - ParserPtr parser(createGlobalDhcp6ConfigParser(config_pair.first)); + ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first)); parser->build(config_pair.second); parsers.push_back(parser); } diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h index 79dae8d6eb..6758c99e4e 100644 --- a/src/bin/dhcp6/config_parser.h +++ b/src/bin/dhcp6/config_parser.h @@ -38,7 +38,7 @@ Dhcp6ConfigError(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) {} }; -class Dhcp6ConfigParser { +class DhcpConfigParser { /// /// \name Constructors and Destructor /// @@ -47,30 +47,24 @@ class Dhcp6ConfigParser { /// pure base class. //@{ private: - Dhcp6ConfigParser(const Dhcp6ConfigParser& source); - Dhcp6ConfigParser& operator=(const Dhcp6ConfigParser& source); + DhcpConfigParser(const DhcpConfigParser& source); + DhcpConfigParser& operator=(const DhcpConfigParser& source); protected: /// \brief The default constructor. /// /// This is intentionally defined as \c protected as this base class should /// never be instantiated (except as part of a derived class). - Dhcp6ConfigParser() {} + DhcpConfigParser() {} public: /// The destructor. - virtual ~Dhcp6ConfigParser() {} + virtual ~DhcpConfigParser() {} //@} - /// Prepare configuration value. + /// \brief Prepare configuration value. /// /// This method parses the "value part" of the configuration identifier /// that corresponds to this derived class and prepares a new value to /// apply to the server. - /// In the above example, the derived class for the identifier "param1" - /// would be passed an data \c Element storing an integer whose value - /// is 10, and would record that value internally; - /// the derived class for the identifier "param2" would be passed a - /// map element and (after parsing) convert it into some internal - /// data structure. /// /// This method must validate the given value both in terms of syntax /// and semantics of the configuration, so that the server will be @@ -84,15 +78,15 @@ public: /// allocation. If it fails, it may throw a corresponding standard /// exception. /// - /// This method is not expected to be called more than once. Although - /// multiple calls are not prohibited by the interface, the behavior - /// is undefined. + /// This method is not expected to be called more than once in the + /// life of the object. Although multiple calls are not prohibited + /// by the interface, the behavior is undefined. /// /// \param config_value The configuration value for the identifier /// corresponding to the derived class. virtual void build(isc::data::ConstElementPtr config_value) = 0; - /// Apply the prepared configuration value to the server. + /// \brief Apply the prepared configuration value to the server. /// /// This method is expected to be exception free, and, as a consequence, /// it should normally not involve resource allocation. @@ -110,7 +104,7 @@ public: }; /// @brief a pointer to configuration parser -typedef boost::shared_ptr ParserPtr; +typedef boost::shared_ptr ParserPtr; /// @brief a collection of parsers /// @@ -118,12 +112,12 @@ typedef boost::shared_ptr ParserPtr; typedef std::vector ParserCollection; -/// Configure an \c Dhcpv6Srv object with a set of configuration values. +/// \brief Configure an \c Dhcpv6Srv object with a set of configuration values. /// /// This function parses configuration information stored in \c config_set /// and configures the \c server by applying the configuration to it. /// It provides the strong exception guarantee as long as the underlying -/// derived class implementations of \c Dhcp6ConfigParser meet the assumption, +/// derived class implementations of \c DhcpConfigParser meet the assumption, /// that is, it ensures that either configuration is fully applied or the /// state of the server is intact. /// @@ -147,38 +141,6 @@ isc::data::ConstElementPtr configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set); - -/// Create a new \c Dhcp6ConfigParser object for a given configuration -/// identifier. -/// -/// It internally identifies an appropriate derived class for the given -/// identifier and creates a new instance of that class. The caller can -/// then configure the \c server regarding the identifier by calling -/// the \c build() and \c commit() methods of the returned object. -/// -/// In practice, this function is only expected to be used as a backend of -/// \c configureDhcp6Server() and is not supposed to be called directly -/// by applications. It is publicly available mainly for testing purposes. -/// When called directly, the created object must be deleted by the caller. -/// Note: this means if this module and the caller use incompatible sets of -/// new/delete, it may cause unexpected strange failure. We could avoid that -/// by providing a separate deallocation function or by using a smart pointer, -/// but since the expected usage of this function is very limited (i.e. for -/// our own testing purposes) it would be an overkilling. We therefore prefer -/// simplicity and keeping the interface intuitive. -/// -/// If the resource allocation for the new object fails, a corresponding -/// standard exception will be thrown. Otherwise this function is not -/// expected to throw an exception, unless the constructor of the underlying -/// derived class implementation (unexpectedly) throws. -/// -/// \param server The \c Dhcpv6Srv object to be configured. -/// \param config_id The configuration identifier for which a parser object -/// is to be created. -/// \return A pointer to an \c Dhcp6ConfigParser object. -Dhcp6ConfigParser* createDhcp6ConfigParser(Dhcpv6Srv& server, - const std::string& config_id); - }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 4f402dd153..7370583d68 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -53,7 +53,8 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { return (configureDhcp6Server(*server_, new_config)); } - // that should never happen as we install config_handler after we + // That should never happen as we install config_handler after we instantiate + // the server. ConstElementPtr answer = isc::config::createAnswer(1, "Configuration rejected, server is during startup/shutdown phase."); return (answer); diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 8c38762bdd..22592e8c2d 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -39,7 +39,9 @@ class Dhcp6ParserTest : public ::testing::Test { public: Dhcp6ParserTest() :rcode_(-1) { - // open port 0 means to not do anything at all + // Open port 0 means to not do anything at all. We don't want to + // deal with sockets here, just check if configuration handling + // is sane. srv_ = new Dhcpv6Srv(0); } @@ -47,13 +49,15 @@ public: delete srv_; }; - Dhcpv6Srv * srv_; + Dhcpv6Srv* srv_; int rcode_; ConstElementPtr comment_; }; - +// Goal of this test is a verification if a very simple config update +// with just a bumped version number. That's the simplest possible +// config update. TEST_F(Dhcp6ParserTest, version) { ConstElementPtr x; @@ -67,6 +71,8 @@ TEST_F(Dhcp6ParserTest, version) { EXPECT_EQ(0, rcode_); } +/// The goal of this test is to verify that the code accepts only +/// valid commands and malformed or unsupported parameters are rejected. TEST_F(Dhcp6ParserTest, bogus_command) { ConstElementPtr x; @@ -80,11 +86,14 @@ TEST_F(Dhcp6ParserTest, bogus_command) { EXPECT_EQ(1, rcode_); } +/// The goal of this test is to verify if wrongly defined subnet will +/// be rejected. Properly defined subnet must include at least one +/// pool definition. TEST_F(Dhcp6ParserTest, empty_subnet) { - ConstElementPtr x; + ConstElementPtr status; - EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, Element::fromJSON("{ \"interface\": [ \"all\" ]," "\"preferred-lifetime\": 3000," "\"rebind-timer\": 2000, " @@ -92,15 +101,17 @@ TEST_F(Dhcp6ParserTest, empty_subnet) { "\"subnet6\": [ ], " "\"valid-lifetime\": 4000 }"))); - // returned value must be 1 (configuration parse error) - ASSERT_TRUE(x); - comment_ = parseAnswer(rcode_, x); + // returned value should be 0 (success) + ASSERT_TRUE(status); + comment_ = parseAnswer(rcode_, status); EXPECT_EQ(0, rcode_); } +/// The goal of this test is to verify if defined subnet uses global +/// parameter timer definitions. TEST_F(Dhcp6ParserTest, subnet_global_defaults) { - ConstElementPtr x; + ConstElementPtr status; string config = "{ \"interface\": [ \"all\" ]," "\"preferred-lifetime\": 3000," @@ -114,13 +125,15 @@ TEST_F(Dhcp6ParserTest, subnet_global_defaults) { ElementPtr json = Element::fromJSON(config); - EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json)); - // returned value must be 1 (configuration parse error) - ASSERT_TRUE(x); - comment_ = parseAnswer(rcode_, x); + // check if returned status is OK + ASSERT_TRUE(status); + comment_ = parseAnswer(rcode_, status); EXPECT_EQ(0, rcode_); + // Now check if the configuration was indeed handled and we have + // expected pool configured. Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); ASSERT_TRUE(subnet); EXPECT_EQ(1000, subnet->getT1()); @@ -129,10 +142,11 @@ TEST_F(Dhcp6ParserTest, subnet_global_defaults) { EXPECT_EQ(4000, subnet->getValid()); } -// +// This test checks if it is possible to override global values +// on a per subnet basis. TEST_F(Dhcp6ParserTest, subnet_local) { - ConstElementPtr x; + ConstElementPtr status; string config = "{ \"interface\": [ \"all\" ]," "\"preferred-lifetime\": 3000," @@ -150,11 +164,11 @@ TEST_F(Dhcp6ParserTest, subnet_local) { ElementPtr json = Element::fromJSON(config); - EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json)); - // returned value must be 1 (configuration parse error) - ASSERT_TRUE(x); - comment_ = parseAnswer(rcode_, x); + // returned value should be 0 (configuration success) + ASSERT_TRUE(status); + comment_ = parseAnswer(rcode_, status); EXPECT_EQ(0, rcode_); Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); @@ -165,9 +179,11 @@ TEST_F(Dhcp6ParserTest, subnet_local) { EXPECT_EQ(4, subnet->getValid()); } +// Test verifies that a subnet with pool values that do not belong to that +// pool are rejected. TEST_F(Dhcp6ParserTest, pool_out_of_subnet) { - ConstElementPtr x; + ConstElementPtr status; string config = "{ \"interface\": [ \"all\" ]," "\"preferred-lifetime\": 3000," @@ -181,17 +197,19 @@ TEST_F(Dhcp6ParserTest, pool_out_of_subnet) { ElementPtr json = Element::fromJSON(config); - EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json)); // returned value must be 2 (values error) // as the pool does not belong to that subnet - ASSERT_TRUE(x); - comment_ = parseAnswer(rcode_, x); + ASSERT_TRUE(status); + comment_ = parseAnswer(rcode_, status); EXPECT_EQ(2, rcode_); - } -TEST_F(Dhcp6ParserTest, subnet_prefix_len) { +// Goal of this test is to verify if pools can be defined +// using prefix/length notation. There is no separate test for min-max +// notation as it was tested in several previous tests. +TEST_F(Dhcp6ParserTest, pool_prefix_len) { ConstElementPtr x; From d12ce3c245fc86e3c34a5fdd5950d899cdf889af Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 11:11:51 -0700 Subject: [PATCH 062/194] [2244] missing update: Component.running() was renamed is_running(). --- src/bin/bind10/bind10_src.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 4567229ece..32c3152099 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -739,7 +739,7 @@ class BoB: component = self.components.pop(pid) logger.info(BIND10_PROCESS_ENDED, component.name(), pid, exit_status) - if component.running() and self.runnable: + if component.is_running() and self.runnable: # Tell it it failed. But only if it matters (we are # not shutting down and the component considers itself # to be running. From 74911480e9ff1f21f63365ac789ecd30f67891b8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:17:59 -0700 Subject: [PATCH 063/194] [2339] changed the search order of python executable: seek python3.x first. this works around a bit awkward installation setup where there's a link from "python3" to "python3.x" but not from "python3-config" to the corresponding "python3.x-config". That happens for recent versions of Homebrew. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9177d21660..9fb813c28b 100644 --- a/configure.ac +++ b/configure.ac @@ -132,7 +132,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes) AC_SUBST(SET_ENV_LIBRARY_PATH) AC_SUBST(ENV_LIBRARY_PATH) -m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1 python3.2]) +m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.2 python3.1 python3]) AC_ARG_WITH([pythonpath], AC_HELP_STRING([--with-pythonpath=PATH], [specify an absolute path to python executable when automatic version check (incorrectly) fails]), From f496bbe69fbff9228c48fc7bf9cc986ed7f420e4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:26:20 -0700 Subject: [PATCH 064/194] [2204] added notes about future extensions of incremental datasrc config update --- src/bin/auth/datasrc_config.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 02e4e5c45e..678b87f556 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -35,6 +35,16 @@ /// You don't need to pay attention to it, use the configureDataSource /// specialization instead. /// +/// \note In future we may want to make the reconfiguration more efficient +/// by only creating newly configured data and just moving the rest from +/// the running configuration if they are used in the new configuration +/// without any parameter change. We could probably do it by passing +/// the old lists in addition to the new config, but further details are +/// still to be defined yet. It will surely require changes in the +/// data source library, too. So, right now, we don't introduce the +/// possibility in the function interface. If and when we decide to introduce +/// the optimization, we'll extend the interface. +/// /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. /// \return A map from RR classes to configured lists. From 86e4008ac1623aef96fd0c6140e5496841c3b9f0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:28:08 -0700 Subject: [PATCH 065/194] [2204] removed an obsolete comment that only made sense for the previous code. --- src/bin/auth/datasrc_config.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 678b87f556..5707c6c8a0 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -58,10 +58,8 @@ configureDataSourceGeneric(const isc::data::ConstElementPtr& config) { boost::shared_ptr new_lists(new ListMap); - // Get the configuration and current state. - const Map& map(config->mapValue()); - // Go through the configuration and create corresponding list. + const Map& map(config->mapValue()); for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { const isc::dns::RRClass rrclass(it->first); ListPtr list(new List(rrclass)); From 593b348cbb8207fca3e9d01620794cfe118455b2 Mon Sep 17 00:00:00 2001 From: Kazunori Fujiwara Date: Wed, 10 Oct 2012 03:51:09 +0530 Subject: [PATCH 066/194] [2302] Fix bugs in Element class Two bugs in src/lib/cc/data.h: * Element::find() does not return value because its argument is not a reference. * getValue() member functions does not work for ConstElementPointer because getValue() member functions are not specified as const. Aharen found these bugs. --- src/lib/cc/data.cc | 16 ++++++++-------- src/lib/cc/data.h | 28 ++++++++++++++-------------- src/lib/cc/tests/data_unittests.cc | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 47b1eb1fa4..8d9b87dd05 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -57,32 +57,32 @@ Element::toWire(std::ostream& ss) const { } bool -Element::getValue(long int&) { +Element::getValue(long int&) const { return (false); } bool -Element::getValue(double&) { +Element::getValue(double&) const { return (false); } bool -Element::getValue(bool&) { +Element::getValue(bool&) const { return (false); } bool -Element::getValue(std::string&) { +Element::getValue(std::string&) const { return (false); } bool -Element::getValue(std::vector&) { +Element::getValue(std::vector&) const { return (false); } bool -Element::getValue(std::map&) { +Element::getValue(std::map&) const { return (false); } @@ -167,7 +167,7 @@ Element::find(const std::string&) const { } bool -Element::find(const std::string&, ConstElementPtr) const { +Element::find(const std::string&, ConstElementPtr&) const { return (false); } @@ -812,7 +812,7 @@ MapElement::set(const std::string& key, ConstElementPtr value) { } bool -MapElement::find(const std::string& id, ConstElementPtr t) const { +MapElement::find(const std::string& id, ConstElementPtr& t) const { try { ConstElementPtr p = find(id); if (p) { diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h index 5c731e6b54..ef177ca988 100644 --- a/src/lib/cc/data.h +++ b/src/lib/cc/data.h @@ -152,12 +152,12 @@ public: /// data to the given reference and returning true /// //@{ - virtual bool getValue(long int& t); - virtual bool getValue(double& t); - virtual bool getValue(bool& t); - virtual bool getValue(std::string& t); - virtual bool getValue(std::vector& t); - virtual bool getValue(std::map& t); + virtual bool getValue(long int& t) const; + virtual bool getValue(double& t) const; + virtual bool getValue(bool& t) const; + virtual bool getValue(std::string& t) const; + virtual bool getValue(std::vector& t) const; + virtual bool getValue(std::map& t) const; //@} /// @@ -253,7 +253,7 @@ public: /// \param identifier The identifier of the element to find /// \param t Reference to store the resulting ElementPtr, if found. /// \return true if the element was found, false if not. - virtual bool find(const std::string& identifier, ConstElementPtr t) const; + virtual bool find(const std::string& identifier, ConstElementPtr& t) const; //@} @@ -378,7 +378,7 @@ public: IntElement(long int v) : Element(integer), i(v) { } long int intValue() const { return (i); } using Element::getValue; - bool getValue(long int& t) { t = i; return (true); } + bool getValue(long int& t) const { t = i; return (true); } using Element::setValue; bool setValue(const long int v) { i = v; return (true); } void toJSON(std::ostream& ss) const; @@ -392,7 +392,7 @@ public: DoubleElement(double v) : Element(real), d(v) {}; double doubleValue() const { return (d); } using Element::getValue; - bool getValue(double& t) { t = d; return (true); } + bool getValue(double& t) const { t = d; return (true); } using Element::setValue; bool setValue(const double v) { d = v; return (true); } void toJSON(std::ostream& ss) const; @@ -406,7 +406,7 @@ public: BoolElement(const bool v) : Element(boolean), b(v) {}; bool boolValue() const { return (b); } using Element::getValue; - bool getValue(bool& t) { t = b; return (true); } + bool getValue(bool& t) const { t = b; return (true); } using Element::setValue; bool setValue(const bool v) { b = v; return (true); } void toJSON(std::ostream& ss) const; @@ -427,7 +427,7 @@ public: StringElement(std::string v) : Element(string), s(v) {}; std::string stringValue() const { return (s); } using Element::getValue; - bool getValue(std::string& t) { t = s; return (true); } + bool getValue(std::string& t) const { t = s; return (true); } using Element::setValue; bool setValue(const std::string& v) { s = v; return (true); } void toJSON(std::ostream& ss) const; @@ -441,7 +441,7 @@ public: ListElement() : Element(list) {} const std::vector& listValue() const { return (l); } using Element::getValue; - bool getValue(std::vector& t) { + bool getValue(std::vector& t) const { t = l; return (true); } @@ -474,7 +474,7 @@ public: return (m); } using Element::getValue; - bool getValue(std::map& t) { + bool getValue(std::map& t) const { t = m; return (true); } @@ -507,7 +507,7 @@ public: // returns true if found, or false if not found (either because // it doesnt exist or one of the elements in the path is not // a MapElement) - bool find(const std::string& id, ConstElementPtr t) const; + bool find(const std::string& id, ConstElementPtr& t) const; bool equals(const Element& other) const; }; diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 87d92f64ab..7da91e2eb4 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -176,6 +176,7 @@ TEST(Element, create_and_value_throws) { // this test checks whether elements throw exceptions if the // incorrect type is requested ElementPtr el; + ConstElementPtr cel; long int i; double d; bool b; @@ -197,6 +198,22 @@ TEST(Element, create_and_value_throws) { EXPECT_FALSE(el->getValue(v)); EXPECT_FALSE(el->getValue(m)); EXPECT_EQ(i, 1); + + cel = Element::create(1); + EXPECT_NO_THROW(cel->intValue()); + EXPECT_THROW(cel->doubleValue(), TypeError); + EXPECT_THROW(cel->boolValue(), TypeError); + EXPECT_THROW(cel->stringValue(), TypeError); + EXPECT_THROW(cel->listValue(), TypeError); + EXPECT_THROW(cel->mapValue(), TypeError); + EXPECT_TRUE(cel->getValue(i)); + EXPECT_FALSE(cel->getValue(d)); + EXPECT_FALSE(cel->getValue(b)); + EXPECT_FALSE(cel->getValue(s)); + EXPECT_FALSE(cel->getValue(v)); + EXPECT_FALSE(cel->getValue(m)); + EXPECT_EQ(i, 1); + i = 2; EXPECT_TRUE(el->setValue(i)); EXPECT_EQ(2, el->intValue()); @@ -401,6 +418,7 @@ TEST(Element, MapElement) { EXPECT_EQ(el->find("value1/")->stringValue(), "bar"); EXPECT_TRUE(el->find("value1", el2)); + EXPECT_EQ("bar", el2->stringValue()); EXPECT_FALSE(el->find("name/error", el2)); // A map element whose (only) element has the maximum length of tag. From 2975fbae44dbaacdff7b9039a395b00c4375f01b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 10 Oct 2012 03:54:29 +0530 Subject: [PATCH 067/194] [2302] Untabify and delete trailing whitespace --- src/lib/cc/data.h | 16 ++++++++-------- src/lib/cc/tests/data_unittests.cc | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h index ef177ca988..c1147a21cd 100644 --- a/src/lib/cc/data.h +++ b/src/lib/cc/data.h @@ -71,7 +71,7 @@ public: /// the type in question. /// class Element { - + private: // technically the type could be omitted; is it useful? // should we remove it or replace it with a pure virtual @@ -112,7 +112,7 @@ public: /// \returns true if the other ElementPtr has the same type and /// value virtual bool equals(const Element& other) const = 0; - + /// Converts the Element to JSON format and appends it to /// the given stringstream. virtual void toJSON(std::ostream& ss) const = 0; @@ -209,7 +209,7 @@ public: virtual size_t size() const; //@} - + /// \name MapElement functions /// /// \brief If the Element on which these functions are called are not @@ -258,7 +258,7 @@ public: /// \name Factory functions - + // TODO: should we move all factory functions to a different class // so as not to burden the Element base with too many functions? // and/or perhaps even to a separate header? @@ -349,7 +349,7 @@ public: /// These function pparse the wireformat at the given stringstream /// (of the given length). If there is a parse error an exception /// of the type isc::cc::DecodeError is raised. - + //@{ /// Creates an Element from the wire format in the given /// stringstream of the given length. @@ -495,7 +495,7 @@ public: return (m.find(s) != m.end()); } void toJSON(std::ostream& ss) const; - + // we should name the two finds better... // find the element at id; raises TypeError if one of the // elements at path except the one we're looking for is not a @@ -569,6 +569,6 @@ bool operator!=(const Element& a, const Element& b); } } #endif // _ISC_DATA_H -// Local Variables: +// Local Variables: // mode: c++ -// End: +// End: diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 7da91e2eb4..30fa82de2b 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -112,7 +112,7 @@ TEST(Element, from_and_to_json) { std::string s = std::string(pe.what()); EXPECT_EQ("String expected in :1:3", s); } - + sv.clear(); sv.push_back("{1}"); //ElementPtr ep = Element::fromJSON("\"aaa\nbbb\"err"); @@ -233,7 +233,7 @@ TEST(Element, create_and_value_throws) { EXPECT_THROW(el->contains("foo"), TypeError); ConstElementPtr tmp; EXPECT_FALSE(el->find("foo", tmp)); - + el = Element::create(1.1); EXPECT_THROW(el->intValue(), TypeError); @@ -399,7 +399,7 @@ TEST(Element, MapElement) { // this function checks the specific functions for ListElements ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }"); ConstElementPtr el2; - + EXPECT_EQ(el->get("name")->stringValue(), "foo"); EXPECT_EQ(el->get("value2")->getType(), Element::map); @@ -413,10 +413,10 @@ TEST(Element, MapElement) { EXPECT_EQ(el->find("value2/number")->intValue(), 42); EXPECT_TRUE(isNull(el->find("value2/nothing/"))); - + EXPECT_EQ(el->find("value1")->stringValue(), "bar"); EXPECT_EQ(el->find("value1/")->stringValue(), "bar"); - + EXPECT_TRUE(el->find("value1", el2)); EXPECT_EQ("bar", el2->stringValue()); EXPECT_FALSE(el->find("name/error", el2)); @@ -428,7 +428,7 @@ TEST(Element, MapElement) { "9123456789abcdefa123456789abcdefb123456789abcdef" "c123456789abcdefd123456789abcdefe123456789abcdef" "f123456789abcde"); - + EXPECT_EQ(255, long_maptag.length()); // check prerequisite el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}"); EXPECT_EQ("bar", el->find(long_maptag)->stringValue()); @@ -707,7 +707,7 @@ TEST(Element, merge) { c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }"); merge(b, a); EXPECT_EQ(*b, *c); - + // And some tests with multiple values a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }"); b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }"); From 28a6c7133b1fa8f5334f91c514f5dc8e0802e4eb Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 16:43:16 -0700 Subject: [PATCH 068/194] [2340] added comment about why we need to explicitly define empty dtor. --- src/lib/util/locks.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h index 3b51a5ad44..f33ff960e3 100644 --- a/src/lib/util/locks.h +++ b/src/lib/util/locks.h @@ -49,6 +49,9 @@ template class scoped_lock { public: scoped_lock(T) {} + + // We need to define this explicitly. Some versions of clang++ would + // complain about this otherwise. See Trac ticket #2340 ~scoped_lock() {} void lock() {} From c6391ad769265a1f5c2d5594dcdb113420c7eb4d Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 11:58:17 +0200 Subject: [PATCH 069/194] [2292] Add a TODO about re-adding tests Planned for merge #2268 --- .../datasrc/tests/memory/memory_client_unittest.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc index 6d3f05505c..004f663c56 100644 --- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc +++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc @@ -682,4 +682,16 @@ TEST_F(MemoryClientTest, getJournalReaderNotImplemented) { EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0), isc::NotImplemented); } + +// TODO (upon merge of #2268): Re-add (and modify not to need +// InMemoryClient::add) the tests removed in +// 7a628baa1a158b5837d6f383e10b30542d2ac59b. Maybe some of them +// are really not needed. +// +// * MemoryClientTest::loadRRSIGsRdataMixedCoveredTypes +// * MemoryClientTest::addRRsetToNonExistentZoneThrows +// * MemoryClientTest::addOutOfZoneThrows +// * MemoryClientTest::addNullRRsetThrows +// * MemoryClientTest::addEmptyRRsetThrows +// * MemoryClientTest::add } From 08d6f75709a7669481b92fc0b2e4176174bc0f48 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 10 Oct 2012 16:03:26 +0200 Subject: [PATCH 070/194] [2140] Changes after review: LeaseMgr::getLeaseX() methods expanded. --- src/lib/dhcp/lease_mgr.h | 114 ++++++++++++++++++++--- src/lib/dhcp/tests/lease_mgr_unittest.cc | 92 ++++++++++++++++-- 2 files changed, 183 insertions(+), 23 deletions(-) diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index 3f90ee0ffc..a6b4c6185c 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -24,6 +24,10 @@ namespace isc { namespace dhcp { +/// @brief specifies unique subnet identifier +/// @todo: Move this to subnet.h once ticket #2237 is merged +typedef uint32_t SubnetID; + /// @brief Structure that holds a lease for IPv4 address /// /// For performance reasons it is a simple structure, not a class. If we chose @@ -67,10 +71,10 @@ struct Lease4 { /// Specifies a timestamp, when last transmission from a client was received. time_t cltt_; - /// @brief Pool identifier + /// @brief Subnet identifier /// - /// Specifies pool-id of the pool that the lease belongs to - uint32_t pool_id_; + /// Specifies subnet-id of the subnet that the lease belongs to + SubnetID subnet_id_; /// @brief Is this a fixed lease? /// @@ -104,6 +108,8 @@ struct Lease4 { /// @brief Pointer to a Lease4 structure. typedef boost::shared_ptr Lease4Ptr; +/// @brief A collection of IPv4 leases. +typedef std::vector< boost::shared_ptr > Lease4Collection; /// @brief Structure that holds a lease for IPv6 address and/or prefix /// @@ -189,10 +195,10 @@ struct Lease6 { /// Specifies a timestamp, when last transmission from a client was received. time_t cltt_; - /// @brief Pool identifier + /// @brief Subnet identifier /// - /// Specifies pool-id of the pool that the lease belongs to - uint32_t pool_id_; + /// Specifies subnet-id of the subnet that the lease belongs to + SubnetID subnet_id_; /// @brief Is this a fixed lease? /// @@ -229,6 +235,8 @@ typedef boost::shared_ptr Lease6Ptr; /// @brief Const pointer to a Lease6 structure. typedef boost::shared_ptr ConstLease6Ptr; +/// @brief A collection of IPv6 leases. +typedef std::vector< boost::shared_ptr > Lease6Collection; /// @brief Abstract Lease Manager /// @@ -264,39 +272,117 @@ public: /// @param lease lease to be added virtual bool addLease(Lease6Ptr lease) = 0; - /// @brief Returns existing IPv4 lease for specified IPv4 address. + /// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id + /// + /// This method is used to get a lease for specific subnet_id. There can be + /// at most one lease for any given subnet, so this method returns a single + /// pointer. /// /// @param addr address of the searched lease + /// @param subnet_id ID of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr, + SubnetID subnet_id) const = 0; + + /// @brief Returns an IPv4 lease for specified IPv4 address + /// + /// This method return a lease that is associated with a given address. + /// For other query types (by hardware addr, by client-id) there can be + /// several leases in different subnets (e.g. for mobile clients that + /// got address in different subnets). However, for a single address + /// there can be only one lease, so this method returns a pointer to + /// a single lease, not a container of leases. + /// + /// @param addr address of the searched lease + /// @param subnet_id ID of the subnet the lease must belong to /// /// @return smart pointer to the lease (or NULL if a lease is not found) virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0; - /// @brief Returns existing IPv4 lease for specified hardware address. + /// @brief Returns existing IPv4 leases for specified hardware address. + /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. /// /// @param hwaddr hardware address of the client /// - /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const HWAddr& hwaddr) const = 0; + /// @return lease collection + virtual Lease4Collection getLease4(const HWAddr& hwaddr) const = 0; + + /// @brief Returns existing IPv4 leases for specified hardware address + /// and a subnet + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param hwaddr hardware address of the client + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const HWAddr& hwaddr, + SubnetID subnet_id) const = 0; /// @brief Returns existing IPv4 lease for specified client-id /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. + /// /// @param clientid client identifier - virtual Lease4Ptr getLease4(const ClientId& clientid) const = 0; + /// + /// @return lease collection + virtual Lease4Collection getLease4(const ClientId& clientid) const = 0; + + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param clientid client identifier + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const ClientId& clientid, + SubnetID subnet_id) const = 0; /// @brief Returns existing IPv6 lease for a given IPv6 address. /// + /// For a given address, we assume that there will be only one lease. + /// The assumtion here is that there will not be site or link-local + /// addresses used, so there is no way of having address duplication. + /// /// @param addr address of the searched lease /// /// @return smart pointer to the lease (or NULL if a lease is not found) virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const = 0; - /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// @brief Returns existing IPv6 leases for a given DUID+IA combination + /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. /// /// @param duid client DUID /// @param iaid IA identifier /// /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid) const = 0; + virtual Lease6Collection getLease6(const DUID& duid, + uint32_t iaid) const = 0; + + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// @param subnet_id subnet id of the subnet the lease belongs to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, + SubnetID subnet_id) const = 0; /// @brief Updates IPv4 lease. /// @@ -346,7 +432,7 @@ public: /// and then check that: /// B>=A and B=C (it is ok to have newer backend, as it should be backward /// compatible) - /// Also if B>C, some database upgrade procedure may happen + /// Also if B>C, some database upgrade procedure may be triggered virtual std::string getVersion() const = 0; /// @todo: Add host management here diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc index 39102bb533..97659a142e 100644 --- a/src/lib/dhcp/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc @@ -59,20 +59,58 @@ public: /// /// @param addr address of the searched lease /// - /// @return smart pointer to the lease (or NULL if a lease is not found) + /// @return a collection of leases virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const; - /// @brief Returns existing IPv4 lease for specified hardware address. + /// @brief Returns existing IPv4 lease for specific address and subnet + /// @param addr address of the searched lease + /// @param subnet_id ID of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr, + SubnetID subnet_id) const; + + /// @brief Returns existing IPv4 leases for specified hardware address. + /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. /// /// @param hwaddr hardware address of the client /// - /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const HWAddr& hwaddr) const; + /// @return lease collection + virtual Lease4Collection getLease4(const HWAddr& hwaddr) const; + + /// @brief Returns existing IPv4 leases for specified hardware address + /// and a subnet + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param hwaddr hardware address of the client + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const HWAddr& hwaddr, + SubnetID subnet_id) const; /// @brief Returns existing IPv4 lease for specified client-id /// /// @param clientid client identifier - virtual Lease4Ptr getLease4(const ClientId& clientid) const; + virtual Lease4Collection getLease4(const ClientId& clientid) const; + + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param clientid client identifier + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const ClientId& clientid, + SubnetID subnet_id) const; /// @brief Returns existing IPv6 lease for a given IPv6 address. /// @@ -86,8 +124,17 @@ public: /// @param duid client DUID /// @param iaid IA identifier /// + /// @return collection of IPv6 leases + Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const; + + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// @param subnet_id identifier of the subnet the lease must belong to + /// /// @return smart pointer to the lease (or NULL if a lease is not found) - Lease6Ptr getLease6(const DUID& duid, uint32_t iaid) const; + Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; /// @brief Updates IPv4 lease. /// @@ -156,19 +203,40 @@ Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const { return (Lease4Ptr()); } -Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr& ) const { +Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const { + return (Lease4Collection()); +} + +Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress , + SubnetID) const { return (Lease4Ptr()); } -Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId& ) const { +Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&, + SubnetID) const { return (Lease4Ptr()); } + +Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&, + SubnetID) const { + return (Lease4Ptr()); +} + +Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const { + return (Lease4Collection()); +} + Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const { return (Lease6Ptr()); } -Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const { +Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const { + return (Lease6Collection()); +} + +Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t, + SubnetID) const { return (Lease6Ptr()); } @@ -201,6 +269,8 @@ public: } }; +// This test checks if the LeaseMgr can be instantiated and that it +// parses parameters string properly. TEST_F(LeaseMgrTest, constructor) { // should not throw any exceptions here @@ -218,5 +288,9 @@ TEST_F(LeaseMgrTest, constructor) { // There's no point in calling any other methods in LeaseMgr, as they // are purely virtual, so we would only call Memfile_LeaseMgr methods. +// Those methods are just stubs that does not return anything. +// It seems likely that we will need to extend the memfile code for +// allocation engine tests, so we may implement tests that call +// Memfile_LeaseMgr methods then. }; // end of anonymous namespace From fd45bcdb5182e09d33b92e222772b54c6e744d01 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 10 Oct 2012 19:33:53 +0530 Subject: [PATCH 071/194] [2302] Add ConstElementPtr version of tests for all types of Element --- src/lib/cc/tests/data_unittests.cc | 298 ++++++++++++++++++++--------- 1 file changed, 211 insertions(+), 87 deletions(-) diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 30fa82de2b..d66e24b3d4 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -172,15 +172,14 @@ TEST(Element, from_and_to_json) { } -TEST(Element, create_and_value_throws) { - // this test checks whether elements throw exceptions if the - // incorrect type is requested - ElementPtr el; - ConstElementPtr cel; +template +void +testGetValueInt() { + T el; long int i; double d; bool b; - std::string s("asdf"); + std::string s; std::vector v; std::map m; @@ -197,23 +196,160 @@ TEST(Element, create_and_value_throws) { EXPECT_FALSE(el->getValue(s)); EXPECT_FALSE(el->getValue(v)); EXPECT_FALSE(el->getValue(m)); - EXPECT_EQ(i, 1); + EXPECT_EQ(1, i); +} - cel = Element::create(1); - EXPECT_NO_THROW(cel->intValue()); - EXPECT_THROW(cel->doubleValue(), TypeError); - EXPECT_THROW(cel->boolValue(), TypeError); - EXPECT_THROW(cel->stringValue(), TypeError); - EXPECT_THROW(cel->listValue(), TypeError); - EXPECT_THROW(cel->mapValue(), TypeError); - EXPECT_TRUE(cel->getValue(i)); - EXPECT_FALSE(cel->getValue(d)); - EXPECT_FALSE(cel->getValue(b)); - EXPECT_FALSE(cel->getValue(s)); - EXPECT_FALSE(cel->getValue(v)); - EXPECT_FALSE(cel->getValue(m)); - EXPECT_EQ(i, 1); +template +void +testGetValueDouble() { + T el; + long int i; + double d; + bool b; + std::string s; + std::vector v; + std::map m; + el = Element::create(1.1); + EXPECT_THROW(el->intValue(), TypeError); + EXPECT_NO_THROW(el->doubleValue()); + EXPECT_THROW(el->boolValue(), TypeError); + EXPECT_THROW(el->stringValue(), TypeError); + EXPECT_THROW(el->listValue(), TypeError); + EXPECT_THROW(el->mapValue(), TypeError); + EXPECT_FALSE(el->getValue(i)); + EXPECT_TRUE(el->getValue(d)); + EXPECT_FALSE(el->getValue(b)); + EXPECT_FALSE(el->getValue(s)); + EXPECT_FALSE(el->getValue(v)); + EXPECT_FALSE(el->getValue(m)); + EXPECT_EQ(1.1, d); +} + +template +void +testGetValueBool() { + T el; + long int i; + double d; + bool b; + std::string s; + std::vector v; + std::map m; + + el = Element::create(true); + EXPECT_THROW(el->intValue(), TypeError); + EXPECT_THROW(el->doubleValue(), TypeError); + EXPECT_NO_THROW(el->boolValue()); + EXPECT_THROW(el->stringValue(), TypeError); + EXPECT_THROW(el->listValue(), TypeError); + EXPECT_THROW(el->mapValue(), TypeError); + EXPECT_FALSE(el->getValue(i)); + EXPECT_FALSE(el->getValue(d)); + EXPECT_TRUE(el->getValue(b)); + EXPECT_FALSE(el->getValue(s)); + EXPECT_FALSE(el->getValue(v)); + EXPECT_FALSE(el->getValue(m)); + EXPECT_EQ(true, b); +} + +template +void +testGetValueString() { + T el; + long int i; + double d; + bool b; + std::string s; + std::vector v; + std::map m; + + el = Element::create("foo"); + EXPECT_THROW(el->intValue(), TypeError); + EXPECT_THROW(el->doubleValue(), TypeError); + EXPECT_THROW(el->boolValue(), TypeError); + EXPECT_NO_THROW(el->stringValue()); + EXPECT_THROW(el->listValue(), TypeError); + EXPECT_THROW(el->mapValue(), TypeError); + EXPECT_FALSE(el->getValue(i)); + EXPECT_FALSE(el->getValue(d)); + EXPECT_FALSE(el->getValue(b)); + EXPECT_TRUE(el->getValue(s)); + EXPECT_FALSE(el->getValue(v)); + EXPECT_FALSE(el->getValue(m)); + EXPECT_EQ("foo", s); +} + +template +void +testGetValueList() { + T el; + long int i; + double d; + bool b; + std::string s; + std::vector v; + std::map m; + + el = Element::createList(); + EXPECT_THROW(el->intValue(), TypeError); + EXPECT_THROW(el->doubleValue(), TypeError); + EXPECT_THROW(el->boolValue(), TypeError); + EXPECT_THROW(el->stringValue(), TypeError); + EXPECT_NO_THROW(el->listValue()); + EXPECT_THROW(el->mapValue(), TypeError); + EXPECT_FALSE(el->getValue(i)); + EXPECT_FALSE(el->getValue(d)); + EXPECT_FALSE(el->getValue(b)); + EXPECT_FALSE(el->getValue(s)); + EXPECT_TRUE(el->getValue(v)); + EXPECT_FALSE(el->getValue(m)); + EXPECT_EQ("[ ]", el->str()); +} + +template +void +testGetValueMap() { + T el; + long int i; + double d; + bool b; + std::string s; + std::vector v; + std::map m; + + el = Element::createMap(); + EXPECT_THROW(el->intValue(), TypeError); + EXPECT_THROW(el->doubleValue(), TypeError); + EXPECT_THROW(el->boolValue(), TypeError); + EXPECT_THROW(el->stringValue(), TypeError); + EXPECT_THROW(el->listValue(), TypeError); + EXPECT_NO_THROW(el->mapValue()); + EXPECT_FALSE(el->getValue(i)); + EXPECT_FALSE(el->getValue(d)); + EXPECT_FALSE(el->getValue(b)); + EXPECT_FALSE(el->getValue(s)); + EXPECT_FALSE(el->getValue(v)); + EXPECT_TRUE(el->getValue(m)); +} + +TEST(Element, create_and_value_throws) { + // this test checks whether elements throw exceptions if the + // incorrect type is requested + ElementPtr el; + ConstElementPtr cel; + long int i = 0; + double d = 0.0; + bool b = false; + std::string s("asdf"); + std::vector v; + std::map m; + ConstElementPtr tmp; + + testGetValueInt(); + testGetValueInt(); + + el = Element::create(1); i = 2; EXPECT_TRUE(el->setValue(i)); EXPECT_EQ(2, el->intValue()); @@ -231,24 +367,12 @@ TEST(Element, create_and_value_throws) { EXPECT_THROW(el->set("foo", el), TypeError); EXPECT_THROW(el->remove("foo"), TypeError); EXPECT_THROW(el->contains("foo"), TypeError); - ConstElementPtr tmp; EXPECT_FALSE(el->find("foo", tmp)); + testGetValueDouble(); + testGetValueDouble(); el = Element::create(1.1); - EXPECT_THROW(el->intValue(), TypeError); - EXPECT_NO_THROW(el->doubleValue()); - EXPECT_THROW(el->boolValue(), TypeError); - EXPECT_THROW(el->stringValue(), TypeError); - EXPECT_THROW(el->listValue(), TypeError); - EXPECT_THROW(el->mapValue(), TypeError); - EXPECT_FALSE(el->getValue(i)); - EXPECT_TRUE(el->getValue(d)); - EXPECT_FALSE(el->getValue(b)); - EXPECT_FALSE(el->getValue(s)); - EXPECT_FALSE(el->getValue(v)); - EXPECT_FALSE(el->getValue(m)); - EXPECT_EQ(d, 1.1); d = 2.2; EXPECT_TRUE(el->setValue(d)); EXPECT_EQ(2.2, el->doubleValue()); @@ -257,75 +381,75 @@ TEST(Element, create_and_value_throws) { EXPECT_FALSE(el->setValue(s)); EXPECT_FALSE(el->setValue(v)); EXPECT_FALSE(el->setValue(m)); + EXPECT_THROW(el->get(1), TypeError); + EXPECT_THROW(el->set(1, el), TypeError); + EXPECT_THROW(el->add(el), TypeError); + EXPECT_THROW(el->remove(1), TypeError); + EXPECT_THROW(el->size(), TypeError); + EXPECT_THROW(el->get("foo"), TypeError); + EXPECT_THROW(el->set("foo", el), TypeError); + EXPECT_THROW(el->remove("foo"), TypeError); + EXPECT_THROW(el->contains("foo"), TypeError); + EXPECT_FALSE(el->find("foo", tmp)); + + testGetValueBool(); + testGetValueBool(); el = Element::create(true); - EXPECT_THROW(el->intValue(), TypeError); - EXPECT_THROW(el->doubleValue(), TypeError); - EXPECT_NO_THROW(el->boolValue()); - EXPECT_THROW(el->stringValue(), TypeError); - EXPECT_THROW(el->listValue(), TypeError); - EXPECT_THROW(el->mapValue(), TypeError); - EXPECT_FALSE(el->getValue(i)); - EXPECT_FALSE(el->getValue(d)); - EXPECT_TRUE(el->getValue(b)); - EXPECT_FALSE(el->getValue(s)); - EXPECT_FALSE(el->getValue(v)); - EXPECT_FALSE(el->getValue(m)); - EXPECT_EQ(b, true); b = false; EXPECT_TRUE(el->setValue(b)); EXPECT_FALSE(el->boolValue()); + EXPECT_FALSE(el->setValue(i)); + EXPECT_FALSE(el->setValue(d)); + EXPECT_FALSE(el->setValue(s)); + EXPECT_FALSE(el->setValue(v)); + EXPECT_FALSE(el->setValue(m)); + EXPECT_THROW(el->get(1), TypeError); + EXPECT_THROW(el->set(1, el), TypeError); + EXPECT_THROW(el->add(el), TypeError); + EXPECT_THROW(el->remove(1), TypeError); + EXPECT_THROW(el->size(), TypeError); + EXPECT_THROW(el->get("foo"), TypeError); + EXPECT_THROW(el->set("foo", el), TypeError); + EXPECT_THROW(el->remove("foo"), TypeError); + EXPECT_THROW(el->contains("foo"), TypeError); + EXPECT_FALSE(el->find("foo", tmp)); + + testGetValueString(); + testGetValueString(); el = Element::create("foo"); - EXPECT_THROW(el->intValue(), TypeError); - EXPECT_THROW(el->doubleValue(), TypeError); - EXPECT_THROW(el->boolValue(), TypeError); - EXPECT_NO_THROW(el->stringValue()); - EXPECT_THROW(el->listValue(), TypeError); - EXPECT_THROW(el->mapValue(), TypeError); - EXPECT_FALSE(el->getValue(i)); - EXPECT_FALSE(el->getValue(d)); - EXPECT_FALSE(el->getValue(b)); - EXPECT_TRUE(el->getValue(s)); - EXPECT_FALSE(el->getValue(v)); - EXPECT_FALSE(el->getValue(m)); - EXPECT_EQ(s, "foo"); s = "bar"; EXPECT_TRUE(el->setValue(s)); EXPECT_EQ("bar", el->stringValue()); + EXPECT_FALSE(el->setValue(i)); + EXPECT_FALSE(el->setValue(b)); + EXPECT_FALSE(el->setValue(d)); + EXPECT_FALSE(el->setValue(v)); + EXPECT_FALSE(el->setValue(m)); + EXPECT_THROW(el->get(1), TypeError); + EXPECT_THROW(el->set(1, el), TypeError); + EXPECT_THROW(el->add(el), TypeError); + EXPECT_THROW(el->remove(1), TypeError); + EXPECT_THROW(el->size(), TypeError); + EXPECT_THROW(el->get("foo"), TypeError); + EXPECT_THROW(el->set("foo", el), TypeError); + EXPECT_THROW(el->remove("foo"), TypeError); + EXPECT_THROW(el->contains("foo"), TypeError); + EXPECT_FALSE(el->find("foo", tmp)); + + testGetValueList(); + testGetValueList(); el = Element::createList(); - EXPECT_THROW(el->intValue(), TypeError); - EXPECT_THROW(el->doubleValue(), TypeError); - EXPECT_THROW(el->boolValue(), TypeError); - EXPECT_THROW(el->stringValue(), TypeError); - EXPECT_NO_THROW(el->listValue()); - EXPECT_THROW(el->mapValue(), TypeError); - EXPECT_FALSE(el->getValue(i)); - EXPECT_FALSE(el->getValue(d)); - EXPECT_FALSE(el->getValue(b)); - EXPECT_FALSE(el->getValue(s)); - EXPECT_TRUE(el->getValue(v)); - EXPECT_FALSE(el->getValue(m)); - EXPECT_EQ("[ ]", el->str()); v.push_back(Element::create(1)); EXPECT_TRUE(el->setValue(v)); EXPECT_EQ("[ 1 ]", el->str()); - el = Element::createMap(); - EXPECT_THROW(el->intValue(), TypeError); - EXPECT_THROW(el->doubleValue(), TypeError); - EXPECT_THROW(el->boolValue(), TypeError); - EXPECT_THROW(el->stringValue(), TypeError); - EXPECT_THROW(el->listValue(), TypeError); - EXPECT_NO_THROW(el->mapValue()); - EXPECT_FALSE(el->getValue(i)); - EXPECT_FALSE(el->getValue(d)); - EXPECT_FALSE(el->getValue(b)); - EXPECT_FALSE(el->getValue(s)); - EXPECT_FALSE(el->getValue(v)); - EXPECT_TRUE(el->getValue(m)); + testGetValueMap(); + testGetValueMap(); + el = Element::createMap(); } // Helper for escape check; it puts the given string in a StringElement, From 7bb6309fbc34c32a0e37ea2b2175738b6110bcf7 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 10 Oct 2012 19:34:44 +0530 Subject: [PATCH 072/194] [2302] Check map data --- src/lib/cc/tests/data_unittests.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index d66e24b3d4..8794d6c735 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -331,6 +331,7 @@ testGetValueMap() { EXPECT_FALSE(el->getValue(s)); EXPECT_FALSE(el->getValue(v)); EXPECT_TRUE(el->getValue(m)); + EXPECT_EQ("{ }", el->str()); } TEST(Element, create_and_value_throws) { From 55178c917f014922f8213921a44f0443b3dc40f8 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Wed, 10 Oct 2012 19:42:35 +0530 Subject: [PATCH 073/194] [2302] Add a set test for map Element --- src/lib/cc/tests/data_unittests.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 8794d6c735..1565418352 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -451,6 +451,8 @@ TEST(Element, create_and_value_throws) { testGetValueMap(); el = Element::createMap(); + EXPECT_NO_THROW(el->set("foo", Element::create("bar"))); + EXPECT_EQ("{ \"foo\": \"bar\" }", el->str()); } // Helper for escape check; it puts the given string in a StringElement, From a0b7dd977eba590ecd94d4f08ed774a0727fbed0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 09:31:30 -0700 Subject: [PATCH 074/194] [master] a minor grammar fix to changelog entry. --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b5c2cf4cbb..f52f18c339 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 486. [bug]* jinmei All public header files for libb10-dns++ are now installed. Template configure.ac and utility AC macros for external projects - using the library is provided under the "examples" directory. + using the library are provided under the "examples" directory. The src/bin/host was moved as part of the examples (and not installed with other BIND 10 programs any more). (Trac #1870, git 4973e638d354d8b56dcadf71123ef23c15662021) From b6cfd9d20c49a0f292d37e663801987632e9d2a6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 09:32:40 -0700 Subject: [PATCH 075/194] [master] added changelog for #2244 --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index f52f18c339..868b02a335 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +487. [bug] jinmei + The bind10 process now terminates a component (subprocess) by the + "config remove Boss/components" bindctl command even if the + process crashes immediately before the command is sent to bind10. + Previously this led to an inconsistent state between the + configuration and an internal component list of bind10, and bind10 + kept trying to restart the component. A known specific case of + this problem is that b10-ddns could keep failing (due to lack of + dependency modules) and the administrator couldn't stop the + restart via bindctl. + (Trac #2244, git 7565788d06f216ab254008ffdfae16678bcd00e5) + 486. [bug]* jinmei All public header files for libb10-dns++ are now installed. Template configure.ac and utility AC macros for external projects From 56dd56426342f9fa3d63c436f35ba86208512ee0 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 10 Oct 2012 19:27:28 +0200 Subject: [PATCH 076/194] [2140] Renew, rebind timers added to lease4, recycle-timer removed --- src/lib/dhcp/lease_mgr.h | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index a6b4c6185c..7dd69d4413 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -53,19 +53,29 @@ struct Lease4 { /// @brief client identifier boost::shared_ptr client_id_; + /// @brief renewal timer + /// + /// Specifies renewal time. Although technically it is a property of IA container, + /// not the address itself, since our data model does not define separate IA + /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes + /// for the same IA, each must have consistent T1 and T2 values. Specified in + /// seconds since cltt. + uint32_t t1_; + + /// @brief rebinding timer + /// + /// Specifies rebinding time. Although technically it is a property of IA container, + /// not the address itself, since our data model does not define separate IA + /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes + /// for the same IA, each must have consistent T1 and T2 values. Specified in + /// seconds since cltt. + uint32_t t2_; + /// @brief valid lifetime /// /// Expressed as number of seconds since cltt uint32_t valid_lft_; - /// @brief Recycle timer - /// - /// Typically, the lease return to free pool immediately after it is released - /// or expired. This timer specifies number of seconds that it should be kept - /// after release or expiration, in case the client returns. This feature is not - /// currently used and this value is set to 0. - uint32_t recycle_time_; - /// @brief client last transmission time /// /// Specifies a timestamp, when last transmission from a client was received. @@ -103,6 +113,8 @@ struct Lease4 { /// Currently not used. It may be used for keeping comments made by the /// system administrator. std::string comments_; + + /// @todo: Add DHCPv4 failover related fields here }; /// @brief Pointer to a Lease4 structure. @@ -182,14 +194,6 @@ struct Lease6 { /// seconds since cltt. uint32_t t2_; - /// @brief Recycle timer - /// - /// Typically, the lease return to free pool immediately after it is released - /// or expired. This timer specifies number of seconds that it should be kept - /// after release or expiration, in case the client returns. This feature is not - /// currently used and this value is set to 0. - uint32_t recycle_time_; - /// @brief client last transmission time /// /// Specifies a timestamp, when last transmission from a client was received. @@ -227,6 +231,8 @@ struct Lease6 { /// /// This field is currently not used. std::string comments_; + + /// @todo: Add DHCPv6 failover related fields here }; /// @brief Pointer to a Lease6 structure. From a1983f43686919fae5dfb05ad8e6da07e5534111 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Wed, 10 Oct 2012 19:28:09 +0200 Subject: [PATCH 077/194] [2140] DUID no longer returns unsafe references, tests updated. --- src/lib/dhcp/duid.cc | 29 ++++------------------------- src/lib/dhcp/duid.h | 24 ++++++------------------ src/lib/dhcp/tests/duid_unittest.cc | 24 ++++++++++-------------- 3 files changed, 20 insertions(+), 57 deletions(-) diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc index 912fc4835a..db7ba25624 100644 --- a/src/lib/dhcp/duid.cc +++ b/src/lib/dhcp/duid.cc @@ -37,8 +37,8 @@ DUID::DUID(const uint8_t * data, size_t len) { duid_ = std::vector(data, data + len); } -const std::vector& DUID::getDuid() const { - return duid_; +const std::vector DUID::getDuid() const { + return (duid_); } DUID::DUIDType DUID::getType() const { @@ -71,30 +71,9 @@ ClientId::ClientId(const uint8_t *clientid, size_t len) :DUID(clientid, len) { } -/// constructor based on IOAddress -ClientId::ClientId(const isc::asiolink::IOAddress& addr) - :DUID(std::vector(4, 0)) { - if (addr.getFamily() != AF_INET) { - isc_throw(BadValue, "Client-id supports only IPv4 addresses"); - } - isc::util::writeUint32(addr, &duid_[0]); -} - -/// @brief returns reference to the client-id data +/// @brief returns a copy of client-id data const std::vector ClientId::getClientId() const { - return duid_; -} - -isc::asiolink::IOAddress ClientId::getAddress() const { - if (duid_.size() != sizeof(uint32_t)) { - isc_throw(BadValue, "This client-id is not an IPv4 address"); - } - - return isc::asiolink::IOAddress( isc::util::readUint32(&duid_[0]) ); -} - -bool ClientId::isAddress() const { - return (duid_.size() == sizeof(uint32_t)); + return (duid_); } // compares two client-ids diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h index 132d1d7e88..53257dbbb5 100644 --- a/src/lib/dhcp/duid.h +++ b/src/lib/dhcp/duid.h @@ -49,9 +49,12 @@ class DUID { /// @brief returns a const reference to the actual DUID value /// - /// Note: This reference is only valid as long as the DUID - /// that returned it. - const std::vector& getDuid() const; + /// Note: For safety reasons, this method returns a copy of data as + /// otherwise the reference would be only valid as long as the object that + /// returned it. In any case, this method should be used only sporadically. + /// If there are frequent uses, we must implement some other method + /// (e.g. storeSelf()) that will avoid data copying. + const std::vector getDuid() const; /// @brief returns DUID type DUIDType getType() const; @@ -80,25 +83,10 @@ class ClientId : DUID { /// constructor based on C-style data ClientId(const uint8_t *clientid, size_t len); - /// constructor based on IOAddress - /// - /// @throw BadValue if specified address is not IPv4 - ClientId(const isc::asiolink::IOAddress& addr); - /// @brief returns reference to the client-id data /// - /// This reference is only valid as long as the object - /// that returned it. const std::vector getClientId() const; - /// @brief return an IPv4 address represented by this client-id - /// - /// @throw BadValue if this client-id is not an IPv4 address - isc::asiolink::IOAddress getAddress() const; - - /// @brief returns if client-id is an address - bool isAddress() const; - // compares two client-ids bool operator == (const ClientId& other) const; diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc index d3da6e8997..93f304c285 100644 --- a/src/lib/dhcp/tests/duid_unittest.cc +++ b/src/lib/dhcp/tests/duid_unittest.cc @@ -33,6 +33,8 @@ using boost::scoped_ptr; namespace { +// This test verifies if the constructors are working as expected +// and process passed parameters. TEST(DuidTest, constructor) { uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; @@ -52,6 +54,8 @@ TEST(DuidTest, constructor) { EXPECT_EQ(DUID::DUID_LLT, duid2->getType()); } +// This test verifies if DUID size restrictions are implemented +// properly. TEST(DuidTest, size) { const int MAX_DUID_SIZE = 128; uint8_t data[MAX_DUID_SIZE + 1]; @@ -78,6 +82,8 @@ TEST(DuidTest, size) { OutOfRange); } +// This test verifies if the implementation supports all defined +// DUID types. TEST(DuidTest, getType) { uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6}; uint8_t en[] = {0, 2, 2, 3, 4, 5, 6}; @@ -98,6 +104,7 @@ TEST(DuidTest, getType) { EXPECT_EQ(DUID::DUID_UNKNOWN, duid_invalid->getType()); } +// This test checks if the comparison operators are sane. TEST(DuidTest, operators) { uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; uint8_t data2[] = {0, 1, 2, 3, 4}; @@ -118,38 +125,27 @@ TEST(DuidTest, operators) { EXPECT_TRUE(*duid1 != *duid3); } +// This test verifies if the ClientId constructors are working properly +// and passed parameters are used TEST(ClientIdTest, constructor) { IOAddress addr2("192.0.2.1"); IOAddress addr3("2001:db8:1::1"); uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; vector data2(data1, data1 + sizeof(data1)); - uint8_t data3[] = {192, 0 , 2, 1 }; // checks for C-style construtor (uint8_t * + len) scoped_ptr id1(new ClientId(data1, sizeof(data1))); vector vecdata = id1->getClientId(); EXPECT_EQ(data2, vecdata); - EXPECT_FALSE(id1->isAddress()); - EXPECT_THROW(id1->getAddress(), BadValue); // checks for vector-based constructor scoped_ptr id2(new ClientId(data2)); vecdata = id2->getClientId(); EXPECT_EQ(data2, vecdata); - EXPECT_FALSE(id1->isAddress()); - EXPECT_THROW(id1->getAddress(), BadValue); - - // checks for IOAddress based constructor - scoped_ptr id3(new ClientId(addr2)); - vecdata = id3->getClientId(); - EXPECT_TRUE(vecdata == vector(data3, data3 + 4)); - EXPECT_EQ("192.0.2.1", id3->getAddress().toText()); - - // should support v4 address only, v6 is a wrong address here - EXPECT_THROW(new ClientId(addr3), BadValue); } +// This test checks if the comparison operators are sane. TEST(ClientIdTest, operators) { uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6}; uint8_t data2[] = {0, 1, 2, 3, 4}; From b10b39f8ac7b07f6b429c0e09fcd9d0a3a873492 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 11:02:24 -0700 Subject: [PATCH 078/194] [2340] corrected the position of closing 'fi' for an 'if GXX = yes'. --- configure.ac | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 900ff371be..422681602c 100644 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,8 @@ if test $with_werror = 1; then CXXFLAGS="$CXXFLAGS_SAVED" fi +fi dnl GXX = yes + AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) # allow building programs with static link. we need to make it selective @@ -354,10 +356,11 @@ fi # TODO: check for _sqlite3.py module +# (g++ only check) # Python 3.2 has an unused parameter in one of its headers. This # has been reported, but not fixed as of yet, so we check if we need # to set -Wno-unused-parameter. -if test $werror_ok = 1; then +if test "X$GXX" = "Xyes" -a test $werror_ok = 1; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS=${PYTHON_INCLUDES} CXXFLAGS_SAVED="$CXXFLAGS" @@ -383,8 +386,6 @@ if test $werror_ok = 1; then CPPFLAGS="$CPPFLAGS_SAVED" fi -fi dnl GXX = yes - # produce PIC unless we disable shared libraries. need this for python bindings. if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then B10_CXXFLAGS="$B10_CXXFLAGS -fPIC" From 99fd73694c6768e36d79563be991d9132adbdd74 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 20:28:16 +0200 Subject: [PATCH 079/194] Remove extra comma at the end of enum This should fix compilation issue on sunstudio. --- src/lib/datasrc/memory/domaintree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 7e68f53bf0..272245d05f 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -947,7 +947,7 @@ public: PARTIALMATCH, ///< A superdomain node was found NOTFOUND, ///< Not even any superdomain was found /// \brief Returned by insert() if a node of the name already exists - ALREADYEXISTS, + ALREADYEXISTS }; /// \brief Allocate and construct \c DomainTree From 65cddb545cbf31c0372ab09e59a727d7cfdf1f89 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 20:42:46 +0200 Subject: [PATCH 080/194] Compilation fix: usual static_cast(NULL) Sunstudio is unhappy with plain NULL inside EXPECT_EQ and thinks it is int. This makes it think otherwise. --- src/lib/datasrc/tests/memory/zone_table_unittest.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 9cf1b3420c..401d43431b 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -101,9 +101,9 @@ TEST_F(ZoneTableTest, addZone) { zname1, holder1.release())); EXPECT_EQ(result::SUCCESS, result1.code); - EXPECT_EQ(NULL, result1.zone_data); + EXPECT_EQ(static_cast(NULL), result1.zone_data); // It got released by it - EXPECT_EQ(NULL, holder1.get()); + EXPECT_EQ(static_cast(NULL), holder1.get()); // Duplicate add doesn't replace the existing data. SegmentObjectHolder holder2( @@ -115,7 +115,7 @@ TEST_F(ZoneTableTest, addZone) { // The old one gets out EXPECT_EQ(data1, result2.zone_data); // It releases this one even when we replace the old zone - EXPECT_EQ(NULL, holder2.get()); + EXPECT_EQ(static_cast(NULL), holder2.get()); // We need to release the old one manually ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_); From f2b4e736bb7d4612c33aa2a648b0b9640a015154 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 13:50:21 -0700 Subject: [PATCH 081/194] [master] changelog for #2339 --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index 868b02a335..47ad2cbdd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +488. [build] jinmei + On configure, changed the search order for Python executable. + It first ties more specific file names such as "python3.2" before + more generic "python3". This will prevent configure failure on + Mac OS X that installs Python3 via recent versions of Homebrew. + (Trac #2339, git 88db890d8d1c64de49be87f03c24a2021bcf63da) + 487. [bug] jinmei The bind10 process now terminates a component (subprocess) by the "config remove Boss/components" bindctl command even if the From e2d1490eb82b26c501df4d0ed7323478b46a1cca Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 15:14:52 -0700 Subject: [PATCH 082/194] [master] minor wording fix in a README file. committing at my discretion. --- examples/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README b/examples/README index 12dbbbaaff..65f777b693 100644 --- a/examples/README +++ b/examples/README @@ -7,7 +7,7 @@ this directory. On the top (sub) directory (where this README file is stored), we provide a sample configure.ac and Makefile.am files for GNU automake -environments with helper autoconf macros to detect the available and +environments with helper autoconf macros to detect the availability and location of BIND 10 header files and library objects. You can use the configure.ac and Makefile.am files with macros under From 5fadea676fc73e068977173bb62b58d04fedf52c Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Thu, 11 Oct 2012 12:21:42 +0530 Subject: [PATCH 083/194] [2266] Update ChangeLog --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index 47ad2cbdd9..1ee100de20 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +489. [func] muks + The isc::dns::RRsetList class has been removed. It was now unused + inside the BIND 10 codebase, and the interface was considered + prone to misuse. + (Trac #2266, git 532ac3d0054f6a11b91ee369964f3a84dabc6040) + 488. [build] jinmei On configure, changed the search order for Python executable. It first ties more specific file names such as "python3.2" before From df196f7609757253c4f2f918cd91012bb3af1163 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 13:40:11 +0200 Subject: [PATCH 084/194] [2140] LeaseMgr minor tweaks, ChangeLog updated. --- ChangeLog | 6 +++++ src/lib/dhcp/lease_mgr.h | 49 +++++++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5aec82c8fb..40c28db882 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +4XX. [func] tomek + libdhcp++: An abstract API for lease database has been + implemented. It offers a common interface to all concrete + database backends. + (Trac #2140, git TBD) + bind10-devel-20120816 released on August 16, 2012 467. [bug] jelte diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index 7dd69d4413..4b7a1afa25 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -21,6 +21,37 @@ #include #include +/// @file dhcp/lease_mgr.h +/// @brief An abstract API for lease database +/// +/// This file contains declarations of Lease4, Lease6 and LeaseMgr classes. +/// They are essential components of the interface to any database backend. +/// Each concrete database backend (e.g. MySQL) will define a class derived +/// from LeaseMgr class. +/// +/// Failover considerations: +/// There are no intermediate plans to implement DHCPv4 failover +/// (draft-ietf-dhc-failover-12.txt). Currently (Oct. 2012) the DHCPv6 failover +/// is being defined in DHC WG in IETF (draft-ietf-dhcpv6-failover-requirements, +/// draft-ietf-dhcpv6-dailover-design), but the work is not advanced enough +/// for implementation plans yet. v4 failover requires additional parameters +/// to be kept with a lease. It is likely that v6 failover will require similar +/// fields. Such implementation will require database schema extension. +/// We have designed a way to expand/upgrade schemas during upgrades: a database +/// schema is versioned and sanity checks about required version will be done +/// upon start and/or upgrade. With this mechanism in place, we can add new +/// fields to the database. In particular we can use that capability to +/// introduce failover related fields. +/// +/// However, there is another approach that can be reliably used to provide +/// failover, even without the actual failover protocol implemented. As the +/// first backend will use MySQL, we will be able to use Multi-Master capability +/// offered by MySQL and use two separatate Kea instances connecting to the +/// same database. +/// +/// Nevertheless, we hope to have failover protocol eventually implemented in +/// the Kea. + namespace isc { namespace dhcp { @@ -102,12 +133,6 @@ struct Lease4 { /// @brief did we update PTR record for this lease? bool fqdn_rev_; - /// @brief additional options stored with this lease - /// - /// This field is currently not used. - /// @todo We need a way to store options in the databased. - Option::OptionCollection options_; - /// @brief Lease comments. /// /// Currently not used. It may be used for keeping comments made by the @@ -220,13 +245,6 @@ struct Lease6 { /// @brief did we update PTR record for this lease? bool fqdn_rev_; - /// @brief additional options stored with this lease - /// - /// That field is currently not used. We may keep extra options assigned - /// for leasequery and possibly other purposes. - /// @todo We need a way to store options in the databased. - Option::OptionCollection options_; - /// @brief Lease comments /// /// This field is currently not used. @@ -328,7 +346,7 @@ public: /// @param subnet_id identifier of the subnet that lease must belong to /// /// @return a pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const HWAddr& hwaddr, + virtual Lease4Ptr getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const = 0; /// @brief Returns existing IPv4 lease for specified client-id @@ -377,7 +395,7 @@ public: /// @param iaid IA identifier /// /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease6Collection getLease6(const DUID& duid, + virtual Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const = 0; /// @brief Returns existing IPv6 lease for a given DUID+IA combination @@ -457,7 +475,6 @@ protected: std::map parameters_; }; - }; // end of isc::dhcp namespace }; // end of isc namespace From 028bed9014b15facf1a29d3d4a822c9d14fc6411 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 14:30:28 +0200 Subject: [PATCH 085/194] [2269] Typo fixed. --- src/bin/dhcp6/config_parser.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index 9f75cd72f5..dbffc40863 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -361,9 +361,9 @@ public: // are allowed string txt = text_pool->stringValue(); - // first let's remove any spaces or tabs - boost::erase_all(txt, " "); - boost::erase_all(txt, "\t"); + // first let's remove any whitespaces + boost::erase_all(txt, " "); // space + boost::erase_all(txt, "\t"); // tabulation // Is this prefix/len notation? size_t pos = txt.find("/"); @@ -373,7 +373,7 @@ public: try { addr = IOAddress(txt.substr(0, pos)); - // start with the first charater after / + // start with the first character after / string prefix_len = txt.substr(pos + 1); // It is lexical cast to int and then downcast to uint8_t. From a78e560343b41f0f692c7903c938b2b2b24bf56b Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 14:55:04 +0200 Subject: [PATCH 086/194] [2237] Changes after review. --- ChangeLog | 2 +- src/lib/dhcp/addr_utilities.cc | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8e6d84ed82..c0763551f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 4XX. [func] tomek libdhcpsrv: The DHCP Configuration Manager is now able to store - information about IPv4 subnets and pool. It is still not possible + information about IPv4 subnets and pools. It is still not possible to configure that information. This will be implemented in a near future. diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc index bf9889a827..de1e8b46a2 100644 --- a/src/lib/dhcp/addr_utilities.cc +++ b/src/lib/dhcp/addr_utilities.cc @@ -35,10 +35,6 @@ const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, /// @brief mask used for first/last address calculation in a IPv6 prefix const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; -} - -namespace isc { -namespace dhcp { /// @brief calculates the first IPv6 address in a IPv6 prefix /// @@ -92,21 +88,12 @@ isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& pref uint8_t len) { uint32_t addr = prefix; if (len > 32) { - isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4"); } return (IOAddress(addr & (~bitMask4[len]))); } -isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, - uint8_t len) { - if (prefix.getFamily() == AF_INET) { - return firstAddrInPrefix4(prefix, len); - } else { - return firstAddrInPrefix6(prefix, len); - } -} - /// @brief calculates the last IPv4 address in a IPv4 prefix /// /// Note: This is a private function. Do not use it directly. @@ -118,7 +105,7 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi uint8_t len) { uint32_t addr = prefix; if (len>32) { - isc_throw(BadValue, "Too large netmask. 0..32 is allowed in IPv4"); + isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4"); } return (IOAddress(addr | bitMask4[len])); @@ -167,6 +154,20 @@ isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefi return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed)); } +}; // end of anonymous namespace + +namespace isc { +namespace dhcp { + +isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix, + uint8_t len) { + if (prefix.getFamily() == AF_INET) { + return firstAddrInPrefix4(prefix, len); + } else { + return firstAddrInPrefix6(prefix, len); + } +} + isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix, uint8_t len) { if (prefix.getFamily() == AF_INET) { From 64bba5b1c6a4155e8179217c10e998fbe3c2822c Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 15:33:40 +0200 Subject: [PATCH 087/194] [master] Compilation fix for Solaris in LeaseMgr --- src/lib/dhcp/lease_mgr.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc index 927c328526..9352c9bf8c 100644 --- a/src/lib/dhcp/lease_mgr.cc +++ b/src/lib/dhcp/lease_mgr.cc @@ -54,7 +54,8 @@ LeaseMgr::LeaseMgr(const std::string& dbconfig) { } std::string LeaseMgr::getParameter(const std::string& name) const { - map::const_iterator param = parameters_.find(name); + std::map::const_iterator param + = parameters_.find(name); if (param == parameters_.end()) { isc_throw(BadValue, "Parameter not found"); } From 50f42396efa039096dd74c13425d1a2bc0efc978 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 16:07:32 +0200 Subject: [PATCH 088/194] [master] Warning shown on Debian6 with g++ 4.4 removed. --- src/lib/dhcp/lease_mgr.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc index 9352c9bf8c..d09bd583c0 100644 --- a/src/lib/dhcp/lease_mgr.cc +++ b/src/lib/dhcp/lease_mgr.cc @@ -37,8 +37,10 @@ LeaseMgr::LeaseMgr(const std::string& dbconfig) { vector tokens; - boost::split(tokens, dbconfig, boost::is_any_of("\t ")); - + // we need to pass a string to is_any_of, not just char *. Otherwise there + // are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4 + // /bits/stl_algo.h:2178 "array subscript is above array bounds" + boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") )); BOOST_FOREACH(std::string token, tokens) { size_t pos = token.find("="); if (pos != string::npos) { From 0375b6599cabcefe4e9cf1b326f54b3519bde8b3 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 11 Oct 2012 16:14:28 +0200 Subject: [PATCH 089/194] [master] Compilation fix for OpenBSD. For some reason comparison operator for std::vector does not work with EXPECT_EQ macro on OpenBSD. --- src/lib/dhcp/tests/duid_unittest.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc index 93f304c285..aaf6d910bc 100644 --- a/src/lib/dhcp/tests/duid_unittest.cc +++ b/src/lib/dhcp/tests/duid_unittest.cc @@ -45,11 +45,11 @@ TEST(DuidTest, constructor) { scoped_ptr duid2(new DUID(data2)); vector vecdata = duid1->getDuid(); - EXPECT_EQ(data2, vecdata); + EXPECT_TRUE(data2 == vecdata); EXPECT_EQ(DUID::DUID_LLT, duid1->getType()); vecdata = duid2->getDuid(); - EXPECT_EQ(data2, vecdata); + EXPECT_TRUE(data2 == vecdata); EXPECT_EQ(DUID::DUID_LLT, duid2->getType()); } @@ -137,12 +137,12 @@ TEST(ClientIdTest, constructor) { // checks for C-style construtor (uint8_t * + len) scoped_ptr id1(new ClientId(data1, sizeof(data1))); vector vecdata = id1->getClientId(); - EXPECT_EQ(data2, vecdata); + EXPECT_TRUE(data2 == vecdata); // checks for vector-based constructor scoped_ptr id2(new ClientId(data2)); vecdata = id2->getClientId(); - EXPECT_EQ(data2, vecdata); + EXPECT_TRUE(data2 == vecdata); } // This test checks if the comparison operators are sane. From 73f22adb3400726277f06f9d1f8760e43bcc133f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 10:13:58 -0700 Subject: [PATCH 090/194] [2340] explictly define destructor of mostly empty template class. without this new versions of clang++ seem to complain that instantiation of the class results in an unused variable. --- src/lib/util/locks.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h index da9e9cd89c..3b51a5ad44 100644 --- a/src/lib/util/locks.h +++ b/src/lib/util/locks.h @@ -42,13 +42,14 @@ class upgradable_mutex { template class sharable_lock { public: - sharable_lock(T) { } + sharable_lock(T) {} }; template class scoped_lock { public: - scoped_lock(T) { } + scoped_lock(T) {} + ~scoped_lock() {} void lock() {} void unlock() {} From 1426bb5c63cd7d79a4a16aedf619aff5ea2394aa Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 12:20:34 -0700 Subject: [PATCH 091/194] [2340] specify thread flag as part CPPFLAGS, not LDFLAGS. newer version of clang treats the latter as a fatal error. --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c6a2f01ffb..8495dc97a6 100644 --- a/configure.ac +++ b/configure.ac @@ -743,7 +743,8 @@ else done fi -LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus $MULTITHREADING_FLAG" +LOG4CPLUS_INCLUDES="$LOG4CPLUS_INCLUDES $MULTITHREADING_FLAG" +LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus" AC_SUBST(LOG4CPLUS_LIBS) AC_SUBST(LOG4CPLUS_INCLUDES) From 6fc2942362354dd4960a25d644095f6eb44b6023 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 12:34:55 -0700 Subject: [PATCH 092/194] [2340] removed an effectively unused variable. clang would complain. --- src/lib/datasrc/memory_datasrc_link.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc index cbbc6db4c1..857223f699 100644 --- a/src/lib/datasrc/memory_datasrc_link.cc +++ b/src/lib/datasrc/memory_datasrc_link.cc @@ -129,7 +129,7 @@ checkConfig(ConstElementPtr config, ElementPtr errors) { result = false; } else { try { - RRClass rrc(config->get("class")->stringValue()); + RRClass(config->get("class")->stringValue()); } catch (const isc::Exception& rrce) { addError(errors, "Error parsing class config for memory backend: " + From a017390f15c132fdb08f244f2e111ae6964e4cc4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 14:12:02 -0700 Subject: [PATCH 093/194] [2340] specify -pthread in CXXFLAGS and via system-indepedent variable. specifying it in LDFLAGS would trigger build error with some versions of clang. hardcoding "-pthread" is less portable. --- src/bin/resolver/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index 47e242c1b4..c56289c08f 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -62,6 +62,7 @@ b10_resolver_SOURCES += common.cc common.h nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h +b10_resolver_CXXFLAGS = $(AM_CPPFLAGS) $(MULTITHREADING_FLAG) b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la @@ -77,7 +78,6 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la -b10_resolver_LDFLAGS = -pthread # TODO: config.h.in is wrong because doesn't honor pkgdatadir # and can't use @datadir@ because doesn't expand default ${prefix} From 9ef297b7faf705148c7aed37df6c796e55660062 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 21:19:23 -0700 Subject: [PATCH 094/194] [2340] Revert "[2340] specify -pthread in CXXFLAGS and via system-indepedent variable." This reverts commit d62a1e4250c88e765b3cd7f77fe9a948db0a565d. I believe the sense of the change is correct, but it has effects on other OSes, so it's probably better to use clang specific workaround (will commit it later). --- src/bin/resolver/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am index c56289c08f..47e242c1b4 100644 --- a/src/bin/resolver/Makefile.am +++ b/src/bin/resolver/Makefile.am @@ -62,7 +62,6 @@ b10_resolver_SOURCES += common.cc common.h nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h -b10_resolver_CXXFLAGS = $(AM_CPPFLAGS) $(MULTITHREADING_FLAG) b10_resolver_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la b10_resolver_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la @@ -78,6 +77,7 @@ b10_resolver_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common b10_resolver_LDADD += $(top_builddir)/src/lib/cache/libb10-cache.la b10_resolver_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la b10_resolver_LDADD += $(top_builddir)/src/lib/resolve/libb10-resolve.la +b10_resolver_LDFLAGS = -pthread # TODO: config.h.in is wrong because doesn't honor pkgdatadir # and can't use @datadir@ because doesn't expand default ${prefix} From b770f510961784b1cbf766a1b40fb026788d8cb0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 21:20:52 -0700 Subject: [PATCH 095/194] [2340] Revert "[2340] specify thread flag as part CPPFLAGS, not LDFLAGS." This reverts commit daf81c8846bf0b4e4308068a019c96beb09c5199. I believe the sense of the change is correct, but it has effects on other OSes, so it's probably better to use clang specific workaround (will commit it later). --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8495dc97a6..c6a2f01ffb 100644 --- a/configure.ac +++ b/configure.ac @@ -743,8 +743,7 @@ else done fi -LOG4CPLUS_INCLUDES="$LOG4CPLUS_INCLUDES $MULTITHREADING_FLAG" -LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus" +LOG4CPLUS_LIBS="$LOG4CPLUS_LIBS -llog4cplus $MULTITHREADING_FLAG" AC_SUBST(LOG4CPLUS_LIBS) AC_SUBST(LOG4CPLUS_INCLUDES) From b05952bb39bc7f038e65c5e61aca711278096dee Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 7 Oct 2012 23:53:26 -0700 Subject: [PATCH 096/194] [2340] added workaround for the clang++ "unused arguments" warn/error. by specifying -Qunused-arguments. Also, as a cleanup for better organization I moved compiler specific settings to earlier part of configure.ac, where this workaround was added. --- configure.ac | 185 +++++++++++++++++++++++++++------------------------ 1 file changed, 98 insertions(+), 87 deletions(-) diff --git a/configure.ac b/configure.ac index c6a2f01ffb..900ff371be 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,104 @@ AC_TRY_LINK([],[], ]) LDFLAGS=$LDFLAGS_SAVED +# Compiler dependent settings: define some mandatory CXXFLAGS here. +# We also use a separate variable B10_CXXFLAGS. This will (and should) be +# used as the default value for each specific AM_CXXFLAGS: +# AM_CXXFLAGS = $(B10_CXXFLAGS) +# AM_CXXFLAGS += ... # add module specific flags +# We need this so that we can disable some specific compiler warnings per +# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since +# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot +# specify the default warning flags in CXXFLAGS and let specific modules +# "override" the default. + +# This may be used to try linker flags. +AC_DEFUN([BIND10_CXX_TRY_FLAG], [ + AC_MSG_CHECKING([whether $CXX supports $1]) + + bind10_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $1" + + AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])], + [bind10_cxx_flag=yes], [bind10_cxx_flag=no]) + CXXFLAGS="$bind10_save_CXXFLAGS" + + if test "x$bind10_cxx_flag" = "xyes"; then + ifelse([$2], , :, [$2]) + else + ifelse([$3], , :, [$3]) + fi + + AC_MSG_RESULT([$bind10_cxx_flag]) +]) + +# SunStudio compiler requires special compiler options for boost +# (http://blogs.sun.com/sga/entry/boost_mini_howto) +if test "$SUNCXX" = "yes"; then +CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic" +MULTITHREADING_FLAG="-mt" +fi + +# Newer versions of clang++ promotes "unused driver arguments" warnings to +# a fatal error with -Werror, causing build failure. Since we use multiple +# compilers on multiple systems, this can easily happen due to settings for +# non clang++ environments that could be just ignored otherwise. It can also +# happen if clang++ is used via ccache. So, although probably suboptimal, +# we suppress this particular warning. Note that it doesn't weaken checks +# on the source code. +if test "$CLANGPP" = "yes"; then + B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments" +fi + +BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers], + [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"]) +AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) + +# gcc specific settings: +if test "X$GXX" = "Xyes"; then +B10_CXXFLAGS="$B10_CXXFLAGS -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" +case "$host" in +*-solaris*) + MULTITHREADING_FLAG=-pthreads + # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces + B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces" + ;; +*) + MULTITHREADING_FLAG=-pthread + ;; +esac + +# Don't use -Werror if configured not to +AC_ARG_WITH(werror, + AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]), + [ + case "${withval}" in + yes) with_werror=1 ;; + no) with_werror=0 ;; + *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;; + esac], + [with_werror=1]) + +werror_ok=0 + +# Certain versions of gcc (g++) have a bug that incorrectly warns about +# the use of anonymous name spaces even if they're closed in a single +# translation unit. For these versions we have to disable -Werror. +if test $with_werror = 1; then + CXXFLAGS_SAVED="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror" + AC_MSG_CHECKING(for in-TU anonymous namespace breakage) + AC_TRY_COMPILE([namespace { class Foo {}; } + namespace isc {class Bar {Foo foo_;};} ],, + [AC_MSG_RESULT(no) + werror_ok=1 + B10_CXXFLAGS="$B10_CXXFLAGS -Werror"], + [AC_MSG_RESULT(yes)]) + CXXFLAGS="$CXXFLAGS_SAVED" +fi + +AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) + # allow building programs with static link. we need to make it selective # because loadable modules cannot be statically linked. AC_ARG_ENABLE([static-link], @@ -256,91 +354,6 @@ fi # TODO: check for _sqlite3.py module -# Compiler dependent settings: define some mandatory CXXFLAGS here. -# We also use a separate variable B10_CXXFLAGS. This will (and should) be -# used as the default value for each specific AM_CXXFLAGS: -# AM_CXXFLAGS = $(B10_CXXFLAGS) -# AM_CXXFLAGS += ... # add module specific flags -# We need this so that we can disable some specific compiler warnings per -# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since -# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot -# specify the default warning flags in CXXFLAGS and let specific modules -# "override" the default. - -# This may be used to try linker flags. -AC_DEFUN([BIND10_CXX_TRY_FLAG], [ - AC_MSG_CHECKING([whether $CXX supports $1]) - - bind10_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $1" - - AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])], - [bind10_cxx_flag=yes], [bind10_cxx_flag=no]) - CXXFLAGS="$bind10_save_CXXFLAGS" - - if test "x$bind10_cxx_flag" = "xyes"; then - ifelse([$2], , :, [$2]) - else - ifelse([$3], , :, [$3]) - fi - - AC_MSG_RESULT([$bind10_cxx_flag]) -]) - -# SunStudio compiler requires special compiler options for boost -# (http://blogs.sun.com/sga/entry/boost_mini_howto) -if test "$SUNCXX" = "yes"; then -CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic" -MULTITHREADING_FLAG="-mt" -fi - -BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers], - [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"]) -AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) - -# gcc specific settings: -if test "X$GXX" = "Xyes"; then -B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" -case "$host" in -*-solaris*) - MULTITHREADING_FLAG=-pthreads - # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces - B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces" - ;; -*) - MULTITHREADING_FLAG=-pthread - ;; -esac - -# Don't use -Werror if configured not to -AC_ARG_WITH(werror, - AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]), - [ - case "${withval}" in - yes) with_werror=1 ;; - no) with_werror=0 ;; - *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;; - esac], - [with_werror=1]) - -werror_ok=0 - -# Certain versions of gcc (g++) have a bug that incorrectly warns about -# the use of anonymous name spaces even if they're closed in a single -# translation unit. For these versions we have to disable -Werror. -if test $with_werror = 1; then - CXXFLAGS_SAVED="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror" - AC_MSG_CHECKING(for in-TU anonymous namespace breakage) - AC_TRY_COMPILE([namespace { class Foo {}; } - namespace isc {class Bar {Foo foo_;};} ],, - [AC_MSG_RESULT(no) - werror_ok=1 - B10_CXXFLAGS="$B10_CXXFLAGS -Werror"], - [AC_MSG_RESULT(yes)]) - CXXFLAGS="$CXXFLAGS_SAVED" -fi - # Python 3.2 has an unused parameter in one of its headers. This # has been reported, but not fixed as of yet, so we check if we need # to set -Wno-unused-parameter. @@ -372,8 +385,6 @@ fi fi dnl GXX = yes -AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) - # produce PIC unless we disable shared libraries. need this for python bindings. if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then B10_CXXFLAGS="$B10_CXXFLAGS -fPIC" From 5cc254b16bd0120a7bacd33f90936333fbecfd79 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 16:43:16 -0700 Subject: [PATCH 097/194] [2340] added comment about why we need to explicitly define empty dtor. --- src/lib/util/locks.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h index 3b51a5ad44..f33ff960e3 100644 --- a/src/lib/util/locks.h +++ b/src/lib/util/locks.h @@ -49,6 +49,9 @@ template class scoped_lock { public: scoped_lock(T) {} + + // We need to define this explicitly. Some versions of clang++ would + // complain about this otherwise. See Trac ticket #2340 ~scoped_lock() {} void lock() {} From 3e2a372012e633d017a97029d13894e743199741 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 11:02:24 -0700 Subject: [PATCH 098/194] [2340] corrected the position of closing 'fi' for an 'if GXX = yes'. --- configure.ac | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 900ff371be..422681602c 100644 --- a/configure.ac +++ b/configure.ac @@ -166,6 +166,8 @@ if test $with_werror = 1; then CXXFLAGS="$CXXFLAGS_SAVED" fi +fi dnl GXX = yes + AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1) # allow building programs with static link. we need to make it selective @@ -354,10 +356,11 @@ fi # TODO: check for _sqlite3.py module +# (g++ only check) # Python 3.2 has an unused parameter in one of its headers. This # has been reported, but not fixed as of yet, so we check if we need # to set -Wno-unused-parameter. -if test $werror_ok = 1; then +if test "X$GXX" = "Xyes" -a test $werror_ok = 1; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS=${PYTHON_INCLUDES} CXXFLAGS_SAVED="$CXXFLAGS" @@ -383,8 +386,6 @@ if test $werror_ok = 1; then CPPFLAGS="$CPPFLAGS_SAVED" fi -fi dnl GXX = yes - # produce PIC unless we disable shared libraries. need this for python bindings. if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then B10_CXXFLAGS="$B10_CXXFLAGS -fPIC" From 55d94bb5f735252542908e9565d7a49c9a6e0966 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 27 Sep 2012 16:00:48 +0200 Subject: [PATCH 099/194] [2278] A number of cppcheck fixes and whitespace things, while i was there --- src/lib/cc/data.cc | 8 ++++---- src/lib/dhcp/tests/iface_mgr_unittest.cc | 2 +- src/lib/dns/benchmarks/message_renderer_bench.cc | 2 +- src/lib/dns/benchmarks/rdatarender_bench.cc | 5 +++-- src/lib/dns/python/edns_python.cc | 3 +-- src/lib/dns/rdata/generic/afsdb_18.cc | 1 - src/lib/dns/tests/rrparamregistry_unittest.cc | 4 ++-- src/lib/log/compiler/message.cc | 10 ++++++---- src/lib/log/tests/logger_example.cc | 1 - src/lib/util/hash/sha1.cc | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index 6ec243a1a0..47b1eb1fa4 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -413,7 +413,7 @@ from_stringstream_number(std::istream &in, int &pos) { isc_throw(JSONError, std::string("Number overflow: ") + number); } } - + if (is_double) { return (Element::create(d)); } else { @@ -501,7 +501,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line, ConstElementPtr value = Element::fromJSON(in, file, line, pos); map->set(key, value); - + skip_to(in, file, line, pos, ",}", WHITESPACE); c = in.get(); pos++; @@ -942,7 +942,7 @@ removeIdentical(ConstElementPtr a, ConstElementPtr b) { if (!b) { return (result); } - + if (a->getType() != Element::map || b->getType() != Element::map) { isc_throw(TypeError, "Non-map Elements passed to removeIdentical"); } @@ -965,7 +965,7 @@ merge(ElementPtr element, ConstElementPtr other) { other->getType() != Element::map) { isc_throw(TypeError, "merge arguments not MapElements"); } - + const std::map& m = other->mapValue(); for (std::map::const_iterator it = m.begin(); it != m.end() ; ++it) { diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 2c7b86b182..462910bd81 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -1096,7 +1096,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac addr = addr.substr(0, addr.find_first_of(" ")); IOAddress a(addr); iface->addAddress(a); - } else if(line.find("Metric")) { + } else if(line.find("Metric") != string::npos) { // flags if (line.find("UP") != string::npos) { iface->flag_up_ = true; diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc index 6376498087..abf3192690 100644 --- a/src/lib/dns/benchmarks/message_renderer_bench.cc +++ b/src/lib/dns/benchmarks/message_renderer_bench.cc @@ -39,7 +39,7 @@ public: renderer_(NULL), names_(names) {} - MessageRendererBenchMark() { + ~MessageRendererBenchMark() { delete renderer_; } unsigned int run() { diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc index 38ee2ace7c..65576ee645 100644 --- a/src/lib/dns/benchmarks/rdatarender_bench.cc +++ b/src/lib/dns/benchmarks/rdatarender_bench.cc @@ -42,9 +42,10 @@ template class RdataRenderBenchMark { public: RdataRenderBenchMark(const vector& dataset) : - dataset_(dataset) + dataset_(dataset), + renderer_(NULL) {} - RdataRenderBenchMark() { + ~RdataRenderBenchMark() { delete renderer_; } unsigned int run() { diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc index 8f0f1a4213..e9d54c1a26 100644 --- a/src/lib/dns/python/edns_python.cc +++ b/src/lib/dns/python/edns_python.cc @@ -269,7 +269,6 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { const PyObject* rrtype; const PyObject* rrttl; const PyObject* rdata; - s_EDNS* edns_obj = NULL; assert(null_self == NULL); @@ -277,7 +276,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) { &rrclass_type, &rrclass, &rrtype_type, &rrtype, &rrttl_type, &rrttl, &rdata_type, &rdata)) { uint8_t extended_rcode; - edns_obj = PyObject_New(s_EDNS, &edns_type); + s_EDNS* edns_obj = PyObject_New(s_EDNS, &edns_type); if (edns_obj == NULL) { return (NULL); } diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc index 6afc4de51e..ec76ee0e00 100644 --- a/src/lib/dns/rdata/generic/afsdb_18.cc +++ b/src/lib/dns/rdata/generic/afsdb_18.cc @@ -57,7 +57,6 @@ AFSDB::AFSDB(const std::string& afsdb_str) : try { const uint32_t subtype = tokenToNum(getToken(iss)); const Name servername(getToken(iss)); - string server; if (!iss.eof()) { isc_throw(InvalidRdataText, "Unexpected input for AFSDB" diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc index d2bec5c0d4..7b0455d2b0 100644 --- a/src/lib/dns/tests/rrparamregistry_unittest.cc +++ b/src/lib/dns/tests/rrparamregistry_unittest.cc @@ -60,7 +60,7 @@ protected: // we assume class/type numbers are officially unassigned. If not we'll // need to update the test cases. - static const uint16_t test_class_code = 65533; + static const uint16_t test_class_code = 65533; static const uint16_t test_type_code = 65534; static const string test_class_str; static const string test_type_str; @@ -77,7 +77,7 @@ TEST_F(RRParamRegistryTest, addRemove) { // the first removal attempt should succeed EXPECT_TRUE(RRParamRegistry::getRegistry().removeType(test_type_code)); - // then toText() should treat it as an "unknown" + // then toText() should treat it as an "unknown" EXPECT_EQ(test_type_unknown_str, RRType(test_type_code).toText()); // attempt of removing non-existent mapping should result in 'false' EXPECT_FALSE(RRParamRegistry::getRegistry().removeType(test_type_code)); diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index 86c5f200e9..ef62b2f331 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -109,11 +109,13 @@ currentTime() { // Get a text representation of the current time. time_t curtime; time(&curtime); - char* buffer = ctime(&curtime); + struct tm* timeinfo; + timeinfo = localtime(&curtime); - // Convert to string and strip out the trailing newline - string current_time = buffer; - return (isc::util::str::trim(current_time)); + char buffer[80]; + strftime(buffer, 80, "%a %b %d %Y %H:%M", timeinfo); + + return (std::string(buffer)); } diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc index 6f95e5d1a3..08c9084ddd 100644 --- a/src/lib/log/tests/logger_example.cc +++ b/src/lib/log/tests/logger_example.cc @@ -118,7 +118,6 @@ int main(int argc, char** argv) { LoggerSpecification cur_spec(ROOT_NAME);// Current specification OutputOption cur_opt; // Current output option vector loggers; // Set of logger specifications - vector options; // Output options for logger std::string severity; // Severity set for logger // Initialize logging system - set the root logger name. diff --git a/src/lib/util/hash/sha1.cc b/src/lib/util/hash/sha1.cc index 091e29316a..1fdadfdfcf 100644 --- a/src/lib/util/hash/sha1.cc +++ b/src/lib/util/hash/sha1.cc @@ -48,7 +48,7 @@ * merchantability of this software or the suitability of this * software for any particular purpose. It is provided "as is" * without express or implied warranty of any kind. - * + * */ #include @@ -79,7 +79,7 @@ SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) { return ((x) ^ (y) ^ (z)); } -static inline int +static inline int SHA1CircularShift(uint8_t bits, uint32_t word) { return ((word << bits) | (word >> (32 - bits))); } From 998645aaeb5a7369ae94e71f84d40512ffee996b Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 27 Sep 2012 16:28:25 +0200 Subject: [PATCH 100/194] [2278] remove a bit of dead code --- src/lib/cache/tests/rrset_entry_unittest.cc | 3 -- src/lib/dns/tests/rrparamregistry_unittest.cc | 1 + src/lib/resolve/recursive_query.cc | 40 +++++++++---------- .../resolve/tests/recursive_query_unittest.cc | 39 ++---------------- src/lib/util/tests/lru_list_unittest.cc | 6 +-- 5 files changed, 28 insertions(+), 61 deletions(-) diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc index c7c3c6eb69..a6ac27e8c3 100644 --- a/src/lib/cache/tests/rrset_entry_unittest.cc +++ b/src/lib/cache/tests/rrset_entry_unittest.cc @@ -50,9 +50,6 @@ class DerivedRRsetEntry: public RRsetEntry { public: DerivedRRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) : RRsetEntry(rrset, level) {}; - void updateTTLForTest() { - - } }; #define TEST_TTL 100 diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc index 7b0455d2b0..c155b53032 100644 --- a/src/lib/dns/tests/rrparamregistry_unittest.cc +++ b/src/lib/dns/tests/rrparamregistry_unittest.cc @@ -37,6 +37,7 @@ protected: { ostringstream oss1; oss1 << test_class_code; + // cppcheck-suppress useInitializationList test_class_unknown_str = "CLASS" + oss1.str(); ostringstream oss2; diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc index 8d03c1c55d..c5b280f5b2 100644 --- a/src/lib/resolve/recursive_query.cc +++ b/src/lib/resolve/recursive_query.cc @@ -176,7 +176,7 @@ class RunningQuery : public IOFetch::Callback { class ResolverNSASCallback : public isc::nsas::AddressRequestCallback { public: ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {} - + void success(const isc::nsas::NameserverAddress& address) { // Success callback, send query to found namesever LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS) @@ -184,7 +184,7 @@ public: rq_->nsasCallbackCalled(); rq_->sendTo(address); } - + void unreachable() { // Nameservers unreachable: drop query or send servfail? LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL); @@ -261,7 +261,7 @@ private: bool done_; // If we have a client timeout, we call back with a failure message, - // but we do not stop yet. We use this variable to make sure we + // but we do not stop yet. We use this variable to make sure we // don't call back a second time later bool callback_called_; @@ -270,7 +270,7 @@ private: // Reference to our cache isc::cache::ResolverCache& cache_; - + // the 'current' zone we are in (i.e.) we start out at the root, // and for each delegation this gets updated with the zone the // delegation points to. @@ -278,7 +278,7 @@ private: // of the call we use it in take a string, we need update those // too). std::string cur_zone_; - + // This is the handler we pass on to the NSAS; it is called when // the NSAS has an address for us to query boost::shared_ptr nsas_callback_; @@ -295,7 +295,7 @@ private: // The moment in time we sent a query to the nameserver above. struct timeval current_ns_qsent_time; - + // RunningQuery deletes itself when it is done. In order for us // to do this safely, we must make sure that there are no events // that might call back to it. There are two types of events in @@ -365,7 +365,7 @@ private: io_.get_io_service().post(query); } } - + // 'general' send, ask the NSAS to give us an address. void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) { protocol_ = protocol; // Store protocol being used for this @@ -397,7 +397,7 @@ private: nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_); } } - + // Called by our NSAS callback handler so we know we do not have // an outstanding NSAS call anymore. void nsasCallbackCalled() { @@ -422,13 +422,13 @@ private: // here (classify() will set it when it walks through // the cname chain to verify it). Name cname_target(question_.getName()); - + isc::resolve::ResponseClassifier::Category category = isc::resolve::ResponseClassifier::classify( question_, incoming, cname_target, cname_count_); bool found_ns = false; - + switch (category) { case isc::resolve::ResponseClassifier::ANSWER: case isc::resolve::ResponseClassifier::ANSWERCNAME: @@ -569,7 +569,7 @@ private: // SERVFAIL if we get FORMERR instead } goto SERVFAIL; - + default: SERVFAIL: // Some error in received packet it. Report it and return SERVFAIL @@ -718,7 +718,7 @@ public: ++outstanding_events_; lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this)); } - + // Setup the timer to send an answer (client_timeout) if (client_timeout >= 0) { client_timer.expires_from_now( @@ -726,7 +726,7 @@ public: ++outstanding_events_; client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this)); } - + doLookup(); } @@ -741,7 +741,7 @@ public: --outstanding_events_; stop(); } - + // called if we have a client timeout; if our callback has // not been called, call it now. But do not stop. void clientTimeout() { @@ -810,7 +810,7 @@ public: // XXX is this the place for TCP retry? assert(outstanding_events_ > 0); --outstanding_events_; - + if (!done_ && result != IOFetch::TIME_OUT) { // we got an answer @@ -890,7 +890,7 @@ public: stop(); } } - + // Clear the answer parts of answer_message, and set the rcode // to servfail void makeSERVFAIL() { @@ -1096,7 +1096,7 @@ RecursiveQuery::resolve(const QuestionPtr& question, // Message found, return that LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND) .arg(questionText(*question)).arg(1); - + // TODO: err, should cache set rcode as well? answer_message->setRcode(Rcode::NOERROR()); callback->success(answer_message); @@ -1146,11 +1146,11 @@ RecursiveQuery::resolve(const Question& question, // TODO: general 'prepareinitialanswer' answer_message->setOpcode(isc::dns::Opcode::QUERY()); answer_message->addQuestion(question); - + // First try to see if we have something cached in the messagecache LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE) .arg(questionText(question)).arg(2); - + if (cache_.lookup(question.getName(), question.getType(), question.getClass(), *answer_message) && answer_message->getRRCount(Message::SECTION_ANSWER) > 0) { @@ -1181,7 +1181,7 @@ RecursiveQuery::resolve(const Question& question, // delete itself when it is done LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND) .arg(questionText(question)).arg(2); - new RunningQuery(io, question, answer_message, + new RunningQuery(io, question, answer_message, test_server_, buffer, crs, query_timeout_, client_timeout_, lookup_timeout_, retries_, nsas_, cache_, rtt_recorder_); diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc index 02721f1d5d..4513458ceb 100644 --- a/src/lib/resolve/tests/recursive_query_unittest.cc +++ b/src/lib/resolve/tests/recursive_query_unittest.cc @@ -218,7 +218,7 @@ protected: } // Receive a UDP packet from a mock server; used for testing - // recursive lookup. The caller must place a RecursiveQuery + // recursive lookup. The caller must place a RecursiveQuery // on the IO Service queue before running this routine. void recvUDP(const int family, void* buffer, size_t& size) { ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true)); @@ -267,7 +267,7 @@ protected: if (ret < 0) { isc_throw(IOError, "recvfrom failed: " << strerror(errno)); } - + // Pass the message size back via the size parameter size = ret; } @@ -693,37 +693,6 @@ createTestSocket() { return (sock.release()); } -int -setSocketTimeout(int sock, size_t tv_sec, size_t tv_usec) { - const struct timeval timeo = { tv_sec, tv_usec }; - int recv_options = 0; - if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) { - if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP() - recv_options = MSG_DONTWAIT; - } else { - isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno)); - } - } - return (recv_options); -} - -// try to read from the socket max time -// *num is incremented for every succesfull read -// returns true if it can read max times, false otherwise -bool tryRead(int sock, int recv_options, size_t max, int* num) { - size_t i = 0; - do { - char inbuff[512]; - if (recv(sock, inbuff, sizeof(inbuff), recv_options) < 0) { - return false; - } else { - ++i; - ++*num; - } - } while (i < max); - return true; -} - // Mock resolver callback for testing forward query. class MockResolverCallback : public isc::resolve::ResolverInterface::Callback { public: @@ -904,7 +873,7 @@ TEST_F(RecursiveQueryTest, lowtimeouts) { TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) { setDNSService(true, false); bool done; - + MockServerStop server(io_service_, &done); vector > empty_vector; RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector, @@ -930,7 +899,7 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) { TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) { setDNSService(true, false); bool done; - + MockServerStop server(io_service_, &done); vector > empty_vector; RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector, diff --git a/src/lib/util/tests/lru_list_unittest.cc b/src/lib/util/tests/lru_list_unittest.cc index bfb3b4dc18..c0201eaa5d 100644 --- a/src/lib/util/tests/lru_list_unittest.cc +++ b/src/lib/util/tests/lru_list_unittest.cc @@ -168,7 +168,7 @@ protected: entry7_(new TestEntry("eta", 1)) {} - virtual ~LruListTest() + virtual ~LruListTest() {} boost::shared_ptr entry1_; @@ -355,7 +355,7 @@ TEST_F(LruListTest, Dropped) { lru.add(entry5_); EXPECT_NE(0, (entry2_->getCode() & 0x8000)); - // Delete an entry and check that the handler does not run. + // Delete an entry and check that the handler does not run. EXPECT_EQ(0, (entry3_->getCode() & 0x8000)); lru.remove(entry3_); EXPECT_EQ(0, (entry3_->getCode() & 0x8000)); @@ -386,7 +386,7 @@ TEST_F(LruListTest, Clear) { EXPECT_NE(0, (entry1_->getCode() & 0x8000)); EXPECT_NE(0, (entry2_->getCode() & 0x8000)); EXPECT_NE(0, (entry3_->getCode() & 0x8000)); - + EXPECT_EQ(0, lru.size()); } From c28be089b929185c72aba5fe90c5f8d355c833ac Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Tue, 2 Oct 2012 09:41:38 +0200 Subject: [PATCH 101/194] [2278] Add temporary suppression for false positives --- src/cppcheck-suppress.lst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst index ff4a79a195..9dc8d99619 100644 --- a/src/cppcheck-suppress.lst +++ b/src/cppcheck-suppress.lst @@ -11,3 +11,18 @@ missingInclude // // // cppcheck-suppress duplicateExpression // EXPECT_FALSE(small_name < small_name); + +// With cppcheck 1.56, there are a number of false positives, which +// All of these should be checked and hopefully removed after upgrading +// cppcheck past 1.56 + +// eraseDereference: This is a known false positive, which has been +// fixed in the current development version of cppcheck +eraseDereference + +// Unused functions: there suddenly are a lot of unused function errors +// We could address those by adding for instance early declarations or +// (unnecessary) header files, but they were all somewhat false positives +// When we upgrade past 1.56, we should re-check this, and perhaps enable +// unused-functions again. +unusedFunction From adb92afc6f7d0ceda51780577ebe4c2487745cad Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 8 Oct 2012 14:42:55 +0530 Subject: [PATCH 102/194] [master] Add libb10-cc.la to tests Makefile.am --- src/lib/datasrc/tests/memory/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/datasrc/tests/memory/Makefile.am b/src/lib/datasrc/tests/memory/Makefile.am index 1cde4f5083..37e9043523 100644 --- a/src/lib/datasrc/tests/memory/Makefile.am +++ b/src/lib/datasrc/tests/memory/Makefile.am @@ -39,6 +39,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(top_builddir)/src/lib/datasrc/libb10-datasrc.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la +run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la From fac39dc29a4f858bfe6d1c48f76737926a043f32 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 3 Oct 2012 16:11:51 +0200 Subject: [PATCH 103/194] [2275] valgrind fix: close db in test case --- src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 718d29b4dd..3c314a09d0 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -694,6 +694,8 @@ TEST_F(SQLite3Create, lockedtest) { // should work now that we closed it SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN"); + + ASSERT_EQ(SQLITE_OK, sqlite3_close(db)); } TEST_F(SQLite3AccessorTest, clone) { From 347e333b52c7589ad5bf4fb9cc6f4ff6474b510d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 3 Oct 2012 16:42:08 +0200 Subject: [PATCH 104/194] [2275] valgrind fix: store singleton instance in auto_ptr --- src/lib/acl/dns.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index d16ec658d3..d164398283 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -106,10 +106,10 @@ internal::RequestCheckCreator::create(const string& name, RequestLoader& getRequestLoader() { - static RequestLoader* loader(NULL); - if (loader == NULL) { + static auto_ptr loader(NULL); + if (loader.get() == NULL) { // Creator registration may throw, so we first store the new loader - // in an auto pointer in order to provide the strong exception + // in a second auto pointer in order to provide the strong exception // guarantee. auto_ptr loader_ptr = auto_ptr(new RequestLoader(REJECT)); @@ -129,7 +129,7 @@ getRequestLoader() { new LogicCreator("ALL"))); // From this point there shouldn't be any exception thrown - loader = loader_ptr.release(); + loader.reset(loader_ptr.release()); } return (*loader); From c3a2105cbd7516aada1b954184b699e1ae8200ee Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Wed, 3 Oct 2012 16:54:08 +0200 Subject: [PATCH 105/194] [2275] valgrind fix: initialize memory in labelsequence test --- src/lib/dns/tests/labelsequence_unittest.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc index 90d470d256..fec1c799c7 100644 --- a/src/lib/dns/tests/labelsequence_unittest.cc +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -861,7 +861,11 @@ TEST_F(LabelSequenceTest, badDeserialize) { EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue); // exceed MAX_LABEL_LEN - const uint8_t offsets_toolonglabel[] = { 2, 0, 64 }; + uint8_t offsets_toolonglabel[LabelSequence::MAX_SERIALIZED_LENGTH]; + memset(&offsets_toolonglabel, 0, LabelSequence::MAX_SERIALIZED_LENGTH); + offsets_toolonglabel[0] = 2; + offsets_toolonglabel[1] = 0; + offsets_toolonglabel[2] = 64; EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue); // Inconsistent data: an offset is lower than the previous offset From b41cca56e918501b7a84cfe31067f786ed1d065e Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Oct 2012 11:44:46 +0200 Subject: [PATCH 106/194] [2275] use const char* for test globals --- src/lib/resolve/tests/recursive_query_unittest_2.cc | 8 ++++---- src/lib/resolve/tests/recursive_query_unittest_3.cc | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc index 2b3d12979b..85e2c384fe 100644 --- a/src/lib/resolve/tests/recursive_query_unittest_2.cc +++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc @@ -79,10 +79,10 @@ using namespace std; namespace isc { namespace asiodns { -const std::string TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address -const uint16_t TEST_PORT = 5301; ///< ... and this port -const size_t BUFFER_SIZE = 1024; ///< For all buffers -const char* WWW_EXAMPLE_ORG = "192.0.2.254"; ///< Address of www.example.org +const char* TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address +const uint16_t TEST_PORT = 5301; ///< ... and this port +const size_t BUFFER_SIZE = 1024; ///< For all buffers +const char* WWW_EXAMPLE_ORG = "192.0.2.254"; ///< Address of www.example.org // As the test is fairly long and complex, debugging "print" statements have // been left in although they are disabled. Set the following to "true" to diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc index 168ec80d6b..fdbae7ba10 100644 --- a/src/lib/resolve/tests/recursive_query_unittest_3.cc +++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc @@ -72,12 +72,11 @@ using namespace std; namespace isc { namespace asiodns { -const std::string TEST_ADDRESS3 = "127.0.0.1"; - ///< Servers are on this address -const uint16_t TEST_PORT3 = 5303; ///< ... and this port +const char* TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address +const uint16_t TEST_PORT3 = 5303; ///< ... and this port const size_t BUFFER_SIZE = 1024; ///< For all buffers -const std::string DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A +const char* DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A class MockResolver3 : public isc::resolve::ResolverInterface { public: From 1c4ec91a6f44300af78f4964496cf000c97e93f8 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Oct 2012 12:21:04 +0200 Subject: [PATCH 107/194] [2275] valgrind fixes: uninitialized memory --- src/lib/server_common/tests/socket_requestor_test.cc | 2 +- src/lib/util/io/fd_share.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/server_common/tests/socket_requestor_test.cc b/src/lib/server_common/tests/socket_requestor_test.cc index 9878c632be..1ee9178e7c 100644 --- a/src/lib/server_common/tests/socket_requestor_test.cc +++ b/src/lib/server_common/tests/socket_requestor_test.cc @@ -410,6 +410,7 @@ private: isc_throw(Unexpected, "mkstemp() created a filename too long for sun_path"); } + memset(socket_address.sun_path, 0, sizeof(socket_address.sun_path)); strncpy(socket_address.sun_path, path_, len); #ifdef HAVE_SA_LEN socket_address.sun_len = len; @@ -542,7 +543,6 @@ TEST_F(SocketRequestorTest, testSocketPassing) { EXPECT_EQ("foo", socket_id.second); EXPECT_EQ(0, close(socket_id.first)); } - // Create a second socket server, to test that multiple different // domains sockets would work as well (even though we don't actually // use that feature) diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc index 7adbbbe9fa..04a3e546fa 100644 --- a/src/lib/util/io/fd_share.cc +++ b/src/lib/util/io/fd_share.cc @@ -142,6 +142,7 @@ send_fd(const int sock, const int fd) { if (msghdr.msg_control == NULL) { return (FD_OTHER_ERROR); } + memset(msghdr.msg_control, 0, msghdr.msg_controllen); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr); cmsg->cmsg_len = cmsg_len(sizeof(int)); From 3d98322d24b54475ce801880a5d17a27dc8f57ef Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Thu, 4 Oct 2012 15:57:43 +0200 Subject: [PATCH 108/194] [2275] valgrind fixes: uninitialized memory --- src/lib/util/tests/fd_tests.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc index 6ba27663c4..b709405116 100644 --- a/src/lib/util/tests/fd_tests.cc +++ b/src/lib/util/tests/fd_tests.cc @@ -34,7 +34,9 @@ class FDTest : public ::testing::Test { // We do not care what is inside, we just need it to be the same data(new unsigned char[TEST_DATA_SIZE]), buffer(NULL) - { } + { + memset(data, 0, TEST_DATA_SIZE); + } ~ FDTest() { delete[] data; delete[] buffer; From 9334389794384c62c517d646608fb23a1abeb40d Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Fri, 5 Oct 2012 12:10:56 +0200 Subject: [PATCH 109/194] [2275] address review comments --- src/lib/acl/dns.cc | 17 ++++++++++------- .../datasrc/tests/sqlite3_accessor_unittest.cc | 8 ++++---- src/lib/dns/tests/labelsequence_unittest.cc | 12 ++++++------ .../resolve/tests/recursive_query_unittest_2.cc | 13 ++++++++----- .../resolve/tests/recursive_query_unittest_3.cc | 14 ++++++++------ 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc index d164398283..5466dade45 100644 --- a/src/lib/acl/dns.cc +++ b/src/lib/acl/dns.cc @@ -12,12 +12,6 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include -#include -#include - -#include - #include #include @@ -31,6 +25,13 @@ #include #include +#include +#include + +#include +#include +#include + using namespace std; using namespace isc::dns; using namespace isc::data; @@ -106,7 +107,9 @@ internal::RequestCheckCreator::create(const string& name, RequestLoader& getRequestLoader() { - static auto_ptr loader(NULL); + // To ensure that the singleton gets destroyed at the end of the + // program's lifetime, we put it in a static scoped_ptr. + static boost::scoped_ptr loader(NULL); if (loader.get() == NULL) { // Creator registration may throw, so we first store the new loader // in a second auto pointer in order to provide the strong exception diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 3c314a09d0..e8a69a5f27 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -665,16 +665,16 @@ TEST_F(SQLite3Create, creationtest) { TEST_F(SQLite3Create, emptytest) { ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE)); - // open one manualle + // open one manually sqlite3* db; ASSERT_EQ(SQLITE_OK, sqlite3_open(SQLITE_NEW_DBFILE, &db)); - // empty, but not locked, so creating it now should work + // empty, but not locked, so creating another accessor should work SQLite3Accessor accessor2(SQLITE_NEW_DBFILE, "IN"); sqlite3_close(db); - // should work now that we closed it + // should still work now that we closed it SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN"); } @@ -692,7 +692,7 @@ TEST_F(SQLite3Create, lockedtest) { sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL); - // should work now that we closed it + // should work now that the transaction has been rolled back SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN"); ASSERT_EQ(SQLITE_OK, sqlite3_close(db)); diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc index fec1c799c7..62cbcec321 100644 --- a/src/lib/dns/tests/labelsequence_unittest.cc +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -860,12 +860,12 @@ TEST_F(LabelSequenceTest, badDeserialize) { const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 }; EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue); - // exceed MAX_LABEL_LEN - uint8_t offsets_toolonglabel[LabelSequence::MAX_SERIALIZED_LENGTH]; - memset(&offsets_toolonglabel, 0, LabelSequence::MAX_SERIALIZED_LENGTH); - offsets_toolonglabel[0] = 2; - offsets_toolonglabel[1] = 0; - offsets_toolonglabel[2] = 64; + // (second) offset does not match actual label length + const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 }; + EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue); + + // offset matches, but exceeds MAX_LABEL_LEN + const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 }; EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue); // Inconsistent data: an offset is lower than the previous offset diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc index 85e2c384fe..6cb404dd23 100644 --- a/src/lib/resolve/tests/recursive_query_unittest_2.cc +++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc @@ -76,13 +76,16 @@ using namespace std; /// directed to one or other of the "servers" in the RecursiveQueryTest2 class, /// regardless of the glue returned in referrals. -namespace isc { -namespace asiodns { - -const char* TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address +namespace { +const char* const TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address const uint16_t TEST_PORT = 5301; ///< ... and this port const size_t BUFFER_SIZE = 1024; ///< For all buffers -const char* WWW_EXAMPLE_ORG = "192.0.2.254"; ///< Address of www.example.org +const char* const WWW_EXAMPLE_ORG = "192.0.2.254"; + ///< Address of www.example.org +} //end anonymous namespace + +namespace isc { +namespace asiodns { // As the test is fairly long and complex, debugging "print" statements have // been left in although they are disabled. Set the following to "true" to diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc index fdbae7ba10..abfea9a10f 100644 --- a/src/lib/resolve/tests/recursive_query_unittest_3.cc +++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc @@ -69,15 +69,17 @@ using namespace std; /// By using the "test_server_" element of RecursiveQuery, all queries are /// directed to one or other of the "servers" in the RecursiveQueryTest3 class. +namespace { +const char* const TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address +const uint16_t TEST_PORT3 = 5303; ///< ... and this port +const size_t BUFFER_SIZE = 1024; ///< For all buffers + +const char* const DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A +} // end anonymous namespace + namespace isc { namespace asiodns { -const char* TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address -const uint16_t TEST_PORT3 = 5303; ///< ... and this port -const size_t BUFFER_SIZE = 1024; ///< For all buffers - -const char* DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A - class MockResolver3 : public isc::resolve::ResolverInterface { public: virtual void resolve(const QuestionPtr& question, From 1eb3ce6df757a23ff59b71469e0f19329893d8c7 Mon Sep 17 00:00:00 2001 From: Jelte Jansen Date: Mon, 8 Oct 2012 09:41:11 +0200 Subject: [PATCH 110/194] [2275] fix strncpy in socketrequestortest --- src/lib/server_common/tests/socket_requestor_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/server_common/tests/socket_requestor_test.cc b/src/lib/server_common/tests/socket_requestor_test.cc index 1ee9178e7c..ac1731f3da 100644 --- a/src/lib/server_common/tests/socket_requestor_test.cc +++ b/src/lib/server_common/tests/socket_requestor_test.cc @@ -410,8 +410,8 @@ private: isc_throw(Unexpected, "mkstemp() created a filename too long for sun_path"); } - memset(socket_address.sun_path, 0, sizeof(socket_address.sun_path)); - strncpy(socket_address.sun_path, path_, len); + strncpy(socket_address.sun_path, path_, + sizeof(socket_address.sun_path)); #ifdef HAVE_SA_LEN socket_address.sun_len = len; #endif From c66fe1c8ad169c7bd0bc15c0cbe2357194acfaa4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 16:41:33 +0000 Subject: [PATCH 111/194] [master] add std:: for memset(). sunstudio requires this with --- src/lib/util/io/fd_share.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc index 04a3e546fa..866644131a 100644 --- a/src/lib/util/io/fd_share.cc +++ b/src/lib/util/io/fd_share.cc @@ -142,7 +142,7 @@ send_fd(const int sock, const int fd) { if (msghdr.msg_control == NULL) { return (FD_OTHER_ERROR); } - memset(msghdr.msg_control, 0, msghdr.msg_controllen); + std::memset(msghdr.msg_control, 0, msghdr.msg_controllen); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr); cmsg->cmsg_len = cmsg_len(sizeof(int)); From 06c667cd025d42798dbce055a24ada9e601f8c35 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 1 Oct 2012 00:45:45 +0530 Subject: [PATCH 112/194] [1899] Add a unique constraint to nsec3 table This is to check that only one NSEC3 RR exists per owner name in a zone. The code to fix #1899 would depend on this assertion. --- src/bin/dbutil/dbutil.py.in | 11 ++++++++++- src/bin/dbutil/tests/dbutil_test.sh.in | 6 +++--- src/bin/dbutil/tests/testdata/Makefile.am | 1 + src/bin/dbutil/tests/testdata/v2_1.sqlite3 | Bin 0 -> 15360 bytes src/lib/datasrc/sqlite3_accessor.cc | 6 ++++-- src/lib/python/isc/datasrc/sqlite3_ds.py | 5 ++++- 6 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 src/bin/dbutil/tests/testdata/v2_1.sqlite3 diff --git a/src/bin/dbutil/dbutil.py.in b/src/bin/dbutil/dbutil.py.in index 4b76a5680e..f91c2768b7 100755 --- a/src/bin/dbutil/dbutil.py.in +++ b/src/bin/dbutil/dbutil.py.in @@ -193,10 +193,19 @@ UPGRADES = [ "ALTER TABLE schema_version " + "ADD COLUMN minor INTEGER NOT NULL DEFAULT 0" ] + }, + + {'from': (2, 0), 'to': (2, 1), + 'statements': [ + # Enforce that only one NSEC3 RR exists for an owner name in + # the zone. + "CREATE UNIQUE INDEX nsec3_by_zoneid_and_owner ON nsec3 " + + "(zone_id, owner)" + ] } # To extend this, leave the above statements in place and add another -# dictionary to the list. The "from" version should be (2, 0), the "to" +# dictionary to the list. The "from" version should be (2, 1), the "to" # version whatever the version the update is to, and the SQL statements are # the statements required to perform the upgrade. This way, the upgrade # program will be able to upgrade both a V1.0 and a V2.0 database. diff --git a/src/bin/dbutil/tests/dbutil_test.sh.in b/src/bin/dbutil/tests/dbutil_test.sh.in index f82eeb031d..35314e818e 100755 --- a/src/bin/dbutil/tests/dbutil_test.sh.in +++ b/src/bin/dbutil/tests/dbutil_test.sh.in @@ -165,7 +165,7 @@ upgrade_ok_test() { if [ $? -eq 0 ] then # Compare schema with the reference - get_schema $testdata/v2_0.sqlite3 + get_schema $testdata/v2_1.sqlite3 expected_schema=$db_schema get_schema $tempfile actual_schema=$db_schema @@ -177,7 +177,7 @@ upgrade_ok_test() { fi # Check the version is set correctly - check_version $tempfile "V2.0" + check_version $tempfile "V2.1" # Check that a backup was made check_backup $1 $2 @@ -449,7 +449,7 @@ copy_file $testdata/old_v1.sqlite3 $tempfile Yes . passzero $? -check_version $tempfile "V2.0" +check_version $tempfile "V2.1" rm -f $tempfile $backupfile echo "13.4 Interactive prompt - no" diff --git a/src/bin/dbutil/tests/testdata/Makefile.am b/src/bin/dbutil/tests/testdata/Makefile.am index 0d850a70f0..f4873f4b4e 100644 --- a/src/bin/dbutil/tests/testdata/Makefile.am +++ b/src/bin/dbutil/tests/testdata/Makefile.am @@ -10,3 +10,4 @@ EXTRA_DIST += old_v1.sqlite3 EXTRA_DIST += README EXTRA_DIST += too_many_version.sqlite3 EXTRA_DIST += v2_0.sqlite3 +EXTRA_DIST += v2_1.sqlite3 diff --git a/src/bin/dbutil/tests/testdata/v2_1.sqlite3 b/src/bin/dbutil/tests/testdata/v2_1.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..9eea287452496300f899d15c6bd727e8d26ecdfd GIT binary patch literal 15360 zcmeI2-%ry}6vunl{eS{4(T67P!=lNew;C*4`hd-`n#!=iDvX_V+X=uyLpF^~?aT30VOl z;RVKmAQZzlxv*o2oU>t@UQnc+)GP{5zCFrOWAQh&nHye`%wZe|0D-?pppd0Sjl^F9 zdBFh$fIwOZ{uhM*Nxh4wQ-1d)WZsvT(f86rg}iu4cEq-bxos>tk*ZI`zo>VtajJ*{Tt0j zPqtFiSI9k<>-%;aZ`A9WTGhwmV+SrDiUqk;LZ23x1D@UPdzK$wvYZp);0~qH3IJz(>S;sO=*D@F-Hc<>?)6#2uEtO=;mU@5_Z^3naIEl+)oGO@U zDVK}6Verpb@GYm)@!2WQ1)mKi2Vc4&`ic7vmini*X9i>+o0jb})QRMI<6OTkl*!zh zo;Fi6Vke)MmzU9SpYz{!$vv^~$EiO!URqi)5q&MWjDWxGJcWMr^#QbIln zCv}UprBG!Uy67~G7VG0OwN~`GysVVaG1Ce6r%);EEpS@A2~`_yYD{l$J}*Bmp|>$T zk4}b`iwT0vhErrCN>?h#>m@W;ifPjDu*{8$FC~cc#{4R5>#N}5YTFADu-8dXcNx~c z$H_;hsU+*;GPPD{{Uh;*KwfYF0U&U92|PeqMJV#$iB~1+Z}eY1ch`UL3Q literal 0 HcmV?d00001 diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index 672121eb48..d21286529b 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -44,7 +44,7 @@ namespace { // program may not be taking advantage of features (possibly performance // improvements) added to the database. const int SQLITE_SCHEMA_MAJOR_VERSION = 2; -const int SQLITE_SCHEMA_MINOR_VERSION = 0; +const int SQLITE_SCHEMA_MINOR_VERSION = 1; } namespace isc { @@ -332,7 +332,7 @@ public: const char* const SCHEMA_LIST[] = { "CREATE TABLE schema_version (version INTEGER NOT NULL, " "minor INTEGER NOT NULL DEFAULT 0)", - "INSERT INTO schema_version VALUES (2, 0)", + "INSERT INTO schema_version VALUES (2, 1)", "CREATE TABLE zones (id INTEGER PRIMARY KEY, " "name TEXT NOT NULL COLLATE NOCASE, " "rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', " @@ -358,6 +358,8 @@ const char* const SCHEMA_LIST[] = { "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, " "rdata TEXT NOT NULL)", "CREATE INDEX nsec3_byhash ON nsec3 (hash)", + // Enforce that only one NSEC3 RR exists for an owner name in the zone. + "CREATE UNIQUE INDEX nsec3_by_zoneid_and_owner ON nsec3 (zone_id, owner)", "CREATE TABLE diffs (id INTEGER PRIMARY KEY, " "zone_id INTEGER NOT NULL, " "version INTEGER NOT NULL, " diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py index f9b47c0832..ef1245a303 100644 --- a/src/lib/python/isc/datasrc/sqlite3_ds.py +++ b/src/lib/python/isc/datasrc/sqlite3_ds.py @@ -25,7 +25,7 @@ RR_RDATA_INDEX = 7 # Current major and minor versions of schema SCHEMA_MAJOR_VERSION = 2 -SCHEMA_MINOR_VERSION = 0 +SCHEMA_MINOR_VERSION = 1 class Sqlite3DSError(Exception): """ Define exceptions.""" @@ -81,6 +81,9 @@ def create(cur): rdtype TEXT NOT NULL COLLATE NOCASE, rdata TEXT NOT NULL)""") cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)") + # Enforce that only one NSEC3 RR exists for an owner name in the zone. + cur.execute("""CREATE UNIQUE INDEX nsec3_by_zoneid_and_owner ON nsec3 + (zone_id, owner)"""); cur.execute("""CREATE TABLE diffs (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, version INTEGER NOT NULL, From dd83505acac98d122777883a304465f69ecd03ec Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 1 Oct 2012 01:16:39 +0530 Subject: [PATCH 113/194] [1899] Break up UNION SQL query into two and merge manually Also drop unnecessary columns and ORDER BYs. --- src/lib/datasrc/sqlite3_accessor.cc | 131 +++++++++++++++++----------- 1 file changed, 80 insertions(+), 51 deletions(-) diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index d21286529b..b89b2bc49a 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -65,19 +65,20 @@ enum StatementID { DEL_ZONE_RECORDS = 6, ADD_RECORD = 7, DEL_RECORD = 8, - ITERATE = 9, - FIND_PREVIOUS = 10, - ADD_RECORD_DIFF = 11, - LOW_DIFF_ID = 12, - HIGH_DIFF_ID = 13, - DIFF_RECS = 14, - NSEC3 = 15, - NSEC3_PREVIOUS = 16, - NSEC3_LAST = 17, - ADD_NSEC3_RECORD = 18, - DEL_ZONE_NSEC3_RECORDS = 19, - DEL_NSEC3_RECORD = 20, - NUM_STATEMENTS = 21 + ITERATE_RECORDS = 9, + ITERATE_NSEC3 = 10, + FIND_PREVIOUS = 11, + ADD_RECORD_DIFF = 12, + LOW_DIFF_ID = 13, + HIGH_DIFF_ID = 14, + DIFF_RECS = 15, + NSEC3 = 16, + NSEC3_PREVIOUS = 17, + NSEC3_LAST = 18, + ADD_NSEC3_RECORD = 19, + DEL_ZONE_NSEC3_RECORDS = 20, + DEL_NSEC3_RECORD = 21, + NUM_STATEMENTS = 22 }; const char* const text_statements[NUM_STATEMENTS] = { @@ -102,19 +103,19 @@ const char* const text_statements[NUM_STATEMENTS] = { "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD "AND rdtype=?3 AND rdata=?4", - // The following iterates the whole zone. As the NSEC3 records - // (and corresponding RRSIGs) live in separate table, we need to - // take both of them. As the RRSIGs are for NSEC3s in the other - // table, we can easily hardcode the sigtype. - // - // The extra column is so we can order it by rname. This is to - // preserve the previous order, mostly for tests. - // TODO: Is it possible to get rid of the ordering? - "SELECT rdtype, ttl, sigtype, rdata, name, rname FROM records " // ITERATE - "WHERE zone_id = ?1 " - "UNION " - "SELECT rdtype, ttl, \"NSEC3\", rdata, owner, owner FROM nsec3 " - "WHERE zone_id = ?1 ORDER by rname, rdtype", + + // ITERATE_RECORDS: + // The following iterates the whole zone in the records table. + "SELECT rdtype, ttl, sigtype, rdata, name FROM records " + "WHERE zone_id = ?1 ORDER BY rname, rdtype", + + // ITERATE_NSEC3: + // The following iterates the whole zone in the nsec3 table. As the + // RRSIGs are for NSEC3s, we can hardcode the sigtype. As there is + // only one RR per-owner per-zone, there's no need to order these + // for the sake of any post-processing. + "SELECT rdtype, ttl, \"NSEC3\", rdata, owner FROM nsec3 " + "WHERE zone_id = ?1", /* * This one looks for previous name with NSEC record. It is done by * using the reversed name. The NSEC is checked because we need to @@ -644,11 +645,21 @@ public: iterator_type_(ITT_ALL), accessor_(accessor), statement_(NULL), + statement2_(NULL), + rc_(SQLITE_OK), + rc2_(SQLITE_OK), name_("") { - // We create the statement now and then just keep getting data from it + // We create the statements now and then just keep getting data + // from them. statement_ = prepare(accessor->dbparameters_->db_, - text_statements[ITERATE]); + text_statements[ITERATE_NSEC3]); + bindZoneId(id); + + std::swap(statement_, statement2_); + + statement_ = prepare(accessor->dbparameters_->db_, + text_statements[ITERATE_RECORDS]); bindZoneId(id); } @@ -667,6 +678,9 @@ public: iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME), accessor_(accessor), statement_(NULL), + statement2_(NULL), + rc_(SQLITE_OK), + rc2_(SQLITE_OK), name_(name) { // Choose the statement text depending on the query type, and @@ -705,29 +719,35 @@ public: // If there's another row, get it // If finalize has been called (e.g. when previous getNext() got // SQLITE_DONE), directly return false - if (statement_ == NULL) { - return false; - } - const int rc(sqlite3_step(statement_)); - if (rc == SQLITE_ROW) { - // For both types, we copy the first four columns - copyColumn(data, TYPE_COLUMN); - copyColumn(data, TTL_COLUMN); - // The NSEC3 lookup does not provide the SIGTYPE, it is not - // necessary and not contained in the table. - if (iterator_type_ != ITT_NSEC3) { - copyColumn(data, SIGTYPE_COLUMN); + while (statement_ != NULL) { + rc_ = sqlite3_step(statement_); + if (rc_ == SQLITE_ROW) { + // For both types, we copy the first four columns + copyColumn(data, TYPE_COLUMN); + copyColumn(data, TTL_COLUMN); + // The NSEC3 lookup does not provide the SIGTYPE, it is not + // necessary and not contained in the table. + if (iterator_type_ != ITT_NSEC3) { + copyColumn(data, SIGTYPE_COLUMN); + } + copyColumn(data, RDATA_COLUMN); + // Only copy Name if we are iterating over every record + if (iterator_type_ == ITT_ALL) { + copyColumn(data, NAME_COLUMN); + } + return (true); + } else if (rc_ != SQLITE_DONE) { + isc_throw(DataSourceError, + "Unexpected failure in sqlite3_step: " << + sqlite3_errmsg(accessor_->dbparameters_->db_)); } - copyColumn(data, RDATA_COLUMN); - // Only copy Name if we are iterating over every record - if (iterator_type_ == ITT_ALL) { - copyColumn(data, NAME_COLUMN); + // We are done with statement_. If statement2_ has not been + // used yet, try that one now. + if ((statement2_ == NULL) || (rc2_ != SQLITE_OK)) { + break; } - return (true); - } else if (rc != SQLITE_DONE) { - isc_throw(DataSourceError, - "Unexpected failure in sqlite3_step: " << - sqlite3_errmsg(accessor_->dbparameters_->db_)); + std::swap(statement_, statement2_); + std::swap(rc_, rc2_); } finalize(); return (false); @@ -773,13 +793,22 @@ private: } void finalize() { - sqlite3_finalize(statement_); - statement_ = NULL; + if (statement_ != NULL) { + sqlite3_finalize(statement_); + statement_ = NULL; + } + if (statement2_ != NULL) { + sqlite3_finalize(statement2_); + statement2_ = NULL; + } } const IteratorType iterator_type_; boost::shared_ptr accessor_; sqlite3_stmt* statement_; + sqlite3_stmt* statement2_; + int rc_; + int rc2_; const std::string name_; }; From 13089ae122acefb8d2a632174da4e4dd53ce4473 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 1 Oct 2012 01:23:58 +0530 Subject: [PATCH 114/194] [1899] Add another unittest to check that NSEC3 RRs are being returned --- .../tests/sqlite3_accessor_unittest.cc | 2 ++ .../tests/testdata/example.org.sqlite3 | Bin 16384 -> 15360 bytes 2 files changed, 2 insertions(+) diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index e8a69a5f27..37c587414a 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -191,6 +191,8 @@ TEST_F(SQLite3AccessorTest, iterator) { checkRR(context, "sub.example.org.", "3600", "NS", "ns.sub.example.org."); checkRR(context, "ns.sub.example.org.", "3600", "A", "192.0.2.101"); checkRR(context, "www.example.org.", "3600", "A", "192.0.2.1"); + checkRR(context, "ns3.example.org.", "3600", "NSEC3", + "1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"); // Check there's no other EXPECT_FALSE(context->getNext(data)); diff --git a/src/lib/datasrc/tests/testdata/example.org.sqlite3 b/src/lib/datasrc/tests/testdata/example.org.sqlite3 index c7388ff920f7aaf8724bc48e7afcb72b6ec7c8fd..939e5e4c85206d975102149f1e41c16e0812841a 100644 GIT binary patch delta 1022 zcmah|%}*0S6rb6dZEHcQfL(0;7@$NNq1f%0Mvb;uus}a(OJicpX4_q*rnK5^v=WsM zjfsg-bP_HeG#vC~NZ{hd|3Ob4I7qy~LF35-i8{MTf)e9y_U-KZ?Qh=iy?JkX8cx^e zoclxofQ>y{R|i|*r^otQM9}-WlV@KLZRnr{Z7lD_j}T|ZOZX>hfV&NB9iVmQUqy7? zW?yQ>*aXo7fF3a4Z8TMf769YDoN$LfmO2qXAI?QbbMas{l#7f6Mv^IU%$F6@DPJ}j zFKa%JT9+rTl~hlqdd1VUCy|Nv`@DqRB9Y~MzEDsUB4$Go|DZpY2?sLqm_IZe^u}|6 zNX{D;#Yl1}O~NFd&cp_Fbzqry{X~rzECR3yeefHy(!KZvbvf=^XHIfvK@i~V7=6Q? zqGz3TG;2rnIdMSz^u33sIvw$Mu0QUVp&n^21Cl1@Hiy0> zhfOA-s|#K$X=rkA(_&`Kvc zH(=K@FJZUcQ4Xrf;`oQDS*m>2;{f7EAO z*2ay*n`p)Fw(AFJMlOIpqIc*Ss-g@!fn2P;n$T2>8M}c^D~ziXvUEeOYQ;*~+h)Q- zSBqX2%`mLwWqCqnZFG>wPWQg5TBuYN?e}X7^%C(A#?GTV;i|!AxEo>T>HXX2^ktwm z=JgUE5!*ODs3Cjn(DXc5&?6ADCinx{(E&!2FCFLcGJjwLEm-ivDQvYG^R)ud3QP6_ d>T{tu{NE74P0|`bYmD>>33d#7A^-Pm!CwaC9{&IU delta 1518 zcma)6O>7fK6rMjj;OwsRhu1&UJ8>L}Nt~ZX2%5AgspX&`V~501+Y4ri7i8hu;5Ai1 z6^R4lSmC}(?V%^QwCaHdAsxGdq4B$ zd$TaNuz+uc_QerGEn-MwU@gT&Q49FkHeW@fIDF-yFiWsmO{Qx zN8^0;%EiVIr=3t{;d}#(Co-wjST@PinZ($1ve(I?lGm&HR^|ei<>{GJDmG3*5AVke zu7zWs+Evn|sw%!d4etludz0-)oFmN^b<=!NB|rQU@H!UZexPg5Y3x>GF?}^Jl;Za(Ehj%39N@w#f#Y$ZA7bX^voNUypj!#G3H>M@8X{iQ z#Y$%1|4YBi`z*$tz=MNC6(%q*AA_&F1;}}i!rR_=;Sc4AG~Bx#%Voua2g2!R$`Ke< zM&KVg0YAy3V9F;TEYB=Uj46yn5s8v-$=`I9uGVXdZsOaj#D^m>vFUrNrs121ZQ_=T z`CPtm?wq;(BPFJi)}d#|K1d~bWLqDBT)^MO$7ZsbiL}6+Os2Drl(0xX?{tFvQ=?)Q z$|XLL&L)p1r?&ax=!Tas8Wp|z=JF%cLl;^@XbnFj6XYA}p{wZ2dab6hMW1ZnF*m5g9pe^v%%SjWAsZyEz;|Je_rR~= zKJ0~O;Z95;qz&K}NNGJn&S|5;n%TP9qe(kd$uwq%Y|#wS(Api=!B$wubGpJi)@c0} z|H98KT^k`wh%Ax6NCrR0pQEqQWW6>_#@paWZ9jIw11dB@ww rCAZqG;#{GWH|DHr?ufDIYTwMhU}tah*%ls%zV&!H>S8Odferc}t+{tP From f7c0410f7d2f1c3a06f886b548713bae6e78e9fb Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Mon, 8 Oct 2012 12:36:20 +0530 Subject: [PATCH 115/194] [1899] Allow other RRtypes in nsec3 table (for RRSIGs) * The unique constraint on NSEC3 owner per zone is now removed * NSEC3 RRSIGs are now returned next to their RR set * Test was adjusted to check for RRSIGs too --- src/bin/dbutil/dbutil.py.in | 6 ++---- src/bin/dbutil/tests/testdata/v2_1.sqlite3 | Bin 15360 -> 15360 bytes src/lib/datasrc/sqlite3_accessor.cc | 9 +++------ .../tests/sqlite3_accessor_unittest.cc | 3 +++ .../tests/testdata/example.org.sqlite3 | Bin 15360 -> 15360 bytes src/lib/python/isc/datasrc/sqlite3_ds.py | 4 +--- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/bin/dbutil/dbutil.py.in b/src/bin/dbutil/dbutil.py.in index f91c2768b7..a84448457f 100755 --- a/src/bin/dbutil/dbutil.py.in +++ b/src/bin/dbutil/dbutil.py.in @@ -197,10 +197,8 @@ UPGRADES = [ {'from': (2, 0), 'to': (2, 1), 'statements': [ - # Enforce that only one NSEC3 RR exists for an owner name in - # the zone. - "CREATE UNIQUE INDEX nsec3_by_zoneid_and_owner ON nsec3 " + - "(zone_id, owner)" + "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 " + + "(hash, rdtype)" ] } diff --git a/src/bin/dbutil/tests/testdata/v2_1.sqlite3 b/src/bin/dbutil/tests/testdata/v2_1.sqlite3 index 9eea287452496300f899d15c6bd727e8d26ecdfd..ca2dee92b7749a5ead4b46f8359718e7a7b173ba 100644 GIT binary patch delta 188 zcmZpuXsDPVEhx;uz`zg0Fd#5d$Cy!gW5P0Np*akU%uGT|@l4B^zB79D!ZK;0eg;NnCLyMHrsYiEnZ1~IF!WDsEUz!( zVHdX-XKa+t%u7kF$SY1wHjYoKjIYYiOU+D)Ps~e+&o9qQEdopMI|sQshPWz(`gsP1 wx+-}3xwu9s;MAhv?+1}o&;XehpP8bg05(griHDV4TwI*7xx8dEpX?_&0MQvY^8f$< diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc index b89b2bc49a..68d655477a 100644 --- a/src/lib/datasrc/sqlite3_accessor.cc +++ b/src/lib/datasrc/sqlite3_accessor.cc @@ -111,11 +111,9 @@ const char* const text_statements[NUM_STATEMENTS] = { // ITERATE_NSEC3: // The following iterates the whole zone in the nsec3 table. As the - // RRSIGs are for NSEC3s, we can hardcode the sigtype. As there is - // only one RR per-owner per-zone, there's no need to order these - // for the sake of any post-processing. + // RRSIGs are for NSEC3s, we can hardcode the sigtype. "SELECT rdtype, ttl, \"NSEC3\", rdata, owner FROM nsec3 " - "WHERE zone_id = ?1", + "WHERE zone_id = ?1 ORDER BY hash, rdtype", /* * This one looks for previous name with NSEC record. It is done by * using the reversed name. The NSEC is checked because we need to @@ -359,8 +357,7 @@ const char* const SCHEMA_LIST[] = { "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, " "rdata TEXT NOT NULL)", "CREATE INDEX nsec3_byhash ON nsec3 (hash)", - // Enforce that only one NSEC3 RR exists for an owner name in the zone. - "CREATE UNIQUE INDEX nsec3_by_zoneid_and_owner ON nsec3 (zone_id, owner)", + "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 (hash, rdtype)", "CREATE TABLE diffs (id INTEGER PRIMARY KEY, " "zone_id INTEGER NOT NULL, " "version INTEGER NOT NULL, " diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc index 37c587414a..100a0ddc60 100644 --- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc +++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc @@ -193,6 +193,9 @@ TEST_F(SQLite3AccessorTest, iterator) { checkRR(context, "www.example.org.", "3600", "A", "192.0.2.1"); checkRR(context, "ns3.example.org.", "3600", "NSEC3", "1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"); + checkRR(context, "ns3.example.org.", "3600", "RRSIG", + "NSEC3 5 3 3600 20000101000000 20000201000000 " + "12345 ns3.example.org. FAKEFAKEFAKE"); // Check there's no other EXPECT_FALSE(context->getNext(data)); diff --git a/src/lib/datasrc/tests/testdata/example.org.sqlite3 b/src/lib/datasrc/tests/testdata/example.org.sqlite3 index 939e5e4c85206d975102149f1e41c16e0812841a..c799d2ef5a97bebeaf2f6f61203789e227fd321e 100644 GIT binary patch delta 433 zcmZpuXsDPVEojZaz`zg0FrYS3$C%N2W5N=C4ko5g49smzpEfo=W8!HPWa4CC^wnh& zZ`7SULD)DnC@9#|-7nbH*;v6;!C1lA%)mgw$N&fofe;KJTq8Kw(8$=tR3Wd}STD6A zF}ENmRWH9NT~EQy(c2YnRVw)V zL8KHkKxXJDKn&Q-Df^d6K#hUCC*8)QY^~)MVrMq{{fJ{Jhl6l=#HFl=%Ge zywoDF1iy2Tt7C|(La3i-V5qBtr=N>!gaS@23jTf&Nd*m%Y4MpUItpO3G@Dpi*~P`h z8JjapHjB!BWD-zeV7|`0mw6I%5pyuJC9}%LMn2}rYRVh53>6I38Cif11lxksDxk8> z|CHGTfPTKsz Date: Tue, 2 Oct 2012 20:24:57 -0700 Subject: [PATCH 116/194] [2203] refactoring 1st step: move session obj outside of datasrc configurator. --- src/bin/auth/datasrc_configurator.h | 24 +++++------------ src/bin/auth/main.cc | 17 ++++++++++-- .../tests/datasrc_configurator_unittest.cc | 27 +++++++++++++------ 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h index 6b2f5826b0..badf62c87c 100644 --- a/src/bin/auth/datasrc_configurator.h +++ b/src/bin/auth/datasrc_configurator.h @@ -54,7 +54,6 @@ private: } } static Server* server_; - static isc::config::ModuleCCSession* session_; typedef boost::shared_ptr ListPtr; public: /// \brief Initializes the class. @@ -74,12 +73,7 @@ public: /// \throw isc::InvalidParameter if any of the parameters is NULL /// \throw isc::config::ModuleCCError if the remote configuration is not /// available for some reason. - static void init(isc::config::ModuleCCSession* session, - Server* server) - { - if (session == NULL) { - isc_throw(isc::InvalidParameter, "The session must not be NULL"); - } + static void init(Server* server) { if (server == NULL) { isc_throw(isc::InvalidParameter, "The server must not be NULL"); } @@ -88,9 +82,8 @@ public: "The configurator is already initialized"); } server_ = server; - session_ = session; - session->addRemoteConfig("data_sources", reconfigureInternal, false); } + /// \brief Deinitializes the class. /// /// This detaches from the session and removes the server from internal @@ -99,12 +92,9 @@ public: /// This can be called even if it is not initialized currently. You /// can initialize it again after this. static void cleanup() { - if (session_ != NULL) { - session_->removeRemoteConfig("data_sources"); - } - session_ = NULL; server_ = NULL; } + /// \brief Reads new configuration and replaces the old one. /// /// It instructs the server to replace the lists with new ones as needed. @@ -210,10 +200,6 @@ public: } }; -template -isc::config::ModuleCCSession* -DataSourceConfiguratorGeneric::session_(NULL); - template Server* DataSourceConfiguratorGeneric::server_(NULL); @@ -224,3 +210,7 @@ typedef DataSourceConfiguratorGenericcontains("classes")) { + DataSourceConfigurator::reconfigure(config->get("classes")); + } +} + void usage() { cerr << "Usage: b10-auth [-v]" @@ -192,7 +202,9 @@ main(int argc, char* argv[]) { auth_server->setTSIGKeyRing(&isc::server_common::keyring); // Start the data source configuration - DataSourceConfigurator::init(config_session, auth_server); + DataSourceConfigurator::init(auth_server); + config_session->addRemoteConfig("data_sources", datasrcConfigHandler, + false); // HACK: The default is not passed to the handler. This one will // get the default (or, current value). Further updates will work // the usual way. @@ -221,7 +233,8 @@ main(int argc, char* argv[]) { xfrin_session->disconnect(); } - DataSourceConfigurator::cleanup(); + //DataSourceConfigurator::cleanup(); + config_session->removeRemoteConfig("data_sources"); delete xfrin_session; delete config_session; delete cc_session; diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc index 254b2380c7..8759a937fb 100644 --- a/src/bin/auth/tests/datasrc_configurator_unittest.cc +++ b/src/bin/auth/tests/datasrc_configurator_unittest.cc @@ -62,6 +62,16 @@ typedef shared_ptr ListPtr; typedef DataSourceConfiguratorGeneric Configurator; +void +datasrcConfigHandler(const std::string&, + isc::data::ConstElementPtr config, + const isc::config::ConfigData&) +{ + if (config->contains("classes")) { + Configurator::reconfigure(config->get("classes")); + } +} + class DatasrcConfiguratorTest : public ::testing::Test { public: // These pretend to be the server @@ -100,6 +110,7 @@ protected: } void TearDown() { // Make sure no matter what we did, it is cleaned up. + mccs->removeRemoteConfig("data_sources"); Configurator::cleanup(); } void init(const ElementPtr& config = ElementPtr()) { @@ -114,7 +125,8 @@ protected: session.getMessages()-> add(createAnswer(0, ElementPtr(new MapElement))); } - Configurator::init(mccs.get(), this); + Configurator::init(this); + mccs->addRemoteConfig("data_sources", datasrcConfigHandler, false); } void SetUp() { init(); @@ -126,10 +138,10 @@ protected: return (external); } void initializeINList() { - const ElementPtr + const ConstElementPtr config(buildConfig("{\"IN\": [{\"type\": \"xxx\"}]}")); - session.addMessage(createCommand("config_update", config), "data_sources", - "*"); + session.addMessage(createCommand("config_update", config), + "data_sources", "*"); mccs->checkCommand(); // Check it called the correct things (check that there's no IN yet and // set a new one. @@ -150,15 +162,14 @@ TEST_F(DatasrcConfiguratorTest, initialization) { EXPECT_THROW(init(), InvalidOperation); EXPECT_TRUE(session.haveSubscription("data_sources", "*")); // Deinitialize to make the tests reasonable + mccs->removeRemoteConfig("data_sources"); Configurator::cleanup(); EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // We can't reconfigure now (not even manually) EXPECT_THROW(Configurator::reconfigure(ElementPtr(new MapElement())), InvalidOperation); - // If one of them is NULL, it does not work - EXPECT_THROW(Configurator::init(NULL, this), InvalidParameter); - EXPECT_FALSE(session.haveSubscription("data_sources", "*")); - EXPECT_THROW(Configurator::init(mccs.get(), NULL), InvalidParameter); + // If the server param is NULL, it does not work + EXPECT_THROW(Configurator::init(NULL), InvalidParameter); EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // But we can initialize it again now EXPECT_NO_THROW(init()); From da22b37709978a87d276b1d88a92b401ae9a2084 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 2 Oct 2012 20:38:31 -0700 Subject: [PATCH 117/194] [2203] changed the callback type of addRemoteConfig to boost::function. this will make it more convenient, e.g., by allowing the caller to pass boost::bind encapsulating a class object and a class method. boost::function is upper compatible to function pointer, so it doesn't ensure source-level compatibility. the functor overhead shouldn't matter in this context, and since this module already uses boost::function this change doesn't introduce additional dependency. --- src/lib/config/ccsession.cc | 6 ++---- src/lib/config/ccsession.h | 13 +++++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index daec005dda..8516d6c863 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -338,7 +338,7 @@ getRelatedLoggers(ConstElementPtr loggers) { // Now find the wildcard names (the one that start with "*"). BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { - std::string cur_name = cur_logger->get("name")->stringValue(); + const std::string cur_name = cur_logger->get("name")->stringValue(); // If name is '*', or starts with '*.', replace * with root // logger name. if (cur_name == "*" || cur_name.length() > 1 && @@ -671,9 +671,7 @@ ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) { std::string ModuleCCSession::addRemoteConfig(const std::string& spec_name, - void (*handler)(const std::string& module, - ConstElementPtr, - const ConfigData&), + RemoteHandler handler, bool spec_is_filename) { // First get the module name, specification and default config diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h index e96a33d44b..4b99a44bd8 100644 --- a/src/lib/config/ccsession.h +++ b/src/lib/config/ccsession.h @@ -283,7 +283,7 @@ public: * the configuration manager must know it). If * spec_is_filename is true (the default), then a * filename is assumed, otherwise a module name. - * \param handler The handler function called whenever there's a change. + * \param handler The handler functor called whenever there's a change. * Called once initally from this function. May be NULL * if you don't want any handler to be called and you're * fine with requesting the data through @@ -296,11 +296,11 @@ public: * \return The name of the module specified in the given specification * file */ + typedef boost::function RemoteHandler; std::string addRemoteConfig(const std::string& spec_name, - void (*handler)(const std::string& module_name, - isc::data::ConstElementPtr - update, - const ConfigData& config_data) = NULL, + RemoteHandler handler = RemoteHandler(), bool spec_is_filename = true); /** @@ -513,9 +513,6 @@ private: const std::string& command, isc::data::ConstElementPtr args); - typedef void (*RemoteHandler)(const std::string&, - isc::data::ConstElementPtr, - const ConfigData&); std::map remote_module_configs_; std::map remote_module_handlers_; From f38d24a77f7a146b6a37f912c98af268df40f5e7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 15:24:22 -0700 Subject: [PATCH 118/194] [2203] refactoring 2nd step: configurator can now be a separate object. i.e., it's not a singleton any more. testReconfigure() method isn't needed any more because it doesn't hold CC session internally. DatasrcConfiguratorTest.initialization test currently fails and is disabled for now. The plan is to make the class completely stateless, at which point we don't even have to think about initialization or cleanup, and then the test will be able to be removed. --- src/bin/auth/benchmarks/query_bench.cc | 14 ++-- src/bin/auth/datasrc_configurator.h | 78 ++++--------------- src/bin/auth/main.cc | 45 ++++++----- src/bin/auth/tests/auth_srv_unittest.cc | 59 +++++++------- src/bin/auth/tests/command_unittest.cc | 18 +++-- .../tests/datasrc_configurator_unittest.cc | 36 +++++---- 6 files changed, 112 insertions(+), 138 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index 93253db8fe..e31668c0cf 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -81,6 +81,7 @@ protected: QueryBenchMark(const BenchQueries& queries, Message& query_message, OutputBuffer& buffer) : server_(new AuthSrv(xfrout_client, ddns_forwarder)), + datasrc_configurator_(server_.get()), queries_(queries), query_message_(query_message), buffer_(buffer), @@ -109,6 +110,7 @@ private: MockSocketSessionForwarder ddns_forwarder; protected: AuthSrvPtr server_; + DataSourceConfigurator datasrc_configurator_; private: const BenchQueries& queries_; Message& query_message_; @@ -125,8 +127,7 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - DataSourceConfigurator::testReconfigure( - server_.get(), + datasrc_configurator_.reconfigure( Element::fromJSON("{\"IN\":" " [{\"type\": \"sqlite3\"," " \"params\": {" @@ -139,13 +140,12 @@ class MemoryQueryBenchMark : public QueryBenchMark { public: MemoryQueryBenchMark(const char* const zone_file, const char* const zone_origin, - const BenchQueries& queries, - Message& query_message, - OutputBuffer& buffer) : + const BenchQueries& queries, + Message& query_message, + OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - DataSourceConfigurator::testReconfigure( - server_.get(), + datasrc_configurator_.reconfigure( Element::fromJSON("{\"IN\":" " [{\"type\": \"MasterFiles\"," " \"cache-enable\": true, " diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h index badf62c87c..94a01bd383 100644 --- a/src/bin/auth/datasrc_configurator.h +++ b/src/bin/auth/datasrc_configurator.h @@ -30,32 +30,25 @@ /// keep the local copy of data source clients in the list in the authoritative /// server. /// -/// The class is slightly unusual. Due to some technical limitations, the hook -/// needs to be static method. Therefore it is not possible to create instances -/// of the class. -/// /// Also, the class is a template. This is simply because of easier testing. /// You don't need to pay attention to it, use the DataSourceConfigurator /// type alias instead. template class DataSourceConfiguratorGeneric { private: - /// \brief Disallow creation of instances - DataSourceConfiguratorGeneric(); - /// \brief Internal method to hook into the ModuleCCSession - /// - /// It simply calls reconfigure. - static void reconfigureInternal(const std::string&, - isc::data::ConstElementPtr config, - const isc::config::ConfigData&) - { - if (config->contains("classes")) { - reconfigure(config->get("classes")); - } - } - static Server* server_; + Server* server_; typedef boost::shared_ptr ListPtr; public: + /// \brief Constructor. + /// + /// \throw isc::InvalidParameter if server is NULL + /// \param server The server to configure. + DataSourceConfiguratorGeneric(Server* server) : server_(server) { + if (server == NULL) { + isc_throw(isc::InvalidParameter, "The server must not be NULL"); + } + } + /// \brief Initializes the class. /// /// This configures which session and server should be used. @@ -66,22 +59,13 @@ public: /// session dies, otherwise it might access them after they /// are destroyed. /// - /// \param session The session to hook into and to access the configuration - /// through. /// \param server It is the server to configure. - /// \throw isc::InvalidOperation if this is called when already initialized. + /// \throw isc::InvalidOperation if this is called when already + /// initialized. /// \throw isc::InvalidParameter if any of the parameters is NULL /// \throw isc::config::ModuleCCError if the remote configuration is not /// available for some reason. - static void init(Server* server) { - if (server == NULL) { - isc_throw(isc::InvalidParameter, "The server must not be NULL"); - } - if (server_ != NULL) { - isc_throw(isc::InvalidOperation, - "The configurator is already initialized"); - } - server_ = server; + void init() { } /// \brief Deinitializes the class. @@ -91,7 +75,7 @@ public: /// /// This can be called even if it is not initialized currently. You /// can initialize it again after this. - static void cleanup() { + void cleanup() { server_ = NULL; } @@ -105,7 +89,7 @@ public: /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. /// \throw InvalidOperation if it is called when not initialized. - static void reconfigure(const isc::data::ConstElementPtr& config) { + void reconfigure(const isc::data::ConstElementPtr& config) { if (server_ == NULL) { isc_throw(isc::InvalidOperation, "Can't reconfigure while not initialized by init()"); @@ -171,38 +155,8 @@ public: throw; } } - /// \brief Version of reconfigure for easier testing. - /// - /// This method can be used to reconfigure a server without first - /// initializing the configurator. This does not need a session. - /// Otherwise, it acts the same as reconfigure. - /// - /// This is not meant for production code. Do not use there. - /// - /// \param server The server to configure. - /// \param config The config to use. - /// \throw isc::InvalidOperation if the configurator is initialized. - /// \throw anything that reconfigure does. - static void testReconfigure(Server* server, - const isc::data::ConstElementPtr& config) - { - if (server_ != NULL) { - isc_throw(isc::InvalidOperation, "Currently initialized."); - } - try { - server_ = server; - reconfigure(config); - server_ = NULL; - } catch (...) { - server_ = NULL; - throw; - } - } }; -template -Server* DataSourceConfiguratorGeneric::server_(NULL); - /// \brief Concrete version of DataSourceConfiguratorGeneric for the /// use in authoritative server. typedef DataSourceConfiguratorGeneric -#include -#include -#include -#include -#include -#include -#include - -#include -#include - #include #include @@ -52,6 +41,20 @@ #include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + using namespace std; using namespace isc::asiodns; using namespace isc::asiolink; @@ -84,12 +87,12 @@ my_command_handler(const string& command, ConstElementPtr args) { } void -datasrcConfigHandler(const std::string&, +datasrcConfigHandler(DataSourceConfigurator* configurator, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { if (config->contains("classes")) { - DataSourceConfigurator::reconfigure(config->get("classes")); + configurator->reconfigure(config->get("classes")); } } @@ -137,6 +140,7 @@ main(int argc, char* argv[]) { ModuleCCSession* config_session = NULL; XfroutClient xfrout_client(getXfroutSocketPath()); SocketSessionForwarder ddns_forwarder(getDDNSSocketPath()); + boost::scoped_ptr datasrc_configurator; try { string specfile; if (getenv("B10_FROM_BUILD")) { @@ -202,13 +206,17 @@ main(int argc, char* argv[]) { auth_server->setTSIGKeyRing(&isc::server_common::keyring); // Start the data source configuration - DataSourceConfigurator::init(auth_server); - config_session->addRemoteConfig("data_sources", datasrcConfigHandler, + datasrc_configurator.reset(new DataSourceConfigurator(auth_server)); + config_session->addRemoteConfig("data_sources", + boost::bind(datasrcConfigHandler, + datasrc_configurator.get(), + _1, _2, _3), false); + // HACK: The default is not passed to the handler. This one will // get the default (or, current value). Further updates will work // the usual way. - DataSourceConfigurator::reconfigure( + datasrc_configurator->reconfigure( config_session->getRemoteConfigValue("data_sources", "classes")); // Now start asynchronous read. @@ -233,8 +241,9 @@ main(int argc, char* argv[]) { xfrin_session->disconnect(); } - //DataSourceConfigurator::cleanup(); - config_session->removeRemoteConfig("data_sources"); + if (datasrc_configurator) { + config_session->removeRemoteConfig("data_sources"); + } delete xfrin_session; delete config_session; delete cc_session; diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 3197e7ecae..559816adde 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -98,7 +98,8 @@ protected: // The empty string is expected value of the parameter of // requestSocket, not the app_name (there's no fallback, it checks // the empty string is passed). - sock_requestor_(dnss_, address_store_, 53210, "") + sock_requestor_(dnss_, address_store_, 53210, ""), + datasrc_configurator_(&server) { server.setDNSService(dnss_); server.setXfrinSession(¬ify_session); @@ -183,6 +184,7 @@ protected: vector response_data; AddressList address_store_; TestSocketRequestor sock_requestor_; + DataSourceConfigurator datasrc_configurator_; }; // A helper function that builds a response to version.bind/TXT/CH that @@ -722,17 +724,21 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) { } void -updateDatabase(AuthSrv* server, const char* params) { +updateDatabase(DataSourceConfigurator& datasrc_configurator, + const char* params) +{ const ConstElementPtr config(Element::fromJSON("{" "\"IN\": [{" " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - DataSourceConfigurator::testReconfigure(server, config); + datasrc_configurator.reconfigure(config); } void -updateInMemory(AuthSrv* server, const char* origin, const char* filename) { +updateInMemory(DataSourceConfigurator& datasrc_configurator, + const char* origin, const char* filename) +{ const ConstElementPtr config(Element::fromJSON("{" "\"IN\": [{" " \"type\": \"MasterFiles\"," @@ -745,17 +751,17 @@ updateInMemory(AuthSrv* server, const char* origin, const char* filename) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - DataSourceConfigurator::testReconfigure(server, config); + datasrc_configurator.reconfigure(config); } void -updateBuiltin(AuthSrv* server) { +updateBuiltin(DataSourceConfigurator& datasrc_configurator) { const ConstElementPtr config(Element::fromJSON("{" "\"CH\": [{" " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - DataSourceConfigurator::testReconfigure(server, config); + datasrc_configurator.reconfigure(config); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -766,7 +772,7 @@ TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin TEST_F(AuthSrvTest, TSIGSigned) { #endif // Prepare key, the client message, etc - updateBuiltin(&server); + updateBuiltin(datasrc_configurator_); const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1"); TSIGContext context(key); UnitTestUtil::createRequestMessage(request_message, opcode, default_qid, @@ -814,7 +820,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) { #else TEST_F(AuthSrvTest, builtInQueryViaDNSServer) { #endif - updateBuiltin(&server); + updateBuiltin(datasrc_configurator_); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -846,7 +852,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQuery) { #else TEST_F(AuthSrvTest, builtInQuery) { #endif - updateBuiltin(&server); + updateBuiltin(datasrc_configurator_); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -867,7 +873,7 @@ TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin #else TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin #endif - updateBuiltin(&server); + updateBuiltin(datasrc_configurator_); createDataFromFile("iquery_fromWire.wire"); (*server.getDNSLookupProvider())(*io_message, parse_message, response_message, @@ -889,7 +895,7 @@ TEST_F(AuthSrvTest, DISABLED_updateConfig) { #else TEST_F(AuthSrvTest, updateConfig) { #endif - updateDatabase(&server, CONFIG_TESTDB); + updateDatabase(datasrc_configurator_, CONFIG_TESTDB); // query for existent data in the installed data source. The resulting // response should have the AA flag on, and have an RR in each answer @@ -907,7 +913,7 @@ TEST_F(AuthSrvTest, DISABLED_datasourceFail) { #else TEST_F(AuthSrvTest, datasourceFail) { #endif - updateDatabase(&server, CONFIG_TESTDB); + updateDatabase(datasrc_configurator_, CONFIG_TESTDB); // This query will hit a corrupted entry of the data source (the zoneload // tool and the data source itself naively accept it). This will result @@ -927,10 +933,10 @@ TEST_F(AuthSrvTest, DISABLED_updateConfigFail) { TEST_F(AuthSrvTest, updateConfigFail) { #endif // First, load a valid data source. - updateDatabase(&server, CONFIG_TESTDB); + updateDatabase(datasrc_configurator_, CONFIG_TESTDB); // Next, try to update it with a non-existent one. This should fail. - EXPECT_THROW(updateDatabase(&server, BADCONFIG_TESTDB), + EXPECT_THROW(updateDatabase(datasrc_configurator_, BADCONFIG_TESTDB), isc::datasrc::DataSourceError); // The original data source should still exist. @@ -953,7 +959,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - DataSourceConfigurator::testReconfigure(&server, config); + datasrc_configurator_.reconfigure(config); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. @@ -974,7 +980,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) { // query handler class, and confirm it returns no error and a non empty // answer section. Detailed examination on the response content // for various types of queries are tested in the query tests. - updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_nodnssec_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -993,7 +999,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) { // Similar to the previous test, but the query has the DO bit on. // The response should contain RRSIGs, and should have more RRs than // the previous case. - updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1013,7 +1019,7 @@ TEST_F(AuthSrvTest, ) { // Set up the in-memory - updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); // This shouldn't affect the result of class CH query UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), @@ -1425,7 +1431,7 @@ TEST_F(AuthSrvTest, ) { // Set real inmem client to proxy - updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); { isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); boost::shared_ptr @@ -1450,10 +1456,11 @@ TEST_F(AuthSrvTest, // If non null rrset is given, it will be passed to the proxy so it can // return some faked response. void -setupThrow(AuthSrv* server, ThrowWhen throw_when, bool isc_exception, +setupThrow(AuthSrv* server, DataSourceConfigurator& datasrc_configurator, + ThrowWhen throw_when, bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr()) { - updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(datasrc_configurator, "example.", CONFIG_INMEMORY_EXAMPLE); isc::util::thread::Mutex::Locker locker(server->getClientListMutex()); boost::shared_ptr @@ -1482,11 +1489,11 @@ TEST_F(AuthSrvTest, RRClass::IN(), RRType::TXT()); for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) { createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(&server, *when, true); + setupThrow(&server, datasrc_configurator_, *when, true); processAndCheckSERVFAIL(); // To be sure, check same for non-isc-exceptions createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(&server, *when, false); + setupThrow(&server, datasrc_configurator_, *when, false); processAndCheckSERVFAIL(); } } @@ -1502,7 +1509,7 @@ TEST_F(AuthSrvTest, ) { createDataFromFile("nsec3query_nodnssec_fromWire.wire"); - setupThrow(&server, THROW_AT_GET_CLASS, true); + setupThrow(&server, datasrc_configurator_, THROW_AT_GET_CLASS, true); // getClass is not called so it should just answer server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1526,7 +1533,7 @@ TEST_F(AuthSrvTest, ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"), RRClass::IN(), RRType::TXT(), RRTTL(0))); - setupThrow(&server, THROW_NEVER, true, empty_rrset); + setupThrow(&server, datasrc_configurator_, THROW_NEVER, true, empty_rrset); // Repeat the query processing two times. Due to the faked RRset, // toWire() should throw, and it should result in SERVFAIL. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index e57de93d67..c5ee8f1e12 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -64,6 +64,7 @@ class AuthCommandTest : public ::testing::Test { protected: AuthCommandTest() : server_(xfrout_, ddns_forwarder_), + datasrc_configurator_(&server_), rcode_(-1), expect_rcode_(0), itimer_(server_.getIOService()) @@ -77,6 +78,7 @@ protected: MockXfroutClient xfrout_; MockSocketSessionForwarder ddns_forwarder_; AuthSrv server_; + DataSourceConfigurator datasrc_configurator_; ConstElementPtr result_; // The shutdown command parameter ConstElementPtr param_; @@ -190,7 +192,7 @@ zoneChecks(AuthSrv& server) { } void -configureZones(AuthSrv& server) { +configureZones(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in " TEST_DATA_BUILDDIR "/test1.zone.copied")); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in " @@ -208,7 +210,7 @@ configureZones(AuthSrv& server) { " \"cache-enable\": true" "}]}")); - DataSourceConfigurator::testReconfigure(&server, config); + datasrc_configurator.reconfigure(config); zoneChecks(server); } @@ -234,7 +236,7 @@ newZoneChecks(AuthSrv& server) { } TEST_F(AuthCommandTest, loadZone) { - configureZones(server_); + configureZones(server_, datasrc_configurator_); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1-new.zone.in " @@ -271,7 +273,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - DataSourceConfigurator::testReconfigure(&server_, config); + datasrc_configurator_.reconfigure(config); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); @@ -335,7 +337,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.com\"]" "}]}")); - EXPECT_THROW(DataSourceConfigurator::testReconfigure(&server_, config2), + EXPECT_THROW(datasrc_configurator_.reconfigure(config2), ConfigurableClientList::ConfigurationError); result_ = execAuthServerCommand(server_, "loadzone", @@ -350,7 +352,7 @@ TEST_F(AuthCommandTest, } TEST_F(AuthCommandTest, loadBrokenZone) { - configureZones(server_); + configureZones(server_, datasrc_configurator_); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1-broken.zone.in " @@ -363,7 +365,7 @@ TEST_F(AuthCommandTest, loadBrokenZone) { } TEST_F(AuthCommandTest, loadUnreadableZone) { - configureZones(server_); + configureZones(server_, datasrc_configurator_); // install the zone file as unreadable ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR @@ -386,7 +388,7 @@ TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) { } TEST_F(AuthCommandTest, loadZoneInvalidParams) { - configureZones(server_); + configureZones(server_, datasrc_configurator_); // null arg result_ = execAuthServerCommand(server_, "loadzone", ElementPtr()); diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc index 8759a937fb..e5b50cb3ff 100644 --- a/src/bin/auth/tests/datasrc_configurator_unittest.cc +++ b/src/bin/auth/tests/datasrc_configurator_unittest.cc @@ -19,9 +19,12 @@ #include #include -#include + +#include #include +#include + using namespace isc; using namespace isc::cc; using namespace isc::config; @@ -63,12 +66,12 @@ typedef DataSourceConfiguratorGeneric Configurator; void -datasrcConfigHandler(const std::string&, +datasrcConfigHandler(Configurator* configurator, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { if (config->contains("classes")) { - Configurator::reconfigure(config->get("classes")); + configurator->reconfigure(config->get("classes")); } } @@ -99,6 +102,7 @@ protected: DatasrcConfiguratorTest() : session(ElementPtr(new ListElement), ElementPtr(new ListElement), ElementPtr(new ListElement)), + configurator_(this), specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec") { initSession(); @@ -111,7 +115,6 @@ protected: void TearDown() { // Make sure no matter what we did, it is cleaned up. mccs->removeRemoteConfig("data_sources"); - Configurator::cleanup(); } void init(const ElementPtr& config = ElementPtr()) { session.getMessages()-> @@ -125,8 +128,9 @@ protected: session.getMessages()-> add(createAnswer(0, ElementPtr(new MapElement))); } - Configurator::init(this); - mccs->addRemoteConfig("data_sources", datasrcConfigHandler, false); + mccs->addRemoteConfig("data_sources", + boost::bind(datasrcConfigHandler, &configurator_, + _1, _2, _3), false); } void SetUp() { init(); @@ -150,6 +154,7 @@ protected: } FakeSession session; auto_ptr mccs; + Configurator configurator_; const string specfile; std::map lists_; string log_; @@ -157,20 +162,17 @@ protected: }; // Check the initialization (and cleanup) -TEST_F(DatasrcConfiguratorTest, initialization) { +TEST_F(DatasrcConfiguratorTest, DISABLED_initialization) { // It can't be initialized again EXPECT_THROW(init(), InvalidOperation); EXPECT_TRUE(session.haveSubscription("data_sources", "*")); - // Deinitialize to make the tests reasonable - mccs->removeRemoteConfig("data_sources"); - Configurator::cleanup(); EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // We can't reconfigure now (not even manually) - EXPECT_THROW(Configurator::reconfigure(ElementPtr(new MapElement())), + EXPECT_THROW(configurator_.reconfigure(ElementPtr(new MapElement())), InvalidOperation); // If the server param is NULL, it does not work - EXPECT_THROW(Configurator::init(NULL), InvalidParameter); - EXPECT_FALSE(session.haveSubscription("data_sources", "*")); + EXPECT_THROW(Configurator(NULL), InvalidParameter); + EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // TBD // But we can initialize it again now EXPECT_NO_THROW(init()); EXPECT_TRUE(session.haveSubscription("data_sources", "*")); @@ -279,7 +281,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - Configurator::reconfigure(config1); + configurator_.reconfigure(config1); const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}")); // This would delete CH. However, the IN one fails. @@ -287,7 +289,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { // and there's no known way to cause an exception during the // deletions, it is not a true rollback, but the result should // be the same. - EXPECT_THROW(Configurator::reconfigure(config2), TypeError); + EXPECT_THROW(configurator_.reconfigure(config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } @@ -300,13 +302,13 @@ TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - Configurator::reconfigure(config1); + configurator_.reconfigure(config1); // Now, the CH happens first. But nevertheless, it should be // restored to the previoeus version. const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"yyy\"}]}")); - EXPECT_THROW(Configurator::reconfigure(config2), TypeError); + EXPECT_THROW(configurator_.reconfigure(config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } From bb21aa5022e544357f9faa327278680acf5f16e0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 15:44:29 -0700 Subject: [PATCH 119/194] [2203] pass server obj to configurator's reconfigure(). now the configurator class is completely stateless. --- src/bin/auth/benchmarks/query_bench.cc | 3 +- src/bin/auth/datasrc_configurator.h | 71 ++++--------------- src/bin/auth/main.cc | 13 ++-- src/bin/auth/tests/auth_srv_unittest.cc | 65 +++++++++-------- src/bin/auth/tests/command_unittest.cc | 7 +- .../tests/datasrc_configurator_unittest.cc | 22 +++--- 6 files changed, 75 insertions(+), 106 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index e31668c0cf..fa1c28e16c 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -81,7 +81,6 @@ protected: QueryBenchMark(const BenchQueries& queries, Message& query_message, OutputBuffer& buffer) : server_(new AuthSrv(xfrout_client, ddns_forwarder)), - datasrc_configurator_(server_.get()), queries_(queries), query_message_(query_message), buffer_(buffer), @@ -128,6 +127,7 @@ public: QueryBenchMark(queries, query_message, buffer) { datasrc_configurator_.reconfigure( + *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"sqlite3\"," " \"params\": {" @@ -146,6 +146,7 @@ public: QueryBenchMark(queries, query_message, buffer) { datasrc_configurator_.reconfigure( + *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"MasterFiles\"," " \"cache-enable\": true, " diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h index 94a01bd383..4921d3613f 100644 --- a/src/bin/auth/datasrc_configurator.h +++ b/src/bin/auth/datasrc_configurator.h @@ -18,8 +18,6 @@ #include "auth_srv.h" #include -#include -#include #include #include @@ -36,48 +34,10 @@ template class DataSourceConfiguratorGeneric { private: - Server* server_; typedef boost::shared_ptr ListPtr; public: - /// \brief Constructor. - /// - /// \throw isc::InvalidParameter if server is NULL - /// \param server The server to configure. - DataSourceConfiguratorGeneric(Server* server) : server_(server) { - if (server == NULL) { - isc_throw(isc::InvalidParameter, "The server must not be NULL"); - } - } - - /// \brief Initializes the class. - /// - /// This configures which session and server should be used. - /// It hooks to the session now and downloads the configuration. - /// It is synchronous (it may block for some time). - /// - /// Note that you need to call cleanup before the server or - /// session dies, otherwise it might access them after they - /// are destroyed. - /// - /// \param server It is the server to configure. - /// \throw isc::InvalidOperation if this is called when already - /// initialized. - /// \throw isc::InvalidParameter if any of the parameters is NULL - /// \throw isc::config::ModuleCCError if the remote configuration is not - /// available for some reason. - void init() { - } - - /// \brief Deinitializes the class. - /// - /// This detaches from the session and removes the server from internal - /// storage. The current configuration in the server is preserved. - /// - /// This can be called even if it is not initialized currently. You - /// can initialize it again after this. - void cleanup() { - server_ = NULL; - } + /// \brief Default constructor. + DataSourceConfiguratorGeneric() {} /// \brief Reads new configuration and replaces the old one. /// @@ -86,16 +46,15 @@ public: /// is unknown and it would be questionable at least). It is called /// automatically on normal updates. /// + /// \param server The server for which the data sources are to be + /// configured. /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. - /// \throw InvalidOperation if it is called when not initialized. - void reconfigure(const isc::data::ConstElementPtr& config) { - if (server_ == NULL) { - isc_throw(isc::InvalidOperation, - "Can't reconfigure while not initialized by init()"); - } + void reconfigure(Server& server, + const isc::data::ConstElementPtr& config) + { // Lock the client lists, we're going to manipulate them. - isc::util::thread::Mutex::Locker locker(server_->getClientListMutex()); + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); typedef std::map Map; typedef std::pair RollbackPair; typedef std::pair @@ -107,14 +66,14 @@ public: // Get the configuration and current state. const Map& map(config->mapValue()); const std::vector - activeVector(server_->getClientListClasses()); + activeVector(server.getClientListClasses()); std::set active(activeVector.begin(), activeVector.end()); // Go through the configuration and change everything. for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { isc::dns::RRClass rrclass(it->first); active.erase(rrclass); - ListPtr list(server_->getClientList(rrclass)); + ListPtr list(server.getClientList(rrclass)); bool need_set(false); if (list) { rollback_configurations. @@ -127,7 +86,7 @@ public: } list->configure(it->second, true); if (need_set) { - server_->setClientList(rrclass, list); + server.setClientList(rrclass, list); } } // Remove the ones that are not in the configuration. @@ -137,20 +96,20 @@ public: // But this is just to make sure in case it did to restore // the original. rollback_sets.push_back( - RollbackPair(*it, server_->getClientList(*it))); - server_->setClientList(*it, ListPtr()); + RollbackPair(*it, server.getClientList(*it))); + server.setClientList(*it, ListPtr()); } } catch (...) { // Perform a rollback of the changes. The old configuration should // work. for (typename std::vector::const_iterator it(rollback_sets.begin()); it != rollback_sets.end(); ++it) { - server_->setClientList(it->first, it->second); + server.setClientList(it->first, it->second); } for (typename std::vector::const_iterator it(rollback_configurations.begin()); it != rollback_configurations.end(); ++it) { - server_->getClientList(it->first)->configure(it->second, true); + server.getClientList(it->first)->configure(it->second, true); } throw; } diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index fa11521898..5a720637ef 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -73,7 +73,7 @@ namespace { /* need global var for config/command handlers. * todo: turn this around, and put handlers in the authserver * class itself? */ -AuthSrv *auth_server; +AuthSrv* auth_server; ConstElementPtr my_config_handler(ConstElementPtr new_config) { @@ -87,12 +87,13 @@ my_command_handler(const string& command, ConstElementPtr args) { } void -datasrcConfigHandler(DataSourceConfigurator* configurator, const std::string&, - isc::data::ConstElementPtr config, +datasrcConfigHandler(AuthSrv* server, DataSourceConfigurator* configurator, + const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { + assert(server != NULL); if (config->contains("classes")) { - configurator->reconfigure(config->get("classes")); + configurator->reconfigure(*server, config->get("classes")); } } @@ -206,9 +207,10 @@ main(int argc, char* argv[]) { auth_server->setTSIGKeyRing(&isc::server_common::keyring); // Start the data source configuration - datasrc_configurator.reset(new DataSourceConfigurator(auth_server)); + datasrc_configurator.reset(new DataSourceConfigurator); config_session->addRemoteConfig("data_sources", boost::bind(datasrcConfigHandler, + auth_server, datasrc_configurator.get(), _1, _2, _3), false); @@ -217,6 +219,7 @@ main(int argc, char* argv[]) { // get the default (or, current value). Further updates will work // the usual way. datasrc_configurator->reconfigure( + *auth_server, config_session->getRemoteConfigValue("data_sources", "classes")); // Now start asynchronous read. diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 559816adde..314f61806b 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -98,8 +98,7 @@ protected: // The empty string is expected value of the parameter of // requestSocket, not the app_name (there's no fallback, it checks // the empty string is passed). - sock_requestor_(dnss_, address_store_, 53210, ""), - datasrc_configurator_(&server) + sock_requestor_(dnss_, address_store_, 53210, "") { server.setDNSService(dnss_); server.setXfrinSession(¬ify_session); @@ -724,7 +723,7 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) { } void -updateDatabase(DataSourceConfigurator& datasrc_configurator, +updateDatabase(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, const char* params) { const ConstElementPtr config(Element::fromJSON("{" @@ -732,11 +731,11 @@ updateDatabase(DataSourceConfigurator& datasrc_configurator, " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - datasrc_configurator.reconfigure(config); + datasrc_configurator.reconfigure(server, config); } void -updateInMemory(DataSourceConfigurator& datasrc_configurator, +updateInMemory(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, const char* origin, const char* filename) { const ConstElementPtr config(Element::fromJSON("{" @@ -751,17 +750,17 @@ updateInMemory(DataSourceConfigurator& datasrc_configurator, " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - datasrc_configurator.reconfigure(config); + datasrc_configurator.reconfigure(server, config); } void -updateBuiltin(DataSourceConfigurator& datasrc_configurator) { +updateBuiltin(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { const ConstElementPtr config(Element::fromJSON("{" "\"CH\": [{" " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - datasrc_configurator.reconfigure(config); + datasrc_configurator.reconfigure(server, config); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -772,7 +771,7 @@ TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin TEST_F(AuthSrvTest, TSIGSigned) { #endif // Prepare key, the client message, etc - updateBuiltin(datasrc_configurator_); + updateBuiltin(server, datasrc_configurator_); const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1"); TSIGContext context(key); UnitTestUtil::createRequestMessage(request_message, opcode, default_qid, @@ -820,7 +819,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) { #else TEST_F(AuthSrvTest, builtInQueryViaDNSServer) { #endif - updateBuiltin(datasrc_configurator_); + updateBuiltin(server, datasrc_configurator_); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -852,7 +851,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQuery) { #else TEST_F(AuthSrvTest, builtInQuery) { #endif - updateBuiltin(datasrc_configurator_); + updateBuiltin(server, datasrc_configurator_); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -873,7 +872,7 @@ TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin #else TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin #endif - updateBuiltin(datasrc_configurator_); + updateBuiltin(server, datasrc_configurator_); createDataFromFile("iquery_fromWire.wire"); (*server.getDNSLookupProvider())(*io_message, parse_message, response_message, @@ -895,7 +894,7 @@ TEST_F(AuthSrvTest, DISABLED_updateConfig) { #else TEST_F(AuthSrvTest, updateConfig) { #endif - updateDatabase(datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); // query for existent data in the installed data source. The resulting // response should have the AA flag on, and have an RR in each answer @@ -913,7 +912,7 @@ TEST_F(AuthSrvTest, DISABLED_datasourceFail) { #else TEST_F(AuthSrvTest, datasourceFail) { #endif - updateDatabase(datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); // This query will hit a corrupted entry of the data source (the zoneload // tool and the data source itself naively accept it). This will result @@ -933,10 +932,11 @@ TEST_F(AuthSrvTest, DISABLED_updateConfigFail) { TEST_F(AuthSrvTest, updateConfigFail) { #endif // First, load a valid data source. - updateDatabase(datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); // Next, try to update it with a non-existent one. This should fail. - EXPECT_THROW(updateDatabase(datasrc_configurator_, BADCONFIG_TESTDB), + EXPECT_THROW(updateDatabase(server, datasrc_configurator_, + BADCONFIG_TESTDB), isc::datasrc::DataSourceError); // The original data source should still exist. @@ -959,7 +959,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - datasrc_configurator_.reconfigure(config); + datasrc_configurator_.reconfigure(server, config); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. @@ -980,7 +980,8 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) { // query handler class, and confirm it returns no error and a non empty // answer section. Detailed examination on the response content // for various types of queries are tested in the query tests. - updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, datasrc_configurator_, "example.", + CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_nodnssec_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -999,7 +1000,8 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) { // Similar to the previous test, but the query has the DO bit on. // The response should contain RRSIGs, and should have more RRs than // the previous case. - updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, datasrc_configurator_, "example.", + CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1019,7 +1021,8 @@ TEST_F(AuthSrvTest, ) { // Set up the in-memory - updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, datasrc_configurator_, "example.", + CONFIG_INMEMORY_EXAMPLE); // This shouldn't affect the result of class CH query UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), @@ -1431,7 +1434,8 @@ TEST_F(AuthSrvTest, ) { // Set real inmem client to proxy - updateInMemory(datasrc_configurator_, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, datasrc_configurator_, "example.", + CONFIG_INMEMORY_EXAMPLE); { isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); boost::shared_ptr @@ -1456,17 +1460,18 @@ TEST_F(AuthSrvTest, // If non null rrset is given, it will be passed to the proxy so it can // return some faked response. void -setupThrow(AuthSrv* server, DataSourceConfigurator& datasrc_configurator, +setupThrow(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, ThrowWhen throw_when, bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr()) { - updateInMemory(datasrc_configurator, "example.", CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, datasrc_configurator, "example.", + CONFIG_INMEMORY_EXAMPLE); - isc::util::thread::Mutex::Locker locker(server->getClientListMutex()); + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); boost::shared_ptr - list(new FakeList(server->getClientList(RRClass::IN()), throw_when, + list(new FakeList(server.getClientList(RRClass::IN()), throw_when, isc_exception, rrset)); - server->setClientList(RRClass::IN(), list); + server.setClientList(RRClass::IN(), list); } TEST_F(AuthSrvTest, @@ -1489,11 +1494,11 @@ TEST_F(AuthSrvTest, RRClass::IN(), RRType::TXT()); for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) { createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(&server, datasrc_configurator_, *when, true); + setupThrow(server, datasrc_configurator_, *when, true); processAndCheckSERVFAIL(); // To be sure, check same for non-isc-exceptions createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(&server, datasrc_configurator_, *when, false); + setupThrow(server, datasrc_configurator_, *when, false); processAndCheckSERVFAIL(); } } @@ -1509,7 +1514,7 @@ TEST_F(AuthSrvTest, ) { createDataFromFile("nsec3query_nodnssec_fromWire.wire"); - setupThrow(&server, datasrc_configurator_, THROW_AT_GET_CLASS, true); + setupThrow(server, datasrc_configurator_, THROW_AT_GET_CLASS, true); // getClass is not called so it should just answer server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1533,7 +1538,7 @@ TEST_F(AuthSrvTest, ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"), RRClass::IN(), RRType::TXT(), RRTTL(0))); - setupThrow(&server, datasrc_configurator_, THROW_NEVER, true, empty_rrset); + setupThrow(server, datasrc_configurator_, THROW_NEVER, true, empty_rrset); // Repeat the query processing two times. Due to the faked RRset, // toWire() should throw, and it should result in SERVFAIL. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index c5ee8f1e12..68e1b12e3b 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -64,7 +64,6 @@ class AuthCommandTest : public ::testing::Test { protected: AuthCommandTest() : server_(xfrout_, ddns_forwarder_), - datasrc_configurator_(&server_), rcode_(-1), expect_rcode_(0), itimer_(server_.getIOService()) @@ -210,7 +209,7 @@ configureZones(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { " \"cache-enable\": true" "}]}")); - datasrc_configurator.reconfigure(config); + datasrc_configurator.reconfigure(server, config); zoneChecks(server); } @@ -273,7 +272,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - datasrc_configurator_.reconfigure(config); + datasrc_configurator_.reconfigure(server_, config); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); @@ -337,7 +336,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.com\"]" "}]}")); - EXPECT_THROW(datasrc_configurator_.reconfigure(config2), + EXPECT_THROW(datasrc_configurator_.reconfigure(server_, config2), ConfigurableClientList::ConfigurationError); result_ = execAuthServerCommand(server_, "loadzone", diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc index e5b50cb3ff..3821dd4429 100644 --- a/src/bin/auth/tests/datasrc_configurator_unittest.cc +++ b/src/bin/auth/tests/datasrc_configurator_unittest.cc @@ -66,12 +66,13 @@ typedef DataSourceConfiguratorGeneric Configurator; void -datasrcConfigHandler(Configurator* configurator, const std::string&, +datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, + Configurator* configurator, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { if (config->contains("classes")) { - configurator->reconfigure(config->get("classes")); + configurator->reconfigure(*fake_server, config->get("classes")); } } @@ -102,7 +103,6 @@ protected: DatasrcConfiguratorTest() : session(ElementPtr(new ListElement), ElementPtr(new ListElement), ElementPtr(new ListElement)), - configurator_(this), specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec") { initSession(); @@ -129,7 +129,8 @@ protected: add(createAnswer(0, ElementPtr(new MapElement))); } mccs->addRemoteConfig("data_sources", - boost::bind(datasrcConfigHandler, &configurator_, + boost::bind(datasrcConfigHandler, + this, &configurator_, _1, _2, _3), false); } void SetUp() { @@ -168,10 +169,11 @@ TEST_F(DatasrcConfiguratorTest, DISABLED_initialization) { EXPECT_TRUE(session.haveSubscription("data_sources", "*")); EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // We can't reconfigure now (not even manually) - EXPECT_THROW(configurator_.reconfigure(ElementPtr(new MapElement())), + EXPECT_THROW(configurator_.reconfigure(*this, + ElementPtr(new MapElement())), InvalidOperation); // If the server param is NULL, it does not work - EXPECT_THROW(Configurator(NULL), InvalidParameter); + EXPECT_THROW(Configurator configurator, InvalidParameter); EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // TBD // But we can initialize it again now EXPECT_NO_THROW(init()); @@ -281,7 +283,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - configurator_.reconfigure(config1); + configurator_.reconfigure(*this, config1); const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}")); // This would delete CH. However, the IN one fails. @@ -289,7 +291,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { // and there's no known way to cause an exception during the // deletions, it is not a true rollback, but the result should // be the same. - EXPECT_THROW(configurator_.reconfigure(config2), TypeError); + EXPECT_THROW(configurator_.reconfigure(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } @@ -302,13 +304,13 @@ TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - configurator_.reconfigure(config1); + configurator_.reconfigure(*this, config1); // Now, the CH happens first. But nevertheless, it should be // restored to the previoeus version. const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"yyy\"}]}")); - EXPECT_THROW(configurator_.reconfigure(config2), TypeError); + EXPECT_THROW(configurator_.reconfigure(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } From e0f618ac8f36e7fa8dbeab6164d40fd838178831 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 17:17:36 -0700 Subject: [PATCH 120/194] [2203] changed configurator class to a simple function. as it's now completely stateless and can work independently. the common specialization for the main implementation is defined in a new created .cc file. --- src/bin/auth/Makefile.am | 2 +- src/bin/auth/benchmarks/Makefile.am | 1 + src/bin/auth/benchmarks/query_bench.cc | 5 +- src/bin/auth/datasrc_configurator.cc | 26 +++ src/bin/auth/datasrc_configurator.h | 161 +++++++++--------- src/bin/auth/main.cc | 19 +-- src/bin/auth/tests/Makefile.am | 1 + src/bin/auth/tests/auth_srv_unittest.cc | 62 +++---- src/bin/auth/tests/command_unittest.cc | 17 +- .../tests/datasrc_configurator_unittest.cc | 67 +++----- 10 files changed, 172 insertions(+), 189 deletions(-) create mode 100644 src/bin/auth/datasrc_configurator.cc diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index 6128a4cf93..df30a428db 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -55,7 +55,7 @@ b10_auth_SOURCES += auth_config.cc auth_config.h b10_auth_SOURCES += command.cc command.h b10_auth_SOURCES += common.h common.cc b10_auth_SOURCES += statistics.cc statistics.h -b10_auth_SOURCES += datasrc_configurator.h +b10_auth_SOURCES += datasrc_configurator.h datasrc_configurator.cc b10_auth_SOURCES += main.cc nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am index 48e552a502..fcfcb8add4 100644 --- a/src/bin/auth/benchmarks/Makefile.am +++ b/src/bin/auth/benchmarks/Makefile.am @@ -17,6 +17,7 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc query_bench_SOURCES += ../auth_config.h ../auth_config.cc query_bench_SOURCES += ../statistics.h ../statistics.cc query_bench_SOURCES += ../auth_log.h ../auth_log.cc +query_bench_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index fa1c28e16c..07111a56d4 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -109,7 +109,6 @@ private: MockSocketSessionForwarder ddns_forwarder; protected: AuthSrvPtr server_; - DataSourceConfigurator datasrc_configurator_; private: const BenchQueries& queries_; Message& query_message_; @@ -126,7 +125,7 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - datasrc_configurator_.reconfigure( + configureDataSource( *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"sqlite3\"," @@ -145,7 +144,7 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - datasrc_configurator_.reconfigure( + configureDataSource( *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"MasterFiles\"," diff --git a/src/bin/auth/datasrc_configurator.cc b/src/bin/auth/datasrc_configurator.cc new file mode 100644 index 0000000000..5469389a0e --- /dev/null +++ b/src/bin/auth/datasrc_configurator.cc @@ -0,0 +1,26 @@ +// Copyright (C) 2012 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 +#include "auth_srv.h" +#include "datasrc_configurator.h" + +// This is a trivial specialization for the commonly used version. +// Defined in .cc to avoid accidental creation of multiple copies. +void +configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config) +{ + return (configureDataSourceGeneric(server, config)); +} diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h index 4921d3613f..b8d21f3e98 100644 --- a/src/bin/auth/datasrc_configurator.h +++ b/src/bin/auth/datasrc_configurator.h @@ -17,110 +17,101 @@ #include "auth_srv.h" +#include #include #include +#include + #include -/// \brief A class to configure the authoritative server's data source lists +/// \brief Configure the authoritative server's data source lists /// /// This will hook into the data_sources module configuration and it will /// keep the local copy of data source clients in the list in the authoritative /// server. /// -/// Also, the class is a template. This is simply because of easier testing. -/// You don't need to pay attention to it, use the DataSourceConfigurator -/// type alias instead. +/// This function is templated. This is simply because of easier testing. +/// You don't need to pay attention to it, use the configureDataSource +/// specialization instead. +/// +/// \param server It is the server to configure. +/// \param config The configuration value to parse. It is in the form +/// as an update from the config manager. template -class DataSourceConfiguratorGeneric { -private: +void +configureDataSourceGeneric(Server& server, + const isc::data::ConstElementPtr& config) +{ typedef boost::shared_ptr ListPtr; -public: - /// \brief Default constructor. - DataSourceConfiguratorGeneric() {} + typedef std::map Map; + typedef std::pair RollbackPair; + typedef std::pair + RollbackConfiguration; - /// \brief Reads new configuration and replaces the old one. - /// - /// It instructs the server to replace the lists with new ones as needed. - /// You don't need to call it directly (but you could, though the benefit - /// is unknown and it would be questionable at least). It is called - /// automatically on normal updates. - /// - /// \param server The server for which the data sources are to be - /// configured. - /// \param config The configuration value to parse. It is in the form - /// as an update from the config manager. - void reconfigure(Server& server, - const isc::data::ConstElementPtr& config) - { - // Lock the client lists, we're going to manipulate them. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - typedef std::map Map; - typedef std::pair RollbackPair; - typedef std::pair - RollbackConfiguration; - // Some structures to be able to perform a rollback - std::vector rollback_sets; - std::vector rollback_configurations; - try { - // Get the configuration and current state. - const Map& map(config->mapValue()); - const std::vector - activeVector(server.getClientListClasses()); - std::set active(activeVector.begin(), - activeVector.end()); - // Go through the configuration and change everything. - for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { - isc::dns::RRClass rrclass(it->first); - active.erase(rrclass); - ListPtr list(server.getClientList(rrclass)); - bool need_set(false); - if (list) { - rollback_configurations. - push_back(RollbackConfiguration(rrclass, - list->getConfiguration())); - } else { - list.reset(new List(rrclass)); - need_set = true; - rollback_sets.push_back(RollbackPair(rrclass, ListPtr())); - } - list->configure(it->second, true); - if (need_set) { - server.setClientList(rrclass, list); - } + // Lock the client lists, we're going to manipulate them. + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + + // Some structures to be able to perform a rollback + std::vector rollback_sets; + std::vector rollback_configurations; + try { + // Get the configuration and current state. + const Map& map(config->mapValue()); + const std::vector + activeVector(server.getClientListClasses()); + std::set active(activeVector.begin(), + activeVector.end()); + // Go through the configuration and change everything. + for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { + const isc::dns::RRClass rrclass(it->first); + active.erase(rrclass); + ListPtr list(server.getClientList(rrclass)); + bool need_set(false); + if (list) { + rollback_configurations. + push_back(RollbackConfiguration(rrclass, + list->getConfiguration())); + } else { + list.reset(new List(rrclass)); + need_set = true; + rollback_sets.push_back(RollbackPair(rrclass, ListPtr())); } - // Remove the ones that are not in the configuration. - for (std::set::iterator it(active.begin()); - it != active.end(); ++it) { - // There seems to be no way the setClientList could throw. - // But this is just to make sure in case it did to restore - // the original. - rollback_sets.push_back( - RollbackPair(*it, server.getClientList(*it))); - server.setClientList(*it, ListPtr()); + list->configure(it->second, true); + if (need_set) { + server.setClientList(rrclass, list); } - } catch (...) { - // Perform a rollback of the changes. The old configuration should - // work. - for (typename std::vector::const_iterator - it(rollback_sets.begin()); it != rollback_sets.end(); ++it) { - server.setClientList(it->first, it->second); - } - for (typename std::vector::const_iterator - it(rollback_configurations.begin()); - it != rollback_configurations.end(); ++it) { - server.getClientList(it->first)->configure(it->second, true); - } - throw; } + // Remove the ones that are not in the configuration. + for (std::set::iterator it(active.begin()); + it != active.end(); ++it) { + // There seems to be no way the setClientList could throw. + // But this is just to make sure in case it did to restore + // the original. + rollback_sets.push_back( + RollbackPair(*it, server.getClientList(*it))); + server.setClientList(*it, ListPtr()); + } + } catch (...) { + // Perform a rollback of the changes. The old configuration should + // work. + for (typename std::vector::const_iterator + it(rollback_sets.begin()); it != rollback_sets.end(); ++it) { + server.setClientList(it->first, it->second); + } + for (typename std::vector::const_iterator + it(rollback_configurations.begin()); + it != rollback_configurations.end(); ++it) { + server.getClientList(it->first)->configure(it->second, true); + } + throw; } -}; +} /// \brief Concrete version of DataSourceConfiguratorGeneric for the -/// use in authoritative server. -typedef DataSourceConfiguratorGeneric - DataSourceConfigurator; +/// use with authoritative server implementation. +void +configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); #endif diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 5a720637ef..eecc5aa1a6 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -87,13 +87,13 @@ my_command_handler(const string& command, ConstElementPtr args) { } void -datasrcConfigHandler(AuthSrv* server, DataSourceConfigurator* configurator, - const std::string&, isc::data::ConstElementPtr config, +datasrcConfigHandler(AuthSrv* server, const std::string&, + isc::data::ConstElementPtr config, const isc::config::ConfigData&) { assert(server != NULL); if (config->contains("classes")) { - configurator->reconfigure(*server, config->get("classes")); + configureDataSource(*server, config->get("classes")); } } @@ -141,7 +141,6 @@ main(int argc, char* argv[]) { ModuleCCSession* config_session = NULL; XfroutClient xfrout_client(getXfroutSocketPath()); SocketSessionForwarder ddns_forwarder(getDDNSSocketPath()); - boost::scoped_ptr datasrc_configurator; try { string specfile; if (getenv("B10_FROM_BUILD")) { @@ -207,19 +206,16 @@ main(int argc, char* argv[]) { auth_server->setTSIGKeyRing(&isc::server_common::keyring); // Start the data source configuration - datasrc_configurator.reset(new DataSourceConfigurator); config_session->addRemoteConfig("data_sources", boost::bind(datasrcConfigHandler, auth_server, - datasrc_configurator.get(), _1, _2, _3), false); // HACK: The default is not passed to the handler. This one will // get the default (or, current value). Further updates will work // the usual way. - datasrc_configurator->reconfigure( - *auth_server, + configureDataSource(*auth_server, config_session->getRemoteConfigValue("data_sources", "classes")); // Now start asynchronous read. @@ -244,9 +240,10 @@ main(int argc, char* argv[]) { xfrin_session->disconnect(); } - if (datasrc_configurator) { - config_session->removeRemoteConfig("data_sources"); - } + // If we haven't registered callback for data sources, this will be just + // no-op. + config_session->removeRemoteConfig("data_sources"); + delete xfrin_session; delete config_session; delete cc_session; diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index 7c01a6bd3f..3b1873678c 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -42,6 +42,7 @@ run_unittests_SOURCES += ../auth_config.h ../auth_config.cc run_unittests_SOURCES += ../command.h ../command.cc run_unittests_SOURCES += ../common.h ../common.cc run_unittests_SOURCES += ../statistics.h ../statistics.cc +run_unittests_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc run_unittests_SOURCES += datasrc_util.h datasrc_util.cc run_unittests_SOURCES += auth_srv_unittest.cc run_unittests_SOURCES += config_unittest.cc diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 314f61806b..d462a3fd76 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -183,7 +183,6 @@ protected: vector response_data; AddressList address_store_; TestSocketRequestor sock_requestor_; - DataSourceConfigurator datasrc_configurator_; }; // A helper function that builds a response to version.bind/TXT/CH that @@ -723,21 +722,17 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) { } void -updateDatabase(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, - const char* params) -{ +updateDatabase(AuthSrv& server, const char* params) { const ConstElementPtr config(Element::fromJSON("{" "\"IN\": [{" " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - datasrc_configurator.reconfigure(server, config); + configureDataSource(server, config); } void -updateInMemory(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, - const char* origin, const char* filename) -{ +updateInMemory(AuthSrv& server, const char* origin, const char* filename) { const ConstElementPtr config(Element::fromJSON("{" "\"IN\": [{" " \"type\": \"MasterFiles\"," @@ -750,17 +745,17 @@ updateInMemory(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - datasrc_configurator.reconfigure(server, config); + configureDataSource(server, config); } void -updateBuiltin(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { +updateBuiltin(AuthSrv& server) { const ConstElementPtr config(Element::fromJSON("{" "\"CH\": [{" " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - datasrc_configurator.reconfigure(server, config); + configureDataSource(server, config); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -771,7 +766,7 @@ TEST_F(AuthSrvTest, DISABLED_TSIGSigned) { // Needs builtin TEST_F(AuthSrvTest, TSIGSigned) { #endif // Prepare key, the client message, etc - updateBuiltin(server, datasrc_configurator_); + updateBuiltin(server); const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1"); TSIGContext context(key); UnitTestUtil::createRequestMessage(request_message, opcode, default_qid, @@ -819,7 +814,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQueryViaDNSServer) { #else TEST_F(AuthSrvTest, builtInQueryViaDNSServer) { #endif - updateBuiltin(server, datasrc_configurator_); + updateBuiltin(server); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -851,7 +846,7 @@ TEST_F(AuthSrvTest, DISABLED_builtInQuery) { #else TEST_F(AuthSrvTest, builtInQuery) { #endif - updateBuiltin(server, datasrc_configurator_); + updateBuiltin(server); UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), default_qid, Name("VERSION.BIND."), RRClass::CH(), RRType::TXT()); @@ -872,7 +867,7 @@ TEST_F(AuthSrvTest, DISABLED_iqueryViaDNSServer) { // Needs builtin #else TEST_F(AuthSrvTest, iqueryViaDNSServer) { // Needs builtin #endif - updateBuiltin(server, datasrc_configurator_); + updateBuiltin(server); createDataFromFile("iquery_fromWire.wire"); (*server.getDNSLookupProvider())(*io_message, parse_message, response_message, @@ -894,7 +889,7 @@ TEST_F(AuthSrvTest, DISABLED_updateConfig) { #else TEST_F(AuthSrvTest, updateConfig) { #endif - updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, CONFIG_TESTDB); // query for existent data in the installed data source. The resulting // response should have the AA flag on, and have an RR in each answer @@ -912,7 +907,7 @@ TEST_F(AuthSrvTest, DISABLED_datasourceFail) { #else TEST_F(AuthSrvTest, datasourceFail) { #endif - updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, CONFIG_TESTDB); // This query will hit a corrupted entry of the data source (the zoneload // tool and the data source itself naively accept it). This will result @@ -932,11 +927,10 @@ TEST_F(AuthSrvTest, DISABLED_updateConfigFail) { TEST_F(AuthSrvTest, updateConfigFail) { #endif // First, load a valid data source. - updateDatabase(server, datasrc_configurator_, CONFIG_TESTDB); + updateDatabase(server, CONFIG_TESTDB); // Next, try to update it with a non-existent one. This should fail. - EXPECT_THROW(updateDatabase(server, datasrc_configurator_, - BADCONFIG_TESTDB), + EXPECT_THROW(updateDatabase(server, BADCONFIG_TESTDB), isc::datasrc::DataSourceError); // The original data source should still exist. @@ -959,7 +953,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - datasrc_configurator_.reconfigure(server, config); + configureDataSource(server, config); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. @@ -980,8 +974,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) { // query handler class, and confirm it returns no error and a non empty // answer section. Detailed examination on the response content // for various types of queries are tested in the query tests. - updateInMemory(server, datasrc_configurator_, "example.", - CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_nodnssec_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1000,8 +993,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) { // Similar to the previous test, but the query has the DO bit on. // The response should contain RRSIGs, and should have more RRs than // the previous case. - updateInMemory(server, datasrc_configurator_, "example.", - CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); createDataFromFile("nsec3query_fromWire.wire"); server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1021,8 +1013,7 @@ TEST_F(AuthSrvTest, ) { // Set up the in-memory - updateInMemory(server, datasrc_configurator_, "example.", - CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); // This shouldn't affect the result of class CH query UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(), @@ -1434,8 +1425,7 @@ TEST_F(AuthSrvTest, ) { // Set real inmem client to proxy - updateInMemory(server, datasrc_configurator_, "example.", - CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); { isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); boost::shared_ptr @@ -1460,12 +1450,10 @@ TEST_F(AuthSrvTest, // If non null rrset is given, it will be passed to the proxy so it can // return some faked response. void -setupThrow(AuthSrv& server, DataSourceConfigurator& datasrc_configurator, - ThrowWhen throw_when, bool isc_exception, +setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr()) { - updateInMemory(server, datasrc_configurator, "example.", - CONFIG_INMEMORY_EXAMPLE); + updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); boost::shared_ptr @@ -1494,11 +1482,11 @@ TEST_F(AuthSrvTest, RRClass::IN(), RRType::TXT()); for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) { createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(server, datasrc_configurator_, *when, true); + setupThrow(server, *when, true); processAndCheckSERVFAIL(); // To be sure, check same for non-isc-exceptions createRequestPacket(request_message, IPPROTO_UDP); - setupThrow(server, datasrc_configurator_, *when, false); + setupThrow(server, *when, false); processAndCheckSERVFAIL(); } } @@ -1514,7 +1502,7 @@ TEST_F(AuthSrvTest, ) { createDataFromFile("nsec3query_nodnssec_fromWire.wire"); - setupThrow(server, datasrc_configurator_, THROW_AT_GET_CLASS, true); + setupThrow(server, THROW_AT_GET_CLASS, true); // getClass is not called so it should just answer server.processMessage(*io_message, *parse_message, *response_obuffer, @@ -1538,7 +1526,7 @@ TEST_F(AuthSrvTest, ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"), RRClass::IN(), RRType::TXT(), RRTTL(0))); - setupThrow(server, datasrc_configurator_, THROW_NEVER, true, empty_rrset); + setupThrow(server, THROW_NEVER, true, empty_rrset); // Repeat the query processing two times. Due to the faked RRset, // toWire() should throw, and it should result in SERVFAIL. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 68e1b12e3b..c7465263ea 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -77,7 +77,6 @@ protected: MockXfroutClient xfrout_; MockSocketSessionForwarder ddns_forwarder_; AuthSrv server_; - DataSourceConfigurator datasrc_configurator_; ConstElementPtr result_; // The shutdown command parameter ConstElementPtr param_; @@ -191,7 +190,7 @@ zoneChecks(AuthSrv& server) { } void -configureZones(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { +configureZones(AuthSrv& server) { ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in " TEST_DATA_BUILDDIR "/test1.zone.copied")); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in " @@ -209,7 +208,7 @@ configureZones(AuthSrv& server, DataSourceConfigurator& datasrc_configurator) { " \"cache-enable\": true" "}]}")); - datasrc_configurator.reconfigure(server, config); + configureDataSource(server, config); zoneChecks(server); } @@ -235,7 +234,7 @@ newZoneChecks(AuthSrv& server) { } TEST_F(AuthCommandTest, loadZone) { - configureZones(server_, datasrc_configurator_); + configureZones(server_); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1-new.zone.in " @@ -272,7 +271,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - datasrc_configurator_.reconfigure(server_, config); + configureDataSource(server_, config); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); @@ -336,7 +335,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.com\"]" "}]}")); - EXPECT_THROW(datasrc_configurator_.reconfigure(server_, config2), + EXPECT_THROW(configureDataSource(server_, config2), ConfigurableClientList::ConfigurationError); result_ = execAuthServerCommand(server_, "loadzone", @@ -351,7 +350,7 @@ TEST_F(AuthCommandTest, } TEST_F(AuthCommandTest, loadBrokenZone) { - configureZones(server_, datasrc_configurator_); + configureZones(server_); ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1-broken.zone.in " @@ -364,7 +363,7 @@ TEST_F(AuthCommandTest, loadBrokenZone) { } TEST_F(AuthCommandTest, loadUnreadableZone) { - configureZones(server_, datasrc_configurator_); + configureZones(server_); // install the zone file as unreadable ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR @@ -387,7 +386,7 @@ TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) { } TEST_F(AuthCommandTest, loadZoneInvalidParams) { - configureZones(server_, datasrc_configurator_); + configureZones(server_); // null arg result_ = execAuthServerCommand(server_, "loadzone", ElementPtr()); diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc index 3821dd4429..f294536475 100644 --- a/src/bin/auth/tests/datasrc_configurator_unittest.cc +++ b/src/bin/auth/tests/datasrc_configurator_unittest.cc @@ -60,19 +60,23 @@ private: typedef shared_ptr ListPtr; -// We use the test fixture as both parameters, this makes it possible -// to easily fake all needed methods and look that they were called. -typedef DataSourceConfiguratorGeneric Configurator; +void +testConfigureDataSource(DatasrcConfiguratorTest& test, + const isc::data::ConstElementPtr& config) +{ + // We use the test fixture for the Server type. This makes it possible + // to easily fake all needed methods and look that they were called. + configureDataSourceGeneric(test, + config); +} void -datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, - Configurator* configurator, const std::string&, +datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { if (config->contains("classes")) { - configurator->reconfigure(*fake_server, config->get("classes")); + testConfigureDataSource(*fake_server, config->get("classes")); } } @@ -113,28 +117,24 @@ protected: false)); } void TearDown() { - // Make sure no matter what we did, it is cleaned up. + // Make sure no matter what we did, it is cleaned up. Also check + // we really have subscribed to the configuration, and after removing + // it we actually cancel it. + EXPECT_TRUE(session.haveSubscription("data_sources", "*")); mccs->removeRemoteConfig("data_sources"); + EXPECT_FALSE(session.haveSubscription("data_sources", "*")); } - void init(const ElementPtr& config = ElementPtr()) { + void SetUp() { session.getMessages()-> add(createAnswer(0, moduleSpecFromFile(string(PLUGIN_DATA_PATH) + "/datasrc.spec"). getFullSpec())); - if (config) { - session.getMessages()->add(createAnswer(0, config)); - } else { - session.getMessages()-> - add(createAnswer(0, ElementPtr(new MapElement))); - } + session.getMessages()->add(createAnswer(0, + ElementPtr(new MapElement))); mccs->addRemoteConfig("data_sources", boost::bind(datasrcConfigHandler, - this, &configurator_, - _1, _2, _3), false); - } - void SetUp() { - init(); + this, _1, _2, _3), false); } ElementPtr buildConfig(const string& config) const { const ElementPtr internal(Element::fromJSON(config)); @@ -155,31 +155,12 @@ protected: } FakeSession session; auto_ptr mccs; - Configurator configurator_; const string specfile; std::map lists_; string log_; mutable isc::util::thread::Mutex mutex_; }; -// Check the initialization (and cleanup) -TEST_F(DatasrcConfiguratorTest, DISABLED_initialization) { - // It can't be initialized again - EXPECT_THROW(init(), InvalidOperation); - EXPECT_TRUE(session.haveSubscription("data_sources", "*")); - EXPECT_FALSE(session.haveSubscription("data_sources", "*")); - // We can't reconfigure now (not even manually) - EXPECT_THROW(configurator_.reconfigure(*this, - ElementPtr(new MapElement())), - InvalidOperation); - // If the server param is NULL, it does not work - EXPECT_THROW(Configurator configurator, InvalidParameter); - EXPECT_FALSE(session.haveSubscription("data_sources", "*")); // TBD - // But we can initialize it again now - EXPECT_NO_THROW(init()); - EXPECT_TRUE(session.haveSubscription("data_sources", "*")); -} - // Push there a configuration with a single list. TEST_F(DatasrcConfiguratorTest, createList) { initializeINList(); @@ -283,7 +264,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - configurator_.reconfigure(*this, config1); + testConfigureDataSource(*this, config1); const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}")); // This would delete CH. However, the IN one fails. @@ -291,7 +272,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { // and there's no known way to cause an exception during the // deletions, it is not a true rollback, but the result should // be the same. - EXPECT_THROW(configurator_.reconfigure(*this, config2), TypeError); + EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } @@ -304,13 +285,13 @@ TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) { const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); - configurator_.reconfigure(*this, config1); + testConfigureDataSource(*this, config1); // Now, the CH happens first. But nevertheless, it should be // restored to the previoeus version. const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"yyy\"}]}")); - EXPECT_THROW(configurator_.reconfigure(*this, config2), TypeError); + EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } From 6f008e19162fe3ae3911db0f770d6d38612bcba0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 17:28:41 -0700 Subject: [PATCH 121/194] [2203] a piggy back fix: prevent redundant initial data configuration. this addresses the issue described in #2291. Still not really clean, but thanks to boost::bind we can centralize all the code logic in the callback, so I think it's now less likely that we forget cleaning it up when the hack is not necessary. --- src/bin/auth/main.cc | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index eecc5aa1a6..d0ff8f48d5 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -87,13 +87,25 @@ my_command_handler(const string& command, ConstElementPtr args) { } void -datasrcConfigHandler(AuthSrv* server, const std::string&, +datasrcConfigHandler(AuthSrv* server, bool* first_time, + ModuleCCSession* config_session, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { assert(server != NULL); if (config->contains("classes")) { - configureDataSource(*server, config->get("classes")); + if (*first_time) { + // HACK: The default is not passed to the handler in the first + // callback. This one will get the default (or, current value). + // Further updates will work the usual way. + assert(config_session != NULL); + *first_time = false; + configureDataSource(*auth_server, + config_session->getRemoteConfigValue( + "data_sources", "classes")); + } else { + configureDataSource(*server, config->get("classes")); + } } } @@ -205,19 +217,16 @@ main(int argc, char* argv[]) { isc::server_common::initKeyring(*config_session); auth_server->setTSIGKeyRing(&isc::server_common::keyring); - // Start the data source configuration + // Start the data source configuration. We pass first_time and + // config_session for the hack described in datasrcConfigHandler. + bool first_time = true; config_session->addRemoteConfig("data_sources", boost::bind(datasrcConfigHandler, - auth_server, + auth_server, &first_time, + config_session, _1, _2, _3), false); - // HACK: The default is not passed to the handler. This one will - // get the default (or, current value). Further updates will work - // the usual way. - configureDataSource(*auth_server, - config_session->getRemoteConfigValue("data_sources", "classes")); - // Now start asynchronous read. config_session->start(); LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_STARTED); From 4da98b699e5d73acbf7f0a01799b3a006aec4832 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 22:58:06 -0700 Subject: [PATCH 122/194] [2203] forotten cleanup: removed now-unused header file. --- src/bin/auth/main.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index d0ff8f48d5..8431a488a2 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -42,7 +42,6 @@ #include #include -#include #include #include From b2cb1e1e337281fe923f1065fa19eac896d63b15 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 10:12:42 -0700 Subject: [PATCH 123/194] [2203] renamed datasrc_configurator to datasrc_config; it represents it better. --- src/bin/auth/Makefile.am | 2 +- src/bin/auth/benchmarks/Makefile.am | 2 +- src/bin/auth/benchmarks/query_bench.cc | 2 +- ...asrc_configurator.cc => datasrc_config.cc} | 2 +- ...atasrc_configurator.h => datasrc_config.h} | 8 ++--- src/bin/auth/main.cc | 2 +- src/bin/auth/tests/Makefile.am | 4 +-- src/bin/auth/tests/auth_srv_unittest.cc | 2 +- src/bin/auth/tests/command_unittest.cc | 2 +- ...unittest.cc => datasrc_config_unittest.cc} | 31 +++++++++---------- 10 files changed, 28 insertions(+), 29 deletions(-) rename src/bin/auth/{datasrc_configurator.cc => datasrc_config.cc} (97%) rename src/bin/auth/{datasrc_configurator.h => datasrc_config.h} (97%) rename src/bin/auth/tests/{datasrc_configurator_unittest.cc => datasrc_config_unittest.cc} (92%) diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am index df30a428db..9eee9d4d54 100644 --- a/src/bin/auth/Makefile.am +++ b/src/bin/auth/Makefile.am @@ -55,7 +55,7 @@ b10_auth_SOURCES += auth_config.cc auth_config.h b10_auth_SOURCES += command.cc command.h b10_auth_SOURCES += common.h common.cc b10_auth_SOURCES += statistics.cc statistics.h -b10_auth_SOURCES += datasrc_configurator.h datasrc_configurator.cc +b10_auth_SOURCES += datasrc_config.h datasrc_config.cc b10_auth_SOURCES += main.cc nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am index fcfcb8add4..c525b665d3 100644 --- a/src/bin/auth/benchmarks/Makefile.am +++ b/src/bin/auth/benchmarks/Makefile.am @@ -17,7 +17,7 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc query_bench_SOURCES += ../auth_config.h ../auth_config.cc query_bench_SOURCES += ../statistics.h ../statistics.cc query_bench_SOURCES += ../auth_log.h ../auth_log.cc -query_bench_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc +query_bench_SOURCES += ../datasrc_config.h ../datasrc_config.cc nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index 07111a56d4..73f702baff 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/src/bin/auth/datasrc_configurator.cc b/src/bin/auth/datasrc_config.cc similarity index 97% rename from src/bin/auth/datasrc_configurator.cc rename to src/bin/auth/datasrc_config.cc index 5469389a0e..73fb5190c6 100644 --- a/src/bin/auth/datasrc_configurator.cc +++ b/src/bin/auth/datasrc_config.cc @@ -14,7 +14,7 @@ #include #include "auth_srv.h" -#include "datasrc_configurator.h" +#include "datasrc_config.h" // This is a trivial specialization for the commonly used version. // Defined in .cc to avoid accidental creation of multiple copies. diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_config.h similarity index 97% rename from src/bin/auth/datasrc_configurator.h rename to src/bin/auth/datasrc_config.h index b8d21f3e98..79ace2811a 100644 --- a/src/bin/auth/datasrc_configurator.h +++ b/src/bin/auth/datasrc_config.h @@ -12,8 +12,8 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#ifndef DATASRC_CONFIGURATOR_H -#define DATASRC_CONFIGURATOR_H +#ifndef DATASRC_CONFIG_H +#define DATASRC_CONFIG_H #include "auth_srv.h" @@ -108,12 +108,12 @@ configureDataSourceGeneric(Server& server, } } -/// \brief Concrete version of DataSourceConfiguratorGeneric for the +/// \brief Concrete version of configureDataSource() for the /// use with authoritative server implementation. void configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); -#endif +#endif // DATASRC_CONFIG_H // Local Variables: // mode: c++ diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 8431a488a2..b425813006 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index 3b1873678c..6b9d385298 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -42,7 +42,7 @@ run_unittests_SOURCES += ../auth_config.h ../auth_config.cc run_unittests_SOURCES += ../command.h ../command.cc run_unittests_SOURCES += ../common.h ../common.cc run_unittests_SOURCES += ../statistics.h ../statistics.cc -run_unittests_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc +run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc run_unittests_SOURCES += datasrc_util.h datasrc_util.cc run_unittests_SOURCES += auth_srv_unittest.cc run_unittests_SOURCES += config_unittest.cc @@ -51,7 +51,7 @@ run_unittests_SOURCES += command_unittest.cc run_unittests_SOURCES += common_unittest.cc run_unittests_SOURCES += query_unittest.cc run_unittests_SOURCES += statistics_unittest.cc -run_unittests_SOURCES += datasrc_configurator_unittest.cc +run_unittests_SOURCES += datasrc_config_unittest.cc run_unittests_SOURCES += run_unittests.cc nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index d462a3fd76..60a9a2ac38 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index c7465263ea..c7994831a5 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc similarity index 92% rename from src/bin/auth/tests/datasrc_configurator_unittest.cc rename to src/bin/auth/tests/datasrc_config_unittest.cc index f294536475..3d3aa58b91 100644 --- a/src/bin/auth/tests/datasrc_configurator_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -12,7 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include +#include #include #include @@ -35,7 +35,7 @@ using namespace boost; namespace { -class DatasrcConfiguratorTest; +class DatasrcConfigTest; class FakeList { public: @@ -61,17 +61,16 @@ private: typedef shared_ptr ListPtr; void -testConfigureDataSource(DatasrcConfiguratorTest& test, +testConfigureDataSource(DatasrcConfigTest& test, const isc::data::ConstElementPtr& config) { // We use the test fixture for the Server type. This makes it possible // to easily fake all needed methods and look that they were called. - configureDataSourceGeneric(test, - config); + configureDataSourceGeneric(test, config); } void -datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, const std::string&, +datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, isc::data::ConstElementPtr config, const isc::config::ConfigData&) { @@ -80,7 +79,7 @@ datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, const std::string&, } } -class DatasrcConfiguratorTest : public ::testing::Test { +class DatasrcConfigTest : public ::testing::Test { public: // These pretend to be the server ListPtr getClientList(const RRClass& rrclass) { @@ -104,7 +103,7 @@ public: return (mutex_); } protected: - DatasrcConfiguratorTest() : + DatasrcConfigTest() : session(ElementPtr(new ListElement), ElementPtr(new ListElement), ElementPtr(new ListElement)), specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec") @@ -162,11 +161,11 @@ protected: }; // Push there a configuration with a single list. -TEST_F(DatasrcConfiguratorTest, createList) { +TEST_F(DatasrcConfigTest, createList) { initializeINList(); } -TEST_F(DatasrcConfiguratorTest, modifyList) { +TEST_F(DatasrcConfigTest, modifyList) { // First, initialize the list initializeINList(); // And now change the configuration of the list @@ -184,7 +183,7 @@ TEST_F(DatasrcConfiguratorTest, modifyList) { } // Check we can have multiple lists at once -TEST_F(DatasrcConfiguratorTest, multiple) { +TEST_F(DatasrcConfigTest, multiple) { const ElementPtr config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); @@ -204,7 +203,7 @@ TEST_F(DatasrcConfiguratorTest, multiple) { // // It's almost like above, but we initialize first with single-list // config. -TEST_F(DatasrcConfiguratorTest, updateAdd) { +TEST_F(DatasrcConfigTest, updateAdd) { initializeINList(); const ElementPtr config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], " @@ -222,7 +221,7 @@ TEST_F(DatasrcConfiguratorTest, updateAdd) { } // We delete a class list in this test. -TEST_F(DatasrcConfiguratorTest, updateDelete) { +TEST_F(DatasrcConfigTest, updateDelete) { initializeINList(); const ElementPtr config(buildConfig("{}")); @@ -239,7 +238,7 @@ TEST_F(DatasrcConfiguratorTest, updateDelete) { } // Check that we can rollback an addition if something else fails -TEST_F(DatasrcConfiguratorTest, rollbackAddition) { +TEST_F(DatasrcConfigTest, rollbackAddition) { initializeINList(); // The configuration is wrong. However, the CH one will get done first. const ElementPtr @@ -258,7 +257,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackAddition) { } // Check that we can rollback a deletion if something else fails -TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { +TEST_F(DatasrcConfigTest, rollbackDeletion) { initializeINList(); // Put the CH there const ElementPtr @@ -279,7 +278,7 @@ TEST_F(DatasrcConfiguratorTest, rollbackDeletion) { // Check that we can roll back configuration change if something // fails later on. -TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) { +TEST_F(DatasrcConfigTest, rollbackConfiguration) { initializeINList(); // Put the CH there const ElementPtr From dc506d0714924ad48c3cd6a93c181ddd663f0059 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 28 Sep 2012 16:32:11 -0700 Subject: [PATCH 124/194] [1870] created a separate dir to store example "3rd party" apps using BIND 10. --- examples/AUTHORS | 0 examples/COPYING | 674 ++++++++++++++++++++++++++++++++++++++++++ examples/ChangeLog | 0 examples/INSTALL | 365 +++++++++++++++++++++++ examples/Makefile.am | 1 + examples/NEWS | 0 examples/README | 0 examples/configure.ac | 90 ++++++ 8 files changed, 1130 insertions(+) create mode 100644 examples/AUTHORS create mode 100644 examples/COPYING create mode 100644 examples/ChangeLog create mode 100644 examples/INSTALL create mode 100644 examples/Makefile.am create mode 100644 examples/NEWS create mode 100644 examples/README create mode 100644 examples/configure.ac diff --git a/examples/AUTHORS b/examples/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/COPYING b/examples/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/examples/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/examples/ChangeLog b/examples/ChangeLog new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/INSTALL b/examples/INSTALL new file mode 100644 index 0000000000..7d1c323bea --- /dev/null +++ b/examples/INSTALL @@ -0,0 +1,365 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008, 2009 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000000..6048e41969 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = host diff --git a/examples/NEWS b/examples/NEWS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/README b/examples/README new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/configure.ac b/examples/configure.ac new file mode 100644 index 0000000000..c4a7e2e8a1 --- /dev/null +++ b/examples/configure.ac @@ -0,0 +1,90 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.59]) +AC_INIT(bind10-examples, 20120817, bind10-dev@isc.org) +AC_CONFIG_SRCDIR([README]) +AM_INIT_AUTOMAKE +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CXX +AC_LANG([C++]) + +# Checks for libraries. + +# Check for BIND10 libdns++ headers +AC_ARG_WITH(bind10-include, + AC_HELP_STRING([--with-bind10-include=PATH], + [specify a path to BIND 10 header files + (PATH, often needs to be in a BIND 10 source such as + /bind10/src/lib)]), + bind10_inc_path="$withval", bind10_inc_path="no") +# If not specified, try some common paths. +if test "$bind10_inc_path" = "no"; then + bind10dirs="/usr/local /usr/pkg /opt /opt/local" + for d in $bind10dirs + do + if test -f $d/dns/rrtype.h; then + bind10_inc_path=$d + break + fi + done +fi +CPPFLAGS_SAVES="$CPPFLAGS" +if test "${bind10_inc_path}" ; then + BIND10_INCLUDES="-I${bind10_inc_path}" + CPPFLAGS="$CPPFLAGS $BIND10_INCLUDES" +fi +AC_CHECK_HEADERS([dns/rrtype.h],, + AC_MSG_ERROR([Missing required BIND 10 header files.])) +CPPFLAGS="$CPPFLAGS_SAVES" +AC_SUBST(BIND10_INCLUDES) + +# Check for BIND10 lib libraries +AC_ARG_WITH(bind10-lib, + AC_HELP_STRING([--with-bind10-lib=PATH], + [specify a path to BIND 10 library files (PATH)]), + bind10_lib_path="$withval", bind10_lib_path="no") + +if test bind10_lib_path != "no"; then + BIND10_LDFLAGS="-L$bind10_lib_path" +fi +BIND10_LDADD="-lb10-dns++ -lb10-util -lb10-exceptions" + +CPPFLAGS_SAVED="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $BIND10_INCLUDES" +LDFLAGS_SAVED="$LDFLAGS" +LDFLAGS="$LDFLAGS $BIND10_LDFLAGS" +LIBS_SAVED=$LIBS +LIBS="$LIBS $BIND10_LDADD" + +AC_MSG_CHECKING([BIND 10 libraries]) +AC_TRY_LINK([ +#include +],[ +isc::dns::RRType rrtype(1); +], +[ AC_MSG_RESULT(yes)], +[ AC_MSG_RESULT(no) + AC_MSG_ERROR(unable to find required BIND 10 libraries)]) + +CPPFLAGS="$CPPFLAGS_SAVED" +LDFLAGS="$LDFLAGS_SAVES" +LIBS="$LIBS_SAVES" + +AC_SUBST(BIND10_LDFLAGS) +AC_SUBST(BIND10_LDADD) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL + +# Checks for library functions. +AC_CHECK_FUNCS([gettimeofday memset socket]) + +AC_CONFIG_FILES([Makefile + host/Makefile]) + +AC_OUTPUT From 83cdd8bb5d8cb846bfc85e65b33fc57fa56939b7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 28 Sep 2012 16:37:47 -0700 Subject: [PATCH 125/194] [1870] moved src/bin/host under example/ (at least for now) there's no way to transform xml doc to other formats. only xml is provided for reference. --- {src/bin => examples}/host/.gitignore | 0 examples/host/Makefile.am | 6 ++++ {src/bin => examples}/host/README | 0 {src/bin => examples}/host/b10-host.xml | 0 {src/bin => examples}/host/host.cc | 0 src/bin/Makefile.am | 2 +- src/bin/host/Makefile.am | 37 ------------------------- 7 files changed, 7 insertions(+), 38 deletions(-) rename {src/bin => examples}/host/.gitignore (100%) create mode 100644 examples/host/Makefile.am rename {src/bin => examples}/host/README (100%) rename {src/bin => examples}/host/b10-host.xml (100%) rename {src/bin => examples}/host/host.cc (100%) delete mode 100644 src/bin/host/Makefile.am diff --git a/src/bin/host/.gitignore b/examples/host/.gitignore similarity index 100% rename from src/bin/host/.gitignore rename to examples/host/.gitignore diff --git a/examples/host/Makefile.am b/examples/host/Makefile.am new file mode 100644 index 0000000000..79d6f3542f --- /dev/null +++ b/examples/host/Makefile.am @@ -0,0 +1,6 @@ +AM_CPPFLAGS = $(BOOST_INCLUDES) $(BIND10_INCLUDES) + +bin_PROGRAMS = b10-host +b10_host_SOURCES = host.cc +b10_host_LDFLAGS = ${BIND10_LDFLAGS} +b10_host_LDADD = ${BIND10_LDADD} diff --git a/src/bin/host/README b/examples/host/README similarity index 100% rename from src/bin/host/README rename to examples/host/README diff --git a/src/bin/host/b10-host.xml b/examples/host/b10-host.xml similarity index 100% rename from src/bin/host/b10-host.xml rename to examples/host/b10-host.xml diff --git a/src/bin/host/host.cc b/examples/host/host.cc similarity index 100% rename from src/bin/host/host.cc rename to examples/host/host.cc diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 7af44f9b8f..0b4c1ae796 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \ +SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq cmdctl auth xfrin \ xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 \ dbutil sysinfo diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am deleted file mode 100644 index 42ef954ce1..0000000000 --- a/src/bin/host/Makefile.am +++ /dev/null @@ -1,37 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib -AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns -AM_CPPFLAGS += $(BOOST_INCLUDES) - -AM_CXXFLAGS = $(B10_CXXFLAGS) - -if USE_STATIC_LINK -AM_LDFLAGS = -static -endif - -CLEANFILES = *.gcno *.gcda - -bin_PROGRAMS = b10-host -b10_host_SOURCES = host.cc -b10_host_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la -b10_host_LDADD += $(top_builddir)/src/lib/util/libb10-util.la -b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la - -man_MANS = b10-host.1 -DISTCLEANFILES = $(man_MANS) -EXTRA_DIST = $(man_MANS) b10-host.xml - -.PHONY: man -if GENERATE_DOCS - -man: b10-host.1 - -b10-host.1: b10-host.xml - @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-host.xml - -else - -$(man_MANS): - @echo Man generation disabled. Creating dummy $@. Configure with --enable-generate-docs to enable it. - @echo Man generation disabled. Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@ - -endif From ecb43490182626c4e62cded9d02ac9a94feb4cfa Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 28 Sep 2012 16:39:38 -0700 Subject: [PATCH 126/194] [1870] make sure install some more headers under dns/ and util/ there may be more, but at least this makes (b10-)host compile as a 3rd party app. --- src/lib/dns/Makefile.am | 1 + src/lib/util/Makefile.am | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 38809e0921..9b03e691fa 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -149,6 +149,7 @@ libdns___include_HEADERS = \ messagerenderer.h \ name.h \ question.h \ + opcode.h \ rcode.h \ rdata.h \ rrparamregistry.h \ diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index fe1b3274a0..13f8f7b595 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -33,3 +33,6 @@ EXTRA_DIST = python/pycppwrapper_util.h libb10_util_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la CLEANFILES = *.gcno *.gcda + +libb10_util_includedir = $(includedir)/$(PACKAGE_NAME)/util +libb10_util_include_HEADERS = buffer.h From 0edff937af7a4fcb6565d2b7ad14e0484b3905b1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 28 Sep 2012 21:24:11 -0700 Subject: [PATCH 127/194] [1870] introduced separate m4 files for dependencies. --- examples/Makefile.am | 3 + examples/configure.ac | 71 +---------------------- examples/host/Makefile.am | 4 +- examples/m4/ax_boost_include.m4 | 64 ++++++++++++++++++++ examples/m4/ax_isc_bind10.m4 | 100 ++++++++++++++++++++++++++++++++ 5 files changed, 171 insertions(+), 71 deletions(-) create mode 100644 examples/m4/ax_boost_include.m4 create mode 100644 examples/m4/ax_isc_bind10.m4 diff --git a/examples/Makefile.am b/examples/Makefile.am index 6048e41969..ef437ebf31 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1 +1,4 @@ SUBDIRS = host + +# Make sure macros under m4 will be included +ACLOCAL_AMFLAGS = -I m4 diff --git a/examples/configure.ac b/examples/configure.ac index c4a7e2e8a1..ec24bb5756 100644 --- a/examples/configure.ac +++ b/examples/configure.ac @@ -11,79 +11,12 @@ AC_CONFIG_HEADERS([config.h]) AC_PROG_CXX AC_LANG([C++]) -# Checks for libraries. - -# Check for BIND10 libdns++ headers -AC_ARG_WITH(bind10-include, - AC_HELP_STRING([--with-bind10-include=PATH], - [specify a path to BIND 10 header files - (PATH, often needs to be in a BIND 10 source such as - /bind10/src/lib)]), - bind10_inc_path="$withval", bind10_inc_path="no") -# If not specified, try some common paths. -if test "$bind10_inc_path" = "no"; then - bind10dirs="/usr/local /usr/pkg /opt /opt/local" - for d in $bind10dirs - do - if test -f $d/dns/rrtype.h; then - bind10_inc_path=$d - break - fi - done -fi -CPPFLAGS_SAVES="$CPPFLAGS" -if test "${bind10_inc_path}" ; then - BIND10_INCLUDES="-I${bind10_inc_path}" - CPPFLAGS="$CPPFLAGS $BIND10_INCLUDES" -fi -AC_CHECK_HEADERS([dns/rrtype.h],, - AC_MSG_ERROR([Missing required BIND 10 header files.])) -CPPFLAGS="$CPPFLAGS_SAVES" -AC_SUBST(BIND10_INCLUDES) - -# Check for BIND10 lib libraries -AC_ARG_WITH(bind10-lib, - AC_HELP_STRING([--with-bind10-lib=PATH], - [specify a path to BIND 10 library files (PATH)]), - bind10_lib_path="$withval", bind10_lib_path="no") - -if test bind10_lib_path != "no"; then - BIND10_LDFLAGS="-L$bind10_lib_path" -fi -BIND10_LDADD="-lb10-dns++ -lb10-util -lb10-exceptions" - -CPPFLAGS_SAVED="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BIND10_INCLUDES" -LDFLAGS_SAVED="$LDFLAGS" -LDFLAGS="$LDFLAGS $BIND10_LDFLAGS" -LIBS_SAVED=$LIBS -LIBS="$LIBS $BIND10_LDADD" - -AC_MSG_CHECKING([BIND 10 libraries]) -AC_TRY_LINK([ -#include -],[ -isc::dns::RRType rrtype(1); -], -[ AC_MSG_RESULT(yes)], -[ AC_MSG_RESULT(no) - AC_MSG_ERROR(unable to find required BIND 10 libraries)]) - -CPPFLAGS="$CPPFLAGS_SAVED" -LDFLAGS="$LDFLAGS_SAVES" -LIBS="$LIBS_SAVES" - -AC_SUBST(BIND10_LDFLAGS) -AC_SUBST(BIND10_LDADD) - -# Checks for header files. +# Checks for BIND 10 headers and libraries +AX_ISC_BIND10 # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL -# Checks for library functions. -AC_CHECK_FUNCS([gettimeofday memset socket]) - AC_CONFIG_FILES([Makefile host/Makefile]) diff --git a/examples/host/Makefile.am b/examples/host/Makefile.am index 79d6f3542f..dbd57a2821 100644 --- a/examples/host/Makefile.am +++ b/examples/host/Makefile.am @@ -1,6 +1,6 @@ -AM_CPPFLAGS = $(BOOST_INCLUDES) $(BIND10_INCLUDES) +AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(BIND10_CPPFLAGS) bin_PROGRAMS = b10-host b10_host_SOURCES = host.cc b10_host_LDFLAGS = ${BIND10_LDFLAGS} -b10_host_LDADD = ${BIND10_LDADD} +b10_host_LDADD = ${BIND10_DNS_LIB} diff --git a/examples/m4/ax_boost_include.m4 b/examples/m4/ax_boost_include.m4 new file mode 100644 index 0000000000..e41614d884 --- /dev/null +++ b/examples/m4/ax_boost_include.m4 @@ -0,0 +1,64 @@ +dnl @synopsis AX_BOOST_INCLUDE +dnl +dnl Test for the Boost C++ header files +dnl +dnl If no path to the installed boost header files is given via the +dnl --with-boost-include option, the macro searchs under +dnl /usr/local /usr/pkg /opt /opt/local directories. +dnl +dnl This macro calls: +dnl +dnl AC_SUBST(BOOST_CPPFLAGS) +dnl + +AC_DEFUN([AX_BOOST_INCLUDE], [ +AC_LANG_SAVE +AC_LANG([C++]) + +# +# Configure Boost header path +# +# If explicitly specified, use it. +AC_ARG_WITH([boost-include], + AS_HELP_STRING([--with-boost-include=PATH], + [specify exact directory for Boost headers]), + [boost_include_path="$withval"]) +# If not specified, try some common paths. +if test -z "$with_boost_include"; then + boostdirs="/usr/local /usr/pkg /opt /opt/local" + for d in $boostdirs + do + if test -f $d/include/boost/shared_ptr.hpp; then + boost_include_path=$d/include + break + fi + done +fi +CPPFLAGS_SAVES="$CPPFLAGS" +if test "${boost_include_path}" ; then + BOOST_CPPFLAGS="-I${boost_include_path}" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +fi +# Make sure some commonly used headers are available +AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/bind.hpp boost/function.hpp],, + AC_MSG_ERROR([Missing required Boost header files.])) + +# Detect whether Boost tries to use threads by default, and, if not, +# make it sure explicitly. In some systems the automatic detection +# may depend on preceding header files, and if inconsistency happens +# it could lead to a critical disruption. +AC_MSG_CHECKING([whether Boost tries to use threads]) +AC_TRY_COMPILE([ +#include +#ifdef BOOST_HAS_THREADS +#error "boost will use threads" +#endif],, +[AC_MSG_RESULT(no) + CPPFLAGS_BOOST_THREADCONF="-DBOOST_DISABLE_THREADS=1"], +[AC_MSG_RESULT(yes)]) + +CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF" +AC_SUBST(BOOST_CPPFLAGS) + +AC_LANG_RESTORE +])dnl AX_BOOST_INCLUDE diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4 new file mode 100644 index 0000000000..4246384081 --- /dev/null +++ b/examples/m4/ax_isc_bind10.m4 @@ -0,0 +1,100 @@ +dnl @synopsis AX_BIND10 +dnl +dnl @summary figure out how to build C++ programs using ISC BIND 10 libraries +dnl +dnl If no path to the installed BIND 10 header files or libraries is given +dnl via the --with-bind10-include or --with-bind10-lib option, the macro +dnl searchs under /usr/local/{include, lib}, /usr/pkg/{include, lib}, +dnl /opt/{include, lib}, /opt/local/{include, lib} directories, respectively. +dnl +dnl This macro calls: +dnl +dnl AC_SUBST(BIND10_CPPFLAGS) +dnl AC_SUBST(BIND10_LDFLAGS) +dnl AC_SUBST(BIND10_DNS_LIB) +dnl + + +AC_DEFUN([AX_ISC_BIND10], [ +AC_REQUIRE([AX_BOOST_INCLUDE]) +AC_LANG_SAVE +AC_LANG([C++]) + +AX_BOOST_INCLUDE + +# Check for BIND10 libdns++ headers + +AC_ARG_WITH(bind10-include, + AS_HELP_STRING([--with-bind10-include=PATH], + [specify a path to BIND 10 header files]), + bind10_inc_path="$withval", bind10_inc_path="no") +# If not specified, try some common paths. +if test "$bind10_inc_path" = "no"; then + for d in /usr/local /usr/pkg /opt /opt/local + do + if test -f $d/include/dns/rrtype.h; then + bind10_inc_path=$d + break + fi + done +fi +CPPFLAGS_SAVES="$CPPFLAGS" +if test "${bind10_inc_path}" ; then + BIND10_CPPFLAGS="-I${bind10_inc_path}" + CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS" +fi +AC_CHECK_HEADERS([dns/rrtype.h],, + AC_MSG_ERROR([Missing required BIND 10 header files.])) +CPPFLAGS="$CPPFLAGS_SAVES" +AC_SUBST(BIND10_CPPFLAGS) + +# Check for BIND10 libraries +CPPFLAGS_SAVED="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS" + +AC_ARG_WITH(bind10-lib, + AS_HELP_STRING([--with-bind10-lib=PATH], + [specify a path to BIND 10 library files]), + bind10_lib_path="$withval", bind10_lib_path="no") +if test $bind10_lib_path != "no"; then + bind10_lib_dirs=$bind10_lib_path +else + # If not specified, try some common paths. + bind10_lib_dirs="/usr/local/lib /usr/pkg/lib /opt/lib /opt/local/lib" +fi + +# make sure we have buildable libraries +AC_MSG_CHECKING([BIND 10 libraries]) +BIND10_DNS_LIB="-lb10-dns++ -lb10-util -lb10-exceptions" +LDFLAGS="$LDFLAGS $BIND10_LDFLAGS" +LIBS="$LIBS $BIND10_DNS_LIB" +for d in $bind10_lib_dirs +do + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS -L$d" + AC_TRY_LINK([ +#include +],[ +isc::dns::RRType rrtype(1); +], [BIND10_LDFLAGS="-L${d}"]) + if test "x$BIND10_LDFLAGS" != "x"; then + break + fi + LDFLAGS="$LDFLAGS_SAVED" +done +if test "x$BIND10_LDFLAGS" != "x"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) + AC_MSG_ERROR(unable to find required BIND 10 libraries) +fi + +CPPFLAGS="$CPPFLAGS_SAVED" +LDFLAGS="$LDFLAGS_SAVES" +LIBS="$LIBS_SAVES" + +AC_SUBST(BIND10_LDFLAGS) +AC_SUBST(BIND10_DNS_LIB) + +AC_LANG_RESTORE +])dnl AX_ISC_BIND10 From 0dca4820d8473e8e1db07b49ef54d52d250e9209 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Oct 2012 15:12:15 -0700 Subject: [PATCH 128/194] [1870] removed src/bin/host/Makefile from toplevel configure.ac. --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 422681602c..6dc49f3a4f 100644 --- a/configure.ac +++ b/configure.ac @@ -1111,7 +1111,6 @@ AC_CONFIG_FILES([Makefile src/bin/dbutil/Makefile src/bin/dbutil/tests/Makefile src/bin/dbutil/tests/testdata/Makefile - src/bin/host/Makefile src/bin/loadzone/Makefile src/bin/loadzone/tests/correct/Makefile src/bin/loadzone/tests/error/Makefile From cdbc11cf13f6cc69498b56a23c61748ed67f3631 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Oct 2012 15:12:52 -0700 Subject: [PATCH 129/194] [1870] separated BIND 10 common libs and DNS lib so that we can eventually selectively choose module specific libraries. --- examples/configure.ac | 5 ++++ examples/m4/ax_isc_bind10.m4 | 50 ++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/examples/configure.ac b/examples/configure.ac index ec24bb5756..937968760d 100644 --- a/examples/configure.ac +++ b/examples/configure.ac @@ -14,6 +14,11 @@ AC_LANG([C++]) # Checks for BIND 10 headers and libraries AX_ISC_BIND10 +# For the example host program, we require the BIND 10 DNS library +if test "x$BIND10_DNS_LIB" = "x"; then + AC_MSG_ERROR([unable to find BIND 10 DNS library needed to build 'host']) +fi + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4 index 4246384081..ab18aecf34 100644 --- a/examples/m4/ax_isc_bind10.m4 +++ b/examples/m4/ax_isc_bind10.m4 @@ -11,9 +11,15 @@ dnl This macro calls: dnl dnl AC_SUBST(BIND10_CPPFLAGS) dnl AC_SUBST(BIND10_LDFLAGS) +dnl AC_SUBST(BIND10_COMMON_LIB) dnl AC_SUBST(BIND10_DNS_LIB) dnl - +dnl If this macro finds CPPFLAGS, LDFLAGS or COMMON_LIB unavailable, it treats +dnl that as a fatal error. +dnl Checks for other BIND 10 module libraries are option, as not all +dnl applications need all libraries. The main configure.ac can handle any +dnl missing library as fatal by checking whether the corresponding +dnl BIND10_xxx_LIB is defined. AC_DEFUN([AX_ISC_BIND10], [ AC_REQUIRE([AX_BOOST_INCLUDE]) @@ -22,7 +28,7 @@ AC_LANG([C++]) AX_BOOST_INCLUDE -# Check for BIND10 libdns++ headers +# Check for BIND10 common headers AC_ARG_WITH(bind10-include, AS_HELP_STRING([--with-bind10-include=PATH], @@ -32,7 +38,7 @@ AC_ARG_WITH(bind10-include, if test "$bind10_inc_path" = "no"; then for d in /usr/local /usr/pkg /opt /opt/local do - if test -f $d/include/dns/rrtype.h; then + if test -f $d/include/util/buffer.h; then bind10_inc_path=$d break fi @@ -43,8 +49,8 @@ if test "${bind10_inc_path}" ; then BIND10_CPPFLAGS="-I${bind10_inc_path}" CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS" fi -AC_CHECK_HEADERS([dns/rrtype.h],, - AC_MSG_ERROR([Missing required BIND 10 header files.])) +AC_CHECK_HEADERS([util/buffer.h],, + AC_MSG_ERROR([Missing a commonly used BIND 10 header files])) CPPFLAGS="$CPPFLAGS_SAVES" AC_SUBST(BIND10_CPPFLAGS) @@ -64,18 +70,18 @@ else fi # make sure we have buildable libraries -AC_MSG_CHECKING([BIND 10 libraries]) -BIND10_DNS_LIB="-lb10-dns++ -lb10-util -lb10-exceptions" +AC_MSG_CHECKING([for BIND 10 common library]) +BIND10_COMMON_LIB="-lb10-util -lb10-exceptions" LDFLAGS="$LDFLAGS $BIND10_LDFLAGS" -LIBS="$LIBS $BIND10_DNS_LIB" +LIBS="$LIBS $BIND10_COMMON_LIB" for d in $bind10_lib_dirs do LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS -L$d" AC_TRY_LINK([ -#include +#include ],[ -isc::dns::RRType rrtype(1); +isc::util::OutputBuffer buffer(0); ], [BIND10_LDFLAGS="-L${d}"]) if test "x$BIND10_LDFLAGS" != "x"; then break @@ -86,15 +92,33 @@ if test "x$BIND10_LDFLAGS" != "x"; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) - AC_MSG_ERROR(unable to find required BIND 10 libraries) + AC_MSG_ERROR([unable to find required BIND 10 libraries]) fi -CPPFLAGS="$CPPFLAGS_SAVED" -LDFLAGS="$LDFLAGS_SAVES" +# restore LIBS once at this point LIBS="$LIBS_SAVES" AC_SUBST(BIND10_LDFLAGS) +AC_SUBST(BIND10_COMMON_LIB) + +# Check per-module BIND 10 libraries + +# DNS library +AC_MSG_CHECKING([for BIND 10 DNS library]) +LIBS="$LIBS $BIND10_COMMON_LIB -lb10-dns++" +AC_TRY_LINK([ +#include +],[ +isc::dns::RRType rrtype(1); +], [BIND10_DNS_LIB="-lb10-dns++" + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no)]) +LIBS="$LIBS_SAVES" AC_SUBST(BIND10_DNS_LIB) +# Restore other flags +CPPFLAGS="$CPPFLAGS_SAVED" +LDFLAGS="$LDFLAGS_SAVES" + AC_LANG_RESTORE ])dnl AX_ISC_BIND10 From 4e5ca3a84b4c02ffef3bf8b6c6360b05c8600a4e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Oct 2012 15:26:25 -0700 Subject: [PATCH 130/194] [1870] provided top level README --- examples/README | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/examples/README b/examples/README index e69de29bb2..12dbbbaaff 100644 --- a/examples/README +++ b/examples/README @@ -0,0 +1,32 @@ +This is the top directory for sample programs that can be developed +using public BIND 10 libraries outside of the BIND 10 project. It's +intended to be built with installed BIND 10 header files and library +objects, so it's not a target of the main build tree, and does not +refer to any other part of the BIND 10 source tree that contains +this directory. + +On the top (sub) directory (where this README file is stored), we +provide a sample configure.ac and Makefile.am files for GNU automake +environments with helper autoconf macros to detect the available and +location of BIND 10 header files and library objects. + +You can use the configure.ac and Makefile.am files with macros under +the "m4" subdirectory as a template for your own project. The key is +to call the AX_ISC_BIND10 function (as the sample configure.ac does) +from your configure.ac. Then it will check the availability of +necessary stuff and set some corresponding AC variables. You can then +use the resulting variables in your Makefile.in or Makefile.ac. + +If you use automake, don't forget adding the following line to the top +level Makefile.am: + +ACLOCAL_AMFLAGS = -I m4 + +This is necessary to incorporate the helper macro definitions. + +If you don't use automake but autoconf, make sure to add the following +to the configure.ac file: + +sinclude(m4/ax_boost_include.m4) +sinclude(m4/ax_isc_bind10.m4) +(and same for other m4 files as they are added under m4/) From 4686e5da4ecc7801a232a1858bf11f124aca3ee7 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Oct 2012 15:31:27 -0700 Subject: [PATCH 131/194] [1870] some more doc cleanups --- examples/COPYING | 687 +---------------------------------------------- examples/INSTALL | 370 +------------------------ 2 files changed, 20 insertions(+), 1037 deletions(-) diff --git a/examples/COPYING b/examples/COPYING index 94a9ed024d..f3febbe839 100644 --- a/examples/COPYING +++ b/examples/COPYING @@ -1,674 +1,13 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +Copyright (C) 2012 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. diff --git a/examples/INSTALL b/examples/INSTALL index 7d1c323bea..43436c2eef 100644 --- a/examples/INSTALL +++ b/examples/INSTALL @@ -1,365 +1,9 @@ -Installation Instructions -************************* +If using git (not the tarball), build the "configure" file: + autoreconf --install -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. +To then build from source: + ./configure + make +You may have to specify the location of BIND 10 header files and +library objects. See configure options by ./configure --help. From 666c34d9e2c70936c264bd0432f874a95af42a1a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 1 Oct 2012 21:13:02 -0700 Subject: [PATCH 132/194] [1870] removed rrsetlist.h from the libdns___include_HEADERS it will be cleaned up and should better be excluded from the publicly visible list. --- src/lib/dns/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 9b03e691fa..40cb943412 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -154,7 +154,6 @@ libdns___include_HEADERS = \ rdata.h \ rrparamregistry.h \ rrset.h \ - rrsetlist.h \ rrttl.h \ tsigkey.h # Purposely not installing these headers: From fe0e9a269f318aae152fb4b55437b45609bfd6cc Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 12:21:40 -0700 Subject: [PATCH 133/194] [1870] removed unnecessary call to AX_BOOST_INCLUDE. since AC_REQUIRE internally does it. --- examples/m4/ax_isc_bind10.m4 | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4 index ab18aecf34..8bf1c35c77 100644 --- a/examples/m4/ax_isc_bind10.m4 +++ b/examples/m4/ax_isc_bind10.m4 @@ -26,8 +26,6 @@ AC_REQUIRE([AX_BOOST_INCLUDE]) AC_LANG_SAVE AC_LANG([C++]) -AX_BOOST_INCLUDE - # Check for BIND10 common headers AC_ARG_WITH(bind10-include, From b675d21e2e7b1864b32824c97e180abf47c1f42e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 3 Oct 2012 14:12:39 -0700 Subject: [PATCH 134/194] [1870] avoid using "no" keyword as a configured value for "bind10-include". --- examples/m4/ax_isc_bind10.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4 index 8bf1c35c77..63e028c407 100644 --- a/examples/m4/ax_isc_bind10.m4 +++ b/examples/m4/ax_isc_bind10.m4 @@ -43,7 +43,7 @@ if test "$bind10_inc_path" = "no"; then done fi CPPFLAGS_SAVES="$CPPFLAGS" -if test "${bind10_inc_path}" ; then +if test "${bind10_inc_path}" != "no"; then BIND10_CPPFLAGS="-I${bind10_inc_path}" CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS" fi From dd752b34220a5b6a07a4d2c057169c6b88394c32 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 14:11:39 -0700 Subject: [PATCH 135/194] [master] added changelog for #1870 --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index bad54da074..b5c2cf4cbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +486. [bug]* jinmei + All public header files for libb10-dns++ are now installed. + Template configure.ac and utility AC macros for external projects + using the library is provided under the "examples" directory. + The src/bin/host was moved as part of the examples (and not + installed with other BIND 10 programs any more). + (Trac #1870, git 4973e638d354d8b56dcadf71123ef23c15662021) + 485. [bug] jelte Several bugs have been fixed in bindctl; tab-completion now works within configuration lists, the problem where sometimes the From fa2026935d4738b6b5b7365d2fab53f9dea09cc4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 14:12:02 -0700 Subject: [PATCH 136/194] [master] define dbutil_test.sh as noinst_SCRIPTS to auto-regenerate on modify. this will prevent surprising test failure on an incrementally updated dev tree. okayed on jabber. --- src/bin/dbutil/tests/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bin/dbutil/tests/Makefile.am b/src/bin/dbutil/tests/Makefile.am index b4231b3fea..aaa57ccd88 100644 --- a/src/bin/dbutil/tests/Makefile.am +++ b/src/bin/dbutil/tests/Makefile.am @@ -2,6 +2,8 @@ SUBDIRS = . testdata # Tests of the update script. +noinst_SCRIPTS = dbutil_test.sh + check-local: B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \ $(SHELL) $(abs_builddir)/dbutil_test.sh From b2eedd0aebf2739acfb5e83363667fd2c1101f7d Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Tue, 9 Oct 2012 10:21:48 +0530 Subject: [PATCH 137/194] [master] Remove empty GNU-style dist files --- NEWS | 0 TODO | 0 configure.ac | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 NEWS delete mode 100644 TODO diff --git a/NEWS b/NEWS deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/TODO b/TODO deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/configure.ac b/configure.ac index 6dc49f3a4f..4799b7d60c 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_PREREQ([2.59]) AC_INIT(bind10-devel, 20120817, bind10-dev@isc.org) AC_CONFIG_SRCDIR(README) -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([foreign]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4macros]) From a25609c439090aeb21c01b487a2070b4df8d435b Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Tue, 9 Oct 2012 10:35:21 +0530 Subject: [PATCH 138/194] [master] Update .gitignore files --- src/lib/dhcp/tests/.gitignore | 1 + src/lib/util/threads/tests/.gitignore | 1 + tests/tools/perfdhcp/templates/.gitignore | 5 +++++ 3 files changed, 7 insertions(+) create mode 100644 src/lib/util/threads/tests/.gitignore create mode 100644 tests/tools/perfdhcp/templates/.gitignore diff --git a/src/lib/dhcp/tests/.gitignore b/src/lib/dhcp/tests/.gitignore index 313429df03..89ea50522c 100644 --- a/src/lib/dhcp/tests/.gitignore +++ b/src/lib/dhcp/tests/.gitignore @@ -1 +1,2 @@ /libdhcp++_unittests +/libdhcpsrv_unittests diff --git a/src/lib/util/threads/tests/.gitignore b/src/lib/util/threads/tests/.gitignore new file mode 100644 index 0000000000..d6d1ec87a1 --- /dev/null +++ b/src/lib/util/threads/tests/.gitignore @@ -0,0 +1 @@ +/run_unittests diff --git a/tests/tools/perfdhcp/templates/.gitignore b/tests/tools/perfdhcp/templates/.gitignore new file mode 100644 index 0000000000..6f865da85b --- /dev/null +++ b/tests/tools/perfdhcp/templates/.gitignore @@ -0,0 +1,5 @@ +/test1.hex +/test2.hex +/test3.hex +/test4.hex +/test5.hex From a841ad0aec2fa5d0966dc43b3036ff99b5f3ae42 Mon Sep 17 00:00:00 2001 From: Mukund Sivaraman Date: Tue, 9 Oct 2012 10:41:32 +0530 Subject: [PATCH 139/194] [master] Set lockfile path for DHCP tests --- src/bin/dhcp4/tests/Makefile.am | 1 + src/bin/dhcp6/tests/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index 906b5d1e59..ddc3000f67 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -16,6 +16,7 @@ check-local: for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ + B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \ $(LIBRARY_PATH_PLACEHOLDER) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 1d9308fcaa..de87582889 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -15,6 +15,7 @@ check-local: for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ $(LIBRARY_PATH_PLACEHOLDER) \ + B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done From 40083686017562d13d812240499a8090383bfa11 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 2 Oct 2012 20:22:23 +0200 Subject: [PATCH 140/194] [2292] Parametrize constness of the chain --- src/lib/datasrc/memory/domaintree.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 20f46936e7..4f1d94aca7 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -684,7 +684,7 @@ DomainTreeNode::predecessor() const { /// DomainTree. /// This is the reason why manipulation methods such as \c push() and \c pop() /// are private (and not shown in the doxygen document). -template +template > class DomainTreeNodeChain { /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has /// knowledge to manipulate it. @@ -817,7 +817,7 @@ private: /// root node of DomainTree /// /// \exception None - const DomainTreeNode* top() const { + NodeType* top() const { assert(!isEmpty()); return (nodes_[level_count_ - 1]); } @@ -840,7 +840,7 @@ private: /// otherwise the node should be the root node of DomainTree. /// /// \exception None - void push(const DomainTreeNode* node) { + void push(NodeType* node) { assert(level_count_ < RBT_MAX_LEVEL); nodes_[level_count_++] = node; } @@ -852,7 +852,7 @@ private: const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS; size_t level_count_; - const DomainTreeNode* nodes_[RBT_MAX_LEVEL]; + NodeType* nodes_[RBT_MAX_LEVEL]; const DomainTreeNode* last_compared_; isc::dns::NameComparisonResult last_comparison_; }; From bb347f194134acc2a3d1e7f2c6d6b4f0d56d2a17 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 2 Oct 2012 21:05:33 +0200 Subject: [PATCH 141/194] [2292] Get rid of the const_cast It was needed when extracting data from a domain tree chain. The chain now can hold mutable pointers too, so we use that (and some amount of template bureaucracy) to avoid the cast. While the interface changed (on the core find function, it is not possible to pass const node chain and have a mutable node get out), it doesn't seem to influence the current code. Also, it is a private interface anyway, so it should be safe. --- src/lib/datasrc/memory/domaintree.h | 46 ++++++++++++++++----------- src/lib/datasrc/memory/zone_finder.cc | 2 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 4f1d94aca7..e54d8eaaed 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1085,9 +1085,10 @@ public: /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, DomainTreeNode** node) const { - DomainTreeNodeChain node_path; + DomainTreeNodeChain > node_path; const isc::dns::LabelSequence ls(name); - return (find(ls, node, node_path, NULL, NULL)); + return (find >(ls, node, node_path, NULL, + NULL)); } /// \brief Simple find returning immutable node. @@ -1097,9 +1098,11 @@ public: Result find(const isc::dns::Name& name, const DomainTreeNode** node) const { DomainTreeNodeChain node_path; - DomainTreeNode *target_node = NULL; + const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find(ls, &target_node, node_path, NULL, NULL)); + Result ret = (find >(ls, &target_node, + node_path, NULL, + NULL)); if (ret != NOTFOUND) { *node = target_node; } @@ -1113,7 +1116,8 @@ public: DomainTreeNodeChain& node_path) const { const isc::dns::LabelSequence ls(name); - return (find(ls, node, node_path, NULL, NULL)); + return (find >(ls, node, node_path, + NULL, NULL)); } /// \brief Simple find returning immutable node, with node_path tracking @@ -1123,9 +1127,11 @@ public: Result find(const isc::dns::Name& name, const DomainTreeNode** node, DomainTreeNodeChain& node_path) const { - DomainTreeNode *target_node = NULL; + const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find(ls, &target_node, node_path, NULL, NULL)); + Result ret = (find >(ls, &target_node, + node_path, NULL, + NULL)); if (ret != NOTFOUND) { *node = target_node; } @@ -1143,7 +1149,7 @@ public: bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { - DomainTreeNode* target_node = NULL; + const DomainTreeNode* target_node = NULL; const isc::dns::LabelSequence ls(name); Result ret = find(ls, &target_node, node_path, callback, callback_arg); @@ -1227,10 +1233,10 @@ public: /// /// \return As in the description, but in case of callback returning /// \c true, it returns immediately with the current node. - template + template Result find(const isc::dns::LabelSequence& target_labels_orig, - DomainTreeNode** node, - DomainTreeNodeChain& node_path, + NodeType** node, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const; @@ -1245,9 +1251,11 @@ public: bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { - DomainTreeNode* target_node = NULL; - Result ret = find(target_labels, &target_node, node_path, - callback, callback_arg); + const DomainTreeNode* target_node = NULL; + Result ret = find >(target_labels, + &target_node, + node_path, callback, + callback_arg); if (ret != NOTFOUND) { *node = target_node; } @@ -1512,11 +1520,11 @@ DomainTree::deleteHelper(util::MemorySegment& mem_sgmt, } template -template +template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - DomainTreeNode** target, - DomainTreeNodeChain& node_path, + NodeType** target, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { @@ -1526,11 +1534,11 @@ DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, " and label sequence"); } - DomainTreeNode* node; + NodeType* node; if (!node_path.isEmpty()) { // Get the top node in the node chain - node = const_cast*>(node_path.top()); + node = node_path.top(); // Start searching from its down pointer node = node->getDown(); } else { diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc index 86dd87428c..11188a038b 100644 --- a/src/lib/datasrc/memory/zone_finder.cc +++ b/src/lib/datasrc/memory/zone_finder.cc @@ -428,7 +428,7 @@ FindNodeResult findNode(const ZoneData& zone_data, ZoneFinder::FindOptions options, bool out_of_zone_ok = false) { - ZoneNode* node = NULL; + const ZoneNode* node = NULL; FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0); const ZoneTree& tree(zone_data.getZoneTree()); From 0a23277c5bcbd35467652610f7feb2b7f7c65a0b Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:13:56 +0200 Subject: [PATCH 142/194] [2292] Remove the setZoneData method Currently, the content of zone is passed to the addZone method. No way to change it later, but the addZone overwrites the old value if called again. This should hopefully allow for removal of mutable find methods from the tree. --- src/lib/datasrc/memory/memory_client.cc | 15 ++--- src/lib/datasrc/memory/zone_table.cc | 41 +++--------- src/lib/datasrc/memory/zone_table.h | 54 ++++++--------- .../tests/memory/zone_table_unittest.cc | 67 +++++++++++++------ 4 files changed, 80 insertions(+), 97 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5f6f510306..5eaf58b547 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -630,22 +630,15 @@ InMemoryClient::InMemoryClientImpl::load( std::string* tstr = node->setData(new std::string(filename)); delete tstr; - ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name)); - if (result.code == result::SUCCESS) { + result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, holder)); + if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. ++zone_count_; } - ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name, - holder.release())); - assert(fr.code == result::SUCCESS); - if (fr.zone_data != NULL) { - ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_); - } - - return (result.code); + return (result); } namespace { diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index a74a61dd27..68c73ad712 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -67,19 +67,11 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, mem_sgmt.deallocate(ztable, sizeof(ZoneTable)); } -ZoneTable::AddResult +result::Result ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, - const Name& zone_name) + const Name& zone_name, + SegmentObjectHolder& content) { - // Create a new ZoneData instance first. If the specified name already - // exists in the table, the new data will soon be destroyed, but we want - // to make sure if this allocation fails the tree won't be changed to - // provide as strong guarantee as possible. In practice, we generally - // expect the caller tries to add a zone only when it's a new one, so - // this should be a minor concern. - SegmentObjectHolder holder( - mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class); - // Get the node where we put the zone ZoneTableNode* node(NULL); switch (zones_->insert(mem_sgmt, zone_name, &node)) { @@ -94,12 +86,13 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, // Can Not Happen assert(node != NULL); - // Is it empty? We either just created it or it might be nonterminal - if (node->isEmpty()) { - node->setData(holder.get()); - return (AddResult(result::SUCCESS, holder.release())); - } else { // There's something there already - return (AddResult(result::EXIST, node->getData())); + // We can release now, setData never throws + ZoneData* old = node->setData(content.release()); + if (old != NULL) { + ZoneData::destroy(mem_sgmt, old, zone_class); + return (result::EXIST); + } else { + return (result::SUCCESS); } } @@ -132,20 +125,6 @@ ZoneTable::findZone(const Name& name) const { return (FindResult(my_result, node->getData())); } -ZoneTable::FindResult -ZoneTable::setZoneData(const Name& name, ZoneData* data) -{ - ZoneTableNode* node(NULL); - - ZoneTableTree::Result result(zones_->find(name, &node)); - - if (result != ZoneTableTree::EXACTMATCH) { - return (FindResult(result::NOTFOUND, NULL)); - } else { - return (FindResult(result::SUCCESS, node->setData(data))); - } -} - } // end of namespace memory } // end of namespace datasrc } // end of namespace isc diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 8ad6213724..aebd4d0d0f 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -36,6 +36,10 @@ namespace memory { // forward declaration: in this header it's mostly an opaque type. class ZoneData; +namespace detail { +template class SegmentObjectHolder; +} + /// \brief A conceptual table of authoritative zones. /// /// This class is actually a simple wrapper for a \c DomainTree whose data is @@ -74,14 +78,6 @@ private: typedef DomainTreeNode ZoneTableNode; public: - /// \brief Result data of addZone() method. - struct AddResult { - AddResult(result::Result param_code, ZoneData* param_zone_data) : - code(param_code), zone_data(param_zone_data) - {} - const result::Result code; - ZoneData* const zone_data; - }; /// \brief Result data of findZone() method. struct FindResult { @@ -140,30 +136,28 @@ public: /// Add a new zone to the \c ZoneTable. /// - /// This method creates a new \c ZoneData for the given zone name and - /// holds it in the internal table. The newly created zone data will be - /// returned via the \c zone_data member of the return value. If the given - /// zone name already exists in the table, a new data object won't be - /// created; instead, the existing corresponding data will be returned. - /// - /// The zone table keeps the ownership of the created zone data; the - /// caller must not try to destroy it directly. (We'll eventually - /// add an interface to delete specific zone data from the table). + /// This method adds a given zone data to the internal table. /// /// \throw std::bad_alloc Internal resource allocation fails. /// /// \param mem_sgmt The \c MemorySegment to allocate zone data to be - /// created. It must be the same segment that was used to create - /// the zone table at the time of create(). + /// created. It must be the same segment that was used to create + /// the zone table at the time of create(). /// \param zone_name The name of the zone to be added. /// \param zone_class The RR class of the zone. It must be the RR class - /// that is supposed to be associated to the zone table. + /// that is supposed to be associated to the zone table. + /// \param content This one should hold the zone content (the ZoneData). + /// When it is added successfully, it is released from the holder. /// \return \c result::SUCCESS If the zone is successfully - /// added to the zone table. - /// \return \c result::EXIST The zone table already contains - /// zone of the same origin. - AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class, - const dns::Name& zone_name); + /// added to the zone table. + /// \return \c result::EXIST The zone table already contained + /// zone of the same origin. The old data is released and replaced + /// by the new one. + result::Result addZone(util::MemorySegment& mem_sgmt, + dns::RRClass zone_class, + const dns::Name& zone_name, + detail::SegmentObjectHolder& content); /// Find a zone that best matches the given name in the \c ZoneTable. /// @@ -185,16 +179,6 @@ public: /// \return A \c FindResult object enclosing the search result (see above). FindResult findZone(const isc::dns::Name& name) const; - /// Override the ZoneData for a node (zone) in the zone tree. - /// - /// \throw none - /// - /// \param name A domain name for which the zone data is set. - /// \param data The new zone data to set. - /// \return A \c FindResult object containing the old data if the - /// zone was found. - FindResult setZoneData(const isc::dns::Name& name, ZoneData* data); - private: boost::interprocess::offset_ptr zones_; }; diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 359df4923f..b9ee3d9441 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -30,6 +31,7 @@ using namespace isc::dns; using namespace isc::datasrc; using namespace isc::datasrc::memory; +using namespace isc::datasrc::memory::detail; namespace { // Memory segment specified for tests. It normally behaves like a "local" @@ -87,46 +89,69 @@ TEST_F(ZoneTableTest, create) { } TEST_F(ZoneTableTest, addZone) { + SegmentObjectHolder holder1( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); // Normal successful case. - const ZoneTable::AddResult result1 = - zone_table->addZone(mem_sgmt_, zclass_, zname1); - EXPECT_EQ(result::SUCCESS, result1.code); + EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, + zname1, holder1)); + // It got released by it + EXPECT_EQ(NULL, holder1.get()); // Duplicate add doesn't replace the existing data. + SegmentObjectHolder holder2( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1).code); - EXPECT_EQ(result1.zone_data, - zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data); + zname1, holder2)); + // It releases this one even when we replace the old zone + EXPECT_EQ(NULL, holder2.get()); + + SegmentObjectHolder holder3( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")), + zclass_); // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - Name("EXAMPLE.COM")).code); + Name("EXAMPLE.COM"), + holder3)); // Add some more different ones. Should just succeed. + SegmentObjectHolder holder4( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2).code); + zone_table->addZone(mem_sgmt_, zclass_, zname2, holder4)); + SegmentObjectHolder holder5( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3).code); + zone_table->addZone(mem_sgmt_, zclass_, zname3, holder5)); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected // in TearDown()). - mem_sgmt_.setThrowCount(2); - EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")), + SegmentObjectHolder holder6( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_); + mem_sgmt_.setThrowCount(1); + EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"), + holder6), std::bad_alloc); } TEST_F(ZoneTableTest, findZone) { - const ZoneTable::AddResult add_result1 = - zone_table->addZone(mem_sgmt_, zclass_, zname1); - EXPECT_EQ(result::SUCCESS, add_result1.code); + SegmentObjectHolder holder1( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); + ZoneData* zone_data = holder1.get(); + EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, + holder1)); + SegmentObjectHolder holder2( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2).code); + zone_table->addZone(mem_sgmt_, zclass_, zname2, holder2)); + SegmentObjectHolder holder3( + mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3).code); + zone_table->addZone(mem_sgmt_, zclass_, zname3, holder3)); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); EXPECT_EQ(result::SUCCESS, find_result1.code); - EXPECT_EQ(add_result1.zone_data, find_result1.zone_data); + EXPECT_EQ(zone_data, find_result1.zone_data); EXPECT_EQ(result::NOTFOUND, zone_table->findZone(Name("example.org")).code); @@ -137,14 +162,16 @@ TEST_F(ZoneTableTest, findZone) { // and the code should be PARTIALMATCH. EXPECT_EQ(result::PARTIALMATCH, zone_table->findZone(Name("www.example.com")).code); - EXPECT_EQ(add_result1.zone_data, + EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); // make sure the partial match is indeed the longest match by adding // a zone with a shorter origin and query again. + SegmentObjectHolder holder4( + mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - Name("com")).code); - EXPECT_EQ(add_result1.zone_data, + Name("com"), holder4)); + EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } } From a270e0425f64f0fca563b7d49708325250911b96 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:38:23 +0200 Subject: [PATCH 143/194] [2292] Remove mutable find methods They are currently used in test code only (so we'll need to update the tests to compile). The real code seams clean now. --- src/lib/datasrc/memory/domaintree.h | 47 ++----------------------- src/lib/datasrc/memory/memory_client.cc | 20 +---------- src/lib/datasrc/memory/memory_client.h | 29 --------------- src/lib/datasrc/memory/zone_table.cc | 2 +- src/lib/datasrc/memory/zone_table.h | 4 +-- 5 files changed, 6 insertions(+), 96 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index e54d8eaaed..50f5d40cf6 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1080,17 +1080,6 @@ public: /// of it. In that case, node parameter is left intact. //@{ - /// \brief Simple find. - /// - /// Acts as described in the \ref find section. - Result find(const isc::dns::Name& name, - DomainTreeNode** node) const { - DomainTreeNodeChain > node_path; - const isc::dns::LabelSequence ls(name); - return (find >(ls, node, node_path, NULL, - NULL)); - } - /// \brief Simple find returning immutable node. /// /// Acts as described in the \ref find section, but returns immutable node @@ -1109,17 +1098,6 @@ public: return (ret); } - /// \brief Simple find, with node_path tracking - /// - /// Acts as described in the \ref find section. - Result find(const isc::dns::Name& name, DomainTreeNode** node, - DomainTreeNodeChain& node_path) const - { - const isc::dns::LabelSequence ls(name); - return (find >(ls, node, node_path, - NULL, NULL)); - } - /// \brief Simple find returning immutable node, with node_path tracking /// /// Acts as described in the \ref find section, but returns immutable node @@ -1138,27 +1116,6 @@ public: return (ret); } - /// \brief Simple find returning immutable node. - /// - /// Acts as described in the \ref find section, but returns immutable - /// node pointer. - template - Result find(const isc::dns::Name& name, - const DomainTreeNode** node, - DomainTreeNodeChain& node_path, - bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const - { - const DomainTreeNode* target_node = NULL; - const isc::dns::LabelSequence ls(name); - Result ret = find(ls, &target_node, node_path, callback, - callback_arg); - if (ret != NOTFOUND) { - *node = target_node; - } - return (ret); - } - /// \brief Find with callback and node chain /// \anchor callback /// @@ -1235,7 +1192,7 @@ public: /// \c true, it returns immediately with the current node. template Result find(const isc::dns::LabelSequence& target_labels_orig, - NodeType** node, + const NodeType** node, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const; @@ -1523,7 +1480,7 @@ template template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - NodeType** target, + const NodeType** target, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index 5eaf58b547..de953ca107 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -725,7 +725,7 @@ InMemoryClient::load(const isc::dns::Name& zone_name, const std::string InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { - FileNameNode* node(NULL); + const FileNameNode* node(NULL); FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, &node); if (result == FileNameTree::EXACTMATCH) { @@ -735,24 +735,6 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { } } -result::Result -InMemoryClient::add(const isc::dns::Name& zone_name, - const ConstRRsetPtr& rrset) -{ - const ZoneTable::FindResult result = - impl_->zone_table_->findZone(zone_name); - if (result.code != result::SUCCESS) { - isc_throw(DataSourceError, "No such zone: " + zone_name.toText()); - } - - const ConstRRsetPtr sig_rrset = - rrset ? rrset->getRRsig() : ConstRRsetPtr(); - impl_->add(rrset, sig_rrset, zone_name, *result.zone_data); - - // add() doesn't allow duplicate add, so we always return SUCCESS. - return (result::SUCCESS); -} - namespace { class MemoryIterator : public ZoneIterator { diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h index 330d62e15f..c37ad53c0b 100644 --- a/src/lib/datasrc/memory/memory_client.h +++ b/src/lib/datasrc/memory/memory_client.h @@ -139,35 +139,6 @@ public: /// zone from a file before. const std::string getFileName(const isc::dns::Name& zone_name) const; - /// \brief Inserts an rrset into the zone. - /// - /// It puts another RRset into the zone. - /// - /// In the current implementation, this method doesn't allow an existing - /// RRset to be updated or overridden. So the caller must make sure that - /// all RRs of the same type and name must be given in the form of a - /// single RRset. The current implementation will also require that - /// when an RRSIG is added, the RRset to be covered has already been - /// added. These restrictions are probably too strict when this data - /// source accepts various forms of input, so they should be revisited - /// later. - /// - /// Except for NullRRset and OutOfZone, this method does not guarantee - /// strong exception safety (it is currently not needed, if it is needed - /// in future, it should be implemented). - /// - /// \throw NullRRset \c rrset is a NULL pointer. - /// \throw OutOfZone The owner name of \c rrset is outside of the - /// origin of the zone. - /// \throw AddError Other general errors. - /// \throw Others This method might throw standard allocation exceptions. - /// - /// \param rrset The set to add. - /// \return SUCCESS or EXIST (if an rrset for given name and type already - /// exists). - result::Result add(const isc::dns::Name& zone_name, - const isc::dns::ConstRRsetPtr& rrset); - /// \brief RRset is NULL exception. /// /// This is thrown if the provided RRset parameter is NULL. diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 68c73ad712..0e6ed3c4ed 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -98,7 +98,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, ZoneTable::FindResult ZoneTable::findZone(const Name& name) const { - ZoneTableNode* node(NULL); + const ZoneTableNode* node(NULL); result::Result my_result; // Translate the return codes diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index aebd4d0d0f..4f309b7c2e 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -82,11 +82,11 @@ public: /// \brief Result data of findZone() method. struct FindResult { FindResult(result::Result param_code, - ZoneData* param_zone_data) : + const ZoneData* param_zone_data) : code(param_code), zone_data(param_zone_data) {} const result::Result code; - ZoneData* const zone_data; + const ZoneData* const zone_data; }; private: From 45ae6bad11071f2b416c267bf2e71f2208487dda Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 16:50:50 +0200 Subject: [PATCH 144/194] [2292] Remove unused template parameters The domain tree no longer supports the mutable find versions, so we don't need the template parameters allowing for them. --- src/lib/datasrc/memory/domaintree.h | 68 +++++++---------------------- 1 file changed, 16 insertions(+), 52 deletions(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 50f5d40cf6..5101c65635 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -684,7 +684,7 @@ DomainTreeNode::predecessor() const { /// DomainTree. /// This is the reason why manipulation methods such as \c push() and \c pop() /// are private (and not shown in the doxygen document). -template > +template class DomainTreeNodeChain { /// DomainTreeNodeChain is initialized by DomainTree, only DomainTree has /// knowledge to manipulate it. @@ -817,7 +817,7 @@ private: /// root node of DomainTree /// /// \exception None - NodeType* top() const { + const DomainTreeNode* top() const { assert(!isEmpty()); return (nodes_[level_count_ - 1]); } @@ -840,7 +840,7 @@ private: /// otherwise the node should be the root node of DomainTree. /// /// \exception None - void push(NodeType* node) { + void push(const DomainTreeNode* node) { assert(level_count_ < RBT_MAX_LEVEL); nodes_[level_count_++] = node; } @@ -852,7 +852,7 @@ private: const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS; size_t level_count_; - NodeType* nodes_[RBT_MAX_LEVEL]; + const DomainTreeNode* nodes_[RBT_MAX_LEVEL]; const DomainTreeNode* last_compared_; isc::dns::NameComparisonResult last_comparison_; }; @@ -1080,39 +1080,25 @@ public: /// of it. In that case, node parameter is left intact. //@{ - /// \brief Simple find returning immutable node. + /// \brief Simple find /// - /// Acts as described in the \ref find section, but returns immutable node - /// pointer. + /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, const DomainTreeNode** node) const { DomainTreeNodeChain node_path; - const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find >(ls, &target_node, - node_path, NULL, - NULL)); - if (ret != NOTFOUND) { - *node = target_node; - } + Result ret = (find(ls, node, node_path, NULL, NULL)); return (ret); } - /// \brief Simple find returning immutable node, with node_path tracking + /// \brief Simple find, with node_path tracking /// - /// Acts as described in the \ref find section, but returns immutable node - /// pointer. + /// Acts as described in the \ref find section. Result find(const isc::dns::Name& name, const DomainTreeNode** node, DomainTreeNodeChain& node_path) const { - const DomainTreeNode *target_node = NULL; const isc::dns::LabelSequence ls(name); - Result ret = (find >(ls, &target_node, - node_path, NULL, - NULL)); - if (ret != NOTFOUND) { - *node = target_node; - } + Result ret = (find(ls, node, node_path, NULL, NULL)); return (ret); } @@ -1190,34 +1176,12 @@ public: /// /// \return As in the description, but in case of callback returning /// \c true, it returns immediately with the current node. - template - Result find(const isc::dns::LabelSequence& target_labels_orig, - const NodeType** node, - DomainTreeNodeChain& node_path, - bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const; - - /// \brief Simple find returning immutable node. - /// - /// Acts as described in the \ref find section, but returns immutable - /// node pointer. template - Result find(const isc::dns::LabelSequence& target_labels, + Result find(const isc::dns::LabelSequence& target_labels_orig, const DomainTreeNode** node, DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), - CBARG callback_arg) const - { - const DomainTreeNode* target_node = NULL; - Result ret = find >(target_labels, - &target_node, - node_path, callback, - callback_arg); - if (ret != NOTFOUND) { - *node = target_node; - } - return (ret); - } + CBARG callback_arg) const; //@} /// \brief return the next bigger node in DNSSEC order from a given node @@ -1477,11 +1441,11 @@ DomainTree::deleteHelper(util::MemorySegment& mem_sgmt, } template -template +template typename DomainTree::Result DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, - const NodeType** target, - DomainTreeNodeChain& node_path, + const DomainTreeNode** target, + DomainTreeNodeChain& node_path, bool (*callback)(const DomainTreeNode&, CBARG), CBARG callback_arg) const { @@ -1491,7 +1455,7 @@ DomainTree::find(const isc::dns::LabelSequence& target_labels_orig, " and label sequence"); } - NodeType* node; + const DomainTreeNode* node; if (!node_path.isEmpty()) { // Get the top node in the node chain From b31ee9e4712a22bd04637aa3222863ab0eb3c2b1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:03:18 +0200 Subject: [PATCH 145/194] [2292] Drop tests for InMemoryClient::add As the method was removed. --- .../tests/memory/memory_client_unittest.cc | 92 ------------------- 1 file changed, 92 deletions(-) diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc index 58979a4351..6d3f05505c 100644 --- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc +++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc @@ -543,29 +543,6 @@ TEST_F(MemoryClientTest, loadRRSIGs) { EXPECT_EQ(1, client_->getZoneCount()); } -TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - RRsetPtr rrset(new RRset(Name("example.org"), - RRClass::IN(), RRType::A(), RRTTL(3600))); - rrset->addRdata(in::A("192.0.2.1")); - rrset->addRdata(in::A("192.0.2.2")); - - RRsetPtr rrsig(new RRset(Name("example.org"), zclass_, - RRType::RRSIG(), RRTTL(300))); - rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 " - "12345 example.org. FAKEFAKEFAKE")); - rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 " - "54321 example.org. FAKEFAKEFAKEFAKE")); - rrset->addRRsig(rrsig); - - EXPECT_THROW(client_->add(Name("example.org"), rrset), - InMemoryClient::AddError); - - // Teardown checks for memory segment leaks -} - TEST_F(MemoryClientTest, getZoneCount) { EXPECT_EQ(0, client_->getZoneCount()); client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone"); @@ -655,75 +632,6 @@ TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) { EXPECT_THROW(iterator->getSOA(), isc::NotImplemented); } -TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) { - // The zone "example.org" doesn't exist, so we can't add an RRset to - // it. - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError); -} - -TEST_F(MemoryClientTest, addOutOfZoneThrows) { - // Out of zone names should throw. - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-empty.zone"); - - RRsetPtr rrset_a(new RRset(Name("a.example.com"), - RRClass::IN(), RRType::A(), RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), - OutOfZone); - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, addNullRRsetThrows) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()), - InMemoryClient::NullRRset); - - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, addEmptyRRsetThrows) { - client_->load(Name("example.org"), - TEST_DATA_DIR "/example.org-rrsigs.zone"); - - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - EXPECT_THROW(client_->add(Name("example.org"), rrset_a), - InMemoryClient::AddError); - - // Teardown checks for memory segment leaks -} - -TEST_F(MemoryClientTest, add) { - client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone"); - - // Add another RRset - RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), - RRTTL(300))); - rrset_a->addRdata(rdata::in::A("192.0.2.1")); - client_->add(Name("example.org"), rrset_a); - - ZoneIteratorPtr iterator(client_->getIterator(Name("example.org"))); - - // First we have the SOA - ConstRRsetPtr rrset(iterator->getNextRRset()); - EXPECT_TRUE(rrset); - EXPECT_EQ(RRType::A(), rrset->getType()); - - rrset = iterator->getNextRRset(); - EXPECT_TRUE(rrset); - EXPECT_EQ(RRType::SOA(), rrset->getType()); - - // There's nothing else in this zone - EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset()); -} - TEST_F(MemoryClientTest, findZoneData) { client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-rrsigs.zone"); From fa5076be0c7037c239c00fb7c2c53d02bdc87260 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:18:33 +0200 Subject: [PATCH 146/194] [2292] Re-add version of find removed by accident This one was not mutable, it just took Name as an argument. --- src/lib/datasrc/memory/domaintree.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 5101c65635..7e68f53bf0 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -1102,6 +1102,23 @@ public: return (ret); } + /// \brief Simple find returning immutable node. + /// + /// Acts as described in the \ref find section, but returns immutable + /// node pointer. + template + Result find(const isc::dns::Name& name, + const DomainTreeNode** node, + DomainTreeNodeChain& node_path, + bool (*callback)(const DomainTreeNode&, CBARG), + CBARG callback_arg) const + { + const isc::dns::LabelSequence ls(name); + Result ret = find(ls, node, node_path, callback, + callback_arg); + return (ret); + } + /// \brief Find with callback and node chain /// \anchor callback /// From e84770a80501161a4de8ca0fd9d821b0ce7c7da9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 7 Oct 2012 17:19:12 +0200 Subject: [PATCH 147/194] [2292] Update tests They don't need the mutable versions to work, but they used them anyway. --- .../tests/memory/domaintree_unittest.cc | 28 +++++++++---------- .../tests/memory/zone_data_unittest.cc | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index cb16e02c79..3754139620 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -256,8 +256,8 @@ TEST_F(DomainTreeTest, subTreeRoot) { // "g.h" is not a subtree root EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("g.h"), &dtnode)); - EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("g.h"), &cdtnode)); + EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); // fission the node "g.h" EXPECT_EQ(TestDomainTree::ALREADYEXISTS, @@ -270,8 +270,8 @@ TEST_F(DomainTreeTest, subTreeRoot) { // "g.h" should be a subtree root now. EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("g.h"), &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("g.h"), &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); } TEST_F(DomainTreeTest, additionalNodeFission) { @@ -286,8 +286,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) { // "t.0" is not a subtree root EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("t.0"), &dtnode)); - EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("t.0"), &cdtnode)); + EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); // fission the node "t.0" EXPECT_EQ(TestDomainTree::ALREADYEXISTS, @@ -300,8 +300,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) { // "t.0" should be a subtree root now. EXPECT_EQ(TestDomainTree::EXACTMATCH, - dtree_expose_empty_node.find(Name("t.0"), &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); + dtree_expose_empty_node.find(Name("t.0"), &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT)); } TEST_F(DomainTreeTest, findName) { @@ -328,10 +328,10 @@ TEST_F(DomainTreeTest, findName) { EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode)); - // find dtnode + // find cdtnode EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"), - &dtnode)); - EXPECT_EQ(Name("q"), dtnode->getName()); + &cdtnode)); + EXPECT_EQ(Name("q"), cdtnode->getName()); } TEST_F(DomainTreeTest, findError) { @@ -411,11 +411,11 @@ performCallbackTest(TestDomainTree& dtree, Name("example"), &parentdtnode)); // the child/parent nodes shouldn't "inherit" the callback flag. - // "dtnode" may be invalid due to the insertion, so we need to re-find + // "cdtnode" may be invalid due to the insertion, so we need to re-find // it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), - &dtnode)); - EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); + &cdtnode)); + EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc index d15fe8beb2..3c28cec229 100644 --- a/src/lib/datasrc/tests/memory/zone_data_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc @@ -108,7 +108,7 @@ void checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type, const RdataSet* expected_set) { - ZoneNode* node = NULL; + const ZoneNode* node = NULL; tree.find(name, &node); ASSERT_NE(static_cast(NULL), node); EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type)); From 7b2aa97d01af2934b408caa6044cbfab4c6679f6 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 11:32:18 -0700 Subject: [PATCH 148/194] [2292] minor cleanups: constify, indentation --- src/lib/datasrc/memory/memory_client.cc | 10 +++++----- src/lib/datasrc/tests/memory/domaintree_unittest.cc | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index de953ca107..a5824fa63b 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -627,11 +627,11 @@ InMemoryClient::InMemoryClientImpl::load( // node must point to a valid node now assert(node != NULL); - std::string* tstr = node->setData(new std::string(filename)); + const std::string* tstr = node->setData(new std::string(filename)); delete tstr; - result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, holder)); + const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, holder)); if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. @@ -726,8 +726,8 @@ InMemoryClient::load(const isc::dns::Name& zone_name, const std::string InMemoryClient::getFileName(const isc::dns::Name& zone_name) const { const FileNameNode* node(NULL); - FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, - &node); + const FileNameTree::Result result = impl_->file_name_tree_->find(zone_name, + &node); if (result == FileNameTree::EXACTMATCH) { return (*node->getData()); } else { diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index 3754139620..81fa576b21 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -414,7 +414,7 @@ performCallbackTest(TestDomainTree& dtree, // "cdtnode" may be invalid due to the insertion, so we need to re-find // it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), - &cdtnode)); + &cdtnode)); EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); From ea4149ff70f0a90ed995a04e41e4fa06664278c4 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 13:39:30 +0200 Subject: [PATCH 149/194] [2292] Don't pass SegmentObjectHolder It is supposed to be mostly private. Use it internally only. --- src/lib/datasrc/memory/memory_client.cc | 3 +- src/lib/datasrc/memory/zone_table.cc | 10 +++++-- src/lib/datasrc/memory/zone_table.h | 11 +++---- .../tests/memory/zone_table_unittest.cc | 29 ++++++++++++------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index a5824fa63b..e33a0f32e9 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -631,7 +631,8 @@ InMemoryClient::InMemoryClientImpl::load( delete tstr; const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, holder)); + zone_name, + holder.release())); if (result == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 0e6ed3c4ed..4a1f184974 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -69,9 +69,13 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, result::Result ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, - const Name& zone_name, - SegmentObjectHolder& content) + const Name& zone_name, ZoneData* content) { + if (content == NULL) { + isc_throw(isc::BadValue, "Zone content must not be NULL"); + } + SegmentObjectHolder holder(mem_sgmt, content, + zone_class); // Get the node where we put the zone ZoneTableNode* node(NULL); switch (zones_->insert(mem_sgmt, zone_name, &node)) { @@ -87,7 +91,7 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, assert(node != NULL); // We can release now, setData never throws - ZoneData* old = node->setData(content.release()); + ZoneData* old = node->setData(holder.release()); if (old != NULL) { ZoneData::destroy(mem_sgmt, old, zone_class); return (result::EXIST); diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 4f309b7c2e..5ade3ac963 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -36,10 +36,6 @@ namespace memory { // forward declaration: in this header it's mostly an opaque type. class ZoneData; -namespace detail { -template class SegmentObjectHolder; -} - /// \brief A conceptual table of authoritative zones. /// /// This class is actually a simple wrapper for a \c DomainTree whose data is @@ -147,7 +143,9 @@ public: /// \param zone_class The RR class of the zone. It must be the RR class /// that is supposed to be associated to the zone table. /// \param content This one should hold the zone content (the ZoneData). - /// When it is added successfully, it is released from the holder. + /// The ownership is passed onto the zone table. Must not be null. + /// Must correspond to the name and class and must be allocated from + /// mem_sgmt. /// \return \c result::SUCCESS If the zone is successfully /// added to the zone table. /// \return \c result::EXIST The zone table already contained @@ -156,8 +154,7 @@ public: result::Result addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class, const dns::Name& zone_name, - detail::SegmentObjectHolder& content); + ZoneData* content); /// Find a zone that best matches the given name in the \c ZoneTable. /// diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index b9ee3d9441..64ec312c02 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -89,11 +89,15 @@ TEST_F(ZoneTableTest, create) { } TEST_F(ZoneTableTest, addZone) { + // It doesn't accept empty (NULL) zones + EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, zname1, NULL), + isc::BadValue); + SegmentObjectHolder holder1( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); // Normal successful case. EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder1)); + zname1, holder1.release())); // It got released by it EXPECT_EQ(NULL, holder1.get()); @@ -101,7 +105,7 @@ TEST_F(ZoneTableTest, addZone) { SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder2)); + zname1, holder2.release())); // It releases this one even when we replace the old zone EXPECT_EQ(NULL, holder2.get()); @@ -111,16 +115,18 @@ TEST_F(ZoneTableTest, addZone) { // names are compared in a case insensitive manner. EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, Name("EXAMPLE.COM"), - holder3)); + holder3.release())); // Add some more different ones. Should just succeed. SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2, holder4)); + zone_table->addZone(mem_sgmt_, zclass_, zname2, + holder4.release())); SegmentObjectHolder holder5( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3, holder5)); + zone_table->addZone(mem_sgmt_, zclass_, zname3, + holder5.release())); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected @@ -129,7 +135,7 @@ TEST_F(ZoneTableTest, addZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_); mem_sgmt_.setThrowCount(1); EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"), - holder6), + holder6.release()), std::bad_alloc); } @@ -138,15 +144,17 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); ZoneData* zone_data = holder1.get(); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, - holder1)); + holder1.release())); SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname2, holder2)); + zone_table->addZone(mem_sgmt_, zclass_, zname2, + holder2.release())); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, - zone_table->addZone(mem_sgmt_, zclass_, zname3, holder3)); + zone_table->addZone(mem_sgmt_, zclass_, zname3, + holder3.release())); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); @@ -170,7 +178,8 @@ TEST_F(ZoneTableTest, findZone) { SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - Name("com"), holder4)); + Name("com"), + holder4.release())); EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } From a3953a556b472084d669dcb629fb801dec67c5dd Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 14:07:13 +0200 Subject: [PATCH 150/194] [2292] Pass the old zone back Instead of releasing it directly. While the internal release was more convenient, it didn't allow for swapping things fast under a mutex and then spending the time releasing it unlocked. --- src/lib/datasrc/memory/memory_client.cc | 14 ++++--- src/lib/datasrc/memory/zone_table.cc | 7 ++-- src/lib/datasrc/memory/zone_table.h | 20 +++++++--- .../tests/memory/zone_table_unittest.cc | 38 ++++++++++++------- 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc index e33a0f32e9..16096114fc 100644 --- a/src/lib/datasrc/memory/memory_client.cc +++ b/src/lib/datasrc/memory/memory_client.cc @@ -630,16 +630,20 @@ InMemoryClient::InMemoryClientImpl::load( const std::string* tstr = node->setData(new std::string(filename)); delete tstr; - const result::Result result(zone_table_->addZone(mem_sgmt_, rrclass_, - zone_name, - holder.release())); - if (result == result::SUCCESS) { + const ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_, + zone_name, + holder.release())); + if (result.code == result::SUCCESS) { // Only increment the zone count if the zone doesn't already // exist. ++zone_count_; } + // Destroy the old instance of the zone if there was any + if (result.zone_data != NULL) { + ZoneData::destroy(mem_sgmt_, result.zone_data, rrclass_); + } - return (result); + return (result.code); } namespace { diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc index 4a1f184974..836b020088 100644 --- a/src/lib/datasrc/memory/zone_table.cc +++ b/src/lib/datasrc/memory/zone_table.cc @@ -67,7 +67,7 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable, mem_sgmt.deallocate(ztable, sizeof(ZoneTable)); } -result::Result +ZoneTable::AddResult ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, const Name& zone_name, ZoneData* content) { @@ -93,10 +93,9 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class, // We can release now, setData never throws ZoneData* old = node->setData(holder.release()); if (old != NULL) { - ZoneData::destroy(mem_sgmt, old, zone_class); - return (result::EXIST); + return (AddResult(result::EXIST, old)); } else { - return (result::SUCCESS); + return (AddResult(result::SUCCESS, NULL)); } } diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h index 5ade3ac963..024558eb30 100644 --- a/src/lib/datasrc/memory/zone_table.h +++ b/src/lib/datasrc/memory/zone_table.h @@ -74,6 +74,14 @@ private: typedef DomainTreeNode ZoneTableNode; public: + /// \brief Result data of addZone() method. + struct AddResult { + AddResult(result::Result param_code, ZoneData* param_zone_data) : + code(param_code), zone_data(param_zone_data) + {} + const result::Result code; + ZoneData* const zone_data; + }; /// \brief Result data of findZone() method. struct FindResult { @@ -149,12 +157,12 @@ public: /// \return \c result::SUCCESS If the zone is successfully /// added to the zone table. /// \return \c result::EXIST The zone table already contained - /// zone of the same origin. The old data is released and replaced - /// by the new one. - result::Result addZone(util::MemorySegment& mem_sgmt, - dns::RRClass zone_class, - const dns::Name& zone_name, - ZoneData* content); + /// zone of the same origin. The old data is replaced and returned + /// inside the result. + AddResult addZone(util::MemorySegment& mem_sgmt, + dns::RRClass zone_class, + const dns::Name& zone_name, + ZoneData* content); /// Find a zone that best matches the given name in the \c ZoneTable. /// diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 64ec312c02..9cf1b3420c 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -95,38 +95,50 @@ TEST_F(ZoneTableTest, addZone) { SegmentObjectHolder holder1( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); + const ZoneData* data1(holder1.get()); // Normal successful case. - EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder1.release())); + const ZoneTable::AddResult result1(zone_table->addZone(mem_sgmt_, zclass_, + zname1, + holder1.release())); + EXPECT_EQ(result::SUCCESS, result1.code); + EXPECT_EQ(NULL, result1.zone_data); // It got released by it EXPECT_EQ(NULL, holder1.get()); // Duplicate add doesn't replace the existing data. SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - zname1, holder2.release())); + const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_, + zname1, + holder2.release())); + EXPECT_EQ(result::EXIST, result2.code); + // The old one gets out + EXPECT_EQ(data1, result2.zone_data); // It releases this one even when we replace the old zone EXPECT_EQ(NULL, holder2.get()); + // We need to release the old one manually + ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")), zclass_); // names are compared in a case insensitive manner. - EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_, - Name("EXAMPLE.COM"), - holder3.release())); + const ZoneTable::AddResult result3(zone_table->addZone(mem_sgmt_, zclass_, + Name("EXAMPLE.COM"), + holder3.release())); + EXPECT_EQ(result::EXIST, result3.code); + ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_); // Add some more different ones. Should just succeed. SegmentObjectHolder holder4( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname2, - holder4.release())); + holder4.release()).code); SegmentObjectHolder holder5( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname3, - holder5.release())); + holder5.release()).code); // Have the memory segment throw an exception in extending the internal // tree. It still shouldn't cause memory leak (which would be detected @@ -144,17 +156,17 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_); ZoneData* zone_data = holder1.get(); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1, - holder1.release())); + holder1.release()).code); SegmentObjectHolder holder2( mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname2, - holder2.release())); + holder2.release()).code); SegmentObjectHolder holder3( mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname3, - holder3.release())); + holder3.release()).code); const ZoneTable::FindResult find_result1 = zone_table->findZone(Name("example.com")); @@ -179,7 +191,7 @@ TEST_F(ZoneTableTest, findZone) { mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_); EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, Name("com"), - holder4.release())); + holder4.release()).code); EXPECT_EQ(zone_data, zone_table->findZone(Name("www.example.com")).zone_data); } From 3efb1402e7d62755971c8e1440d31f85f14053e3 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Tue, 9 Oct 2012 14:11:25 +0200 Subject: [PATCH 151/194] [2292] Clarify test And make it slightly stronger (it now checks something that should be mostly obvious too, but who knows, with software). --- src/lib/datasrc/tests/memory/domaintree_unittest.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc index 81fa576b21..45e256a0b4 100644 --- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc +++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc @@ -411,10 +411,11 @@ performCallbackTest(TestDomainTree& dtree, Name("example"), &parentdtnode)); // the child/parent nodes shouldn't "inherit" the callback flag. - // "cdtnode" may be invalid due to the insertion, so we need to re-find - // it. + // "dtnode" should still validly point to "callback.example", but we + // explicitly confirm it. EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"), &cdtnode)); + ASSERT_EQ(dtnode, cdtnode); EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK)); From 44b1d42219a1787165a01497c21fec2661ba8277 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 11:58:17 +0200 Subject: [PATCH 152/194] [2292] Add a TODO about re-adding tests Planned for merge #2268 --- .../datasrc/tests/memory/memory_client_unittest.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc index 6d3f05505c..004f663c56 100644 --- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc +++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc @@ -682,4 +682,16 @@ TEST_F(MemoryClientTest, getJournalReaderNotImplemented) { EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0), isc::NotImplemented); } + +// TODO (upon merge of #2268): Re-add (and modify not to need +// InMemoryClient::add) the tests removed in +// 7a628baa1a158b5837d6f383e10b30542d2ac59b. Maybe some of them +// are really not needed. +// +// * MemoryClientTest::loadRRSIGsRdataMixedCoveredTypes +// * MemoryClientTest::addRRsetToNonExistentZoneThrows +// * MemoryClientTest::addOutOfZoneThrows +// * MemoryClientTest::addNullRRsetThrows +// * MemoryClientTest::addEmptyRRsetThrows +// * MemoryClientTest::add } From f44918fd6cd6c3357609c82865a1e6c4a0e4d44a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 14:03:52 -0700 Subject: [PATCH 153/194] [2204] changed internal representation of auth_srv's "client_lists_" to ptrs. this will be necessary later in this branch. also renamed the member variable "datasrc_client_lists_" as the mere "client" can be ambiguous. --- src/bin/auth/auth_srv.cc | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index b870ae2cb9..880a4ac1e8 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -69,6 +69,8 @@ using namespace std; +using boost::shared_ptr; + using namespace isc; using namespace isc::cc; using namespace isc::datasrc; @@ -264,23 +266,21 @@ public: AddressList listen_addresses_; /// The TSIG keyring - const boost::shared_ptr* keyring_; + const shared_ptr* keyring_; - /// The client list - std::map > - client_lists_; + /// The data source client list + shared_ptr > > + datasrc_client_lists_; - boost::shared_ptr getClientList(const RRClass& - rrclass) - { + shared_ptr getClientList(const RRClass& rrclass) { // TODO: Debug-build only check if (!mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked!"); } - const std::map >:: - const_iterator it(client_lists_.find(rrclass)); - if (it == client_lists_.end()) { - return (boost::shared_ptr()); + const std::map >:: + const_iterator it(datasrc_client_lists_->find(rrclass)); + if (it == datasrc_client_lists_->end()) { + return (shared_ptr()); } else { return (it->second); } @@ -335,6 +335,8 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client, xfrin_session_(NULL), counters_(), keyring_(NULL), + datasrc_client_lists_(new std::map >()), ddns_base_forwarder_(ddns_forwarder), ddns_forwarder_(NULL), xfrout_connected_(false), @@ -650,7 +652,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message, try { const ConstQuestionPtr question = *message.beginQuestion(); - const boost::shared_ptr + const shared_ptr list(getClientList(question->getClass())); if (list) { const RRType& qtype = question->getType(); @@ -911,7 +913,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) { } void -AuthSrv::setTSIGKeyRing(const boost::shared_ptr* keyring) { +AuthSrv::setTSIGKeyRing(const shared_ptr* keyring) { impl_->keyring_ = keyring; } @@ -932,19 +934,21 @@ AuthSrv::destroyDDNSForwarder() { void AuthSrv::setClientList(const RRClass& rrclass, - const boost::shared_ptr& list) { + const shared_ptr& list) +{ // TODO: Debug-build only check if (!impl_->mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked"); } if (list) { - impl_->client_lists_[rrclass] = list; + (*impl_->datasrc_client_lists_)[rrclass] = list; } else { - impl_->client_lists_.erase(rrclass); + impl_->datasrc_client_lists_->erase(rrclass); } } -boost::shared_ptr + +shared_ptr AuthSrv::getClientList(const RRClass& rrclass) { return (impl_->getClientList(rrclass)); } @@ -957,9 +961,9 @@ AuthSrv::getClientListClasses() const { } vector result; - for (std::map >:: - const_iterator it(impl_->client_lists_.begin()); - it != impl_->client_lists_.end(); ++it) { + for (std::map >:: + const_iterator it(impl_->datasrc_client_lists_->begin()); + it != impl_->datasrc_client_lists_->end(); ++it) { result.push_back(it->first); } return (result); From 919fe74ccbeecf5d546b60f017c3e9e1b073655a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 15:15:18 -0700 Subject: [PATCH 154/194] [2204] simplify configureDataSource by always creating a new lists and swap. so we don't have to worry about what are in the current lists or rollback operations. swapDataSrcClientLists() is newly introduced for AuthSrv. No direc tests yet (technically bad in terms TDD but the definition is very simple), which will be provided in the next step. the lock is now moved inside swapDataSrcClientLists(). note: even though this version builds everything, the amount of work should be mostly the same because the only save is to create the empty ClientList when the new and old have the same class of client. The expensive part is ClientList::configure(). This version doesn't need any more call to configure() than the old version. --- src/bin/auth/auth_srv.cc | 13 +++- src/bin/auth/auth_srv.h | 18 ++++- src/bin/auth/datasrc_config.h | 76 +++++-------------- src/bin/auth/tests/command_unittest.cc | 2 + src/bin/auth/tests/datasrc_config_unittest.cc | 68 ++++++++--------- 5 files changed, 78 insertions(+), 99 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 880a4ac1e8..391416b82e 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -269,8 +269,7 @@ public: const shared_ptr* keyring_; /// The data source client list - shared_ptr > > - datasrc_client_lists_; + AuthSrv::DataSrcClientListsPtr datasrc_client_lists_; shared_ptr getClientList(const RRClass& rrclass) { // TODO: Debug-build only check @@ -948,6 +947,16 @@ AuthSrv::setClientList(const RRClass& rrclass, } } +AuthSrv::DataSrcClientListsPtr +AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { + { + thread::Mutex::Locker locker(impl_->mutex_); + std::swap(new_lists, impl_->datasrc_client_lists_); + } + + return (new_lists); +} + shared_ptr AuthSrv::getClientList(const RRClass& rrclass) { return (impl_->getClientList(rrclass)); diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index ee7bd52bb2..48aac067b9 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -15,10 +15,11 @@ #ifndef __AUTH_SRV_H #define __AUTH_SRV_H 1 -#include - #include + #include +#include + #include #include #include @@ -35,6 +36,11 @@ #include #include +#include + +#include +#include + namespace isc { namespace util { namespace io { @@ -309,6 +315,14 @@ public: boost::shared_ptr& list); + typedef boost::shared_ptr > > + DataSrcClientListsPtr; + + DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr + new_lists); + /// \brief Returns the currently used client list for the class. /// /// \param rrclass The class for which to get the list. diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 79ace2811a..02354ffa26 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -19,10 +19,10 @@ #include #include -#include #include +#include #include /// \brief Configure the authoritative server's data source lists @@ -45,67 +45,25 @@ configureDataSourceGeneric(Server& server, { typedef boost::shared_ptr ListPtr; typedef std::map Map; - typedef std::pair RollbackPair; - typedef std::pair - RollbackConfiguration; + typedef std::map ListMap; - // Lock the client lists, we're going to manipulate them. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + boost::shared_ptr new_lists(new ListMap); - // Some structures to be able to perform a rollback - std::vector rollback_sets; - std::vector rollback_configurations; - try { - // Get the configuration and current state. - const Map& map(config->mapValue()); - const std::vector - activeVector(server.getClientListClasses()); - std::set active(activeVector.begin(), - activeVector.end()); - // Go through the configuration and change everything. - for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { - const isc::dns::RRClass rrclass(it->first); - active.erase(rrclass); - ListPtr list(server.getClientList(rrclass)); - bool need_set(false); - if (list) { - rollback_configurations. - push_back(RollbackConfiguration(rrclass, - list->getConfiguration())); - } else { - list.reset(new List(rrclass)); - need_set = true; - rollback_sets.push_back(RollbackPair(rrclass, ListPtr())); - } - list->configure(it->second, true); - if (need_set) { - server.setClientList(rrclass, list); - } - } - // Remove the ones that are not in the configuration. - for (std::set::iterator it(active.begin()); - it != active.end(); ++it) { - // There seems to be no way the setClientList could throw. - // But this is just to make sure in case it did to restore - // the original. - rollback_sets.push_back( - RollbackPair(*it, server.getClientList(*it))); - server.setClientList(*it, ListPtr()); - } - } catch (...) { - // Perform a rollback of the changes. The old configuration should - // work. - for (typename std::vector::const_iterator - it(rollback_sets.begin()); it != rollback_sets.end(); ++it) { - server.setClientList(it->first, it->second); - } - for (typename std::vector::const_iterator - it(rollback_configurations.begin()); - it != rollback_configurations.end(); ++it) { - server.getClientList(it->first)->configure(it->second, true); - } - throw; + // Get the configuration and current state. + const Map& map(config->mapValue()); + + // Go through the configuration and create corresponding list. + for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { + const isc::dns::RRClass rrclass(it->first); + ListPtr list(new List(rrclass)); + list->configure(it->second, true); + new_lists->insert(std::pair(rrclass, + list)); } + + // Replace the server's lists. By ignoring the return value we let the + // old lists be destroyed. + server.swapDataSrcClientLists(new_lists); } /// \brief Concrete version of configureDataSource() for the diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index c7994831a5..b5e43edda2 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -16,6 +16,8 @@ #include "datasrc_util.h" +#include + #include #include #include diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 3d3aa58b91..c0c9b5ce99 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -81,27 +81,27 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: - // These pretend to be the server - ListPtr getClientList(const RRClass& rrclass) { - log_ += "get " + rrclass.toText() + "\n"; - return (lists_[rrclass]); - } - void setClientList(const RRClass& rrclass, const ListPtr& list) { - log_ += "set " + rrclass.toText() + " " + - (list ? list->getConf() : "") + "\n"; - lists_[rrclass] = list; - } - vector getClientListClasses() const { - vector result; - for (std::map::const_iterator it(lists_.begin()); - it != lists_.end(); ++it) { - result.push_back(it->first); + // To pretend to be the server: + void swapDataSrcClientLists(shared_ptr > + new_lists) + { + lists_.clear(); // first empty it + + // Record the operation and results. Note that map elements are + // sorted by RRClass, so the ordering should be predictable. + for (std::map::const_iterator it = + new_lists->begin(); + it != new_lists->end(); + ++it) + { + const RRClass rrclass = it->first; + ListPtr list = it->second; + log_ += "set " + rrclass.toText() + " " + + (list ? list->getConf() : "") + "\n"; + lists_[rrclass] = list; } - return (result); - } - isc::util::thread::Mutex& getClientListMutex() const { - return (mutex_); } + protected: DatasrcConfigTest() : session(ElementPtr(new ListElement), ElementPtr(new ListElement), @@ -147,9 +147,8 @@ protected: session.addMessage(createCommand("config_update", config), "data_sources", "*"); mccs->checkCommand(); - // Check it called the correct things (check that there's no IN yet and - // set a new one. - EXPECT_EQ("get IN\nset IN xxx\n", log_); + // Check that the passed config is stored. + EXPECT_EQ("set IN xxx\n", log_); EXPECT_EQ(1, lists_.size()); } FakeSession session; @@ -166,8 +165,10 @@ TEST_F(DatasrcConfigTest, createList) { } TEST_F(DatasrcConfigTest, modifyList) { - // First, initialize the list + // First, initialize the list, and confirm the current config initializeINList(); + EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf()); + // And now change the configuration of the list const ElementPtr config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}")); @@ -175,9 +176,7 @@ TEST_F(DatasrcConfigTest, modifyList) { "*"); log_ = ""; mccs->checkCommand(); - // This one does not set - EXPECT_EQ("get IN\n", log_); - // But this should contain the yyy configuration + // Now the new one should be installed. EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ(1, lists_.size()); } @@ -191,7 +190,7 @@ TEST_F(DatasrcConfigTest, multiple) { "*"); mccs->checkCommand(); // We have set commands for both classes. - EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_); + EXPECT_EQ("set IN yyy\nset CH xxx\n", log_); // We should have both there EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); @@ -212,9 +211,7 @@ TEST_F(DatasrcConfigTest, updateAdd) { "*"); log_ = ""; mccs->checkCommand(); - // The CH is set, IN not - EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_); - // But this should contain the yyy configuration + EXPECT_EQ("set IN yyy\nset CH xxx\n", log_); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ(2, lists_.size()); @@ -229,12 +226,11 @@ TEST_F(DatasrcConfigTest, updateDelete) { "*"); log_ = ""; mccs->checkCommand(); - EXPECT_EQ("get IN\nset IN \n", log_); - EXPECT_FALSE(lists_[RRClass::IN()]); - // In real auth server, the NULL one would be removed. However, we just - // store it, so the IN bucket is still in there. This checks there's nothing - // else. - EXPECT_EQ(1, lists_.size()); + + // No operation takes place in the configuration, and the old one is + // just dropped + EXPECT_EQ("", log_); + EXPECT_TRUE(lists_.empty()); } // Check that we can rollback an addition if something else fails From d7e42bb0c48c961c320ba1bf4324c49b9c43c19a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:02:01 -0700 Subject: [PATCH 155/194] [2204] completely replaced setClientList with swapDataSrcClientLists. the test cases using setClientList were updated so they use swapDataSrcClientLists (some of them work as a test for the "swap" itself). now we don't need setClientList, so it was removed. --- src/bin/auth/auth_srv.cc | 24 ++------ src/bin/auth/auth_srv.h | 38 ++++++++---- src/bin/auth/datasrc_config.h | 10 +++- src/bin/auth/tests/auth_srv_unittest.cc | 59 +++++++++++-------- src/bin/auth/tests/datasrc_config_unittest.cc | 5 +- 5 files changed, 76 insertions(+), 60 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 391416b82e..1102ae3ed3 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -931,29 +931,13 @@ AuthSrv::destroyDDNSForwarder() { } } -void -AuthSrv::setClientList(const RRClass& rrclass, - const shared_ptr& list) -{ - // TODO: Debug-build only check - if (!impl_->mutex_.locked()) { - isc_throw(isc::Unexpected, "Not locked"); - } - - if (list) { - (*impl_->datasrc_client_lists_)[rrclass] = list; - } else { - impl_->datasrc_client_lists_->erase(rrclass); - } -} - AuthSrv::DataSrcClientListsPtr AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { - { - thread::Mutex::Locker locker(impl_->mutex_); - std::swap(new_lists, impl_->datasrc_client_lists_); + // TODO: Debug-build only check + if (!impl_->mutex_.locked()) { + isc_throw(isc::Unexpected, "Not locked!"); } - + std::swap(new_lists, impl_->datasrc_client_lists_); return (new_lists); } diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index 48aac067b9..f1e73c1158 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -302,24 +302,36 @@ public: /// If there was no forwarder yet, this method does nothing. void destroyDDNSForwarder(); - /// \brief Sets the currently used list for data sources of given - /// class. - /// - /// Replaces the internally used client list with a new one. Other - /// classes are not changed. - /// - /// \param rrclass The class to modify. - /// \param list Shared pointer to the client list. If it is NULL, - /// the list is removed instead. - void setClientList(const isc::dns::RRClass& rrclass, const - boost::shared_ptr& - list); - typedef boost::shared_ptr > > DataSrcClientListsPtr; + /// \brief Swap the currently used set of data source client lists with + /// given one. + /// + /// The "set" of lists is actually given in the form of map from + /// RRClasses to shared pointers to isc::datasrc::ConfigurableClientList. + /// + /// This method returns the swapped set of lists, which was previously + /// used by the server. + /// + /// This method is intended to be used by a separate method to update + /// the data source configuration "at once". The caller must hold + /// a lock for the mutex object returned by \c getClientListMutex() + /// before calling this method. + /// + /// The ownership of the returned pointer is transferred to the caller. + /// The caller is generally expected to release the resources used in + /// the old lists. Note that it could take longer time if some of the + /// data source clients contain a large size of in-memory data. + /// + /// The caller can pass a NULL pointer. This effectively disables + /// any data source for the server. + /// + /// \param new_lists Shared pointer to a new set of data source client + /// lists. + /// \return The previous set of lists. It can be NULL. DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr new_lists); diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 02354ffa26..532aa34a2f 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -17,6 +17,8 @@ #include "auth_srv.h" +#include + #include #include @@ -62,8 +64,12 @@ configureDataSourceGeneric(Server& server, } // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. - server.swapDataSrcClientLists(new_lists); + // old lists be destroyed. Lock will be released immediately after the + // swap. + { + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(new_lists); + } } /// \brief Concrete version of configureDataSource() for the diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 60a9a2ac38..dd60d896ad 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -63,6 +63,7 @@ using namespace std; using namespace isc::cc; using namespace isc::dns; +using namespace isc::datasrc; using namespace isc::util; using namespace isc::util::io::internal; using namespace isc::util::unittests; @@ -90,6 +91,9 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone"; // a signed example zone. const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed"; +// shortcut commonly used in tests +typedef boost::shared_ptr ListPtr; + class AuthSrvTest : public SrvTestBase { protected: AuthSrvTest() : @@ -1431,7 +1435,9 @@ TEST_F(AuthSrvTest, boost::shared_ptr list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER, false)); - server.setClientList(RRClass::IN(), list); + AuthSrv::DataSrcClientListsPtr lists(new std::map); + lists->insert(pair(RRClass::IN(), list)); + server.swapDataSrcClientLists(lists); } createDataFromFile("nsec3query_nodnssec_fromWire.wire"); @@ -1459,7 +1465,9 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception, boost::shared_ptr list(new FakeList(server.getClientList(RRClass::IN()), throw_when, isc_exception, rrset)); - server.setClientList(RRClass::IN(), list); + AuthSrv::DataSrcClientListsPtr lists(new std::map); + lists->insert(pair(RRClass::IN(), list)); + server.swapDataSrcClientLists(lists); } TEST_F(AuthSrvTest, @@ -1772,34 +1780,37 @@ TEST_F(AuthSrvTest, clientList) { // There's a debug-build only check in them to make sure everything // locks them and we call them directly here. isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + + AuthSrv::DataSrcClientListsPtr lists; // initially empty + // The lists don't exist. Therefore, the list of RRClasses is empty. - // We also have no IN list. - EXPECT_TRUE(server.getClientListClasses().empty()); - EXPECT_EQ(boost::shared_ptr(), - server.getClientList(RRClass::IN())); + EXPECT_TRUE(server.swapDataSrcClientLists(lists)->empty()); + // Put something in. - const boost::shared_ptr - list(new isc::datasrc::ConfigurableClientList(RRClass::IN())); - const boost::shared_ptr - list2(new isc::datasrc::ConfigurableClientList(RRClass::CH())); - server.setClientList(RRClass::IN(), list); - server.setClientList(RRClass::CH(), list2); - // There are two things in the list and they are IN and CH - vector classes(server.getClientListClasses()); - ASSERT_EQ(2, classes.size()); - EXPECT_EQ(RRClass::IN(), classes[0]); - EXPECT_EQ(RRClass::CH(), classes[1]); + const ListPtr list(new ConfigurableClientList(RRClass::IN())); + const ListPtr list2(new ConfigurableClientList(RRClass::CH())); + + lists.reset(new std::map); + lists->insert(pair(RRClass::IN(), list)); + lists->insert(pair(RRClass::CH(), list2)); + server.swapDataSrcClientLists(lists); + // And the lists can be retrieved. EXPECT_EQ(list, server.getClientList(RRClass::IN())); EXPECT_EQ(list2, server.getClientList(RRClass::CH())); - // Remove one of them - server.setClientList(RRClass::CH(), - boost::shared_ptr()); - // This really got deleted, including the class. - classes = server.getClientListClasses(); - ASSERT_EQ(1, classes.size()); - EXPECT_EQ(RRClass::IN(), classes[0]); + + // Replace the lists with new lists containing only one list. + lists.reset(new std::map); + lists->insert(pair(RRClass::IN(), list)); + lists = server.swapDataSrcClientLists(lists); + + // Old one had two lists. That confirms our swap for IN and CH classes + // (i.e., no other entries were there). + EXPECT_EQ(2, lists->size()); + + // The CH list really got deleted. EXPECT_EQ(list, server.getClientList(RRClass::IN())); + EXPECT_FALSE(server.getClientList(RRClass::CH())); } // We just test the mutex can be locked (exactly once). diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index c0c9b5ce99..329c5d11be 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -81,7 +81,10 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: - // To pretend to be the server: + // These pretend to be the server + isc::util::thread::Mutex& getClientListMutex() const { + return (mutex_); + } void swapDataSrcClientLists(shared_ptr > new_lists) { From b15be74974e7e1d4f9acc0590e8853a064e3b6bf Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:40:10 -0700 Subject: [PATCH 156/194] [2204] extracted swapDataSrcClientLists() from configureDataSource(). now configureDataSource() can take time. --- src/bin/auth/datasrc_config.cc | 2 +- src/bin/auth/datasrc_config.h | 17 ++++--------- src/bin/auth/main.cc | 24 +++++++++++++++---- src/bin/auth/tests/auth_srv_unittest.cc | 16 +++++++++---- src/bin/auth/tests/command_unittest.cc | 12 ++++++++-- src/bin/auth/tests/datasrc_config_unittest.cc | 20 +++++++++++----- 6 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/bin/auth/datasrc_config.cc b/src/bin/auth/datasrc_config.cc index 73fb5190c6..bef6e57de7 100644 --- a/src/bin/auth/datasrc_config.cc +++ b/src/bin/auth/datasrc_config.cc @@ -18,7 +18,7 @@ // This is a trivial specialization for the commonly used version. // Defined in .cc to avoid accidental creation of multiple copies. -void +AuthSrv::DataSrcClientListsPtr configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config) { return (configureDataSourceGeneric - #include #include @@ -41,8 +39,9 @@ /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. template -void -configureDataSourceGeneric(Server& server, +boost::shared_ptr > > // = ListMap below +configureDataSourceGeneric(Server& /*server*/, const isc::data::ConstElementPtr& config) { typedef boost::shared_ptr ListPtr; @@ -63,18 +62,12 @@ configureDataSourceGeneric(Server& server, list)); } - // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. Lock will be released immediately after the - // swap. - { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - server.swapDataSrcClientLists(new_lists); - } + return (new_lists); } /// \brief Concrete version of configureDataSource() for the /// use with authoritative server implementation. -void +AuthSrv::DataSrcClientListsPtr configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); #endif // DATASRC_CONFIG_H diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index b425813006..2a17112ba4 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -93,18 +94,33 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, { assert(server != NULL); if (config->contains("classes")) { + AuthSrv::DataSrcClientListsPtr lists; + if (*first_time) { // HACK: The default is not passed to the handler in the first // callback. This one will get the default (or, current value). // Further updates will work the usual way. assert(config_session != NULL); *first_time = false; - configureDataSource(*auth_server, - config_session->getRemoteConfigValue( - "data_sources", "classes")); + lists = configureDataSource( + *auth_server, + config_session->getRemoteConfigValue("data_sources", + "classes")); } else { - configureDataSource(*server, config->get("classes")); + lists = configureDataSource(*server, config->get("classes")); } + + // Replace the server's lists. By ignoring the return value we let the + // old lists be destroyed. Lock will be released immediately after the + // swap. + { + isc::util::thread::Mutex::Locker locker( + server->getClientListMutex()); + lists = server->swapDataSrcClientLists(lists); + } + // The previous lists are destroyed here. Note that it's outside + // of the critical section protected by the locker. So this can + // take time if running on a separate thread. } } diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index dd60d896ad..f250d70f05 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -725,6 +725,14 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) { EXPECT_FALSE(dnsserv.hasAnswer()); } +void +installDataSrcClientLists(AuthSrv& server, + AuthSrv::DataSrcClientListsPtr lists) +{ + thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(lists); +} + void updateDatabase(AuthSrv& server, const char* params) { const ConstElementPtr config(Element::fromJSON("{" @@ -732,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) { " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } void @@ -749,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } void @@ -759,7 +767,7 @@ updateBuiltin(AuthSrv& server) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -957,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index b5e43edda2..36d13906b4 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -191,6 +191,14 @@ zoneChecks(AuthSrv& server) { find(Name("ns.test2.example"), RRType::AAAA())->code); } +void +installDataSrcClientLists(AuthSrv& server, + AuthSrv::DataSrcClientListsPtr lists) +{ + isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + server.swapDataSrcClientLists(lists); +} + void configureZones(AuthSrv& server) { ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in " @@ -210,7 +218,7 @@ configureZones(AuthSrv& server) { " \"cache-enable\": true" "}]}")); - configureDataSource(server, config); + installDataSrcClientLists(server, configureDataSource(server, config)); zoneChecks(server); } @@ -273,7 +281,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - configureDataSource(server_, config); + installDataSrcClientLists(server_, configureDataSource(server_, config)); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 329c5d11be..82067d30d8 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -60,14 +60,11 @@ private: typedef shared_ptr ListPtr; +// Forward declaration. We need precise definition of DatasrcConfigTest +// to complete this function. void testConfigureDataSource(DatasrcConfigTest& test, - const isc::data::ConstElementPtr& config) -{ - // We use the test fixture for the Server type. This makes it possible - // to easily fake all needed methods and look that they were called. - configureDataSourceGeneric(test, config); -} + const isc::data::ConstElementPtr& config); void datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, @@ -162,6 +159,17 @@ protected: mutable isc::util::thread::Mutex mutex_; }; +void +testConfigureDataSource(DatasrcConfigTest& test, + const isc::data::ConstElementPtr& config) +{ + // We use the test fixture for the Server type. This makes it possible + // to easily fake all needed methods and look that they were called. + shared_ptr > lists = + configureDataSourceGeneric(test, config); + test.swapDataSrcClientLists(lists); +} + // Push there a configuration with a single list. TEST_F(DatasrcConfigTest, createList) { initializeINList(); From ed2aceb01d516724a4f289343c4ad1c65b4322b4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:48:03 -0700 Subject: [PATCH 157/194] [2204] removed server function/template param from configureDataSourceGeneric. This is pure cleanup. This function already doesn't use the server. --- src/bin/auth/benchmarks/query_bench.cc | 2 -- src/bin/auth/datasrc_config.cc | 7 +++---- src/bin/auth/datasrc_config.h | 18 ++++++++---------- src/bin/auth/main.cc | 3 +-- src/bin/auth/tests/auth_srv_unittest.cc | 8 ++++---- src/bin/auth/tests/command_unittest.cc | 6 +++--- src/bin/auth/tests/datasrc_config_unittest.cc | 6 +++--- 7 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index 73f702baff..d732f945be 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -126,7 +126,6 @@ public: QueryBenchMark(queries, query_message, buffer) { configureDataSource( - *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"sqlite3\"," " \"params\": {" @@ -145,7 +144,6 @@ public: QueryBenchMark(queries, query_message, buffer) { configureDataSource( - *server_, Element::fromJSON("{\"IN\":" " [{\"type\": \"MasterFiles\"," " \"cache-enable\": true, " diff --git a/src/bin/auth/datasrc_config.cc b/src/bin/auth/datasrc_config.cc index bef6e57de7..62c3c7a6ba 100644 --- a/src/bin/auth/datasrc_config.cc +++ b/src/bin/auth/datasrc_config.cc @@ -19,8 +19,7 @@ // This is a trivial specialization for the commonly used version. // Defined in .cc to avoid accidental creation of multiple copies. AuthSrv::DataSrcClientListsPtr -configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config) -{ - return (configureDataSourceGeneric(server, config)); +configureDataSource(const isc::data::ConstElementPtr& config) { + return (configureDataSourceGeneric< + isc::datasrc::ConfigurableClientList>(config)); } diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 9a6f7ac64a..842d2ca927 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -25,25 +25,23 @@ #include #include -/// \brief Configure the authoritative server's data source lists +/// \brief Configure data source client lists /// -/// This will hook into the data_sources module configuration and it will -/// keep the local copy of data source clients in the list in the authoritative -/// server. +/// This will hook into the data_sources module configuration and it return +/// a new set (in the form of a shared pointer to map) of data source client +/// lists corresponding to the configuration. /// /// This function is templated. This is simply because of easier testing. /// You don't need to pay attention to it, use the configureDataSource /// specialization instead. /// -/// \param server It is the server to configure. /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. -template +/// \return A map from RR classes to configured lists. +template boost::shared_ptr > > // = ListMap below -configureDataSourceGeneric(Server& /*server*/, - const isc::data::ConstElementPtr& config) -{ +configureDataSourceGeneric(const isc::data::ConstElementPtr& config) { typedef boost::shared_ptr ListPtr; typedef std::map Map; typedef std::map ListMap; @@ -68,7 +66,7 @@ configureDataSourceGeneric(Server& /*server*/, /// \brief Concrete version of configureDataSource() for the /// use with authoritative server implementation. AuthSrv::DataSrcClientListsPtr -configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config); +configureDataSource(const isc::data::ConstElementPtr& config); #endif // DATASRC_CONFIG_H diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 2a17112ba4..d9b01314a2 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -103,11 +103,10 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, assert(config_session != NULL); *first_time = false; lists = configureDataSource( - *auth_server, config_session->getRemoteConfigValue("data_sources", "classes")); } else { - lists = configureDataSource(*server, config->get("classes")); + lists = configureDataSource(config->get("classes")); } // Replace the server's lists. By ignoring the return value we let the diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index f250d70f05..2668ecc79a 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -740,7 +740,7 @@ updateDatabase(AuthSrv& server, const char* params) { " \"type\": \"sqlite3\"," " \"params\": " + string(params) + "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } void @@ -757,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } void @@ -767,7 +767,7 @@ updateBuiltin(AuthSrv& server) { " \"type\": \"static\"," " \"params\": \"" + string(STATIC_DSRC_FILE) + "\"" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); } // Try giving the server a TSIG signed request and see it can anwer signed as @@ -965,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) { " \"params\": {}," " \"cache-enable\": true" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); // after successful configuration, we should have one (with empty zoneset). // The memory data source is empty, should return REFUSED rcode. diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 36d13906b4..48b0040067 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -218,7 +218,7 @@ configureZones(AuthSrv& server) { " \"cache-enable\": true" "}]}")); - installDataSrcClientLists(server, configureDataSource(server, config)); + installDataSrcClientLists(server, configureDataSource(config)); zoneChecks(server); } @@ -281,7 +281,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.org\"]" "}]}")); - installDataSrcClientLists(server_, configureDataSource(server_, config)); + installDataSrcClientLists(server_, configureDataSource(config)); { isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); @@ -345,7 +345,7 @@ TEST_F(AuthCommandTest, " \"cache-enable\": true," " \"cache-zones\": [\"example.com\"]" "}]}")); - EXPECT_THROW(configureDataSource(server_, config2), + EXPECT_THROW(configureDataSource(config2), ConfigurableClientList::ConfigurationError); result_ = execAuthServerCommand(server_, "loadzone", diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 82067d30d8..ba554fe859 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -163,10 +163,10 @@ void testConfigureDataSource(DatasrcConfigTest& test, const isc::data::ConstElementPtr& config) { - // We use the test fixture for the Server type. This makes it possible - // to easily fake all needed methods and look that they were called. + // We use customized (faked lists) for the List type. This makes it + // possible to easily look that they were called. shared_ptr > lists = - configureDataSourceGeneric(test, config); + configureDataSourceGeneric(config); test.swapDataSrcClientLists(lists); } From 52272e7986c23da3a0b6c4897f94245bfb408420 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 16:52:37 -0700 Subject: [PATCH 158/194] [2204] regression fix: make sure the datasrc config is installed to server. --- src/bin/auth/benchmarks/query_bench.cc | 34 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index d732f945be..a319555140 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -18,6 +18,8 @@ #include #include +#include + #include #include #include @@ -125,12 +127,15 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - configureDataSource( - Element::fromJSON("{\"IN\":" - " [{\"type\": \"sqlite3\"," - " \"params\": {" - " \"database_file\": \"" + - string(datasrc_file) + "\"}}]}")); + isc::util::thread::Mutex::Locker locker( + server_->getClientListMutex()); + server_->swapDataSrcClientLists( + configureDataSource( + Element::fromJSON("{\"IN\":" + " [{\"type\": \"sqlite3\"," + " \"params\": {" + " \"database_file\": \"" + + string(datasrc_file) + "\"}}]}"))); } }; @@ -143,13 +148,16 @@ public: OutputBuffer& buffer) : QueryBenchMark(queries, query_message, buffer) { - configureDataSource( - Element::fromJSON("{\"IN\":" - " [{\"type\": \"MasterFiles\"," - " \"cache-enable\": true, " - " \"params\": {\"" + - string(zone_origin) + "\": \"" + - string(zone_file) + "\"}}]}")); + isc::util::thread::Mutex::Locker locker( + server_->getClientListMutex()); + server_->swapDataSrcClientLists( + configureDataSource( + Element::fromJSON("{\"IN\":" + " [{\"type\": \"MasterFiles\"," + " \"cache-enable\": true, " + " \"params\": {\"" + + string(zone_origin) + "\": \"" + + string(zone_file) + "\"}}]}"))); } }; From a6cafe644f6a9413ba7f917e741b82cc292d3577 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 5 Oct 2012 17:04:28 -0700 Subject: [PATCH 159/194] [2204] cleanup: removed unused func, and renamed "ClientList" with "DataSrc". getClientListClasses() was removed. It's not used anymore. a few methods named with "ClientList" were renamed with adding "DataSrc". The resulting names are a bit long, but since there's no namespace qualification, simply saying "client" can be confusing (it could be interpreted to mean DNS clients, for example). --- src/bin/auth/auth_srv.cc | 30 +++-------- src/bin/auth/auth_srv.h | 21 +++----- src/bin/auth/benchmarks/query_bench.cc | 4 +- src/bin/auth/command.cc | 5 +- src/bin/auth/main.cc | 2 +- src/bin/auth/tests/auth_srv_unittest.cc | 32 ++++++----- src/bin/auth/tests/command_unittest.cc | 53 ++++++++++++------- src/bin/auth/tests/datasrc_config_unittest.cc | 2 +- 8 files changed, 74 insertions(+), 75 deletions(-) diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc index 1102ae3ed3..e73eb528db 100644 --- a/src/bin/auth/auth_srv.cc +++ b/src/bin/auth/auth_srv.cc @@ -271,7 +271,9 @@ public: /// The data source client list AuthSrv::DataSrcClientListsPtr datasrc_client_lists_; - shared_ptr getClientList(const RRClass& rrclass) { + shared_ptr getDataSrcClientList( + const RRClass& rrclass) + { // TODO: Debug-build only check if (!mutex_.locked()) { isc_throw(isc::Unexpected, "Not locked!"); @@ -646,13 +648,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message, } // Lock the client lists and keep them under the lock until the processing // and rendering is done (this is the same mutex as from - // AuthSrv::getClientListMutex()). + // AuthSrv::getDataSrcClientListMutex()). isc::util::thread::Mutex::Locker locker(mutex_); try { const ConstQuestionPtr question = *message.beginQuestion(); const shared_ptr - list(getClientList(question->getClass())); + list(getDataSrcClientList(question->getClass())); if (list) { const RRType& qtype = question->getType(); const Name& qname = question->getName(); @@ -942,28 +944,12 @@ AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) { } shared_ptr -AuthSrv::getClientList(const RRClass& rrclass) { - return (impl_->getClientList(rrclass)); -} - -vector -AuthSrv::getClientListClasses() const { - // TODO: Debug-build only check - if (!impl_->mutex_.locked()) { - isc_throw(isc::Unexpected, "Not locked"); - } - - vector result; - for (std::map >:: - const_iterator it(impl_->datasrc_client_lists_->begin()); - it != impl_->datasrc_client_lists_->end(); ++it) { - result.push_back(it->first); - } - return (result); +AuthSrv::getDataSrcClientList(const RRClass& rrclass) { + return (impl_->getDataSrcClientList(rrclass)); } util::thread::Mutex& -AuthSrv::getClientListMutex() const { +AuthSrv::getDataSrcClientListMutex() const { return (impl_->mutex_); } diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h index f1e73c1158..0849bdd1d0 100644 --- a/src/bin/auth/auth_srv.h +++ b/src/bin/auth/auth_srv.h @@ -302,6 +302,7 @@ public: /// If there was no forwarder yet, this method does nothing. void destroyDDNSForwarder(); + /// \brief Shortcut typedef used for swapDataSrcClientLists(). typedef boost::shared_ptr > > @@ -318,7 +319,7 @@ public: /// /// This method is intended to be used by a separate method to update /// the data source configuration "at once". The caller must hold - /// a lock for the mutex object returned by \c getClientListMutex() + /// a lock for the mutex object returned by \c getDataSrcClientListMutex() /// before calling this method. /// /// The ownership of the returned pointer is transferred to the caller. @@ -340,13 +341,7 @@ public: /// \param rrclass The class for which to get the list. /// \return The list, or NULL if no list is set for the class. boost::shared_ptr - getClientList(const isc::dns::RRClass& rrclass); - - /// \brief Returns a list of classes that have a client list. - /// - /// \return List of classes for which a non-NULL client list - /// has been set by setClientList. - std::vector getClientListClasses() const; + getDataSrcClientList(const isc::dns::RRClass& rrclass); /// \brief Return a mutex for the client lists. /// @@ -357,9 +352,9 @@ public: /// is correct: /// \code /// { - /// Mutex::Locker locker(auth->getClientListMutex()); + /// Mutex::Locker locker(auth->getDataSrcClientListMutex()); /// boost::shared_ptr - /// list(auth->getClientList(RRClass::IN())); + /// list(auth->getDataSrcClientList(RRClass::IN())); /// // Do some processing here /// } /// \endcode @@ -368,8 +363,8 @@ public: /// \code /// boost::shared_ptr list; /// { - /// Mutex::Locker locker(auth->getClientListMutex()); - /// list = auth->getClientList(RRClass::IN())); + /// Mutex::Locker locker(auth->getDataSrcClientListMutex()); + /// list = auth->getDataSrcClientList(RRClass::IN())); /// } /// // Do some processing here /// \endcode @@ -378,7 +373,7 @@ public: /// (lock) the mutex. It's because locking of the mutex is not really /// a modification of the server object and it is needed to protect the /// lists even on read-only operations. - isc::util::thread::Mutex& getClientListMutex() const; + isc::util::thread::Mutex& getDataSrcClientListMutex() const; /// \brief Sets the timeout for incoming TCP connections /// diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc index a319555140..37976a3358 100644 --- a/src/bin/auth/benchmarks/query_bench.cc +++ b/src/bin/auth/benchmarks/query_bench.cc @@ -128,7 +128,7 @@ public: QueryBenchMark(queries, query_message, buffer) { isc::util::thread::Mutex::Locker locker( - server_->getClientListMutex()); + server_->getDataSrcClientListMutex()); server_->swapDataSrcClientLists( configureDataSource( Element::fromJSON("{\"IN\":" @@ -149,7 +149,7 @@ public: QueryBenchMark(queries, query_message, buffer) { isc::util::thread::Mutex::Locker locker( - server_->getClientListMutex()); + server_->getDataSrcClientListMutex()); server_->swapDataSrcClientLists( configureDataSource( Element::fromJSON("{\"IN\":" diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc index 448a31bc8e..d26cf091de 100644 --- a/src/bin/auth/command.cc +++ b/src/bin/auth/command.cc @@ -192,9 +192,10 @@ public: // We're going to work with the client lists. They may be used // from a different thread too, protect them. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); const boost::shared_ptr - list(server.getClientList(zone_class)); + list(server.getDataSrcClientList(zone_class)); if (!list) { isc_throw(AuthCommandError, "There's no client list for " diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index d9b01314a2..1e3ca9c3de 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -114,7 +114,7 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, // swap. { isc::util::thread::Mutex::Locker locker( - server->getClientListMutex()); + server->getDataSrcClientListMutex()); lists = server->swapDataSrcClientLists(lists); } // The previous lists are destroyed here. Note that it's outside diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc index 2668ecc79a..e248e81483 100644 --- a/src/bin/auth/tests/auth_srv_unittest.cc +++ b/src/bin/auth/tests/auth_srv_unittest.cc @@ -729,7 +729,7 @@ void installDataSrcClientLists(AuthSrv& server, AuthSrv::DataSrcClientListsPtr lists) { - thread::Mutex::Locker locker(server.getClientListMutex()); + thread::Mutex::Locker locker(server.getDataSrcClientListMutex()); server.swapDataSrcClientLists(lists); } @@ -1439,10 +1439,11 @@ TEST_F(AuthSrvTest, // Set real inmem client to proxy updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); boost::shared_ptr - list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER, - false)); + list(new FakeList(server.getDataSrcClientList(RRClass::IN()), + THROW_NEVER, false)); AuthSrv::DataSrcClientListsPtr lists(new std::map); lists->insert(pair(RRClass::IN(), list)); server.swapDataSrcClientLists(lists); @@ -1469,10 +1470,11 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception, { updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE); - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); boost::shared_ptr - list(new FakeList(server.getClientList(RRClass::IN()), throw_when, - isc_exception, rrset)); + list(new FakeList(server.getDataSrcClientList(RRClass::IN()), + throw_when, isc_exception, rrset)); AuthSrv::DataSrcClientListsPtr lists(new std::map); lists->insert(pair(RRClass::IN(), list)); server.swapDataSrcClientLists(lists); @@ -1787,7 +1789,8 @@ TEST_F(AuthSrvTest, clientList) { // We need to lock the mutex to make the (get|set)ClientList happy. // There's a debug-build only check in them to make sure everything // locks them and we call them directly here. - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); AuthSrv::DataSrcClientListsPtr lists; // initially empty @@ -1804,8 +1807,8 @@ TEST_F(AuthSrvTest, clientList) { server.swapDataSrcClientLists(lists); // And the lists can be retrieved. - EXPECT_EQ(list, server.getClientList(RRClass::IN())); - EXPECT_EQ(list2, server.getClientList(RRClass::CH())); + EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN())); + EXPECT_EQ(list2, server.getDataSrcClientList(RRClass::CH())); // Replace the lists with new lists containing only one list. lists.reset(new std::map); @@ -1817,19 +1820,20 @@ TEST_F(AuthSrvTest, clientList) { EXPECT_EQ(2, lists->size()); // The CH list really got deleted. - EXPECT_EQ(list, server.getClientList(RRClass::IN())); - EXPECT_FALSE(server.getClientList(RRClass::CH())); + EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN())); + EXPECT_FALSE(server.getDataSrcClientList(RRClass::CH())); } // We just test the mutex can be locked (exactly once). TEST_F(AuthSrvTest, mutex) { - isc::util::thread::Mutex::Locker l1(server.getClientListMutex()); + isc::util::thread::Mutex::Locker l1(server.getDataSrcClientListMutex()); // TODO: Once we have non-debug build, this one will not work, since // we currently use the fact that we can't lock twice from the same // thread. In the non-debug mode, this would deadlock. // Skip then. EXPECT_THROW({ - isc::util::thread::Mutex::Locker l2(server.getClientListMutex()); + isc::util::thread::Mutex::Locker l2( + server.getDataSrcClientListMutex()); }, isc::InvalidOperation); } diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc index 48b0040067..8aa2322151 100644 --- a/src/bin/auth/tests/command_unittest.cc +++ b/src/bin/auth/tests/command_unittest.cc @@ -176,17 +176,18 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) { // zones, and checks the zones are correctly loaded. void zoneChecks(AuthSrv& server) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::AAAA())->code); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::AAAA())->code); } @@ -195,7 +196,8 @@ void installDataSrcClientLists(AuthSrv& server, AuthSrv::DataSrcClientListsPtr lists) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); server.swapDataSrcClientLists(lists); } @@ -225,20 +227,22 @@ configureZones(AuthSrv& server) { void newZoneChecks(AuthSrv& server) { - isc::util::thread::Mutex::Locker locker(server.getClientListMutex()); - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + isc::util::thread::Mutex::Locker locker( + server.getDataSrcClientListMutex()); + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::A())->code); // now test1.example should have ns/AAAA - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test1.example")).finder_-> find(Name("ns.test1.example"), RRType::AAAA())->code); // test2.example shouldn't change - EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::A())->code); - EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXRRSET, + server.getDataSrcClientList(RRClass::IN())-> find(Name("ns.test2.example")).finder_-> find(Name("ns.test2.example"), RRType::AAAA())->code); } @@ -284,9 +288,11 @@ TEST_F(AuthCommandTest, installDataSrcClientLists(server_, configureDataSource(config)); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // Check that the A record at www.example.org does not exist - EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXDOMAIN, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); @@ -306,7 +312,8 @@ TEST_F(AuthCommandTest, sql_updater->addRRset(*rrset); sql_updater->commit(); - EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::NXDOMAIN, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); } @@ -318,9 +325,11 @@ TEST_F(AuthCommandTest, checkAnswer(0, "Successful load"); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // And now it should be present too. - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("www.example.org"), RRType::A())->code); } @@ -331,9 +340,11 @@ TEST_F(AuthCommandTest, checkAnswer(1, "example.com"); { - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // The previous zone is not hurt in any way - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("example.org"), RRType::SOA())->code); } @@ -352,9 +363,11 @@ TEST_F(AuthCommandTest, Element::fromJSON("{\"origin\": \"example.com\"}")); checkAnswer(1, "Unreadable"); - isc::util::thread::Mutex::Locker locker(server_.getClientListMutex()); + isc::util::thread::Mutex::Locker locker( + server_.getDataSrcClientListMutex()); // The previous zone is not hurt in any way - EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())-> + EXPECT_EQ(ZoneFinder::SUCCESS, + server_.getDataSrcClientList(RRClass::IN())-> find(Name("example.org")).finder_-> find(Name("example.org"), RRType::SOA())->code); } diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index ba554fe859..97d89bf2bb 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -79,7 +79,7 @@ datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&, class DatasrcConfigTest : public ::testing::Test { public: // These pretend to be the server - isc::util::thread::Mutex& getClientListMutex() const { + isc::util::thread::Mutex& getDataSrcClientListMutex() const { return (mutex_); } void swapDataSrcClientLists(shared_ptr > From 4d27a4533c2cbdcb3c9e74c12d03024733e79895 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 16:56:10 -0700 Subject: [PATCH 160/194] [2204] updated comments about releasing old data source client lists the previous one was not really accurate and was confusing. --- src/bin/auth/main.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index 1e3ca9c3de..99080662ff 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -109,17 +109,16 @@ datasrcConfigHandler(AuthSrv* server, bool* first_time, lists = configureDataSource(config->get("classes")); } - // Replace the server's lists. By ignoring the return value we let the - // old lists be destroyed. Lock will be released immediately after the - // swap. + // Replace the server's lists. The returned lists will be stored + // in a local variable 'lists', and will be destroyed outside of + // the temporary block for the lock scope. That way we can minimize + // the range of the critical section. { isc::util::thread::Mutex::Locker locker( server->getDataSrcClientListMutex()); lists = server->swapDataSrcClientLists(lists); } - // The previous lists are destroyed here. Note that it's outside - // of the critical section protected by the locker. So this can - // take time if running on a separate thread. + // The previous lists are destroyed here. } } From 5703d87a9e031873305da04b88fb49ac5172c3c1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 17:27:23 -0700 Subject: [PATCH 161/194] [2204] updated test comments without using "rollback". as the new code actually doesn't do rollback operation internally, even though the observable effect is the same. --- src/bin/auth/tests/datasrc_config_unittest.cc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc index 97d89bf2bb..877f92166f 100644 --- a/src/bin/auth/tests/datasrc_config_unittest.cc +++ b/src/bin/auth/tests/datasrc_config_unittest.cc @@ -244,10 +244,11 @@ TEST_F(DatasrcConfigTest, updateDelete) { EXPECT_TRUE(lists_.empty()); } -// Check that we can rollback an addition if something else fails -TEST_F(DatasrcConfigTest, rollbackAddition) { +// Check that broken new configuration doesn't break the running configuration. +TEST_F(DatasrcConfigTest, brokenConfigForAdd) { initializeINList(); - // The configuration is wrong. However, the CH one will get done first. + // The configuration is wrong. However, the CH one will be handled + // without an error first. const ElementPtr config(buildConfig("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"xxx\"}]}")); @@ -263,8 +264,9 @@ TEST_F(DatasrcConfigTest, rollbackAddition) { EXPECT_FALSE(lists_[RRClass::CH()]); } -// Check that we can rollback a deletion if something else fails -TEST_F(DatasrcConfigTest, rollbackDeletion) { +// Similar to the previous one, but the broken config would delete part of +// the running config. +TEST_F(DatasrcConfigTest, brokenConfigForDelete) { initializeINList(); // Put the CH there const ElementPtr @@ -273,27 +275,25 @@ TEST_F(DatasrcConfigTest, rollbackDeletion) { testConfigureDataSource(*this, config1); const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}")); - // This would delete CH. However, the IN one fails. - // As the deletions happen after the additions/settings - // and there's no known way to cause an exception during the - // deletions, it is not a true rollback, but the result should - // be the same. + // This would delete CH. However, the new config is broken, so it won't + // actually apply. EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError); EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf()); EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf()); } -// Check that we can roll back configuration change if something -// fails later on. -TEST_F(DatasrcConfigTest, rollbackConfiguration) { +// Similar to the previous cases, but the broken config would modify the +// running config of one of the classes. +TEST_F(DatasrcConfigTest, brokenConfigForModify) { initializeINList(); // Put the CH there const ElementPtr config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], " "\"CH\": [{\"type\": \"xxx\"}]}")); testConfigureDataSource(*this, config1); - // Now, the CH happens first. But nevertheless, it should be - // restored to the previoeus version. + // Now, the CH change will be handled first without an error, then + // the change to the IN class will fail, and the none of the changes + // will apply. const ElementPtr config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], " "\"CH\": [{\"type\": \"yyy\"}]}")); From 0fc7d94b29ecc8a267d32047636d6525a3133612 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 17:29:15 -0700 Subject: [PATCH 162/194] [2204] grammar fix in documentation --- src/bin/auth/datasrc_config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 842d2ca927..02e4e5c45e 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -27,9 +27,9 @@ /// \brief Configure data source client lists /// -/// This will hook into the data_sources module configuration and it return -/// a new set (in the form of a shared pointer to map) of data source client -/// lists corresponding to the configuration. +/// This will hook into the data_sources module configuration and it will +/// return a new set (in the form of a shared pointer to map) of data source +/// client lists corresponding to the configuration. /// /// This function is templated. This is simply because of easier testing. /// You don't need to pay attention to it, use the configureDataSource From 64bcd98477cfc62851a21aa64ced28d0cff92201 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:26:20 -0700 Subject: [PATCH 163/194] [2204] added notes about future extensions of incremental datasrc config update --- src/bin/auth/datasrc_config.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 02e4e5c45e..678b87f556 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -35,6 +35,16 @@ /// You don't need to pay attention to it, use the configureDataSource /// specialization instead. /// +/// \note In future we may want to make the reconfiguration more efficient +/// by only creating newly configured data and just moving the rest from +/// the running configuration if they are used in the new configuration +/// without any parameter change. We could probably do it by passing +/// the old lists in addition to the new config, but further details are +/// still to be defined yet. It will surely require changes in the +/// data source library, too. So, right now, we don't introduce the +/// possibility in the function interface. If and when we decide to introduce +/// the optimization, we'll extend the interface. +/// /// \param config The configuration value to parse. It is in the form /// as an update from the config manager. /// \return A map from RR classes to configured lists. From c76f74659b3bc90b28f4af8940bd47193b92a28a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:28:08 -0700 Subject: [PATCH 164/194] [2204] removed an obsolete comment that only made sense for the previous code. --- src/bin/auth/datasrc_config.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h index 678b87f556..5707c6c8a0 100644 --- a/src/bin/auth/datasrc_config.h +++ b/src/bin/auth/datasrc_config.h @@ -58,10 +58,8 @@ configureDataSourceGeneric(const isc::data::ConstElementPtr& config) { boost::shared_ptr new_lists(new ListMap); - // Get the configuration and current state. - const Map& map(config->mapValue()); - // Go through the configuration and create corresponding list. + const Map& map(config->mapValue()); for (Map::const_iterator it(map.begin()); it != map.end(); ++it) { const isc::dns::RRClass rrclass(it->first); ListPtr list(new List(rrclass)); From 214377d8511c7f8fdb8b8c8052bcb301aad1d06f Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:27:15 -0700 Subject: [PATCH 165/194] [2244] added a test case that reproduces one aspect of the problem. the configurator should be able to delete a component to be removed even if the it's failing (unexpectedly). The current implementation doesn't handle that condition correctly. --- .../python/isc/bind10/tests/component_test.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 3f26870452..db7707e21b 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -872,12 +872,13 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): 'priority': 6, 'special': 'test', 'process': 'additional', - 'kind': 'needed' + 'kind': 'dispensable' # need to be dispensable so it could restart } self.log = [] plan = configurator._build_plan(self.__build_components(self.__core), bigger) - self.assertEqual([('additional', 'init'), ('additional', 'needed')], + self.assertEqual([('additional', 'init'), + ('additional', 'dispensable')], self.log) self.assertEqual(1, len(plan)) self.assertTrue('component' in plan[0]) @@ -888,15 +889,27 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): # Now remove the one component again # We run the plan so the component is wired into internal structures configurator._run_plan(plan) - self.log = [] - plan = configurator._build_plan(self.__build_components(bigger), - self.__core) - self.assertEqual([], self.log) - self.assertEqual([{ - 'command': 'stop', - 'name': 'additional', - 'component': component - }], plan) + # We should have the 'additional' component in the configurator. + self.assertTrue('additional' in configurator._components) + for count in [0, 1]: # repeat two times, see below + self.log = [] + plan = configurator._build_plan(self.__build_components(bigger), + self.__core) + self.assertEqual([], self.log) + self.assertEqual([{ + 'command': 'stop', + 'name': 'additional', + 'component': component + }], plan) + + if count is 0: + # We then emulate unexpected failure of the component (but + # before it restarts). It shouldn't confuse the + # configurator in the second phase of the test + component.failed(0) + # Run the plan, confirm the specified component is gone. + configurator._run_plan(plan) + self.assertFalse('additional' in configurator._components) # We want to switch a component. So, prepare the configurator so it # holds one From a969883cd42fef3c3782fea117c156b76014df25 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:37:09 -0700 Subject: [PATCH 166/194] [2244] introduced a new "Failed" state of Component. --- src/lib/python/isc/bind10/component.py | 17 ++++++++++++++--- .../python/isc/bind10/tests/component_test.py | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 9c29ace589..77196b02e9 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -45,6 +45,7 @@ COMPONENT_RESTART_DELAY = 10 STATE_DEAD = 'dead' STATE_STOPPED = 'stopped' +STATE_FAILED = 'failed' STATE_RUNNING = 'running' def get_signame(signal_number): @@ -68,6 +69,7 @@ class BaseComponent: explicitly). - Running - after start() was called, it started successfully and is now running. + - Failed - the component failed (crashed) and is waiting for a restart - Dead - it failed and can not be resurrected. Init @@ -79,11 +81,11 @@ class BaseComponent: | | | |failure | failed() | | | | - v | | + v | | start()/restart() +<-----------+ | | | | kind == dispensable or kind|== needed and failed late - +-----------------------------+ + +-----------------------> Failed | | kind == core or kind == needed and it failed too soon v @@ -236,7 +238,7 @@ class BaseComponent: exit_str) if not self.running(): raise ValueError("Can't fail component that isn't running") - self.__state = STATE_STOPPED + self.__state = STATE_FAILED self._failed_internal() # If it is a core component or the needed component failed to start # (including it stopped really soon) @@ -296,6 +298,15 @@ class BaseComponent: """ return self.__state == STATE_RUNNING + def is_failed(self): + """Informs if the component has failed and is waiting for a restart. + + Unlike the case of running(), if this returns True it always means + the corresponding process has died and not yet restarted. + + """ + return self.__state == STATE_FAILED + def _start_internal(self): """ This method does the actual starting of a process. You need to override diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index db7707e21b..1cfc30f8b4 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -192,6 +192,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) self.assertRaises(ValueError, component.failed, 1) @@ -205,6 +206,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertTrue(component.running()) + self.assertFalse(component.is_failed()) def __check_dead(self, component): """ @@ -216,6 +218,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) # Nor started @@ -235,6 +238,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertTrue(component.running()) + self.assertFalse(component.is_failed()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -247,6 +251,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertFalse(component.running()) + self.assertTrue(component.is_failed()) def __do_start_stop(self, kind): """ @@ -271,6 +276,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.running()) + self.assertFalse(component.is_failed()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) # Or failed From 9f77979fd649eaa9ec1cc3e9ce5b80059e2df3a5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:38:43 -0700 Subject: [PATCH 167/194] [2244] make sure configurtor's build_plan() handles 'failed' component. this fixes the test case introduced at the beginning of the branch. --- src/lib/python/isc/bind10/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 77196b02e9..0b822a83e2 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -602,7 +602,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.running(): + if component.running() or component.is_failed(): plan.append({ 'command': STOP_CMD, 'component': component, From 5097c34950518370eabcade6cb71ba952bfeefd4 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 17:55:57 -0700 Subject: [PATCH 168/194] [2244] added a simple has_component() method to configurtor. should be pretty trivial, so added a couple of test cases as part of other tests. --- src/lib/python/isc/bind10/component.py | 4 ++++ src/lib/python/isc/bind10/tests/component_test.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 0b822a83e2..57f9428586 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -571,6 +571,10 @@ class Configurator: self._running = False self.__reconfigure_internal(self._components, {}) + def has_component(self, component): + '''Return if a specified component is configured.''' + return component in map(lambda x: x[1], self._components.values()) + def reconfigure(self, configuration): """ Changes configuration from the current one to the provided. It diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 1cfc30f8b4..d8a89d578a 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -896,7 +896,7 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): # We run the plan so the component is wired into internal structures configurator._run_plan(plan) # We should have the 'additional' component in the configurator. - self.assertTrue('additional' in configurator._components) + self.assertTrue(configurator.has_component(component)) for count in [0, 1]: # repeat two times, see below self.log = [] plan = configurator._build_plan(self.__build_components(bigger), @@ -915,6 +915,8 @@ class ConfiguratorTest(BossUtils, unittest.TestCase): component.failed(0) # Run the plan, confirm the specified component is gone. configurator._run_plan(plan) + self.assertFalse(configurator.has_component(component)) + # There shouldn't be any other object that has the same name self.assertFalse('additional' in configurator._components) # We want to switch a component. So, prepare the configurator so it From e3079eb5ceeeca83507f6dfbf712b374661c3935 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 20:53:37 -0700 Subject: [PATCH 169/194] [2244] added comment about has_comonent as it may not be very obvious. --- src/lib/python/isc/bind10/component.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 57f9428586..aad3e694df 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -573,6 +573,9 @@ class Configurator: def has_component(self, component): '''Return if a specified component is configured.''' + # Values of self._components are tuples of (name, component). + # Extract the components of the tuples and see if the given one + # is included. return component in map(lambda x: x[1], self._components.values()) def reconfigure(self, configuration): From 6207e0ccf7924b0976a21ffb7f901d946a273c41 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 21:08:55 -0700 Subject: [PATCH 170/194] [2244] a minor editorial fix to the previous comment update --- src/lib/python/isc/bind10/component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index aad3e694df..fbb9e0e1bb 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -573,7 +573,7 @@ class Configurator: def has_component(self, component): '''Return if a specified component is configured.''' - # Values of self._components are tuples of (name, component). + # Values of self._components are tuples of (config, component). # Extract the components of the tuples and see if the given one # is included. return component in map(lambda x: x[1], self._components.values()) From 1645116db47739bc194d10db67a3a085b1ac551d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:10:40 -0700 Subject: [PATCH 171/194] [2244] make sure restarting components only when they are in config. this should fix the main problem of this ticket. --- src/bin/bind10/bind10_messages.mes | 10 ++++++++ src/bin/bind10/bind10_src.py.in | 7 +++++- src/bin/bind10/tests/bind10_test.py.in | 35 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index c7515833e8..d18df9e926 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -295,3 +295,13 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. + +% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 +The boss module tried to restart a component after it failed (crashed) +unexpectedly, but the boss then found that the component had been removed +from its local configuration of components to run. This is an unusal +situation but can happen if the administrator removes the component from +the configuration after the component's crash and before the restart time. +The boss module simply skipped restarting that module, and the whole syste +went back to the expected state (except that the crash itself is likely +to be a bug). diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index bb2edaffaa..4567229ece 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -771,7 +771,12 @@ class BoB: next_restart_time = None now = time.time() for component in self.components_to_restart: - if not component.restart(now): + # If the component was removed from the configurator between since + # scheduled to restart, just ignore it. The object will just be + # dropped here. + if not self._component_configurator.has_component(component): + logger.info(BIND10_RESTART_COMPONENT_SKIPPED, component.name()) + elif not component.restart(now): still_dead.append(component) if next_restart_time is None or\ next_restart_time > component.get_restart_time(): diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 9a40e42a84..0b119606e0 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -929,7 +929,14 @@ class MockComponent: self.name = lambda: name self.pid = lambda: pid self.address = lambda: address + self.restarted = False + def get_restart_time(self): + return 0 # arbitrary dummy value + + def restart(self, now): + self.restarted = True + return True class TestBossCmd(unittest.TestCase): def test_ping(self): @@ -1266,6 +1273,34 @@ class TestBossComponents(unittest.TestCase): bob.start_all_components() self.__check_extended(self.__param) + def __setup_restart(self, bob, component): + '''Common procedure for restarting a component used below.''' + bob.components_to_restart = { component } + component.restarted = False + bob.restart_processes() + + def test_restart_processes(self): + '''Check some behavior on restarting processes.''' + bob = MockBob() + bob.runnable = True + component = MockComponent('test', 53) + + # A component to be restarted will actually be restarted iff it's + # in the configurator's configuration. + # We bruteforce the configurator internal below; ugly, but the easiest + # way for the test. + bob._component_configurator._components['test'] = (None, component) + self.__setup_restart(bob, component) + self.assertTrue(component.restarted) + self.assertFalse(component in bob.components_to_restart) + + # Remove the component from the configuration. It won't be restarted + # even if scheduled, nor will remain in the to-be-restarted list. + del bob._component_configurator._components['test'] + self.__setup_restart(bob, component) + self.assertFalse(component.restarted) + self.assertFalse(component in bob.components_to_restart) + class SocketSrvTest(unittest.TestCase): """ This tests some methods of boss related to the unix domain sockets used From 35bdd068cf6192eba30d94d6a7a73751be4220ff Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:11:45 -0700 Subject: [PATCH 172/194] [2244] cleanup: reordered log message file --- src/bin/bind10/bind10_messages.mes | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index d18df9e926..dd36ef96e3 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -141,6 +141,16 @@ it now. The new configuration is printed. % BIND10_RECEIVED_SIGNAL received signal %1 The boss module received the given signal. +% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 +The boss module tried to restart a component after it failed (crashed) +unexpectedly, but the boss then found that the component had been removed +from its local configuration of components to run. This is an unusal +situation but can happen if the administrator removes the component from +the configuration after the component's crash and before the restart time. +The boss module simply skipped restarting that module, and the whole syste +went back to the expected state (except that the crash itself is likely +to be a bug). + % BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2) The given process has been restarted successfully, and is now running with the given process id. @@ -295,13 +305,3 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. - -% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1 -The boss module tried to restart a component after it failed (crashed) -unexpectedly, but the boss then found that the component had been removed -from its local configuration of components to run. This is an unusal -situation but can happen if the administrator removes the component from -the configuration after the component's crash and before the restart time. -The boss module simply skipped restarting that module, and the whole syste -went back to the expected state (except that the crash itself is likely -to be a bug). From dda8b3fa72979fd24d5c3bdca6709408b5bafedb Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 4 Oct 2012 22:19:27 -0700 Subject: [PATCH 173/194] [2244] rename Componet.running() is_running() for consistency. this branch introduced is_failed() (and failed() was already defined for a different a purpose), so for consistency it would be better to name the running version is_xxx too. --- src/lib/python/isc/bind10/component.py | 14 ++++++------ .../python/isc/bind10/tests/component_test.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index fbb9e0e1bb..6b96b943e9 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -165,7 +165,7 @@ class BaseComponent: """ if self.__state == STATE_DEAD: raise ValueError("Can't resurrect already dead component") - if self.running(): + if self.is_running(): raise ValueError("Can't start already running component") logger.info(BIND10_COMPONENT_START, self.name()) self.__state = STATE_RUNNING @@ -190,7 +190,7 @@ class BaseComponent: """ # This is not tested. It talks with the outher world, which is out # of scope of unittests. - if not self.running(): + if not self.is_running(): raise ValueError("Can't stop a component which is not running") logger.info(BIND10_COMPONENT_STOP, self.name()) self.__state = STATE_STOPPED @@ -236,7 +236,7 @@ class BaseComponent: logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(), exit_str) - if not self.running(): + if not self.is_running(): raise ValueError("Can't fail component that isn't running") self.__state = STATE_FAILED self._failed_internal() @@ -286,7 +286,7 @@ class BaseComponent: else: return False - def running(self): + def is_running(self): """ Informs if the component is currently running. It assumes the failed is called whenever the component really fails and there might be some @@ -301,7 +301,7 @@ class BaseComponent: def is_failed(self): """Informs if the component has failed and is waiting for a restart. - Unlike the case of running(), if this returns True it always means + Unlike the case of is_running(), if this returns True it always means the corresponding process has died and not yet restarted. """ @@ -609,7 +609,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.running() or component.is_failed(): + if component.is_running() or component.is_failed(): plan.append({ 'command': STOP_CMD, 'component': component, @@ -692,7 +692,7 @@ class Configurator: self._components[task['name']] = (task['config'], component) elif command == STOP_CMD: - if component.running(): + if component.is_running(): component.stop() del self._components[task['name']] else: diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index d8a89d578a..339a9399ca 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -191,7 +191,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__start_called) self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) @@ -205,7 +205,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertFalse(component.is_failed()) def __check_dead(self, component): @@ -217,7 +217,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) @@ -237,7 +237,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertFalse(component.is_failed()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -250,7 +250,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertTrue(component.is_failed()) def __do_start_stop(self, kind): @@ -275,7 +275,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__start_called) self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertFalse(component.is_failed()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) @@ -559,10 +559,10 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertIsNone(component.pid()) self.assertEqual(['hello'], component._params) self.assertEqual('Address', component._address) - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertEqual({}, self.__registered_processes) component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) # Some versions of unittest miss assertIsInstance self.assertTrue(isinstance(component._procinfo, TestProcInfo)) self.assertEqual(42, component.pid()) @@ -586,11 +586,11 @@ class ComponentTests(BossUtils, unittest.TestCase): """ component = Component('component', self, 'needed', 'Address') component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) self.assertEqual('component', self.__start_simple_params) component.pid = lambda: 42 component.stop() - self.assertFalse(component.running()) + self.assertFalse(component.is_running()) self.assertEqual(('component', 'Address', 42), self.__stop_process_params) @@ -615,7 +615,7 @@ class ComponentTests(BossUtils, unittest.TestCase): component = Component('component', self, 'needed', 'Address', [], ProcInfo) component.start() - self.assertTrue(component.running()) + self.assertTrue(component.is_running()) component.kill() self.assertTrue(process.terminated) self.assertFalse(process.killed) From ff2fabced6d1b9e88a2c6c2da66573ad96ae4e00 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 15:49:24 -0700 Subject: [PATCH 174/194] [2244] fixed a typo in a log message. --- src/bin/bind10/bind10_messages.mes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index dd36ef96e3..2f48325498 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -147,7 +147,7 @@ unexpectedly, but the boss then found that the component had been removed from its local configuration of components to run. This is an unusal situation but can happen if the administrator removes the component from the configuration after the component's crash and before the restart time. -The boss module simply skipped restarting that module, and the whole syste +The boss module simply skipped restarting that module, and the whole system went back to the expected state (except that the crash itself is likely to be a bug). From ed72f2bf21f13a214106d222f6537a43f6f3318d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 8 Oct 2012 16:14:16 -0700 Subject: [PATCH 175/194] [2244] renamed BaseComponent.is_failed() to is_restarting(). so it sounds more natural as an English term. --- src/lib/python/isc/bind10/component.py | 14 +++++++------- src/lib/python/isc/bind10/tests/component_test.py | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py index 6b96b943e9..1f7006cb57 100644 --- a/src/lib/python/isc/bind10/component.py +++ b/src/lib/python/isc/bind10/component.py @@ -45,7 +45,7 @@ COMPONENT_RESTART_DELAY = 10 STATE_DEAD = 'dead' STATE_STOPPED = 'stopped' -STATE_FAILED = 'failed' +STATE_RESTARTING = 'restarting' STATE_RUNNING = 'running' def get_signame(signal_number): @@ -69,7 +69,7 @@ class BaseComponent: explicitly). - Running - after start() was called, it started successfully and is now running. - - Failed - the component failed (crashed) and is waiting for a restart + - Restarting - the component failed (crashed) and is waiting for a restart - Dead - it failed and can not be resurrected. Init @@ -85,7 +85,7 @@ class BaseComponent: +<-----------+ | | | | kind == dispensable or kind|== needed and failed late - +-----------------------> Failed + +-----------------------> Restarting | | kind == core or kind == needed and it failed too soon v @@ -238,7 +238,7 @@ class BaseComponent: exit_str) if not self.is_running(): raise ValueError("Can't fail component that isn't running") - self.__state = STATE_FAILED + self.__state = STATE_RESTARTING # tentatively set, maybe changed to DEAD self._failed_internal() # If it is a core component or the needed component failed to start # (including it stopped really soon) @@ -298,14 +298,14 @@ class BaseComponent: """ return self.__state == STATE_RUNNING - def is_failed(self): + def is_restarting(self): """Informs if the component has failed and is waiting for a restart. Unlike the case of is_running(), if this returns True it always means the corresponding process has died and not yet restarted. """ - return self.__state == STATE_FAILED + return self.__state == STATE_RESTARTING def _start_internal(self): """ @@ -609,7 +609,7 @@ class Configurator: for cname in old.keys(): if cname not in new: component = self._components[cname][1] - if component.is_running() or component.is_failed(): + if component.is_running() or component.is_restarting(): plan.append({ 'command': STOP_CMD, 'component': component, diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py index 339a9399ca..18efea7451 100644 --- a/src/lib/python/isc/bind10/tests/component_test.py +++ b/src/lib/python/isc/bind10/tests/component_test.py @@ -192,7 +192,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # We can't stop or fail the component yet self.assertRaises(ValueError, component.stop) self.assertRaises(ValueError, component.failed, 1) @@ -206,7 +206,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertFalse(self.__failed_called) self.assertTrue(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) def __check_dead(self, component): """ @@ -218,7 +218,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__failed_called) self.assertEqual(1, self._exitcode) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Surely it can't be stopped when already dead self.assertRaises(ValueError, component.stop) # Nor started @@ -238,7 +238,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertTrue(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Check it can't be started again self.assertRaises(ValueError, component.start) @@ -251,7 +251,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertFalse(self.__stop_called) self.assertTrue(self.__failed_called) self.assertFalse(component.is_running()) - self.assertTrue(component.is_failed()) + self.assertTrue(component.is_restarting()) def __do_start_stop(self, kind): """ @@ -276,7 +276,7 @@ class ComponentTests(BossUtils, unittest.TestCase): self.assertTrue(self.__stop_called) self.assertFalse(self.__failed_called) self.assertFalse(component.is_running()) - self.assertFalse(component.is_failed()) + self.assertFalse(component.is_restarting()) # Check it can't be stopped twice self.assertRaises(ValueError, component.stop) # Or failed From 52ba8a9314e28f0f2f39510e66c2dc03531c47e1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 11:11:51 -0700 Subject: [PATCH 176/194] [2244] missing update: Component.running() was renamed is_running(). --- src/bin/bind10/bind10_src.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 4567229ece..32c3152099 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -739,7 +739,7 @@ class BoB: component = self.components.pop(pid) logger.info(BIND10_PROCESS_ENDED, component.name(), pid, exit_status) - if component.running() and self.runnable: + if component.is_running() and self.runnable: # Tell it it failed. But only if it matters (we are # not shutting down and the component considers itself # to be running. From aea67d494996a4b86a1671591e4cfa0ed1cbee77 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 09:31:30 -0700 Subject: [PATCH 177/194] [master] a minor grammar fix to changelog entry. --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b5c2cf4cbb..f52f18c339 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 486. [bug]* jinmei All public header files for libb10-dns++ are now installed. Template configure.ac and utility AC macros for external projects - using the library is provided under the "examples" directory. + using the library are provided under the "examples" directory. The src/bin/host was moved as part of the examples (and not installed with other BIND 10 programs any more). (Trac #1870, git 4973e638d354d8b56dcadf71123ef23c15662021) From bd7ef43a90c7e6f3a9b7140d0cacb2459dcd4091 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 09:32:40 -0700 Subject: [PATCH 178/194] [master] added changelog for #2244 --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index f52f18c339..868b02a335 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +487. [bug] jinmei + The bind10 process now terminates a component (subprocess) by the + "config remove Boss/components" bindctl command even if the + process crashes immediately before the command is sent to bind10. + Previously this led to an inconsistent state between the + configuration and an internal component list of bind10, and bind10 + kept trying to restart the component. A known specific case of + this problem is that b10-ddns could keep failing (due to lack of + dependency modules) and the administrator couldn't stop the + restart via bindctl. + (Trac #2244, git 7565788d06f216ab254008ffdfae16678bcd00e5) + 486. [bug]* jinmei All public header files for libb10-dns++ are now installed. Template configure.ac and utility AC macros for external projects From 47cfab4fa97829309b1c6093d65c27a142b1c87a Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 20:28:16 +0200 Subject: [PATCH 179/194] Remove extra comma at the end of enum This should fix compilation issue on sunstudio. --- src/lib/datasrc/memory/domaintree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h index 7e68f53bf0..272245d05f 100644 --- a/src/lib/datasrc/memory/domaintree.h +++ b/src/lib/datasrc/memory/domaintree.h @@ -947,7 +947,7 @@ public: PARTIALMATCH, ///< A superdomain node was found NOTFOUND, ///< Not even any superdomain was found /// \brief Returned by insert() if a node of the name already exists - ALREADYEXISTS, + ALREADYEXISTS }; /// \brief Allocate and construct \c DomainTree From b6501267d7058d8ed879eb4ce63f3d502ccebcc1 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Wed, 10 Oct 2012 20:42:46 +0200 Subject: [PATCH 180/194] Compilation fix: usual static_cast(NULL) Sunstudio is unhappy with plain NULL inside EXPECT_EQ and thinks it is int. This makes it think otherwise. --- src/lib/datasrc/tests/memory/zone_table_unittest.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc index 9cf1b3420c..401d43431b 100644 --- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc +++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc @@ -101,9 +101,9 @@ TEST_F(ZoneTableTest, addZone) { zname1, holder1.release())); EXPECT_EQ(result::SUCCESS, result1.code); - EXPECT_EQ(NULL, result1.zone_data); + EXPECT_EQ(static_cast(NULL), result1.zone_data); // It got released by it - EXPECT_EQ(NULL, holder1.get()); + EXPECT_EQ(static_cast(NULL), holder1.get()); // Duplicate add doesn't replace the existing data. SegmentObjectHolder holder2( @@ -115,7 +115,7 @@ TEST_F(ZoneTableTest, addZone) { // The old one gets out EXPECT_EQ(data1, result2.zone_data); // It releases this one even when we replace the old zone - EXPECT_EQ(NULL, holder2.get()); + EXPECT_EQ(static_cast(NULL), holder2.get()); // We need to release the old one manually ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_); From ecdf1aeb4bfd97f636cc081cce30cc3394b38d11 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 9 Oct 2012 13:17:59 -0700 Subject: [PATCH 181/194] [2339] changed the search order of python executable: seek python3.x first. this works around a bit awkward installation setup where there's a link from "python3" to "python3.x" but not from "python3-config" to the corresponding "python3.x-config". That happens for recent versions of Homebrew. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4799b7d60c..4cea7ae8e3 100644 --- a/configure.ac +++ b/configure.ac @@ -232,7 +232,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes) AC_SUBST(SET_ENV_LIBRARY_PATH) AC_SUBST(ENV_LIBRARY_PATH) -m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1 python3.2]) +m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.2 python3.1 python3]) AC_ARG_WITH([pythonpath], AC_HELP_STRING([--with-pythonpath=PATH], [specify an absolute path to python executable when automatic version check (incorrectly) fails]), From 16823263f5bc357de97f8b5d72f7b4d046583d23 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 10 Oct 2012 13:50:21 -0700 Subject: [PATCH 182/194] [master] changelog for #2339 --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index 868b02a335..47ad2cbdd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +488. [build] jinmei + On configure, changed the search order for Python executable. + It first ties more specific file names such as "python3.2" before + more generic "python3". This will prevent configure failure on + Mac OS X that installs Python3 via recent versions of Homebrew. + (Trac #2339, git 88db890d8d1c64de49be87f03c24a2021bcf63da) + 487. [bug] jinmei The bind10 process now terminates a component (subprocess) by the "config remove Boss/components" bindctl command even if the From 55be177fc4f7537143ab6ef5a728bd44bdf9d783 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 09:41:47 -0700 Subject: [PATCH 183/194] [2340] removed a bogus 'test' within a test(1) expression. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4cea7ae8e3..72c7be2619 100644 --- a/configure.ac +++ b/configure.ac @@ -360,7 +360,7 @@ fi # Python 3.2 has an unused parameter in one of its headers. This # has been reported, but not fixed as of yet, so we check if we need # to set -Wno-unused-parameter. -if test "X$GXX" = "Xyes" -a test $werror_ok = 1; then +if test "X$GXX" = "Xyes" -a $werror_ok = 1; then CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS=${PYTHON_INCLUDES} CXXFLAGS_SAVED="$CXXFLAGS" From f97ecbe97b6b3e098ebde2c984041dd24ce2dbbe Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 10:42:39 -0700 Subject: [PATCH 184/194] [master] add changelog entry for #2340 --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index b421f01e88..65505ec02a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +493. [build] jinmei + Fixed build failure with newer versions of clang++. These + versions are stricter regarding "unused variable" and "unused + (driver) arguments" warnings, and cause fatal build error + with -Werror. The affected versions of clang++ include Apple's + customized version 4.1 included in Xcode 4.5.1. So this fix + will solve build errors for Mac OS X that uses newer versions of + Xcode. + (Trac #2340, git 55be177fc4f7537143ab6ef5a728bd44bdf9d783, + 3e2a372012e633d017a97029d13894e743199741 and commits before it + with [2340] in the commit log) + 492. [func] tomek libdhcpsrv: The DHCP Configuration Manager is now able to store information about IPv4 subnets and pools. It is still not possible From 92f2cc51478005befb016e410a4fcb9c768ea3b8 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 14:03:58 -0700 Subject: [PATCH 185/194] [1858] explicitly set close_fds to True in sockcreator's Popen(). --- src/lib/python/isc/bind10/sockcreator.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/python/isc/bind10/sockcreator.py b/src/lib/python/isc/bind10/sockcreator.py index c681d07fee..55142eeda4 100644 --- a/src/lib/python/isc/bind10/sockcreator.py +++ b/src/lib/python/isc/bind10/sockcreator.py @@ -214,9 +214,14 @@ class Creator(Parser): socket.SOCK_STREAM) env = copy.deepcopy(os.environ) env['PATH'] = path + # We explicitly set close_fs to True; it's False by default before + # Python 3.2. If we don't close the remaining FDs, the copy of + # 'local' will prevent the child process from terminating when + # the parent process died abruptly. self.__process = subprocess.Popen(['b10-sockcreator'], env=env, stdin=remote.fileno(), stdout=remote2.fileno(), + close_fds=True, preexec_fn=self.__preexec_work) remote.close() remote2.close() From c9de286421049dc1a75a981c511506d16c014501 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 14:41:47 -0700 Subject: [PATCH 186/194] [1858] refactoring: unify the SIGTERM/KILL cases into a single helper method. --- src/bin/bind10/bind10_src.py.in | 43 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 32c3152099..0cc1f1f390 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -693,32 +693,35 @@ class BoB: # from doing so if not self.nokill: # next try sending a SIGTERM - components_to_stop = list(self.components.values()) - for component in components_to_stop: - logger.info(BIND10_SEND_SIGTERM, component.name(), component.pid()) - try: - component.kill() - except OSError: - # ignore these (usually ESRCH because the child - # finally exited) - pass - # finally, send SIGKILL (unmaskable termination) until everybody dies + self.__terminate_children(False) + # finally, send SIGKILL (unmaskable termination) until everybody + # dies while self.components: # XXX: some delay probably useful... how much is uncertain time.sleep(0.1) self.reap_children() - components_to_stop = list(self.components.values()) - for component in components_to_stop: - logger.info(BIND10_SEND_SIGKILL, component.name(), - component.pid()) - try: - component.kill(True) - except OSError: - # ignore these (usually ESRCH because the child - # finally exited) - pass + self.__terminate_children(True) logger.info(BIND10_SHUTDOWN_COMPLETE) + def __terminate_children(self, forceful): + '''Terminate remaining subprocesses by sending a signal. + + The forceful paramter will be passed Component.kill(). + This is a dedicated subroutine of shutdown(), just to unify two + similar cases. + + ''' + logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM + # TODO: we should be able to skip list() + for component in list(self.components.values()): + logger.info(logmsg, component.name(), component.pid()) + try: + component.kill(forceful) + except OSError: + # ignore these (usually ESRCH because the child + # finally exited) + pass + def _get_process_exit_status(self): return os.waitpid(-1, os.WNOHANG) From 9dac9afb2569288bb630b00d2a89a490ad944710 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 14:42:48 -0700 Subject: [PATCH 187/194] [1858] cleanup: removed unnecessary list() conversion from dict values. dict.values() is an iterable, so we can use it directly in the for statement. (and we don't modify components within the loop). --- src/bin/bind10/bind10_src.py.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 0cc1f1f390..b840303dbc 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -712,8 +712,7 @@ class BoB: ''' logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM - # TODO: we should be able to skip list() - for component in list(self.components.values()): + for component in self.components.values(): logger.info(logmsg, component.name(), component.pid()) try: component.kill(forceful) From 9e11459509c9223201dcbe10636b15ba923e1516 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 14:49:33 -0700 Subject: [PATCH 188/194] [1858] rename terminate_children() kill_... for consistency --- src/bin/bind10/bind10_src.py.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index b840303dbc..f15c7185d9 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -693,17 +693,17 @@ class BoB: # from doing so if not self.nokill: # next try sending a SIGTERM - self.__terminate_children(False) + self.__kill_children(False) # finally, send SIGKILL (unmaskable termination) until everybody # dies while self.components: # XXX: some delay probably useful... how much is uncertain time.sleep(0.1) self.reap_children() - self.__terminate_children(True) + self.__kill_children(True) logger.info(BIND10_SHUTDOWN_COMPLETE) - def __terminate_children(self, forceful): + def __kill_children(self, forceful): '''Terminate remaining subprocesses by sending a signal. The forceful paramter will be passed Component.kill(). From 1e0c106c45251a61579d37a5148e9f258b3e5cea Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 15:05:01 -0700 Subject: [PATCH 189/194] [1858] added a test case where killing child process results in exception. --- src/bin/bind10/tests/bind10_test.py.in | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 0b119606e0..4839008560 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -1181,7 +1181,7 @@ class TestBossComponents(unittest.TestCase): # We check somewhere else that the shutdown is actually called # from there (the test_kills). - def __real_test_kill(self, nokill = False): + def __real_test_kill(self, nokill=False, ex_on_kill=None): """ Helper function that does the actual kill functionality testing. """ @@ -1197,6 +1197,13 @@ class TestBossComponents(unittest.TestCase): """ def kill(self, forceful=False): killed.append(forceful) + if ex_on_kill is not None: + # If exception is given by the test, raise it here. + # In the case of ESRCH, the process should have gone + # somehow, so we clear the components. + if ex_on_kill.errno == errno.ESRCH: + bob.components = {} + raise ex_on_kill if forceful: bob.components = {} def pid(self): @@ -1224,7 +1231,10 @@ class TestBossComponents(unittest.TestCase): if nokill: self.assertEqual([], killed) else: - self.assertEqual([False, True], killed) + if ex_on_kill is not None: + self.assertEqual([False], killed) + else: + self.assertEqual([False, True], killed) self.assertTrue(self.__called) @@ -1236,6 +1246,13 @@ class TestBossComponents(unittest.TestCase): """ self.__real_test_kill() + def test_kill_fail(self): + """Test cases where kill() results in an exception due to OS error.""" + + ex = OSError() + ex.errno = errno.ESRCH + self.__real_test_kill(ex_on_kill=ex) + def test_nokill(self): """ Test that the boss *doesn't* kill components which don't want to From 9c655172a298766a37cc83173438f70fff83264c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 15:39:53 -0700 Subject: [PATCH 190/194] [1858] stop sending hopeless signal when it once fails with EPERM. --- src/bin/bind10/bind10_messages.mes | 24 ++++++++++++++++++++++++ src/bin/bind10/bind10_src.py.in | 18 +++++++++++++----- src/bin/bind10/tests/bind10_test.py.in | 19 +++++++++++++++++-- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 2f48325498..e8a2eb2ee5 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -305,3 +305,27 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. + +% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4 +The boss module sent a single (either SIGTERM or SIGKILL) to a process, +but it failed due to some system level error. There are two major cases: +the target process has already terminated but the boss module had sent +the signal before it noticed the termination. In this case an error +message should indicate something like "no such process". This can be +safely ignored. The other case is that the boss module doesn't have +the privilege to send a signal to the process. It can typically +happen when the boss module started as a privileged process, spawned a +subprocess, and then dropped the privilege. It includes the case for +the socket creator when the boss process runs with the -u command line +option. In this case, the boss module simply gives up to terminate +the process explicitly because it's unlikely to succeed by keeping +sending the signal. Although the socket creator is implemented so +that it will terminate automatically when the boss process exits +(and that should be the case for any other future process running with +a higher privilege), but it's recommended to check if there's any +remaining BIND 10 process if this message is logged. For all other +cases, the boss module will keep sending the signal until it confirms +all child processes terminate. Although unlikely, this could prevent +the boss module from exiting, just keeping sending the signals. So, +again, it's advisable to check if it really terminates when this +message is logged. diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index f15c7185d9..7a245ae5bf 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -712,14 +712,22 @@ class BoB: ''' logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM - for component in self.components.values(): + # We need to make a copy of values as the components may be modified + # in the loop. + for component in list(self.components.values()): logger.info(logmsg, component.name(), component.pid()) try: component.kill(forceful) - except OSError: - # ignore these (usually ESRCH because the child - # finally exited) - pass + except OSError as ex: + # If kill() failed due to EPERM, it doesn't make sense to + # keep trying, so we just log the fact and forget that + # component. Ignore other OSErrors (usually ESRCH because + # the child finally exited) + signame = "SIGKILL" if forceful else "SIGTERM" + logger.info(BIND10_SEND_SIGNAL_FAIL, signame, + component.name(), component.pid(), ex) + if ex.errno == errno.EPERM: + del self.components[component.pid()] def _get_process_exit_status(self): return os.waitpid(-1, os.WNOHANG) diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 4839008560..ece6370c3a 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -1195,7 +1195,15 @@ class TestBossComponents(unittest.TestCase): (anyway it is not told so). It does not die if it is killed the first time. It dies only when killed forcefully. """ + def __init__(self): + # number of kill() calls, preventing infinite loop. + self.__call_count = 0 + def kill(self, forceful=False): + self.__call_count += 1 + if self.__call_count > 2: + raise Exception('Too many calls to ImmortalComponent.kill') + killed.append(forceful) if ex_on_kill is not None: # If exception is given by the test, raise it here. @@ -1247,10 +1255,17 @@ class TestBossComponents(unittest.TestCase): self.__real_test_kill() def test_kill_fail(self): - """Test cases where kill() results in an exception due to OS error.""" + """Test cases where kill() results in an exception due to OS error. + + The behavior should be different for EPERM, so we test two cases. + + """ ex = OSError() - ex.errno = errno.ESRCH + ex.errno, ex.strerror = errno.ESRCH, 'No such process' + self.__real_test_kill(ex_on_kill=ex) + + ex.errno, ex.strerror = errno.EPERM, 'Operation not permitted' self.__real_test_kill(ex_on_kill=ex) def test_nokill(self): From 2768eb3551fdaa2c4728563535dfb69f732dc033 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 11 Oct 2012 15:41:36 -0700 Subject: [PATCH 191/194] [1858] cleanup: reorder log messages --- src/bin/bind10/bind10_messages.mes | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index e8a2eb2ee5..6c3d12e229 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -167,6 +167,30 @@ so BIND 10 will now shut down. The specific error is printed. % BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2) The boss module is sending a SIGKILL signal to the given process. +% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4 +The boss module sent a single (either SIGTERM or SIGKILL) to a process, +but it failed due to some system level error. There are two major cases: +the target process has already terminated but the boss module had sent +the signal before it noticed the termination. In this case an error +message should indicate something like "no such process". This can be +safely ignored. The other case is that the boss module doesn't have +the privilege to send a signal to the process. It can typically +happen when the boss module started as a privileged process, spawned a +subprocess, and then dropped the privilege. It includes the case for +the socket creator when the boss process runs with the -u command line +option. In this case, the boss module simply gives up to terminate +the process explicitly because it's unlikely to succeed by keeping +sending the signal. Although the socket creator is implemented so +that it will terminate automatically when the boss process exits +(and that should be the case for any other future process running with +a higher privilege), but it's recommended to check if there's any +remaining BIND 10 process if this message is logged. For all other +cases, the boss module will keep sending the signal until it confirms +all child processes terminate. Although unlikely, this could prevent +the boss module from exiting, just keeping sending the signals. So, +again, it's advisable to check if it really terminates when this +message is logged. + % BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2) The boss module is sending a SIGTERM signal to the given process. @@ -305,27 +329,3 @@ the configuration manager to start up. The total length of time Boss will wait for the configuration manager before reporting an error is set with the command line --wait switch, which has a default value of ten seconds. - -% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4 -The boss module sent a single (either SIGTERM or SIGKILL) to a process, -but it failed due to some system level error. There are two major cases: -the target process has already terminated but the boss module had sent -the signal before it noticed the termination. In this case an error -message should indicate something like "no such process". This can be -safely ignored. The other case is that the boss module doesn't have -the privilege to send a signal to the process. It can typically -happen when the boss module started as a privileged process, spawned a -subprocess, and then dropped the privilege. It includes the case for -the socket creator when the boss process runs with the -u command line -option. In this case, the boss module simply gives up to terminate -the process explicitly because it's unlikely to succeed by keeping -sending the signal. Although the socket creator is implemented so -that it will terminate automatically when the boss process exits -(and that should be the case for any other future process running with -a higher privilege), but it's recommended to check if there's any -remaining BIND 10 process if this message is logged. For all other -cases, the boss module will keep sending the signal until it confirms -all child processes terminate. Although unlikely, this could prevent -the boss module from exiting, just keeping sending the signals. So, -again, it's advisable to check if it really terminates when this -message is logged. From ae0565a4ed6e8a947120a7bfe826c1cf9d672760 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 12 Oct 2012 13:48:46 -0700 Subject: [PATCH 192/194] [1858] (unrelated cleanup) removed BIND10_START_AS_NON_ROOT_AUTH log. with the introduction of socket creation this log message hasn't been making sense. It would be rather confusing and harmful. --- src/bin/bind10/bind10_messages.mes | 7 ------- src/bin/bind10/bind10_src.py.in | 2 -- 2 files changed, 9 deletions(-) diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes index 6c3d12e229..ed2a5d939b 100644 --- a/src/bin/bind10/bind10_messages.mes +++ b/src/bin/bind10/bind10_messages.mes @@ -298,13 +298,6 @@ During the startup process, a number of messages are exchanged between the Boss process and the processes it starts. This error is output when a message received by the Boss process is not recognised. -% BIND10_START_AS_NON_ROOT_AUTH starting b10-auth as a user, not root. This might fail. -The authoritative server is being started or restarted without root privileges. -If the module needs these privileges, it may have problems starting. -Note that this issue should be resolved by the pending 'socket-creator' -process; once that has been implemented, modules should not need root -privileges anymore. See tickets #800 and #801 for more information. - % BIND10_START_AS_NON_ROOT_RESOLVER starting b10-resolver as a user, not root. This might fail. The resolver is being started or restarted without root privileges. If the module needs these privileges, it may have problems starting. diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in index 7a245ae5bf..45a2ccb961 100755 --- a/src/bin/bind10/bind10_src.py.in +++ b/src/bin/bind10/bind10_src.py.in @@ -546,8 +546,6 @@ class BoB: """ Start the Authoritative server """ - if self.uid is not None and self.__started: - logger.warn(BIND10_START_AS_NON_ROOT_AUTH) authargs = ['b10-auth'] if self.verbose: authargs += ['-v'] From 9565a7939e66bb0f3bbb4f3be8778dec13582db1 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Mon, 15 Oct 2012 15:55:19 +0200 Subject: [PATCH 193/194] [master] Using the default value of second parameter for substr --- src/lib/dhcp/lease_mgr.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc index d09bd583c0..8291df3e61 100644 --- a/src/lib/dhcp/lease_mgr.cc +++ b/src/lib/dhcp/lease_mgr.cc @@ -45,7 +45,7 @@ LeaseMgr::LeaseMgr(const std::string& dbconfig) { size_t pos = token.find("="); if (pos != string::npos) { string name = token.substr(0, pos); - string value = token.substr(pos + 1, -1); + string value = token.substr(pos + 1); parameters_.insert(pair(name, value)); } else { isc_throw(InvalidParameter, "Cannot parse " << token From 453b58784344390cbc5375624e6e92a86b4ff3f1 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 15 Oct 2012 13:16:24 -0700 Subject: [PATCH 194/194] [master] added changelog for #1858 --- ChangeLog | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ChangeLog b/ChangeLog index 65505ec02a..9e76a18744 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +494. [bug] jinmei + Fixed a problem that shutting down BIND 10 kept some of the + processes alive. It was two-fold: when the main bind10 process + started as a root, started b10-sockcreator with the privilege, and + then dropped the privilege, the bind10 process cannot kill the + sockcreator via signal any more (when it has to), but it kept + sending the signal and didn't stop. Also, when running on Python + 3.1 (or older), the sockcreator had some additional file + descriptor open, which prevented it from exiting even after the + bind10 process terminated. Now the bind10 process simply gives up + killing a subprocess if it fails due to lack of permission, and it + makes sure the socket creator is spawned without any unnecessary + FDs open. + (Trac #1858, git 405d85c8a0042ba807a3a123611ff383c4081ee1) + 493. [build] jinmei Fixed build failure with newer versions of clang++. These versions are stricter regarding "unused variable" and "unused