Commit 90a950d1 authored by Marcin Siodelski's avatar Marcin Siodelski

[#642,!373] Extended StampedElement to hold multiple server tags.

parent 4b491c08
...@@ -5,7 +5,8 @@ AM_CPPFLAGS += $(BOOST_INCLUDES) ...@@ -5,7 +5,8 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(KEA_CXXFLAGS) AM_CXXFLAGS = $(KEA_CXXFLAGS)
lib_LTLIBRARIES = libkea-cc.la lib_LTLIBRARIES = libkea-cc.la
libkea_cc_la_SOURCES = data.cc data.h libkea_cc_la_SOURCES = base_stamped_element.cc base_stamped_element.h
libkea_cc_la_SOURCES += data.cc data.h
libkea_cc_la_SOURCES += element_value.h libkea_cc_la_SOURCES += element_value.h
libkea_cc_la_SOURCES += cfg_to_element.h dhcp_config_error.h libkea_cc_la_SOURCES += cfg_to_element.h dhcp_config_error.h
libkea_cc_la_SOURCES += command_interpreter.cc command_interpreter.h libkea_cc_la_SOURCES += command_interpreter.cc command_interpreter.h
...@@ -26,6 +27,7 @@ libkea_cc_la_LDFLAGS = -no-undefined -version-info 8:0:0 ...@@ -26,6 +27,7 @@ libkea_cc_la_LDFLAGS = -no-undefined -version-info 8:0:0
# Specify the headers for copying into the installation directory tree. # Specify the headers for copying into the installation directory tree.
libkea_cc_includedir = $(pkgincludedir)/cc libkea_cc_includedir = $(pkgincludedir)/cc
libkea_cc_include_HEADERS = \ libkea_cc_include_HEADERS = \
base_stamped_element.h \
cfg_to_element.h \ cfg_to_element.h \
command_interpreter.h \ command_interpreter.h \
data.h \ data.h \
......
// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <cc/base_stamped_element.h>
namespace isc {
namespace data {
BaseStampedElement::BaseStampedElement()
/// @todo Change it to microsec_clock once we transition to subsecond
/// precision.
: id_(0), timestamp_(boost::posix_time::second_clock::local_time()) {
}
void
BaseStampedElement::updateModificationTime() {
/// @todo Change it to microsec_clock once we transition to subsecond
/// precision.
setModificationTime(boost::posix_time::second_clock::local_time());
}
} // end of namespace isc::data
} // end of namespace isc
// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef BASE_STAMPED_ELEMENT_H
#define BASE_STAMPED_ELEMENT_H
#include <cc/data.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <cstdint>
namespace isc {
namespace data {
/// @brief This class represents configuration element which is
/// associated with database identifier and the modification
/// timestamp.
///
/// The @c StampedElement class derives from this class to extend
/// it with the capability to associate the configuration elements
/// with server tags. The @c db::Server class derives from it to
/// store a single server tag identifying a server it describes.
///
/// @note This class is not derived from @c Element and should not
/// be confused with the classes being derived from @c Element class.
/// Those classes are used to represent JSON structures, whereas this
/// class represents data fetched from the database.
class BaseStampedElement {
public:
/// @brief Constructor.
///
/// Sets timestamp to the current time.
BaseStampedElement();
/// @brief Sets element's database identifier.
///
/// @param id New id.
void setId(const uint64_t id) {
id_ = id;
}
/// @brief Returns element's database identifier.
uint64_t getId() const {
return (id_);
}
/// @brief Sets timestamp to the explicitly provided value.
///
/// @param timestamp New timestamp value.
void setModificationTime(const boost::posix_time::ptime& timestamp) {
timestamp_ = timestamp;
}
/// @brief Sets timestmp to the current time.
void updateModificationTime();
/// @brief Returns timestamp.
boost::posix_time::ptime getModificationTime() const {
return (timestamp_);
}
protected:
/// @brief Database identifier of the configuration element.
///
/// The default value of 0 indicates that the identifier is
/// not set.
uint64_t id_;
/// @brief Holds timestamp value.
boost::posix_time::ptime timestamp_;
};
} // end of namespace isc::data
} // end of namespace isc
#endif
...@@ -10,33 +10,35 @@ namespace isc { ...@@ -10,33 +10,35 @@ namespace isc {
namespace data { namespace data {
StampedElement::StampedElement() StampedElement::StampedElement()
/// @todo Change it to microsec_clock once we transition to subsecond : BaseStampedElement(), server_tags_() {
/// precision.
: id_(0), timestamp_(boost::posix_time::second_clock::local_time()),
server_tag_() {
} }
void bool
StampedElement::updateModificationTime() { StampedElement::hasServerTag(const ServerTag& server_tag) const {
/// @todo Change it to microsec_clock once we transition to subsecond for (auto tag : server_tags_) {
/// precision. if (tag.get() == server_tag.get()) {
setModificationTime(boost::posix_time::second_clock::local_time()); return (true);
} }
}
std::string return (false);
StampedElement:: getServerTag() const {
return (server_tag_.get());
} }
bool bool
StampedElement::allServers() const { StampedElement::hasAllServerTag() const {
return (server_tag_.amAll()); return (hasServerTag(ServerTag(ServerTag::ALL)));
} }
ElementPtr ElementPtr
StampedElement::getMetadata() const { StampedElement::getMetadata() const {
ElementPtr metadata = Element::createMap(); ElementPtr metadata = Element::createMap();
metadata->set("server-tag", Element::create(getServerTag())); ElementPtr tags = Element::createList();
for (auto server_tag : server_tags_) {
tags->add(Element::create(server_tag.get()));
}
metadata->set("server-tags", tags);
return (metadata); return (metadata);
} }
......
...@@ -7,19 +7,16 @@ ...@@ -7,19 +7,16 @@
#ifndef STAMPED_ELEMENT_H #ifndef STAMPED_ELEMENT_H
#define STAMPED_ELEMENT_H #define STAMPED_ELEMENT_H
#include <cc/data.h> #include <cc/base_stamped_element.h>
#include <cc/server_tag.h> #include <cc/server_tag.h>
#include <boost/date_time/posix_time/posix_time.hpp> #include <vector>
#include <boost/scoped_ptr.hpp>
#include <cstdint>
#include <string>
namespace isc { namespace isc {
namespace data { namespace data {
/// @brief This class represents configuration element which is /// @brief This class represents configuration element which is
/// associated with database identifier and the modification /// associated with database identifiee, modification timestamp
/// timestamp. /// and servers.
/// ///
/// Classes storing Kea configuration should derive from this object /// Classes storing Kea configuration should derive from this object
/// to track ids and modification times of the configuration objects. /// to track ids and modification times of the configuration objects.
...@@ -33,8 +30,8 @@ namespace data { ...@@ -33,8 +30,8 @@ namespace data {
/// Those classes are used to represent JSON structures, whereas this /// Those classes are used to represent JSON structures, whereas this
/// class represents data fetched from the database. /// class represents data fetched from the database.
/// ///
/// @todo Find a better name for @c StamepedElement. /// @todo Find a better name for @c StampedElement.
class StampedElement { class StampedElement : public BaseStampedElement {
public: public:
/// @brief Constructor. /// @brief Constructor.
...@@ -42,51 +39,31 @@ public: ...@@ -42,51 +39,31 @@ public:
/// Sets timestamp to the current time. /// Sets timestamp to the current time.
StampedElement(); StampedElement();
/// @brief Sets element's database identifier. /// @brief Adds new server tag.
///
/// @param id New id.
void setId(const uint64_t id) {
id_ = id;
}
/// @brief Returns element's database identifier.
uint64_t getId() const {
return (id_);
}
/// @brief Sets timestamp to the explicitly provided value.
///
/// @param timestamp New timestamp value.
void setModificationTime(const boost::posix_time::ptime& timestamp) {
timestamp_ = timestamp;
}
/// @brief Sets timestmp to the current time.
void updateModificationTime();
/// @brief Returns timestamp.
boost::posix_time::ptime getModificationTime() const {
return (timestamp_);
}
/// @brief Sets new server tag.
/// ///
/// @param server_tag new server tag. /// @param server_tag new server tag.
/// @throw BadValue if the server tag length exceeds 256 characters. /// @throw BadValue if the server tag length exceeds 256 characters.
void setServerTag(const std::string& server_tag) { void setServerTag(const std::string& server_tag) {
server_tag_ = ServerTag(server_tag); server_tags_.push_back(ServerTag(server_tag));
} }
/// @brief Returns server tag. /// @brief Returns server tags.
/// ///
/// @return Server tag as string. /// @return Server tag as string.
std::string getServerTag() const; std::vector<ServerTag> getServerTags() const {
return (server_tags_);
}
/// @brief Checks if the element has the given server tag.
///
/// @param server_tag Server tag to be found.
/// @return true if the server tag was found, false otherwise.
bool hasServerTag(const ServerTag& server_tag) const;
/// @brief Checks if the stamped element is for all servers. /// @brief Checks if the element has 'all' server tag.
/// ///
/// @return true if the stamped element is associated with all servers, /// @return true if the server tag was found, false otherwise.
/// false otherwise. bool hasAllServerTag() const;
bool allServers() const;
/// @brief Returns an object representing metadata to be returned /// @brief Returns an object representing metadata to be returned
/// with objects from the configuration backend. /// with objects from the configuration backend.
...@@ -96,17 +73,8 @@ public: ...@@ -96,17 +73,8 @@ public:
private: private:
/// @brief Database identifier of the configuration element. /// @brief Holds server tags.
/// std::vector<ServerTag> server_tags_;
/// The default value of 0 indicates that the identifier is
/// not set.
uint64_t id_;
/// @brief Holds timestamp value.
boost::posix_time::ptime timestamp_;
/// @brief Holds server tag.
ServerTag server_tag_;
}; };
} // end of namespace isc::data } // end of namespace isc::data
......
...@@ -221,9 +221,9 @@ typedef boost::multi_index_container< ...@@ -221,9 +221,9 @@ typedef boost::multi_index_container<
boost::multi_index::ordered_non_unique< boost::multi_index::ordered_non_unique<
boost::multi_index::tag<StampedValueModificationTimeIndexTag>, boost::multi_index::tag<StampedValueModificationTimeIndexTag>,
boost::multi_index::const_mem_fun< boost::multi_index::const_mem_fun<
StampedElement, BaseStampedElement,
boost::posix_time::ptime, boost::posix_time::ptime,
&StampedElement::getModificationTime &BaseStampedElement::getModificationTime
> >
> >
> >
......
...@@ -23,8 +23,8 @@ TEST(StampedElementTest, create) { ...@@ -23,8 +23,8 @@ TEST(StampedElementTest, create) {
// Default identifier is 0. // Default identifier is 0.
EXPECT_EQ(0, element.getId()); EXPECT_EQ(0, element.getId());
// Default server tag is 'all'. // By default there is no server tag.
EXPECT_EQ(ServerTag::ALL, element.getServerTag()); EXPECT_TRUE(element.getServerTags().empty());
// Checking that the delta between now and the timestamp is within // Checking that the delta between now and the timestamp is within
// 5s range should be sufficient. // 5s range should be sufficient.
...@@ -70,11 +70,25 @@ TEST(StampedElementTest, update) { ...@@ -70,11 +70,25 @@ TEST(StampedElementTest, update) {
EXPECT_LT(delta.seconds(), 5); EXPECT_LT(delta.seconds(), 5);
} }
// Tests that server tag can be overriden by a new value. // Tests that one or more server tag can be specified.
TEST(StampedElementTest, setServerTag) { TEST(StampedElementTest, setServerTags) {
StampedElement element; StampedElement element;
element.setServerTag("foo"); element.setServerTag("foo");
EXPECT_EQ("foo", element.getServerTag()); EXPECT_EQ(1, element.getServerTags().size());
EXPECT_EQ("foo", element.getServerTags()[0].get());
element.setServerTag("bar");
EXPECT_EQ(2, element.getServerTags().size());
EXPECT_EQ("foo", element.getServerTags()[0].get());
EXPECT_EQ("bar", element.getServerTags()[1].get());
EXPECT_TRUE(element.hasServerTag(ServerTag("foo")));
EXPECT_TRUE(element.hasServerTag(ServerTag("bar")));
EXPECT_FALSE(element.hasServerTag(ServerTag("xyz")));
EXPECT_FALSE(element.hasAllServerTag());
element.setServerTag(ServerTag::ALL);
EXPECT_TRUE(element.hasAllServerTag());
} }
// Test that metadata can be created from the StampedElement. // Test that metadata can be created from the StampedElement.
...@@ -85,7 +99,12 @@ TEST(StampedElementTest, getMetadata) { ...@@ -85,7 +99,12 @@ TEST(StampedElementTest, getMetadata) {
ASSERT_TRUE(metadata); ASSERT_TRUE(metadata);
ASSERT_EQ(Element::map, metadata->getType()); ASSERT_EQ(Element::map, metadata->getType());
auto server_tag_element = metadata->get("server-tag"); auto server_tags_element = metadata->get("server-tags");
ASSERT_TRUE(server_tags_element);
EXPECT_EQ(Element::list, server_tags_element->getType());
EXPECT_EQ(1, server_tags_element->size());
auto server_tag_element = server_tags_element->get(0);
ASSERT_TRUE(server_tag_element); ASSERT_TRUE(server_tag_element);
EXPECT_EQ(Element::string, server_tag_element->getType()); EXPECT_EQ(Element::string, server_tag_element->getType());
EXPECT_EQ("world", server_tag_element->stringValue()); EXPECT_EQ("world", server_tag_element->stringValue());
......
...@@ -15,14 +15,12 @@ namespace isc { ...@@ -15,14 +15,12 @@ namespace isc {
namespace db { namespace db {
Server::Server(const ServerTag& tag, const std::string& description) Server::Server(const ServerTag& tag, const std::string& description)
: StampedElement(), description_(description) { : BaseStampedElement(), server_tag_(tag), description_(description) {
if (description_.length() > 65536) { if (description_.length() > 65536) {
isc_throw(BadValue, "server description must not be longer than" isc_throw(BadValue, "server description must not be longer than"
" 65536 characters"); " 65536 characters");
} }
setServerTag(tag.get());
} }
ServerPtr ServerPtr
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
#ifndef DB_SERVER_H #ifndef DB_SERVER_H
#define DB_SERVER_H #define DB_SERVER_H
#include <cc/stamped_element.h> #include <cc/base_stamped_element.h>
#include <cc/server_tag.h>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <string> #include <string>
...@@ -27,7 +28,7 @@ typedef boost::shared_ptr<Server> ServerPtr; ...@@ -27,7 +28,7 @@ typedef boost::shared_ptr<Server> ServerPtr;
/// provided by the administrator and the metadata. /// provided by the administrator and the metadata.
/// ///
/// This class extends the base class with the server description field. /// This class extends the base class with the server description field.
class Server : public data::StampedElement { class Server : public data::BaseStampedElement {
public: public:
/// @brief Constructor. /// @brief Constructor.
...@@ -48,6 +49,18 @@ public: ...@@ -48,6 +49,18 @@ public:
static ServerPtr create(const data::ServerTag& tag, static ServerPtr create(const data::ServerTag& tag,
const std::string& description = ""); const std::string& description = "");
/// @brief Returns server tag.
data::ServerTag getServerTag() const {
return (server_tag_);
}
/// @brief Returns server tag as text.
///
/// @return Server tag as text.
std::string getServerTagAsText() const {
return (server_tag_.get());
}
/// @brief Returns the description of the server. /// @brief Returns the description of the server.
/// ///
/// @return Description of the server or an empty string if no /// @return Description of the server or an empty string if no
...@@ -58,6 +71,9 @@ public: ...@@ -58,6 +71,9 @@ public:
private: private:
/// @brief Server tag.
data::ServerTag server_tag_;
/// @brief Description of the server. /// @brief Description of the server.
std::string description_; std::string description_;
}; };
......
...@@ -28,8 +28,8 @@ typedef boost::multi_index_container< ...@@ -28,8 +28,8 @@ typedef boost::multi_index_container<
boost::multi_index::indexed_by< boost::multi_index::indexed_by<
boost::multi_index::ordered_unique< boost::multi_index::ordered_unique<
boost::multi_index::tag<ServerTagIndexTag>, boost::multi_index::tag<ServerTagIndexTag>,
boost::multi_index::const_mem_fun<data::StampedElement, std::string, boost::multi_index::const_mem_fun<Server, std::string,
&Server::getServerTag> &Server::getServerTagAsText>
> >
> >
> ServerCollection; > ServerCollection;
......
...@@ -24,7 +24,7 @@ TEST(ServerTest, constructor) { ...@@ -24,7 +24,7 @@ TEST(ServerTest, constructor) {
server = Server::create(ServerTag("xyz"), "my first server") server = Server::create(ServerTag("xyz"), "my first server")
); );
ASSERT_TRUE(server); ASSERT_TRUE(server);
EXPECT_EQ("xyz", server->getServerTag()); EXPECT_EQ("xyz", server->getServerTagAsText());
EXPECT_EQ("my first server", server->getDescription()); EXPECT_EQ("my first server", server->getDescription());
} }
...@@ -47,17 +47,17 @@ TEST(ServerFetcherTest, getByTag) { ...@@ -47,17 +47,17 @@ TEST(ServerFetcherTest, getByTag) {
auto alpha = ServerFetcher::get(servers, ServerTag("alpha")); auto alpha = ServerFetcher::get(servers, ServerTag("alpha"));
ASSERT_TRUE(alpha); ASSERT_TRUE(alpha);
EXPECT_EQ("alpha", alpha->getServerTag()); EXPECT_EQ("alpha", alpha->getServerTagAsText());
EXPECT_EQ("alpha description", alpha->getDescription()); EXPECT_EQ("alpha description", alpha->getDescription());
auto beta = ServerFetcher::get(servers, ServerTag("beta")); auto beta = ServerFetcher::get(servers, ServerTag("beta"));
ASSERT_TRUE(beta); ASSERT_TRUE(beta);
EXPECT_EQ("beta", beta->getServerTag()); EXPECT_EQ("beta", beta->getServerTagAsText());
EXPECT_EQ("beta description", beta->getDescription()); EXPECT_EQ("beta description", beta->getDescription());
auto gamma = ServerFetcher::get(servers, ServerTag("gamma")); auto gamma = ServerFetcher::get(servers, ServerTag("gamma"));
ASSERT_TRUE(gamma); ASSERT_TRUE(gamma);
EXPECT_EQ("gamma", gamma->getServerTag()); EXPECT_EQ("gamma", gamma->getServerTagAsText());
EXPECT_EQ("gamma description", gamma->getDescription()); EXPECT_EQ("gamma description", gamma->getDescription());
// Null pointer should be returned when a given server does not exist. // Null pointer should be returned when a given server does not exist.
......
...@@ -891,9 +891,9 @@ typedef boost::multi_index_container< ...@@ -891,9 +891,9 @@ typedef boost::multi_index_container<
// Start definition of index #3 // Start definition of index #3
boost::multi_index::ordered_non_unique< boost::multi_index::ordered_non_unique<
// Use option definition modification time as the index key. // Use option definition modification time as the index key.
// This value is returned by the StampedElement::getModificationTime // This value is returned by the BaseStampedElement::getModificationTime
boost::multi_index::const_mem_fun< boost::multi_index::const_mem_fun<
data::StampedElement, data::BaseStampedElement,
boost::posix_time::ptime, boost::posix_time::ptime,
&data::StampedElement::getModificationTime &data::StampedElement::getModificationTime
> >
...@@ -902,8 +902,8 @@ typedef boost::multi_index_container< ...@@ -902,8 +902,8 @@ typedef boost::multi_index_container<
// Use StampedElement::getId as a key. // Use StampedElement::getId as a key.
boost::multi_index::hashed_non_unique< boost::multi_index::hashed_non_unique<
boost::multi_index::tag<OptionIdIndexTag>, boost::multi_index::tag<OptionIdIndexTag>,
boost::multi_index::const_mem_fun<data::StampedElement, uint64_t, boost::multi_index::const_mem_fun<data::BaseStampedElement, uint64_t,
&data::StampedElement::getId> &data::BaseStampedElement::getId>
> >
>