blob: 450bdb911e75bf71d4539e764ded52265c2d16dd [file] [log] [blame]
Radek Krejcie0854c02017-10-10 21:11:29 +02001/**
2 * @file ssh.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief SSH parameters management
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 <string.h>
20
21#include <libyang/libyang.h>
Radek Krejci97211012017-10-13 16:05:45 +020022#include <libyang/swigpyrun.h>
Radek Krejcie0854c02017-10-10 21:11:29 +020023
Radek Krejcic2074282017-11-06 15:57:22 +010024#include "../src/messages_p.h"
Radek Krejcie0854c02017-10-10 21:11:29 +020025#include "netconf.h"
26#include "session.h"
27
28#define TIMEOUT_SEND 1000 /* 1 second */
29#define TIMEOUT_RECV 10000 /* 10 second */
30
31extern PyObject *libnetconf2Error;
Radek Krejci0f3499a2017-10-13 13:39:36 +020032extern PyObject *libnetconf2ReplyError;
Radek Krejcie0854c02017-10-10 21:11:29 +020033
Radek Krejcib0bf24c2018-02-07 16:40:27 +010034static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"};
35const char *rpcedit_dfltop2str[] = {NULL, "merge", "replace", "none"};
36const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"};
37const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error", "rollback-on-error"};
38
Radek Krejcie0854c02017-10-10 21:11:29 +020039static struct nc_reply *
40rpc_send_recv(struct nc_session *session, struct nc_rpc *rpc)
41{
42 uint64_t msgid;
43 NC_MSG_TYPE msgtype;
44 struct nc_reply *reply;
45
46 msgtype = nc_send_rpc(session, rpc, TIMEOUT_SEND, &msgid);
47 if (msgtype == NC_MSG_ERROR) {
48 PyErr_SetString(PyExc_ConnectionError, "Failed to send a request.");
49 return NULL;
50 } else if (msgtype == NC_MSG_WOULDBLOCK) {
51 PyErr_SetString(PyExc_ConnectionError, "Sending a request timeouted.");
52 return NULL;
53 }
54
55recv_reply:
56 msgtype = nc_recv_reply(session, rpc, msgid, TIMEOUT_RECV, LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS, &reply);
57 if (msgtype == NC_MSG_ERROR) {
58 PyErr_SetString(PyExc_ConnectionError, "Failed to receive a reply.");
59 return NULL;
60 } else if (msgtype == NC_MSG_WOULDBLOCK) {
61 PyErr_SetString(PyExc_ConnectionError, "Receiving a reply timeouted.");
62 return NULL;
63 } else if (msgtype == NC_MSG_NOTIF) {
64 /* read again */
65 goto recv_reply;
66 } else if (msgtype == NC_MSG_REPLY_ERR_MSGID) {
67 /* unexpected message, try reading again to get the correct reply */
68 nc_reply_free(reply);
69 goto recv_reply;
70 }
71
72 return reply;
73}
74
75static PyObject *
Radek Krejci0f3499a2017-10-13 13:39:36 +020076err_reply_converter(struct nc_client_reply_error *reply)
77{
Radek Krejci97211012017-10-13 16:05:45 +020078 uint32_t i = 0;
79 ncErrObject *e;
80 PyObject *result;
Radek Krejci0f3499a2017-10-13 13:39:36 +020081
Radek Krejci97211012017-10-13 16:05:45 +020082 result = PyList_New(reply->count);
83 for (i = 0; i < reply->count; i++) {
84 e = PyObject_New(ncErrObject, &ncErrType);
85 e->ctx = reply->ctx;
86 e->err = malloc(sizeof *e->err);
87 memcpy(e->err, &reply->err[i], sizeof *e->err);
88 PyList_SET_ITEM(result, i, (PyObject*)e);
89 }
90 free(reply->err); /* pointers to the data were moved, so we are freeing just a container for the data */
Radek Krejci0f3499a2017-10-13 13:39:36 +020091 reply->err = NULL;
92
93 return (PyObject*)result;
94}
95
96#define RAISE_REPLY_ERROR(reply) PyErr_SetObject(libnetconf2ReplyError,err_reply_converter((struct nc_client_reply_error *)reply))
97
98static PyObject *
Radek Krejcie0854c02017-10-10 21:11:29 +020099process_reply_data(struct nc_reply *reply)
100{
Radek Krejci97211012017-10-13 16:05:45 +0200101 PyObject *result, *data = NULL, *module;
Radek Krejcie0854c02017-10-10 21:11:29 +0200102
103 /* check the type of the received reply message */
104 if (reply->type != NC_RPL_DATA) {
105 if (reply->type == NC_RPL_ERROR) {
Radek Krejci0f3499a2017-10-13 13:39:36 +0200106 RAISE_REPLY_ERROR(reply);
Radek Krejcie0854c02017-10-10 21:11:29 +0200107 } else {
108 PyErr_SetString(libnetconf2Error, "Unexpected reply received.");
109 }
Radek Krejci97211012017-10-13 16:05:45 +0200110 goto error;
Radek Krejcie0854c02017-10-10 21:11:29 +0200111 }
112
Radek Krejci97211012017-10-13 16:05:45 +0200113 //lyd_print_file(stdout, ((struct nc_reply_data*)reply)->data, LYD_XML, LYP_FORMAT);
114
Radek Krejcie0854c02017-10-10 21:11:29 +0200115 /* process the received data */
Radek Krejci97211012017-10-13 16:05:45 +0200116 data = SWIG_NewPointerObj(((struct nc_reply_data*)reply)->data, SWIG_Python_TypeQuery("lyd_node*"), 0);
117 if (!data) {
118 PyErr_SetString(libnetconf2Error, "Building Python object from data reply failed.");
119 goto error;
120 }
Radek Krejcie0854c02017-10-10 21:11:29 +0200121 ((struct nc_reply_data*)reply)->data = NULL;
Radek Krejci97211012017-10-13 16:05:45 +0200122
Radek Krejcieed7f872018-02-13 16:57:42 +0100123 module = PyImport_ImportModule("yang");
Radek Krejci97211012017-10-13 16:05:45 +0200124 if (module == NULL) {
Radek Krejcieed7f872018-02-13 16:57:42 +0100125 PyErr_SetString(libnetconf2Error, "Could not import libyang python module");
Radek Krejci97211012017-10-13 16:05:45 +0200126 goto error;
127 }
128
129 result = PyObject_CallMethod(module, "create_new_Data_Node", "(O)", data);
130 Py_DECREF(module);
131 Py_DECREF(data);
132 if (result == NULL) {
133 PyErr_SetString(libnetconf2Error, "Could not create Data_Node object.");
134 goto error;
135 }
136
Radek Krejcie0854c02017-10-10 21:11:29 +0200137 nc_reply_free(reply);
Radek Krejci97211012017-10-13 16:05:45 +0200138 return result;
Radek Krejcie0854c02017-10-10 21:11:29 +0200139
Radek Krejci97211012017-10-13 16:05:45 +0200140error:
141 Py_XDECREF(data);
142 nc_reply_free(reply);
143 return NULL;
Radek Krejcie0854c02017-10-10 21:11:29 +0200144}
145
146PyObject *
147ncRPCGet(ncSessionObject *self, PyObject *args, PyObject *keywords)
148{
149 const char *xml = NULL, *xpath = NULL;
150 static char *kwlist[] = {"subtree", "xpath", NULL};
151 struct nc_rpc *rpc;
152 struct nc_reply *reply;
153
154 if (!PyArg_ParseTupleAndKeywords(args, keywords, "|ss:ncRPCGet", kwlist, &xml, &xpath)) {
155 return NULL;
156 }
157
158 rpc = nc_rpc_get(xml ? xml : xpath, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
159 if (!rpc) {
160 return NULL;
161 }
162
163 reply = rpc_send_recv(self->session, rpc);
164 nc_rpc_free(rpc);
165 if (!reply) {
166 return NULL;
167 }
168
169 return process_reply_data(reply);
170}
171
172PyObject *
173ncRPCGetConfig(ncSessionObject *self, PyObject *args, PyObject *keywords)
174{
175 const char *xml = NULL, *xpath = NULL;
176 static char *kwlist[] = {"datastore", "subtree", "xpath", NULL};
177 struct nc_rpc *rpc;
178 struct nc_reply *reply;
179 NC_DATASTORE datastore;
180
181 if (!PyArg_ParseTupleAndKeywords(args, keywords, "i|ss:ncRPCGetConfig", kwlist, &datastore, &xml, &xpath)) {
182 return NULL;
183 }
184
185 rpc = nc_rpc_getconfig(datastore, xml ? xml : xpath, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST);
186 if (!rpc) {
187 return NULL;
188 }
189
190 reply = rpc_send_recv(self->session, rpc);
191 nc_rpc_free(rpc);
192 if (!reply) {
193 return NULL;
194 }
195
196 return process_reply_data(reply);
197}
Radek Krejcib0bf24c2018-02-07 16:40:27 +0100198
199PyObject *
200ncRPCEditConfig(ncSessionObject *self, PyObject *args, PyObject *keywords)
201{
202 static char *kwlist[] = {"datastore", "data", "defop", "testopt", "erropt", NULL};
203 struct lyd_node *data = NULL, *node, *content_tree = NULL;
204 char *content_str = NULL;
205 const struct lys_module *ietfnc;
206 NC_DATASTORE datastore;
207 NC_RPC_EDIT_DFLTOP defop = 0;
208 NC_RPC_EDIT_TESTOPT testopt = 0;
209 NC_RPC_EDIT_ERROPT erropt = 0;
210 PyObject *content_o = NULL, *py_lyd_node;
211 struct nc_rpc *rpc;
212 struct nc_reply *reply;
213
214 ietfnc = ly_ctx_get_module(self->ctx, "ietf-netconf", NULL, 1);
215 if (!ietfnc) {
216 PyErr_SetString(libnetconf2Error, "Missing \"ietf-netconf\" schema in the context.");
217 return NULL;
218 }
219
220 if (!PyArg_ParseTupleAndKeywords(args, keywords, "iO|iii:ncRPCEditConfig", kwlist, &datastore, &content_o, &defop, &testopt, &erropt)) {
221 return NULL;
222 }
223
224 if (PyUnicode_Check(content_o)) {
225 content_str = PyUnicode_AsUTF8(content_o);
Radek Krejci5da81062018-02-08 12:50:59 +0100226 } else if (SWIG_Python_GetSwigThis(content_o)) {
Radek Krejcib0bf24c2018-02-07 16:40:27 +0100227 py_lyd_node = PyObject_CallMethod(content_o, "C_lyd_node", NULL);
228 if (!SWIG_IsOK(SWIG_Python_ConvertPtr(py_lyd_node, (void**)&content_tree, SWIG_Python_TypeQuery("lyd_node *"), SWIG_POINTER_DISOWN))) {
229 PyErr_SetString(PyExc_TypeError, "Invalid object representing <edit-config> content. Data_Node is accepted.");
230 goto error;
231 }
232 } else if (content_o != Py_None) {
233 PyErr_SetString(PyExc_TypeError, "Invalid object representing <edit-config> content. String or Data_Node is accepted.");
234 goto error;
235 }
236
237 data = lyd_new(NULL, ietfnc, "edit-config");
238 node = lyd_new(data, ietfnc, "target");
239 node = lyd_new_leaf(node, ietfnc, ncds2str[datastore], NULL);
240 if (!node) {
241 goto error;
242 }
243
244 if (defop) {
245 node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[defop]);
246 if (!node) {
247 goto error;
248 }
249 }
250
251 if (testopt) {
252 node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[testopt]);
253 if (!node) {
254 goto error;
255 }
256 }
257
258 if (erropt) {
259 node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[erropt]);
260 if (!node) {
261 goto error;
262 }
263 }
264
265 if (content_str) {
266 if (!content_str[0] || (content_str[0] == '<')) {
267 node = lyd_new_anydata(data, ietfnc, "config", content_str, LYD_ANYDATA_SXML);
268 } else {
269 node = lyd_new_leaf(data, ietfnc, "url", content_str);
270 }
271 } else if (content_tree) {
272 node = lyd_new_anydata(data, ietfnc, "config", content_tree, LYD_ANYDATA_DATATREE);
273 }
274 if (!node) {
275 goto error;
276 }
277
278 rpc = nc_rpc_act_generic(data, NC_PARAMTYPE_FREE);
279 data = NULL;
280 if (!rpc) {
281 goto error;
282 }
283
284 reply = rpc_send_recv(self->session, rpc);
285 nc_rpc_free(rpc);
286 if (!reply) {
287 goto error;
288 }
289 if (reply->type != NC_RPL_OK) {
290 if (reply->type == NC_RPL_ERROR) {
291 RAISE_REPLY_ERROR(reply);
292 } else {
293 PyErr_SetString(libnetconf2Error, "Unexpected reply received.");
294 }
295 goto error;
296 }
297
298 Py_RETURN_NONE;
299
300error:
301 lyd_free(data);
302 return NULL;
303}