Browse Source

[1179] pydoc with script from #933

Jelte Jansen 13 years ago
parent
commit
54c6127e00

+ 176 - 0
src/lib/python/isc/datasrc/client_inc.cc

@@ -0,0 +1,176 @@
+namespace {
+
+const char* const DataSourceClient_doc = "\
+The base class of data source clients.\n\
+\n\
+This is an abstract base class that defines the common interface for\n\
+various types of data source clients. A data source client is a top\n\
+level access point to a data source, allowing various operations on\n\
+the data source such as lookups, traversing or updates. The client\n\
+class itself has limited focus and delegates the responsibility for\n\
+these specific operations to other classes; in general methods of this\n\
+class act as factories of these other classes.\n\
+\n\
+- InMemoryClient: A client of a conceptual data source that stores all\n\
+  necessary data in memory for faster lookups\n\
+- DatabaseClient: A client that uses a real database backend (such as\n\
+  an SQL database). It would internally hold a connection to the\n\
+  underlying database system.\n\
+\n\
+It is intentional that while the term these derived classes don't\n\
+contain \"DataSource\" unlike their base class. It's also noteworthy\n\
+that the naming of the base class is somewhat redundant because the\n\
+namespace datasrc would indicate that it's related to a data source.\n\
+The redundant naming comes from the observation that namespaces are\n\
+often omitted with using directives, in which case \"Client\" would be\n\
+too generic. On the other hand, concrete derived classes are generally\n\
+not expected to be referenced directly from other modules and\n\
+applications, so we'll give them more concise names such as\n\
+InMemoryClient. A single DataSourceClient object is expected to handle\n\
+only a single RR class even if the underlying data source contains\n\
+records for multiple RR classes. Likewise, (when we support views) a\n\
+DataSourceClient object is expected to handle only a single view.\n\
+\n\
+If the application uses multiple threads, each thread will need to\n\
+create and use a separate DataSourceClient. This is because some\n\
+database backend doesn't allow multiple threads to share the same\n\
+connection to the database.\n\
+\n\
+For a client using an in memory backend, this may result in having a\n\
+multiple copies of the same data in memory, increasing the memory\n\
+footprint substantially. Depending on how to support multiple CPU\n\
+cores for concurrent lookups on the same single data source (which is\n\
+not fully fixed yet, and for which multiple threads may be used), this\n\
+design may have to be revisited. This class (and therefore its derived\n\
+classes) are not copyable. This is because the derived classes would\n\
+generally contain attributes that are not easy to copy (such as a\n\
+large size of in memory data or a network connection to a database\n\
+server). In order to avoid a surprising disruption with a naive copy\n\
+it's prohibited explicitly. For the expected usage of the client\n\
+classes the restriction should be acceptable.\n\
+\n\
+TodoThis class is still not complete. It will need more factory\n\
+methods, e.g. for (re)loading a zone.\n\
+\n\
+DataSourceClient()\n\
+\n\
+    Default constructor.\n\
+\n\
+    This is intentionally defined as protected as this base class\n\
+    should never be instantiated directly.\n\
+\n\
+    The constructor of a concrete derived class may throw an\n\
+    exception. This interface does not specify which exceptions can\n\
+    happen (at least at this moment), and the caller should expect any\n\
+    type of exception and react accordingly.\n\
+\n\
+";
+
+const char* const DataSourceClient_findZone_doc = "\
+find_zone(name) -> FindResult\n\
+\n\
+Returns a ZoneFinder for a zone that best matches the given name.\n\
+\n\
+- code: The result code of the operation.result.SUCCESS: A zone that\n\
+  gives an exact match is foundresult.PARTIALMATCH: A zone whose\n\
+  origin is a super domain of name is found (but there is no exact\n\
+  match)result.NOTFOUND: For all other cases.\n\
+- result.SUCCESS: A zone that gives an exact match is found\n\
+- result.PARTIALMATCH: A zone whose origin is a super domain of name\n\
+  is found (but there is no exact match)\n\
+- result.NOTFOUND: For all other cases.\n\
+- zone_finder: Pointer to a ZoneFinder object for the found zone if\n\
+  one is found; otherwise NULL.\n\
+\n\
+A specific derived version of this method may throw an exception. This\n\
+interface does not specify which exceptions can happen (at least at\n\
+this moment), and the caller should expect any type of exception and\n\
+react accordingly.\n\
+\n\
+Parameters:\n\
+  name       A domain name for which the search is performed.\n\
+\n\
+Return Value(s): A FindResult object enclosing the search result (see\n\
+above).\n\
+";
+
+const char* const DataSourceClient_getIterator_doc = "\
+get_iterator(name) -> ZoneIterator\n\
+\n\
+Returns an iterator to the given zone.\n\
+\n\
+This allows for traversing the whole zone. The returned object can\n\
+provide the RRsets one by one.\n\
+\n\
+This throws DataSourceError when the zone does not exist in the\n\
+datasource.\n\
+\n\
+The default implementation throws isc.NotImplemented. This allows for\n\
+easy and fast deployment of minimal custom data sources, where the\n\
+user/implementator doesn't have to care about anything else but the\n\
+actual queries. Also, in some cases, it isn't possible to traverse the\n\
+zone from logic point of view (eg. dynamically generated zone data).\n\
+\n\
+It is not fixed if a concrete implementation of this method can throw\n\
+anything else.\n\
+\n\
+Parameters:\n\
+  name       The name of zone apex to be traversed. It doesn't do\n\
+             nearest match as find_zone.\n\
+\n\
+Return Value(s): Pointer to the iterator.\n\
+";
+
+const char* const DataSourceClient_getUpdater_doc = "\
+get_updater(name, replace) -> ZoneUpdater\n\
+\n\
+Return an updater to make updates to a specific zone.\n\
+\n\
+The RR class of the zone is the one that the client is expected to\n\
+handle (see the detailed description of this class).\n\
+\n\
+If the specified zone is not found via the client, a NULL pointer will\n\
+be returned; in other words a completely new zone cannot be created\n\
+using an updater. It must be created beforehand (even if it's an empty\n\
+placeholder) in a way specific to the underlying data source.\n\
+\n\
+Conceptually, the updater will trigger a separate transaction for\n\
+subsequent updates to the zone within the context of the updater (the\n\
+actual implementation of the \"transaction\" may vary for the specific\n\
+underlying data source). Until commit() is performed on the updater,\n\
+the intermediate updates won't affect the results of other methods\n\
+(and the result of the object's methods created by other factory\n\
+methods). Likewise, if the updater is destructed without performing\n\
+commit(), the intermediate updates will be effectively canceled and\n\
+will never affect other methods.\n\
+\n\
+If the underlying data source allows concurrent updates, this method\n\
+can be called multiple times while the previously returned updater(s)\n\
+are still active. In this case each updater triggers a different\n\
+\"transaction\". Normally it would be for different zones for such a\n\
+case as handling multiple incoming AXFR streams concurrently, but this\n\
+interface does not even prohibit an attempt of getting more than one\n\
+updater for the same zone, as long as the underlying data source\n\
+allows such an operation (and any conflict resolution is left to the\n\
+specific derived class implementation).\n\
+\n\
+If replace is true, any existing RRs of the zone will be deleted on\n\
+successful completion of updates (after commit() on the updater); if\n\
+it's false, the existing RRs will be intact unless explicitly deleted\n\
+by delete_r_rset() on the updater.\n\
+\n\
+A data source can be \"read only\" or can prohibit partial updates. In\n\
+such cases this method will result in an isc.NotImplemented exception\n\
+unconditionally or when replace is false).\n\
+\n\
+Exceptions:\n\
+  NotImplemented The underlying data source does not support updates.\n\
+  DataSourceError Internal error in the underlying data source.\n\
+  std.bad_alloc Resource allocation failure.\n\
+\n\
+Parameters:\n\
+  name       The zone name to be updated\n\
+  replace    Whether to delete existing RRs before making updates\n\
+\n\
+";
+} // unnamed namespace

+ 11 - 7
src/lib/python/isc/datasrc/client_python.cc

@@ -40,6 +40,7 @@
 #include "finder_python.h"
 #include "iterator_python.h"
 #include "updater_python.h"
+#include "client_inc.cc"
 
 using namespace std;
 using namespace isc::util::python;
@@ -79,7 +80,7 @@ void DataSourceClient_destroy(s_DataSourceClient* self);
 // These are the functions we export
 //
 PyObject*
-DataSourceClient_FindZone(PyObject* po_self, PyObject* args) {
+DataSourceClient_findZone(PyObject* po_self, PyObject* args) {
     s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
     PyObject *name;
     if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name)) {
@@ -100,7 +101,7 @@ DataSourceClient_FindZone(PyObject* po_self, PyObject* args) {
 }
 
 PyObject*
-DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) {
+DataSourceClient_getIterator(PyObject* po_self, PyObject* args) {
     s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
     PyObject *name_obj;
     if (PyArg_ParseTuple(args, "O!", &isc::dns::python::name_type, &name_obj)) {
@@ -120,7 +121,7 @@ DataSourceClient_GetIterator(PyObject* po_self, PyObject* args) {
 }
 
 PyObject*
-DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) {
+DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) {
     s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
     PyObject *name_obj;
     PyObject *replace_obj;
@@ -150,9 +151,12 @@ DataSourceClient_GetUpdater(PyObject* po_self, PyObject* args) {
 // 3. Argument type
 // 4. Documentation
 PyMethodDef DataSourceClient_methods[] = {
-    { "find_zone", DataSourceClient_FindZone, METH_VARARGS, "TODO" },
-    { "get_iterator", DataSourceClient_GetIterator, METH_VARARGS, "TODO" },
-    { "get_updater", DataSourceClient_GetUpdater, METH_VARARGS, "TODO" },
+    { "find_zone", reinterpret_cast<PyCFunction>(DataSourceClient_findZone), METH_VARARGS,
+      DataSourceClient_findZone_doc },
+    { "get_iterator", reinterpret_cast<PyCFunction>(DataSourceClient_getIterator), METH_VARARGS,
+      DataSourceClient_getIterator_doc },
+    { "get_updater", reinterpret_cast<PyCFunction>(DataSourceClient_getUpdater), METH_VARARGS,
+      DataSourceClient_getUpdater_doc },
     { NULL, NULL, 0, NULL }
 };
 
@@ -231,7 +235,7 @@ PyTypeObject datasourceclient_type = {
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
     Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The DataSourceClient class objects is...(COMPLETE THIS)",
+    DataSourceClient_doc,
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
     NULL,                               // tp_richcompare

+ 105 - 0
src/lib/python/isc/datasrc/finder_inc.cc

@@ -0,0 +1,105 @@
+namespace {
+const char* const ZoneFinder_doc = "\
+The base class to search a zone for RRsets.\n\
+\n\
+The ZoneFinder class is an abstract base class for representing an\n\
+object that performs DNS lookups in a specific zone accessible via a\n\
+data source. In general, different types of data sources (in-memory,\n\
+database-based, etc) define their own derived classes of ZoneFinder,\n\
+implementing ways to retrieve the required data through the common\n\
+interfaces declared in the base class. Each concrete ZoneFinder object\n\
+is therefore (conceptually) associated with a specific zone of one\n\
+specific data source instance.\n\
+\n\
+The origin name and the RR class of the associated zone are available\n\
+via the get_origin() and get_class() methods, respectively.\n\
+\n\
+The most important method of this class is find(), which performs the\n\
+lookup for a given domain and type. See the description of the method\n\
+for details.\n\
+\n\
+It's not clear whether we should request that a zone finder form a\n\
+\"transaction\", that is, whether to ensure the finder is not\n\
+susceptible to changes made by someone else than the creator of the\n\
+finder. If we don't request that, for example, two different lookup\n\
+results for the same name and type can be different if other threads\n\
+or programs make updates to the zone between the lookups. We should\n\
+revisit this point as we gain more experiences.\n\
+\n\
+ZoneFinder()\n\
+\n\
+    The default constructor.\n\
+\n\
+    This is intentionally defined as protected as this base class\n\
+    should never be instantiated (except as part of a derived class).\n\
+\n\
+";
+
+const char* const ZoneFinder_getOrigin_doc = "\
+get_origin() -> isc.dns.Name\n\
+\n\
+Return the origin name of the zone.\n\
+\n\
+";
+
+const char* const ZoneFinder_getClass_doc = "\
+get_class() -> isc.dns.RRClass\n\
+\n\
+Return the RR class of the zone.\n\
+\n\
+";
+
+const char* const ZoneFinder_find_doc = "\
+find(name, type, target=NULL, options=FIND_DEFAULT) -> FindResult\n\
+\n\
+Search the zone for a given pair of domain name and RR type.\n\
+\n\
+- If the search name belongs under a zone cut, it returns the code of\n\
+  DELEGATION and the NS RRset at the zone cut.\n\
+- If there is no matching name, it returns the code of NXDOMAIN, and,\n\
+  if DNSSEC is requested, the NSEC RRset that proves the non-\n\
+  existence.\n\
+- If there is a matching name but no RRset of the search type, it\n\
+  returns the code of NXRRSET, and, if DNSSEC is required, the NSEC\n\
+  RRset for that name.\n\
+- If there is a CNAME RR of the searched name but there is no RR of\n\
+  the searched type of the name (so this type is different from\n\
+  CNAME), it returns the code of CNAME and that CNAME RR. Note that if\n\
+  the searched RR type is CNAME, it is considered a successful match,\n\
+  and the code of SUCCESS will be returned.\n\
+- If the search name matches a delegation point of DNAME, it returns\n\
+  the code of DNAME and that DNAME RR.\n\
+- If the target isn't NULL, all RRsets under the domain are inserted\n\
+  there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned\n\
+  instead of normall processing. This is intended to handle ANY query.\n\
+  : this behavior is controversial as we discussed in\n\
+  https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html\n\
+  We should revisit the interface before we heavily rely on it. The\n\
+  options parameter specifies customized behavior of the search. Their\n\
+  semantics is as follows:\n\
+- GLUE_OK Allow search under a zone cut. By default the search will\n\
+  stop once it encounters a zone cut. If this option is specified it\n\
+  remembers information about the highest zone cut and continues the\n\
+  search until it finds an exact match for the given name or it\n\
+  detects there is no exact match. If an exact match is found, RRsets\n\
+  for that name are searched just like the normal case; otherwise, if\n\
+  the search has encountered a zone cut, DELEGATION with the\n\
+  information of the highest zone cut will be returned.\n\
+\n\
+A derived version of this method may involve internal resource\n\
+allocation, especially for constructing the resulting RRset, and may\n\
+throw an exception if it fails. It throws DuplicateRRset exception if\n\
+there are duplicate rrsets under the same domain. It should not throw\n\
+other types of exceptions.\n\
+\n\
+Parameters:\n\
+  name       The domain name to be searched for.\n\
+  type       The RR type to be searched for.\n\
+  target     If target is not NULL, insert all RRs under the domain\n\
+             into it.\n\
+  options    The search options.\n\
+\n\
+Return Value(s): A FindResult object enclosing the search result (see\n\
+above).\n\
+";
+} // unnamed namespace

+ 11 - 7
src/lib/python/isc/datasrc/finder_python.cc

@@ -40,6 +40,7 @@
 
 #include "datasrc.h"
 #include "finder_python.h"
+#include "finder_inc.cc"
 
 using namespace std;
 using namespace isc::util::python;
@@ -89,7 +90,7 @@ ZoneFinder_destroy(s_ZoneFinder* const self) {
 
 // These are the functions we export
 //
-PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) {
+PyObject* ZoneFinder_getClass(PyObject* po_self, PyObject*) {
     s_ZoneFinder* self = static_cast<s_ZoneFinder*>(po_self);
     try {
         return (isc::dns::python::createRRClassObject(self->cppobj->getClass()));
@@ -99,7 +100,7 @@ PyObject* ZoneFinder_GetClass(PyObject* po_self, PyObject*) {
     }
 }
 
-PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) {
+PyObject* ZoneFinder_getOrigin(PyObject* po_self, PyObject*) {
     s_ZoneFinder* self = static_cast<s_ZoneFinder*>(po_self);
     try {
         return (isc::dns::python::createNameObject(self->cppobj->getOrigin()));
@@ -109,7 +110,7 @@ PyObject* ZoneFinder_GetOrigin(PyObject* po_self, PyObject*) {
     }
 }
 
-PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) {
+PyObject* ZoneFinder_find(PyObject* po_self, PyObject* args) {
     s_ZoneFinder* const self = static_cast<s_ZoneFinder*>(po_self);
     PyObject *name;
     PyObject *rrtype;
@@ -157,9 +158,12 @@ PyObject* ZoneFinder_Find(PyObject* po_self, PyObject* args) {
 // 3. Argument type
 // 4. Documentation
 PyMethodDef ZoneFinder_methods[] = {
-    { "get_class", ZoneFinder_GetClass, METH_NOARGS, "TODO" },
-    { "get_origin", ZoneFinder_GetOrigin, METH_NOARGS, "TODO" },
-    { "find", ZoneFinder_Find, METH_VARARGS, "TODO" },
+    { "get_origin", reinterpret_cast<PyCFunction>(ZoneFinder_getOrigin), METH_NOARGS,
+      ZoneFinder_getOrigin_doc },
+    { "get_class", reinterpret_cast<PyCFunction>(ZoneFinder_getClass), METH_NOARGS,
+      ZoneFinder_getClass_doc },
+    { "find", reinterpret_cast<PyCFunction>(ZoneFinder_find), METH_VARARGS,
+      ZoneFinder_find_doc },
     { NULL, NULL, 0, NULL }
 };
 
@@ -189,7 +193,7 @@ PyTypeObject zonefinder_type = {
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
     Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The ZoneFinder class objects is...(TODO COMPLETE THIS)",
+    ZoneFinder_doc,
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
     NULL,                               // tp_richcompare

+ 33 - 0
src/lib/python/isc/datasrc/iterator_inc.cc

@@ -0,0 +1,33 @@
+namespace {
+
+const char* const ZoneIterator_doc = "\
+Read-only iterator to a zone.\n\
+\n\
+You can get an instance of (descendand of) ZoneIterator from\n\
+DataSourceClient.get_iterator() method. The actual concrete\n\
+implementation will be different depending on the actual data source\n\
+used. This is the abstract interface.\n\
+\n\
+There's no way to start iterating from the beginning again or return.\n\
+\n\
+";
+
+const char* const ZoneIterator_getNextRRset_doc = "\
+get_next_r_rset() -> isc.dns.ConstRRset\n\
+\n\
+Get next RRset from the zone.\n\
+\n\
+This returns the next RRset in the zone as a shared pointer. The\n\
+shared pointer is used to allow both accessing in-memory data and\n\
+automatic memory management.\n\
+\n\
+Any special order is not guaranteed.\n\
+\n\
+While this can potentially throw anything (including standard\n\
+allocation errors), it should be rare.\n\
+\n\
+Pointer to the next RRset or NULL pointer when the iteration gets to\n\
+the end of the zone.\n\
+\n\
+";
+} // unnamed namespace

+ 6 - 3
src/lib/python/isc/datasrc/iterator_python.cc

@@ -36,6 +36,8 @@
 #include "datasrc.h"
 #include "iterator_python.h"
 
+#include "iterator_inc.cc"
+
 using namespace std;
 using namespace isc::util::python;
 using namespace isc::datasrc;
@@ -84,7 +86,7 @@ ZoneIterator_destroy(s_ZoneIterator* const self) {
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
 //
-PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) {
+PyObject* ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) {
     s_ZoneIterator* self = static_cast<s_ZoneIterator*>(po_self);
     try {
         isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset();
@@ -117,7 +119,8 @@ PyObject* ZoneIterator_GetNextRRset(PyObject* po_self, PyObject*) {
 // 3. Argument type
 // 4. Documentation
 PyMethodDef ZoneIterator_methods[] = {
-    { "get_next_rrset", ZoneIterator_GetNextRRset, METH_NOARGS, "TODO" },
+    { "get_next_rrset", reinterpret_cast<PyCFunction>(ZoneIterator_getNextRRset),
+      METH_NOARGS, ZoneIterator_getNextRRset_doc },
     { NULL, NULL, 0, NULL }
 };
 
@@ -148,7 +151,7 @@ PyTypeObject zoneiterator_type = {
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
     Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The ZoneIterator class objects is...(TODO COMPLETE THIS)",
+    ZoneIterator_doc,
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
     NULL,                               // tp_richcompare

+ 203 - 0
src/lib/python/isc/datasrc/updater_inc.cc

@@ -0,0 +1,203 @@
+namespace {
+
+const char* const ZoneUpdater_doc = "\
+The base class to make updates to a single zone.\n\
+\n\
+On construction, each derived class object will start a\n\
+\"transaction\" for making updates to a specific zone (this means a\n\
+constructor of a derived class would normally take parameters to\n\
+identify the zone to be updated). The underlying realization of a\n\
+\"transaction\" will differ for different derived classes; if it uses\n\
+a general purpose database as a backend, it will involve performing\n\
+some form of \"begin transaction\" statement for the database.\n\
+\n\
+Updates (adding or deleting RRs) are made via add_r_rset() and\n\
+delete_r_rset() methods. Until the commit() method is called the\n\
+changes are local to the updater object. For example, they won't be\n\
+visible via a ZoneFinder object except the one returned by the\n\
+updater's own get_finder() method. The commit() completes the\n\
+transaction and makes the changes visible to others.\n\
+\n\
+This class does not provide an explicit \"rollback\" interface. If\n\
+something wrong or unexpected happens during the updates and the\n\
+caller wants to cancel the intermediate updates, the caller should\n\
+simply destruct the updater object without calling commit(). The\n\
+destructor is supposed to perform the \"rollback\" operation,\n\
+depending on the internal details of the derived class.\n\
+\n\
+This initial implementation provides a quite simple interface of\n\
+adding and deleting RRs (see the description of the related methods).\n\
+It may be revisited as we gain more experiences.\n\
+\n\
+";
+/*
+const char* const ZoneUpdater_getFinder_doc = "\
+get_finder() -> ZoneFinder \n\
+\n\
+Return a finder for the zone being updated.\n\
+\n\
+The returned finder provides the functionalities of ZoneFinder for the\n\
+zone as updates are made via the updater. That is, before making any\n\
+update, the finder will be able to find all RRsets that exist in the\n\
+zone at the time the updater is created. If RRsets are added or\n\
+deleted via add_r_rset() or delete_r_rset(), this finder will find the\n\
+added ones or miss the deleted ones respectively.\n\
+\n\
+The finder returned by this method is effective only while the updates\n\
+are performed, i.e., from the construction of the corresponding\n\
+updater until commit() is performed or the updater is destructed\n\
+without commit. The result of a subsequent call to this method (or the\n\
+use of the result) after that is undefined.\n\
+\n\
+A reference to a ZoneFinder for the updated zone\n\
+\n\
+";
+*/
+const char* const ZoneUpdater_addRRset_doc = "\
+add_r_rset(rrset) -> void\n\
+\n\
+Add an RRset to a zone via the updater.\n\
+\n\
+- Whether the RR class is identical to that for the zone to be updated\n\
+- Whether the RRset is not empty, i.e., it has at least one RDATA\n\
+- Whether the RRset is not associated with an RRSIG, i.e., whether\n\
+  get_r_rsig() on the RRset returns a NULL pointer.\n\
+\n\
+and otherwise does not check any oddity. For example, it doesn't check\n\
+whether the owner name of the specified RRset is a subdomain of the\n\
+zone's origin; it doesn't care whether or not there is already an\n\
+RRset of the same name and RR type in the zone, and if there is,\n\
+whether any of the existing RRs have duplicate RDATA with the added\n\
+ones. If these conditions matter the calling application must examine\n\
+the existing data beforehand using the ZoneFinder returned by\n\
+get_finder().\n\
+\n\
+The validation requirement on the associated RRSIG is temporary. If we\n\
+find it more reasonable and useful to allow adding a pair of RRset and\n\
+its RRSIG RRset as we gain experiences with the interface, we may\n\
+remove this restriction. Until then we explicitly check it to prevent\n\
+accidental misuse.\n\
+\n\
+Conceptually, on successful call to this method, the zone will have\n\
+the specified RRset, and if there is already an RRset of the same name\n\
+and RR type, these two sets will be \"merged\". \"Merged\" means that\n\
+a subsequent call to ZoneFinder.find() for the name and type will\n\
+result in success and the returned RRset will contain all previously\n\
+existing and newly added RDATAs with the TTL being the minimum of the\n\
+two RRsets. The underlying representation of the \"merged\" RRsets may\n\
+vary depending on the characteristic of the underlying data source.\n\
+For example, if it uses a general purpose database that stores each RR\n\
+of the same RRset separately, it may simply be a larger sets of RRs\n\
+based on both the existing and added RRsets; the TTLs of the RRs may\n\
+be different within the database, and there may even be duplicate RRs\n\
+in different database rows. As long as the RRset returned via\n\
+ZoneFinder.find() conforms to the concept of \"merge\", the actual\n\
+internal representation is up to the implementation.\n\
+\n\
+This method must not be called once commit() is performed. If it calls\n\
+after commit() the implementation must throw a DataSourceError\n\
+exception.\n\
+\n\
+TodoAs noted above we may have to revisit the design details as we\n\
+gain experiences:\n\
+\n\
+- we may want to check (and maybe reject) if there is already a\n\
+  duplicate RR (that has the same RDATA).\n\
+- we may want to check (and maybe reject) if there is already an RRset\n\
+  of the same name and RR type with different TTL\n\
+- we may even want to check if there is already any RRset of the same\n\
+  name and RR type.\n\
+- we may want to add an \"options\" parameter that can control the\n\
+  above points\n\
+- we may want to have this method return a value containing the\n\
+  information on whether there's a duplicate, etc.\n\
+\n\
+Exceptions:\n\
+  DataSourceError Called after commit(), RRset is invalid (see above),\n\
+             internal data source error\n\
+  std.bad_alloc Resource allocation failure\n\
+\n\
+Parameters:\n\
+  rrset      The RRset to be added\n\
+\n\
+";
+
+const char* const ZoneUpdater_deleteRRset_doc = "\
+delete_r_rset(rrset) -> void\n\
+\n\
+Delete an RRset from a zone via the updater.\n\
+\n\
+Like add_r_rset(), the detailed semantics and behavior of this method\n\
+may have to be revisited in a future version. The following are based\n\
+on the initial implementation decisions.\n\
+\n\
+- Existing RRs that don't match any of the specified RDATAs will\n\
+  remain in the zone.\n\
+- Any RRs of the specified RRset that doesn't exist in the zone will\n\
+  simply be ignored; the implementation of this method is not supposed\n\
+  to check that condition.\n\
+- The TTL of the RRset is ignored; matching is only performed by the\n\
+  owner name, RR type and RDATA\n\
+\n\
+Ignoring the TTL may not look sensible, but it's based on the\n\
+observation that it will result in more intuitive result, especially\n\
+when the underlying data source is a general purpose database. See\n\
+also DatabaseAccessor.delete_record_in_zone() on this point. It also\n\
+matches the dynamic update protocol (RFC2136), where TTLs are ignored\n\
+when deleting RRs.\n\
+\n\
+- Whether the RR class is identical to that for the zone to be updated\n\
+- Whether the RRset is not empty, i.e., it has at least one RDATA\n\
+- Whether the RRset is not associated with an RRSIG, i.e., whether\n\
+  get_r_rsig() on the RRset returns a NULL pointer.\n\
+\n\
+This method must not be called once commit() is performed. If it calls\n\
+after commit() the implementation must throw a DataSourceError\n\
+exception.\n\
+\n\
+TodoAs noted above we may have to revisit the design details as we\n\
+gain experiences:\n\
+\n\
+- we may want to check (and maybe reject) if some or all of the RRs\n\
+  for the specified RRset don't exist in the zone\n\
+- we may want to allow an option to \"delete everything\" for\n\
+  specified name and/or specified name + RR type.\n\
+- as mentioned above, we may want to include the TTL in matching the\n\
+  deleted RRs\n\
+- we may want to add an \"options\" parameter that can control the\n\
+  above points\n\
+- we may want to have this method return a value containing the\n\
+  information on whether there's any RRs that are specified but don't\n\
+  exit, the number of actually deleted RRs, etc.\n\
+\n\
+Exceptions:\n\
+  DataSourceError Called after commit(), RRset is invalid (see above),\n\
+             internal data source error\n\
+  std.bad_alloc Resource allocation failure\n\
+\n\
+Parameters:\n\
+  rrset      The RRset to be deleted\n\
+\n\
+";
+
+const char* const ZoneUpdater_commit_doc = "\
+commit() -> void\n\
+\n\
+Commit the updates made in the updater to the zone.\n\
+\n\
+This method completes the \"transaction\" started at the creation of\n\
+the updater. After successful completion of this method, the updates\n\
+will be visible outside the scope of the updater. The actual internal\n\
+behavior will defer for different derived classes. For a derived class\n\
+with a general purpose database as a backend, for example, this method\n\
+would perform a \"commit\" statement for the database.\n\
+\n\
+This operation can only be performed at most once. A duplicate call\n\
+must result in a DatasourceError exception.\n\
+\n\
+Exceptions:\n\
+  DataSourceError Duplicate call of the method, internal data source\n\
+             error\n\
+\n\
+";
+} // unnamed namespace

+ 12 - 8
src/lib/python/isc/datasrc/updater_python.cc

@@ -37,6 +37,8 @@
 #include "datasrc.h"
 #include "updater_python.h"
 
+#include "updater_inc.cc"
+
 using namespace std;
 using namespace isc::util::python;
 using namespace isc::datasrc;
@@ -87,7 +89,7 @@ ZoneUpdater_destroy(s_ZoneUpdater* const self) {
 
 // These are the functions we export
 //
-PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) {
+PyObject* ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) {
     // TODO err handling
     s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
     PyObject* rrset_obj;
@@ -107,8 +109,7 @@ PyObject* ZoneUpdater_AddRRset(PyObject* po_self, PyObject* args) {
     }
 }
 
-PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) {
-    // TODO err handling
+PyObject* ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) {
     s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
     PyObject* rrset_obj;
     if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrset_type, &rrset_obj)) {
@@ -127,7 +128,7 @@ PyObject* ZoneUpdater_DeleteRRset(PyObject* po_self, PyObject* args) {
     }
 }
 
-PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) {
+PyObject* ZoneUpdater_commit(PyObject* po_self, PyObject*) {
     s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
     try {
         self->cppobj->commit();
@@ -153,9 +154,12 @@ PyObject* ZoneUpdater_Commit(PyObject* po_self, PyObject*) {
 // 4. Documentation
 PyMethodDef ZoneUpdater_methods[] = {
 /*TODO    { "get_finder", ZoneUpdater_GetFinder, METH_NOARGS, "TODO" },*/
-    { "add_rrset", ZoneUpdater_AddRRset, METH_VARARGS, "TODO" },
-    { "delete_rrset", ZoneUpdater_DeleteRRset, METH_VARARGS, "TODO" },
-    { "commit", ZoneUpdater_Commit, METH_NOARGS, "TODO" },
+    { "add_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_addRRset), METH_VARARGS,
+      ZoneUpdater_addRRset_doc },
+    { "delete_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_deleteRRset), METH_VARARGS,
+      ZoneUpdater_deleteRRset_doc },
+    { "commit", reinterpret_cast<PyCFunction>(ZoneUpdater_commit), METH_NOARGS,
+      ZoneUpdater_commit_doc },
     { NULL, NULL, 0, NULL }
 };
 
@@ -185,7 +189,7 @@ PyTypeObject zoneupdater_type = {
     NULL,                               // tp_setattro
     NULL,                               // tp_as_buffer
     Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The ZoneUpdater class objects is...(TODO COMPLETE THIS)",
+    ZoneUpdater_doc,
     NULL,                               // tp_traverse
     NULL,                               // tp_clear
     NULL,                               // tp_richcompare