Commit 3cb881a1 authored by Francis Dupont's avatar Francis Dupont

[4029] Applied #4028 removals

parent d45798e0
......@@ -21,6 +21,6 @@ endif
# Include common libraries being used by shell-based tests.
SHLIBS = dhcp_test_lib.sh.in
EXTRA_DIST = portconfig.h socket_request.h $(SHLIBS)
EXTRA_DIST = $(SHLIBS)
CLEANFILES = dhcp_test_lib.sh
// 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.
#ifndef ISC_TESTUTILS_PORTCONFIG_H
#define ISC_TESTUTILS_PORTCONFIG_H
#include <gtest/gtest.h>
#include <cc/data.h>
#include <server_common/portconfig.h>
namespace isc {
namespace testutils {
/**
* \brief Bits of tests for server port configuration.
*
* These are bits of tests that can be reused by server classes to check if
* configuration of the listening addresses work.
*
* You can put any of these functions into a TEST_F test and pass the server
* to it.
*
* \todo There's quite a lot of common code in the basic server handling.
* We should refactor it, so both Resolver server and Auth server have
* a common base class. When this is done, the common parts would be put
* there and the tests would be at the base class, not here.
*/
namespace portconfig {
/**
* \brief Check setting of the listening addresses directly (as a list) works.
*
* \param server The server to test against.
*/
template<class Server>
void
listenAddresses(Server& server) {
using namespace isc::server_common::portconfig;
// In this test we assume the address list is originally empty.
EXPECT_TRUE(server.getListenAddresses().empty());
// Try putting there some addresses
AddressList addresses;
addresses.push_back(AddressPair("127.0.0.1", 53210));
addresses.push_back(AddressPair("::1", 53210));
server.setListenAddresses(addresses);
EXPECT_EQ(2, server.getListenAddresses().size());
EXPECT_EQ("::1", server.getListenAddresses()[1].first);
// Is it independent from what we do with the vector later?
addresses.clear();
EXPECT_EQ(2, server.getListenAddresses().size());
// If we set to an empty list next, the server configuration should
// become empty, too.
server.setListenAddresses(addresses);
EXPECT_TRUE(server.getListenAddresses().empty());
}
/**
* \brief Check setting of the addresses by config value.
*
* This passes an listen_on element to the server's updateConfig function.
* It tries little bit of switching around. It tries both setting a presumably
* valid addresses and then setting something that cant be bound, rolling back
* back to original.
*
* \param server The server object to test against.
*/
template<class Server>
void
listenAddressConfig(Server& server) {
using namespace isc::data;
// Try putting there some address
ElementPtr config(Element::fromJSON("{"
"\"listen_on\": ["
" {"
" \"address\": \"127.0.0.1\","
" \"port\": 53210"
" }"
"]"
"}"));
ConstElementPtr result(server.updateConfig(config));
EXPECT_EQ(result->toWire(), isc::config::createAnswer()->toWire());
ASSERT_EQ(1, server.getListenAddresses().size());
EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
EXPECT_EQ(53210, server.getListenAddresses()[0].second);
// This address is rejected by the test socket requestor
config = Element::fromJSON("{"
"\"listen_on\": ["
" {"
" \"address\": \"192.0.2.2\","
" \"port\": 53210"
" }"
"]"
"}");
result = server.updateConfig(config);
EXPECT_FALSE(result->equals(*isc::config::createAnswer()));
ASSERT_EQ(1, server.getListenAddresses().size());
EXPECT_EQ("127.0.0.1", server.getListenAddresses()[0].first);
EXPECT_EQ(53210, server.getListenAddresses()[0].second);
}
/**
* \brief Check that given config is rejected.
*
* Try if given config is considered invalid by the server and is rejected.
* The value is converted from JSON to the data elements and passed to server's
* updateConfig method. It should not crash, but return a negative answer.
*
* It is used internally by invalidListenAddressConfig, but you can use it
* to test any other invalid configs.
*
* \todo It might be better to put it to some other namespace, as this is more
* generic. But right now it is used only here, so until something else
* needs it, it might as well stay here.
* \param server The server to test against.
* \param JSON Config to use.
* \param name It is used in the output if the test fails.
*/
template<class Server>
void
configRejected(Server& server, const std::string& JSON,
const std::string& name)
{
SCOPED_TRACE(name);
using namespace isc::data;
ElementPtr config(Element::fromJSON(JSON));
EXPECT_FALSE(server.updateConfig(config)->
equals(*isc::config::createAnswer())) <<
"Accepted invalid config " << JSON;
}
/**
* \brief Check some invalid address configs.
*
* It tries a series of invalid listen_on configs against the server and checks
* it is rejected.
* \param server The server to check against.
*/
template<class Server>
void
invalidListenAddressConfig(Server& server) {
configRejected(server, "{"
"\"listen_on\": \"error\""
"}", "Wrong element type");
configRejected(server, "{"
"\"listen_on\": [{}]"
"}", "Empty address element");
configRejected(server, "{"
"\"listen_on\": [{"
" \"port\": 1.5,"
" \"address\": \"192.0.2.1\""
"}]}", "Float port");
configRejected(server, "{"
"\"listen_on\": [{"
" \"port\": -5,"
" \"address\": \"192.0.2.1\""
"}]}", "Negative port");
configRejected(server, "{"
"\"listen_on\": [{"
" \"port\": 1000000,"
" \"address\": \"192.0.2.1\""
"}]}", "Huge port");
configRejected(server, "{"
"\"listen_on\": [{"
" \"port\": 53,"
" \"address\": \"bad_address\""
"}]}", "Bad address");
}
}
}
}
#endif // ISC_TESTUTILS_PORTCONFIG_H
// 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.
#ifndef ISC_TESTUTILS_SOCKETREQUEST_H
#define ISC_TESTUTILS_SOCKETREQUEST_H 1
#include <server_common/socket_request.h>
#include <server_common/portconfig.h>
#include <asiodns/asiodns.h>
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <vector>
#include <string>
namespace isc {
namespace testutils {
/// \brief A testcase part for faking the SocketRequestor in tests
///
/// It's awkward to request real sockets from the real socket creator
/// during tests (for one, because it would have to be running, for
/// another, we need to block real ports). If you instantiate this class in
/// a test case, the socket requestor will be initialized to a test one which
/// handles fake socket FDs and stores what was requested, etc.
///
/// Furthermore, you can check if the code requested or released the correct
/// list of sockets using the checkTokens() method.
///
/// Some member variables are intentionally made public so that test cases
/// can easily check the value of them. We prefer convenience for tests over
/// class integrity here.
class TestSocketRequestor : public isc::server_common::SocketRequestor {
public:
/// \brief Constructor
///
/// \param dnss The DNS service. It is expected this gets initialized
/// after the TestSocketRequestor constructor is called, as the
/// TestSocketRequestor should be a base class and the service only
/// a member.
/// \param store Address store used when cleaning up.
/// \param expect_port The port which is expected to be requested. If
/// the application requests a different port, it is considered
/// a failure.
/// \param expected_app The share name for which all the requests should
/// be made. This is not the usual app_name - the requestSocket does
/// not fall back to this value if its share_name is left empty, if
/// you want to check the code relies on the requestor to use the
/// app name, you set this to empty string.
TestSocketRequestor(asiodns::DNSServiceBase& dnss,
server_common::portconfig::AddressList& store,
uint16_t expect_port,
const std::string& expected_app) :
last_token_(0), break_rollback_(false), break_release_(false),
dnss_(dnss), store_(store), expect_port_(expect_port),
expected_app_(expected_app)
{
// Prepare the requestor (us) for the test
server_common::initTestSocketRequestor(this);
}
/// \brief Destructor
///
/// Removes the addresses (if any) installed by installListenAddresses,
/// resets the socket requestor to uninitialized state and turns off
/// the portconfig test mode.
virtual ~TestSocketRequestor() {
// Make sure no sockets are left inside (if installListenAddresses
// wasn't used, this is NOP, so it won't hurt).
server_common::portconfig::AddressList list;
server_common::portconfig::installListenAddresses(list, store_, dnss_);
// Don't leave invalid pointers here
server_common::initTestSocketRequestor(NULL);
}
/// \brief Tokens released by releaseSocket
///
/// They are stored here by this class and you can examine them.
std::vector<std::string> released_tokens_;
/// \brief Tokens returned from requestSocket
///
/// They are stored here by this class and you can examine them.
std::vector<std::string> given_tokens_;
private:
// Last token number and fd given out
size_t last_token_;
public:
/// \brief Support a broken rollback case
///
/// If this is set to true, the requestSocket will throw when the
/// ::1 address is requested.
bool break_rollback_;
/// \brief Throw on releaseSocket
///
/// If this is set to true, the releaseSocket will throw SocketError.
/// Defaults to false.
bool break_release_;
/// \brief Release a socket
///
/// This only stores the token passed.
/// \param token The socket to release
///
/// \throw SocketError in case the break_release_ is set to true. This is
/// to test exception handling.
void releaseSocket(const std::string& token) {
if (break_release_) {
isc_throw(SocketError, "Fatal test socket error");
}
released_tokens_.push_back(token);
}
/// \brief Request a socket
///
/// This creates a new token and fakes a new socket and returns it.
/// The token is stored.
///
/// In case the address is 192.0.2.2, it throws SocketAllocateError
/// or if the break_rollback_ is true and address is ::1, it throws
/// ShareError. If the address is 192.0.2.3, it throws SocketError.
///
/// The tokens produced are in form of protocol:address:port:fd. The fds
/// start at 1 and increase by each successfull call.
///
/// \param protocol The protocol to request
/// \param address to bind to
/// \param port to bind to
/// \param mode checked to be SHARE_SAME for now
/// \param name checked to be the same as expected_app parameter of the
/// constructor. Note that this class does not provide the fallback
/// to an app_name if this is empty string. To check the code relies
/// on the fallback (wants to use the app_name instead of providing
/// its own share name), you need to create this class with empty
/// expected_app.
/// \return The token and FD
/// \throw SocketAllocateError as described above, to test error handling
/// \throw ShareError as described above, to test error handling
/// \throw SocketError as described above, to test error handling
SocketID requestSocket(Protocol protocol, const std::string& address,
uint16_t port, ShareMode mode,
const std::string& name)
{
if (address == "192.0.2.2") {
isc_throw(SocketAllocateError, "This address is not allowed");
}
if (address == "192.0.2.3") {
isc_throw(SocketError, "Fatal test error");
}
if (address == "::1" && break_rollback_) {
// This is valid address, but in case we need to break the
// rollback, it needs to be busy or whatever
//
// We break the second address to see the first one was
// allocated and then returned
isc_throw(ShareError,
"This address is available, but not for you");
}
const std::string proto(protocol == TCP ? "TCP" : "UDP");
const size_t number = ++ last_token_;
EXPECT_EQ(expect_port_, port);
EXPECT_EQ(SHARE_SAME, mode);
EXPECT_EQ(expected_app_, name);
const std::string token(proto + ":" + address + ":" +
boost::lexical_cast<std::string>(port) + ":" +
boost::lexical_cast<std::string>(number));
given_tokens_.push_back(token);
return (SocketID(number, token));
}
/// \brief Check the list of tokens is as expected
///
/// Compares the expected and real tokens.
///
/// \param expected List of the expected tokens, as NULL-terminated array
/// of C strings (it is more convenient to type as a constant than to
/// manually push_back all the strings to a vector).
/// \param real The token list that was produced by this class (usually
/// either given_tokens_ or released_tokens_).
/// \param scope Human readable identifier of which checkTokens call it is.
/// It is printed as a part of failure message.
void checkTokens(const char** expected,
const std::vector<std::string>& real,
const char* scope) const
{
SCOPED_TRACE(scope);
size_t position(0);
while (expected[position] != NULL) {
ASSERT_LT(position, real.size());
EXPECT_EQ(expected[position], real[position]) << position;
position ++;
}
EXPECT_EQ(position, real.size());
}
private:
asiodns::DNSServiceBase& dnss_;
server_common::portconfig::AddressList& store_;
const uint16_t expect_port_;
const std::string expected_app_;
};
}
}
#endif // ISC_TESTUTILS_SOCKETREQUEST_H
......@@ -12,7 +12,6 @@ lib_LTLIBRARIES = libkea-util.la
libkea_util_la_SOURCES = boost_time_utils.h boost_time_utils.cc
libkea_util_la_SOURCES += csv_file.h csv_file.cc
libkea_util_la_SOURCES += filename.h filename.cc
libkea_util_la_SOURCES += locks.h lru_list.h
libkea_util_la_SOURCES += strutil.h strutil.cc
libkea_util_la_SOURCES += buffer.h io_utilities.h
libkea_util_la_SOURCES += time_utilities.h time_utilities.cc
......
// 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.
/// This file (right now) provides dummy locks
/// It also contains code to use boost/threads locks:
///
///
/// All locks are dummy classes that don't actually do anything. At this moment,
/// only the very minimal set of methods that we actually use is defined.
///
/// Note that we need to include <config.h> in our .cc files for that
/// to be set. we might want to enfore this at compile time with a check
/// (TODO)
#ifndef LOCKS
#define LOCKS
namespace isc {
namespace util {
namespace locks {
class mutex {
};
class recursive_mutex {
};
class upgradable_mutex {
};
template <typename T>
class sharable_lock {
public:
sharable_lock(T) {}
};
template <typename T>
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() {}
void unlock() {}
};
} // namespace locks
} // namespace util
} // namespace isc
#endif // LOCKS
// 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 LRU_LIST_H
#define LRU_LIST_H
#include <list>
#include <string>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <util/locks.h>
namespace isc {
namespace util {
/// \brief LRU List
///
/// Provides the LRU list for the zone and nameserver objects. The list is
/// created with a specific size. Entries are added to the back of the list
/// and removed from the front. It is also possible to pull an element out
/// of the middle of the list and add it to the end of the list, an action that
/// should be done when the element is referenced.
///
/// It is not intended that the class be copied, and the derivation from
/// boost::noncopyable enforces this.
template <typename T>
class LruList : boost::noncopyable {
public:
typedef typename std::list<boost::shared_ptr<T> > lru_list;
typedef typename lru_list::iterator iterator;
/// \brief Dropped Operation
///
/// When an object is dropped from the LRU list because it has not been
/// accessed for some time, it is possible that the action should trigger
/// some other functions. For this reason, it is possible to register
/// a list-wide functor object to execute in this case.
///
/// Note that the function does not execute as the result of a call to
/// remove() - that is an explicit call and it is assumed that the caller
/// will handle any additional operations needed.
class Dropped {
public:
/// \brief Constructor
Dropped(){}
/// \brief Virtual Destructor
virtual ~Dropped(){}
/// \brief Dropped Object Handler
///
/// Function object called when the object drops off the end of the
/// LRU list.
///
/// \param drop Object being dropped.
virtual void operator()(T* drop) const = 0;
};
/// \brief Constructor
///
/// \param max_size Maximum size of the list before elements are dropped.
/// \param dropped Pointer to a function object that will get called as
/// elements are dropped. This object will be stored using a shared_ptr,
/// so should be allocated with new().
LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
max_size_(max_size), count_(0), dropped_(dropped)
{}
/// \brief Virtual Destructor
virtual ~LruList()
{}
/// \brief Add Element
///
/// Add a new element to the end of the list.
///
/// \param element Reference to the element to add.
///
/// \return Handle describing the element in the LRU list.
virtual void add(boost::shared_ptr<T>& element);
/// \brief Remove Element
///
/// Removes an element from the list. If the element is not present (i.e.
/// its internal list pointer is invalid), this is a no-op.
///
/// \param element Reference to the element to remove.
virtual void remove(boost::shared_ptr<T>& element);
/// \brief Touch Element
///
/// The name comes from the Unix "touch" command. All this does is to
/// move the specified entry from the middle of the list to the end of
/// the list.
///
/// \param element Reference to the element to touch.
virtual void touch(boost::shared_ptr<T>& element);
/// \brief Drop All the Elements in the List .
///
/// All the elements will be dropped from the list container, and their
/// drop handler(if there is one) will be called, when done, the size of
/// of list will be 0.
virtual void clear();
/// \brief Return Size of the List
///
/// An independent count is kept of the list size, as list.size() may take
/// some time if the list is big.
///
/// \return Number of elements in the list
virtual uint32_t size() const {
// Don't bother to lock the mutex. If an update is in progress, we
// receive either the value just before the update or just after it.
// Either way, this call could have come just before or just after
// that operation, so the value would have been just as uncertain.
return count_;
}
/// \brief Return Maximum Size
///
/// \return Maximum size of the list
virtual uint32_t getMaxSize() const {
return max_size_;
}
/// \brief Set Maximum Size
///
/// \param max_size New maximum list size
virtual void setMaxSize(uint32_t max_size) {
max_size_ = max_size;
}
private:
locks::mutex mutex_; ///< List protection
std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
uint32_t max_size_; ///< Max size of the list
uint32_t count_; ///< Count of elements
boost::shared_ptr<Dropped> dropped_; ///< Dropped object
};
// Add entry to the list
template <typename T>
void LruList<T>::add(boost::shared_ptr<T>& element) {
// Protect list against concurrent access
locks::scoped_lock<locks::mutex> lock(mutex_);
// Add the entry and set its pointer field to point into the list.
// insert() is used to get the pointer.
element->setLruIterator(lru_.insert(lru_.end(), element));
// ... and update the count while we have the mutex.
++count_;
// If the count takes us above the maximum size of the list, remove elements
// from the front. The current list size could be more than one above the
// maximum size of the list if the maximum size was changed after
// construction.
while (count_ > max_size_) {
if (!lru_.empty()) {
// Run the drop handler (if there is one) on the
// to-be-dropped object.
if (dropped_) {
(*dropped_)(lru_.begin()->get());
}
// ... and get rid of it from the list
lru_.pop_front();
--count_;
}
else {
// TODO: Log this condition (count_ > 0 when list empty) -
// it should not happen
count_ = 0;
break;
}
}
}
// Remove an element from the list
template <typename T>
void LruList<T>::remove(boost::shared_ptr<T>& element) {
// An element can only be removed it its internal pointer is valid.