BIND 10 master, updated. 956a0a589db0a8250ec94ece377657783ac15caf [master] changelog for #1333
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Nov 18 18:36:45 UTC 2011
The branch, master has been updated
via 956a0a589db0a8250ec94ece377657783ac15caf (commit)
via 39def1d39c9543fc485eceaa5d390062edb97676 (commit)
via 38d84c59fbc097e57d03ac10d6a83edc63c4cffa (commit)
via c0cc183880fc5e1949bcc97585c20ac2ab21e281 (commit)
via 2d85e22f10321fbc5b9cd12f70e90907cb01830f (commit)
via 7344d2788cd06e54ca7ca3e3a3f69010dac80670 (commit)
via ce546dddcbbf7efc4778c1d0d4210ca139ed5bf9 (commit)
via fa89a0798d166574e089b38d7bd43a701eda5467 (commit)
via 12b1a920f219e627bb5860f0a0217cc5c86749e5 (commit)
via cd342dae58399be6cdfad55a466a76ee385ccb08 (commit)
from bcb432839cacdf10172d49dec94292871aee3526 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 956a0a589db0a8250ec94ece377657783ac15caf
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Fri Nov 18 10:36:27 2011 -0800
[master] changelog for #1333
commit 39def1d39c9543fc485eceaa5d390062edb97676
Merge: bcb4328 38d84c5
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Fri Nov 18 10:35:02 2011 -0800
[master] Merge branch 'trac1333r' with fixing conflicts.
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 9 ++
src/lib/python/isc/datasrc/Makefile.am | 2 +
src/lib/python/isc/datasrc/client_inc.cc | 54 +++++++
src/lib/python/isc/datasrc/client_python.cc | 46 +++++-
src/lib/python/isc/datasrc/datasrc.cc | 41 ++++++
src/lib/python/isc/datasrc/journal_reader_inc.cc | 80 +++++++++++
...iterator_python.cc => journal_reader_python.cc} | 120 ++++++-----------
.../{iterator_python.h => journal_reader_python.h} | 27 ++--
src/lib/python/isc/datasrc/tests/datasrc_test.py | 145 +++++++++++++++++---
9 files changed, 401 insertions(+), 123 deletions(-)
create mode 100644 src/lib/python/isc/datasrc/journal_reader_inc.cc
copy src/lib/python/isc/datasrc/{iterator_python.cc => journal_reader_python.cc} (61%)
copy src/lib/python/isc/datasrc/{iterator_python.h => journal_reader_python.h} (61%)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 0769954..7d660a8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+325. [func] jinmei
+ Python isc.datasrc: added interfaces for difference management:
+ DataSourceClient.get_updater() now has the 'journaling' parameter
+ to enable storing diffs to the data source, and a new class
+ ZoneJournalReader was introduced to retrieve them, which can be
+ created by the new DataSourceClient.get_journal_reader() method.
+ (Trac #1333, git 3e19362bc1ba7dc67a87768e2b172c48b32417f5,
+ git 39def1d39c9543fc485eceaa5d390062edb97676)
+
324. [bug] jinmei
Fixed reference leak in the isc.log Python module. Most of all
BIND 10 Python programs had memory leak (even though the pace of
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index a5b4ca3..fb6d151 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -17,6 +17,7 @@ datasrc_la_SOURCES += client_python.cc client_python.h
datasrc_la_SOURCES += iterator_python.cc iterator_python.h
datasrc_la_SOURCES += finder_python.cc finder_python.h
datasrc_la_SOURCES += updater_python.cc updater_python.h
+datasrc_la_SOURCES += journal_reader_python.cc journal_reader_python.h
datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
@@ -30,6 +31,7 @@ EXTRA_DIST = client_inc.cc
EXTRA_DIST += finder_inc.cc
EXTRA_DIST += iterator_inc.cc
EXTRA_DIST += updater_inc.cc
+EXTRA_DIST += journal_reader_inc.cc
CLEANDIRS = __pycache__
diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc
index f4fb01b..575e33c 100644
--- a/src/lib/python/isc/datasrc/client_inc.cc
+++ b/src/lib/python/isc/datasrc/client_inc.cc
@@ -190,4 +190,58 @@ Parameters:\n\
journaling The zone updater should store a journal of the changes.\n\
\n\
";
+
+// Modifications from C++ doc:
+// pointer -> (removed)
+// Null -> None
+// exception types
+const char* const DataSourceClient_getJournalReader_doc = "\
+get_journal_reader(zone, begin_serial, end_serial) ->\n\
+ (int, ZoneJournalReader)\n\
+\n\
+Return a journal reader to retrieve differences of a zone.\n\
+\n\
+A derived version of this method creates a concrete ZoneJournalReader\n\
+object specific to the underlying data source for the specified name\n\
+of zone and differences between the versions specified by the\n\
+beginning and ending serials of the corresponding SOA RRs. The RR\n\
+class of the zone is the one that the client is expected to handle\n\
+(see the detailed description of this class).\n\
+\n\
+Note that the SOA serials are compared by the semantics of the serial\n\
+number arithmetic. So, for example, begin_serial can be larger than\n\
+end_serial as bare unsigned integers. The underlying data source\n\
+implementation is assumed to keep track of sufficient history to\n\
+identify (if exist) the corresponding difference between the specified\n\
+versions.\n\
+\n\
+This method returns the result as a pair of a result code and a\n\
+ZoneJournalReader object. On success, the result code is\n\
+SUCCESS and the object must not be None; otherwise the result code is\n\
+something other than SUCCESS and the object must be None.\n\
+\n\
+If the specified zone is not found in the data source, the result code\n\
+is NO_SUCH_ZONE. Otherwise, if specified range of difference for the\n\
+zone is not found in the data source, the result code is\n\
+NO_SUCH_VERSION.\n\
+\n\
+Handling differences is an optional feature of data source. If the\n\
+underlying data source does not support difference handling, this\n\
+method for that type of data source can throw an exception of class\n\
+isc.datasrc.NotImplemented.\n\
+\n\
+Exceptions:\n\
+ isc.datasrc.NotImplemented The data source does not support differences.\n\
+ isc.datasrc.Error Other operational errors at the data source level.\n\
+\n\
+Parameters:\n\
+ zone The name of the zone for which the difference should be\n\
+ retrieved.\n\
+ begin_serial The SOA serial of the beginning version of the\n\
+ differences.\n\
+ end_serial The SOA serial of the ending version of the differences.\n\
+\n\
+Return Value(s): A pair of result code and a ZoneJournalReader object\n\
+(which can be None)\n \
+";
} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc
index 2740355..22b5a48 100644
--- a/src/lib/python/isc/datasrc/client_python.cc
+++ b/src/lib/python/isc/datasrc/client_python.cc
@@ -38,6 +38,7 @@
#include "finder_python.h"
#include "iterator_python.h"
#include "updater_python.h"
+#include "journal_reader_python.h"
#include "client_inc.cc"
using namespace std;
@@ -173,6 +174,31 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) {
}
}
+PyObject*
+DataSourceClient_getJournalReader(PyObject* po_self, PyObject* args) {
+ s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
+ PyObject *name_obj;
+ unsigned long begin_obj, end_obj;
+
+ if (PyArg_ParseTuple(args, "O!kk", &name_type, &name_obj,
+ &begin_obj, &end_obj)) {
+ pair<ZoneJournalReader::Result, ZoneJournalReaderPtr> result =
+ self->cppobj->getInstance().getJournalReader(
+ PyName_ToName(name_obj), static_cast<uint32_t>(begin_obj),
+ static_cast<uint32_t>(end_obj));
+ PyObject* po_reader;
+ if (result.first == ZoneJournalReader::SUCCESS) {
+ po_reader = createZoneJournalReaderObject(result.second, po_self);
+ } else {
+ po_reader = Py_None;
+ Py_INCREF(po_reader); // this will soon be released
+ }
+ PyObjectContainer container(po_reader);
+ return (Py_BuildValue("(iO)", result.first, container.get()));
+ }
+ return (NULL);
+}
+
// This list contains the actual set of functions we have in
// python. Each entry has
// 1. Python method name
@@ -180,18 +206,21 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) {
// 3. Argument type
// 4. Documentation
PyMethodDef DataSourceClient_methods[] = {
- { "find_zone", reinterpret_cast<PyCFunction>(DataSourceClient_findZone),
- METH_VARARGS, DataSourceClient_findZone_doc },
+ { "find_zone", DataSourceClient_findZone, METH_VARARGS,
+ DataSourceClient_findZone_doc },
{ "get_iterator",
- reinterpret_cast<PyCFunction>(DataSourceClient_getIterator), METH_VARARGS,
+ DataSourceClient_getIterator, METH_VARARGS,
DataSourceClient_getIterator_doc },
- { "get_updater", reinterpret_cast<PyCFunction>(DataSourceClient_getUpdater),
+ { "get_updater", DataSourceClient_getUpdater,
METH_VARARGS, DataSourceClient_getUpdater_doc },
+ { "get_journal_reader", DataSourceClient_getJournalReader,
+ METH_VARARGS, DataSourceClient_getJournalReader_doc },
{ NULL, NULL, 0, NULL }
};
int
-DataSourceClient_init(s_DataSourceClient* self, PyObject* args) {
+DataSourceClient_init(PyObject* po_self, PyObject* args, PyObject*) {
+ s_DataSourceClient* self = static_cast<s_DataSourceClient*>(po_self);
char* ds_type_str;
char* ds_config_str;
try {
@@ -236,7 +265,8 @@ DataSourceClient_init(s_DataSourceClient* self, PyObject* args) {
}
void
-DataSourceClient_destroy(s_DataSourceClient* const self) {
+DataSourceClient_destroy(PyObject* po_self) {
+ s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
delete self->cppobj;
self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
@@ -255,7 +285,7 @@ PyTypeObject datasourceclient_type = {
"datasrc.DataSourceClient",
sizeof(s_DataSourceClient), // tp_basicsize
0, // tp_itemsize
- reinterpret_cast<destructor>(DataSourceClient_destroy),// tp_dealloc
+ DataSourceClient_destroy, // tp_dealloc
NULL, // tp_print
NULL, // tp_getattr
NULL, // tp_setattr
@@ -286,7 +316,7 @@ PyTypeObject datasourceclient_type = {
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
- reinterpret_cast<initproc>(DataSourceClient_init),// tp_init
+ DataSourceClient_init, // tp_init
NULL, // tp_alloc
PyType_GenericNew, // tp_new
NULL, // tp_free
diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc
index 6ab29d8..1573b81 100644
--- a/src/lib/python/isc/datasrc/datasrc.cc
+++ b/src/lib/python/isc/datasrc/datasrc.cc
@@ -27,6 +27,7 @@
#include "finder_python.h"
#include "iterator_python.h"
#include "updater_python.h"
+#include "journal_reader_python.h"
#include <util/python/pycppwrapper_util.h>
#include <dns/python/pydnspp_common.h>
@@ -192,6 +193,41 @@ initModulePart_ZoneUpdater(PyObject* mod) {
return (true);
}
+bool
+initModulePart_ZoneJournalReader(PyObject* mod) {
+ if (PyType_Ready(&journal_reader_type) < 0) {
+ return (false);
+ }
+ void* p = &journal_reader_type;
+ if (PyModule_AddObject(mod, "ZoneJournalReader",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&journal_reader_type);
+
+ try {
+ installClassVariable(journal_reader_type, "SUCCESS",
+ Py_BuildValue("I", ZoneJournalReader::SUCCESS));
+ installClassVariable(journal_reader_type, "NO_SUCH_ZONE",
+ Py_BuildValue("I",
+ ZoneJournalReader::NO_SUCH_ZONE));
+ installClassVariable(journal_reader_type, "NO_SUCH_VERSION",
+ Py_BuildValue("I",
+ ZoneJournalReader::NO_SUCH_VERSION));
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in ZoneJournalReader initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in ZoneJournalReader initialization");
+ return (false);
+ }
+
+ return (true);
+}
PyObject* po_DataSourceError;
PyObject* po_NotImplemented;
@@ -239,6 +275,11 @@ PyInit_datasrc(void) {
return (NULL);
}
+ if (!initModulePart_ZoneJournalReader(mod)) {
+ Py_DECREF(mod);
+ return (NULL);
+ }
+
try {
po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
NULL);
diff --git a/src/lib/python/isc/datasrc/journal_reader_inc.cc b/src/lib/python/isc/datasrc/journal_reader_inc.cc
new file mode 100644
index 0000000..35ba70e
--- /dev/null
+++ b/src/lib/python/isc/datasrc/journal_reader_inc.cc
@@ -0,0 +1,80 @@
+namespace {
+const char* const ZoneJournalReader_doc = "\
+The base class for retrieving differences between two versions of a\n\
+zone.\n\
+\n\
+On construction, each derived class object will internally set up\n\
+retrieving sequences of differences between two specific version of a\n\
+specific zone managed in a particular data source. So the constructor\n\
+of a derived class would normally take parameters to identify the zone\n\
+and the two versions for which the differences should be retrieved.\n\
+See DataSourceClient.get_journal_reader for more concrete details used\n\
+in this API.\n\
+\n\
+Once constructed, an object of this class will act like an iterator\n\
+over the sequences. Every time the get_next_diff() method is called it\n\
+returns one element of the differences in the form of an RRset until\n\
+it reaches the end of the entire sequences.\n\
+\n\
+";
+
+// Modifications from C++ doc:
+// ConstRRsetPtr -> RRset
+// Null -> None
+// InvalidOperation -> ValueError
+const char* const ZoneJournalReader_getNextDiff_doc = "\
+get_next_diff() -> isc.dns.RRset\n\
+\n\
+Return the next difference RR of difference sequences.\n\
+\n\
+In this API, the difference between two versions of a zone is\n\
+conceptually represented as IXFR-style difference sequences: Each\n\
+difference sequence is a sequence of RRs: an older version of SOA (to\n\
+be deleted), zero or more other deleted RRs, the post-transaction SOA\n\
+(to be added), and zero or more other added RRs. (Note, however, that\n\
+the underlying data source implementation may or may not represent the\n\
+difference in straightforward realization of this concept. The mapping\n\
+between the conceptual difference and the actual implementation is\n\
+hidden in each derived class).\n\
+\n\
+This method provides an application with a higher level interface to\n\
+retrieve the difference along with the conceptual model: the\n\
+ZoneJournalReader object iterates over the entire sequences from the\n\
+beginning SOA (which is to be deleted) to one of the added RR of with\n\
+the ending SOA, and each call to this method returns one RR in the\n\
+form of an RRset that contains exactly one RDATA in the order of the\n\
+sequences.\n\
+\n\
+Note that the ordering of the sequences specifies the semantics of\n\
+each difference: add or delete. For example, the first RR is to be\n\
+deleted, and the last RR is to be added. So the return value of this\n\
+method does not explicitly indicate whether the RR is to be added or\n\
+deleted.\n\
+\n\
+This method ensures the returned RRset represents an RR, that is, it\n\
+contains exactly one RDATA. However, it does not necessarily ensure\n\
+that the resulting sequences are in the form of IXFR-style. For\n\
+example, the first RR is supposed to be an SOA, and it should normally\n\
+be the case, but this interface does not necessarily require the\n\
+derived class implementation ensure this. Normally the differences are\n\
+expected to be stored using this API (via a ZoneUpdater object), and\n\
+as long as that is the case and the underlying implementation follows\n\
+the requirement of the API, the result of this method should be a\n\
+valid IXFR-style sequences. So this API does not mandate the almost\n\
+redundant check as part of the interface. If the application needs to\n\
+make it sure 100%, it must check the resulting sequence itself.\n\
+\n\
+Once the object reaches the end of the sequences, this method returns\n\
+None. Any subsequent call will result in an exception of class\n\
+ValueError.\n\
+\n\
+Exceptions:\n\
+ ValueError The method is called beyond the end of the\n\
+ difference sequences.\n\
+ isc.datasrc.Error Underlying data is broken and the RR cannot be\n\
+ created or other low level data source error.\n\
+\n\
+Return Value(s): An RRset that contains one RDATA corresponding to the\n\
+next difference in the sequences.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/journal_reader_python.cc b/src/lib/python/isc/datasrc/journal_reader_python.cc
new file mode 100644
index 0000000..ff398d1
--- /dev/null
+++ b/src/lib/python/isc/datasrc/journal_reader_python.cc
@@ -0,0 +1,200 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+
+#include <dns/python/rrset_python.h>
+
+#include "datasrc.h"
+#include "journal_reader_python.h"
+
+#include "journal_reader_inc.cc"
+
+using namespace isc::util::python;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneJournalReader : public PyObject {
+public:
+ s_ZoneJournalReader() : cppobj(ZoneJournalReaderPtr()), base_obj(NULL) {};
+ ZoneJournalReaderPtr cppobj;
+ // This is a reference to a base object; if the object of this class
+ // depends on another object to be in scope during its lifetime,
+ // we use INCREF the base object upon creation, and DECREF it at
+ // the end of the destructor
+ // This is an optional argument to createXXX(). If NULL, it is ignored.
+ PyObject* base_obj;
+};
+
+// General creation and destruction
+int
+ZoneJournalReader_init(PyObject*, PyObject*, PyObject*) {
+ // can't be called directly
+ PyErr_SetString(PyExc_TypeError,
+ "ZoneJournalReader cannot be constructed directly");
+
+ return (-1);
+}
+
+void
+ZoneJournalReader_destroy(PyObject* po_self) {
+ s_ZoneJournalReader* const self =
+ static_cast<s_ZoneJournalReader*>(po_self) ;
+ // cppobj is a shared ptr, but to make sure things are not destroyed in
+ // the wrong order, we reset it here.
+ self->cppobj.reset();
+ if (self->base_obj != NULL) {
+ Py_DECREF(self->base_obj);
+ }
+ Py_TYPE(self)->tp_free(self);
+}
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+PyObject*
+ZoneJournalReader_getNextDiff(PyObject* po_self, PyObject*) {
+ s_ZoneJournalReader* self = static_cast<s_ZoneJournalReader*>(po_self);
+ try {
+ isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextDiff();
+ if (!rrset) {
+ Py_RETURN_NONE;
+ }
+ return (createRRsetObject(*rrset));
+ } catch (const isc::InvalidOperation& ex) {
+ PyErr_SetString(PyExc_ValueError, ex.what());
+ return (NULL);
+ } catch (const isc::Exception& isce) {
+ PyErr_SetString(getDataSourceException("Error"), isce.what());
+ return (NULL);
+ } catch (const std::exception& exc) {
+ PyErr_SetString(getDataSourceException("Error"), exc.what());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(getDataSourceException("Error"),
+ "Unexpected exception");
+ return (NULL);
+ }
+}
+
+PyObject*
+ZoneJournalReader_iter(PyObject *self) {
+ Py_INCREF(self);
+ return (self);
+}
+
+PyObject*
+ZoneJournalReader_next(PyObject* self) {
+ PyObject* result = ZoneJournalReader_getNextDiff(self, NULL);
+ // iter_next must return NULL without error instead of Py_None
+ if (result == Py_None) {
+ Py_DECREF(result);
+ return (NULL);
+ } else {
+ return (result);
+ }
+}
+
+PyMethodDef ZoneJournalReader_methods[] = {
+ { "get_next_diff", ZoneJournalReader_getNextDiff, METH_NOARGS,
+ ZoneJournalReader_getNextDiff_doc },
+ { NULL, NULL, 0, NULL }
+};
+
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+PyTypeObject journal_reader_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "datasrc.ZoneJournalReader",
+ sizeof(s_ZoneJournalReader), // tp_basicsize
+ 0, // tp_itemsize
+ ZoneJournalReader_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ ZoneJournalReader_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ ZoneJournalReader_iter, // tp_iter
+ ZoneJournalReader_next, // tp_iternext
+ ZoneJournalReader_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ ZoneJournalReader_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+PyObject*
+createZoneJournalReaderObject(ZoneJournalReaderPtr source,
+ PyObject* base_obj)
+{
+ s_ZoneJournalReader* po = static_cast<s_ZoneJournalReader*>(
+ journal_reader_type.tp_alloc(&journal_reader_type, 0));
+ if (po != NULL) {
+ po->cppobj = source;
+ po->base_obj = base_obj;
+ if (base_obj != NULL) {
+ Py_INCREF(base_obj);
+ }
+ }
+ return (po);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/python/isc/datasrc/journal_reader_python.h b/src/lib/python/isc/datasrc/journal_reader_python.h
new file mode 100644
index 0000000..56344df
--- /dev/null
+++ b/src/lib/python/isc/datasrc/journal_reader_python.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_JOURNAL_READER_H
+#define __PYTHON_DATASRC_JOURNAL_READER_H 1
+
+#include <Python.h>
+
+#include <datasrc/zone.h>
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+extern PyTypeObject journal_reader_type;
+
+/// \brief Create a ZoneJournalReader python object
+///
+/// \param source The zone journal reader pointer to wrap
+/// \param base_obj An optional PyObject that this ZoneJournalReader depends on
+/// Its refcount is increased, and will be decreased when
+/// this reader is destroyed, making sure that the
+/// base object is never destroyed before this reader.
+PyObject* createZoneJournalReaderObject(
+ isc::datasrc::ZoneJournalReaderPtr source,
+ PyObject* base_obj = NULL);
+
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // __PYTHON_DATASRC_JOURNAL_READER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index 02020e2..b0e06fd 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -15,7 +15,7 @@
import isc.log
import isc.datasrc
-from isc.datasrc import ZoneFinder
+from isc.datasrc import ZoneFinder, ZoneJournalReader
from isc.dns import *
import unittest
import sqlite3
@@ -62,6 +62,13 @@ def check_for_rrset(expected_rrsets, rrset):
return True
return False
+def create_soa(serial):
+ soa = RRset(Name('example.org'), RRClass.IN(), RRType.SOA(), RRTTL(3600))
+ soa.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
+ 'ns1.example.org. admin.example.org. ' +
+ str(serial) + ' 3600 1800 2419200 7200'))
+ return soa
+
class DataSrcClient(unittest.TestCase):
def test_(self):
@@ -606,14 +613,6 @@ class JournalWrite(unittest.TestCase):
self.assertEqual(expected, actual)
conn.close()
- def create_soa(self, serial):
- soa = RRset(Name('example.org'), RRClass.IN(), RRType.SOA(),
- RRTTL(3600))
- soa.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
- 'ns1.example.org. admin.example.org. ' +
- str(serial) + ' 3600 1800 2419200 7200'))
- return soa
-
def create_a(self, address):
a_rr = RRset(Name('www.example.org'), RRClass.IN(), RRType.A(),
RRTTL(3600))
@@ -624,9 +623,9 @@ class JournalWrite(unittest.TestCase):
# This is a straightforward port of the C++ 'journal' test
# Note: we add/delete 'out of zone' data (example.org in the
# example.com zone for convenience.
- self.updater.delete_rrset(self.create_soa(1234))
+ self.updater.delete_rrset(create_soa(1234))
self.updater.delete_rrset(self.create_a('192.0.2.2'))
- self.updater.add_rrset(self.create_soa(1235))
+ self.updater.add_rrset(create_soa(1235))
self.updater.add_rrset(self.create_a('192.0.2.2'))
self.updater.commit()
@@ -645,11 +644,11 @@ class JournalWrite(unittest.TestCase):
# This is a straightforward port of the C++ 'journalMultiple' test
expected = []
for i in range(1, 100):
- self.updater.delete_rrset(self.create_soa(1234 + i - 1))
+ self.updater.delete_rrset(create_soa(1234 + i - 1))
expected.append(("example.org.", "SOA", 3600,
"ns1.example.org. admin.example.org. " +
str(1234 + i - 1) + " 3600 1800 2419200 7200"))
- self.updater.add_rrset(self.create_soa(1234 + i))
+ self.updater.add_rrset(create_soa(1234 + i))
expected.append(("example.org.", "SOA", 3600,
"ns1.example.org. admin.example.org. " +
str(1234 + i) + " 3600 1800 2419200 7200"))
@@ -665,27 +664,27 @@ class JournalWrite(unittest.TestCase):
# Add before delete
self.updater = self.dsc.get_updater(Name("example.com"), False, True)
self.assertRaises(isc.datasrc.Error, self.updater.add_rrset,
- self.create_soa(1234))
+ create_soa(1234))
# Add A before SOA
self.updater = self.dsc.get_updater(Name("example.com"), False, True)
- self.updater.delete_rrset(self.create_soa(1234))
+ self.updater.delete_rrset(create_soa(1234))
self.assertRaises(isc.datasrc.Error, self.updater.add_rrset,
self.create_a('192.0.2.1'))
# Commit before add
self.updater = self.dsc.get_updater(Name("example.com"), False, True)
- self.updater.delete_rrset(self.create_soa(1234))
+ self.updater.delete_rrset(create_soa(1234))
self.assertRaises(isc.datasrc.Error, self.updater.commit)
# Delete two SOAs
self.updater = self.dsc.get_updater(Name("example.com"), False, True)
- self.updater.delete_rrset(self.create_soa(1234))
+ self.updater.delete_rrset(create_soa(1234))
self.assertRaises(isc.datasrc.Error, self.updater.delete_rrset,
- self.create_soa(1235))
+ create_soa(1235))
# Add two SOAs
self.updater = self.dsc.get_updater(Name("example.com"), False, True)
- self.updater.delete_rrset(self.create_soa(1234))
- self.updater.add_rrset(self.create_soa(1235))
+ self.updater.delete_rrset(create_soa(1234))
+ self.updater.add_rrset(create_soa(1235))
self.assertRaises(isc.datasrc.Error, self.updater.add_rrset,
- self.create_soa(1236))
+ create_soa(1236))
def test_journal_write_onerase(self):
self.updater = None
@@ -700,6 +699,110 @@ class JournalWrite(unittest.TestCase):
self.assertRaises(TypeError, dsc.get_updater, Name("example.com"),
1, True)
+class JournalRead(unittest.TestCase):
+ def setUp(self):
+ # Make a fresh copy of the writable database with all original content
+ self.zname = Name('example.com')
+ shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
+ self.dsc = isc.datasrc.DataSourceClient("sqlite3",
+ WRITE_ZONE_DB_CONFIG)
+ self.reader = None
+
+ def tearDown(self):
+ # Some tests leave the reader in the middle of sequence, holding
+ # the lock. Since the unittest framework keeps each test object
+ # until the end of the entire tests, we need to make sure the reader
+ # is released at the end of each test. The client shouldn't do harm
+ # but we clean it up, too, just in case.
+ self.dsc = None
+ self.reader = None
+
+ def make_simple_diff(self, begin_soa):
+ updater = self.dsc.get_updater(self.zname, False, True)
+ updater.delete_rrset(begin_soa)
+ updater.add_rrset(create_soa(1235))
+ updater.commit()
+
+ def test_journal_reader(self):
+ # This is a straightforward port of the C++ 'journalReader' test
+ self.make_simple_diff(create_soa(1234))
+ result, self.reader = self.dsc.get_journal_reader(self.zname, 1234,
+ 1235)
+ self.assertEqual(ZoneJournalReader.SUCCESS, result)
+ self.assertNotEqual(None, self.reader)
+ rrsets_equal(create_soa(1234), self.reader.get_next_diff())
+ rrsets_equal(create_soa(1235), self.reader.get_next_diff())
+ self.assertEqual(None, self.reader.get_next_diff())
+ self.assertRaises(ValueError, self.reader.get_next_diff)
+
+ def test_journal_reader_with_large_serial(self):
+ # similar to the previous one, but use a very large serial to check
+ # if the python wrapper code has unexpected integer overflow
+ self.make_simple_diff(create_soa(4294967295))
+ result, self.reader = self.dsc.get_journal_reader(self.zname,
+ 4294967295, 1235)
+ self.assertNotEqual(None, self.reader)
+ # dump to text and compare them in case create_soa happens to have
+ # an overflow bug
+ self.assertEqual('example.org. 3600 IN SOA ns1.example.org. ' + \
+ 'admin.example.org. 4294967295 3600 1800 ' + \
+ '2419200 7200\n',
+ self.reader.get_next_diff().to_text())
+
+ def test_journal_reader_large_journal(self):
+ # This is a straightforward port of the C++ 'readLargeJournal' test.
+ # In this test we use the ZoneJournalReader object as a Python
+ # iterator.
+ updater = self.dsc.get_updater(self.zname, False, True)
+ expected = []
+ for i in range(0, 100):
+ rrset = create_soa(1234 + i)
+ updater.delete_rrset(rrset)
+ expected.append(rrset)
+
+ rrset = create_soa(1234 + i + 1)
+ updater.add_rrset(rrset)
+ expected.append(rrset)
+
+ updater.commit()
+ _, self.reader = self.dsc.get_journal_reader(self.zname, 1234, 1334)
+ self.assertNotEqual(None, self.reader)
+ i = 0
+ for rr in self.reader:
+ self.assertNotEqual(len(expected), i)
+ rrsets_equal(expected[i], rr)
+ i += 1
+ self.assertEqual(len(expected), i)
+
+ def test_journal_reader_no_range(self):
+ # This is a straightforward port of the C++ 'readJournalForNoRange'
+ # test
+ self.make_simple_diff(create_soa(1234))
+ result, self.reader = self.dsc.get_journal_reader(self.zname, 1200,
+ 1235)
+ self.assertEqual(ZoneJournalReader.NO_SUCH_VERSION, result)
+ self.assertEqual(None, self.reader)
+
+ def test_journal_reader_no_zone(self):
+ # This is a straightforward port of the C++ 'journalReaderForNXZone'
+ # test
+ result, self.reader = self.dsc.get_journal_reader(Name('nosuchzone'),
+ 0, 1)
+ self.assertEqual(ZoneJournalReader.NO_SUCH_ZONE, result)
+ self.assertEqual(None, self.reader)
+
+ def test_journal_reader_bad_params(self):
+ self.assertRaises(TypeError, self.dsc.get_journal_reader,
+ 'example.com.', 0, 1)
+ self.assertRaises(TypeError, self.dsc.get_journal_reader,
+ self.zname, 'must be int', 1)
+ self.assertRaises(TypeError, self.dsc.get_journal_reader,
+ self.zname, 0, 'must be int')
+
+ def test_journal_reader_direct_construct(self):
+ # ZoneJournalReader can only be constructed via a factory
+ self.assertRaises(TypeError, ZoneJournalReader)
+
if __name__ == "__main__":
isc.log.init("bind10")
isc.log.resetUnitTestRootLogger()
More information about the bind10-changes
mailing list