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}/.."],