Commit 738540ce authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[2438] added python wrapper for ZoneUpdater::getRRsetCollection().

parent 3b37e9c1
......@@ -198,23 +198,6 @@ initModulePart_ZoneLoader(PyObject* mod) {
return (true);
}
bool
initModulePart_ZoneUpdater(PyObject* mod) {
// We initialize the static description object with PyType_Ready(),
// then add it to the module. This is not just a check! (leaving
// this out results in segmentation faults)
if (PyType_Ready(&zoneupdater_type) < 0) {
return (false);
}
void* zip = &zoneupdater_type;
if (PyModule_AddObject(mod, "ZoneUpdater", static_cast<PyObject*>(zip)) < 0) {
return (false);
}
Py_INCREF(&zoneupdater_type);
return (true);
}
bool
initModulePart_ZoneJournalReader(PyObject* mod) {
if (PyType_Ready(&journal_reader_type) < 0) {
......
......@@ -528,6 +528,41 @@ class DataSrcUpdater(unittest.TestCase):
self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
rrset.to_text())
def test_updater_rrset_collection(self):
dsc = isc.datasrc.DataSourceClient("sqlite3", WRITE_ZONE_DB_CONFIG)
updater = dsc.get_updater(isc.dns.Name("example.com"), False)
updater_refs = sys.getrefcount(updater)
# Get (internally create) updater's RRset collection
rrsets = updater.get_rrset_collection()
# From this point we cannot make further updates
rrset = RRset(isc.dns.Name('www.example.com'), isc.dns.RRClass.IN(),
isc.dns.RRType.AAAA(), isc.dns.RRTTL(10))
rrset.add_rdata(isc.dns.Rdata(isc.dns.RRType.AAAA(),
isc.dns.RRClass.IN(), '2001:db8::1'))
self.assertRaises(isc.datasrc.Error, updater.add_rrset, rrset)
# Checks basic API
found = rrsets.find(isc.dns.Name("www.example.com"),
isc.dns.RRClass.IN(), isc.dns.RRType.A())
self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
found.to_text())
self.assertEqual(None, rrsets.find(isc.dns.Name("www.example.com"),
isc.dns.RRClass.IN(),
isc.dns.RRType.AAAA()))
# Once committed collection cannot be used any more.
updater.commit()
self.assertRaises(isc.dns.RRsetCollectionError,
rrsets.find, isc.dns.Name("www.example.com"),
isc.dns.RRClass.IN(), isc.dns.RRType.A())
# When we destroy the RRsetCollection it should release the refcount
# to the updater.
rrsets = None
self.assertEqual(updater_refs, sys.getrefcount(updater))
def test_two_modules(self):
# load two modules, and check if they don't interfere
mem_cfg = { "type": "memory", "class": "IN", "zones": [] };
......
......@@ -76,6 +76,10 @@ This method must not be called once commit() is performed. If it calls\n\
after commit() the implementation must throw a isc.datasrc.Error\n\
exception.\n\
\n\
Implementations of ZoneUpdater may not allow adding or deleting RRsets\n\
after get_rrset_collection() is called. In this case, implementations\n\
throw an InvalidOperation exception.\n\
\n\
Todo As noted above we may have to revisit the design details as we\n\
gain experiences:\n\
\n\
......@@ -133,6 +137,10 @@ This method must not be called once commit() is performed. If it calls\n\
after commit() the implementation must throw a isc.datasrc.Error\n\
exception.\n\
\n\
Implementations of ZoneUpdater may not allow adding or deleting RRsets\n\
after get_rrset_collection() is called. In this case, implementations\n\
throw an InvalidOperation exception.\n\
\n\
Todo: As noted above we may have to revisit the design details as we\n\
gain experiences:\n\
\n\
......@@ -178,4 +186,32 @@ Exceptions:\n\
error, or wrapper error\n\\n\
\n\
";
// Modifications
// - isc.datasrc.RRsetCollectionBase => isc.dns.RRsetCollectionBase
// (in the Python wrapper, the former is completely invisible)
// - remove other reference to isc.datasrc.RRsetCollectionBase
const char* const ZoneUpdater_getRRsetCollection_doc = "\
get_rrset_collection() -> isc.dns.RRsetCollectionBase \n\
\n\
Return an RRsetCollection for the updater.\n\
\n\
This method returns an RRsetCollection for the updater, implementing\n\
the isc.dns.RRsetCollectionBase interface. Typically, the returned\n\
RRsetCollection is a singleton for its ZoneUpdater. The returned\n\
RRsetCollection object must not be used after its corresponding\n\
ZoneUpdater has been destroyed. The returned RRsetCollection object\n\
may be used to search RRsets from the ZoneUpdater. The actual\n\
RRsetCollection returned has a behavior dependent on the ZoneUpdater\n\
implementation.\n\
\n\
The behavior of the RRsetCollection is similar to the behavior of the\n\
Zonefinder returned by get_finder(). Implementations of ZoneUpdater\n\
may not allow adding or deleting RRsets after get_rrset_collection()\n\
is called. Implementations of ZoneUpdater may disable a previously\n\
returned RRsetCollection after commit() is called. If an\n\
RRsetCollection is disabled, using methods such as find() and using\n\
its iterator would cause an exception to be thrown.\n\
\n\
";
} // unnamed namespace
......@@ -32,6 +32,7 @@
#include <dns/python/rrset_python.h>
#include <dns/python/rrclass_python.h>
#include <dns/python/rrtype_python.h>
#include <dns/python/rrset_collection_python.h>
#include "datasrc.h"
#include "updater_python.h"
......@@ -195,6 +196,107 @@ ZoneUpdater_find_all(PyObject* po_self, PyObject* args) {
&self->cppobj->getFinder(), args));
}
namespace {
// Below we define Python RRsetCollection class generated by the updater.
// It's never expected to be instantiated directly from Python code, so
// everything is hidden here, and tp_init always fails.
class s_UpdaterRRsetCollection : public s_RRsetCollection {
public:
s_UpdaterRRsetCollection() : s_RRsetCollection() {}
PyObject* base_obj_;
};
int
RRsetCollection_init(PyObject*, PyObject*, PyObject*) {
// can't be called directly; actually, the constructor shouldn't even be
// called, but we catch the case just in case.
PyErr_SetString(PyExc_TypeError,
"datasrc.RRsetCollection cannot be constructed directly");
return (-1);
}
void
RRsetCollection_destroy(PyObject* po_self) {
s_UpdaterRRsetCollection* const self =
static_cast<s_UpdaterRRsetCollection*>(po_self);
// We don't own the C++ collection object; we shouldn't delete it here.
// Note: we need to check if this is NULL; it remains NULL in case of
// direct instantiation (which fails).
if (self->base_obj_ != NULL) {
Py_DECREF(self->base_obj_);
}
Py_TYPE(self)->tp_free(self);
}
PyTypeObject updater_rrset_collection_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"datasrc.UpdaterRRsetCollection",
sizeof(s_UpdaterRRsetCollection), // tp_basicsize
0, // tp_itemsize
RRsetCollection_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
NULL,
NULL, // tp_traverse
NULL, // tp_clear
NULL, // tp_richcompare
0, // tp_weaklistoffset
NULL, // tp_iter
NULL, // tp_iternext
NULL, // tp_methods
NULL, // tp_members
NULL, // tp_getset
NULL, // tp_base (rrset_collection_base_type, set at run time)
NULL, // tp_dict
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
RRsetCollection_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
};
} // unnamed namespace
PyObject*
ZoneUpdater_getRRsetCollection(PyObject* po_self, PyObject*) {
s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
s_UpdaterRRsetCollection* collection =
static_cast<s_UpdaterRRsetCollection*>(
PyObject_New(s_RRsetCollection, &updater_rrset_collection_type));
collection->cppobj = &self->cppobj->getRRsetCollection();
collection->base_obj_ = po_self;;
Py_INCREF(collection->base_obj_);
return (collection);
}
// This list contains the actual set of functions we have in
// python. Each entry has
// 1. Python method name
......@@ -207,6 +309,8 @@ PyMethodDef ZoneUpdater_methods[] = {
{ "delete_rrset", ZoneUpdater_deleteRRset,
METH_VARARGS, ZoneUpdater_deleteRRset_doc },
{ "commit", ZoneUpdater_commit, METH_NOARGS, ZoneUpdater_commit_doc },
{ "get_rrset_collection", ZoneUpdater_getRRsetCollection,
METH_NOARGS, ZoneUpdater_getRRsetCollection_doc },
// Instead of a getFinder, we implement the finder functionality directly
// This is because ZoneFinder is non-copyable, and we should not create
// a ZoneFinder object from a reference only (which is what is returned
......@@ -292,6 +396,56 @@ createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source,
return (py_zu);
}
bool
initModulePart_ZoneUpdater(PyObject* mod) {
// We initialize the static description object with PyType_Ready(),
// then add it to the module. This is not just a check! (leaving
// this out results in segmentation faults)
if (PyType_Ready(&zoneupdater_type) < 0) {
return (false);
}
void* zip = &zoneupdater_type;
if (PyModule_AddObject(mod, "ZoneUpdater",
static_cast<PyObject*>(zip)) < 0)
{
return (false);
}
Py_INCREF(&zoneupdater_type);
// get_rrset_collection() needs the base class type information. Since
// it's defined in a separate loadable module, we retrieve its C object
// via the Python interpreter. Directly referring to
// isc::dns::python::rrset_collection_base_type might work depending on
// the runtime environment (and in fact it does for some), but that would
// be less portable.
try {
if (updater_rrset_collection_type.tp_base == NULL) {
PyObjectContainer dns_module(PyImport_ImportModule("isc.dns"));
PyObjectContainer dns_dict(PyModule_GetDict(dns_module.get()));
// GetDict doesn't acquire a reference, so we need to get it to
// meet the container's requirement.
Py_INCREF(dns_dict.get());
PyObjectContainer base(
PyDict_GetItemString(dns_dict.get(), "RRsetCollectionBase"));
PyTypeObject* pt_rrset_collection_base =
static_cast<PyTypeObject*>(static_cast<void*>(base.get()));
updater_rrset_collection_type.tp_base = pt_rrset_collection_base;
if (PyType_Ready(&updater_rrset_collection_type) < 0) {
isc_throw(Unexpected, "failed to import isc.dns module");
}
// Make sure the base type won't suddenly disappear. Note that we
// are effectively leaking it; it's intentional.
Py_INCREF(base.get());
}
} catch (...) {
PyErr_SetString(PyExc_SystemError,
"Unexpected failure in ZoneUpdater initialization");
return (false);
}
return (true);
}
} // namespace python
} // namespace datasrc
} // namespace isc
......
......@@ -36,7 +36,7 @@ extern PyTypeObject zoneupdater_type;
PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source,
PyObject* base_obj = NULL);
bool initModulePart_ZoneUpdater(PyObject* mod);
} // namespace python
} // namespace datasrc
} // namespace isc
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment