Commit 214e0409 authored by Jeremy C. Reed's avatar Jeremy C. Reed

[master]Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10

parents e5a42900 a489ad14
This diff is collapsed.
......@@ -47,6 +47,7 @@ namespace dns {
namespace python {
// For our 'general' isc::Exceptions
PyObject* po_IscException;
PyObject* po_InvalidOperation;
PyObject* po_InvalidParameter;
// For our own isc::dns::Exception
......@@ -91,6 +92,22 @@ addClassVariable(PyTypeObject& c, const char* name, PyObject* obj) {
}
return (PyDict_SetItemString(c.tp_dict, name, obj));
}
bool
initClass(PyTypeObject& type, const char* name, 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)
//
void* p = &type;
if (PyType_Ready(&type) < 0 ||
PyModule_AddObject(mod, name, static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&type);
return (true);
}
}
}
}
......@@ -28,6 +28,7 @@ namespace dns {
namespace python {
// For our 'general' isc::Exceptions
extern PyObject* po_IscException;
extern PyObject* po_InvalidOperation;
extern PyObject* po_InvalidParameter;
// For our own isc::dns::Exception
......@@ -47,6 +48,18 @@ int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence);
int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
/// \brief Initialize a wrapped class type, and add to module
///
/// The type object is initalized, and its refcount is increased after
/// successful addition to the module.
///
/// \param type The type object to initialize
/// \param name The python name of the class to add
/// \param mod The python module to add the class to
///
/// \return true on success, false on failure
bool initClass(PyTypeObject& type, const char* name, PyObject* mod);
// Short term workaround for unifying the return type of tp_hash
#if PY_MINOR_VERSION < 2
typedef long Py_hash_t;
......
......@@ -20,6 +20,7 @@ datasrc_la_SOURCES += updater_python.cc updater_python.h
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_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
......@@ -35,6 +36,7 @@ EXTRA_DIST += finder_inc.cc
EXTRA_DIST += iterator_inc.cc
EXTRA_DIST += updater_inc.cc
EXTRA_DIST += journal_reader_inc.cc
EXTRA_DIST += zone_loader_inc.cc
CLEANDIRS = __pycache__
......
......@@ -2,7 +2,7 @@ import sys
import os
# The datasource factory loader uses dlopen, as does python
# for its modules. Some dynamic linkers do not play nice if
# for its modules. Some dynamic linkers do not play nice if
# modules are not loaded with RTLD_GLOBAL, a symptom of which
# is that exceptions are not recognized by type. So to make
# sure this doesn't happen, we temporarily set RTLD_GLOBAL
......@@ -31,5 +31,4 @@ else:
sys.setdlopenflags(flags)
from isc.datasrc.sqlite3_ds import *
from isc.datasrc.master import *
......@@ -407,6 +407,16 @@ wrapDataSourceClient(DataSourceClient* client,
return (container.release());
}
DataSourceClient&
PyDataSourceClient_ToDataSourceClient(PyObject* client_obj) {
if (client_obj == NULL) {
isc_throw(PyCPPWrapperException,
"argument NULL in DataSourceClient PyObject conversion");
}
s_DataSourceClient* client = static_cast<s_DataSourceClient*>(client_obj);
return (*client->client);
}
} // namespace python
} // namespace datasrc
} // namespace isc
......@@ -44,6 +44,17 @@ wrapDataSourceClient(DataSourceClient* client,
LifeKeeper>& life_keeper = boost::shared_ptr<ClientList::
FindResult::LifeKeeper>());
/// \brief Returns a reference to the DataSourceClient object contained
/// in the given Python object.
///
/// \note The given object MUST be of type DataSourceClient; this can be
/// checked with the right call to ParseTuple("O!")
///
/// \param client_obj Python object holding the DataSourceClient
/// \return reference to the DataSourceClient object
DataSourceClient&
PyDataSourceClient_ToDataSourceClient(PyObject* client_obj);
} // namespace python
} // namespace datasrc
} // namespace isc
......
......@@ -29,6 +29,7 @@
#include "updater_python.h"
#include "journal_reader_python.h"
#include "configurableclientlist_python.h"
#include "zone_loader_python.h"
#include <util/python/pycppwrapper_util.h>
#include <dns/python/pydnspp_common.h>
......@@ -180,6 +181,23 @@ initModulePart_ZoneIterator(PyObject* mod) {
return (true);
}
bool
initModulePart_ZoneLoader(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(&zone_loader_type) < 0) {
return (false);
}
void* p = &zone_loader_type;
if (PyModule_AddObject(mod, "ZoneLoader", static_cast<PyObject*>(p)) < 0) {
return (false);
}
Py_INCREF(&zone_loader_type);
return (true);
}
bool
initModulePart_ZoneUpdater(PyObject* mod) {
// We initialize the static description object with PyType_Ready(),
......@@ -234,8 +252,9 @@ initModulePart_ZoneJournalReader(PyObject* mod) {
}
PyObject* po_DataSourceError;
PyObject* po_OutOfZone;
PyObject* po_MasterFileError;
PyObject* po_NotImplemented;
PyObject* po_OutOfZone;
PyModuleDef iscDataSrc = {
{ PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
......@@ -260,6 +279,26 @@ PyInit_datasrc(void) {
return (NULL);
}
try {
po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
NULL);
PyObjectContainer(po_DataSourceError).installToModule(mod, "Error");
po_MasterFileError = PyErr_NewException("isc.datasrc.MasterFileError",
po_DataSourceError, NULL);
PyObjectContainer(po_MasterFileError).
installToModule(mod, "MasterFileError");
po_OutOfZone = PyErr_NewException("isc.datasrc.OutOfZone", NULL, NULL);
PyObjectContainer(po_OutOfZone).installToModule(mod, "OutOfZone");
po_NotImplemented = PyErr_NewException("isc.datasrc.NotImplemented",
NULL, NULL);
PyObjectContainer(po_NotImplemented).installToModule(mod,
"NotImplemented");
} catch (...) {
Py_DECREF(mod);
return (NULL);
}
if (!initModulePart_DataSourceClient(mod)) {
Py_DECREF(mod);
return (NULL);
......@@ -290,17 +329,7 @@ PyInit_datasrc(void) {
return (NULL);
}
try {
po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
NULL);
PyObjectContainer(po_DataSourceError).installToModule(mod, "Error");
po_OutOfZone = PyErr_NewException("isc.datasrc.OutOfZone", NULL, NULL);
PyObjectContainer(po_OutOfZone).installToModule(mod, "OutOfZone");
po_NotImplemented = PyErr_NewException("isc.datasrc.NotImplemented",
NULL, NULL);
PyObjectContainer(po_NotImplemented).installToModule(mod,
"NotImplemented");
} catch (...) {
if (!initModulePart_ZoneLoader(mod)) {
Py_DECREF(mod);
return (NULL);
}
......
......@@ -182,7 +182,7 @@ def records(input):
if paren == 1 or not record:
continue
ret = ' '.join(record)
record = []
oldsize = size
......
PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
# old tests, TODO remove or change to use new API?
#PYTESTS = master_test.py
PYTESTS = datasrc_test.py sqlite3_ds_test.py clientlist_test.py
PYTESTS = datasrc_test.py sqlite3_ds_test.py
PYTESTS += clientlist_test.py zone_loader_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testdata/brokendb.sqlite3
EXTRA_DIST += testdata/example.com.sqlite3
EXTRA_DIST += testdata/example.com.source.sqlite3
EXTRA_DIST += testdata/newschema.sqlite3
EXTRA_DIST += testdata/oldschema.sqlite3
EXTRA_DIST += testdata/new_minor_schema.sqlite3
EXTRA_DIST += testdata/example.com
EXTRA_DIST += testdata/example.com.ch
CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied
CLEANFILES += $(abs_builddir)/zoneloadertest.sqlite3
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
......
example.com. 1000 IN SOA a.dns.example.com. mail.example.com. 1 1 1 1 1
example.com. 1000 IN NS a.dns.example.com.
example.com. 1000 IN NS b.dns.example.com.
example.com. 1000 IN NS c.dns.example.com.
a.dns.example.com. 1000 IN A 1.1.1.1
b.dns.example.com. 1000 IN A 3.3.3.3
b.dns.example.com. 1000 IN AAAA 4:4::4:4
b.dns.example.com. 1000 IN AAAA 5:5::5:5
example.com. 1000 CH SOA a.dns.example.com. mail.example.com. 1 1 1 1 1
example.com. 1000 CH NS a.dns.example.com.
example.com. 1000 CH NS b.dns.example.com.
example.com. 1000 CH NS c.dns.example.com.
a.dns.example.com. 1000 CH A 1.1.1.1
b.dns.example.com. 1000 CH A 3.3.3.3
b.dns.example.com. 1000 CH AAAA 4:4::4:4
b.dns.example.com. 1000 CH AAAA 5:5::5:5
# Copyright (C) 2012 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
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM 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.
import isc.datasrc
import isc.dns
import os
import unittest
import shutil
import sys
# Constants and common data used in tests
TESTDATA_PATH = os.environ['TESTDATA_PATH']
TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH']
ZONE_FILE = TESTDATA_PATH + '/example.com'
STATIC_ZONE_FILE = '../../../../datasrc/static.zone'
SOURCE_DB_FILE = TESTDATA_PATH + '/example.com.source.sqlite3'
ORIG_DB_FILE = TESTDATA_PATH + '/example.com.sqlite3'
DB_FILE = TESTDATA_WRITE_PATH + '/zoneloadertest.sqlite3'
DB_CLIENT_CONFIG = '{ "database_file": "' + DB_FILE + '" }'
DB_SOURCE_CLIENT_CONFIG = '{ "database_file": "' + SOURCE_DB_FILE + '" }'
ORIG_SOA_TXT = 'example.com. 3600 IN SOA master.example.com. ' +\
'admin.example.com. 1234 3600 1800 2419200 7200\n'
NEW_SOA_TXT = 'example.com. 1000 IN SOA a.dns.example.com. ' +\
'mail.example.com. 1 1 1 1 1\n'
class ZoneLoaderTests(unittest.TestCase):
def setUp(self):
self.test_name = isc.dns.Name("example.com")
self.test_file = ZONE_FILE
self.client = isc.datasrc.DataSourceClient("sqlite3", DB_CLIENT_CONFIG)
# Make a fresh copy of the database
shutil.copyfile(ORIG_DB_FILE, DB_FILE)
# Some tests set source client; if so, check refcount in
# tearDown, since most tests don't, set it to None by default.
self.source_client = None
self.loader = None
self.assertEqual(2, sys.getrefcount(self.test_name))
self.assertEqual(2, sys.getrefcount(self.client))
def tearDown(self):
# We can only create 1 loader at a time (it locks the db), and it
# may not be destroyed immediately if there is an exception in a
# test. So the tests that do create one should put it in self, and
# we make sure to invalidate it here.
# We can also use this to check reference counts; if a loader
# exists, the client and source client (if any) should have
# an increased reference count (but the name should not, this
# is only used in the initializer)
if self.loader is not None:
self.assertEqual(2, sys.getrefcount(self.test_name))
self.assertEqual(3, sys.getrefcount(self.client))
if self.source_client is not None:
self.assertEqual(3, sys.getrefcount(self.source_client))
self.loader = None
# Now that the loader has been destroyed, the refcounts
# of its arguments should be back to their originals
self.assertEqual(2, sys.getrefcount(self.test_name))
self.assertEqual(2, sys.getrefcount(self.client))
if self.source_client is not None:
self.assertEqual(2, sys.getrefcount(self.source_client))
def test_bad_constructor(self):
self.assertRaises(TypeError, isc.datasrc.ZoneLoader)
self.assertRaises(TypeError, isc.datasrc.ZoneLoader, 1)
self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
None, self.test_name, self.test_file)
self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
self.client, None, self.test_file)
self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
self.client, self.test_name, None)
self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
self.client, self.test_name, self.test_file, 1)
def check_zone_soa(self, soa_txt):
"""
Check that the given SOA RR exists and matches the expected string
"""
result, finder = self.client.find_zone(self.test_name)
self.assertEqual(self.client.SUCCESS, result)
result, rrset, _ = finder.find(self.test_name, isc.dns.RRType.SOA())
self.assertEqual(finder.SUCCESS, result)
self.assertEqual(soa_txt, rrset.to_text())
def check_load(self):
self.check_zone_soa(ORIG_SOA_TXT)
self.loader.load()
self.check_zone_soa(NEW_SOA_TXT)
# And after that, it should throw
self.assertRaises(isc.dns.InvalidOperation, self.loader.load)
def test_load_from_file(self):
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
self.test_file)
self.check_load()
def test_load_from_client(self):
self.source_client = isc.datasrc.DataSourceClient('sqlite3',
DB_SOURCE_CLIENT_CONFIG)
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
self.source_client)
self.check_load()
def check_load_incremental(self):
# New zone has 8 RRs
# After 5, it should return False
self.assertFalse(self.loader.load_incremental(5))
# New zone should not have been loaded yet
self.check_zone_soa(ORIG_SOA_TXT)
# After 5 more, it should return True (only having read 3)
self.assertTrue(self.loader.load_incremental(5))
# New zone should now be loaded
self.check_zone_soa(NEW_SOA_TXT)
# And after that, it should throw
self.assertRaises(isc.dns.InvalidOperation,
self.loader.load_incremental, 5)
def test_load_from_file_incremental(self):
# Create loader and load the zone
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
self.test_file)
self.check_load_incremental()
def test_load_from_client_incremental(self):
self.source_client = isc.datasrc.DataSourceClient('sqlite3',
DB_SOURCE_CLIENT_CONFIG)
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
self.source_client)
self.check_load_incremental()
def test_bad_file(self):
self.check_zone_soa(ORIG_SOA_TXT)
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
'no such file')
self.assertRaises(isc.datasrc.MasterFileError, self.loader.load)
self.check_zone_soa(ORIG_SOA_TXT)
def test_bad_file_incremental(self):
self.check_zone_soa(ORIG_SOA_TXT)
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
'no such file')
self.assertRaises(isc.datasrc.MasterFileError,
self.loader.load_incremental, 1)
self.check_zone_soa(ORIG_SOA_TXT)
def test_no_such_zone_in_target(self):
self.assertRaises(isc.datasrc.Error, isc.datasrc.ZoneLoader,
self.client, isc.dns.Name("unknownzone"),
self.test_file)
def test_no_such_zone_in_source(self):
# Reuse a zone that exists in target but not in source
zone_name = isc.dns.Name("sql1.example.com")
self.source_client = isc.datasrc.DataSourceClient('sqlite3',
DB_SOURCE_CLIENT_CONFIG)
# make sure the zone exists in the target
found, _ = self.client.find_zone(zone_name)
self.assertEqual(self.client.SUCCESS, found)
# And that it does not in the source
found, _ = self.source_client.find_zone(zone_name)
self.assertNotEqual(self.source_client.SUCCESS, found)
self.assertRaises(isc.datasrc.Error, isc.datasrc.ZoneLoader,
self.client, zone_name, self.source_client)
def test_no_ds_load_support(self):
# This may change in the future, but atm, the in-mem ds does
# not support the API the zone loader uses (it has direct load calls)
inmem_client = isc.datasrc.DataSourceClient('memory',
'{ "type": "memory" }');
self.assertRaises(isc.datasrc.NotImplemented,
isc.datasrc.ZoneLoader,
inmem_client, self.test_name, self.test_file)
def test_wrong_class_from_file(self):
# If the file has wrong class, it is not detected until load time
self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
self.test_file + '.ch')
self.assertRaises(isc.datasrc.MasterFileError, self.loader.load)
def test_wrong_class_from_client(self):
# For ds->ds loading, wrong class is detected upon construction
# Need a bit of the extended setup for CH source client
clientlist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.CH())
clientlist.configure('[ { "type": "static", "params": "' +
STATIC_ZONE_FILE +'" } ]', False)
self.source_client, _, _ = clientlist.find(isc.dns.Name("bind."),
False, False)
self.assertRaises(isc.dns.InvalidParameter, isc.datasrc.ZoneLoader,
self.client, isc.dns.Name("bind."),
self.source_client)
def test_exception(self):
# Just check if masterfileerror is subclass of datasrc.Error
self.assertTrue(issubclass(isc.datasrc.MasterFileError,
isc.datasrc.Error))
if __name__ == "__main__":
isc.log.init("bind10")
isc.log.resetUnitTestRootLogger()
unittest.main()
namespace {
const char* const ZoneLoader_doc = "\
Class to load data into a data source client.\n\
\n\
This is a small wrapper class that is able to load data into a data\n\
source. It can load either from another data source or from a master\n\
file. The purpose of the class is only to hold the state for\n\
incremental loading.\n\
\n\
The old content of zone is discarded and no journal is stored.\n\
\n\
ZoneLoader(destination, zone_name, master_file)\n\
\n\
Constructor from master file.\n\
\n\
This initializes the zone loader to load from a master file.\n\
\n\
Exceptions:\n\
DataSourceError in case the zone does not exist in destination.\n\
This class does not support creating brand new zones,\n\
only loading data into them. In case a new zone is\n\
needed, it must be created beforehand.\n\
DataSourceError in case of other (possibly low-level) errors,\n\
such as read-only data source or database error.\n\
\n\
Parameters:\n\
destination (isc.datasrc.DataSourceClient) The data source into\n\
which the loaded data should go.\n\
zone_name (isc.dns.Name) The origin of the zone. The class is\n\
implicit in the destination.\n\
master_file (string) Path to the master file to read data from.\n\
\n\
ZoneLoader(destination, zone_name, source)\n\
\n\
Constructor from another data source.\n\
\n\
Parameters:\n\
destination (isc.datasrc.DataSourceClient) The data source into\n\
which the loaded data should go.\n\
zone_name (isc.dns.Name) The origin of the zone. The class is\n\
implicit in the destination.\n\
source (isc.datasrc.DataSourceClient) The data source from\n\
which the data would be read.\n\
\n\
Exceptions:\n\
InvalidParameter in case the class of destination and source\n\
differs.\n\
NotImplemented in case the source data source client doesn't\n\
provide an iterator.\n\
DataSourceError in case the zone does not exist in destination.\n\
This class does not support creating brand new zones,\n\
only loading data into them. In case a new zone is\n\
needed, it must be created beforehand.\n\
DataSourceError in case the zone does not exist in the source.\n\
DataSourceError in case of other (possibly low-level) errors,\n\
such as read-only data source or database error.\n\
\n\
Parameters:\n\
destination The data source into which the loaded data should\n\
go.\n\
zone_name The origin of the zone.\n\
source The data source from which the data would be read.\n\
\n\
";
const char* const ZoneLoader_load_doc = "\
load() -> None\n\
\n\
Perform the whole load.\n\
\n\
This performs the whole loading operation. It may take a long time.\n\
\n\
Exceptions:\n\
InvalidOperation in case the loading was already completed before\n\
this call.\n\
DataSourceError in case some error (possibly low-level) happens.\n\
MasterFileError when the master_file is badly formatted or some\n\
similar problem is found when loading the master file.\n\
\n\
";
const char* const ZoneLoader_loadIncremental_doc = "\
load_incremental(limit) -> bool\n\
\n\
Load up to limit RRs.\n\
\n\
This performs a part of the loading. In case there's enough data in\n\
the source, it copies limit RRs. It can copy less RRs during the final\n\
call (when there's less than limit left).\n\
\n\
This can be called repeatedly until the whole zone is loaded, having\n\
pauses in the loading for some purposes (for example reporting\n\
progress).\n\
\n\
Exceptions:\n\
InvalidOperation in case the loading was already completed before\n\
this call (by load() or by a load_incremental that\n\
returned true).\n\
DataSourceError in case some error (possibly low-level) happens.\n\
MasterFileError when the master_file is badly formatted or some\n\
similar problem is found when loading the master file.\n\
\n\
Parameters:\n\
limit (integer) The maximum allowed number of RRs to be\n\
loaded during this call.\n\
\n\
Return Value(s): True in case the loading is completed, false if\n\
there's more to load.\n\
\n\
Note that if the limit is exactly the number of RRs available to be\n\
loaded, the method will still return False, and True will be returned\n\
on the next call (which will load 0 RRs). This is because the end of\n\
iterator or master file is detected when reading past the end, not\n\
when the last one is read.\n\
";
} // unnamed namespace
// Copyright (C) 2012 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/zone_loader.h>
#include <dns/python/name_python.h>
#include <dns/python/pydnspp_common.h>
#include <exceptions/exceptions.h>
#include "client_python.h"
#include "datasrc.h"
#include "zone_loader_inc.cc"
using namespace std;
using namespace isc::dns::python;
using namespace isc::datasrc;
using namespace isc::datasrc::python;
using namespace isc::util::python;
namespace {
// The s_* Class simply covers one instantiation of the object
class s_ZoneLoader : public PyObject {
public:
s_ZoneLoader() : cppobj(NULL), target_client(NULL), source_client(NULL)
{};
ZoneLoader* cppobj;
// a zoneloader should not survive its associated client(s),
// so add a ref to it at init
PyObject* target_client;
PyObject* source_client;
};
// General creation and destruction
int
ZoneLoader_init(PyObject* po_self, PyObject* args, PyObject*) {
s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self