Browse Source

[1333r] added C++-python wrapper for ZoneJournalReader class and
DataSourceClient.get_journal_reader().

JINMEI Tatuya 13 years ago
parent
commit
fa89a0798d

+ 1 - 0
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)

+ 38 - 8
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_getUpdater_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

+ 41 - 0
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);

+ 205 - 0
src/lib/python/isc/datasrc/journal_reader_python.cc

@@ -0,0 +1,205 @@
+// 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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// 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"
+
+// NOTYET
+//#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

+ 47 - 0
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:

+ 115 - 21
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):
@@ -591,14 +598,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))
@@ -609,9 +608,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()
 
@@ -630,11 +629,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"))
@@ -650,27 +649,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
@@ -685,6 +684,101 @@ 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):
+        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')
+
 if __name__ == "__main__":
     isc.log.init("bind10")
     isc.log.resetUnitTestRootLogger()