blob: a14daff9b9f5355595dbcadcc020c27200464eed [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{
34 switch (level) {
35 case NC_VERB_ERROR:
36 PyErr_SetString(libnetconf2Error, msg);
37 if (syslogEnabled) {syslog(LOG_ERR, "%s", msg);}
38 break;
39 case NC_VERB_WARNING:
40 if (syslogEnabled) {syslog(LOG_WARNING, "%s", msg);}
41 PyErr_WarnEx(libnetconf2Warning, msg, 1);
42 break;
43 case NC_VERB_VERBOSE:
44 if (syslogEnabled) {syslog(LOG_INFO, "%s", msg);}
45 break;
46 case NC_VERB_DEBUG:
47 if (syslogEnabled) {syslog(LOG_DEBUG, "%s", msg);}
48 break;
49 }
50}
51
52static PyObject *
53setSyslog(PyObject *self, PyObject *args, PyObject *keywds)
54{
55 char* name = NULL;
56 static char* logname = NULL;
57 static int option = LOG_PID;
Radek Krejcib03ebe42017-07-04 14:00:33 +020058 static int facility = LOG_USER;
Radek Krejcic61f0b42017-06-07 13:21:41 +020059
60 static char *kwlist[] = {"enabled", "name", "option", "facility", NULL};
61
62 if (! PyArg_ParseTupleAndKeywords(args, keywds, "p|sii", kwlist, &syslogEnabled, &name, &option, &facility)) {
63 return NULL;
64 }
65
66 if (name) {
67 free(logname);
68 logname = strdup(name);
69 } else {
70 free(logname);
71 logname = NULL;
72 }
73 closelog();
74 openlog(logname, option, facility);
75
76 Py_RETURN_NONE;
77}
78
79static PyObject *
80setVerbosity(PyObject *self, PyObject *args, PyObject *keywds)
81{
82 int level = NC_VERB_ERROR; /* 0 */
83
84 static char *kwlist[] = {"level", NULL};
85
86 if (! PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &level)) {
87 return NULL;
88 }
89
90 /* normalize level value if not from the enum */
91 if (level < NC_VERB_ERROR) {
92 nc_verbosity(NC_VERB_ERROR);
93 } else if (level > NC_VERB_DEBUG) {
94 nc_verbosity(NC_VERB_DEBUG);
95 } else {
96 nc_verbosity(level);
97 }
98
99 Py_RETURN_NONE;
100}
101
Radek Krejci74ca4812017-09-21 13:03:33 +0200102static PyObject *
103setSearchpath(PyObject *self, PyObject *args, PyObject *keywds)
104{
105 char *path;
106 static char *kwlist[] = {"path", NULL};
107
108 if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", kwlist, &path)) {
109 return NULL;
110 }
111
112 if (nc_client_set_schema_searchpath(path)) {
113 return NULL;
114 }
115
116 Py_RETURN_NONE;
117}
118
Radek Krejcic61f0b42017-06-07 13:21:41 +0200119static PyMethodDef netconf2Methods[] = {
Radek Krejcib03ebe42017-07-04 14:00:33 +0200120 {"setVerbosity", (PyCFunction)setVerbosity, METH_VARARGS | METH_KEYWORDS,
121 "setVerbosity(level)\n--\n\n"
122 "Set verbose level\n\n"
123 ":param level: Verbosity level (0 - errors, 1 - warnings, 2 - verbose, 3 - debug)\n"
124 ":type level: int\n"
125 ":returns: None\n"},
126 {"setSyslog", (PyCFunction)setSyslog, METH_VARARGS | METH_KEYWORDS,
127 "setSyslog(enabled[, name=None][, option=LOG_PID][, facility=LOG_USER])\n--\n\n"
128 "Set application settings for syslog.\n\n"
129 ":param enabled: Flag to enable/disable logging into syslog.\n"
130 ":type enabled: bool\n"
131 ":param name: Identifier (program name is set by default).\n"
132 ":type name: string\n"
133 ":param option: ORed value of syslog options (LOG_PID by default).\n"
134 ":type option: int\n"
135 ":param facility: Type of the program logging the message (LOG_USER by default).\n"
136 ":type facility: int\n"
137 ":returns: None\n\n"
138 ".. seealso:: syslog.openlog\n"},
Radek Krejci74ca4812017-09-21 13:03:33 +0200139 {"setSearchpath", (PyCFunction)setSearchpath, METH_VARARGS | METH_KEYWORDS,
140 "setSearchpath(path)\n--\n\n"
141 "Set location where YANG/YIN schemas are searched and where the schemas\n"
142 "retrieved via <get-schema> opration are stored.\n\n"
143 ":param path: Search directory.\n"
144 ":type path: string\n"
145 ":returns: None\n"},
Radek Krejcic61f0b42017-06-07 13:21:41 +0200146 {NULL, NULL, 0, NULL}
147};
148
Radek Krejcib03ebe42017-07-04 14:00:33 +0200149static char netconf2Docs[] =
150 "NETCONF Protocol client-side implementation using libnetconf2\n"
151 "\n"
152 "netconf2 is a wrapper around libnetconf2 functions designed for NETCONF\n"
153 "clients. it provides a higher level API than the original libnetconf2 to\n"
154 "better fit the usage in Python.\n";
155
Radek Krejcic61f0b42017-06-07 13:21:41 +0200156static struct PyModuleDef ncModule = {
157 PyModuleDef_HEAD_INIT,
158 "netconf2",
Radek Krejcib03ebe42017-07-04 14:00:33 +0200159 netconf2Docs,
Radek Krejcic61f0b42017-06-07 13:21:41 +0200160 -1,
161 netconf2Methods,
162};
163
164/* module initializer */
165PyMODINIT_FUNC
166PyInit_netconf2(void)
167{
168 PyObject *nc;
169
Radek Krejci97211012017-10-13 16:05:45 +0200170 /* import libyang Python module to have it available */
171 if (!PyImport_ImportModule("libyang")) {
172 return NULL;
173 }
174
Radek Krejcic61f0b42017-06-07 13:21:41 +0200175 /* initiate libnetconf2 client part */
176 nc_client_init();
177
178 /* set schema searchpath
179 * nc_client_set_schema_searchpath()
180 */
181
182 /* set print callback */
183 nc_set_print_clb(clb_print);
184
185 if (PyType_Ready(&ncSessionType) == -1) {
186 return NULL;
187 }
188
189 ncSSHType.tp_new = PyType_GenericNew;
190 if (PyType_Ready(&ncSSHType) == -1) {
191 return NULL;
192 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200193/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200194 ncTLSType.tp_new = PyType_GenericNew;
195 if (PyType_Ready(&ncTLSType) == -1) {
196 return NULL;
197 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200198*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200199 ncErrType.tp_new = PyType_GenericNew;
200 if (PyType_Ready(&ncErrType) == -1) {
201 return NULL;
202 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200203
204 /* create netconf as the Python module */
205 nc = PyModule_Create(&ncModule);
206 if (nc == NULL) {
207 return NULL;
208 }
209
210 Py_INCREF(&ncSSHType);
211 PyModule_AddObject(nc, "SSH", (PyObject *)&ncSSHType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200212/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200213 Py_INCREF(&ncTLSType);
214 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200215*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200216 Py_INCREF(&ncErrType);
217 PyModule_AddObject(nc, "ReplyErrorInfo", (PyObject *)&ncErrType);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200218
219 Py_INCREF(&ncSessionType);
220 PyModule_AddObject(nc, "Session", (PyObject *)&ncSessionType);
221
222/*
223 Py_INCREF(&ncTLSType);
224 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
225*/
226/*
227 PyModule_AddStringConstant(nc, "NETCONFv1_0", NETCONF_CAP_BASE10);
228 PyModule_AddStringConstant(nc, "NETCONFv1_1", NETCONF_CAP_BASE11);
229 PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH);
230 PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS);
231*/
Radek Krejcie0854c02017-10-10 21:11:29 +0200232 PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING);
233 PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP);
234 PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200235
236 /* init libnetconf exceptions for use in clb_print() */
Radek Krejci0f3499a2017-10-13 13:39:36 +0200237 libnetconf2Error = PyErr_NewExceptionWithDoc("netconf2.Error",
Radek Krejcic61f0b42017-06-07 13:21:41 +0200238 "Error passed from the underlying libnetconf2 library.",
239 NULL, NULL);
240 Py_INCREF(libnetconf2Error);
241 PyModule_AddObject(nc, "Error", libnetconf2Error);
242
Radek Krejci0f3499a2017-10-13 13:39:36 +0200243 libnetconf2Warning = PyErr_NewExceptionWithDoc("netconf2.Warning",
Radek Krejcic61f0b42017-06-07 13:21:41 +0200244 "Warning passed from the underlying libnetconf2 library.",
245 PyExc_Warning, NULL);
246 Py_INCREF(libnetconf2Warning);
247 PyModule_AddObject(nc, "Warning", libnetconf2Warning);
248
Radek Krejci0f3499a2017-10-13 13:39:36 +0200249 libnetconf2ReplyError = PyErr_NewExceptionWithDoc("netconf2.ReplyError",
250 "NETCONF error returned from the server.",
251 NULL, NULL);
252 Py_INCREF(libnetconf2ReplyError);
253 PyModule_AddObject(nc, "ReplyError", libnetconf2ReplyError);
254
Radek Krejcic61f0b42017-06-07 13:21:41 +0200255 return nc;
256}