Browse Source

[2541] add createZone() to DataSourceClient interface

Note: currently called createZone so as not to interfere with an existing addZone() call in MemoryClient.
Instead of pure virtual I made it have a default 'NotImplemented' method.
Also, the DatabaseClient holds the transaction (not the SQLite3Accessor); we could do it on that level but we already have a transaction API on a higher level and it makes a little bit more sense to me to do it there
Jelte Jansen 12 years ago
parent
commit
3a5d06d390

+ 14 - 0
src/lib/datasrc/client.h

@@ -378,6 +378,20 @@ public:
         isc_throw(isc::NotImplemented,
                   "Data source doesn't support getZoneCount");
     }
+
+    // It first checks if the specified name of the zone exists.  If it
+    // exists it returns false; otherwise it adds information of the
+    // new zone in backend-dependent manner and returns true.
+    // The DB-based version of this method would perform the check and add in
+    // a single transaction.
+    //
+    // Throws on any unexpected failure.
+    // Default implementation throws isc::NotImplemented
+
+    virtual bool createZone(const dns::Name&) {
+        isc_throw(isc::NotImplemented,
+                  "Data source doesn't support addZone");
+    };
 };
 }
 }

+ 36 - 0
src/lib/datasrc/database.cc

@@ -43,6 +43,30 @@ using boost::scoped_ptr;
 
 namespace isc {
 namespace datasrc {
+// RAII-style transaction holder; roll back the transaction unless explicitely committed
+namespace {
+class TransactionHolder {
+public:
+    TransactionHolder(DatabaseAccessor& accessor) : accessor_(accessor),
+                                                    committed_(false)
+    {
+        accessor_.startTransaction();
+    }
+    ~TransactionHolder() {
+        if (!committed_) {
+            accessor_.rollback();
+        }
+    }
+    void commit() {
+        accessor_.commit();
+        committed_ = true;
+    }
+private:
+    DatabaseAccessor& accessor_;
+    bool committed_;
+};
+} // end unnamed namespace
+
 
 DatabaseClient::DatabaseClient(RRClass rrclass,
                                boost::shared_ptr<DatabaseAccessor>
@@ -80,6 +104,18 @@ DatabaseClient::findZone(const Name& name) const {
     return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
 }
 
+bool
+DatabaseClient::createZone(const Name& name) {
+    TransactionHolder transaction(*accessor_);
+    std::pair<bool, int> zone(accessor_->getZone(name.toText()));
+    if (zone.first) {
+        return (false);
+    }
+    accessor_->addZone(name.toText());
+    transaction.commit();
+    return (true);
+}
+
 DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
                                int zone_id, const isc::dns::Name& origin) :
     accessor_(accessor),

+ 2 - 0
src/lib/datasrc/database.h

@@ -1393,6 +1393,8 @@ public:
     ///     should use it as a ZoneFinder only.
     virtual FindResult findZone(const isc::dns::Name& name) const;
 
+    virtual bool createZone(const isc::dns::Name&);
+
     /// \brief Get the zone iterator
     ///
     /// The iterator allows going through the whole zone content. If the

+ 4 - 0
src/lib/datasrc/tests/client_unittest.cc

@@ -60,4 +60,8 @@ TEST_F(ClientTest, defaultGetZoneCount) {
     EXPECT_THROW(client_.getZoneCount(), isc::NotImplemented);
 }
 
+TEST_F(ClientTest, defaultCreateZone) {
+    EXPECT_THROW(client_.createZone(Name("example.com.")), isc::NotImplemented);
+}
+
 }

+ 23 - 0
src/lib/datasrc/tests/database_unittest.cc

@@ -334,6 +334,7 @@ public:
         isc_throw(isc::NotImplemented,
                   "This test database knows nothing about NSEC3 nor order");
     }
+
 private:
     const std::string database_name_;
 
@@ -4097,4 +4098,26 @@ TYPED_TEST(DatabaseClientTest, findNSEC3) {
     performNSEC3Test(*finder, true);
 }
 
+TYPED_TEST(DatabaseClientTest, createZone) {
+    const Name new_name("example.com");
+    const DataSourceClient::FindResult
+        zone(this->client_->findZone(new_name));
+    ASSERT_EQ(result::NOTFOUND, zone.code);
+    // The mock implementation does not do createZone,
+    // in which case it should throw NotImplemented (from
+    // the base class)
+    if(this->is_mock_) {
+        ASSERT_THROW(this->client_->createZone(new_name), isc::NotImplemented);
+    } else {
+        // But in the real case, it should work and return true
+        ASSERT_TRUE(this->client_->createZone(new_name));
+        const DataSourceClient::FindResult
+            zone2(this->client_->findZone(new_name));
+        ASSERT_EQ(result::SUCCESS, zone2.code);
+        // And the second call should return false since
+        // it already exists
+        ASSERT_FALSE(this->client_->createZone(new_name));
+    }
+}
+
 }