Commit 638087de authored by Paul Selkirk's avatar Paul Selkirk
Browse files

Merge branch 'trac2908'

parents 67704c85 926a9b1b
......@@ -121,8 +121,6 @@ private:
MemorySegmentState state_;
};
typedef boost::shared_ptr<const ZoneTableAccessor> ConstZoneTableAccessorPtr;
/// \brief The list of data source clients.
///
/// The purpose of this class is to hold several data source clients and search
......
......@@ -202,6 +202,9 @@ public:
virtual IteratorPtr getIterator() const = 0;
};
typedef boost::shared_ptr<ZoneTableAccessor> ZoneTableAccessorPtr;
typedef boost::shared_ptr<const ZoneTableAccessor> ConstZoneTableAccessorPtr;
}
}
......
......@@ -21,6 +21,8 @@ datasrc_la_SOURCES += journal_reader_python.cc journal_reader_python.h
datasrc_la_SOURCES += configurableclientlist_python.cc
datasrc_la_SOURCES += configurableclientlist_python.h
datasrc_la_SOURCES += zone_loader_python.cc zone_loader_python.h
datasrc_la_SOURCES += zonetable_accessor_python.cc zonetable_accessor_python.h
datasrc_la_SOURCES += zonetable_iterator_python.cc zonetable_iterator_python.h
datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 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
......@@ -35,6 +35,7 @@
#include "datasrc.h"
#include "finder_python.h"
#include "client_python.h"
#include "zonetable_accessor_python.h"
using namespace std;
using namespace isc::util::python;
......@@ -206,6 +207,37 @@ ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
}
}
PyObject*
ConfigurableClientList_getZoneTableAccessor(PyObject* po_self, PyObject* args) {
s_ConfigurableClientList* self =
static_cast<s_ConfigurableClientList*>(po_self);
try {
const char* datasrc_name;
int use_cache;
if (PyArg_ParseTuple(args, "zi", &datasrc_name, &use_cache)) {
// python 'None' will be read as NULL, which we convert to an
// empty string, meaning "any data source"
const std::string name(datasrc_name ? datasrc_name : "");
const ConstZoneTableAccessorPtr
z(self->cppobj->getZoneTableAccessor(name, use_cache));
if (z == NULL) {
Py_RETURN_NONE;
} else {
return (createZoneTableAccessorObject(z, po_self));
}
} else {
return (NULL);
}
} catch (const std::exception& exc) {
PyErr_SetString(getDataSourceException("Error"), exc.what());
return (NULL);
} catch (...) {
PyErr_SetString(getDataSourceException("Error"),
"Unknown C++ exception");
return (NULL);
}
}
// This list contains the actual set of functions we have in
// python. Each entry has
// 1. Python method name
......@@ -261,6 +293,16 @@ you don't need it, but if you do need it, it is better to set it to True\
instead of getting it from the datasrc_client later.\n\
\n\
If no answer is found, the datasrc_client and zone_finder are None." },
{ "get_zone_table_accessor", ConfigurableClientList_getZoneTableAccessor,
METH_VARARGS,
"get_zone_table_accessor(datasrc_name, use_cache) -> \
isc.datasrc.ZoneTableAccessor\n\
\n\
Create a ZoneTableAccessor object for the specified data source.\n\
\n\
Parameters:\n\
datasrc_name If not empty, the name of the data source\
use_cache If true, create a zone table for in-memory cache." },
{ NULL, NULL, 0, NULL }
};
......@@ -286,9 +328,9 @@ namespace python {
PyTypeObject configurableclientlist_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"datasrc.ConfigurableClientList",
sizeof(s_ConfigurableClientList), // tp_basicsize
sizeof(s_ConfigurableClientList), // tp_basicsize
0, // tp_itemsize
ConfigurableClientList_destroy, // tp_dealloc
ConfigurableClientList_destroy, // tp_dealloc
NULL, // tp_print
NULL, // tp_getattr
NULL, // tp_setattr
......@@ -311,7 +353,7 @@ PyTypeObject configurableclientlist_type = {
0, // tp_weaklistoffset
NULL, // tp_iter
NULL, // tp_iternext
ConfigurableClientList_methods, // tp_methods
ConfigurableClientList_methods, // tp_methods
NULL, // tp_members
NULL, // tp_getset
NULL, // tp_base
......@@ -319,7 +361,7 @@ PyTypeObject configurableclientlist_type = {
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
ConfigurableClientList_init, // tp_init
ConfigurableClientList_init, // tp_init
NULL, // tp_alloc
PyType_GenericNew, // tp_new
NULL, // tp_free
......
......@@ -33,6 +33,8 @@
#include "journal_reader_python.h"
#include "configurableclientlist_python.h"
#include "zone_loader_python.h"
#include "zonetable_accessor_python.h"
#include "zonetable_iterator_python.h"
#include <util/python/pycppwrapper_util.h>
#include <dns/python/pydnspp_common.h>
......@@ -255,6 +257,42 @@ initModulePart_ZoneJournalReader(PyObject* mod) {
return (true);
}
bool
initModulePart_ZoneTableAccessor(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(&zonetableaccessor_type) < 0) {
return (false);
}
void* p = &zonetableaccessor_type;
if (PyModule_AddObject(mod, "ZoneTableAccessor",
static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&zonetableaccessor_type);
return (true);
}
bool
initModulePart_ZoneTableIterator(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(&zonetableiterator_type) < 0) {
return (false);
}
void* p = &zonetableiterator_type;
if (PyModule_AddObject(mod, "ZoneTableIterator",
static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&zonetableiterator_type);
return (true);
}
PyObject* po_DataSourceError;
PyObject* po_MasterFileError;
PyObject* po_NotImplemented;
......@@ -340,5 +378,15 @@ PyInit_datasrc(void) {
return (NULL);
}
if (!initModulePart_ZoneTableAccessor(mod)) {
Py_DECREF(mod);
return (NULL);
}
if (!initModulePart_ZoneTableIterator(mod)) {
Py_DECREF(mod);
return (NULL);
}
return (mod);
}
......@@ -64,7 +64,7 @@ getFindResultFlags(const ZoneFinder::Context& context) {
namespace isc_datasrc_internal {
// This is the shared code for the find() call in the finder and the updater
// Is is intentionally not available through any header, nor at our standard
// It is intentionally not available through any header, nor at our standard
// namespace, as it is not supposed to be called anywhere but from finder and
// updater
PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
......@@ -184,7 +184,7 @@ public:
ZoneFinderPtr 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
// we 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;
......
# Copyright (C) 2012 Internet Systems Consortium.
# Copyright (C) 2012-2013 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
......@@ -57,7 +57,7 @@ class ClientListTest(unittest.TestCase):
def test_configure(self):
"""
Test we can configure the client list. This tests if the valid
ones are acceptend and invalid rejected. We check the changes
ones are accepted and invalid rejected. We check the changes
have effect.
"""
self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
......@@ -151,6 +151,86 @@ class ClientListTest(unittest.TestCase):
self.assertRaises(TypeError, self.clist.find, "example.org")
self.assertRaises(TypeError, self.clist.find)
def test_get_zone_table_accessor(self):
"""
Test that we can get the zone table accessor and, thereby,
the zone table iterator.
"""
self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN)
# null configuration
self.clist.configure("[]", True)
self.assertIsNone(self.clist.get_zone_table_accessor(None, True))
# empty configuration
self.clist.configure('''[{
"type": "MasterFiles",
"params": {},
"cache-enable": true
}]''', True)
# bogus datasrc
self.assertIsNone(self.clist.get_zone_table_accessor("bogus", True))
# first datasrc - empty zone table
table = self.clist.get_zone_table_accessor(None, True)
self.assertIsNotNone(table)
iterator = iter(table)
self.assertIsNotNone(iterator)
self.assertEqual(0, len(list(iterator)))
# normal configuration
self.clist.configure('''[{
"type": "MasterFiles",
"params": {
"example.org": "''' + TESTDATA_PATH + '''example.org.zone"
},
"cache-enable": true
}]''', True)
# !use_cache => NotImplemented
self.assertRaises(isc.datasrc.Error,
self.clist.get_zone_table_accessor, None, False)
# bogus datasrc
self.assertIsNone(self.clist.get_zone_table_accessor("bogus", True))
# first datasrc
table = self.clist.get_zone_table_accessor(None, True)
self.assertIsNotNone(table)
zonelist = list(table)
self.assertEqual(1, len(zonelist))
self.assertEqual(zonelist[0][1], isc.dns.Name("example.org"))
# named datasrc
table = self.clist.get_zone_table_accessor("MasterFiles", True)
self.assertEqual(zonelist, list(table))
# longer zone list for non-trivial iteration
self.clist.configure('''[{
"type": "MasterFiles",
"params": {
"example.org": "''' + TESTDATA_PATH + '''example.org.zone",
"example.com": "''' + TESTDATA_PATH + '''example.com.zone",
"example.net": "''' + TESTDATA_PATH + '''example.net.zone",
"example.biz": "''' + TESTDATA_PATH + '''example.biz.zone",
"example.edu": "''' + TESTDATA_PATH + '''example.edu.zone"
},
"cache-enable": true
}]''', True)
zonelist = list(self.clist.get_zone_table_accessor(None, True))
self.assertEqual(5, len(zonelist))
self.assertTrue((0, isc.dns.Name("example.net.")) in zonelist)
# ensure the iterator returns exactly and only the zones we expect
zonelist = [
isc.dns.Name("example.org"),
isc.dns.Name("example.com"),
isc.dns.Name("example.net"),
isc.dns.Name("example.biz"),
isc.dns.Name("example.edu")]
table = self.clist.get_zone_table_accessor("MasterFiles", True)
for index, zone in table:
self.assertTrue(zone in zonelist)
zonelist.remove(zone)
self.assertEqual(0, len(zonelist))
if __name__ == "__main__":
isc.log.init("bind10")
isc.log.resetUnitTestRootLogger()
......
// Copyright (C) 2013 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 <datasrc/zone_table_accessor.h>
#include "datasrc.h"
#include "zonetable_accessor_python.h"
#include "zonetable_iterator_python.h"
using namespace std;
using namespace isc::datasrc;
using namespace isc::datasrc::python;
namespace {
// The s_* Class simply covers one instantiation of the object
class s_ZoneTableAccessor : public PyObject {
public:
s_ZoneTableAccessor() : cppobj(ConstZoneTableAccessorPtr()) {};
ConstZoneTableAccessorPtr 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;
};
int
ZoneTableAccessor_init(PyObject*, PyObject*, PyObject*) {
// can't be called directly
PyErr_SetString(PyExc_TypeError,
"ZoneTableAccessor cannot be constructed directly");
return (-1);
}
void
ZoneTableAccessor_destroy(PyObject* po_self) {
s_ZoneTableAccessor* const self =
static_cast<s_ZoneTableAccessor*>(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);
}
PyObject*
ZoneTableAccessor_iter(PyObject* po_self) {
s_ZoneTableAccessor* const self =
static_cast<s_ZoneTableAccessor*>(po_self);
try {
return (createZoneTableIteratorObject(self->cppobj->getIterator(),
po_self));
} catch (const std::exception& exc) {
PyErr_SetString(getDataSourceException("Error"), exc.what());
return (NULL);
} catch (...) {
PyErr_SetString(getDataSourceException("Error"),
"Unexpected exception");
return (NULL);
}
}
// This list contains the actual set of functions we have in
// python. Each entry has
// 1. Python method name
// 2. Our static function here
// 3. Argument type
// 4. Documentation
PyMethodDef ZoneTableAccessor_methods[] = {
{ NULL, NULL, 0, NULL }
};
const char* const ZoneTableAccessor_doc = "\
An accessor to a zone table for a data source.\n\
\n\
This class object is intended to be used by applications that load zones\
into memory, so that the application can get a list of zones to be loaded.";
} // end anonymous namespace
namespace isc {
namespace datasrc {
namespace python {
// This defines the complete type for reflection in python and
// parsing of PyObject* to s_ZoneTableAccessor
// Most of the functions are not actually implemented and NULL here.
PyTypeObject zonetableaccessor_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"datasrc.ZoneTableAccessor",
sizeof(s_ZoneTableAccessor), // tp_basicsize
0, // tp_itemsize
ZoneTableAccessor_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
ZoneTableAccessor_doc, // tp_doc
NULL, // tp_traverse
NULL, // tp_clear
NULL, // tp_richcompare
0, // tp_weaklistoffset
ZoneTableAccessor_iter, // tp_iter
NULL, // tp_iternext
ZoneTableAccessor_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
ZoneTableAccessor_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*
createZoneTableAccessorObject(isc::datasrc::ConstZoneTableAccessorPtr source,
PyObject* base_obj)
{
s_ZoneTableAccessor* py_zt = static_cast<s_ZoneTableAccessor*>(
zonetableaccessor_type.tp_alloc(&zonetableaccessor_type, 0));
if (py_zt != NULL) {
py_zt->cppobj = source;
py_zt->base_obj = base_obj;
if (base_obj != NULL) {
Py_INCREF(base_obj);
}
}
return (py_zt);
}
} // namespace python
} // namespace datasrc
} // namespace isc
// Copyright (C) 2013 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_ZONETABLE_ACCESSOR_H
#define PYTHON_ZONETABLE_ACCESSOR_H 1
#include <Python.h>
namespace isc {
namespace datasrc {
namespace python {
extern PyTypeObject zonetableaccessor_type;
/// \brief Create a ZoneTableAccessor python object
///
/// \param source The zone iterator pointer to wrap
/// \param base_obj An optional PyObject that this ZoneFinder depends on
/// Its refcount is increased, and will be decreased when
/// this zone iterator is destroyed, making sure that the
/// base object is never destroyed before this zonefinder.
PyObject* createZoneTableAccessorObject(
isc::datasrc::ConstZoneTableAccessorPtr source, PyObject* base_obj);
} // namespace python
} // namespace datasrc
} // namespace isc
#endif // PYTHON_ZONETABLE_ACCESSOR_H
// Local Variables:
// mode: c++
// End:
// Copyright (C) 2013 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 <datasrc/zone_table_accessor.h>
#include <dns/python/name_python.h>
#include "datasrc.h"
#include "zonetable_iterator_python.h"
using namespace std;
using namespace isc::datasrc;
using namespace isc::datasrc::python;
namespace {
// The s_* Class simply covers one instantiation of the object
class s_ZoneTableIterator : public PyObject {
public:
s_ZoneTableIterator() :
cppobj(ZoneTableAccessor::IteratorPtr()), base_obj(NULL)
{};
ZoneTableAccessor::IteratorPtr 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
ZoneTableIterator_init(s_ZoneTableIterator* self, PyObject* args) {
// can't be called directly
PyErr_SetString(PyExc_TypeError,
"ZoneTableIterator cannot be constructed directly");
return (-1);
}
void
ZoneTableIterator_destroy(s_ZoneTableIterator* const 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) {