blob: 10ecb0053608193280237bd78aa4ed4fc4b49d52 [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 */
Radek Krejcic61f0b42017-06-07 13:21:41 +020019#include <syslog.h>
20
21#include "netconf.h"
22
23PyObject *libnetconf2Error;
24PyObject *libnetconf2Warning;
Radek Krejci0f3499a2017-10-13 13:39:36 +020025PyObject *libnetconf2ReplyError;
Radek Krejcic61f0b42017-06-07 13:21:41 +020026
27/* syslog usage flag */
28static int syslogEnabled = 0;
29
30static void
31clb_print(NC_VERB_LEVEL level, const char* msg)
32{
Radek Krejci0242d842017-10-13 16:12:16 +020033 switch (level) {
34 case NC_VERB_ERROR:
35 PyErr_SetString(libnetconf2Error, msg);
36 if (syslogEnabled) {
37 syslog(LOG_ERR, "%s", msg);
38 }
39 break;
40 case NC_VERB_WARNING:
41 if (syslogEnabled) {
42 syslog(LOG_WARNING, "%s", msg);
43 }
44 PyErr_WarnEx(libnetconf2Warning, msg, 1);
45 break;
46 case NC_VERB_VERBOSE:
47 if (syslogEnabled) {
48 syslog(LOG_INFO, "%s", msg);
49 }
50 break;
51 case NC_VERB_DEBUG:
52 if (syslogEnabled) {
53 syslog(LOG_DEBUG, "%s", msg);
54 }
55 break;
56 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020057}
58
59static PyObject *
60setSyslog(PyObject *self, PyObject *args, PyObject *keywds)
61{
Radek Krejci0242d842017-10-13 16:12:16 +020062 char* name = NULL;
63 static char* logname = NULL;
64 static int option = LOG_PID;
65 static int facility = LOG_USER;
Radek Krejcic61f0b42017-06-07 13:21:41 +020066
Radek Krejci0242d842017-10-13 16:12:16 +020067 static char *kwlist[] = {"enabled", "name", "option", "facility", NULL};
Radek Krejcic61f0b42017-06-07 13:21:41 +020068
Radek Krejci0242d842017-10-13 16:12:16 +020069 if (!PyArg_ParseTupleAndKeywords(args, keywds, "p|sii", kwlist, &syslogEnabled, &name, &option, &facility)) {
70 return NULL;
71 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020072
Radek Krejci0242d842017-10-13 16:12:16 +020073 if (name) {
74 free(logname);
75 logname = strdup(name);
76 } else {
77 free(logname);
78 logname = NULL;
79 }
80 closelog();
81 openlog(logname, option, facility);
Radek Krejcic61f0b42017-06-07 13:21:41 +020082
Radek Krejci0242d842017-10-13 16:12:16 +020083 Py_RETURN_NONE;
Radek Krejcic61f0b42017-06-07 13:21:41 +020084}
85
86static PyObject *
87setVerbosity(PyObject *self, PyObject *args, PyObject *keywds)
88{
Radek Krejci0242d842017-10-13 16:12:16 +020089 int level = NC_VERB_ERROR; /* 0 */
Radek Krejcic61f0b42017-06-07 13:21:41 +020090
Radek Krejci0242d842017-10-13 16:12:16 +020091 static char *kwlist[] = {"level", NULL};
Radek Krejcic61f0b42017-06-07 13:21:41 +020092
Radek Krejci0242d842017-10-13 16:12:16 +020093 if (!PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &level)) {
94 return NULL;
95 }
Radek Krejcic61f0b42017-06-07 13:21:41 +020096
Radek Krejci0242d842017-10-13 16:12:16 +020097 /* normalize level value if not from the enum */
98 if (level < NC_VERB_ERROR) {
99 nc_verbosity(NC_VERB_ERROR);
100 } else if (level > NC_VERB_DEBUG) {
101 nc_verbosity(NC_VERB_DEBUG);
102 } else {
103 nc_verbosity(level);
104 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200105
Radek Krejci0242d842017-10-13 16:12:16 +0200106 Py_RETURN_NONE;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200107}
108
Radek Krejci74ca4812017-09-21 13:03:33 +0200109static PyObject *
110setSearchpath(PyObject *self, PyObject *args, PyObject *keywds)
111{
112 char *path;
113 static char *kwlist[] = {"path", NULL};
114
115 if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", kwlist, &path)) {
116 return NULL;
117 }
118
119 if (nc_client_set_schema_searchpath(path)) {
120 return NULL;
121 }
122
123 Py_RETURN_NONE;
124}
125
Radek Krejcic61f0b42017-06-07 13:21:41 +0200126static PyMethodDef netconf2Methods[] = {
Radek Krejci0242d842017-10-13 16:12:16 +0200127 {"setVerbosity", (PyCFunction)setVerbosity, METH_VARARGS | METH_KEYWORDS,
128 "setVerbosity(level)\n--\n\n"
129 "Set verbose level\n\n"
130 ":param level: Verbosity level (0 - errors, 1 - warnings, 2 - verbose, 3 - debug)\n"
131 ":type level: int\n"
132 ":returns: None\n"},
133 {"setSyslog", (PyCFunction)setSyslog, METH_VARARGS | METH_KEYWORDS,
134 "setSyslog(enabled[, name=None][, option=LOG_PID][, facility=LOG_USER])\n--\n\n"
135 "Set application settings for syslog.\n\n"
136 ":param enabled: Flag to enable/disable logging into syslog.\n"
137 ":type enabled: bool\n"
138 ":param name: Identifier (program name is set by default).\n"
139 ":type name: string\n"
140 ":param option: ORed value of syslog options (LOG_PID by default).\n"
141 ":type option: int\n"
142 ":param facility: Type of the program logging the message (LOG_USER by default).\n"
143 ":type facility: int\n"
144 ":returns: None\n\n"
145 ".. seealso:: syslog.openlog\n"},
146 {"setSearchpath", (PyCFunction)setSearchpath, METH_VARARGS | METH_KEYWORDS,
147 "setSearchpath(path)\n--\n\n"
148 "Set location where YANG/YIN schemas are searched and where the schemas\n"
149 "retrieved via <get-schema> opration are stored.\n\n"
150 ":param path: Search directory.\n"
151 ":type path: string\n"
152 ":returns: None\n"},
153 {NULL, NULL, 0, NULL}
Radek Krejcic61f0b42017-06-07 13:21:41 +0200154};
155
Radek Krejcib03ebe42017-07-04 14:00:33 +0200156static char netconf2Docs[] =
157 "NETCONF Protocol client-side implementation using libnetconf2\n"
158 "\n"
159 "netconf2 is a wrapper around libnetconf2 functions designed for NETCONF\n"
160 "clients. it provides a higher level API than the original libnetconf2 to\n"
161 "better fit the usage in Python.\n";
162
Radek Krejcic61f0b42017-06-07 13:21:41 +0200163static struct PyModuleDef ncModule = {
Radek Krejci0242d842017-10-13 16:12:16 +0200164 PyModuleDef_HEAD_INIT,
165 "netconf2",
166 netconf2Docs,
167 -1,
168 netconf2Methods,
Radek Krejcic61f0b42017-06-07 13:21:41 +0200169};
170
171/* module initializer */
172PyMODINIT_FUNC
173PyInit_netconf2(void)
174{
Radek Krejcie205eec2018-02-15 16:44:03 +0100175 void* clb;
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 */
Radek Krejcieed7f872018-02-13 16:57:42 +0100179 if (!PyImport_ImportModule("yang")) {
Radek Krejci97211012017-10-13 16:05:45 +0200180 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 */
Radek Krejcie205eec2018-02-15 16:44:03 +0100191 clb = ly_get_log_clb();
Radek Krejci0242d842017-10-13 16:12:16 +0200192 nc_set_print_clb(clb_print);
Radek Krejcie205eec2018-02-15 16:44:03 +0100193 ly_set_log_clb(clb, 1);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200194
195 if (PyType_Ready(&ncSessionType) == -1) {
196 return NULL;
197 }
198
199 ncSSHType.tp_new = PyType_GenericNew;
200 if (PyType_Ready(&ncSSHType) == -1) {
201 return NULL;
202 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200203/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200204 ncTLSType.tp_new = PyType_GenericNew;
205 if (PyType_Ready(&ncTLSType) == -1) {
206 return NULL;
207 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200208*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200209 ncErrType.tp_new = PyType_GenericNew;
210 if (PyType_Ready(&ncErrType) == -1) {
211 return NULL;
212 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200213
Radek Krejci0242d842017-10-13 16:12:16 +0200214 /* create netconf as the Python module */
215 nc = PyModule_Create(&ncModule);
216 if (nc == NULL) {
217 return NULL;
218 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200219
220 Py_INCREF(&ncSSHType);
221 PyModule_AddObject(nc, "SSH", (PyObject *)&ncSSHType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200222/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200223 Py_INCREF(&ncTLSType);
224 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200225*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200226 Py_INCREF(&ncErrType);
227 PyModule_AddObject(nc, "ReplyErrorInfo", (PyObject *)&ncErrType);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200228
229 Py_INCREF(&ncSessionType);
230 PyModule_AddObject(nc, "Session", (PyObject *)&ncSessionType);
231
232/*
233 Py_INCREF(&ncTLSType);
234 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
235*/
236/*
237 PyModule_AddStringConstant(nc, "NETCONFv1_0", NETCONF_CAP_BASE10);
238 PyModule_AddStringConstant(nc, "NETCONFv1_1", NETCONF_CAP_BASE11);
239 PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH);
240 PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS);
241*/
Radek Krejcie0854c02017-10-10 21:11:29 +0200242 PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING);
243 PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP);
244 PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200245
Radek Krejcib0bf24c2018-02-07 16:40:27 +0100246 PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_STOP", NC_RPC_EDIT_ERROPT_STOP);
247 PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_CONTINUE", NC_RPC_EDIT_ERROPT_CONTINUE);
248 PyModule_AddIntConstant(nc, "RPC_EDIT_ERROPT_ROLLBACK", NC_RPC_EDIT_ERROPT_ROLLBACK);
249
250 PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_TESTSET", NC_RPC_EDIT_TESTOPT_TESTSET);
251 PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_SET", NC_RPC_EDIT_TESTOPT_SET);
252 PyModule_AddIntConstant(nc, "RPC_EDIT_TESTOPT_TEST", NC_RPC_EDIT_TESTOPT_TEST);
253
254 PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_MERGE", NC_RPC_EDIT_DFLTOP_MERGE);
255 PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_REPLACE", NC_RPC_EDIT_DFLTOP_REPLACE);
256 PyModule_AddIntConstant(nc, "RPC_EDIT_DFLTOP_NONE", NC_RPC_EDIT_DFLTOP_NONE);
257
Radek Krejci0242d842017-10-13 16:12:16 +0200258 /* init libnetconf exceptions for use in clb_print() */
259 libnetconf2Error = PyErr_NewExceptionWithDoc("netconf2.Error",
260 "Error passed from the underlying libnetconf2 library.",
261 NULL, NULL);
262 Py_INCREF(libnetconf2Error);
263 PyModule_AddObject(nc, "Error", libnetconf2Error);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200264
Radek Krejci0242d842017-10-13 16:12:16 +0200265 libnetconf2Warning = PyErr_NewExceptionWithDoc("netconf2.Warning",
266 "Warning passed from the underlying libnetconf2 library.",
267 PyExc_Warning, NULL);
268 Py_INCREF(libnetconf2Warning);
269 PyModule_AddObject(nc, "Warning", libnetconf2Warning);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200270
Radek Krejci0f3499a2017-10-13 13:39:36 +0200271 libnetconf2ReplyError = PyErr_NewExceptionWithDoc("netconf2.ReplyError",
Radek Krejci0242d842017-10-13 16:12:16 +0200272 "NETCONF error returned from the server.",
273 NULL, NULL);
Radek Krejci0f3499a2017-10-13 13:39:36 +0200274 Py_INCREF(libnetconf2ReplyError);
275 PyModule_AddObject(nc, "ReplyError", libnetconf2ReplyError);
276
Radek Krejci0242d842017-10-13 16:12:16 +0200277 return nc;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200278}