updater_python.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. // Enable this if you use s# variants with PyArg_ParseTuple(), see
  15. // http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
  16. //#define PY_SSIZE_T_CLEAN
  17. // Python.h needs to be placed at the head of the program file, see:
  18. // http://docs.python.org/py3k/extending/extending.html#a-simple-example
  19. #include <Python.h>
  20. #include <util/python/pycppwrapper_util.h>
  21. #include <datasrc/client.h>
  22. #include <datasrc/database.h>
  23. #include <datasrc/exceptions.h>
  24. #include <datasrc/sqlite3_accessor.h>
  25. #include <datasrc/zone.h>
  26. #include <dns/python/name_python.h>
  27. #include <dns/python/rrset_python.h>
  28. #include <dns/python/rrclass_python.h>
  29. #include <dns/python/rrtype_python.h>
  30. #include <dns/python/rrset_collection_python.h>
  31. #include "datasrc.h"
  32. #include "updater_python.h"
  33. #include "updater_inc.cc"
  34. #include "finder_inc.cc"
  35. using namespace std;
  36. using namespace isc::util::python;
  37. using namespace isc::dns::python;
  38. using namespace isc::datasrc;
  39. using namespace isc::datasrc::python;
  40. namespace isc_datasrc_internal {
  41. // See finder_python.cc
  42. PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args);
  43. PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args);
  44. }
  45. namespace {
  46. // The s_* Class simply covers one instantiation of the object
  47. class s_ZoneUpdater : public PyObject {
  48. public:
  49. s_ZoneUpdater() : cppobj(ZoneUpdaterPtr()), base_obj(NULL) {};
  50. ZoneUpdaterPtr cppobj;
  51. // This is a reference to a base object; if the object of this class
  52. // depends on another object to be in scope during its lifetime,
  53. // we use INCREF the base object upon creation, and DECREF it at
  54. // the end of the destructor
  55. // This is an optional argument to createXXX(). If NULL, it is ignored.
  56. PyObject* base_obj;
  57. };
  58. // Shortcut type which would be convenient for adding class variables safely.
  59. typedef CPPPyObjectContainer<s_ZoneUpdater, ZoneUpdater> ZoneUpdaterContainer;
  60. //
  61. // We declare the functions here, the definitions are below
  62. // the type definition of the object, since both can use the other
  63. //
  64. // General creation and destruction
  65. int
  66. ZoneUpdater_init(PyObject*, PyObject*, PyObject*) {
  67. // can't be called directly
  68. PyErr_SetString(PyExc_TypeError,
  69. "ZoneUpdater cannot be constructed directly");
  70. return (-1);
  71. }
  72. void
  73. ZoneUpdater_destroy(PyObject* po_self) {
  74. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  75. // cppobj is a shared ptr, but to make sure things are not destroyed in
  76. // the wrong order, we reset it here.
  77. self->cppobj.reset();
  78. if (self->base_obj != NULL) {
  79. Py_DECREF(self->base_obj);
  80. }
  81. Py_TYPE(self)->tp_free(self);
  82. }
  83. PyObject*
  84. ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) {
  85. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  86. PyObject* rrset_obj;
  87. if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) {
  88. try {
  89. self->cppobj->addRRset(PyRRset_ToRRset(rrset_obj));
  90. Py_RETURN_NONE;
  91. } catch (const DataSourceError& dse) {
  92. PyErr_SetString(getDataSourceException("Error"), dse.what());
  93. return (NULL);
  94. } catch (const std::exception& exc) {
  95. PyErr_SetString(getDataSourceException("Error"), exc.what());
  96. return (NULL);
  97. }
  98. } else {
  99. return (NULL);
  100. }
  101. }
  102. PyObject*
  103. ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) {
  104. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  105. PyObject* rrset_obj;
  106. if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) {
  107. try {
  108. self->cppobj->deleteRRset(PyRRset_ToRRset(rrset_obj));
  109. Py_RETURN_NONE;
  110. } catch (const DataSourceError& dse) {
  111. PyErr_SetString(getDataSourceException("Error"), dse.what());
  112. return (NULL);
  113. } catch (const std::exception& exc) {
  114. PyErr_SetString(getDataSourceException("Error"), exc.what());
  115. return (NULL);
  116. }
  117. } else {
  118. return (NULL);
  119. }
  120. }
  121. PyObject*
  122. ZoneUpdater_commit(PyObject* po_self, PyObject*) {
  123. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  124. try {
  125. self->cppobj->commit();
  126. Py_RETURN_NONE;
  127. } catch (const DataSourceError& dse) {
  128. PyErr_SetString(getDataSourceException("Error"), dse.what());
  129. return (NULL);
  130. } catch (const std::exception& exc) {
  131. PyErr_SetString(getDataSourceException("Error"), exc.what());
  132. return (NULL);
  133. }
  134. }
  135. PyObject*
  136. ZoneUpdater_getClass(PyObject* po_self, PyObject*) {
  137. s_ZoneUpdater* self = static_cast<s_ZoneUpdater*>(po_self);
  138. try {
  139. return (createRRClassObject(self->cppobj->getFinder().getClass()));
  140. } catch (const std::exception& exc) {
  141. PyErr_SetString(getDataSourceException("Error"), exc.what());
  142. return (NULL);
  143. } catch (...) {
  144. PyErr_SetString(getDataSourceException("Error"),
  145. "Unexpected exception");
  146. return (NULL);
  147. }
  148. }
  149. PyObject*
  150. ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) {
  151. s_ZoneUpdater* self = static_cast<s_ZoneUpdater*>(po_self);
  152. try {
  153. return (createNameObject(self->cppobj->getFinder().getOrigin()));
  154. } catch (const std::exception& exc) {
  155. PyErr_SetString(getDataSourceException("Error"), exc.what());
  156. return (NULL);
  157. } catch (...) {
  158. PyErr_SetString(getDataSourceException("Error"),
  159. "Unexpected exception");
  160. return (NULL);
  161. }
  162. }
  163. PyObject*
  164. ZoneUpdater_find(PyObject* po_self, PyObject* args) {
  165. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  166. return (isc_datasrc_internal::ZoneFinder_helper(&self->cppobj->getFinder(),
  167. args));
  168. }
  169. PyObject*
  170. ZoneUpdater_find_all(PyObject* po_self, PyObject* args) {
  171. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  172. return (isc_datasrc_internal::ZoneFinder_helper_all(
  173. &self->cppobj->getFinder(), args));
  174. }
  175. namespace {
  176. // Below we define Python RRsetCollection class generated by the updater.
  177. // It's never expected to be instantiated directly from Python code, so
  178. // everything is hidden here, and tp_init always fails.
  179. class s_UpdaterRRsetCollection : public s_RRsetCollection {
  180. public:
  181. s_UpdaterRRsetCollection() : s_RRsetCollection(), base_obj_(NULL) {}
  182. PyObject* base_obj_;
  183. };
  184. int
  185. RRsetCollection_init(PyObject*, PyObject*, PyObject*) {
  186. // can't be called directly; actually, the constructor shouldn't even be
  187. // called, but we catch the case just in case.
  188. PyErr_SetString(PyExc_TypeError,
  189. "datasrc.RRsetCollection cannot be constructed directly");
  190. return (-1);
  191. }
  192. void
  193. RRsetCollection_destroy(PyObject* po_self) {
  194. s_UpdaterRRsetCollection* const self =
  195. static_cast<s_UpdaterRRsetCollection*>(po_self);
  196. // We don't own the C++ collection object; we shouldn't delete it here.
  197. // Note: we need to check if this is NULL; it remains NULL in case of
  198. // direct instantiation (which fails).
  199. if (self->base_obj_ != NULL) {
  200. Py_DECREF(self->base_obj_);
  201. }
  202. Py_TYPE(self)->tp_free(self);
  203. }
  204. PyTypeObject updater_rrset_collection_type = {
  205. PyVarObject_HEAD_INIT(NULL, 0)
  206. "datasrc.UpdaterRRsetCollection",
  207. sizeof(s_UpdaterRRsetCollection), // tp_basicsize
  208. 0, // tp_itemsize
  209. RRsetCollection_destroy, // tp_dealloc
  210. NULL, // tp_print
  211. NULL, // tp_getattr
  212. NULL, // tp_setattr
  213. NULL, // tp_reserved
  214. NULL, // tp_repr
  215. NULL, // tp_as_number
  216. NULL, // tp_as_sequence
  217. NULL, // tp_as_mapping
  218. NULL, // tp_hash
  219. NULL, // tp_call
  220. NULL, // tp_str
  221. NULL, // tp_getattro
  222. NULL, // tp_setattro
  223. NULL, // tp_as_buffer
  224. Py_TPFLAGS_DEFAULT, // tp_flags
  225. NULL,
  226. NULL, // tp_traverse
  227. NULL, // tp_clear
  228. NULL, // tp_richcompare
  229. 0, // tp_weaklistoffset
  230. NULL, // tp_iter
  231. NULL, // tp_iternext
  232. NULL, // tp_methods
  233. NULL, // tp_members
  234. NULL, // tp_getset
  235. NULL, // tp_base (rrset_collection_base_type, set at run time)
  236. NULL, // tp_dict
  237. NULL, // tp_descr_get
  238. NULL, // tp_descr_set
  239. 0, // tp_dictoffset
  240. RRsetCollection_init, // tp_init
  241. NULL, // tp_alloc
  242. PyType_GenericNew, // tp_new
  243. NULL, // tp_free
  244. NULL, // tp_is_gc
  245. NULL, // tp_bases
  246. NULL, // tp_mro
  247. NULL, // tp_cache
  248. NULL, // tp_subclasses
  249. NULL, // tp_weaklist
  250. NULL, // tp_del
  251. 0 // tp_version_tag
  252. };
  253. } // unnamed namespace
  254. PyObject*
  255. ZoneUpdater_getRRsetCollection(PyObject* po_self, PyObject*) {
  256. s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
  257. s_UpdaterRRsetCollection* collection =
  258. static_cast<s_UpdaterRRsetCollection*>(
  259. PyObject_New(s_RRsetCollection, &updater_rrset_collection_type));
  260. collection->cppobj = &self->cppobj->getRRsetCollection();
  261. collection->base_obj_ = po_self;;
  262. Py_INCREF(collection->base_obj_);
  263. return (collection);
  264. }
  265. // This list contains the actual set of functions we have in
  266. // python. Each entry has
  267. // 1. Python method name
  268. // 2. Our static function here
  269. // 3. Argument type
  270. // 4. Documentation
  271. PyMethodDef ZoneUpdater_methods[] = {
  272. { "add_rrset", ZoneUpdater_addRRset,
  273. METH_VARARGS, ZoneUpdater_addRRset_doc },
  274. { "delete_rrset", ZoneUpdater_deleteRRset,
  275. METH_VARARGS, ZoneUpdater_deleteRRset_doc },
  276. { "commit", ZoneUpdater_commit, METH_NOARGS, ZoneUpdater_commit_doc },
  277. { "get_rrset_collection", ZoneUpdater_getRRsetCollection,
  278. METH_NOARGS, ZoneUpdater_getRRsetCollection_doc },
  279. // Instead of a getFinder, we implement the finder functionality directly
  280. // This is because ZoneFinder is non-copyable, and we should not create
  281. // a ZoneFinder object from a reference only (which is what is returned
  282. // by getFinder(). Apart from that
  283. { "get_origin", ZoneUpdater_getOrigin,
  284. METH_NOARGS, ZoneFinder_getOrigin_doc },
  285. { "get_class", ZoneUpdater_getClass,
  286. METH_NOARGS, ZoneFinder_getClass_doc },
  287. { "find", ZoneUpdater_find, METH_VARARGS, ZoneFinder_find_doc },
  288. { "find_all", ZoneUpdater_find_all, METH_VARARGS,
  289. ZoneFinder_findAll_doc },
  290. { NULL, NULL, 0, NULL }
  291. };
  292. } // end of unnamed namespace
  293. namespace isc {
  294. namespace datasrc {
  295. namespace python {
  296. PyTypeObject zoneupdater_type = {
  297. PyVarObject_HEAD_INIT(NULL, 0)
  298. "datasrc.ZoneUpdater",
  299. sizeof(s_ZoneUpdater), // tp_basicsize
  300. 0, // tp_itemsize
  301. ZoneUpdater_destroy, // tp_dealloc
  302. NULL, // tp_print
  303. NULL, // tp_getattr
  304. NULL, // tp_setattr
  305. NULL, // tp_reserved
  306. NULL, // tp_repr
  307. NULL, // tp_as_number
  308. NULL, // tp_as_sequence
  309. NULL, // tp_as_mapping
  310. NULL, // tp_hash
  311. NULL, // tp_call
  312. NULL, // tp_str
  313. NULL, // tp_getattro
  314. NULL, // tp_setattro
  315. NULL, // tp_as_buffer
  316. Py_TPFLAGS_DEFAULT, // tp_flags
  317. ZoneUpdater_doc,
  318. NULL, // tp_traverse
  319. NULL, // tp_clear
  320. NULL, // tp_richcompare
  321. 0, // tp_weaklistoffset
  322. NULL, // tp_iter
  323. NULL, // tp_iternext
  324. ZoneUpdater_methods, // tp_methods
  325. NULL, // tp_members
  326. NULL, // tp_getset
  327. NULL, // tp_base
  328. NULL, // tp_dict
  329. NULL, // tp_descr_get
  330. NULL, // tp_descr_set
  331. 0, // tp_dictoffset
  332. ZoneUpdater_init, // tp_init
  333. NULL, // tp_alloc
  334. PyType_GenericNew, // tp_new
  335. NULL, // tp_free
  336. NULL, // tp_is_gc
  337. NULL, // tp_bases
  338. NULL, // tp_mro
  339. NULL, // tp_cache
  340. NULL, // tp_subclasses
  341. NULL, // tp_weaklist
  342. NULL, // tp_del
  343. 0 // tp_version_tag
  344. };
  345. PyObject*
  346. createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source,
  347. PyObject* base_obj)
  348. {
  349. s_ZoneUpdater* py_zu = static_cast<s_ZoneUpdater*>(
  350. zoneupdater_type.tp_alloc(&zoneupdater_type, 0));
  351. if (py_zu != NULL) {
  352. py_zu->cppobj = source;
  353. py_zu->base_obj = base_obj;
  354. if (base_obj != NULL) {
  355. Py_INCREF(base_obj);
  356. }
  357. }
  358. return (py_zu);
  359. }
  360. bool
  361. initModulePart_ZoneUpdater(PyObject* mod) {
  362. // We initialize the static description object with PyType_Ready(),
  363. // then add it to the module. This is not just a check! (leaving
  364. // this out results in segmentation faults)
  365. if (PyType_Ready(&zoneupdater_type) < 0) {
  366. return (false);
  367. }
  368. void* zip = &zoneupdater_type;
  369. if (PyModule_AddObject(mod, "ZoneUpdater",
  370. static_cast<PyObject*>(zip)) < 0)
  371. {
  372. return (false);
  373. }
  374. Py_INCREF(&zoneupdater_type);
  375. // get_rrset_collection() needs the base class type information. Since
  376. // it's defined in a separate loadable module, we retrieve its C object
  377. // via the Python interpreter. Directly referring to
  378. // isc::dns::python::rrset_collection_base_type might work depending on
  379. // the runtime environment (and in fact it does for some), but that would
  380. // be less portable.
  381. try {
  382. if (updater_rrset_collection_type.tp_base == NULL) {
  383. PyObjectContainer dns_module(PyImport_ImportModule("isc.dns"));
  384. PyObjectContainer dns_dict(PyModule_GetDict(dns_module.get()));
  385. // GetDict doesn't acquire a reference, so we need to get it to
  386. // meet the container's requirement.
  387. Py_INCREF(dns_dict.get());
  388. PyObjectContainer base(
  389. PyDict_GetItemString(dns_dict.get(), "RRsetCollectionBase"));
  390. PyTypeObject* pt_rrset_collection_base =
  391. static_cast<PyTypeObject*>(static_cast<void*>(base.get()));
  392. updater_rrset_collection_type.tp_base = pt_rrset_collection_base;
  393. if (PyType_Ready(&updater_rrset_collection_type) < 0) {
  394. isc_throw(Unexpected, "failed to import isc.dns module");
  395. }
  396. // Make sure the base type won't suddenly disappear. Note that we
  397. // are effectively leaking it; it's intentional.
  398. Py_INCREF(base.get());
  399. }
  400. } catch (...) {
  401. PyErr_SetString(PyExc_SystemError,
  402. "Unexpected failure in ZoneUpdater initialization");
  403. return (false);
  404. }
  405. return (true);
  406. }
  407. } // namespace python
  408. } // namespace datasrc
  409. } // namespace isc