}
}
+void
+CfgMgr::revert(const size_t index) {
+ ensureCurrentAllocated();
+ if (index == 0) {
+ isc_throw(isc::OutOfRange, "invalid commit index 0 when reverting"
+ " to an old configuration");
+ } else if (index > configs_.size() - 1) {
+ isc_throw(isc::OutOfRange, "unable to revert to commit index '"
+ << index << "', only '" << configs_.size() - 1
+ << "' previous commits available");
+ }
+
+ // Let's rollback an existing configuration to make sure that the last
+ // configuration on the list is the current one. Note that all remaining
+ // operations in this function should be exception free so there shouldn't
+ // be a problem that the revert operation fails and the staging
+ // configuration is destroyed by this rollback.
+ rollback();
+
+ // Get the iterator to the current configuration and then advance to the
+ // desired one.
+ ConfigurationList::const_reverse_iterator it = configs_.rbegin();
+ std::advance(it, index);
+
+ // Copy the desired configuration to the new staging configuration. The
+ // staging configuration is re-created here because we rolled back earlier
+ // in this function.
+ (*it)->copy(*getStagingCfg());
+
+ // Make the staging configuration a current one.
+ commit();
+}
+
ConstConfigurationPtr
CfgMgr::getCurrentCfg() {
ensureCurrentAllocated();
/// This function is exception safe.
void rollback();
+ /// @brief Reverts to one of the previous configurations.
+ ///
+ /// This function reverts to selected previous configuration. The previous
+ /// configuration is entirely copied to a new @c Configuration instance. This
+ /// new instance has a unique sequence id (sequence id is not copied). The
+ /// previous configuration (being copied) is not modified by this operation.
+ ///
+ /// The configuration to be copied is identified by the index value which
+ /// is the distance between the current (most recent) and desired
+ /// configuration. If the index is out of range an exception is thrown.
+ ///
+ /// @warning Revert operation will rollback any changes to the staging
+ /// configuration (if it exists).
+ ///
+ /// @throw isc::OutOfRange if the specified index is out of range.
+ void revert(const size_t index);
+
/// @brief Returns a pointer to the current configuration.
///
/// This function returns pointer to the current configuration. If the
EXPECT_EQ(0, config->getLoggingInfo().size());
}
+// This test verifies that it is possible to revert to an old configuration.
+TEST_F(CfgMgrTest, revert) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ // Let's create 5 unique configurations: differing by a debug level in the
+ // range of 10 to 14.
+ for (int i = 0; i < 5; ++i) {
+ ConfigurationPtr config = cfg_mgr.getStagingCfg();
+ LoggingInfo logging_info;
+ logging_info.debuglevel_ = i + 10;
+ config->addLoggingInfo(logging_info);
+ cfg_mgr.commit();
+ }
+
+ // Now we have 5 configurations with:
+ // - debuglevel = 99 (a default one)
+ // - debuglevel = 10
+ // - debuglevel = 11
+ // - debuglevel = 12
+ // - debuglevel = 13
+ // - debuglevel = 14 (current)
+
+ // Hence, the maximum index of the configuration to revert is 5 (which
+ // points to the configuration with debuglevel = 99). For the index greater
+ // than 5 we should get an exception.
+ ASSERT_THROW(cfg_mgr.revert(6), isc::OutOfRange);
+ // Value of 0 also doesn't make sense.
+ ASSERT_THROW(cfg_mgr.revert(0), isc::OutOfRange);
+
+ // We should be able to revert to configuration with debuglevel = 10.
+ ASSERT_NO_THROW(cfg_mgr.revert(4));
+ // And this configuration should be now the current one and the debuglevel
+ // of this configuration is 10.
+ EXPECT_EQ(10, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
+
+ // The new set of configuration is now as follows:
+ // - debuglevel = 99
+ // - debuglevel = 10
+ // - debuglevel = 11
+ // - debuglevel = 12
+ // - debuglevel = 13
+ // - debuglevel = 14
+ // - debuglevel = 10 (current)
+ // So, reverting to configuration having index 3 means that the debug level
+ // of the current configuration will become 12.
+ ASSERT_NO_THROW(cfg_mgr.revert(3));
+ EXPECT_EQ(12, cfg_mgr.getCurrentCfg()->getLoggingInfo()[0].debuglevel_);
+}
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
/// - addActiveIface() with the same interface twice