| /** |
| * @file ssh.c |
| * @author Radek Krejci <rkrejci@cesnet.cz> |
| * @brief SSH parameters management |
| * |
| * Copyright (c) 2017 CESNET, z.s.p.o. |
| * |
| * This source code is licensed under BSD 3-Clause License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://opensource.org/licenses/BSD-3-Clause |
| */ |
| |
| /* Python API header */ |
| #include <Python.h> |
| |
| /* standard headers */ |
| #include <string.h> |
| |
| #include <libyang/libyang.h> |
| #include <libyang/swigpyrun.h> |
| |
| #include "../src/messages_p.h" |
| #include "netconf.h" |
| #include "session.h" |
| |
| #define TIMEOUT_SEND 1000 /* 1 second */ |
| #define TIMEOUT_RECV 10000 /* 10 second */ |
| |
| extern PyObject *libnetconf2Error; |
| extern PyObject *libnetconf2ReplyError; |
| |
| static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"}; |
| const char *rpcedit_dfltop2str[] = {NULL, "merge", "replace", "none"}; |
| const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"}; |
| const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error", "rollback-on-error"}; |
| |
| static struct nc_reply * |
| rpc_send_recv(struct nc_session *session, struct nc_rpc *rpc) |
| { |
| uint64_t msgid; |
| NC_MSG_TYPE msgtype; |
| struct nc_reply *reply; |
| |
| msgtype = nc_send_rpc(session, rpc, TIMEOUT_SEND, &msgid); |
| if (msgtype == NC_MSG_ERROR) { |
| PyErr_SetString(PyExc_ConnectionError, "Failed to send a request."); |
| return NULL; |
| } else if (msgtype == NC_MSG_WOULDBLOCK) { |
| PyErr_SetString(PyExc_ConnectionError, "Sending a request timeouted."); |
| return NULL; |
| } |
| |
| recv_reply: |
| msgtype = nc_recv_reply(session, rpc, msgid, TIMEOUT_RECV, LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS, &reply); |
| if (msgtype == NC_MSG_ERROR) { |
| PyErr_SetString(PyExc_ConnectionError, "Failed to receive a reply."); |
| return NULL; |
| } else if (msgtype == NC_MSG_WOULDBLOCK) { |
| PyErr_SetString(PyExc_ConnectionError, "Receiving a reply timeouted."); |
| return NULL; |
| } else if (msgtype == NC_MSG_NOTIF) { |
| /* read again */ |
| goto recv_reply; |
| } else if (msgtype == NC_MSG_REPLY_ERR_MSGID) { |
| /* unexpected message, try reading again to get the correct reply */ |
| nc_reply_free(reply); |
| goto recv_reply; |
| } |
| |
| return reply; |
| } |
| |
| static PyObject * |
| err_reply_converter(struct nc_client_reply_error *reply) |
| { |
| uint32_t i = 0; |
| ncErrObject *e; |
| PyObject *result; |
| |
| result = PyList_New(reply->count); |
| for (i = 0; i < reply->count; i++) { |
| e = PyObject_New(ncErrObject, &ncErrType); |
| e->ctx = reply->ctx; |
| e->err = malloc(sizeof *e->err); |
| memcpy(e->err, &reply->err[i], sizeof *e->err); |
| PyList_SET_ITEM(result, i, (PyObject*)e); |
| } |
| free(reply->err); /* pointers to the data were moved, so we are freeing just a container for the data */ |
| reply->err = NULL; |
| |
| return (PyObject*)result; |
| } |
| |
| #define RAISE_REPLY_ERROR(reply) PyErr_SetObject(libnetconf2ReplyError,err_reply_converter((struct nc_client_reply_error *)reply)) |
| |
| static PyObject * |
| process_reply_data(struct nc_reply *reply) |
| { |
| PyObject *result, *data = NULL, *module; |
| |
| /* check the type of the received reply message */ |
| if (reply->type != NC_RPL_DATA) { |
| if (reply->type == NC_RPL_ERROR) { |
| RAISE_REPLY_ERROR(reply); |
| } else { |
| PyErr_SetString(libnetconf2Error, "Unexpected reply received."); |
| } |
| goto error; |
| } |
| |
| //lyd_print_file(stdout, ((struct nc_reply_data*)reply)->data, LYD_XML, LYP_FORMAT); |
| |
| /* process the received data */ |
| data = SWIG_NewPointerObj(((struct nc_reply_data*)reply)->data, SWIG_Python_TypeQuery("lyd_node*"), 0); |
| if (!data) { |
| PyErr_SetString(libnetconf2Error, "Building Python object from data reply failed."); |
| goto error; |
| } |
| ((struct nc_reply_data*)reply)->data = NULL; |
| |
| module = PyImport_ImportModule("yang"); |
| if (module == NULL) { |
| PyErr_SetString(libnetconf2Error, "Could not import libyang python module"); |
| goto error; |
| } |
| |
| result = PyObject_CallMethod(module, "create_new_Data_Node", "(O)", data); |
| Py_DECREF(module); |
| Py_DECREF(data); |
| if (result == NULL) { |
| PyErr_SetString(libnetconf2Error, "Could not create Data_Node object."); |
| goto error; |
| } |
| |
| nc_reply_free(reply); |
| return result; |
| |
| error: |
| Py_XDECREF(data); |
| nc_reply_free(reply); |
| return NULL; |
| } |
| |
| PyObject * |
| ncRPCGet(ncSessionObject *self, PyObject *args, PyObject *keywords) |
| { |
| const char *xml = NULL, *xpath = NULL; |
| static char *kwlist[] = {"subtree", "xpath", NULL}; |
| struct nc_rpc *rpc; |
| struct nc_reply *reply; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywords, "|ss:ncRPCGet", kwlist, &xml, &xpath)) { |
| return NULL; |
| } |
| |
| rpc = nc_rpc_get(xml ? xml : xpath, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST); |
| if (!rpc) { |
| return NULL; |
| } |
| |
| reply = rpc_send_recv(self->session, rpc); |
| nc_rpc_free(rpc); |
| if (!reply) { |
| return NULL; |
| } |
| |
| return process_reply_data(reply); |
| } |
| |
| PyObject * |
| ncRPCGetConfig(ncSessionObject *self, PyObject *args, PyObject *keywords) |
| { |
| const char *xml = NULL, *xpath = NULL; |
| static char *kwlist[] = {"datastore", "subtree", "xpath", NULL}; |
| struct nc_rpc *rpc; |
| struct nc_reply *reply; |
| NC_DATASTORE datastore; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywords, "i|ss:ncRPCGetConfig", kwlist, &datastore, &xml, &xpath)) { |
| return NULL; |
| } |
| |
| rpc = nc_rpc_getconfig(datastore, xml ? xml : xpath, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST); |
| if (!rpc) { |
| return NULL; |
| } |
| |
| reply = rpc_send_recv(self->session, rpc); |
| nc_rpc_free(rpc); |
| if (!reply) { |
| return NULL; |
| } |
| |
| return process_reply_data(reply); |
| } |
| |
| PyObject * |
| ncRPCEditConfig(ncSessionObject *self, PyObject *args, PyObject *keywords) |
| { |
| static char *kwlist[] = {"datastore", "data", "defop", "testopt", "erropt", NULL}; |
| struct lyd_node *data = NULL, *node, *content_tree = NULL; |
| char *content_str = NULL; |
| const struct lys_module *ietfnc; |
| NC_DATASTORE datastore; |
| NC_RPC_EDIT_DFLTOP defop = 0; |
| NC_RPC_EDIT_TESTOPT testopt = 0; |
| NC_RPC_EDIT_ERROPT erropt = 0; |
| PyObject *content_o = NULL, *py_lyd_node; |
| struct nc_rpc *rpc; |
| struct nc_reply *reply; |
| |
| ietfnc = ly_ctx_get_module(self->ctx, "ietf-netconf", NULL, 1); |
| if (!ietfnc) { |
| PyErr_SetString(libnetconf2Error, "Missing \"ietf-netconf\" schema in the context."); |
| return NULL; |
| } |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywords, "iO|iii:ncRPCEditConfig", kwlist, &datastore, &content_o, &defop, &testopt, &erropt)) { |
| return NULL; |
| } |
| |
| if (PyUnicode_Check(content_o)) { |
| content_str = PyUnicode_AsUTF8(content_o); |
| } else if (SWIG_Python_GetSwigThis(content_o)) { |
| py_lyd_node = PyObject_CallMethod(content_o, "C_lyd_node", NULL); |
| if (!SWIG_IsOK(SWIG_Python_ConvertPtr(py_lyd_node, (void**)&content_tree, SWIG_Python_TypeQuery("lyd_node *"), SWIG_POINTER_DISOWN))) { |
| PyErr_SetString(PyExc_TypeError, "Invalid object representing <edit-config> content. Data_Node is accepted."); |
| goto error; |
| } |
| } else if (content_o != Py_None) { |
| PyErr_SetString(PyExc_TypeError, "Invalid object representing <edit-config> content. String or Data_Node is accepted."); |
| goto error; |
| } |
| |
| data = lyd_new(NULL, ietfnc, "edit-config"); |
| node = lyd_new(data, ietfnc, "target"); |
| node = lyd_new_leaf(node, ietfnc, ncds2str[datastore], NULL); |
| if (!node) { |
| goto error; |
| } |
| |
| if (defop) { |
| node = lyd_new_leaf(data, ietfnc, "default-operation", rpcedit_dfltop2str[defop]); |
| if (!node) { |
| goto error; |
| } |
| } |
| |
| if (testopt) { |
| node = lyd_new_leaf(data, ietfnc, "test-option", rpcedit_testopt2str[testopt]); |
| if (!node) { |
| goto error; |
| } |
| } |
| |
| if (erropt) { |
| node = lyd_new_leaf(data, ietfnc, "error-option", rpcedit_erropt2str[erropt]); |
| if (!node) { |
| goto error; |
| } |
| } |
| |
| if (content_str) { |
| if (!content_str[0] || (content_str[0] == '<')) { |
| node = lyd_new_anydata(data, ietfnc, "config", content_str, LYD_ANYDATA_SXML); |
| } else { |
| node = lyd_new_leaf(data, ietfnc, "url", content_str); |
| } |
| } else if (content_tree) { |
| node = lyd_new_anydata(data, ietfnc, "config", content_tree, LYD_ANYDATA_DATATREE); |
| } |
| if (!node) { |
| goto error; |
| } |
| |
| rpc = nc_rpc_act_generic(data, NC_PARAMTYPE_FREE); |
| data = NULL; |
| if (!rpc) { |
| goto error; |
| } |
| |
| reply = rpc_send_recv(self->session, rpc); |
| nc_rpc_free(rpc); |
| if (!reply) { |
| goto error; |
| } |
| if (reply->type != NC_RPL_OK) { |
| if (reply->type == NC_RPL_ERROR) { |
| RAISE_REPLY_ERROR(reply); |
| } else { |
| PyErr_SetString(libnetconf2Error, "Unexpected reply received."); |
| } |
| goto error; |
| } |
| |
| Py_RETURN_NONE; |
| |
| error: |
| lyd_free(data); |
| return NULL; |
| } |