virtual ConstElementPtr exec(AuthSrv& server,
isc::data::ConstElementPtr args)
{
- if (args == NULL) {
- isc_throw(AuthCommandError, "Null argument");
+ try {
+ server.loadZone(args);
+ return (createAnswer());
+ } catch (const LoadZoneCommandError& lzce) {
+ return (createAnswer(1, lzce.what()));
}
-
- ConstElementPtr class_elem = args->get("class");
- RRClass zone_class(class_elem ? RRClass(class_elem->stringValue()) :
- RRClass::IN());
-
- ConstElementPtr origin_elem = args->get("origin");
- if (!origin_elem) {
- isc_throw(AuthCommandError, "Zone origin is missing");
- }
- const Name origin(origin_elem->stringValue());
-
- DataSrcClientsMgr::Holder holder(server.getDataSrcClientsMgr());
- boost::shared_ptr<ConfigurableClientList> list =
- holder.findClientList(zone_class);
-
- if (!list) {
- isc_throw(AuthCommandError, "There's no client list for "
- "class " << zone_class);
- }
-
- switch (list->reload(origin)) {
- case ConfigurableClientList::ZONE_RELOADED:
- // Everything worked fine.
- LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
- .arg(zone_class).arg(origin);
- return (createAnswer());
- case ConfigurableClientList::ZONE_NOT_FOUND:
- isc_throw(AuthCommandError, "Zone " << origin << "/" <<
- zone_class << " was not found in any configured "
- "data source. Configure it first.");
- case ConfigurableClientList::ZONE_NOT_CACHED:
- isc_throw(AuthCommandError, "Zone " << origin << "/" <<
- zone_class << " is not served from memory, but "
- "directly from the data source. It is not possible "
- "to reload it into memory. Configure it to be cached "
- "first.");
- case ConfigurableClientList::CACHE_DISABLED:
- // This is an internal error. Auth server must have the cache
- // enabled.
- isc_throw(isc::Unexpected, "Cache disabled in client list of "
- "class " << zone_class);
- }
- return (createAnswer());
}
};
namespace isc {
namespace auth {
+/// \brief An exception that is thrown if the arguments to the loadZone
+/// call are not in the right format
+class LoadZoneCommandError : public isc::Exception {
+public:
+ LoadZoneCommandError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
namespace datasrc_clientmgr_internal {
// This namespace is essentially private for DataSrcClientsMgr(Base) and
// DataSrcClientsBuilder(Base). This is exposed in the public header
clients_map_ = new_lists;
}
+ /// \brief Instruct internal thread to (re)load a zone
+ ///
+ /// \param args Element argument that should be a map of the form
+ /// { "class": "IN", "origin": "example.com" }
+ /// (but class is optional and will default to IN)
+ ///
+ /// \exception LoadZoneCommandError if the args value is null, or not in
+ /// the expected format
+ void
+ loadZone(data::ConstElementPtr args) {
+ if (!args) {
+ isc_throw(LoadZoneCommandError, "loadZone argument empty");
+ }
+ if (args->getType() != isc::data::Element::map) {
+ isc_throw(LoadZoneCommandError, "loadZone argument not a map");
+ }
+ if (!args->contains("origin")) {
+ isc_throw(LoadZoneCommandError,
+ "loadZone argument has no 'origin' value");
+ }
+
+ sendCommand(datasrc_clientmgr_internal::LOADZONE, args);
+ }
+
private:
// This is expected to be called at the end of the destructor. It
// actually does nothing, but provides a customization point for
// called via the manager in practice. manager is expected to do the
// minimal validation.
assert(arg);
- assert(arg->get("class"));
assert(arg->get("origin"));
- const dns::RRClass rrclass(arg->get("class")->stringValue());
+ isc::data::ConstElementPtr class_elem = arg->get("class");
+ const dns::RRClass rrclass(class_elem ?
+ dns::RRClass(class_elem->stringValue()) :
+ dns::RRClass::IN());
const dns::Name origin(arg->get("origin")->stringValue());
ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
if (found == (*clients_map_)->end()) {
find(Name("ns.test2.example"), RRType::AAAA())->code);
}
-TEST_F(AuthCommandTest, loadZone) {
- configureZones(server_);
-
- ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
- "/test1-new.zone.in "
- TEST_DATA_BUILDDIR "/test1.zone.copied"));
- ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
- "/test2-new.zone.in "
- TEST_DATA_BUILDDIR "/test2.zone.copied"));
-
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
- checkAnswer(0);
- newZoneChecks(server_);
-}
-
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
- DISABLED_loadZoneSQLite3
-#else
- loadZoneSQLite3
-#endif
- )
-{
- // Prepare the database first
- const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
- const string bad_db = TEST_DATA_BUILDDIR "/does-not-exist.sqlite3";
- stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
- createSQLite3DB(RRClass::IN(), Name("example.org"), test_db.c_str(), ss);
- // This describes the data source in the configuration
- const ConstElementPtr config(Element::fromJSON("{"
- "\"IN\": [{"
- " \"type\": \"sqlite3\","
- " \"params\": {\"database_file\": \"" + test_db + "\"},"
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"example.org\"]"
- "}]}"));
- installDataSrcClientLists(server_, configureDataSource(config));
-
- {
- DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
-
- // Check that the A record at www.example.org does not exist
- EXPECT_EQ(ZoneFinder::NXDOMAIN,
- holder.findClientList(RRClass::IN())->
- find(Name("example.org")).finder_->
- find(Name("www.example.org"), RRType::A())->code);
-
- // Add the record to the underlying sqlite database, by loading
- // it as a separate datasource, and updating it
- ConstElementPtr sql_cfg = Element::fromJSON("{ \"type\": \"sqlite3\","
- "\"database_file\": \""
- + test_db + "\"}");
- DataSourceClientContainer sql_ds("sqlite3", sql_cfg);
- ZoneUpdaterPtr sql_updater =
- sql_ds.getInstance().getUpdater(Name("example.org"), false);
- RRsetPtr rrset(new RRset(Name("www.example.org."), RRClass::IN(),
- RRType::A(), RRTTL(60)));
- rrset->addRdata(rdata::createRdata(rrset->getType(),
- rrset->getClass(),
- "192.0.2.1"));
- sql_updater->addRRset(*rrset);
- sql_updater->commit();
-
- EXPECT_EQ(ZoneFinder::NXDOMAIN,
- holder.findClientList(RRClass::IN())->
- find(Name("example.org")).finder_->
- find(Name("www.example.org"), RRType::A())->code);
- }
-
- // Now send the command to reload it
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"example.org\"}"));
- checkAnswer(0, "Successful load");
-
- {
- DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
- // And now it should be present too.
- EXPECT_EQ(ZoneFinder::SUCCESS,
- holder.findClientList(RRClass::IN())->
- find(Name("example.org")).finder_->
- find(Name("www.example.org"), RRType::A())->code);
- }
-
- // Some error cases. First, the zone has no configuration. (note .com here)
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": \"example.com\"}"));
- checkAnswer(1, "example.com");
-
- {
- DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
- // The previous zone is not hurt in any way
- EXPECT_EQ(ZoneFinder::SUCCESS,
- holder.findClientList(RRClass::IN())->
- find(Name("example.org")).finder_->
- find(Name("example.org"), RRType::SOA())->code);
- }
-
- const ConstElementPtr config2(Element::fromJSON("{"
- "\"IN\": [{"
- " \"type\": \"sqlite3\","
- " \"params\": {\"database_file\": \"" + bad_db + "\"},"
- " \"cache-enable\": true,"
- " \"cache-zones\": [\"example.com\"]"
- "}]}"));
- EXPECT_THROW(configureDataSource(config2),
- ConfigurableClientList::ConfigurationError);
-
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": \"example.com\"}"));
- checkAnswer(1, "Unreadable");
-
- DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
- // The previous zone is not hurt in any way
- EXPECT_EQ(ZoneFinder::SUCCESS,
- holder.findClientList(RRClass::IN())->
- find(Name("example.org")).finder_->
- find(Name("example.org"), RRType::SOA())->code);
-}
-
-TEST_F(AuthCommandTest, loadBrokenZone) {
- configureZones(server_);
-
- ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
- "/test1-broken.zone.in "
- TEST_DATA_BUILDDIR "/test1.zone.copied"));
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
- checkAnswer(1);
- zoneChecks(server_); // zone shouldn't be replaced
-}
-
-TEST_F(AuthCommandTest, loadUnreadableZone) {
- configureZones(server_);
-
- // install the zone file as unreadable
- ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
- "/test1.zone.in "
- TEST_DATA_BUILDDIR "/test1.zone.copied"));
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
- checkAnswer(1);
- zoneChecks(server_); // zone shouldn't be replaced
-}
-
-TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
- // try to execute load command without configuring the zone beforehand.
- // it should fail.
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
- checkAnswer(1);
-}
-
-TEST_F(AuthCommandTest, loadZoneInvalidParams) {
- configureZones(server_);
-
- // null arg
- result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
- checkAnswer(1, "Null arg");
-
- // zone class is bogus
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"class\": \"no_such_class\"}"));
- checkAnswer(1, "No such class");
-
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"class\": 1}"));
- checkAnswer(1, "Integral class");
-
-
- // origin is missing
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{}"));
- checkAnswer(1, "Missing origin");
-
- // zone doesn't exist in the data source
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": \"xx\"}"));
- checkAnswer(1, "No such zone");
-
- // origin is bogus
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"...\"}"));
- checkAnswer(1, "Wrong name");
-
- result_ = execAuthServerCommand(server_, "loadzone",
- Element::fromJSON("{\"origin\": 10}"));
- checkAnswer(1, "Integral name");
-}
-
TEST_F(AuthCommandTest, getStats) {
result_ = execAuthServerCommand(server_, "getstats", ConstElementPtr());
parseAnswer(rcode_, result_);