blob: e51e532164116bf250806c8a30fb628f5fd98fe0 [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 Krejci0242d842017-10-13 16:12:16 +0200175 PyObject *nc;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200176
Radek Krejci97211012017-10-13 16:05:45 +0200177 /* import libyang Python module to have it available */
178 if (!PyImport_ImportModule("libyang")) {
179 return NULL;
180 }
181
Radek Krejci0242d842017-10-13 16:12:16 +0200182 /* initiate libnetconf2 client part */
183 nc_client_init();
Radek Krejcic61f0b42017-06-07 13:21:41 +0200184
Radek Krejci0242d842017-10-13 16:12:16 +0200185 /* set schema searchpath
186 * nc_client_set_schema_searchpath()
187 */
Radek Krejcic61f0b42017-06-07 13:21:41 +0200188
Radek Krejci0242d842017-10-13 16:12:16 +0200189 /* set print callback */
190 nc_set_print_clb(clb_print);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200191
192 if (PyType_Ready(&ncSessionType) == -1) {
193 return NULL;
194 }
195
196 ncSSHType.tp_new = PyType_GenericNew;
197 if (PyType_Ready(&ncSSHType) == -1) {
198 return NULL;
199 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200200/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200201 ncTLSType.tp_new = PyType_GenericNew;
202 if (PyType_Ready(&ncTLSType) == -1) {
203 return NULL;
204 }
Radek Krejcia2429d12017-10-13 13:37:24 +0200205*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200206 ncErrType.tp_new = PyType_GenericNew;
207 if (PyType_Ready(&ncErrType) == -1) {
208 return NULL;
209 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200210
Radek Krejci0242d842017-10-13 16:12:16 +0200211 /* create netconf as the Python module */
212 nc = PyModule_Create(&ncModule);
213 if (nc == NULL) {
214 return NULL;
215 }
Radek Krejcic61f0b42017-06-07 13:21:41 +0200216
217 Py_INCREF(&ncSSHType);
218 PyModule_AddObject(nc, "SSH", (PyObject *)&ncSSHType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200219/*
Radek Krejcic61f0b42017-06-07 13:21:41 +0200220 Py_INCREF(&ncTLSType);
221 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
Radek Krejcia2429d12017-10-13 13:37:24 +0200222*/
Radek Krejci0f3499a2017-10-13 13:39:36 +0200223 Py_INCREF(&ncErrType);
224 PyModule_AddObject(nc, "ReplyErrorInfo", (PyObject *)&ncErrType);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200225
226 Py_INCREF(&ncSessionType);
227 PyModule_AddObject(nc, "Session", (PyObject *)&ncSessionType);
228
229/*
230 Py_INCREF(&ncTLSType);
231 PyModule_AddObject(nc, "TLS", (PyObject *)&ncTLSType);
232*/
233/*
234 PyModule_AddStringConstant(nc, "NETCONFv1_0", NETCONF_CAP_BASE10);
235 PyModule_AddStringConstant(nc, "NETCONFv1_1", NETCONF_CAP_BASE11);
236 PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH);
237 PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS);
238*/
Radek Krejcie0854c02017-10-10 21:11:29 +0200239 PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING);
240 PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP);
241 PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200242
Radek Krejci0242d842017-10-13 16:12:16 +0200243 /* init libnetconf exceptions for use in clb_print() */
244 libnetconf2Error = PyErr_NewExceptionWithDoc("netconf2.Error",
245 "Error passed from the underlying libnetconf2 library.",
246 NULL, NULL);
247 Py_INCREF(libnetconf2Error);
248 PyModule_AddObject(nc, "Error", libnetconf2Error);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200249
Radek Krejci0242d842017-10-13 16:12:16 +0200250 libnetconf2Warning = PyErr_NewExceptionWithDoc("netconf2.Warning",
251 "Warning passed from the underlying libnetconf2 library.",
252 PyExc_Warning, NULL);
253 Py_INCREF(libnetconf2Warning);
254 PyModule_AddObject(nc, "Warning", libnetconf2Warning);
Radek Krejcic61f0b42017-06-07 13:21:41 +0200255
Radek Krejci0f3499a2017-10-13 13:39:36 +0200256 libnetconf2ReplyError = PyErr_NewExceptionWithDoc("netconf2.ReplyError",
Radek Krejci0242d842017-10-13 16:12:16 +0200257 "NETCONF error returned from the server.",
258 NULL, NULL);
Radek Krejci0f3499a2017-10-13 13:39:36 +0200259 Py_INCREF(libnetconf2ReplyError);
260 PyModule_AddObject(nc, "ReplyError", libnetconf2ReplyError);
261
Radek Krejci0242d842017-10-13 16:12:16 +0200262 return nc;
Radek Krejcic61f0b42017-06-07 13:21:41 +0200263}