pyapi FEATURE get and get-config RPCs
- experimental interconnection with libyang python API
diff --git a/python/netconf.c b/python/netconf.c
index 3b80328..16e7991 100644
--- a/python/netconf.c
+++ b/python/netconf.c
@@ -215,6 +215,9 @@
PyModule_AddStringConstant(nc, "TRANSPORT_SSH", NETCONF_TRANSPORT_SSH);
PyModule_AddStringConstant(nc, "TRANSPORT_TLS", NETCONF_TRANSPORT_TLS);
*/
+ PyModule_AddIntConstant(nc, "DATASTORE_RUNNING", NC_DATASTORE_RUNNING);
+ PyModule_AddIntConstant(nc, "DATASTORE_STARTUP", NC_DATASTORE_STARTUP);
+ PyModule_AddIntConstant(nc, "DATASTORE_CANDIDATE", NC_DATASTORE_CANDIDATE);
/* init libnetconf exceptions for use in clb_print() */
libnetconf2Error = PyErr_NewExceptionWithDoc("netconf.Error",
diff --git a/python/rpc.c b/python/rpc.c
new file mode 100644
index 0000000..2d731df
--- /dev/null
+++ b/python/rpc.c
@@ -0,0 +1,151 @@
+/**
+ * @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 "netconf.h"
+#include "session.h"
+
+#define TIMEOUT_SEND 1000 /* 1 second */
+#define TIMEOUT_RECV 10000 /* 10 second */
+
+extern PyObject *libnetconf2Error;
+
+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 *
+process_reply_data(struct nc_reply *reply)
+{
+ struct lyd_node *data;
+ PyObject *result;
+
+ /* check the type of the received reply message */
+ if (reply->type != NC_RPL_DATA) {
+ if (reply->type == NC_RPL_ERROR) {
+ PyErr_SetString(libnetconf2Error, ((struct nc_reply_error*)reply)->err->message);
+ } else {
+ PyErr_SetString(libnetconf2Error, "Unexpected reply received.");
+ }
+ nc_reply_free(reply);
+ return NULL;
+ }
+
+ /* process the received data */
+ data = ((struct nc_reply_data*)reply)->data;
+ ((struct nc_reply_data*)reply)->data = NULL;
+ nc_reply_free(reply);
+
+ lyd_print_file(stdout, data, LYD_XML, LYP_FORMAT);
+
+ result = SWIG_NewPointerObj(data, SWIG_Python_TypeQuery("std::shared_ptr<Data_Node>*"), SWIG_POINTER_DISOWN);
+ if (!result) {
+ PyErr_SetString(libnetconf2Error, "Building Python object from lyd_node* failed");
+ }
+
+ return result;
+}
+
+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);
+}
diff --git a/python/rpc.h b/python/rpc.h
new file mode 100644
index 0000000..262325e
--- /dev/null
+++ b/python/rpc.h
@@ -0,0 +1,29 @@
+/**
+ * @file rpc.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief RPC functions for the Session object
+ *
+ * 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
+ */
+
+#ifndef PYRPC_H_
+#define PYRPC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+PyObject *ncRPCGet(ncSSHObject *self, PyObject *args, PyObject *keywords);
+PyObject *ncRPCGetConfig(ncSSHObject *self, PyObject *args, PyObject *keywords);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PYRPC_H_ */
diff --git a/python/session.c b/python/session.c
index 10e0b48..bd6e7df 100644
--- a/python/session.c
+++ b/python/session.c
@@ -22,13 +22,8 @@
#include "../src/config.h"
#include "netconf.h"
-
-typedef struct {
- PyObject_HEAD
- struct ly_ctx *ctx;
- unsigned int *ctx_counter;
- struct nc_session *session;
-} ncSessionObject;
+#include "session.h"
+#include "rpc.h"
char *
auth_password_clb(const char *UNUSED(username), const char *UNUSED(hostname), void *priv)
@@ -366,6 +361,15 @@
"newChannel()\n--\n\n"
"Create another NETCONF session on existing SSH session using separated SSH channel\n\n"
":returns: New netconf2.Session instance.\n"},
+ /* RPCs */
+ {"rpcGet", (PyCFunction)ncRPCGet, METH_VARARGS | METH_KEYWORDS,
+ "Send NETCONF <get> operation on the Session.\n\n"
+ "ncRPCGet(subtree=None, xpath=None)\n"
+ ":returns: Reply from the server.\n"},
+ {"rpcGetConfig", (PyCFunction)ncRPCGetConfig, METH_VARARGS | METH_KEYWORDS,
+ "Send NETCONF <get-config> operation on the Session.\n\n"
+ "ncRPCGetConfig(datastore, subtree=None, xpath=None)\n"
+ ":returns: Reply from the server.\n"},
{NULL} /* Sentinel */
};
diff --git a/python/session.h b/python/session.h
new file mode 100644
index 0000000..9dbb056
--- /dev/null
+++ b/python/session.h
@@ -0,0 +1,33 @@
+/**
+ * @file rpc.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief RPC functions for the Session object
+ *
+ * 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
+ */
+
+#ifndef PYSESSION_H_
+#define PYSESSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ struct ly_ctx *ctx;
+ unsigned int *ctx_counter;
+ struct nc_session *session;
+} ncSessionObject;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PYSESSION_H_ */
diff --git a/python/setup.py.in b/python/setup.py.in
index 8c9dd25..2a459d3 100644
--- a/python/setup.py.in
+++ b/python/setup.py.in
@@ -1,9 +1,16 @@
from distutils.core import setup, Extension
netconf2Module = Extension("netconf2",
- sources=["${CMAKE_CURRENT_SOURCE_DIR}/netconf.c", "${CMAKE_CURRENT_SOURCE_DIR}/session.c",
- "${CMAKE_CURRENT_SOURCE_DIR}/ssh.c", "${CMAKE_CURRENT_SOURCE_DIR}/tls.c"],
- depends=["${CMAKE_CURRENT_SOURCE_DIR}/netconf.h"],
+ sources=["${CMAKE_CURRENT_SOURCE_DIR}/netconf.c",
+ "${CMAKE_CURRENT_SOURCE_DIR}/session.c",
+ "${CMAKE_CURRENT_SOURCE_DIR}/ssh.c",
+ "${CMAKE_CURRENT_SOURCE_DIR}/tls.c",
+ "${CMAKE_CURRENT_SOURCE_DIR}/rpc.c"
+ ],
+ depends=["${CMAKE_CURRENT_SOURCE_DIR}/netconf.h",
+ "${CMAKE_CURRENT_SOURCE_DIR}/session.h",
+ "${CMAKE_CURRENT_COURCE_DIR}/rpc.h"
+ ],
libraries=["netconf2"],
extra_compile_args=["-Wall", "-I${CMAKE_CURRENT_SOURCE_DIR}/../src/", ],
extra_link_args=["-L${CMAKE_CURRENT_BINARY_DIR}/.."],