BIND 10 #2379: add python wrapper of datasrc::ZoneLoader

BIND 10 Development do-not-reply at isc.org
Fri Dec 14 17:17:04 UTC 2012


#2379: add python wrapper of datasrc::ZoneLoader
-------------------------------------+-------------------------------------
            Reporter:  jinmei        |                        Owner:
                Type:  task          |  jinmei
            Priority:  medium        |                       Status:
           Component:  data source   |  reviewing
            Keywords:                |                    Milestone:
           Sensitive:  0             |  Sprint-20121218
         Sub-Project:  DNS           |                   Resolution:
Estimated Difficulty:  4             |                 CVSS Scoring:
         Total Hours:  10            |              Defect Severity:  N/A
                                     |  Feature Depending on Ticket:
                                     |  loadzone-ng
                                     |          Add Hours to Ticket:  0
                                     |                    Internal?:  0
-------------------------------------+-------------------------------------
Changes (by jelte):

 * owner:  jelte => jinmei
 * totalhours:  0 => 10


Comment:

 Replying to [comment:6 jinmei]:
 > '''pydnspp.cc'''
 >
 > I'd like to make it a bit more safer at this opportunity using
 > `PyObjectContainer` and `installToModule()`.  See, e.g.
 > src/lib/python/isc/util/cio/socketsession_python.cc
 >

 Done.

 While I was at it, I noticed the type/class initialization code was quite
 inconsistent (different cast types, return value of addToModule not always
 checked, etc), so I also added an initClass() function to pydnspp_common,
 and used that for all types in pydnspp.cc

 If that approach looks ok, we should probably add it to the general python
 util class as well.

 > '''client_python.h'''
 >
 > - I'd like to see some documentation for
 `PyDataSourceClient_ToDataSourceClient()`.
 >

 added

 > '''client_python.cc'''
 >
 > - the what() message of the exception seems incorrect (it's not `Name`):
 > {{{#!cpp
 >     if (client_obj == NULL) {
 >         isc_throw(PyCPPWrapperException,
 >                   "obj argument NULL in Name PyObject conversion");
 >     }
 > }}}

 fixed

 > - not a big deal in this context, but I suspect technically `client`
 >   cannot be a const pointer, because we are effectively going to allow
 >   this object to be modified.  So, this matches the reality better:
 > {{{#!cpp
 >     s_DataSourceClient* client =
 static_cast<s_DataSourceClient*>(client_obj);
 > }}}
 >

 fixed

 > '''zone_loader_python.cc'''
 >
 > - `ZoneLoader_init`: it doesn't seem to be exception safe regarding
 >   the reference to po_target_client.  I suspect it should be protected
 >   using `PyObjectContainer`.  Also writing in that case (that checks
 >   the refcount of the target client) is a good idea.

 Done, though the incref/set could probably just go at the end in this
 specific scenario. But I may be misunderstanding the use of the container
 here :)

 Also added a check that should trigger too many DECREFS. But have not
 really thought of a good way to test reference leaks.

 Not sure what that last line means.

 > - With this code, the error string may not always be helpful:
 >   It would say "TypeError: must be str, not int", but it may not
 >   necessarily have to be str (can be another client).  So in such
 >   cases I'd add our own error string, saying some error in parameters
 >   and what are acceptable.

 fixed

 > - is it okay not to gain a reference to source_client?

 No, added

 > - `ZoneLoader_loadIncremental`: maybe a matter of taste, but we don't
 >   need `complete`.
 >

 removed

 > '''zone_loader_test.py'''
 >
 > - some resources don't seem to be cleaned up cleanly in case a test
 >   fails.  for example, if we do this in test_bad_file
 > {{{#!python
 >         self.check_zone_soa(ORIG_SOA_TXT)
 >         loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
 >                                         'no such file')
 >         loader.load()
 >         ...
 > }}}
 >   some other tests also fail with "database is locked".

 Hmz, initially I thought this may be a refcounting problem (and it looks
 like it is, in a way, but not what I was expecting).

 Turns out, when there is an exception, local variables aren't actually
 completely destroyed until all tests finish (exception context gets a
 reference too). Normally, this isn't much of a problem, but in the case of
 the loader, we can only have 1 instance at a time (since it locks the db).

 But it certainly messes up normal C++-style RAII usage...

 There are several workarounds;

 - put the loader as a member in the test class, and set it to None in
 tearDown(); (tbh, i was surprised that that worked)

 - try/catch every single test that creates one (then set loader to None
 and re-raise), or never put the loader in a local variable (but this is
 not always possible for extensive tests)

 - destroy the internal cppobj for any exception (...) and disallow further
 operations (so the db gets unlocked). Perhaps even in the c++ class itself
 (with the current API i don't think there's ever a case where you want to
 or even can continue to use the loader instance after a failure, but
 perhaps that may change). This would probably be safest in any
 environment, but it'd also be quite unexpected behaviour.

 - make a much smarter loader class :p (that can share db access). meh.

 None of these seem nice though... I think I dislike option 1 the least.
 Therefore I'll do that but as the last commit, so if you have a better
 idea, I can revert it.

 > - not looking into the code, but does this case "no such zone in
 >   source"?
 > {{{#!python
 >     def test_no_such_zone_in_source(self):
 >         source_client = isc.datasrc.DataSourceClient('sqlite3',
 >
 DB_SOURCE_CLIENT_CONFIG)
 >         self.assertRaises(isc.datasrc.Error, isc.datasrc.ZoneLoader,
 >                           self.client, isc.dns.Name("unknownzone"),
 >                           source_client)
 > }}}
 >   maybe it happens to be so, but to be sure it's better to use an
 >   existing zone for the destination but not in the source (or do we do
 >   this?  If so, please clarify that in the comment).

 Ack, it actually didn't. Updated test to do so, and to make sure it does.

 > - the tests seem to be pretty different from the C++ counterpart.
 >   does the Python version have the same level of coverage?
 >

 Not entirely, and IMO it shouldn't have to; the important thing here is
 that we test the wrapper code, not necessarily the underlying c++ classes
 (they have their own tests); there is a lot of overlap (they are
 essentially doing the same thing), but I don't think we need to repeat
 every single test. Of course I could be missing some here.

 > '''zone_loader_inc.cc'''
 >
 > The same comment as that for #2542 applies.  I'm attaching the C++
 > file that the auto-generation tool produces for reference (if we want
 > to use it we still normally need to edit it a bit).

 Updated it a bit and replaced the original.

 Two comments if you're cleaning up your script:
 - 'void' tends to be None in python (as a return value for methods)
 - the \note for loadIncremental was omitted

-- 
Ticket URL: <http://bind10.isc.org/ticket/2379#comment:8>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list