blob: 741a1eaee1bce83436f01b4517e5db3473d3c8e0 [file] [log] [blame]
Radek Krejcic61f0b42017-06-07 13:21:41 +02001/**
2 * @file netconf.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Python3 bindings for libnetconf2 (client-side)
5 *
6 * Copyright (c) 2017 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15/* Python API header */
16#include <Python.h>
17
18/* standard headers */
19#include <nc_client.h>
20#include <syslog.h>
21
22#include "netconf.h"
23
24PyObject *libnetconf2Error;
25PyObject *libnetconf2Warning;
Radek Krejci0f3499a2017-10-13 13:39:36 +020026PyObject *libnetconf2ReplyError;
Radek Krejcic61f0b42017-06-07 13:21:41 +020027
28/* syslog usage flag */
29static int syslogEnabled = 0;
30
31static void
32clb_print(NC_VERB_LEVEL level, const char* msg)
33{
Radek Krejci0242d842017-10-13 16:12:16 +020034 switch (level) {
35 case NC_VERB_ERROR:
36 PyErr_SetString(libnetconf2Error, msg);
37 if (syslogEnabled) {
38 syslog(LOG_ERR, "%s", msg);
39 }
40 break;
41 case NC_VERB_WARNING:
42 if (syslogEnabled) {
43 syslog(LOG_WARNING, "%s", msg);
44 }
45 PyErr_WarnEx(libnetconf2Warning, msg, 1);
46 break;
47 case NC_VERB_VERBOSE:
48 if (syslogEnabled) {
49 syslog(LOG_INFO, "%s", msg);
50 }
51 break;
52 case NC_VERB_DEBUG:
53 if (syslogEnabled) {
54 syslog(LOG_DEBUG, "%s", msg);
55 }
56 break;
57 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020058}
59
60static PyObject *
61setSyslog(PyObject *self, PyObject *args, PyObject *keywds)
62{
Radek Krejci0242d842017-10-13 16:12:16 +020063 char* name = NULL;
64 static char* logname = NULL;
65 static int option = LOG_PID;
66 static int facility = LOG_USER;
Radek Krejcic61f0b42017-06-07 13:21:41 +020067
Radek Krejci0242d842017-10-13 16:12:16 +020068 static char *kwlist[] = {"enabled", "name", "option", "facility", NULL};
Radek Krejcic61f0b42017-06-07 13:21:41 +020069
Radek Krejci0242d842017-10-13 16:12:16 +020070 if (!PyArg_ParseTupleAndKeywords(args, keywds, "p|sii", kwlist, &syslogEnabled, &name, &option, &facility)) {
71 return NULL;
72 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020073
Radek Krejci0242d842017-10-13 16:12:16 +020074 if (name) {
75 free(logname);
76 logname = strdup(name);
77 } else {
78 free(logname);
79 logname = NULL;
80 }
81 closelog();
82 openlog(logname, option, facility);
Radek Krejcic61f0b42017-06-07 13:21:41 +020083
Radek Krejci0242d842017-10-13 16:12:16 +020084 Py_RETURN_NONE;
Radek Krejcic61f0b42017-06-07 13:21:41 +020085}
86
87static PyObject *
88setVerbosity(PyObject *self, PyObject *args, PyObject *keywds)
89{
Radek Krejci0242d842017-10-13 16:12:16 +020090 int level = NC_VERB_ERROR; /* 0 */
Radek Krejcic61f0b42017-06-07 13:21:41 +020091
Radek Krejci0242d842017-10-13 16:12:16 +020092 static char *kwlist[] = {"level", NULL};
Radek Krejcic61f0b42017-06-07 13:21:41 +020093
Radek Krejci0242d842017-10-13 16:12:16 +020094 if (!PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &level)) {
95 return NULL;
96 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020097
Radek Krejci0242d842017-10-13 16:12:16 +020098 /* normalize level value if not from the enum */
99 if (level < NC_VERB_ERROR) {
100 nc_verbosity(NC_VERB_ERROR);
101 } else if (level > NC_VERB_DEBUG) {
102 nc_verbosity(NC_VERB_DEBUG);
103 } else {
104 nc_verbosity(level);
105 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200106
Radek Krejci0242d842017-10-13 16:12:16 +0200107 Py_RETURN_NONE;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200108}
109
Radek Krejci74ca4812017-09-21 13:03:33 +0200110static PyObject *
111setSearchpath(PyObject *self, PyObject *args, PyObject *keywds)
112{
113 char *path;
114 static char *kwlist[] = {"path", NULL};
115
116 if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", kwlist, &path)) {
117 return NULL;
118 }
119
120 if (nc_client_set_schema_searchpath(path)) {
121 return NULL;
122 }
123
124 Py_RETURN_NONE;
125}
126
Radek Krejcic61f0b42017-06-07 13:21:41 +0200127static PyMethodDef netconf2Methods[] = {
Radek Krejci0242d842017-10-13 16:12:16 +0200128 {"setVerbosity", (PyCFunction)setVerbosity, METH_VARARGS | METH_KEYWORDS,
129 "setVerbosity(level)\n--\n\n"
130 "Set verbose level\n\n"
131 ":param level: Verbosity level (0 - errors, 1 - warnings, 2 - verbose, 3 - debug)\n"
132 ":type level: int\n"
133 ":returns: None\n"},
134 {"setSyslog", (PyCFunction)setSyslog, METH_VARARGS | METH_KEYWORDS,
135 "setSyslog(enabled[, name=None][, option=LOG_PID][, facility=LOG_USER])\n--\n\n"
136 "Set application settings for syslog.\n\n"
137 ":param enabled: Flag to enable/disable logging into syslog.\n"
138 ":type enabled: bool\n"
139 ":param name: Identifier (program name is set by default).\n"
140 ":type name: string\n"
141 ":param option: ORed value of syslog options (LOG_PID by default).\n"
142 ":type option: int\n"
143 ":param facility: Type of the program logging the message (LOG_USER by default).\n"
144 ":type facility: int\n"
145 ":returns: None\n\n"
146 ".. seealso:: syslog.openlog\n"},
147 {"setSearchpath", (PyCFunction)setSearchpath, METH_VARARGS | METH_KEYWORDS,
148 "setSearchpath(path)\n--\n\n"
149 "Set location where YANG/YIN schemas are searched and where the schemas\n"
150 "retrieved via <get-schema> opration are stored.\n\n"
151 ":param path: Search directory.\n"
152 ":type path: string\n"
153 ":returns: None\n"},
154 {NULL, NULL, 0, NULL}
Radek Krejcic61f0b42017-06-07 13:21:41 +0200155};
156
Radek Krejcib03ebe42017-07-04 14:00:33 +0200157static char netconf2Docs[] =
158 "NETCONF Protocol client-side implementation using libnetconf2\n"
159 "\n"
160 "netconf2 is a wrapper around libnetconf2 functions designed for NETCONF\n"
161 "clients. it provides a higher level API than the original libnetconf2 to\n"
162 "better fit the usage in Python.\n";
163
Radek Krejcic61f0b42017-06-07 13:21:41 +0200164static struct PyModuleDef ncModule = {
Radek Krejci0242d842017-10-13 16:12:16 +0200165 PyModuleDef_HEAD_INIT,
166 "netconf2",
167 netconf2Docs,
168 -1,
169 netconf2Methods,
Radek Krejcic61f0b42017-06-07 13:21:41 +0200170};
171
172/* module initializer */
173PyMODINIT_FUNC
174PyInit_netconf2(void)
175{
Radek Krejci0242d842017-10-13 16:12:16 +0200176 PyObject *nc;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200177
Radek Krejci97211012017-10-13 16:05:45 +0200178 /* import libyang Python module to have it available */
179 if (!PyImport_ImportModule("libyang")) {
180 return NULL;
181 }
182
Radek Krejci0242d842017-10-13 16:12:16 +0200183 /* initiate libnetconf2 client part */
184 nc_client_init();
Radek Krejcic61f0b42017-06-07 13:21:41 +0200185
Radek Krejci0242d842017-10-13 16:12:16 +0200186 /* set schema searchpath
187 * nc_client_set_schema_searchpath()
188 */
Radek Krejcic61f0b42017-06-07 13:21:41 +0200189
Radek Krejci0242d842017-10-13 16:12:16 +0200190 /* set print callback */
191 nc_set_print_clb(clb_print);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200192
193 if (PyType_Ready(&ncSessionType) == -1) {
194 return NULL;
195 }
196
197 ncSSHType.tp_new = PyType_GenericNew;
198 if (PyType_Ready(&ncSSHType) == -1) {
199 return NULL;
200 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200201/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200202 ncTLSType.tp_new = PyType_GenericNew;
203 if (PyType_Ready(&ncTLSType) == -1) {
204 return NULL;
205 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200206*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200207 ncErrType.tp_new = PyType_GenericNew;
208 if (PyType_Ready(&ncErrType) == -1) {
209 return NULL;
210 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200211
Radek Krejci0242d842017-10-13 16:12:16 +0200212 /* create netconf as the Python module */
213 nc = PyModule_Create(&ncModule);
214 if (nc == NULL) {
215 return NULL;
216 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200217
218 Py_INCREF(&ncSSHType);
219 PyModule_AddObject(nc, "SSH", (PyObject *)&ncSSHType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200220/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200221 Py_INCREF(&ncTLSType);
222 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200223*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200224 Py_INCREF(&ncErrType);
225 PyModule_AddObject(nc, "ReplyErrorInfo", (PyObject *)&ncErrType);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200226
227 Py_INCREF(&ncSessionType);
228 PyModule_AddObject(nc, "Session", (PyObject *)&ncSessionType);
229
230/*
231 Py_INCREF(&ncTLSType);
232 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
233*/
234/*
235 PyModule_AddStringConstant(nc, "NETCONFv1_0", NETCONF_CAP_BASE10);
236 PyModule_AddStringConstant(nc, "NETCONFv1_1", NETCONF_CAP_BASE11);
237 PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH);
238 PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS);
239*/
Radek Krejcie0854c02017-10-10 21:11:29 +0200240 PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING);
241 PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP);
242 PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200243
Radek Krejci0242d842017-10-13 16:12:16 +0200244 /* init libnetconf exceptions for use in clb_print() */
245 libnetconf2Error = PyErr_NewExceptionWithDoc("netconf2.Error",
246 "Error passed from the underlying libnetconf2 library.",
247 NULL, NULL);
248 Py_INCREF(libnetconf2Error);
249 PyModule_AddObject(nc, "Error", libnetconf2Error);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200250
Radek Krejci0242d842017-10-13 16:12:16 +0200251 libnetconf2Warning = PyErr_NewExceptionWithDoc("netconf2.Warning",
252 "Warning passed from the underlying libnetconf2 library.",
253 PyExc_Warning, NULL);
254 Py_INCREF(libnetconf2Warning);
255 PyModule_AddObject(nc, "Warning", libnetconf2Warning);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200256
Radek Krejci0f3499a2017-10-13 13:39:36 +0200257 libnetconf2ReplyError = PyErr_NewExceptionWithDoc("netconf2.ReplyError",
Radek Krejci0242d842017-10-13 16:12:16 +0200258 "NETCONF error returned from the server.",
259 NULL, NULL);
Radek Krejci0f3499a2017-10-13 13:39:36 +0200260 Py_INCREF(libnetconf2ReplyError);
261 PyModule_AddObject(nc, "ReplyError", libnetconf2ReplyError);
262
Radek Krejci0242d842017-10-13 16:12:16 +0200263 return nc;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200264}