Browse Source

Merge branch 'trac2852_3'

Conflicts:
	src/lib/datasrc/client_list.h
Mukund Sivaraman 12 years ago
parent
commit
d97165b669

+ 15 - 0
src/lib/datasrc/client_list.cc

@@ -322,6 +322,21 @@ ConfigurableClientList::findInternal(MutableResult& candidate,
     // and the need_updater parameter is true, get the zone there.
 }
 
+void
+ConfigurableClientList::resetMemorySegment
+    (const std::string& datasrc_name,
+     ZoneTableSegment::MemorySegmentOpenMode mode,
+     ConstElementPtr config_params)
+{
+    BOOST_FOREACH(DataSourceInfo& info, data_sources_) {
+        if (info.name_ == datasrc_name) {
+            ZoneTableSegment& segment = *info.ztable_segment_;
+            segment.reset(mode, config_params);
+            break;
+        }
+    }
+}
+
 ConfigurableClientList::ZoneWriterPair
 ConfigurableClientList::getCachedZoneWriter(const Name& name,
                                             const std::string& datasrc_name)

+ 16 - 1
src/lib/datasrc/client_list.h

@@ -21,7 +21,7 @@
 #include <dns/rrclass.h>
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
-#include "memory/zone_table_segment.h"
+#include <datasrc/memory/zone_table_segment.h>
 #include <datasrc/zone_table_accessor.h>
 
 #include <vector>
@@ -364,6 +364,21 @@ public:
         return (configuration_);
     }
 
+    /// \brief Resets the zone table segment for a datasource with a new
+    /// memory segment.
+    ///
+    /// See documentation of \c ZoneTableSegment interface
+    /// implementations (such as \c ZoneTableSegmentMapped) for the
+    /// syntax of \c config_params.
+    ///
+    /// \param datasrc_name The name of the data source whose segment to reset
+    /// \param mode The open mode for the new memory segment
+    /// \param config_params The configuration for the new memory segment.
+    void resetMemorySegment
+        (const std::string& datasrc_name,
+         memory::ZoneTableSegment::MemorySegmentOpenMode mode,
+         isc::data::ConstElementPtr config_params);
+
 private:
     /// \brief Convenience type shortcut
     typedef boost::shared_ptr<memory::ZoneWriter> ZoneWriterPtr;

+ 134 - 42
src/lib/datasrc/tests/client_list_unittest.cc

@@ -32,7 +32,9 @@
 
 #include <gtest/gtest.h>
 
+#include <boost/format.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/interprocess/file_mapping.hpp>
 
 #include <set>
 #include <fstream>
@@ -105,7 +107,18 @@ const char* ds_zones[][3] = {
 
 const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
 
-class ListTest : public ::testing::Test {
+class SegmentType {
+public:
+    virtual ~SegmentType() {}
+    virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
+        const = 0;
+    virtual void reset(ConfigurableClientList& list,
+                       const std::string& datasrc_name,
+                       ZoneTableSegment::MemorySegmentOpenMode mode,
+                       ConstElementPtr config_params) = 0;
+};
+
+class ListTest : public ::testing::TestWithParam<SegmentType*> {
 public:
     ListTest() :
         rrclass_(RRClass::IN()),
@@ -121,8 +134,7 @@ public:
             "   \"type\": \"test_type\","
             "   \"params\": [\"example.org\", \"example.com\", "
             "                \"noiter.org\", \"null.org\"]"
-            "}]")),
-        ztable_segment_(ZoneTableSegment::create(rrclass_, "local"))
+            "}]"))
     {
         for (size_t i(0); i < ds_count; ++ i) {
             shared_ptr<MockDataSourceClient>
@@ -135,6 +147,24 @@ public:
         }
     }
 
+    ~ListTest() {
+        ds_info_.clear();
+        for (size_t i(0); i < ds_count; ++ i) {
+            ds_[i].reset();
+        }
+        ds_.clear();
+
+        for (size_t i(0); i < ds_count; ++ i) {
+            boost::interprocess::file_mapping::remove(
+                getMappedFilename(i).c_str());
+        }
+    }
+
+    static std::string getMappedFilename(size_t index) {
+         return (boost::str(boost::format(TEST_DATA_BUILDDIR "/test%d.mapped")
+                            % index));
+    }
+
     // Install a "fake" cached zone using a temporary underlying data source
     // client.  If 'enabled' is set to false, emulate a disabled cache, in
     // which case there will be no data in memory.
@@ -152,11 +182,7 @@ public:
         // Build new cache config to load the specified zone, and replace
         // the data source info with the new config.
         ConstElementPtr cache_conf_elem =
-            Element::fromJSON("{\"type\": \"mock\","
-                              " \"cache-enable\": " +
-                              string(enabled ? "true," : "false,") +
-                              " \"cache-zones\": "
-                              "   [\"" + zone.toText() + "\"]}");
+            GetParam()->getCacheConfig(enabled, zone);
         boost::shared_ptr<internal::CacheConfig> cache_conf(
             new internal::CacheConfig("mock", mock_client, *cache_conf_elem,
                                       true));
@@ -167,6 +193,15 @@ public:
 
         // Load the data into the zone table.
         if (enabled) {
+            const ConstElementPtr config_ztable_segment(
+                Element::fromJSON("{\"mapped-file\": \"" +
+                                  getMappedFilename(index) +
+                                  "\"}"));
+
+            GetParam()->reset(*list_, dsrc_info.name_,
+                              memory::ZoneTableSegment::CREATE,
+                              config_ztable_segment);
+
             boost::scoped_ptr<memory::ZoneWriter> writer(
                 new memory::ZoneWriter(
                     *dsrc_info.ztable_segment_,
@@ -175,6 +210,10 @@ public:
             writer->load();
             writer->install();
             writer->cleanup(); // not absolutely necessary, but just in case
+
+            GetParam()->reset(*list_, dsrc_info.name_,
+                              memory::ZoneTableSegment::READ_WRITE,
+                              config_ztable_segment);
         }
 
         // On completion of load revert to the previous state of underlying
@@ -275,11 +314,64 @@ public:
     vector<shared_ptr<MockDataSourceClient> > ds_;
     vector<ConfigurableClientList::DataSourceInfo> ds_info_;
     const ConstElementPtr config_elem_, config_elem_zones_;
-    shared_ptr<ZoneTableSegment> ztable_segment_;
 };
 
+class LocalSegmentType : public SegmentType {
+public:
+    virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
+        const
+    {
+        return (Element::fromJSON("{\"type\": \"mock\","
+                                  " \"cache-enable\": " +
+                                  string(enabled ? "true," : "false,") +
+                                  " \"cache-zones\": "
+                                  "   [\"" + zone.toText() + "\"]}"));
+    }
+    virtual void reset(ConfigurableClientList&, const std::string&,
+                       ZoneTableSegment::MemorySegmentOpenMode,
+                       ConstElementPtr) {
+        // We must not call reset on local ZoneTableSegments.
+    }
+};
+
+LocalSegmentType local_segment_type;
+
+INSTANTIATE_TEST_CASE_P(ListTestLocal, ListTest,
+                        ::testing::Values(static_cast<SegmentType*>(
+                                              &local_segment_type)));
+
+#ifdef USE_SHARED_MEMORY
+
+class MappedSegmentType : public SegmentType {
+public:
+    virtual ConstElementPtr getCacheConfig(bool enabled, const Name& zone)
+        const
+    {
+        return (Element::fromJSON("{\"type\": \"mock\","
+                                  " \"cache-enable\": " +
+                                  string(enabled ? "true," : "false,") +
+                                  " \"cache-type\": \"mapped\"," +
+                                  " \"cache-zones\": "
+                                  "   [\"" + zone.toText() + "\"]}"));
+    }
+    virtual void reset(ConfigurableClientList& list,
+                       const std::string& datasrc_name,
+                       ZoneTableSegment::MemorySegmentOpenMode mode,
+                       ConstElementPtr config_params) {
+        list.resetMemorySegment(datasrc_name, mode, config_params);
+    }
+};
+
+MappedSegmentType mapped_segment_type;
+
+INSTANTIATE_TEST_CASE_P(ListTestMapped, ListTest,
+                        ::testing::Values(static_cast<SegmentType*>(
+                                              &mapped_segment_type)));
+
+#endif
+
 // Test the test itself
-TEST_F(ListTest, selfTest) {
+TEST_P(ListTest, selfTest) {
     EXPECT_EQ(result::SUCCESS, ds_[0]->findZone(Name("example.org")).code);
     EXPECT_EQ(result::PARTIALMATCH,
               ds_[0]->findZone(Name("sub.example.org")).code);
@@ -293,14 +385,14 @@ TEST_F(ListTest, selfTest) {
 }
 
 // Test the list we create with empty configuration is, in fact, empty
-TEST_F(ListTest, emptyList) {
+TEST_P(ListTest, emptyList) {
     EXPECT_TRUE(list_->getDataSources().empty());
 }
 
 // Check the values returned by a find on an empty list. It should be
 // a negative answer (nothing found) no matter if we want an exact or inexact
 // match.
-TEST_F(ListTest, emptySearch) {
+TEST_P(ListTest, emptySearch) {
     // No matter what we try, we don't get an answer.
 
     // Note: we don't have operator<< for the result class, so we cannot use
@@ -317,7 +409,7 @@ TEST_F(ListTest, emptySearch) {
 
 // Put a single data source inside the list and check it can find an
 // exact match if there's one.
-TEST_F(ListTest, singleDSExactMatch) {
+TEST_P(ListTest, singleDSExactMatch) {
     list_->getDataSources().push_back(ds_info_[0]);
     // This zone is not there
     EXPECT_TRUE(negative_result_ == list_->find(Name("org."), true));
@@ -331,7 +423,7 @@ TEST_F(ListTest, singleDSExactMatch) {
 }
 
 // When asking for a partial match, we get all that the exact one, but more.
-TEST_F(ListTest, singleDSBestMatch) {
+TEST_P(ListTest, singleDSBestMatch) {
     list_->getDataSources().push_back(ds_info_[0]);
     // This zone is not there
     EXPECT_TRUE(negative_result_ == list_->find(Name("org.")));
@@ -351,7 +443,7 @@ const char* const test_names[] = {
     "With a duplicity"
 };
 
-TEST_F(ListTest, multiExactMatch) {
+TEST_P(ListTest, multiExactMatch) {
     // Run through all the multi-configurations
     for (size_t i(0); i < sizeof(test_names) / sizeof(*test_names); ++i) {
         SCOPED_TRACE(test_names[i]);
@@ -370,7 +462,7 @@ TEST_F(ListTest, multiExactMatch) {
     }
 }
 
-TEST_F(ListTest, multiBestMatch) {
+TEST_P(ListTest, multiBestMatch) {
     // Run through all the multi-configurations
     for (size_t i(0); i < 4; ++ i) {
         SCOPED_TRACE(test_names[i]);
@@ -391,7 +483,7 @@ TEST_F(ListTest, multiBestMatch) {
 }
 
 // Check the configuration is empty when the list is empty
-TEST_F(ListTest, configureEmpty) {
+TEST_P(ListTest, configureEmpty) {
     const ConstElementPtr elem(new ListElement);
     list_->configure(elem, true);
     EXPECT_TRUE(list_->getDataSources().empty());
@@ -400,7 +492,7 @@ TEST_F(ListTest, configureEmpty) {
 }
 
 // Check we can get multiple data sources and they are in the right order.
-TEST_F(ListTest, configureMulti) {
+TEST_P(ListTest, configureMulti) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
@@ -422,7 +514,7 @@ TEST_F(ListTest, configureMulti) {
 }
 
 // Check we can pass whatever we want to the params
-TEST_F(ListTest, configureParams) {
+TEST_P(ListTest, configureParams) {
     const char* params[] = {
         "true",
         "false",
@@ -447,7 +539,7 @@ TEST_F(ListTest, configureParams) {
     }
 }
 
-TEST_F(ListTest, status) {
+TEST_P(ListTest, status) {
     EXPECT_TRUE(list_->getStatus().empty());
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
@@ -474,7 +566,7 @@ TEST_F(ListTest, status) {
     EXPECT_EQ("local", statuses[1].getSegmentType());
 }
 
-TEST_F(ListTest, wrongConfig) {
+TEST_P(ListTest, wrongConfig) {
     const char* configs[] = {
         // A lot of stuff missing from there
         "[{\"type\": \"test_type\", \"params\": 13}, {}]",
@@ -572,7 +664,7 @@ TEST_F(ListTest, wrongConfig) {
 }
 
 // The param thing defaults to null. Cache is not used yet.
-TEST_F(ListTest, defaults) {
+TEST_P(ListTest, defaults) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\""
@@ -583,7 +675,7 @@ TEST_F(ListTest, defaults) {
 }
 
 // Check we can call the configure multiple times, to change the configuration
-TEST_F(ListTest, reconfigure) {
+TEST_P(ListTest, reconfigure) {
     const ConstElementPtr empty(new ListElement);
     list_->configure(config_elem_, true);
     checkDS(0, "test_type", "{}", false);
@@ -594,7 +686,7 @@ TEST_F(ListTest, reconfigure) {
 }
 
 // Make sure the data source error exception from the factory is propagated
-TEST_F(ListTest, dataSrcError) {
+TEST_P(ListTest, dataSrcError) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"error\""
@@ -606,7 +698,7 @@ TEST_F(ListTest, dataSrcError) {
 }
 
 // Check we can get the cache
-TEST_F(ListTest, configureCacheEmpty) {
+TEST_P(ListTest, configureCacheEmpty) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
@@ -628,7 +720,7 @@ TEST_F(ListTest, configureCacheEmpty) {
 }
 
 // But no cache if we disallow it globally
-TEST_F(ListTest, configureCacheDisabled) {
+TEST_P(ListTest, configureCacheDisabled) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
@@ -650,7 +742,7 @@ TEST_F(ListTest, configureCacheDisabled) {
 }
 
 // Put some zones into the cache
-TEST_F(ListTest, cacheZones) {
+TEST_P(ListTest, cacheZones) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
@@ -684,7 +776,7 @@ TEST_F(ListTest, cacheZones) {
 
 // Check the caching handles misbehaviour from the data source and
 // misconfiguration gracefully
-TEST_F(ListTest, badCache) {
+TEST_P(ListTest, badCache) {
     list_->configure(config_elem_, true);
     checkDS(0, "test_type", "{}", false);
     // First, the zone is not in the data source. configure() should still
@@ -733,7 +825,7 @@ TEST_F(ListTest, badCache) {
 }
 
 // This test relies on the property of mapped type of cache.
-TEST_F(ListTest,
+TEST_P(ListTest,
 #ifdef USE_SHARED_MEMORY
        cacheInNonWritableSegment
 #else
@@ -760,7 +852,7 @@ TEST_F(ListTest,
               doReload(Name("example.org")));
 }
 
-TEST_F(ListTest, masterFiles) {
+TEST_P(ListTest, masterFiles) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"MasterFiles\","
@@ -785,7 +877,7 @@ TEST_F(ListTest, masterFiles) {
 }
 
 // Test the names are set correctly and collission is detected.
-TEST_F(ListTest, names) {
+TEST_P(ListTest, names) {
     // Explicit name
     const ConstElementPtr elem1(Element::fromJSON("["
         "{"
@@ -832,7 +924,7 @@ TEST_F(ListTest, names) {
                  ConfigurableClientList::ConfigurationError);
 }
 
-TEST_F(ListTest, BadMasterFile) {
+TEST_P(ListTest, BadMasterFile) {
     // Configuration should succeed, and the good zones in the list
     // below should be loaded.  Bad zones won't be "loaded" in its usual sense,
     // but are still recognized with conceptual "empty" data.
@@ -908,7 +1000,7 @@ ListTest::doReload(const Name& origin, const string& datasrc_name) {
 }
 
 // Test we can reload a zone
-TEST_F(ListTest, reloadSuccess) {
+TEST_P(ListTest, reloadSuccess) {
     list_->configure(config_elem_zones_, true);
     const Name name("example.org");
     prepareCache(0, name);
@@ -928,7 +1020,7 @@ TEST_F(ListTest, reloadSuccess) {
 }
 
 // The cache is not enabled. The load should be rejected.
-TEST_F(ListTest, reloadNotAllowed) {
+TEST_P(ListTest, reloadNotAllowed) {
     list_->configure(config_elem_zones_, false);
     const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the thing.
@@ -948,7 +1040,7 @@ TEST_F(ListTest, reloadNotAllowed) {
 }
 
 // Similar to the previous case, but the cache is disabled in config.
-TEST_F(ListTest, reloadNotEnabled) {
+TEST_P(ListTest, reloadNotEnabled) {
     list_->configure(config_elem_zones_, true);
     const Name name("example.org");
     // We put the cache, actually disabling it.
@@ -959,7 +1051,7 @@ TEST_F(ListTest, reloadNotEnabled) {
 }
 
 // Test several cases when the zone does not exist
-TEST_F(ListTest, reloadNoSuchZone) {
+TEST_P(ListTest, reloadNoSuchZone) {
     list_->configure(config_elem_zones_, true);
     const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the
@@ -990,7 +1082,7 @@ TEST_F(ListTest, reloadNoSuchZone) {
 
 // Check we gracefully reject reloading (i.e. no exception) when a zone
 // disappeared in the underlying data source when we want to reload it
-TEST_F(ListTest, reloadZoneGone) {
+TEST_P(ListTest, reloadZoneGone) {
     list_->configure(config_elem_zones_, true);
     const Name name("example.org");
     // We put in a cache for non-existent zone. This emulates being loaded
@@ -1011,7 +1103,7 @@ TEST_F(ListTest, reloadZoneGone) {
               list_->find(name).finder_->find(name, RRType::SOA())->code);
 }
 
-TEST_F(ListTest, reloadNewZone) {
+TEST_P(ListTest, reloadNewZone) {
     // Test the case where a zone to be cached originally doesn't exist
     // in the underlying data source and is added later.  reload() will
     // succeed once it's available in the data source.
@@ -1038,7 +1130,7 @@ TEST_F(ListTest, reloadNewZone) {
 }
 
 // The underlying data source throws. Check we don't modify the state.
-TEST_F(ListTest, reloadZoneThrow) {
+TEST_P(ListTest, reloadZoneThrow) {
     list_->configure(config_elem_zones_, true);
     const Name name("noiter.org");
     prepareCache(0, name);
@@ -1052,7 +1144,7 @@ TEST_F(ListTest, reloadZoneThrow) {
               list_->find(name).finder_->find(name, RRType::SOA())->code);
 }
 
-TEST_F(ListTest, reloadNullIterator) {
+TEST_P(ListTest, reloadNullIterator) {
     list_->configure(config_elem_zones_, true);
     const Name name("null.org");
     prepareCache(0, name);
@@ -1067,7 +1159,7 @@ TEST_F(ListTest, reloadNullIterator) {
 }
 
 // Test we can reload the master files too (special-cased)
-TEST_F(ListTest, reloadMasterFile) {
+TEST_P(ListTest, reloadMasterFile) {
     const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
         "/root.zone " TEST_DATA_BUILDDIR "/root.zone.copied";
     if (system(install_cmd) != 0) {
@@ -1102,7 +1194,7 @@ TEST_F(ListTest, reloadMasterFile) {
                                                    RRType::TXT())->code);
 }
 
-TEST_F(ListTest, reloadByDataSourceName) {
+TEST_P(ListTest, reloadByDataSourceName) {
     // We use three data sources (and their clients).  2nd and 3rd have
     // the same name of the zones.
     const ConstElementPtr config_elem = Element::fromJSON(

+ 72 - 0
src/lib/python/isc/datasrc/configurableclientlist_python.cc

@@ -27,6 +27,7 @@
 
 #include <dns/python/rrclass_python.h>
 #include <dns/python/name_python.h>
+#include <dns/python/pydnspp_common.h>
 
 #include <datasrc/client_list.h>
 
@@ -38,7 +39,9 @@
 using namespace std;
 using namespace isc::util::python;
 using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
 using namespace isc::datasrc::python;
+using namespace isc::dns::python;
 
 //
 // ConfigurableClientList
@@ -116,6 +119,39 @@ ConfigurableClientList_configure(PyObject* po_self, PyObject* args) {
 }
 
 PyObject*
+ConfigurableClientList_resetMemorySegment(PyObject* po_self, PyObject* args) {
+    s_ConfigurableClientList* self =
+        static_cast<s_ConfigurableClientList*>(po_self);
+    try {
+        const char* datasrc_name_p;
+        int mode_int;
+        const char* config_p;
+        if (PyArg_ParseTuple(args, "sis", &datasrc_name_p, &mode_int,
+                             &config_p)) {
+            const std::string datasrc_name(datasrc_name_p);
+            const isc::data::ConstElementPtr
+                config(isc::data::Element::fromJSON(std::string(config_p)));
+            ZoneTableSegment::MemorySegmentOpenMode mode =
+                static_cast<ZoneTableSegment::MemorySegmentOpenMode>
+                    (mode_int);
+            self->cppobj->resetMemorySegment(datasrc_name, mode, config);
+            Py_RETURN_NONE;
+        }
+    } catch (const isc::data::JSONError& jse) {
+        const string ex_what(std::string("JSON parse error in memory segment"
+                               " configuration: ") + jse.what());
+        PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unknown C++ exception");
+    }
+
+    return (NULL);
+}
+
+PyObject*
 ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
     s_ConfigurableClientList* self =
         static_cast<s_ConfigurableClientList*>(po_self);
@@ -191,6 +227,19 @@ configuration preserved.\n\
 Parameters:\n\
   configuration     The configuration, as a JSON encoded string.\
   allow_cache       If caching is allowed." },
+    { "reset_memory_segment", ConfigurableClientList_resetMemorySegment,
+      METH_VARARGS,
+        "reset_memory_segment(datasrc_name, mode, config_params) -> None\n\
+\n\
+Wrapper around C++ ConfigurableClientList::resetMemorySegment\n\
+\n\
+This resets the zone table segment for a datasource with a new\n\
+memory segment.\n\
+\n\
+Parameters:\n\
+  datasrc_name      The name of the data source whose segment to reset.\
+  mode              The open mode for the new memory segment.\
+  config_params     The configuration for the new memory segment, as a JSON encoded string." },
     { "find", ConfigurableClientList_find, METH_VARARGS,
 "find(zone, want_exact_match=False, want_finder=True) -> datasrc_client,\
 zone_finder, exact_match\n\
@@ -300,6 +349,29 @@ initModulePart_ConfigurableClientList(PyObject* mod) {
     }
     Py_INCREF(&configurableclientlist_type);
 
+    // FIXME: These should eventually be moved to the ZoneTableSegment
+    // class when we add Python bindings for the memory data source
+    // specific bits. But for now, we add these enums here to support
+    // reloading a zone table segment.
+    try {
+        installClassVariable(configurableclientlist_type, "CREATE",
+                             Py_BuildValue("I", ZoneTableSegment::CREATE));
+        installClassVariable(configurableclientlist_type, "READ_WRITE",
+                             Py_BuildValue("I", ZoneTableSegment::READ_WRITE));
+        installClassVariable(configurableclientlist_type, "READ_ONLY",
+                             Py_BuildValue("I", ZoneTableSegment::READ_ONLY));
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in ConfigurableClientList initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+            "Unexpected failure in ConfigurableClientList initialization");
+        return (false);
+    }
+
     return (true);
 }