blob: 387f5ed9b1296afbb22e2e85467adbc48bac6537 [file] [log] [blame]
Radek Krejcic61f0b42017-06-07 13:21:41 +02001/**
2 * @file session.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief NETCONF session management in 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#include <structmember.h>
18
19/* standard headers */
20#include <string.h>
21#include <libyang/libyang.h>
22
23#include "netconf.h"
24
25typedef struct {
26 PyObject_HEAD
27 struct ly_ctx *ctx;
28 struct nc_session *session;
29} ncSessionObject;
30
31char *
32auth_password_clb(const char *UNUSED(username), const char *UNUSED(hostname), void *priv)
33{
34 /* password is provided as priv when setting up the callback */
35 return strdup((char *)priv);
36}
37char *
38auth_interactive_clb(const char *UNUSED(auth_name), const char *UNUSED(instruction), const char *UNUSED(prompt),
39 int UNUSED(echo), void *priv)
40{
41 /* password is provided as priv when setting up the callback */
42 return strdup((char *)priv);
43}
44
45char *
46auth_privkey_passphrase_clb(const char *privkey_path, void *priv)
47{
48 /* password is provided as priv when setting up the callback */
49 return strdup((char *)priv);
50}
51
52static void
53ncSessionFree(ncSessionObject *self)
54{
55 PyObject *err_type, *err_value, *err_traceback;
56
57 /* save the current exception state */
58 PyErr_Fetch(&err_type, &err_value, &err_traceback);
59
60 nc_session_free(self->session, NULL);
61 ly_ctx_destroy(self->ctx, NULL);
62
63 /* restore the saved exception state */
64 PyErr_Restore(err_type, err_value, err_traceback);
65
66 Py_TYPE(self)->tp_free((PyObject*)self);
67}
68
69static PyObject *
70ncSessionNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
71{
72 ncSessionObject *self;
73
74 self = (ncSessionObject *)type->tp_alloc(type, 0);
75 if (self != NULL) {
76 /* NULL initiation */
77 self->session = NULL;
78
79 /* prepare libyang context or use the one already present in the session */
Radek Krejci82441b02017-06-21 13:40:40 +020080 self->ctx = ly_ctx_new(SCHEMAS_DIR);
Radek Krejcic61f0b42017-06-07 13:21:41 +020081 if (!self->ctx) {
82 Py_DECREF(self);
83 return NULL;
84 }
85 }
86
87 return (PyObject *)self;
88}
89
90static int
91ncSessionInit(ncSessionObject *self, PyObject *args, PyObject *kwds)
92{
93 const char *host = NULL;
94 PyObject *transport = NULL;
95 unsigned short port = 0;
96 struct nc_session *session;
97
98 char *kwlist[] = {"host", "port", "transport", NULL};
99
100 /* Get input parameters */
101 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zHO", kwlist, &host, &port, &transport)) {
102 return -1;
103 }
104
105 /* connect */
106 if (transport && PyObject_TypeCheck(transport, &ncTLSType)) {
107 session = nc_connect_tls(host, port, self->ctx);
108 } else {
109 if (transport) {
110 /* set SSH parameters */
111 if (((ncSSHObject*)transport)->username) {
112 nc_client_ssh_set_username(PyUnicode_AsUTF8(((ncSSHObject*)transport)->username));
113 }
114 if (((ncSSHObject*)transport)->password) {
115 nc_client_ssh_set_auth_password_clb(&auth_password_clb,
116 (void *)PyUnicode_AsUTF8(((ncSSHObject*)transport)->password));
117 nc_client_ssh_set_auth_interactive_clb(&auth_interactive_clb,
118 (void *)PyUnicode_AsUTF8(((ncSSHObject*)transport)->password));
119 nc_client_ssh_set_auth_privkey_passphrase_clb(&auth_privkey_passphrase_clb,
120 (void *)PyUnicode_AsUTF8(((ncSSHObject*)transport)->password));
121 }
122 }
123
124 /* create connection */
125 session = nc_connect_ssh(host, port, self->ctx);
126
127 /* cleanup */
128 if (transport) {
129 if (((ncSSHObject*)transport)->username) {
130 nc_client_ssh_set_username(NULL);
131 }
132 if (((ncSSHObject*)transport)->password) {
133 nc_client_ssh_set_auth_password_clb(NULL, NULL);
134 nc_client_ssh_set_auth_interactive_clb(NULL, NULL);
135 nc_client_ssh_set_auth_privkey_passphrase_clb(NULL, NULL);
136 }
137 }
138 }
139
140 /* check the result */
141 if (!session) {
142 return -1;
143 }
144
145 /* replace the previous (if any) data in the session object */
146 nc_session_free(self->session, NULL);
147 self->session = session;
148
149 return 0;
150}
151
152static PyObject *
153ncSessionStr(ncSessionObject *self)
154{
155 return PyUnicode_FromFormat("NETCONF Session %u to %s:%u (%lu references)", nc_session_get_id(self->session),
156 nc_session_get_host(self->session), nc_session_get_port(self->session),
157 ((PyObject*)(self))->ob_refcnt);
158}
159
160/*
161 * tp_methods callbacks held by ncSessionMethods[]
162 */
163
164/*
165 * tp_getset callbacs held by ncSessionGetSetters[]
166 */
167#if 0
168static PyObject *
169ncSessionGetSTatus(ncSessionObject *self, void *closure)
170{
171 NC_STATUS s;
172
173 s = nc_session_get_status(self->session);
174 switch(s) {
175 case NC_STATUS_ERR:
176 /* exception */
177 return NULL;
178 }
179 return PyUnicode_FromFormat("%u", nc_session_get_id(self->session));
180}
181#endif
182
183static PyObject *
184ncSessionGetId(ncSessionObject *self, void *closure)
185{
186 return PyUnicode_FromFormat("%u", nc_session_get_id(self->session));
187}
188
189static PyObject *
190ncSessionGetHost(ncSessionObject *self, void *closure)
191{
192 return PyUnicode_FromString(nc_session_get_host(self->session));
193}
194
195static PyObject *
196ncSessionGetPort(ncSessionObject *self, void *closure)
197{
198 return PyUnicode_FromFormat("%u", nc_session_get_port(self->session));
199}
200
201static PyObject *
202ncSessionGetUser(ncSessionObject *self, void *closure)
203{
204 return PyUnicode_FromString(nc_session_get_username(self->session));
205}
206
207static PyObject *
208ncSessionGetTransport(ncSessionObject *self, void *closure)
209{
210 NC_TRANSPORT_IMPL ti = nc_session_get_ti(self->session);
211 switch (ti) {
212 case NC_TI_LIBSSH:
213 return PyUnicode_FromString("SSH");
214 case NC_TI_OPENSSL:
215 return PyUnicode_FromString("TLS");
216 default:
217 return PyUnicode_FromString("unknown");
218 }
219}
220
221static PyObject *
222ncSessionGetCapabilities(ncSessionObject *self, void *closure)
223{
224 PyObject *list;
225 const char * const *cpblts;
226 ssize_t pos;
227
228 cpblts = nc_session_get_cpblts(self->session);
229 if (cpblts == NULL) {
230 return (NULL);
231 }
232
233 list = PyList_New(0);
234 for(pos = 0; cpblts[pos]; ++pos) {
235 PyList_Append(list, PyUnicode_FromString(cpblts[pos]));
236 }
237
238 return list;
239}
240
241static PyObject *
242ncSessionGetVersion(ncSessionObject *self, void *closure)
243{
244 if (nc_session_get_version(self->session)) {
245 return PyUnicode_FromString("1.1");
246 } else {
247 return PyUnicode_FromString("1.0");
248 }
249}
250
251/*
252 * Callback structures
253 */
254
255static PyGetSetDef ncSessionGetSetters[] = {
256 {"id", (getter)ncSessionGetId, NULL, "NETCONF Session id.", NULL},
257 {"host", (getter)ncSessionGetHost, NULL, "Host where the NETCONF Session is connected.", NULL},
258 {"port", (getter)ncSessionGetPort, NULL, "Port number where the NETCONF Session is connected.", NULL},
259 {"user", (getter)ncSessionGetUser, NULL, "Username of the user connected with the NETCONF Session.", NULL},
260 {"transport", (getter)ncSessionGetTransport, NULL, "Transport protocol used for the NETCONF Session.", NULL},
261 {"version", (getter)ncSessionGetVersion, NULL, "NETCONF Protocol version used for the NETCONF Session.", NULL},
262 {"capabilities", (getter)ncSessionGetCapabilities, NULL, "Capabilities of the NETCONF Session.", NULL},
263 {NULL} /* Sentinel */
264};
265
266static PyMemberDef ncSessionMembers[] = {
267 {NULL} /* Sentinel */
268};
269
270static PyMethodDef ncSessionMethods[] = {
271 {NULL} /* Sentinel */
272};
273
274PyDoc_STRVAR(sessionDoc,
275 "Create the NETCONF Session\n"
276 "netconf2.Session([host, port, transport]) -> connect to the NETCONF server\n"
277 "Arguments:\n"
278 "\thost - address of the server (default localhost)\n"
279 "\tport - port where to connect to (default 830)\n"
280 "\ttransport - NETCONF transport protocol (default SSH), supported SSH or TLS\n");
281
282PyTypeObject ncSessionType = {
283 PyVarObject_HEAD_INIT(NULL, 0)
284 "netconf2.Session", /* tp_name */
285 sizeof(ncSessionObject), /* tp_basicsize */
286 0, /* tp_itemsize */
287 (destructor)ncSessionFree, /* tp_dealloc */
288 0, /* tp_print */
289 0, /* tp_getattr */
290 0, /* tp_setattr */
291 0, /* tp_reserved */
292 (reprfunc)ncSessionStr, /* tp_repr */
293 0, /* tp_as_number */
294 0, /* tp_as_sequence */
295 0, /* tp_as_mapping */
296 0, /* tp_hash */
297 0, /* tp_call */
298 (reprfunc)ncSessionStr, /* tp_str */
299 0, /* tp_getattro */
300 0, /* tp_setattro */
301 0, /* tp_as_buffer */
302 Py_TPFLAGS_DEFAULT |
303 Py_TPFLAGS_BASETYPE, /* tp_flags */
304 sessionDoc, /* tp_doc */
305 0, /* tp_traverse */
306 0, /* tp_clear */
307 0, /* tp_richcompare */
308 0, /* tp_weaklistoffset */
309 0, /* tp_iter */
310 0, /* tp_iternext */
311 ncSessionMethods, /* tp_methods */
312 ncSessionMembers, /* tp_members */
313 ncSessionGetSetters, /* tp_getset */
314 0, /* tp_base */
315 0, /* tp_dict */
316 0, /* tp_descr_get */
317 0, /* tp_descr_set */
318 0, /* tp_dictoffset */
319 (initproc)ncSessionInit, /* tp_init */
320 0, /* tp_alloc */
321 ncSessionNew, /* tp_new */
322};
323