]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3253] Added AlarmStore classes
authorThomas Markwalder <tmark@isc.org>
Tue, 27 Feb 2024 20:30:55 +0000 (15:30 -0500)
committerThomas Markwalder <tmark@isc.org>
Mon, 18 Mar 2024 19:12:14 +0000 (19:12 +0000)
src/hooks/dhcp/perfmon/alarm_store.h
src/hooks/dhcp/perfmon/alarm_store.cc
src/hooks/dhcp/perfmon/testsalarm_store_unittests.cc
    new files

src/hooks/dhcp/perfmon/Makefile.am
   Added alarm_store.cc and alarm_store.h

src/hooks/dhcp/perfmon/Makefile.am
src/hooks/dhcp/perfmon/alarm_store.cc [new file with mode: 0644]
src/hooks/dhcp/perfmon/alarm_store.h [new file with mode: 0644]
src/hooks/dhcp/perfmon/monitored_duration_store.cc
src/hooks/dhcp/perfmon/monitored_duration_store.h
src/hooks/dhcp/perfmon/tests/Makefile.am
src/hooks/dhcp/perfmon/tests/alarm_store_unittests.cc [new file with mode: 0644]
src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc

index f981893c695871a6ef4f238fa47e9dc28a0cea14..36302b8cc25b700f7d063d3c8380ea1dab5d5139 100644 (file)
@@ -20,6 +20,7 @@ libperfmon_la_SOURCES += perfmon_messages.cc perfmon_messages.h
 libperfmon_la_SOURCES += monitored_duration.cc monitored_duration.h
 libperfmon_la_SOURCES += alarm.cc alarm.h
 libperfmon_la_SOURCES += monitored_duration_store.cc monitored_duration_store.h
+libperfmon_la_SOURCES += alarm_store.cc alarm_store.h
 libperfmon_la_SOURCES += version.cc
 
 libperfmon_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/hooks/dhcp/perfmon/alarm_store.cc b/src/hooks/dhcp/perfmon/alarm_store.cc
new file mode 100644 (file)
index 0000000..582daf8
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright (C) 2024 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 <exceptions/exceptions.h>
+#include <alarm_store.h>
+#include <util/multi_threading_mgr.h>
+
+using namespace isc;
+using namespace isc::util;
+
+namespace isc {
+namespace perfmon {
+
+AlarmStore::AlarmStore(uint16_t family)
+    : family_(family),
+      alarms_(),
+      mutex_(new std::mutex) {
+    if (family_ != AF_INET && family_ != AF_INET6) {
+        isc_throw(BadValue, "AlarmStore - invalid family "
+                            << family_ << ", must be AF_INET or AF_INET6");
+    }
+}
+
+void
+AlarmStore::validateKey(const std::string& label, DurationKeyPtr key) const {
+    if (!key) {
+        isc_throw(BadValue, "AlarmStore::" << label << " - key is empty");
+    }
+
+    if (key->getFamily() != family_) {
+        isc_throw(BadValue, "AlarmStore::" << label
+                            << " - family mismatch, key is " << (family_ == AF_INET ?
+                            "v6, store is v4" : "v4, store is v6"));
+    }
+}
+
+AlarmPtr
+AlarmStore::addAlarm(AlarmPtr alarm) {
+    {
+        MultiThreadingLock lock(*mutex_);
+        auto ret = alarms_.insert(alarm);
+        if (ret.second == false) {
+            isc_throw(DuplicateAlarm,
+                      "AlarmStore::addAlarm: alarm already exists for: "
+                      << alarm->getLabel());
+        }
+    }
+
+    // Return a copy of what we inserted.
+    return (AlarmPtr(new Alarm(*alarm)));
+}
+
+AlarmPtr
+AlarmStore::addAlarm(DurationKeyPtr key, const Duration& low_water,
+                      const Duration& high_water, bool enabled /* = true */) {
+    validateKey("addAlarm", key);
+
+    // Create the alarm instance.
+    AlarmPtr alarm;
+    try {
+        alarm.reset(new Alarm(*key, low_water, high_water, enabled));
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "AlarmStore::addAlarm failed: " << ex.what());
+    }
+
+    return(addAlarm(alarm));
+}
+
+AlarmPtr
+AlarmStore::getAlarm(DurationKeyPtr key) {
+    validateKey("getAlarm", key);
+
+    MultiThreadingLock lock(*mutex_);
+    const auto& index = alarms_.get<AlarmPrimaryKeyTag>();
+    auto alarm_iter = index.find(*key);
+    return (alarm_iter == index.end() ? AlarmPtr()
+            : AlarmPtr(new Alarm(**alarm_iter)));
+}
+
+void
+AlarmStore::updateAlarm(AlarmPtr& alarm) {
+    validateKey("updateAlarm", alarm);
+
+    MultiThreadingLock lock(*mutex_);
+    auto& index = alarms_.get<AlarmPrimaryKeyTag>();
+    auto alarm_iter = index.find(*alarm);
+    if (alarm_iter == index.end()) {
+        isc_throw(InvalidOperation, "AlarmStore::updateAlarm alarm not found: "
+                  << alarm->getLabel());
+    }
+
+    // Use replace() which only re-indexes if keys change.
+    index.replace(alarm_iter, AlarmPtr(new Alarm(*alarm)));
+}
+
+void
+AlarmStore::deleteAlarm(DurationKeyPtr key) {
+    validateKey("deleteAlarm", key);
+
+    MultiThreadingLock lock(*mutex_);
+    auto& index = alarms_.get<AlarmPrimaryKeyTag>();
+    auto alarm_iter = index.find(*key);
+    if (alarm_iter == index.end()) {
+        // Not there, just return.
+        return;
+    }
+
+    // Remove the alarm from the store.
+    alarms_.erase(alarm_iter);
+}
+
+AlarmCollectionPtr
+AlarmStore::getAll() {
+    MultiThreadingLock lock(*mutex_);
+    const auto& index = alarms_.get<AlarmPrimaryKeyTag>();
+    AlarmCollectionPtr collection(new AlarmCollection());
+    for (auto alarm_iter = index.begin(); alarm_iter != index.end(); ++alarm_iter) {
+        collection->push_back(AlarmPtr(new Alarm(**alarm_iter)));
+    }
+
+    return (collection);
+}
+
+void
+AlarmStore::clear() {
+    MultiThreadingLock lock(*mutex_);
+    alarms_.clear();
+}
+
+} // end of namespace perfmon
+} // end of namespace isc
+
diff --git a/src/hooks/dhcp/perfmon/alarm_store.h b/src/hooks/dhcp/perfmon/alarm_store.h
new file mode 100644 (file)
index 0000000..35a6a3e
--- /dev/null
@@ -0,0 +1,165 @@
+// Copyright (C) 2024 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 ALARM_STORE_H
+#define ALARM_STORE_H
+
+#include <exceptions/exceptions.h>
+#include <alarm.h>
+
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <string>
+
+namespace isc {
+namespace perfmon {
+
+/// @brief Exception thrown when an attempt was made to add a duplicate key
+/// to either the duration or alarm stores.
+class DuplicateAlarm : public Exception {
+public:
+    DuplicateAlarm(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Tag for index by primary key (DurationKey).
+struct AlarmPrimaryKeyTag { };
+
+/// @brief A multi index container holding pointers to Alarms.
+///
+/// The durations in the container may be accessed using different indexes:
+/// - using the full key index
+///   <TBD>
+///
+/// Indexes can be accessed using the index number (from 0 to n) or a
+/// name tag. It is recommended to use the tags to access indexes as
+/// they do not depend on the order of indexes in the container.
+typedef boost::multi_index_container<
+    // It holds pointers to Lease6 objects.
+    AlarmPtr,
+    boost::multi_index::indexed_by<
+        // Specification of the first index starts here.
+        // This index sorts using DurationKey::operators
+        boost::multi_index::ordered_unique<
+            boost::multi_index::tag<AlarmPrimaryKeyTag>,
+            boost::multi_index::identity<DurationKey>
+        >
+    >
+> AlarmContainer;
+
+/// @brief Type for a collection of AlarmPtrs.
+typedef std::vector<AlarmPtr> AlarmCollection;
+
+/// @brief Type for a pointer to a collection of AlarmPtrs.
+typedef boost::shared_ptr<AlarmCollection> AlarmCollectionPtr;
+
+/// @brief Maintains an in-memory store of Alarms
+///
+/// Provides essential CRUD functions for managing a collection of
+/// Alarms.  Additionally there are finders that can return
+/// durations by DurationKey  <TBD>
+/// All finders return copies of the durations found, rather than the
+/// stored duration itself.
+class AlarmStore {
+public:
+    /// @brief Constructor
+    ///
+    /// @param family protocol family AF_INET or AF_INET6
+    explicit AlarmStore(uint16_t family);
+
+    /// @brief Destructor
+    ~AlarmStore() = default;
+
+    /// @brief Creates a new Alarm and adds it to the store
+    ///
+    /// @param key key value of the Alarm to create.
+    /// @param low_water threshold below which the average duration must fall to clear the alarm
+    /// @brief high_water threshold above which the average duration must rise to trigger the alarm.
+    /// @brief enabled true sets state to CLEAR, otherwise DISABLED, defaults to true.
+    ///
+    /// @return pointer to the newly created Alarm.
+    /// @throw DuplicateAlarm if a duration for the given key already exists in
+    /// the store.
+    AlarmPtr addAlarm(DurationKeyPtr key, const Duration& low_water,
+                      const Duration& high_water, bool enabled = true);
+
+    /// @brief Adds an Alarm  to the store.
+    ///
+    /// @return pointer to a copy of the Alarm added.
+    AlarmPtr addAlarm(AlarmPtr alarm);
+
+    /// @brief Fetches a duration from the store for a given key.
+    ///
+    /// @param key key value of the alarm to fetch.
+    ///
+    /// @return Pointer the desired alarm or an empty pointer.
+    AlarmPtr getAlarm(DurationKeyPtr key);
+
+    /// @brief Updates an alarm in the store.
+    ///
+    /// The alarm is assumed to already exist in the store.
+    ///
+    /// @param alarm alarm to update.
+    ///
+    /// @throw InvalidOperation if the alarm does not exist in the store.
+    void updateAlarm(AlarmPtr& alarm);
+
+    /// @brief Removes the alarm from the store.
+    ///
+    /// If the alarm does not exist in the store, it simply returns.
+    ///
+    /// @param key key value of the alarm to delete.
+    void deleteAlarm(DurationKeyPtr key);
+
+    /// @brief Fetches all of the alarms (in order by target)
+    ///
+    /// @return a collection of all alarms in the store.
+    AlarmCollectionPtr getAll();
+
+    /// @brief Removes all alarms from the store.
+    void clear();
+
+    /// @brief Get protocol family
+    ///
+    /// @return uint16_t containing the family (AF_INET or AF_INET6)
+    uint16_t getFamily() {
+        return (family_);
+    }
+
+private:
+    /// @brief Convenience method to verify a key is valid for an operation.
+    ///
+    /// @param label description of where the check is being made, appears in exception text.
+    /// @param key key to validate
+    ///
+    /// @throw BadValue if the key is either empty or its family does not
+    /// match the store.
+    void validateKey(const std::string& label, DurationKeyPtr key) const;
+
+    /// @brief Protocol family AF_INET or AF_INET6.
+    uint16_t family_;
+
+    /// @brief Container instance.
+    AlarmContainer alarms_;
+
+    /// @brief The mutex used to protect internal state.
+    const boost::scoped_ptr<std::mutex> mutex_;
+};
+
+typedef boost::shared_ptr<AlarmStore> AlarmStorePtr;
+
+} // end of namespace perfmon
+} // end of namespace isc
+
+#endif
index 5d51f8be73defc8e13d4638349ac45f3b405b942..7e20708b028a661316b38db6d1f20baa59f5f641 100644 (file)
@@ -22,6 +22,11 @@ MonitoredDurationStore::MonitoredDurationStore(uint16_t family,
       interval_duration_(interval_duration),
       durations_(),
       mutex_(new std::mutex) {
+    if (family != AF_INET && family_ != AF_INET6) {
+        isc_throw(BadValue, "MonitoredDurationStore - invalid family "
+                            << family_ << ", must be AF_INET or AF_INET6");
+    }
+
     if (interval_duration_ <= DurationDataInterval::ZERO_DURATION()) {
         isc_throw(BadValue, "MonitoredDurationStore - invalid interval_duration "
                             << interval_duration_ << ", must be greater than zero");
@@ -96,7 +101,7 @@ MonitoredDurationStore::updateDuration(MonitoredDurationPtr& duration) {
                   << duration->getLabel());
     }
 
-    // Use replace() to re-index durations.
+    // Use replace() which only re-indexes if keys change.
     index.replace(duration_iter, MonitoredDurationPtr(new MonitoredDuration(*duration)));
 }
 
index ea9a7768c36c3c693fd4326218ef7775d8da3a71..b5d9f9a91268687de673848aaffe22b5b1677114 100644 (file)
@@ -157,7 +157,7 @@ private:
 
 typedef boost::shared_ptr<MonitoredDurationStore> MonitoredDurationStorePtr;
 
-} // end of namespace ping_check
+} // end of namespace perfmon
 } // end of namespace isc
 
 #endif
index 75dc950b671926f4cd26888c02c2b4ea1e3fc272..e74c1d8c3395f175f29b44483b0161d26ae16e9b 100644 (file)
@@ -30,6 +30,7 @@ perfmon_unittests_SOURCES = run_unittests.cc
 perfmon_unittests_SOURCES += monitored_duration_unittests.cc
 perfmon_unittests_SOURCES += alarm_unittests.cc
 perfmon_unittests_SOURCES += monitored_duration_store_unittests.cc
+perfmon_unittests_SOURCES += alarm_store_unittests.cc
 
 perfmon_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 
diff --git a/src/hooks/dhcp/perfmon/tests/alarm_store_unittests.cc b/src/hooks/dhcp/perfmon/tests/alarm_store_unittests.cc
new file mode 100644 (file)
index 0000000..7d22486
--- /dev/null
@@ -0,0 +1,430 @@
+// Copyright (C) 2024 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/.
+
+/// @file This file contains tests which exercise the AlarmStore class.
+#include <config.h>
+#include <alarm_store.h>
+#include <dhcp/dhcp6.h>
+#include <testutils/gtest_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfmon;
+using namespace isc::test;
+using namespace boost::posix_time;
+
+namespace {
+
+// Verifies AlarmStore valid construction.
+TEST(AlarmStore, validConstructors) {
+    AlarmStorePtr store;
+
+    // Construct and verify v4 store.
+    EXPECT_NO_THROW_LOG(store.reset(new AlarmStore(AF_INET)));
+    ASSERT_TRUE(store);
+    EXPECT_EQ(store->getFamily(), AF_INET);
+
+    AlarmCollectionPtr alarms;
+    ASSERT_NO_THROW_LOG(alarms = store->getAll());
+    ASSERT_TRUE(alarms);
+    EXPECT_TRUE(alarms->empty());
+
+    // Construct and verify v6 store.
+    EXPECT_NO_THROW_LOG(store.reset(new AlarmStore(AF_INET6)));
+    ASSERT_TRUE(store);
+    EXPECT_EQ(store->getFamily(), AF_INET6);
+
+    ASSERT_NO_THROW_LOG(alarms = store->getAll());
+    ASSERT_TRUE(alarms);
+    EXPECT_TRUE(alarms->empty());
+}
+
+// Verifies AlarmStore invalid construction.
+TEST(AlarmStore, invalidConstructors) {
+    AlarmStorePtr store;
+
+    // Invalid family should throw.
+    EXPECT_THROW_MSG(AlarmStore(777),
+                     BadValue,
+                     "AlarmStore - invalid family 777, must be AF_INET or AF_INET6");
+}
+
+/// @brief Text fixture class for @c AlarmStore
+///
+/// In order to facilitate single and multi threaded testing,
+/// individual tests are implemented as methods that are called
+/// from within TEST_F bodies rather than in TEST_F bodies.
+class AlarmStoreTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    AlarmStoreTest() = default;
+
+    /// @brief Destructor
+    virtual ~AlarmStoreTest() = default;
+
+    /// @brief Creates a protocol-specific DurationKey for a given subnet
+    ///
+    /// The message-pair and socket-event pairs are fixed.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    /// @param subnet SubnetID of the duration
+    DurationKeyPtr makeKey(uint16_t family, SubnetID subnet = 1) {
+        DurationKeyPtr key;
+        if (family == AF_INET) {
+            return (DurationKeyPtr(new DurationKey(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                                   "socket_received", "buffer_read", subnet)));
+        }
+
+        return (DurationKeyPtr(new DurationKey(AF_INET6, DHCPV6_SOLICIT, DHCPV6_REPLY,
+                                               "socket_received", "buffer_read", subnet)));
+    }
+
+    /// @brief Verifies that alarms can be added to the store and fetched
+    /// by DurationKey.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void addAlarmTest(uint16_t family) {
+        Duration low_water(milliseconds(10));
+        Duration high_water(milliseconds(250));
+        AlarmStore store(family);
+
+        // Add four alarms with decreaing subnet ids.
+        std::vector<AlarmPtr> orig_alarms;
+        for (int subnet = 4; subnet > 0; --subnet) {
+            AlarmPtr alarm;
+            ASSERT_NO_THROW_LOG(alarm = store.addAlarm(makeKey(family, subnet),
+                                                       low_water, high_water));
+            ASSERT_TRUE(alarm);
+            orig_alarms.push_back(alarm);
+        }
+
+        // Get all should retrieve all four in ascending order.
+        AlarmCollectionPtr alarms = store.getAll();
+        ASSERT_EQ(alarms->size(), orig_alarms.size());
+
+        int idx = orig_alarms.size() - 1;
+        for (auto const& d : *alarms) {
+            EXPECT_EQ(*d, *orig_alarms[idx]) << "failed on pass :" << idx;
+            --idx;
+        }
+
+        // Make sure we can fetch them all individually.
+        for (auto const& alarm : orig_alarms) {
+            AlarmPtr found;
+            ASSERT_NO_THROW_LOG(found = store.getAlarm(alarm));
+            ASSERT_TRUE(found);
+            EXPECT_EQ(*alarm, *found);
+        }
+
+        // Verify that clear() discards store contents.
+        store.clear();
+        alarms = store.getAll();
+        ASSERT_TRUE(alarms->empty());
+    }
+    /// @brief Verifies that duplicate alarms cannot be added to the store.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void addAlarmDuplicateTest(uint16_t family) {
+        AlarmStore store(family);
+
+        // Add a duration.
+        AlarmPtr alarm;
+        ASSERT_NO_THROW_LOG(alarm = store.addAlarm(makeKey(family), milliseconds(10),
+                                                   milliseconds(250)));
+        ASSERT_TRUE(alarm);
+
+        // Attempting to add it again should evoke a duplicate key exception.
+        ASSERT_THROW(store.addAlarm(alarm), DuplicateAlarm);
+    }
+
+    /// @brief Verifies that duration key must be valid to add a duration to the store.
+    ///
+    /// Tests both v4 and v6.
+    void addAlarmInvalidTest() {
+        // Create a v4 store.
+        AlarmStorePtr store(new AlarmStore(AF_INET));
+
+        // Attempting to add with an empty key should throw.
+        ASSERT_THROW_MSG(store->addAlarm(DurationKeyPtr(),
+                         milliseconds(10), milliseconds(250)),
+                         BadValue,
+                         "AlarmStore::addAlarm - key is empty");
+
+        // Attempting to a v6 key should fail.
+        ASSERT_THROW_MSG(store->addAlarm(makeKey(AF_INET6),
+                         milliseconds(10), milliseconds(250)),
+                         BadValue,
+                         "AlarmStore::addAlarm"
+                         " - family mismatch, key is v6, store is v4");
+
+        // Create a v6 store.
+        store.reset(new AlarmStore(AF_INET6));
+
+        // Attempting to add a v4 key should fail.
+        ASSERT_THROW_MSG(store->addAlarm(makeKey(AF_INET),
+                         milliseconds(10), milliseconds(250)),
+                         BadValue,
+                         "AlarmStore::addAlarm"
+                         " - family mismatch, key is v4, store is v6");
+    }
+
+    /// @brief Verify that alarms can be deleted from the store.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void deleteAlarmTest(uint16_t family) {
+        AlarmStore store(family);
+
+        std::vector<DurationKeyPtr> keys;
+        for (int subnet = 0; subnet < 3; ++subnet) {
+            AlarmPtr alarm;
+            DurationKeyPtr key = makeKey(family, subnet);
+            ASSERT_NO_THROW_LOG(alarm = store.addAlarm(key, milliseconds(10), milliseconds(250)));
+            ASSERT_TRUE(alarm);
+            keys.push_back(key);
+        }
+
+        // Verify we added three of them.
+        auto alarms = store.getAll();
+        ASSERT_EQ(alarms->size(), 3);
+
+        // Fetch the second duration.
+        AlarmPtr alarm;
+        ASSERT_NO_THROW_LOG(alarm = store.getAlarm(keys[1]));
+        ASSERT_TRUE(alarm);
+        EXPECT_EQ(*alarm, *(keys[1]));
+
+        // Delete it.
+        ASSERT_NO_THROW_LOG(store.deleteAlarm(alarm));
+
+        // Try to fetch it, shouldn't find it.
+        AlarmPtr alarm2;
+        ASSERT_NO_THROW_LOG(alarm2 = store.getAlarm(alarm));
+        ASSERT_FALSE(alarm2);
+
+        // Deleting it again should do no harm.
+        ASSERT_NO_THROW_LOG(store.deleteAlarm(alarm));
+
+        // Verify there are two left.
+        alarms = store.getAll();
+        ASSERT_EQ(alarms->size(), 2);
+    }
+
+    /// @brief Verify an invalid duration key on delete is detected.
+    ///
+    /// Tests both v4 and v6.
+    void deleteAlarmInvalidTest() {
+         // Create a v4 store.
+        AlarmStorePtr store(new AlarmStore(AF_INET));
+
+        // Attempting to delete an empty key should throw.
+        DurationKeyPtr key;
+        ASSERT_THROW_MSG(store->deleteAlarm(key),
+                         BadValue,
+                         "AlarmStore::deleteAlarm - key is empty");
+
+        // Attempting to delete a v6 key should fail.
+        ASSERT_THROW_MSG(store->deleteAlarm(makeKey(AF_INET6)),
+                         BadValue,
+                         "AlarmStore::deleteAlarm"
+                         " - family mismatch, key is v6, store is v4");
+
+        // Create a v6 store.
+        store.reset(new AlarmStore(AF_INET6));
+
+        // Attempting to delete a v4 key should fail.
+        ASSERT_THROW_MSG(store->deleteAlarm(makeKey(AF_INET)),
+                         BadValue,
+                         "AlarmStore::deleteAlarm"
+                         " - family mismatch, key is v4, store is v6");
+    }
+
+
+    /// @brief Verify that alarms in the store can be updated.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void updateAlarmTest(uint16_t family) {
+        AlarmStore store(family);
+
+        // Add the duration to the store.
+        AlarmPtr alarm;
+        ASSERT_NO_THROW(alarm.reset(new Alarm(*makeKey(family), milliseconds(10),
+                                              milliseconds(250))));
+        ASSERT_NO_THROW(store.addAlarm(alarm));
+
+        // Fetch it.
+        AlarmPtr found;
+        ASSERT_NO_THROW_LOG(found = store.getAlarm(alarm));
+        ASSERT_TRUE(found);
+
+        // Verify the fetched object is a copy.
+        ASSERT_NE(found, alarm);
+        ASSERT_EQ(*found, *alarm);
+
+        // Now change the thresholds and update it.
+        alarm->setLowWater(milliseconds(125));
+        alarm->setHighWater(milliseconds(500));
+        ASSERT_NO_THROW(store.updateAlarm(alarm));
+
+        // Fetch it again.
+        ASSERT_NO_THROW_LOG(found = store.getAlarm(alarm));
+
+        // Verify it has the expected thresholds.
+        EXPECT_EQ(found->getLowWater(), milliseconds(125));
+        EXPECT_EQ(found->getHighWater(), milliseconds(500));
+    }
+
+    /// @brief Verify an invalid duration key on update is detected.
+    ///
+    /// Tests both v4 and v6.
+    void updateAlarmInvalidTest() {
+        AlarmPtr alarm;
+
+         // Create a v4 store.
+        AlarmStorePtr store(new AlarmStore(AF_INET));
+
+        // Attempting to update an empty key should throw.
+        ASSERT_THROW_MSG(store->updateAlarm(alarm),
+                         BadValue,
+                         "AlarmStore::updateAlarm - key is empty");
+
+        // Create a v6 alarm.
+        ASSERT_NO_THROW_LOG(alarm.reset(new Alarm(*makeKey(AF_INET6), milliseconds(10),
+                                                   milliseconds(250))));
+
+        // Attempting to update v6 alarm to a v4 store should fail.
+        ASSERT_THROW_MSG(store->updateAlarm(alarm),
+                         BadValue,
+                         "AlarmStore::updateAlarm"
+                         " - family mismatch, key is v6, store is v4");
+
+        // Create a v6 store.
+        store.reset(new AlarmStore(AF_INET6));
+
+        // Updating a non-existent alarm should fail.
+        ASSERT_THROW_MSG(store->updateAlarm(alarm),
+                         InvalidOperation,
+                         "AlarmStore::updateAlarm alarm not found:"
+                         " SOLICIT-REPLY.socket_received-buffer_read.1");
+
+        // Create a v4 alarm.
+        ASSERT_NO_THROW_LOG(alarm.reset(new Alarm(*makeKey(AF_INET), milliseconds(10),
+                                                   milliseconds(250))));
+
+        // Attempting to update v4 duration to a v6 store fail.
+        ASSERT_THROW_MSG(store->updateAlarm(alarm),
+                         BadValue,
+                         "AlarmStore::updateAlarm"
+                         " - family mismatch, key is v4, store is v6");
+    }
+};
+
+TEST_F(AlarmStoreTest, addAlarm) {
+    addAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, addAlarmMultiThreading) {
+    MultiThreadingTest mt;
+    addAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, addAlarm6) {
+    addAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, addAlarm6MultiThreading) {
+    MultiThreadingTest mt;
+    addAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, addAlarmDuplicate) {
+    addAlarmDuplicateTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, addAlarmDuplicateMultiThreading) {
+    MultiThreadingTest mt;
+    addAlarmDuplicateTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, addAlarm6Duplicate) {
+    addAlarmDuplicateTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, addAlarm6DuplicateMultiThreading) {
+    MultiThreadingTest mt;
+    addAlarmDuplicateTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, addAlarmInvalid) {
+    addAlarmInvalidTest();
+}
+
+TEST_F(AlarmStoreTest, addAlarmInvalidMultiThreading) {
+    MultiThreadingTest mt;
+    addAlarmInvalidTest();
+}
+
+TEST_F(AlarmStoreTest, deleteAlarm) {
+    deleteAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, deleteAlarmMultiThreading) {
+    MultiThreadingTest mt;
+    deleteAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, deleteAlarm6) {
+    deleteAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, deleteAlarm6MultiThreading) {
+    MultiThreadingTest mt;
+    deleteAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, deleteAlarmInvalid) {
+    deleteAlarmInvalidTest();
+}
+
+TEST_F(AlarmStoreTest, deleteAlarmInvalidMultiThreading) {
+    MultiThreadingTest mt;
+    deleteAlarmInvalidTest();
+}
+
+TEST_F(AlarmStoreTest, updateAlarm) {
+    updateAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, updateAlarmMultiThreading) {
+    MultiThreadingTest mt;
+    updateAlarmTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, updateAlarm6) {
+    updateAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, updateAlarm6MultiThreading) {
+    MultiThreadingTest mt;
+    updateAlarmTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, updateAlarmInvalid) {
+    updateAlarmInvalidTest();
+}
+
+TEST_F(AlarmStoreTest, updateAlarmInvalidMultiThreading) {
+    MultiThreadingTest mt;
+    updateAlarmInvalidTest();
+}
+
+
+} // end of anonymous namespace
index 6a19c9a7e1c21839ade8467835f432524ec6776d..5b94d04d3fd793963e14d118d1eb72c8b9f5201e 100644 (file)
@@ -40,6 +40,10 @@ TEST(MonitoredDurationStore, validConstructors) {
 TEST(MonitoredDurationStore, invalidConstructors) {
     MonitoredDurationStorePtr store;
 
+    EXPECT_THROW_MSG(MonitoredDurationStore(777, seconds(60)),
+                     BadValue,
+                     "MonitoredDurationStore - invalid family 777, must be AF_INET or AF_INET6");
+
     EXPECT_THROW_MSG(MonitoredDurationStore(AF_INET, milliseconds(0)),
                      BadValue,
                      "MonitoredDurationStore - invalid interval_duration"
@@ -82,7 +86,7 @@ public:
                                                "socket_received", "buffer_read", subnet)));
     }
 
-    /// @brief Verifies that durations be added to the store and fetched
+    /// @brief Verifies that durations can be added to the store and fetched
     /// by DurationKey.
     ///
     /// @param family protocol family to test, AF_INET or AF_INET6
@@ -288,12 +292,12 @@ public:
         MonitoredDurationPtr mond;
 
          // Create a v4 store.
-        MonitoredDurationStorePtr store(new MonitoredDurationStore(AF_INET6, interval_duration));
+        MonitoredDurationStorePtr store(new MonitoredDurationStore(AF_INET, interval_duration));
 
         // Attempting to update an empty key should throw.
         ASSERT_THROW_MSG(store->updateDuration(mond),
                          BadValue,
-                         "MonitoredDurationStore::deleteDuration - key is empty");
+                         "MonitoredDurationStore::updateDuration - key is empty");
 
         // Create a v6 duration.
         ASSERT_NO_THROW_LOG(mond.reset(new MonitoredDuration(*makeKey(AF_INET6), interval_duration)));
@@ -309,9 +313,9 @@ public:
 
         // Updating a non-existent duration should fail.
         ASSERT_THROW_MSG(store->updateDuration(mond),
-                         BadValue,
+                         InvalidOperation,
                          "MonitoredDurationStore::updateDuration duration not found:"
-                         " SOLICIT-ADVERTISE.mt_queued-process_started.77");
+                         " SOLICIT-REPLY.socket_received-buffer_read.1");
 
         // Create a v4 duration.
         ASSERT_NO_THROW_LOG(mond.reset(new MonitoredDuration(*makeKey(AF_INET), interval_duration)));
@@ -414,4 +418,13 @@ TEST_F(MonitoredDurationStoreTest, updateDuration6MultiThreading) {
     updateDurationTest(AF_INET6);
 }
 
+TEST_F(MonitoredDurationStoreTest, updateDurationInvalid) {
+    updateDurationInvalidTest();
+}
+
+TEST_F(MonitoredDurationStoreTest, updateDurationInvalidMultiThreading) {
+    MultiThreadingTest mt;
+    updateDurationInvalidTest();
+}
+
 } // end of anonymous namespace