BIND 10 master, updated. a489ad14ad44a593195b2f50f35c4825258a176f [master] Merge branch 'trac2379_2'
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Dec 18 18:02:09 UTC 2012
The branch, master has been updated
via a489ad14ad44a593195b2f50f35c4825258a176f (commit)
via ee7c565a4d00c5ccac5f22858b430aff836550f2 (commit)
via a602d28cbe49a1287a4beb85ace69c854d025b02 (commit)
via 5fa05448d68be1c4aafc3806ab0fc3302cd32597 (commit)
via 00fd5f949667517a3d02c70b36476f430c0721c9 (commit)
via ca8707b6528b0779ce9cc6de42169d53af561b8a (commit)
via 5a34019b3d874b4e8d1209d47d38592281b971ca (commit)
via 45596b6bd556fd78555930c4e883a5025912ec4d (commit)
via 3fb09910a4c862fef0e8f3dc631df0fba49c8107 (commit)
via 94d0481e3dbbc16f3178bcdb71e4b02af6ef85bb (commit)
via 3d8e49eb1d45b2a70b5ecb90d77116653a26b11f (commit)
via d7368c1d83dc9c6cbbfd4230efa03858307062d4 (commit)
via c9a786613ef05879fd0bcf88e6db0adf855e6ea1 (commit)
via a8b060aa956d679e7a4330450c9baf7e470ad6a5 (commit)
via bb85c71585b1bafb659287dba976940d37178630 (commit)
via 52b93e86bad0bc578336b9fc15e2a76c79dbecb9 (commit)
via 6bd0c1d9f2f14339f83ce9fb1882f42ae23036a1 (commit)
via 65ce062ac130500177a0adc4100289b8879c601c (commit)
via 153bda11f4773ba8123c66d4a2d498a18a7b686a (commit)
via 634b1cb2a47760b880e3fb6aca5a30cfd0602871 (commit)
via 98d529881bbb26780812a812cf0d1916a7195406 (commit)
via 75effbfc1dd4d69ae829974dcbf48c9d9c13580f (commit)
via baa520eb355a55fa9c9459d2cd4cd705533345f9 (commit)
via 2a98f3d106983b1e917af84747c089c5d8708df5 (commit)
from 3449632270ba83f6d2114028e82f6f4c5098c2dc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a489ad14ad44a593195b2f50f35c4825258a176f
Merge: 3449632 ee7c565
Author: Jelte Jansen <jelte at isc.org>
Date: Tue Dec 18 18:18:37 2012 +0100
[master] Merge branch 'trac2379_2'
-----------------------------------------------------------------------
Summary of changes:
src/lib/dns/python/pydnspp.cc | 385 ++++++++++----------
src/lib/dns/python/pydnspp_common.cc | 17 +
src/lib/dns/python/pydnspp_common.h | 13 +
src/lib/python/isc/datasrc/Makefile.am | 2 +
src/lib/python/isc/datasrc/__init__.py | 3 +-
src/lib/python/isc/datasrc/client_python.cc | 10 +
src/lib/python/isc/datasrc/client_python.h | 11 +
src/lib/python/isc/datasrc/datasrc.cc | 53 ++-
src/lib/python/isc/datasrc/master.py | 2 +-
src/lib/python/isc/datasrc/tests/Makefile.am | 7 +-
.../{notify => datasrc}/tests/testdata/example.com | 2 -
.../isc/datasrc/tests/testdata/example.com.ch | 8 +
...mple.com.sqlite3 => example.com.source.sqlite3} | Bin 70656 -> 70656 bytes
.../isc/datasrc/tests/testdata/example.com.sqlite3 | Bin 70656 -> 70656 bytes
.../python/isc/datasrc/tests/zone_loader_test.py | 223 ++++++++++++
src/lib/python/isc/datasrc/zone_loader_inc.cc | 116 ++++++
src/lib/python/isc/datasrc/zone_loader_python.cc | 262 +++++++++++++
.../zone_loader_python.h} | 17 +-
18 files changed, 912 insertions(+), 219 deletions(-)
copy src/lib/python/isc/{notify => datasrc}/tests/testdata/example.com (80%)
create mode 100644 src/lib/python/isc/datasrc/tests/testdata/example.com.ch
copy src/lib/python/isc/datasrc/tests/testdata/{example.com.sqlite3 => example.com.source.sqlite3} (51%)
create mode 100644 src/lib/python/isc/datasrc/tests/zone_loader_test.py
create mode 100644 src/lib/python/isc/datasrc/zone_loader_inc.cc
create mode 100644 src/lib/python/isc/datasrc/zone_loader_python.cc
copy src/lib/python/isc/{util/cio/socketsession_python.h => datasrc/zone_loader_python.h} (76%)
-----------------------------------------------------------------------
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 23ed463..6d1bd89 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -65,21 +65,13 @@ namespace {
bool
initModulePart_EDNS(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)
- //
// After the type has been initialized, we initialize any exceptions
// that are defined in the wrapper for this class, and add constants
// to the type, if any
- if (PyType_Ready(&edns_type) < 0) {
+ if (!initClass(edns_type, "EDNS", mod)) {
return (false);
}
- Py_INCREF(&edns_type);
- void* p = &edns_type;
- PyModule_AddObject(mod, "EDNS", static_cast<PyObject*>(p));
-
addClassVariable(edns_type, "SUPPORTED_VERSION",
Py_BuildValue("B", EDNS::SUPPORTED_VERSION));
@@ -88,14 +80,9 @@ initModulePart_EDNS(PyObject* mod) {
bool
initModulePart_Message(PyObject* mod) {
- if (PyType_Ready(&message_type) < 0) {
- return (false);
- }
- void* p = &message_type;
- if (PyModule_AddObject(mod, "Message", static_cast<PyObject*>(p)) < 0) {
+ if (!initClass(message_type, "Message", mod)) {
return (false);
}
- Py_INCREF(&message_type);
try {
//
@@ -186,32 +173,25 @@ initModulePart_Message(PyObject* mod) {
bool
initModulePart_MessageRenderer(PyObject* mod) {
- if (PyType_Ready(&messagerenderer_type) < 0) {
+ if (!initClass(messagerenderer_type, "MessageRenderer", mod)) {
return (false);
}
- Py_INCREF(&messagerenderer_type);
addClassVariable(messagerenderer_type, "CASE_INSENSITIVE",
Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE));
addClassVariable(messagerenderer_type, "CASE_SENSITIVE",
Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE));
- PyModule_AddObject(mod, "MessageRenderer",
- reinterpret_cast<PyObject*>(&messagerenderer_type));
return (true);
}
bool
-initModulePart_Name(PyObject* mod) {
-
- //
- // NameComparisonResult
- //
- if (PyType_Ready(&name_comparison_result_type) < 0) {
+initModulePart_NameComparisonResult(PyObject* mod) {
+ if (!initClass(name_comparison_result_type,
+ "NameComparisonResult", mod)) {
return (false);
}
- Py_INCREF(&name_comparison_result_type);
// Add the enums to the module
po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}",
@@ -231,17 +211,14 @@ initModulePart_Name(PyObject* mod) {
addClassVariable(name_comparison_result_type, "COMMONANCESTOR",
Py_BuildValue("I", NameComparisonResult::COMMONANCESTOR));
- PyModule_AddObject(mod, "NameComparisonResult",
- reinterpret_cast<PyObject*>(&name_comparison_result_type));
-
- //
- // Name
- //
+ return (true);
+}
- if (PyType_Ready(&name_type) < 0) {
+bool
+initModulePart_Name(PyObject* mod) {
+ if (!initClass(name_type, "Name", mod)) {
return (false);
}
- Py_INCREF(&name_type);
// Add the constants to the module
addClassVariable(name_type, "MAX_WIRE",
@@ -260,51 +237,56 @@ initModulePart_Name(PyObject* mod) {
addClassVariable(name_type, "ROOT_NAME",
createNameObject(Name::ROOT_NAME()));
- PyModule_AddObject(mod, "Name",
- reinterpret_cast<PyObject*>(&name_type));
-
-
// Add the exceptions to the module
- po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
- PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel);
+ try {
+ po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
+ PyObjectContainer(po_EmptyLabel).installToModule(mod, "EmptyLabel");
- po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
- PyModule_AddObject(mod, "TooLongName", po_TooLongName);
+ po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
+ PyObjectContainer(po_TooLongName).installToModule(mod, "TooLongName");
- po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
- PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel);
+ po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
+ PyObjectContainer(po_TooLongLabel).installToModule(mod, "TooLongLabel");
- po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
- PyModule_AddObject(mod, "BadLabelType", po_BadLabelType);
+ po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
+ PyObjectContainer(po_BadLabelType).installToModule(mod, "BadLabelType");
- po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
- PyModule_AddObject(mod, "BadEscape", po_BadEscape);
+ po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
+ PyObjectContainer(po_BadEscape).installToModule(mod, "BadEscape");
- po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL);
- PyModule_AddObject(mod, "IncompleteName", po_IncompleteName);
+ po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL,
+ NULL);
+ PyObjectContainer(po_IncompleteName).installToModule(mod, "IncompleteName");
- po_InvalidBufferPosition =
- PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
- PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition);
+ po_InvalidBufferPosition =
+ PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
+ PyObjectContainer(po_InvalidBufferPosition).installToModule(
+ mod, "InvalidBufferPosition");
- // This one could have gone into the message_python.cc file, but is
- // already needed here.
- po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
- NULL, NULL);
- PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR);
+ // This one could have gone into the message_python.cc file, but is
+ // already needed here.
+ po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
+ NULL, NULL);
+ PyObjectContainer(po_DNSMessageFORMERR).installToModule(
+ mod, "DNSMessageFORMERR");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in Name initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in Name initialization");
+ return (false);
+ }
return (true);
}
bool
initModulePart_Opcode(PyObject* mod) {
- if (PyType_Ready(&opcode_type) < 0) {
- return (false);
- }
- Py_INCREF(&opcode_type);
- void* p = &opcode_type;
- if (PyModule_AddObject(mod, "Opcode", static_cast<PyObject*>(p)) != 0) {
- Py_DECREF(&opcode_type);
+ if (!initClass(opcode_type, "Opcode", mod)) {
return (false);
}
@@ -346,25 +328,12 @@ initModulePart_Opcode(PyObject* mod) {
bool
initModulePart_Question(PyObject* mod) {
- if (PyType_Ready(&question_type) < 0) {
- return (false);
- }
- Py_INCREF(&question_type);
- PyModule_AddObject(mod, "Question",
- reinterpret_cast<PyObject*>(&question_type));
-
- return (true);
+ return (initClass(question_type, "Question", mod));
}
bool
initModulePart_Rcode(PyObject* mod) {
- if (PyType_Ready(&rcode_type) < 0) {
- return (false);
- }
- Py_INCREF(&rcode_type);
- void* p = &rcode_type;
- if (PyModule_AddObject(mod, "Rcode", static_cast<PyObject*>(p)) != 0) {
- Py_DECREF(&rcode_type);
+ if (!initClass(rcode_type, "Rcode", mod)) {
return (false);
}
@@ -408,126 +377,168 @@ initModulePart_Rcode(PyObject* mod) {
bool
initModulePart_Rdata(PyObject* mod) {
- if (PyType_Ready(&rdata_type) < 0) {
+ if (!initClass(rdata_type, "Rdata", mod)) {
return (false);
}
- Py_INCREF(&rdata_type);
- PyModule_AddObject(mod, "Rdata",
- reinterpret_cast<PyObject*>(&rdata_type));
// Add the exceptions to the class
- po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength",
- NULL, NULL);
- PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength);
-
- po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText",
- NULL, NULL);
- PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText);
-
- po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong",
- NULL, NULL);
- PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong);
-
+ try {
+ po_InvalidRdataLength =
+ PyErr_NewException("pydnspp.InvalidRdataLength", NULL, NULL);
+ PyObjectContainer(po_InvalidRdataLength).installToModule(
+ mod, "InvalidRdataLength");
+
+ po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidRdataText).installToModule(
+ mod, "InvalidRdataText");
+
+ po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong",
+ NULL, NULL);
+ PyObjectContainer(po_CharStringTooLong).installToModule(
+ mod, "CharStringTooLong");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in Rdata initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in Rdata initialization");
+ return (false);
+ }
return (true);
}
bool
initModulePart_RRClass(PyObject* mod) {
- po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass",
- NULL, NULL);
- Py_INCREF(po_InvalidRRClass);
- PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
- po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass",
- NULL, NULL);
- Py_INCREF(po_IncompleteRRClass);
- PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);
+ if (!initClass(rrclass_type, "RRClass", mod)) {
+ return (false);
+ }
- if (PyType_Ready(&rrclass_type) < 0) {
+ try {
+ po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidRRClass).installToModule(
+ mod, "InvalidRRClass");
+
+ po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass",
+ NULL, NULL);
+ PyObjectContainer(po_IncompleteRRClass).installToModule(
+ mod, "IncompleteRRClass");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in RRClass initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in RRClass initialization");
return (false);
}
- Py_INCREF(&rrclass_type);
- PyModule_AddObject(mod, "RRClass",
- reinterpret_cast<PyObject*>(&rrclass_type));
return (true);
}
bool
initModulePart_RRset(PyObject* mod) {
- po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
- PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
+ if (!initClass(rrset_type, "RRset", mod)) {
+ return (false);
+ }
- // NameComparisonResult
- if (PyType_Ready(&rrset_type) < 0) {
+ try {
+ po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
+ PyObjectContainer(po_EmptyRRset).installToModule(mod, "EmptyRRset");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in RRset initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in RRset initialization");
return (false);
}
- Py_INCREF(&rrset_type);
- PyModule_AddObject(mod, "RRset",
- reinterpret_cast<PyObject*>(&rrset_type));
return (true);
}
bool
initModulePart_RRTTL(PyObject* mod) {
- po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL);
- PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL);
- po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL",
- NULL, NULL);
- PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL);
+ if (!initClass(rrttl_type, "RRTTL", mod)) {
+ return (false);
+ }
+
+ try {
+ po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidRRTTL).installToModule(mod,
+ "InvalidRRTTL");
- if (PyType_Ready(&rrttl_type) < 0) {
+ po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL",
+ NULL, NULL);
+ PyObjectContainer(po_IncompleteRRTTL).installToModule(
+ mod, "IncompleteRRTTL");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in RRTTL initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in RRTTL initialization");
return (false);
}
- Py_INCREF(&rrttl_type);
- PyModule_AddObject(mod, "RRTTL",
- reinterpret_cast<PyObject*>(&rrttl_type));
return (true);
}
bool
initModulePart_RRType(PyObject* mod) {
- // Add the exceptions to the module
- po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL);
- PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType);
- po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType",
- NULL, NULL);
- PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType);
+ if (!initClass(rrtype_type, "RRType", mod)) {
+ return (false);
+ }
+
+ try {
+ po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidRRType).installToModule(mod,
+ "InvalidRRType");
- if (PyType_Ready(&rrtype_type) < 0) {
+ po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType",
+ NULL, NULL);
+ PyObjectContainer(po_IncompleteRRType).installToModule(
+ mod, "IncompleteRRType");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in RRType initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in RRType initialization");
return (false);
}
- Py_INCREF(&rrtype_type);
- PyModule_AddObject(mod, "RRType",
- reinterpret_cast<PyObject*>(&rrtype_type));
return (true);
}
bool
initModulePart_Serial(PyObject* mod) {
- if (PyType_Ready(&serial_type) < 0) {
- return (false);
- }
- Py_INCREF(&serial_type);
- PyModule_AddObject(mod, "Serial",
- reinterpret_cast<PyObject*>(&serial_type));
-
- return (true);
+ return (initClass(serial_type, "Serial", mod));
}
bool
initModulePart_TSIGError(PyObject* mod) {
- if (PyType_Ready(&tsigerror_type) < 0) {
- return (false);
- }
- void* p = &tsigerror_type;
- if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
+ if (!initClass(tsigerror_type, "TSIGError", mod)) {
return (false);
}
- Py_INCREF(&tsigerror_type);
try {
// Constant class variables
@@ -595,14 +606,9 @@ initModulePart_TSIGError(PyObject* mod) {
bool
initModulePart_TSIGKey(PyObject* mod) {
- if (PyType_Ready(&tsigkey_type) < 0) {
- return (false);
- }
- void* p = &tsigkey_type;
- if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
+ if (!initClass(tsigkey_type, "TSIGKey", mod)) {
return (false);
}
- Py_INCREF(&tsigkey_type);
try {
// Constant class variables
@@ -635,14 +641,7 @@ initModulePart_TSIGKey(PyObject* mod) {
bool
initModulePart_TSIGKeyRing(PyObject* mod) {
- if (PyType_Ready(&tsigkeyring_type) < 0) {
- return (false);
- }
- Py_INCREF(&tsigkeyring_type);
- void* p = &tsigkeyring_type;
- if (PyModule_AddObject(mod, "TSIGKeyRing",
- static_cast<PyObject*>(p)) != 0) {
- Py_DECREF(&tsigkeyring_type);
+ if (!initClass(tsigkeyring_type, "TSIGKeyRing", mod)) {
return (false);
}
@@ -658,15 +657,9 @@ initModulePart_TSIGKeyRing(PyObject* mod) {
bool
initModulePart_TSIGContext(PyObject* mod) {
- if (PyType_Ready(&tsigcontext_type) < 0) {
- return (false);
- }
- void* p = &tsigcontext_type;
- if (PyModule_AddObject(mod, "TSIGContext",
- static_cast<PyObject*>(p)) < 0) {
+ if (!initClass(tsigcontext_type, "TSIGContext", mod)) {
return (false);
}
- Py_INCREF(&tsigcontext_type);
try {
// Class specific exceptions
@@ -707,28 +700,14 @@ initModulePart_TSIGContext(PyObject* mod) {
bool
initModulePart_TSIG(PyObject* mod) {
- if (PyType_Ready(&tsig_type) < 0) {
- return (false);
- }
- void* p = &tsig_type;
- if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
- return (false);
- }
- Py_INCREF(&tsig_type);
-
- return (true);
+ return (initClass(tsig_type, "TSIG", mod));
}
bool
initModulePart_TSIGRecord(PyObject* mod) {
- if (PyType_Ready(&tsigrecord_type) < 0) {
- return (false);
- }
- void* p = &tsigrecord_type;
- if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+ if (!initClass(tsigrecord_type, "TSIGRecord", mod)) {
return (false);
}
- Py_INCREF(&tsigrecord_type);
try {
// Constant class variables
@@ -773,16 +752,38 @@ PyInit_pydnspp(void) {
return (NULL);
}
- // Add the exceptions to the class
- po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
- PyModule_AddObject(mod, "IscException", po_IscException);
-
- po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
- NULL, NULL);
- PyModule_AddObject(mod, "InvalidParameter", po_InvalidParameter);
+ try {
+ // Add the exceptions to the class
+ po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
+ PyObjectContainer(po_IscException).installToModule(mod, "IscException");
+
+ po_InvalidOperation = PyErr_NewException("pydnspp.InvalidOperation",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidOperation).installToModule(
+ mod, "InvalidOperation");
+
+ po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
+ NULL, NULL);
+ PyObjectContainer(po_InvalidParameter).installToModule(
+ mod, "InvalidParameter");
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Unexpected failure in pydnspp initialization: " +
+ std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in pydnspp initialization");
+ return (NULL);
+ }
// for each part included above, we call its specific initializer
+ if (!initModulePart_NameComparisonResult(mod)) {
+ return (NULL);
+ }
+
if (!initModulePart_Name(mod)) {
return (NULL);
}
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 8e623c5..e9d62e0 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -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);
+}
+
}
}
}
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 5ca1cd8..4095f54 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -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;
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index 84da788..f177f00 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -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__
diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py
index 7ebd918..6e0f5ae 100644
--- a/src/lib/python/isc/datasrc/__init__.py
+++ b/src/lib/python/isc/datasrc/__init__.py
@@ -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 *
diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc
index d0206d1..f360445 100644
--- a/src/lib/python/isc/datasrc/client_python.cc
+++ b/src/lib/python/isc/datasrc/client_python.cc
@@ -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
diff --git a/src/lib/python/isc/datasrc/client_python.h b/src/lib/python/isc/datasrc/client_python.h
index 71aee8b..e700fde 100644
--- a/src/lib/python/isc/datasrc/client_python.h
+++ b/src/lib/python/isc/datasrc/client_python.h
@@ -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
diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc
index 533a08c..37c9baa 100644
--- a/src/lib/python/isc/datasrc/datasrc.cc
+++ b/src/lib/python/isc/datasrc/datasrc.cc
@@ -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>
@@ -181,6 +182,23 @@ initModulePart_ZoneIterator(PyObject* mod) {
}
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(),
// then add it to the module. This is not just a check! (leaving
@@ -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);
}
diff --git a/src/lib/python/isc/datasrc/master.py b/src/lib/python/isc/datasrc/master.py
index 2c22190..d41f872 100644
--- a/src/lib/python/isc/datasrc/master.py
+++ b/src/lib/python/isc/datasrc/master.py
@@ -182,7 +182,7 @@ def records(input):
if paren == 1 or not record:
continue
-
+
ret = ' '.join(record)
record = []
oldsize = size
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index 34af092..3eb74fd 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -1,15 +1,20 @@
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.
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com b/src/lib/python/isc/datasrc/tests/testdata/example.com
new file mode 100644
index 0000000..24e22e1
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/testdata/example.com
@@ -0,0 +1,8 @@
+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
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.ch b/src/lib/python/isc/datasrc/tests/testdata/example.com.ch
new file mode 100644
index 0000000..95c0e9a
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/testdata/example.com.ch
@@ -0,0 +1,8 @@
+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
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3
new file mode 100644
index 0000000..d4de6da
Binary files /dev/null and b/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3
index 9c71cb5..8ac72f4 100644
Binary files a/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 and b/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/zone_loader_test.py b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
new file mode 100644
index 0000000..cb239ed
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
@@ -0,0 +1,223 @@
+# 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()
diff --git a/src/lib/python/isc/datasrc/zone_loader_inc.cc b/src/lib/python/isc/datasrc/zone_loader_inc.cc
new file mode 100644
index 0000000..405ad1a
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_inc.cc
@@ -0,0 +1,116 @@
+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
diff --git a/src/lib/python/isc/datasrc/zone_loader_python.cc b/src/lib/python/isc/datasrc/zone_loader_python.cc
new file mode 100644
index 0000000..98264b3
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_python.cc
@@ -0,0 +1,262 @@
+// 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);
+ PyObject *po_target_client = NULL;
+ PyObject *po_source_client = NULL;
+ PyObject *po_name = NULL;
+ char* master_file;
+ if (!PyArg_ParseTuple(args, "O!O!s", &datasourceclient_type,
+ &po_target_client, &name_type, &po_name,
+ &master_file) &&
+ !PyArg_ParseTuple(args, "O!O!O!", &datasourceclient_type,
+ &po_target_client, &name_type, &po_name,
+ &datasourceclient_type, &po_source_client)
+ ) {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to ZoneLoader constructor, "
+ "expects isc.datasrc.DataSourceClient, isc.dns.Name, "
+ "and either a string or another DataSourceClient");
+ return (-1);
+ }
+ PyErr_Clear();
+ try {
+ // The associated objects must be alive during the lifetime
+ // of this instance, so incref them (through a container in case
+ // of exceptions in this method)
+ Py_INCREF(po_target_client);
+ PyObjectContainer target_client(po_target_client);
+ if (po_source_client != NULL) {
+ // See above
+ Py_INCREF(po_source_client);
+ PyObjectContainer source_client(po_source_client);
+ self->cppobj = new ZoneLoader(
+ PyDataSourceClient_ToDataSourceClient(po_target_client),
+ PyName_ToName(po_name),
+ PyDataSourceClient_ToDataSourceClient(po_source_client));
+ self->source_client = source_client.release();
+ } else {
+ self->cppobj = new ZoneLoader(
+ PyDataSourceClient_ToDataSourceClient(po_target_client),
+ PyName_ToName(po_name),
+ master_file);
+ }
+ self->target_client = target_client.release();
+ return (0);
+ } catch (const isc::InvalidParameter& ivp) {
+ PyErr_SetString(po_InvalidParameter, ivp.what());
+ } catch (const isc::datasrc::DataSourceError& dse) {
+ PyErr_SetString(getDataSourceException("Error"), dse.what());
+ } catch (const isc::NotImplemented& ni) {
+ PyErr_SetString(getDataSourceException("NotImplemented"), ni.what());
+ } catch (const std::exception& stde) {
+ PyErr_SetString(getDataSourceException("Error"), stde.what());
+ } catch (...) {
+ PyErr_SetString(getDataSourceException("Error"),
+ "Unexpected exception");
+ }
+ return (-1);
+}
+
+void
+ZoneLoader_destroy(PyObject* po_self) {
+ s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+ delete self->cppobj;
+ self->cppobj = NULL;
+ if (self->target_client != NULL) {
+ Py_DECREF(self->target_client);
+ }
+ if (self->source_client != NULL) {
+ Py_DECREF(self->source_client);
+ }
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+ZoneLoader_load(PyObject* po_self, PyObject*) {
+ s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+ try {
+ self->cppobj->load();
+ Py_RETURN_NONE;
+ } catch (const isc::InvalidOperation& ivo) {
+ PyErr_SetString(po_InvalidOperation, ivo.what());
+ return (NULL);
+ } catch (const isc::datasrc::MasterFileError& mfe) {
+ PyErr_SetString(getDataSourceException("MasterFileError"), mfe.what());
+ return (NULL);
+ } catch (const isc::datasrc::DataSourceError& dse) {
+ PyErr_SetString(getDataSourceException("Error"), dse.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*
+ZoneLoader_loadIncremental(PyObject* po_self, PyObject* args) {
+ s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+
+ int limit;
+ if (!PyArg_ParseTuple(args, "i", &limit)) {
+ return (NULL);
+ }
+ if (limit < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "load_incremental argument must be positive");
+ return (NULL);
+ }
+ try {
+ if (self->cppobj->loadIncremental(limit)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ } catch (const isc::InvalidOperation& ivo) {
+ PyErr_SetString(po_InvalidOperation, ivo.what());
+ return (NULL);
+ } catch (const isc::datasrc::MasterFileError& mfe) {
+ PyErr_SetString(getDataSourceException("MasterFileError"), mfe.what());
+ return (NULL);
+ } catch (const isc::datasrc::DataSourceError& dse) {
+ PyErr_SetString(getDataSourceException("Error"), dse.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);
+ }
+}
+
+// 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 ZoneLoader_methods[] = {
+ { "load", ZoneLoader_load, METH_NOARGS, ZoneLoader_load_doc },
+ { "load_incremental", ZoneLoader_loadIncremental, METH_VARARGS,
+ ZoneLoader_loadIncremental_doc },
+ { NULL, NULL, 0, NULL }
+};
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+PyTypeObject zone_loader_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "datasrc.ZoneLoader",
+ sizeof(s_ZoneLoader), // tp_basicsize
+ 0, // tp_itemsize
+ ZoneLoader_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
+ ZoneLoader_doc,
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ ZoneLoader_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
+ ZoneLoader_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
+};
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
diff --git a/src/lib/python/isc/datasrc/zone_loader_python.h b/src/lib/python/isc/datasrc/zone_loader_python.h
new file mode 100644
index 0000000..f148efa
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_python.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef PYTHON_DATASRC_ZONE_LOADER_H
+#define PYTHON_DATASRC_ZONE_LOADER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+
+namespace python {
+
+extern PyTypeObject zone_loader_type;
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // PYTHON_DATASRC_ZONE_LOADER_H
+
+// Local Variables:
+// mode: c++
+// End:
More information about the bind10-changes
mailing list