]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5442] Implemented NetworkState class.
authorMarcin Siodelski <marcin@isc.org>
Fri, 1 Dec 2017 11:06:59 +0000 (12:06 +0100)
committerMarcin Siodelski <marcin@isc.org>
Fri, 1 Dec 2017 11:06:59 +0000 (12:06 +0100)
src/lib/dhcpsrv/Makefile.am
src/lib/dhcpsrv/network_state.cc [new file with mode: 0644]
src/lib/dhcpsrv/network_state.h [new file with mode: 0644]
src/lib/dhcpsrv/tests/Makefile.am
src/lib/dhcpsrv/tests/network_state_unittest.cc [new file with mode: 0644]

index cc3e14863405fd9639dbbbe44758be064cde7223..de492e611bad7d564a5993f1fcdb40d150b207f5 100644 (file)
@@ -140,6 +140,7 @@ endif
 
 libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
 libkea_dhcpsrv_la_SOURCES += network.cc network.h
+libkea_dhcpsrv_la_SOURCES += network_state.cc network_state.h
 
 if HAVE_PGSQL
 libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
diff --git a/src/lib/dhcpsrv/network_state.cc b/src/lib/dhcpsrv/network_state.cc
new file mode 100644 (file)
index 0000000..3b29f66
--- /dev/null
@@ -0,0 +1,145 @@
+// Copyright (C) 2017 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 <exceptions/exceptions.h>
+#include <dhcpsrv/network_state.h>
+#include <dhcpsrv/timer_mgr.h>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Implementation of the @c NetworkState class.
+class NetworkStateImpl : public boost::enable_shared_from_this<NetworkStateImpl> {
+public:
+
+    /// @brief Constructor.
+    NetworkStateImpl()
+        : globally_disabled_(false), disabled_subnets_(), disabled_networks_(),
+          timer_present_(false), timer_mgr_(TimerMgr::instance()) {
+    }
+
+    /// @brief Globally disables or enables DHCP service.
+    void setDisableService(const bool disable) {
+        globally_disabled_ = disable;
+    }
+
+    /// @brief Enables DHCP service globally and per scopes.
+    ///
+    /// If delayed enabling DHCP service has been scheduled, it cancels it.
+    void enableAll() {
+        setDisableService(false);
+
+        /// @todo Enable service for all subnets and networks here.
+
+        destroyTimer();
+    }
+
+    /// @brief Creates a timer couting the time when @c enableAll should be
+    /// automatically called.
+    ///
+    /// If the timer has been already scheduled, it is destroyed and replaced
+    /// with a new timer.
+    ///
+    /// @param seconds Number of seconds to elapse before the @c enableAll is
+    /// called.
+    void createTimer(const unsigned int seconds) {
+        destroyTimer();
+        timer_mgr_->registerTimer("network-state-timer",
+                                  boost::bind(&NetworkStateImpl::enableAll,
+                                              shared_from_this()),
+                                  seconds * 1000,
+                                  asiolink::IntervalTimer::ONE_SHOT);
+        timer_mgr_->setup("network-state-timer");
+        timer_present_ = true;
+    }
+
+    /// @brief Destroys a timer if present.
+    void destroyTimer() {
+        if (timer_present_) {
+            timer_mgr_->unregisterTimer("network-state-timer");
+            timer_present_ = false;
+        }
+    }
+
+    /// @brief A flag indicating if DHCP service is globally disabled.
+    bool globally_disabled_;
+
+    /// @brief A list of subnets for which the DHCP service has been disabled.
+    NetworkState::Subnets disabled_subnets_;
+
+    /// @brief A list of networks for which the DHCP service has been disabled.
+    NetworkState::Networks disabled_networks_;
+
+    /// @brief Boolean flag indicating if the delayed enabling of the DHCP service
+    /// has been scheduled.
+    bool timer_present_;
+
+    /// @brief A pointer to the common timer manager.
+    ///
+    /// This pointer is held here to make sure that the timer manager is not
+    /// destroyed before an instance of this class is destroyed.
+    TimerMgrPtr timer_mgr_;
+};
+
+NetworkState::NetworkState()
+    : impl_(new NetworkStateImpl()) {
+}
+
+void
+NetworkState::disableService() {
+    impl_->setDisableService(true);
+}
+
+void
+NetworkState::enableService() {
+    impl_->setDisableService(false);
+}
+
+void
+NetworkState::enableAll() {
+    impl_->enableAll();
+}
+
+void
+NetworkState::delayedEnableAll(const unsigned int seconds) {
+    impl_->createTimer(seconds);
+}
+
+bool
+NetworkState::isServiceEnabled() const {
+    return (!impl_->globally_disabled_);
+}
+
+bool
+NetworkState::isDelayedEnableAll() const {
+    return (impl_->timer_present_);
+}
+
+void
+NetworkState::selectiveDisable(const NetworkState::Subnets& subnets) {
+    isc_throw(NotImplemented, "selectiveDisableService is not implemented");
+}
+
+void
+NetworkState::selectiveDisable(const NetworkState::Networks& networks) {
+    isc_throw(NotImplemented, "selectiveDisableService is not implemented");
+}
+
+void
+NetworkState::selectiveEnable(const NetworkState::Subnets& subnets) {
+    isc_throw(NotImplemented, "selectiveEnableService is not implemented");
+}
+
+void
+NetworkState::selectiveEnable(const NetworkState::Networks& networks) {
+    isc_throw(NotImplemented, "selectiveEnableService is not implemented");
+}
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/network_state.h b/src/lib/dhcpsrv/network_state.h
new file mode 100644 (file)
index 0000000..300d91d
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2017 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 NETWORK_STATE_H
+#define NETWORK_STATE_H
+
+#include <cc/data.h>
+#include <dhcpsrv/subnet_id.h>
+#include <boost/shared_ptr.hpp>
+#include <set>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+class NetworkStateImpl;
+
+/// @brief Holds information about service enabling for specific networks.
+///
+/// When the DHCP server receives a command to disable DHCP service entirely
+/// or for specific networks, this has to be recorded to allow for re-enabling
+/// DHCP service for these networks as a result of receiving a command from
+/// the administrator or when the tiemout for re-enabling the service occurs.
+///
+/// In the future, it will be possible to specify "disabled" parameter for
+/// a subnet or network to indicate that this subnet should be excluded from
+/// the service. When a command is subsequently sent to disable a service for
+/// some other subnets and the timeout value is specified, only those subnets
+/// for which a timeout has been specified should be re-enabled. This class
+/// fulfils this requirement by re-enabling only those subnets which have been
+/// temporarily disabled. The subnets specified as "disabled" in the configuration
+/// file should remain disabled until explcitly enabled with a control command.
+///
+/// Disabling DHCP service with a timeout is useful to guard against cases when
+/// the controlling client dies after disabling the DHCP service on the server,
+/// e.g. failover peers may instruct each other to disable the DHCP service while
+/// database synchronization takes place. If the peer subsequently dies, the
+/// surviving server must re-enable DHCP on its own.
+///
+/// @todo This class currently supports only the case of completely disabling
+/// the DHCP service. Disabling per network/subnet will be added later.
+class NetworkState {
+public:
+
+    /// @brief Type of the container holding collection of subnet identifiers.
+    typedef std::set<SubnetID> Subnets;
+
+    /// @brief Type of the container holding collection of shared network names.
+    typedef std::set<std::string> Networks;
+
+    /// @brief Constructor.
+    NetworkState();
+
+    /// @brief Globally disables DHCP service.
+    ///
+    /// The DHCP service becomes disabled for all subnets and networks,
+    /// regardless of the per scope settings.
+    void disableService();
+
+    /// @brief Globally enables DHCP service.
+    ///
+    /// The DHCP service becomes enabled but per scope settings are in effect.
+    /// In order to enable the service for all scopes previously disabled with
+    /// a control command, use @c enableAll.
+    void enableService();
+
+    /// @brief Enables DHCP service globally and for scopes which have been
+    /// disabled as a result of control command.
+    void enableAll();
+
+    /// @brief Schedules enabling DHCP service in the future.
+    ///
+    /// @param seconds Number of seconds after which the service should be enabled
+    /// unless @c enableAll is enabled before that time.
+    void delayedEnableAll(const unsigned int seconds);
+
+    /// @brief Checks if the DHCP service is globally enabled.
+    ///
+    /// @return true if the service is globally enabled, false otherwise.
+    bool isServiceEnabled() const;
+
+    /// @brief Checks if delayed enabling of DHCP services is scheduled.
+    ///
+    /// It indicates that the timer is present which counts the time until
+    /// @c enableAll function will be called automatically.
+    ///
+    /// @return true if delayed enabling of the DHCP service is scheduled,
+    /// false otherwise.
+    bool isDelayedEnableAll() const;
+
+    /// @name Selective disabling/enabling DHCP service per scopes
+    //@{
+
+    /// @brief Disable DHCP service for selected subnets.
+    ///
+    /// @param subnets Collection of subnet identifiers for which the service
+    /// should be disabled.
+    ///
+    /// @throw isc::NotImplemented
+    void selectiveDisable(const NetworkState::Subnets& subnets);
+
+    /// @brief Disable DHCP service for selected networks.
+    ///
+    /// @param networks Collection of shared network names for which the service
+    /// should be disabled.
+    ///
+    /// @throw isc::NotImplemented
+    void selectiveDisable(const NetworkState::Networks& networks);
+
+    /// @brief Enable DHCP service for selected subnets.
+    ///
+    /// @param subnets Collection of subnet identifiers for which the service
+    /// should be disabled.
+    ///
+    /// @throw isc::NotImplemented
+    void selectiveEnable(const NetworkState::Subnets& subnets);
+
+    /// @brief Enable DHCP service for selected networks.
+    ///
+    /// @param networks Collection of shared network names for which the service
+    /// should be enabled.
+    ///
+    /// @throw isc::NotImplemented
+    void selectiveEnable(const NetworkState::Networks& networks);
+
+    //@}
+
+private:
+
+    /// @brief Pointer to the @c NetworkState implementation.
+    boost::shared_ptr<NetworkStateImpl> impl_;
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // NETWORK_STATE_H
index 342ed921d8f650eac8fc19b908716de8993338f5..8f55790f3cae1a2af1ea6714a1d4b8601b0ada7a 100644 (file)
@@ -133,6 +133,7 @@ libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_hand
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
 libdhcpsrv_unittests_SOURCES += test_utils.cc test_utils.h
 libdhcpsrv_unittests_SOURCES += timer_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += network_state_unittest.cc
 
 libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 if HAVE_MYSQL
diff --git a/src/lib/dhcpsrv/tests/network_state_unittest.cc b/src/lib/dhcpsrv/tests/network_state_unittest.cc
new file mode 100644 (file)
index 0000000..13f6396
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright (C) 2017 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 <config.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <dhcpsrv/network_state.h>
+#include <dhcpsrv/timer_mgr.h>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace  {
+
+/// @brief Test fixture class for @c NetworkState class.
+class NetworkStateTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor.
+    NetworkStateTest()
+        : io_service_(new IOService()),
+          test_timer_(*io_service_) {
+        TimerMgr::instance()->unregisterTimers();
+        TimerMgr::instance()->setIOService(io_service_);
+    }
+
+    /// @brief Destructor.
+    virtual ~NetworkStateTest() {
+        // Cancel timers.
+        TimerMgr::instance()->unregisterTimers();
+        test_timer_.cancel();
+        // Make sure IO service will stop when no timers are scheduled.
+        io_service_->stopWork();
+        // Run outstanding tasks.
+        io_service_->run();
+    }
+
+    /// @brief Test timer callback stopping IO service.
+    void testTimerCallback() {
+        TimerMgr::instance()->unregisterTimers();
+        io_service_->stop();
+    }
+
+    /// @brief Runs IO service with a timeout.
+    ///
+    /// @param timeout_ms Timeout for runninr IO service in milliseconds.
+    void runIOService(const long timeout_ms) {
+        test_timer_.setup(boost::bind(&NetworkStateTest::testTimerCallback, this), timeout_ms,
+                          IntervalTimer::ONE_SHOT);
+        io_service_->run();
+    }
+
+    /// @brief IO service used during the tests.
+    IOServicePtr io_service_;
+
+    /// @brief Timeout detecting timer.
+    IntervalTimer test_timer_;
+};
+
+// This test verifies that it is possible to disable and then enable service.
+TEST_F(NetworkStateTest, disableEnableService) {
+    NetworkState state;
+    state.disableService();
+    EXPECT_FALSE(state.isServiceEnabled());
+    state.enableService();
+    EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that enableAll() enables the service. This test will be extended
+// in the future to verify that it also enables disabled scopes.
+TEST_F(NetworkStateTest, enableAll) {
+    NetworkState state;
+    state.disableService();
+    EXPECT_FALSE(state.isServiceEnabled());
+    state.enableAll();
+    EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that it is possible to setup delayed execution of enableAll
+// function.
+TEST_F(NetworkStateTest, delayedEnableAll) {
+    NetworkState state;
+    // Disable the service and then schedule enabling it in 1 second.
+    state.disableService();
+    state.delayedEnableAll(1);
+    // Initially the service should be still disabled.
+    EXPECT_FALSE(state.isServiceEnabled());
+    // After running IO service for 2 seconds, the service should be enabled.
+    runIOService(2000);
+    EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that explicitly enabling the service cancels the timer
+// scheduled for automatically enabling it.
+TEST_F(NetworkStateTest, earlyEnableAll) {
+    NetworkState state;
+    // Disable the service.
+    state.disableService();
+    EXPECT_FALSE(state.isServiceEnabled());
+    // Schedule enabling the service in 2 seconds.
+    state.delayedEnableAll(2);
+    // Explicitly enable the service.
+    state.enableAll();
+    // The timer should be now canceled and the service should be enabled.
+    EXPECT_FALSE(state.isDelayedEnableAll());
+    EXPECT_TRUE(state.isServiceEnabled());
+}
+
+// This test verifies that it is possible to call delayedEnableAll multiple times
+// and that it results in only one timer being scheduled.
+TEST_F(NetworkStateTest, multipleDelayedEnableAll) {
+    NetworkState state;
+    // Disable the service and then schedule enabling it in 1 second.
+    state.disableService();
+    // Schedule the first timer for 5 seconds.
+    state.delayedEnableAll(5);
+    // When calling it the second time the old timer should be destroyed and
+    // the timeout should be set to 2 seconds.
+    state.delayedEnableAll(2);
+    // Initially the service should be still disabled.
+    EXPECT_FALSE(state.isServiceEnabled());
+    // After running IO service for 3 seconds, the service should be enabled.
+    runIOService(3000);
+    EXPECT_TRUE(state.isServiceEnabled());
+    // The timer should not be present, even though the first timer was created
+    // with 5 seconds interval.
+    EXPECT_FALSE(state.isDelayedEnableAll());
+}
+
+
+} // end of anonymous namespace