Skip to content

Commit

Permalink
Resolved issue #914
Browse files Browse the repository at this point in the history
  • Loading branch information
ygorelik authored and Abhi Keshav committed May 15, 2019
1 parent cd65076 commit 2087735
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Duplicate code in generated cisco-ios-xe Go bundle ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891))
* Memory leaks in YDK C++ core ([#899](https://github.com/CiscoDevNet/ydk-gen/issues/899))
* Memory leaks in YDK C++ gNMI Service component ([#902](https://github.com/CiscoDevNet/ydk-gen/issues/902))
* RPC execution stuck when NETCONF server closes session unexpectedly ([#914](https://github.com/CiscoDevNet/ydk-gen/issues/914))

##### Note
The solution for GitHub issue ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891)) changed model API. However all model bundles generated with YDK-Gen version 0.7.3 and later are still compatible with core YDK components.
Expand Down
1 change: 1 addition & 0 deletions sdk/cpp/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Duplicate code in generated cisco-ios-xe Go bundle ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891))
* Memory leaks in YDK C++ core ([#899](https://github.com/CiscoDevNet/ydk-gen/issues/899))
* Memory leaks in YDK C++ gNMI Service component ([#902](https://github.com/CiscoDevNet/ydk-gen/issues/902))
* RPC execution stuck when NETCONF server closes session unexpectedly ([#914](https://github.com/CiscoDevNet/ydk-gen/issues/914))

##### Note
The solution for GitHub issue ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891)) changed model API. However all model bundles generated with YDK-Gen version 0.7.3 and later are still compatible with core YDK components.
Expand Down
4 changes: 2 additions & 2 deletions sdk/cpp/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ set(libnetconf_headers_location ${CMAKE_CURRENT_BINARY_DIR}/project_libnetconf/h
include(ExternalProject)
ExternalProject_Add(project_libnetconf
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/project_libnetconf"
URL "https://github.com/abhikeshav/libnetconf/archive/master.zip"
CONFIGURE_COMMAND "./configure"
URL "https://github.com/ygorelik/libnetconf/archive/master.zip"
CONFIGURE_COMMAND ./configure
BUILD_COMMAND "make"
INSTALL_DIR "${libnetconf_location}"
INSTALL_COMMAND mkdir -p ${libnetconf_headers_location}/libnetconf && cp ${libnetconf_headers} ${libnetconf_headers_location}/libnetconf
Expand Down
1 change: 1 addition & 0 deletions sdk/cpp/core/src/netconf_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class NetconfClient
virtual std::string execute_payload(const std::string & payload) = 0;
virtual std::vector<std::string> get_capabilities() = 0;
virtual std::string get_hostname_port() = 0;
virtual void perform_session_check(const std::string & message) = 0;
};


Expand Down
5 changes: 5 additions & 0 deletions sdk/cpp/core/src/netconf_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ NetconfSession::~NetconfSession()
YLOG_INFO("Disconnected from device");
}

void NetconfSession::check_session_state()
{
client->perform_session_check("Netconf session is not connected");
}

vector<string> NetconfSession::get_capabilities() const
{
return server_capabilities;
Expand Down
52 changes: 37 additions & 15 deletions sdk/cpp/core/src/netconf_ssh_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
//
//////////////////////////////////////////////////////////////////


#include <iostream>
#include <sstream>
#include <stdio.h>
#include <signal.h>

#include <libnetconf.h>
#include <libnetconf_ssh.h>
Expand Down Expand Up @@ -100,6 +100,10 @@ NetconfSSHClient::NetconfSSHClient(

int NetconfSSHClient::connect()
{
// We expect write failures to occur, but we want to handle them where
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

session = nc_session_connect(hostname.c_str(), port, username.c_str(), NULL);
perform_session_check("Could not connect to " + hostname);
init_capabilities();
Expand Down Expand Up @@ -129,19 +133,21 @@ void NetconfSSHClient::init_capabilities()

string NetconfSSHClient::execute_payload(const string & payload)
{
perform_session_check("Could not execute payload. Not connected to " + hostname);
perform_session_check("Cannot not execute payload. Not connected to " + hostname);

nc_reply *reply;
nc_rpc *rpc = build_rpc_request(payload);

YLOG_DEBUG("Netconf SSH Client: sending rpc");
YLOG_DEBUG("Netconf SSH Client: sending RPC");
const nc_msgid msgid = nc_session_send_rpc(session, rpc);
if (msgid == NULL) {
YLOG_ERROR("Could not send payload {}", payload );
throw(YClientError{"Could not send payload"});
YLOG_ERROR("Failed to send RPC\n{}", payload );
throw(YClientError{"Failed to send RPC"});
}

YLOG_DEBUG("Netconf SSH Client: receiving rpc");
perform_session_check("Cannot not receive RPC reply. Not connected to " + hostname);

YLOG_DEBUG("Netconf SSH Client: receiving reply RPC");
NC_MSG_TYPE msg_type = nc_session_recv_reply(session, timeout, &reply);
YLOG_DEBUG("Netconf SSH Client: processing reply");
string receive_msg = process_rpc_reply(msg_type, reply);
Expand All @@ -163,8 +169,8 @@ nc_rpc* NetconfSSHClient::build_rpc_request(const string & payload)

if (rpc == NULL)
{
YLOG_ERROR("Could not build rpc payload: {}", payload );
throw(YClientError{"Could not build payload"});
YLOG_ERROR("Could not build RPC payload:\n{}", payload );
throw(YClientError{"Could not build RPC payload"});
}
return rpc;
}
Expand Down Expand Up @@ -192,7 +198,7 @@ string NetconfSSHClient::process_rpc_reply(int msg_type, const nc_rpc* reply)
throw(YClientError{"Connection timed out"});
case NC_MSG_UNKNOWN:
YLOG_ERROR("RPC error occurred");
throw(YClientError{"RPC error occured"});
throw(YClientError{"RPC error occurred"});
}
return {};
}
Expand Down Expand Up @@ -245,7 +251,7 @@ char* NetconfSSHClient::clb_set_interactive(const char *name, const char *instru
char* password_buffer = (char*) malloc(sizeof(char) * (password_string.size() + 1));
snprintf(password_buffer, password_string.size() + 1, "%s", password_string.c_str());

YLOG_DEBUG("looked up password for interactive: {}", password_buffer);
YLOG_DEBUG("Looked up password for interactive: {}", password_buffer);
return password_buffer;
}

Expand All @@ -255,7 +261,7 @@ char* NetconfSSHClient::clb_set_passphrase(const char *user_name, const char *ho
char* password_buffer = (char*) malloc(sizeof(char) * (password_string.size() + 1));
snprintf(password_buffer, password_string.size() + 1, "%s", password_string.c_str());

YLOG_DEBUG("looked up password for passphrase: {}", password_buffer);
YLOG_DEBUG("Looked up password for passphrase: {}", password_buffer);
return password_buffer;
}

Expand All @@ -267,12 +273,28 @@ int NetconfSSHClient::clb_ssh_host_authenticity_check(const char *hostname,

void NetconfSSHClient::perform_session_check(const string & message)
{
if (session == NULL)
int session_status = nc_session_get_status(session);
YLOG_DEBUG("NetconfSSHClient: NC session status: {}", session_status);
if (session_status != NC_SESSION_STATUS_WORKING && session_status != NC_SESSION_STATUS_CLOSING)
{
YLOG_ERROR(message.c_str());
throw(YClientError{message});
string msg = message + ": Session status: ";
if (session_status == NC_SESSION_STATUS_ERROR)
msg += "Undefined";
else if (session_status == NC_SESSION_STATUS_STARTUP)
msg += "Setting up";
else if (session_status == NC_SESSION_STATUS_CLOSED)
msg += "Closed";
else if (session_status == NC_SESSION_STATUS_DUMMY)
msg += "Dummy";
YLOG_ERROR(msg.c_str());
throw(YClientError{msg});
}
if (!nc_session_is_ssh_connected(session))
{
string msg = message + ": SSH session is not connected";
YLOG_ERROR(msg.c_str());
throw(YClientError{msg});
}
}


}
13 changes: 5 additions & 8 deletions sdk/cpp/core/src/netconf_ssh_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ namespace ydk

class NetconfSSHClient : public NetconfClient
{

public:
public:
static std::map<std::pair<std::string, std::string>, std::string> password_lookup;

public:
NetconfSSHClient(
std::string username,
std::string password,
Expand All @@ -69,7 +67,9 @@ class NetconfSSHClient : public NetconfClient
virtual std::vector<std::string> get_capabilities();
virtual std::string get_hostname_port();

private:
void perform_session_check(const std::string & message);

private:

static void clb_print(NC_VERB_LEVEL level, const char* msg);
static void clb_error_print(const char* tag, const char* type,
Expand All @@ -79,15 +79,12 @@ class NetconfSSHClient : public NetconfClient
static char* clb_set_password(const char* username, const char* hostname);
static char* clb_set_interactive(const char *name, const char *instruction, const char *prompt, int echo);
static char* clb_set_passphrase(const char *username, const char *hostname, const char *priv_key_file);
static int clb_ssh_host_authenticity_check(const char *hostname,
ssh_session session);
static int clb_ssh_host_authenticity_check(const char *hostname, ssh_session session);

nc_rpc* build_rpc_request(const std::string & payload);
std::string process_rpc_reply(int reply_type, const nc_reply* reply);
void init_capabilities();
void perform_session_check(const std::string & message);

private:
struct nc_session *session;

std::string username;
Expand Down
15 changes: 15 additions & 0 deletions sdk/cpp/core/src/netconf_tcp_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,21 @@ void NetconfTCPClient::check_timeout(CURLcode res, int for_recv, const char* fmt
}
}

void NetconfTCPClient::perform_session_check(const std::string & message)
{
// get socket descriptor
// CURLINFO_LASTSOCKET is deprecated after curl >= 7.45.0 (470272)
CURLcode res;
#if LIBCURL_VERSION_NUM >= 470272
res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd);
#else
res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd);
#endif

std::string msg = message + ": Unable to retrieve sockfd: {}";
check_ok(res, msg.c_str());
}

// from libcurl examples
/* Auxiliary function that waits on the socket. */
static int wait_on_socket(curl_socket_t sockfd, int for_recv, long timeout_ms)
Expand Down
2 changes: 2 additions & 0 deletions sdk/cpp/core/src/netconf_tcp_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class NetconfTCPClient : public NetconfClient
virtual std::vector<std::string> get_capabilities();
virtual std::string get_hostname_port();

void perform_session_check(const std::string & message);

private:
void initialize(const std::string& address, int port);
void initialize_curl(const std::string& address, int port);
Expand Down
4 changes: 2 additions & 2 deletions sdk/cpp/core/src/path_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1086,11 +1086,11 @@ class NetconfSession : public Session {

// This function is for YDK internal tests only
std::shared_ptr<path::DataNode> handle_action_rpc_output(const std::string & rpc_reply, path::DataNode& action_dn);
void check_session_state(); // throws YClientError exception when state is other than connected

private:
std::vector<std::string> get_yang_1_1_capabilities() const;
std::shared_ptr<DataNode> handle_crud_edit(
Rpc& rpc, Annotation ann) const;
std::shared_ptr<DataNode> handle_crud_edit(Rpc& rpc, Annotation ann) const;
std::shared_ptr<DataNode> handle_crud_read(Rpc& rpc) const;
std::shared_ptr<DataNode> handle_netconf_operation(Rpc& ydk_rpc) const;
void initialize_client_with_key(const std::string& address,
Expand Down
5 changes: 4 additions & 1 deletion sdk/cpp/core/tests/core_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ class TestClient: public ydk::NetconfClient
~TestClient()
{
}

void perform_session_check(const std::string & message)
{
return;
}
int connect()
{
return 0;
Expand Down
1 change: 1 addition & 0 deletions sdk/python/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* YDK netconf read fails when <data> tag has namespace prefix ([#799](https://github.com/CiscoDevNet/ydk-gen/issues/799))
* YDK return value of YANG action missing some attributes ([#871](https://github.com/CiscoDevNet/ydk-gen/issues/871))
* Duplicate code in generated cisco-ios-xe Go bundle ([#891](https://github.com/CiscoDevNet/ydk-gen/issues/891))
* YDK-Py docker container does not support Python3 ([#905](https://github.com/CiscoDevNet/ydk-gen/issues/905))
* Installation documentation for YDK-Py needs an update ([#906](https://github.com/CiscoDevNet/ydk-gen/issues/906))
* README file for YDK-Py repo is not rendering correctly ([#907](https://github.com/CiscoDevNet/ydk-gen/issues/907))

Expand Down

0 comments on commit 2087735

Please sign in to comment.