configurableclientlist_python.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // Copyright (C) 2012 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 <string>
  21. #include <stdexcept>
  22. #include <util/python/pycppwrapper_util.h>
  23. #include <dns/python/rrclass_python.h>
  24. #include <dns/python/name_python.h>
  25. #include <dns/python/pydnspp_common.h>
  26. #include <datasrc/client_list.h>
  27. #include "configurableclientlist_python.h"
  28. #include "datasrc.h"
  29. #include "finder_python.h"
  30. #include "client_python.h"
  31. #include "zonewriter_python.h"
  32. using namespace std;
  33. using namespace isc::util::python;
  34. using namespace isc::datasrc;
  35. using namespace isc::datasrc::memory;
  36. using namespace isc::datasrc::python;
  37. using namespace isc::datasrc::memory::python;
  38. using namespace isc::dns::python;
  39. //
  40. // ConfigurableClientList
  41. //
  42. // Trivial constructor.
  43. s_ConfigurableClientList::s_ConfigurableClientList() : cppobj(NULL) {
  44. }
  45. namespace {
  46. int
  47. ConfigurableClientList_init(PyObject* po_self, PyObject* args, PyObject*) {
  48. s_ConfigurableClientList* self =
  49. static_cast<s_ConfigurableClientList*>(po_self);
  50. try {
  51. const PyObject* rrclass;
  52. if (PyArg_ParseTuple(args, "O!", &isc::dns::python::rrclass_type,
  53. &rrclass)) {
  54. self->cppobj =
  55. new ConfigurableClientList(isc::dns::python::
  56. PyRRClass_ToRRClass(rrclass));
  57. return (0);
  58. }
  59. } catch (const exception& ex) {
  60. const string ex_what =
  61. "Failed to construct ConfigurableClientList object: " +
  62. string(ex.what());
  63. PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
  64. return (-1);
  65. } catch (...) {
  66. PyErr_SetString(PyExc_SystemError, "Unexpected C++ exception");
  67. return (-1);
  68. }
  69. return (-1);
  70. }
  71. void
  72. ConfigurableClientList_destroy(PyObject* po_self) {
  73. s_ConfigurableClientList* self =
  74. static_cast<s_ConfigurableClientList*>(po_self);
  75. delete self->cppobj;
  76. self->cppobj = NULL;
  77. Py_TYPE(self)->tp_free(self);
  78. }
  79. PyObject*
  80. ConfigurableClientList_configure(PyObject* po_self, PyObject* args) {
  81. s_ConfigurableClientList* self =
  82. static_cast<s_ConfigurableClientList*>(po_self);
  83. try {
  84. const char* configuration;
  85. int allow_cache;
  86. if (PyArg_ParseTuple(args, "si", &configuration, &allow_cache)) {
  87. const isc::data::ConstElementPtr
  88. element(isc::data::Element::fromJSON(string(configuration)));
  89. self->cppobj->configure(element, allow_cache);
  90. Py_RETURN_NONE;
  91. } else {
  92. return (NULL);
  93. }
  94. } catch (const isc::data::JSONError& jse) {
  95. const string ex_what(std::string("JSON parse error in data source"
  96. " configuration: ") + jse.what());
  97. PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
  98. return (NULL);
  99. } catch (const std::exception& exc) {
  100. PyErr_SetString(getDataSourceException("Error"), exc.what());
  101. return (NULL);
  102. } catch (...) {
  103. PyErr_SetString(getDataSourceException("Error"),
  104. "Unknown C++ exception");
  105. return (NULL);
  106. }
  107. }
  108. PyObject*
  109. ConfigurableClientList_resetMemorySegment(PyObject* po_self, PyObject* args) {
  110. s_ConfigurableClientList* self =
  111. static_cast<s_ConfigurableClientList*>(po_self);
  112. try {
  113. const char* datasrc_name_p;
  114. int mode_int;
  115. const char* config_p;
  116. if (PyArg_ParseTuple(args, "sis", &datasrc_name_p, &mode_int,
  117. &config_p)) {
  118. const std::string datasrc_name(datasrc_name_p);
  119. const isc::data::ConstElementPtr
  120. config(isc::data::Element::fromJSON(std::string(config_p)));
  121. ZoneTableSegment::MemorySegmentOpenMode mode =
  122. static_cast<ZoneTableSegment::MemorySegmentOpenMode>
  123. (mode_int);
  124. self->cppobj->resetMemorySegment(datasrc_name, mode, config);
  125. Py_RETURN_NONE;
  126. }
  127. } catch (const isc::data::JSONError& jse) {
  128. const string ex_what(std::string("JSON parse error in memory segment"
  129. " configuration: ") + jse.what());
  130. PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
  131. } catch (const std::exception& exc) {
  132. PyErr_SetString(getDataSourceException("Error"), exc.what());
  133. } catch (...) {
  134. PyErr_SetString(getDataSourceException("Error"),
  135. "Unknown C++ exception");
  136. }
  137. return (NULL);
  138. }
  139. PyObject*
  140. ConfigurableClientList_getCachedZoneWriter(PyObject* po_self, PyObject* args) {
  141. s_ConfigurableClientList* self =
  142. static_cast<s_ConfigurableClientList*>(po_self);
  143. try {
  144. PyObject* name_obj;
  145. const char* datasrc_name_p = "";
  146. if (PyArg_ParseTuple(args, "O!|s", &isc::dns::python::name_type,
  147. &name_obj, &datasrc_name_p)) {
  148. const isc::dns::Name
  149. name(isc::dns::python::PyName_ToName(name_obj));
  150. const std::string datasrc_name(datasrc_name_p);
  151. const ConfigurableClientList::ZoneWriterPair result =
  152. self->cppobj->getCachedZoneWriter(name, datasrc_name);
  153. PyObjectContainer writer;
  154. if (!result.second) {
  155. // Use the Py_BuildValue, as it takes care of the
  156. // reference counts correctly.
  157. writer.reset(Py_BuildValue(""));
  158. } else {
  159. // Make sure it keeps the writer alive.
  160. writer.reset(createZoneWriterObject(result.second,
  161. writer.get()));
  162. }
  163. return (Py_BuildValue("IO", result.first, writer.get()));
  164. } else {
  165. return (NULL);
  166. }
  167. } catch (const std::exception& exc) {
  168. PyErr_SetString(getDataSourceException("Error"), exc.what());
  169. return (NULL);
  170. } catch (...) {
  171. PyErr_SetString(getDataSourceException("Error"),
  172. "Unknown C++ exception");
  173. return (NULL);
  174. }
  175. }
  176. PyObject*
  177. ConfigurableClientList_find(PyObject* po_self, PyObject* args) {
  178. s_ConfigurableClientList* self =
  179. static_cast<s_ConfigurableClientList*>(po_self);
  180. try {
  181. PyObject* name_obj;
  182. int want_exact_match = 0;
  183. int want_finder = 1;
  184. if (PyArg_ParseTuple(args, "O!|ii", &isc::dns::python::name_type,
  185. &name_obj, &want_exact_match, &want_finder)) {
  186. const isc::dns::Name
  187. name(isc::dns::python::PyName_ToName(name_obj));
  188. const ClientList::FindResult
  189. result(self->cppobj->find(name, want_exact_match,
  190. want_finder));
  191. PyObjectContainer dsrc;
  192. if (result.dsrc_client_ == NULL) {
  193. // Use the Py_BuildValue, as it takes care of the
  194. // reference counts correctly.
  195. dsrc.reset(Py_BuildValue(""));
  196. } else {
  197. // Make sure we have a keeper there too, so it doesn't
  198. // die when the underlying client list dies or is
  199. // reconfigured.
  200. //
  201. // However, as it is inside the C++ part, is there a
  202. // reasonable way to test it?
  203. dsrc.reset(wrapDataSourceClient(result.dsrc_client_,
  204. result.life_keeper_));
  205. }
  206. PyObjectContainer finder;
  207. if (result.finder_ == NULL) {
  208. finder.reset(Py_BuildValue(""));
  209. } else {
  210. // Make sure it keeps the data source client alive.
  211. finder.reset(createZoneFinderObject(result.finder_,
  212. dsrc.get()));
  213. }
  214. PyObjectContainer exact(PyBool_FromLong(result.exact_match_));
  215. return (Py_BuildValue("OOO", dsrc.get(), finder.get(),
  216. exact.get()));
  217. } else {
  218. return (NULL);
  219. }
  220. } catch (const std::exception& exc) {
  221. PyErr_SetString(getDataSourceException("Error"), exc.what());
  222. return (NULL);
  223. } catch (...) {
  224. PyErr_SetString(getDataSourceException("Error"),
  225. "Unknown C++ exception");
  226. return (NULL);
  227. }
  228. }
  229. // This list contains the actual set of functions we have in
  230. // python. Each entry has
  231. // 1. Python method name
  232. // 2. Our static function here
  233. // 3. Argument type
  234. // 4. Documentation
  235. PyMethodDef ConfigurableClientList_methods[] = {
  236. { "configure", ConfigurableClientList_configure, METH_VARARGS,
  237. "configure(configuration, allow_cache) -> None\n\
  238. \n\
  239. Wrapper around C++ ConfigurableClientList::configure\n\
  240. \n\
  241. This sets the active configuration. It fills the ConfigurableClientList with\
  242. corresponding data source clients.\n\
  243. \n\
  244. If any error is detected, an exception is raised and the previous\
  245. configuration preserved.\n\
  246. \n\
  247. Parameters:\n\
  248. configuration The configuration, as a JSON encoded string.\
  249. allow_cache If caching is allowed." },
  250. { "reset_memory_segment", ConfigurableClientList_resetMemorySegment,
  251. METH_VARARGS,
  252. "reset_memory_segment(datasrc_name, mode, config_params) -> None\n\
  253. \n\
  254. Wrapper around C++ ConfigurableClientList::resetMemorySegment\n\
  255. \n\
  256. This resets the zone table segment for a datasource with a new\n\
  257. memory segment.\n\
  258. \n\
  259. Parameters:\n\
  260. datasrc_name The name of the data source whose segment to reset.\
  261. mode The open mode for the new memory segment.\
  262. config_params The configuration for the new memory segment, as a JSON encoded string." },
  263. { "get_cached_zone_writer", ConfigurableClientList_getCachedZoneWriter,
  264. METH_VARARGS,
  265. "get_cached_zone_writer(zone, datasrc_name) -> status, zone_writer\n\
  266. \n\
  267. Wrapper around C++ ConfigurableClientList::getCachedZoneWriter\n\
  268. \n\
  269. This returns a ZoneWriter that can be used to (re)load a zone.\n\
  270. \n\
  271. Parameters:\n\
  272. zone The name of the zone to (re)load.\
  273. datasrc_name The name of the data source where the zone is to be loaded." },
  274. { "find", ConfigurableClientList_find, METH_VARARGS,
  275. "find(zone, want_exact_match=False, want_finder=True) -> datasrc_client,\
  276. zone_finder, exact_match\n\
  277. \n\
  278. Look for a data source containing the given zone.\n\
  279. \n\
  280. It searches through the contained data sources and returns a data source\
  281. containing the zone, the zone finder of the zone and a boolean if the answer\
  282. is an exact match.\n\
  283. \n\
  284. The first parameter is isc.dns.Name object of a name in the zone. If the\
  285. want_exact_match is True, only zone with this exact origin is returned.\
  286. If it is False, the best matching zone is returned.\n\
  287. \n\
  288. If the want_finder is False, the returned zone_finder might be None even\
  289. if the data source is identified (in such case, the datasrc_client is not\
  290. None). Setting it to false allows the client list some optimisations, if\
  291. you don't need it, but if you do need it, it is better to set it to True\
  292. instead of getting it from the datasrc_client later.\n\
  293. \n\
  294. If no answer is found, the datasrc_client and zone_finder are None." },
  295. { NULL, NULL, 0, NULL }
  296. };
  297. const char* const ConfigurableClientList_doc = "\
  298. The list of data source clients\n\
  299. \n\
  300. The purpose is to have several data source clients of the same class\
  301. and then be able to search through them to identify the one containing\
  302. a given zone.\n\
  303. \n\
  304. Unlike the C++ version, we don't have the abstract base class. Abstract\
  305. classes are not needed due to the duck typing nature of python.\
  306. ";
  307. } // end of unnamed namespace
  308. namespace isc {
  309. namespace datasrc {
  310. namespace python {
  311. // This defines the complete type for reflection in python and
  312. // parsing of PyObject* to s_ConfigurableClientList
  313. // Most of the functions are not actually implemented and NULL here.
  314. PyTypeObject configurableclientlist_type = {
  315. PyVarObject_HEAD_INIT(NULL, 0)
  316. "datasrc.ConfigurableClientList",
  317. sizeof(s_ConfigurableClientList), // tp_basicsize
  318. 0, // tp_itemsize
  319. ConfigurableClientList_destroy, // tp_dealloc
  320. NULL, // tp_print
  321. NULL, // tp_getattr
  322. NULL, // tp_setattr
  323. NULL, // tp_reserved
  324. NULL, // tp_repr
  325. NULL, // tp_as_number
  326. NULL, // tp_as_sequence
  327. NULL, // tp_as_mapping
  328. NULL, // tp_hash
  329. NULL, // tp_call
  330. NULL, // tp_str
  331. NULL, // tp_getattro
  332. NULL, // tp_setattro
  333. NULL, // tp_as_buffer
  334. Py_TPFLAGS_DEFAULT, // tp_flags
  335. ConfigurableClientList_doc,
  336. NULL, // tp_traverse
  337. NULL, // tp_clear
  338. NULL, // tp_richcompare
  339. 0, // tp_weaklistoffset
  340. NULL, // tp_iter
  341. NULL, // tp_iternext
  342. ConfigurableClientList_methods, // tp_methods
  343. NULL, // tp_members
  344. NULL, // tp_getset
  345. NULL, // tp_base
  346. NULL, // tp_dict
  347. NULL, // tp_descr_get
  348. NULL, // tp_descr_set
  349. 0, // tp_dictoffset
  350. ConfigurableClientList_init, // tp_init
  351. NULL, // tp_alloc
  352. PyType_GenericNew, // tp_new
  353. NULL, // tp_free
  354. NULL, // tp_is_gc
  355. NULL, // tp_bases
  356. NULL, // tp_mro
  357. NULL, // tp_cache
  358. NULL, // tp_subclasses
  359. NULL, // tp_weaklist
  360. NULL, // tp_del
  361. 0 // tp_version_tag
  362. };
  363. // Module Initialization, all statics are initialized here
  364. bool
  365. initModulePart_ConfigurableClientList(PyObject* mod) {
  366. // We initialize the static description object with PyType_Ready(),
  367. // then add it to the module. This is not just a check! (leaving
  368. // this out results in segmentation faults)
  369. if (PyType_Ready(&configurableclientlist_type) < 0) {
  370. return (false);
  371. }
  372. void* p = &configurableclientlist_type;
  373. if (PyModule_AddObject(mod, "ConfigurableClientList",
  374. static_cast<PyObject*>(p)) < 0) {
  375. return (false);
  376. }
  377. Py_INCREF(&configurableclientlist_type);
  378. try {
  379. // ConfigurableClientList::CacheStatus enum
  380. installClassVariable(configurableclientlist_type,
  381. "CACHE_STATUS_CACHE_DISABLED",
  382. Py_BuildValue("I", ConfigurableClientList::CACHE_DISABLED));
  383. installClassVariable(configurableclientlist_type,
  384. "CACHE_STATUS_ZONE_NOT_CACHED",
  385. Py_BuildValue("I", ConfigurableClientList::ZONE_NOT_CACHED));
  386. installClassVariable(configurableclientlist_type,
  387. "CACHE_STATUS_ZONE_NOT_FOUND",
  388. Py_BuildValue("I", ConfigurableClientList::ZONE_NOT_FOUND));
  389. installClassVariable(configurableclientlist_type,
  390. "CACHE_STATUS_CACHE_NOT_WRITABLE",
  391. Py_BuildValue("I", ConfigurableClientList::CACHE_NOT_WRITABLE));
  392. installClassVariable(configurableclientlist_type,
  393. "CACHE_STATUS_DATASRC_NOT_FOUND",
  394. Py_BuildValue("I", ConfigurableClientList::DATASRC_NOT_FOUND));
  395. installClassVariable(configurableclientlist_type,
  396. "CACHE_STATUS_ZONE_SUCCESS",
  397. Py_BuildValue("I", ConfigurableClientList::ZONE_SUCCESS));
  398. // FIXME: These should eventually be moved to the
  399. // ZoneTableSegment class when we add Python bindings for the
  400. // memory data source specific bits. But for now, we add these
  401. // enums here to support reloading a zone table segment.
  402. installClassVariable(configurableclientlist_type, "CREATE",
  403. Py_BuildValue("I", ZoneTableSegment::CREATE));
  404. installClassVariable(configurableclientlist_type, "READ_WRITE",
  405. Py_BuildValue("I", ZoneTableSegment::READ_WRITE));
  406. installClassVariable(configurableclientlist_type, "READ_ONLY",
  407. Py_BuildValue("I", ZoneTableSegment::READ_ONLY));
  408. } catch (const std::exception& ex) {
  409. const std::string ex_what =
  410. "Unexpected failure in ConfigurableClientList initialization: " +
  411. std::string(ex.what());
  412. PyErr_SetString(po_IscException, ex_what.c_str());
  413. return (false);
  414. } catch (...) {
  415. PyErr_SetString(PyExc_SystemError,
  416. "Unexpected failure in ConfigurableClientList initialization");
  417. return (false);
  418. }
  419. return (true);
  420. }
  421. } // namespace python
  422. } // namespace datasrc
  423. } // namespace isc