I have created and initialized a custom NETCONF server using libnetconf2 and libyang within a C++ program. My intention is to access this server and pull the configuration data using ncclient in a Python program. I have been able to successfully connect to the server, but anytime I try to send an rpc <get>
or <get-config>
command I get the following error
Request could not be completed because the requested operation is not supported by this implementation.
I have confirmed that I loaded the default YANG modules into my context so I am at a loss of why the commands are not being recognized. I have included the relevant parts of my code, my custom XML file, and my server capabilities below.
My Python function to connect to the server.
def connect_to_netconf_server():
host = '127.0.0.1'
port = 830
username = 'user'
password = 'password'
# Define the NETCONF filter for the <get> operation
filter = '''
<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:ex="urn:example">
<ex:device>
<ex:hostname/>
<ex:ip-address/>
</ex:device>
</filter>
'''
# Establish a NETCONF session
with manager.connect(host=host,
port=port,
username=username,
password=password,
hostkey_verify=False,
allow_agent=False,
look_for_keys=False) as m:
print("Server Capabilities:")
for capability in m.server_capabilities:
print(capability)
# Perform the <get>/<get-config> operation
try:
running_config = m.get(filter=filter)
print("Running Configuration:")
print(running_config)
except Exception as e:
print(f"Error during <get> operation: {e}")
try:
print(m.get_config(source='running').data_xml)
except Exception as e:
print(f"Error during <get-config> operation: {e}")
My C++ code to create a new context, load default libnetconf2 modules, and load a custom YANG module.
// Initialize libyang context with required netconf yang models
if (ly_ctx_new("libnetconf2/modules", 0, &m_ctx) != 0) {
printf("Failed to create a new libyang context.");
lyd_free_all(tree);
tearDown();
return;
}
if (nc_server_init_ctx(&m_ctx) != 0) {
printf("Failed to initialize libyang context.");
lyd_free_all(tree);
tearDown();
return;
}
else {
printf("Libyang context created and initialized.");
if (nc_server_config_load_modules(&m_ctx) != 0) {
printf("ERROR loading modules required for configuration of the server.");
lyd_free_all(tree);
tearDown();
return;
}
else{
const char *yang_module_content =
R"(module example
{
namespace "urn:example";
prefix "ex";
container device {
leaf hostname {
type string;
}
leaf ip-address {
type string;
}
}
})";
// Load your custom YANG module into the context
struct lys_module *custom_module = NULL;
lys_parse_mem(m_ctx, yang_module_content, LYS_IN_YANG, &custom_module);
if (!custom_module) {
printf("Failed to parse YANG module.");
lyd_free_all(tree);
tearDown();
return;
}
printf("Successfully added custom yang module to server");
}
if (nc_server_config_setup_path(m_ctx, "network_almanac.xml") != 0) {
printf("ERROR configuring server data.");
lyd_free_all(tree);
tearDown();
return;
}
// configure server based on yang ssh data above
if(nc_server_config_setup_data(tree) != 0){
printf("ERROR configuring server with YANG data.");
lyd_free_all(tree);
tearDown();
return;
}
// Initialize NETCONF server
if (nc_server_init() != 0) {
printf("ERROR occurred while initializing the server.");
lyd_free_all(tree);
tearDown();
return;
}
else if(nc_client_init() != 0){
printf("ERROR occurred while initializing the client.");
lyd_free_all(tree);
tearDown();
return;
}
else {
printf("NETCONF server and client have been initialized.");
lyd_free_all(tree);
m_pollSession = nc_ps_new();
if (!m_pollSession) {
printf("Failed to create poll session.");
tearDown();
return;
}
}
}
XML File
<ex:device xmlns:ex="urn:example">
<ex:hostname>example-host</ex:hostname>
<ex:ip-address>192.168.0.1</ex:ip-address>
</ex:device>
Server Capabilities
urn:ietf:params:netconf:base:1.0
urn:ietf:params:netconf:base:1.1
urn:ietf:params:netconf:capability:writable-running:1.0
urn:ietf:params:netconf:capability:candidate:1.0
urn:ietf:params:netconf:capability:confirmed-commit:1.1
urn:ietf:params:netconf:capability:rollback-on-error:1.0
urn:ietf:params:netconf:capability:validate:1.1
urn:ietf:params:netconf:capability:startup:1.0
urn:ietf:params:netconf:capability:xpath:1.0
urn:ietf:params:xml:ns:yang:ietf-yang-metadata?module=ietf-yang-metadata&revision=2016-
08-05
urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15
urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2013-07-15
urn:ietf:params:netconf:capability:yang-library:1.1?revision=2019-01-04&content-id=254
urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2013-09-
29&features=writable-running,candidate,confirmed-commit,rollback-on-
error,validate,startup,url,xpath
urn:ietf:params:xml:ns:yang:ietf-netconf-acm?module=ietf-netconf-acm&revision=2018-02-
14
urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-
monitoring&revision=2010-10-04
urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name?module=ietf-x509-cert-to-
name&revision=2014-12-10
urn:ietf:params:xml:ns:yang:iana-crypt-hash?module=iana-crypt-hash&revision=2014-04-
04&features=crypt-hash-md5,crypt-hash-sha-256,crypt-hash-sha-512
urn:example?module=example
I am able to see that my custom module (urn:example?module=example
) was successfully added to the server and from my understanding the capabilities such as urn:ietf:params:netconf:base:1.0
, urn:ietf:params:netconf:base:1.1
, urn:ietf:params:netconf:capability:writable-running:1.0
should allow rpc commands such as <get>
and <get-config>
. I have not been able to find many resources regarding this error and would really appreciate any help regarding my implementation and/or understanding of NETCONF. I have also verified the behavior is the same when connecting to the server using the libnetconf2 library from a C++ program as well.