How to simulate the "Command Timed out" while provisioning?

I want to simulate a negative scenario while provisioning some commands on configuration mode.

Ex: “Command Timed out.”

The way I have implemented now is connecting and subscribing to cdb for a particular container/leaf. (In my case its /system/contact) and then try to provision one of the command with commit. The Network device has to wait for an indefinite amount of time, after that it should fail with “Command Time out”.

Actual Behaviour:

admin@NE-1% set system contact abc
[edit]
admin@NE-1% commit
Commit complete.

Expected Behaviour:

admin@NE-1% set system contact abc
[edit]
admin@NE-1% commit
Command Timed out

The solution I tried:

I tried decreasing the queryTimeout in confd.conf

 <capi>
       <newSessionTimeout>PT120S</newSessionTimeout>
        <queryTimeout>PT120S</queryTimeout>
 </capi>

and Increasing the wait time after commit on the device (sleep(600)) to be more than queryTimeout.

Code snippet:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/poll.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <system.h>
#ifndef MAX_ARGS
#define MAX_ARGS 32
#endif

#ifndef BUFSIZ
#define BUFSIZ 1024
#endif

/CONFD and custom libraries/

#include <confd_lib.h>
#include <confd_dp.h>
#include <confd_cdb.h>
#include <confd_maapi.h>
#include <confd_cdb.h>

/* Our daemon context as a global variable */

static struct confd_daemon_ctx *dctx;
struct listElement *list_map = NULL;
int debuglevel = CONFD_SILENT;

int main(int argc, char *argv){

struct sockaddr_in addr;
int debuglevel = CONFD_SILENT;
int subsock;
int status;
int spoint;
confd_init("cdb_listen_att", stderr, debuglevel);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(CONFD_PORT);

if (confd_load_schemas((struct sockaddr*)&addr, sizeof (struct sockaddr_in)) != CONFD_OK) {
    confd_fatal("Failed to load schemas from confd\n");
}

if ((dctx = confd_init_daemon("cdb_listen_att")) == NULL){
    confd_fatal("Failed to initialize confdlib\n");
}

 if ((subsock = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) {
    confd_fatal("Failed to open ctlsocket\n");
}

if (cdb_connect(subsock, CDB_SUBSCRIPTION_SOCKET, (struct sockaddr*)&addr,
                  sizeof (struct sockaddr_in)) < 0)
    confd_fatal("Sub: Failed to cdb_connect() to confd \n");

if ((status = cdb_subscribe(subsock, 3, sys_ns, &spoint, "/system/contact/"))
    != CONFD_OK) {
    fprintf(stderr, "Terminate: subscribe %d\n", status);
    exit(0);
}
if (cdb_subscribe_done(subsock) != CONFD_OK)
    confd_fatal("cdb_subscribe_done() failed");
printf("Subscription point = %d\n", spoint);


while (1) {
    struct pollfd set[1];

    set[0].fd = subsock;
    set[0].events = POLLIN;
    set[0].revents = 0;


    if (poll(set, sizeof(set)/sizeof(*set), -1) < 0) {
        perror("Poll failed:");
        continue;
    }
    else{
  	printf("Wait Time for the Command Timeout\n");
        sleep(600);
  }
    /* Check for I/O */
    if (set[0].revents & POLLIN) {
        int sub_points[1];
        int reslen;
        if ((status = cdb_read_subscription_socket(subsock,
                                                   &sub_points[0],
                                                   &reslen)) != CONFD_OK)
            exit(status);



        if ((status = cdb_sync_subscription_socket(subsock,
                                                   CDB_DONE_PRIORITY))
            != CONFD_OK) {
            exit(status);
        }
    }
}

}

yang for system :

container system {
description
“System related configurations”;
leaf contact {
type string;
description
“Contact information for this system”;
}
}

P.S: You can use any provisioning command. To reproduce in your setup. the only line you need to change in code snippet is.

if ((status = cdb_subscribe(subsock, 3, sys_ns, &spoint, “/system/contact/”))

change the namespace and the container/leaf where the delay should happen for commit, eventually occurring Command Timed out.

See:
int cdb_set_timeout(int sock, int timeout_secs);
in the confd_lib_cdb(3) man pages, and:
/confdConfig/cdb/clientTimeout
in the confd.conf(5) man pages.

More of the same in this post from a few years back:

1 Like

A “normal” (i.e. not using CDB_SUB_RUNNING_TWOPHASE with cdb_subscribe2()) CDB subscriber is notified when the changes have already been committed to CDB, it can’t affect the success of the commit, only the time it takes to complete. The timeout that @cohult mentions is the way to protect against a “misbehaving” subscriber, but the commit will succeed even if the timeout fires.

1 Like

Hi @cohult,

Does this mean I need to use

int cdb_set_timeout(int sock, int timeout_secs);

in my code ? If Yes where do I need to use it.

I tried

/confdConfig/cdb/clientTimeout

by setting it to 100 seconds and then increasing the sleep to be more than 100 seconds in my code for it to fail with Time out ,but that didn’t work.

Thanks,
Sachin Siddappa

HI @per,

Can you elaborate what needs to be done, as I am still in a learning phase.
Anything with the example would greatly help my understanding to the concept.

Thanks,
Sachin Siddappa

An example based on the examples.confd/cdb_subscribe/twophase example:

$ pwd
/Users/tailf/confd-7.3/examples.confd/cdb_subscription/twophase
$ cat confd.conf |grep clientTimeout
  <clientTimeout>PT20S</clientTimeout>
  <!-- <clientTimeout>infinity</clientTimeout> -->
$ diff -u twophase.c.orig twophase.c
--- twophase.c.orig
+++ twophase.c
@@ -164,6 +164,7 @@
     int di = 1;
     int di_schema_order = 0;
     char *gp = NULL;
+    int st = 0;
 
     /* Setup progname (without path component) */
     if ((progname = strrchr(argv[0], (int)'/')) == NULL)
@@ -181,7 +182,7 @@
     }
 
     /* Parse command line */
-    while ((c = getopt(argc, argv, "da:p:intr:o:semfg:")) != EOF) {
+    while ((c = getopt(argc, argv, "da:p:g:s:")) != EOF) {
         switch (c) {
         case 'd':
           debug++;
@@ -195,6 +196,9 @@
         case 'g':// try to Get path after notification
           gp = optarg;
           break;
+        case 's':
+          st = atoi(optarg);
+          break;
         default:
           exit(1);
         }
@@ -279,6 +283,7 @@
           res = cdb_read_subscription_socket2(ss, &type, &flags, &subp, &len);
 
           if (res == CONFD_EOF) {
+            fprintf(stderr, "ConfD closed the subscriber socket. Exit...\n");
             exit(0);
           }
           OK(res);
@@ -299,6 +304,7 @@
             case CDB_SUB_PREPARE:
               printf("\nTYPE=CDB_SUB_PREPARE\n");
               {
+                if (st) { printf("sleep %d seconds\n", st); sleep(st); }
                 if (di) call_diff_iterate(ss, "diff prepare", subp, len,
                                           di_schema_order);
                 if (gp) pget(gp, 0);
$ make all hardware_up
...
$ confd -c confd.conf --addloadpath $CONFD_DIR/etc/confd
$ ./twophase -s21 /system &
$ time netconf-console --edit-config=-<<<'<system xmlns="http://tail-f.com/ns/example/config"><nodes><name>some_name</name></nodes></system>'

recv notification PREPARE flags=LAST subids=7 

TYPE=CDB_SUB_PREPARE
sleep 21 seconds
<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1">
  <rpc-error>
    <error-type>application</error-type>
    <error-tag>operation-failed</error-tag>
    <error-severity>error</error-severity>
    <error-message xml:lang="en">application communication failure</error-message>
  </rpc-error>
</rpc-reply>

real	0m20.592s
user	0m0.224s
sys	0m0.071s
send CDB_DONE_PRIORITY
ConfD closed the subscriber socket. Exit...

In the twophase.c example, note that the transaction will only fail if we are in the prepare phase of the transaction (as @per pointed out above) and a mandatory subscriber fail to reply, for example before the client timeout expire.

1 Like