| /** |
| * @file netconf.c |
| * @author Radek Krejci <rkrejci@cesnet.cz> |
| * @brief Python3 bindings for libnetconf2 (client-side) |
| * |
| * Copyright (c) 2017 CESNET, z.s.p.o. |
| * |
| * This source code is licensed under BSD 3-Clause License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://opensource.org/licenses/BSD-3-Clause |
| */ |
| |
| /* Python API header */ |
| #include <Python.h> |
| |
| /* standard headers */ |
| #include <syslog.h> |
| |
| #include "netconf.h" |
| |
| PyObject *libnetconf2Error; |
| PyObject *libnetconf2Warning; |
| PyObject *libnetconf2ReplyError; |
| |
| /* syslog usage flag */ |
| static int syslogEnabled = 0; |
| |
| /* libyang schema callback */ |
| static PyObject *schemaCallback = NULL; |
| static void *schemaCallbackData = NULL; |
| |
| static void |
| clb_print(NC_VERB_LEVEL level, const char* msg) |
| { |
| switch (level) { |
| case NC_VERB_ERROR: |
| PyErr_SetString(libnetconf2Error, msg); |
| if (syslogEnabled) { |
| syslog(LOG_ERR, "%s", msg); |
| } |
| break; |
| case NC_VERB_WARNING: |
| if (syslogEnabled) { |
| syslog(LOG_WARNING, "%s", msg); |
| } |
| PyErr_WarnEx(libnetconf2Warning, msg, 1); |
| break; |
| case NC_VERB_VERBOSE: |
| if (syslogEnabled) { |
| syslog(LOG_INFO, "%s", msg); |
| } |
| break; |
| case NC_VERB_DEBUG: |
| case NC_VERB_DEBUG_LOWLVL: |
| if (syslogEnabled) { |
| syslog(LOG_DEBUG, "%s", msg); |
| } |
| break; |
| } |
| } |
| |
| static PyObject * |
| setSyslog(PyObject *self, PyObject *args, PyObject *keywds) |
| { |
| char* name = NULL; |
| static char* logname = NULL; |
| static int option = LOG_PID; |
| static int facility = LOG_USER; |
| |
| static char *kwlist[] = {"enabled", "name", "option", "facility", NULL}; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywds, "p|sii", kwlist, &syslogEnabled, &name, &option, &facility)) { |
| return NULL; |
| } |
| |
| if (name) { |
| free(logname); |
| logname = strdup(name); |
| } else { |
| free(logname); |
| logname = NULL; |
| } |
| closelog(); |
| openlog(logname, option, facility); |
| |
| Py_RETURN_NONE; |
| } |
| |
| static PyObject * |
| setVerbosity(PyObject *self, PyObject *args, PyObject *keywds) |
| { |
| int level = NC_VERB_ERROR; /* 0 */ |
| |
| static char *kwlist[] = {"level", NULL}; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &level)) { |
| return NULL; |
| } |
| |
| /* normalize level value if not from the enum */ |
| if (level < NC_VERB_ERROR) { |
| nc_verbosity(NC_VERB_ERROR); |
| } else if (level > NC_VERB_DEBUG) { |
| nc_verbosity(NC_VERB_DEBUG); |
| } else { |
| nc_verbosity(level); |
| } |
| |
| Py_RETURN_NONE; |
| } |
| |
| static PyObject * |
| setSearchpath(PyObject *self, PyObject *args, PyObject *keywds) |
| { |
| char *path; |
| static char *kwlist[] = {"path", NULL}; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", kwlist, &path)) { |
| return NULL; |
| } |
| |
| if (nc_client_set_schema_searchpath(path)) { |
| return NULL; |
| } |
| |
| Py_RETURN_NONE; |
| } |
| |
| static void |
| schemaCallbackModelFree(void *model_data, void *user_data) |
| { |
| free(model_data); |
| } |
| |
| const char * |
| schemaCallbackWrapper(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, |
| void *user_data, LYS_INFORMAT *format, void (**free_module_data)(void *model_data, void *user_data)) |
| { |
| PyObject *arglist, *result, *data = NULL; |
| char *str = NULL; |
| |
| arglist = Py_BuildValue("(ssssO)", mod_name, mod_rev, submod_name, sub_rev, schemaCallbackData ? schemaCallbackData : Py_None); |
| if (!arglist) { |
| PyErr_Print(); |
| return NULL; |
| } |
| result = PyObject_CallObject(schemaCallback, arglist); |
| Py_DECREF(arglist); |
| |
| if (result) { |
| if (!PyArg_ParseTuple(result, "iU", format, &data)) { |
| Py_DECREF(result); |
| return NULL; |
| } |
| Py_DECREF(result); |
| *free_module_data = schemaCallbackModelFree; |
| str = strdup(PyUnicode_AsUTF8(data)); |
| Py_DECREF(data); |
| } |
| |
| return str; |
| } |
| |
| static PyObject * |
| setSchemaCallback(PyObject *self, PyObject *args, PyObject *keywds) |
| { |
| PyObject *clb = NULL, *data = NULL; |
| static char *kwlist[] = {"func", "priv", NULL}; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|O:setSchemaCallback", kwlist, &clb, &data)) { |
| return NULL; |
| } |
| |
| if (!clb || clb == Py_None) { |
| Py_XDECREF(schemaCallback); |
| Py_XDECREF(schemaCallbackData); |
| data = NULL; |
| } else if (!PyCallable_Check(clb)) { |
| PyErr_SetString(PyExc_TypeError, "The callback must be a function."); |
| return NULL; |
| } else { |
| Py_XDECREF(schemaCallback); |
| Py_XDECREF(schemaCallbackData); |
| |
| Py_INCREF(clb); |
| if (data) { |
| Py_INCREF(data); |
| } |
| } |
| nc_client_set_schema_callback(schemaCallbackWrapper, NULL); |
| schemaCallback = clb; |
| schemaCallbackData = data; |
| |
| Py_RETURN_NONE; |
| } |
| |
| static PyMethodDef netconf2Methods[] = { |
| {"setVerbosity", (PyCFunction)setVerbosity, METH_VARARGS | METH_KEYWORDS, |
| "setVerbosity(level)\n--\n\n" |
| "Set verbose level\n\n" |
| ":param level: Verbosity level (0 - errors, 1 - warnings, 2 - verbose, 3 - debug)\n" |
| ":type level: int\n" |
| ":returns: None\n"}, |
| {"setSyslog", (PyCFunction)setSyslog, METH_VARARGS | METH_KEYWORDS, |
| "setSyslog(enabled[, name=None][, option=LOG_PID][, facility=LOG_USER])\n--\n\n" |
| "Set application settings for syslog.\n\n" |
| ":param enabled: Flag to enable/disable logging into syslog.\n" |
| ":type enabled: bool\n" |
| ":param name: Identifier (program name is set by default).\n" |
| ":type name: string\n" |
| ":param option: ORed value of syslog options (LOG_PID by default).\n" |
| ":type option: int\n" |
| ":param facility: Type of the program logging the message (LOG_USER by default).\n" |
| ":type facility: int\n" |
| ":returns: None\n\n" |
| ".. seealso:: syslog.openlog\n"}, |
| {"setSearchpath", (PyCFunction)setSearchpath, METH_VARARGS | METH_KEYWORDS, |
| "setSearchpath(path)\n--\n\n" |
| "Set location where YANG/YIN schemas are searched and where the schemas\n" |
| "retrieved via <get-schema> opration are stored.\n\n" |
| ":param path: Search directory.\n" |
| ":type path: string\n" |
| ":returns: None\n"}, |
| {"setSchemaCallback", (PyCFunction)setSchemaCallback, METH_VARARGS | METH_KEYWORDS, |
| "Set schema search callaback.\n\n" |
| "setSchemaCallback(func, priv=None)\n" |
| "with func(str mod_name, str mod_rev, str submod_name, str submod_rev, priv)\n" |
| "callback returns tuple of format (e.g. LYS_IN_YANG) and string of the schema content.\n"}, |
| {NULL, NULL, 0, NULL} |
| }; |
| |
| static char netconf2Docs[] = |
| "NETCONF Protocol client-side implementation using libnetconf2\n" |
| "\n" |
| "netconf2 is a wrapper around libnetconf2 functions designed for NETCONF\n" |
| "clients. it provides a higher level API than the original libnetconf2 to\n" |
| "better fit the usage in Python.\n"; |
| |
| static struct PyModuleDef ncModule = { |
| PyModuleDef_HEAD_INIT, |
| "netconf2", |
| netconf2Docs, |
| -1, |
| netconf2Methods, |
| }; |
| |
| /* module initializer */ |
| PyMODINIT_FUNC |
| PyInit_netconf2(void) |
| { |
| void* clb; |
| PyObject *nc; |
| |
| /* import libyang Python module to have it available */ |
| if (!PyImport_ImportModule("yang")) { |
| return NULL; |
| } |
| |
| /* initiate libnetconf2 client part */ |
| nc_client_init(); |
| |
| /* set schema searchpath |
| * nc_client_set_schema_searchpath() |
| */ |
| |
| /* set print callback */ |
| clb = ly_get_log_clb(); |
| nc_set_print_clb(clb_print); |
| ly_set_log_clb(clb, 1); |
| |
| if (PyType_Ready(&ncSessionType) == -1) { |
| return NULL; |
| } |
| |
| ncSSHType.tp_new = PyType_GenericNew; |
| if (PyType_Ready(&ncSSHType) == -1) { |
| return NULL; |
| } |
| /* |
| ncTLSType.tp_new = PyType_GenericNew; |
| if (PyType_Ready(&ncTLSType) == -1) { |
| return NULL; |
| } |
| */ |
| ncErrType.tp_new = PyType_GenericNew; |
| if (PyType_Ready(&ncErrType) == -1) { |
| return NULL; |
| } |
| |
| /* create netconf as the Python module */ |
| nc = PyModule_Create(&ncModule); |
| if (nc == NULL) { |
| return NULL; |
| } |
| |
| Py_INCREF(&ncSSHType); |
| PyModule_AddObject(nc, "SSH", (PyObject *)&ncSSHType); |
| /* |
| Py_INCREF(&ncTLSType); |
| PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType); |
| */ |
| Py_INCREF(&ncErrType); |
| PyModule_AddObject(nc, "ReplyErrorInfo", (PyObject *)&ncErrType); |
| |
| Py_INCREF(&ncSessionType); |
| PyModule_AddObject(nc, "Session", (PyObject *)&ncSessionType); |
| |
| /* |
| Py_INCREF(&ncTLSType); |
| PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType); |
| */ |
| /* |
| PyModule_AddStringConstant(nc, "NETCONFv1_0", NETCONF_CAP_BASE10); |
| PyModule_AddStringConstant(nc, "NETCONFv1_1", NETCONF_CAP_BASE11); |
| PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH); |
| PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS); |
| */ |
| PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING); |
| PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP); |
| PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE); |
| |
| PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_STOP", NC_RPC_EDIT_ERROPT_STOP); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_CONTINUE", NC_RPC_EDIT_ERROPT_CONTINUE); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_ROLLBACK", NC_RPC_EDIT_ERROPT_ROLLBACK); |
| |
| PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_TESTSET", NC_RPC_EDIT_TESTOPT_TESTSET); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_SET", NC_RPC_EDIT_TESTOPT_SET); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_TEST", NC_RPC_EDIT_TESTOPT_TEST); |
| |
| PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_MERGE", NC_RPC_EDIT_DFLTOP_MERGE); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_REPLACE", NC_RPC_EDIT_DFLTOP_REPLACE); |
| PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_NONE", NC_RPC_EDIT_DFLTOP_NONE); |
| |
| /* init libnetconf exceptions for use in clb_print() */ |
| libnetconf2Error = PyErr_NewExceptionWithDoc("netconf2.Error", |
| "Error passed from the underlying libnetconf2 library.", |
| NULL, NULL); |
| Py_INCREF(libnetconf2Error); |
| PyModule_AddObject(nc, "Error", libnetconf2Error); |
| |
| libnetconf2Warning = PyErr_NewExceptionWithDoc("netconf2.Warning", |
| "Warning passed from the underlying libnetconf2 library.", |
| PyExc_Warning, NULL); |
| Py_INCREF(libnetconf2Warning); |
| PyModule_AddObject(nc, "Warning", libnetconf2Warning); |
| |
| libnetconf2ReplyError = PyErr_NewExceptionWithDoc("netconf2.ReplyError", |
| "NETCONF error returned from the server.", |
| NULL, NULL); |
| Py_INCREF(libnetconf2ReplyError); |
| PyModule_AddObject(nc, "ReplyError", libnetconf2ReplyError); |
| |
| return nc; |
| } |