Commit fa89a079 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1333r] added C++-python wrapper for ZoneJournalReader class and

DataSourceClient.get_journal_reader().
parent 12b1a920
......@@ -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,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
......
......@@ -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);
......
// 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
// 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:
......@@ -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):