For completeness, I’ll add my test to the “reference example” pile.
Implemented a simple mapping from the IETF interfaces YANG model to a ifstatus.yang model that implements a subset of the interfaces state.
ifstatus.yang:
module ifstatus {
namespace "http://tail-f.com/ns/example/if";
prefix ifs;
import tailf-common {
prefix tailf;
}
revision 2019-07-22 {
description "Initial revision.";
}
container interfaces-state {
list interface {
key name;
config false;
tailf:callpoint ifs;
leaf name {
type string;
}
container statistics {
leaf in-octets {
type uint64;
mandatory true;
}
leaf in-unicast-pkts {
type uint64;
mandatory true;
}
leaf in-errors {
type uint32;
mandatory true;
}
leaf in-discards {
type uint32;
mandatory true;
}
leaf out-octets {
type uint64;
mandatory true;
}
leaf out-unicast-pkts {
type uint64;
mandatory true;
}
leaf out-errors {
type uint32;
mandatory true;
}
leaf out-discards {
type uint32;
mandatory true;
}
}
}
}
}
The ifstatus YANG model has a data provider application that get the data it provides from the IETF Interfaces YANG model data which is stored in the CDB operational datastore.
The data provider registers the get_elem(), get_next(), get_next_object(), and find_next_object() and get the data from the CDB operational datastore using for example cdb_get_values(). For this example you would only need the get_elem() (mandatory) and find_next_object() callbacks registered. Comment out the rest if you dare.
The data provider application:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/poll.h>
#include <confd_lib.h>
#include <confd_dp.h>
#include <confd_cdb.h>
#include "ietf-interfaces.h"
#include "ifstatus.h"
#define USE_CASE_ASSUME_NOTHING 0
#define USE_CASE_OPTIMAL_FOR_LARGE_LISTS 1
#define USE_CASE_ALL_IN_DEMO 2
/* Our daemon context as a global variable */
static struct confd_daemon_ctx *dctx;
static int ctlsock;
static int workersock;
static int cdbsock;
static int s_init(struct confd_trans_ctx *tctx)
{
confd_trans_set_fd(tctx, workersock);
return CONFD_OK;
}
static int get_next(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath,
long next)
{
confd_value_t v;
confd_tag_value_t tv[3];
if (next == -1) { /* first call */
next = 0;
}
if (cdb_num_instances(cdbsock, "/if:interfaces-state/interface") <= next) {
/* we have reached the end of the list */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
/* get the key */
CONFD_SET_TAG_CDBBEGIN(&tv[0], if_interface, if__ns, next);
CONFD_SET_TAG_NOEXISTS(&tv[1], if_name);
CONFD_SET_TAG_XMLEND(&tv[2], if_interface, if__ns);
if (cdb_get_values(cdbsock, tv, 3, "/if:interfaces-state") != CONFD_OK) {
/* key not found */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
/* return the key from the list entry and 'next + 1' for next entry */
CONFD_SET_STR(&v, CONFD_GET_CBUFPTR(CONFD_GET_TAG_VALUE(&tv[1])));
confd_data_reply_next_key(tctx, &v, 1, next + 1);
confd_free_value(&v); /* name must be freed since it's a C_BUF */
return CONFD_OK;
}
static int get_elem(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath)
{
confd_value_t v;
if (cdb_get(cdbsock, &v, "%h", keypath) != CONFD_OK) {
confd_data_reply_not_found(tctx);
} else {
confd_data_reply_value(tctx, &v);
}
confd_free_value(&v); /* name must be freed since it's a C_BUF, other values is a nop */
return CONFD_OK;
}
static int num_instances(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath)
{
confd_value_t v;
CONFD_SET_INT32(&v, cdb_num_instances(cdbsock, "/if:interfaces-state/interface"));
confd_data_reply_value(tctx, &v);
return CONFD_OK;
}
static int ifs_max_obj_tvsize = 11;
static int get_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath)
{
confd_tag_value_t itv[ifs_max_obj_tvsize+2], tv[ifs_max_obj_tvsize];
int pos, j;
pos = cdb_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", &keypath->v[0][0]);
if (pos < 0) {
/* No list entry with a maching key */
confd_data_reply_not_found(tctx);
return CONFD_OK;
}
j = 0;
CONFD_SET_TAG_CDBBEGIN(&itv[j], if_interface, if__ns, pos); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_name); j++;
CONFD_SET_TAG_XMLBEGIN(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_discards); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_discards); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_interface, if__ns); j++;
if (cdb_get_values(cdbsock, itv, j, "/if:interfaces-state") != CONFD_OK)
confd_fatal("cdb_get_values() from /if:interfaces-state failed\n");
j = 0;
j++; /* get rid of the begin list tag */
CONFD_SET_TAG_VALUE(&tv[0], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[1], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[2], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[3], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[4], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[5], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[6], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[7], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[8], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[9], itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&tv[10], itv[j].tag.tag, &itv[j].v); j++;
j++; /* dispose of the end list tag (here only for clarity) */
confd_data_reply_tag_value_array(tctx, &tv[0], ifs_max_obj_tvsize);
confd_free_value(&tv[0].v); /* name must be freed since it's a C_BUF */
return CONFD_OK;
}
static int max_nobjs = 100;
static int get_next_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath, long next)
{
int pos, n_list_entries, nobj, i, j;
confd_tag_value_t *tv, *itv;
struct confd_tag_next_object *tobj;
if (next == -1) { /* first call */
pos = 0;
} else {
pos = next;
}
if ((n_list_entries = cdb_num_instances(cdbsock, "/if:interfaces-state/interface")) <= pos) {
/* we have reached the end of the list */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
if (n_list_entries - pos > max_nobjs) {
nobj = max_nobjs;
} else {
nobj = n_list_entries - pos;
}
//fprintf(stderr, "\nifs_max_obj_tvsize %d n_list_entries %d nobj %d pos %d\n", ifs_max_obj_tvsize, n_list_entries, nobj, pos);
/* get the list entries */
itv = (confd_tag_value_t *) malloc(sizeof(confd_tag_value_t) * nobj * (ifs_max_obj_tvsize + 2)); /* "+ 2" for the begin and end tags */
j = 0;
for (i = 0; i < nobj; i++) {
CONFD_SET_TAG_CDBBEGIN(&itv[j], if_interface, if__ns, pos+i); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_name); j++;
CONFD_SET_TAG_XMLBEGIN(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_discards); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_discards); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_interface, if__ns); j++;
}
if (cdb_get_values(cdbsock, itv, nobj * (ifs_max_obj_tvsize + 2), "/if:interfaces-state") != CONFD_OK)
confd_fatal("cdb_get_values() from /if:interfaces-state failed\n");
tobj = malloc(sizeof(struct confd_tag_next_object) * (max_nobjs + 1));
tv = (confd_tag_value_t *) malloc(sizeof(confd_tag_value_t) * max_nobjs * ifs_max_obj_tvsize);
/* create reply */
j = 0;
for (i = 0; i < nobj; i++) {
tobj[i].tv = &tv[i * ifs_max_obj_tvsize];
j++; /* get rid of the begin list tag */
CONFD_SET_TAG_VALUE(&(tobj[i].tv[0]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[1]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[2]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[3]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[4]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[5]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[6]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[7]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[8]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[9]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[10]), itv[j].tag.tag, &itv[j].v); j++;
j++; /* dispose of the end list tag */
tobj[i].n = ifs_max_obj_tvsize;
tobj[i].next = (long)pos+i+1;
}
if (pos + i == n_list_entries)
tobj[i++].tv = NULL; /* indicate no more list entries */
/* reply */
confd_data_reply_next_object_tag_value_arrays(tctx, tobj, i, 0);
for (i = 0; pos + i < nobj; i++) {
confd_free_value(CONFD_GET_TAG_VALUE(&(tobj[i].tv[0]))); /* name must be freed since it's a C_BUF */
}
free(itv);
free(tv);
free(tobj);
return CONFD_OK;
}
static int find_next(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp,
enum confd_find_next_type type,
confd_value_t *keys, int nkeys)
{
confd_value_t v;
confd_tag_value_t tv[3];
int pos = -1;
switch (nkeys) {
case 0:
pos = 0; /* first call */
break;
case 1:
switch (type) {
case CONFD_FIND_SAME_OR_NEXT:
if((pos = cdb_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys)) < 0) {
pos = cdb_next_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys);
}
break;
case CONFD_FIND_NEXT:
pos = cdb_next_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys);
break;
default:
confd_fatal("invalid find next type");
break;
}
if (pos < 0) {
/* key does not exist */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
break;
default:
confd_fatal("invalid number of keys");
break;
}
/* get the key */
CONFD_SET_TAG_CDBBEGIN(&tv[0], if_interface, if__ns, pos);
CONFD_SET_TAG_NOEXISTS(&tv[1], if_name);
CONFD_SET_TAG_XMLEND(&tv[2], if_interface, if__ns);
if (cdb_get_values(cdbsock, tv, 3, "/if:interfaces-state") != CONFD_OK) {
/* key not found in the unlikely event that it was deleted after our
cdb_index() check */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
CONFD_SET_STR(&v, CONFD_GET_CBUFPTR(CONFD_GET_TAG_VALUE(&tv[1])));
/* reply */
confd_data_reply_next_key(tctx, &v, 1, pos+1);
confd_free_value(&v);
return CONFD_OK;
}
static int find_next_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *keypath,
enum confd_find_next_type type,
confd_value_t *keys, int nkeys)
{
int pos = 0, n_list_entries, nobj, i, j;
confd_tag_value_t *tv, *itv;
struct confd_tag_next_object *tobj;
switch (nkeys) {
case 0:
pos = 0; /* first call */
break;
case 1:
switch (type) {
case CONFD_FIND_SAME_OR_NEXT:
if((pos = cdb_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys)) < 0) {
pos = cdb_next_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys);
}
break;
case CONFD_FIND_NEXT:
pos = cdb_next_index(cdbsock, "%s{%x}", "/if:interfaces-state/interface", keys);
break;
default:
confd_fatal("invalid find next type");
break;
}
break;
default:
confd_fatal("invalid number of keys");
break;
}
if (pos == -1 || (n_list_entries = cdb_num_instances(cdbsock, "/if:interfaces-state/interface")) <= pos) {
/* we have reached the end of the list */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
if (n_list_entries - pos > max_nobjs) {
nobj = max_nobjs;
} else {
nobj = n_list_entries - pos;
}
//fprintf(stderr, "\nifs_max_obj_tvsize %d n_list_entries %d nobj %d pos %d nkeys %d\n", ifs_max_obj_tvsize, n_list_entries, nobj, pos, nkeys);
/* get the list entries */
itv = (confd_tag_value_t *) malloc(sizeof(confd_tag_value_t) * nobj * (ifs_max_obj_tvsize + 2)); /* "+ 2" for the begin and end tags */
j = 0;
for (i = 0; i < nobj; i++) {
CONFD_SET_TAG_CDBBEGIN(&itv[j], if_interface, if__ns, pos+i); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_name); j++;
CONFD_SET_TAG_XMLBEGIN(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_in_discards); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_octets); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_unicast_pkts); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_errors); j++;
CONFD_SET_TAG_NOEXISTS(&itv[j], if_out_discards); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_statistics, if__ns); j++;
CONFD_SET_TAG_XMLEND(&itv[j], if_interface, if__ns); j++;
}
if (cdb_get_values(cdbsock, itv, nobj * (ifs_max_obj_tvsize + 2), "/if:interfaces-state") != CONFD_OK)
confd_fatal("cdb_get_values() from /if:interfaces-state failed\n");
tobj = malloc(sizeof(struct confd_tag_next_object) * (max_nobjs + 1));
tv = (confd_tag_value_t *) malloc(sizeof(confd_tag_value_t) * max_nobjs * ifs_max_obj_tvsize);
/* create reply */
j = 0;
for (i = 0; i < nobj; i++) {
tobj[i].tv = &tv[i * ifs_max_obj_tvsize];
j++; /* get rid of the begin list tag */
CONFD_SET_TAG_VALUE(&(tobj[i].tv[0]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[1]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[2]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[3]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[4]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[5]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[6]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[7]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[8]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[9]), itv[j].tag.tag, &itv[j].v); j++;
CONFD_SET_TAG_VALUE(&(tobj[i].tv[10]), itv[j].tag.tag, &itv[j].v); j++;
j++; /* dispose of the end list tag */
tobj[i].n = ifs_max_obj_tvsize;
tobj[i].next = (long)pos+i+1;
}
if (pos + i >= n_list_entries)
tobj[i++].tv = NULL; /* indicate no more list entries */
/* reply */
confd_data_reply_next_object_tag_value_arrays(tctx, tobj, i, 0);
for (i = 0; pos + i < nobj; i++) {
confd_free_value(CONFD_GET_TAG_VALUE(&(tobj[i].tv[0]))); /* name must be freed since it's a C_BUF */
}
free(itv);
free(tv);
free(tobj);
return CONFD_OK;
}
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int c, debuglevel = CONFD_DEBUG;
int use_case_type = USE_CASE_ASSUME_NOTHING;
struct confd_trans_cbs trans;
struct confd_data_cbs data;
while ((c = getopt(argc, argv, "noaxzdpts")) != EOF) {
switch(c) {
case 'n':
use_case_type = USE_CASE_ASSUME_NOTHING;
break;
case 'o':
use_case_type = USE_CASE_OPTIMAL_FOR_LARGE_LISTS;
break;
case 'a':
use_case_type = USE_CASE_ALL_IN_DEMO;
break;
case 'x':
max_nobjs = atoi(optarg);
break;
case 'z':
ifs_max_obj_tvsize = atoi(optarg);
break;
case 'd':
debuglevel = CONFD_DEBUG;
break;
case 'p':
debuglevel = CONFD_PROTO_TRACE;
break;
case 't':
debuglevel = CONFD_TRACE;
break;
case 's':
debuglevel = CONFD_SILENT;
break;
}
}
/* Initialize confd library */
confd_init("ifstatus-cdb", 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("ifstatus-cdb")) == NULL)
confd_fatal("Failed to initialize daemon\n");
/* Create and connect the control and worker sockets */
if ((ctlsock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
confd_fatal("Failed to open ctlsocket\n");
if (confd_connect(dctx, ctlsock, CONTROL_SOCKET, (struct sockaddr*)&addr,
sizeof (struct sockaddr_in)) < 0)
confd_fatal("Failed to confd_connect() to confd \n");
if ((workersock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
confd_fatal("Failed to open workersocket\n");
if (confd_connect(dctx, workersock, WORKER_SOCKET,(struct sockaddr*)&addr,
sizeof (struct sockaddr_in)) < 0)
confd_fatal("Failed to confd_connect() to confd \n");
/* Register callbacks */
memset(&trans, 0, sizeof(trans));
trans.init = s_init;
if (confd_register_trans_cb(dctx, &trans) == CONFD_ERR)
confd_fatal("Failed to register trans cb\n");
memset(&data, 0, sizeof (struct confd_data_cbs));
switch (use_case_type) {
case USE_CASE_OPTIMAL_FOR_LARGE_LISTS:
/* assuming large lists and not the content of
individual leafs are typically requested */
data.num_instances = num_instances;
data.get_object = get_object;
data.find_next = find_next;
data.find_next_object = find_next_object;
break;
case USE_CASE_ALL_IN_DEMO:
/* going "all in" as a demo */
data.get_elem = get_elem;
data.get_object = get_object;
data.num_instances = num_instances;
data.get_next = get_next;
data.get_next_object = get_next_object;
data.find_next = find_next;
data.find_next_object = find_next_object;
break;
default: /* USE_CASE_ASSUME_NOTHING */
/* assuming nothing about the use case */
data.get_elem = get_elem;
data.num_instances = num_instances;
data.get_object = get_object;
data.find_next = find_next;
data.find_next_object = find_next_object;
break;
}
strcpy(data.callpoint, ifs__callpointid_ifs);
if (confd_register_data_cb(dctx, &data) == CONFD_ERR)
confd_fatal("Failed to register data cb\n");
if (confd_register_done(dctx) != CONFD_OK)
confd_fatal("Failed to complete registration \n");
/* Start a CDB session towards the CDB operational datastore */
if ((cdbsock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
confd_fatal("Failed to create the CDB socket");
if (cdb_connect(cdbsock, CDB_DATA_SOCKET, (struct sockaddr *)&addr,
sizeof(struct sockaddr_in)) < 0)
confd_fatal("Failed to connect to ConfD CDB");
if (cdb_start_session(cdbsock, CDB_OPERATIONAL) != CONFD_OK)
confd_fatal("Failed to start a CDB session\n");
if (cdb_set_namespace(cdbsock, if__ns) != CONFD_OK)
confd_fatal("Failed to set namespace\n");
while(1) {
struct pollfd set[2];
int ret;
set[0].fd = ctlsock;
set[0].events = POLLIN;
set[0].revents = 0;
set[1].fd = workersock;
set[1].events = POLLIN;
set[1].revents = 0;
if (poll(set, sizeof(set)/sizeof(set[0]), -1) < 0) {
perror("Poll failed:");
continue;
}
/* Check for I/O */
if (set[0].revents & POLLIN) {
if ((ret = confd_fd_ready(dctx, ctlsock)) == CONFD_EOF) {
confd_fatal("Control socket closed\n");
} else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
confd_fatal("Error on control socket request: %s (%d): %s\n",
confd_strerror(confd_errno), confd_errno, confd_lasterr());
}
}
if (set[1].revents & POLLIN) {
if ((ret = confd_fd_ready(dctx, workersock)) == CONFD_EOF) {
confd_fatal("Worker socket closed\n");
} else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
confd_fatal("Error on worker socket request: %s (%d): %s\n",
confd_strerror(confd_errno), confd_errno, confd_lasterr());
}
}
}
}
The MAAPI client that get some list entries from the ifstatus.yang list using maapi_get_next() and maapi_get_objects() that trigger the find_next_object() callback:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <confd_lib.h>
#include <confd_maapi.h>
#include "ietf-interfaces.h"
#include "ifstatus.h"
static void print_value_array(confd_value_t vs[], int n)
{
char tmpbuf[BUFSIZ];
int i;
for (i=0; i<n; i++) {
confd_pp_value(tmpbuf, BUFSIZ, &vs[i]);
printf("%s ", tmpbuf);
}
}
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
struct maapi_cursor mc;
int c, i, thandle, maapisock, values_per_entry, nobj;
int debuglevel = CONFD_DEBUG, entries_per_request = 5;
confd_value_t *v, inkeys[1];
struct confd_cs_node *object;
struct confd_ip ip;
const char *groups[] = { "admin" }, *context = "system";
char *inkey = "lo1";
while ((c = getopt(argc, argv, "k:e:dpts")) != EOF) {
switch(c) {
case 'k':
inkey = optarg;
break;
case 'e':
entries_per_request = atoi(optarg);
break;
case 'd':
debuglevel = CONFD_DEBUG;
break;
case 'p':
debuglevel = CONFD_PROTO_TRACE;
break;
case 't':
debuglevel = CONFD_TRACE;
break;
case 's':
debuglevel = CONFD_SILENT;
break;
}
}
confd_init("maapi-get-obj", 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");
object = confd_cs_node_cd(NULL, "/ifs:interfaces-state/interface");
values_per_entry = confd_max_object_size(object);
if ((maapisock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
confd_fatal("Failed to create the MAAPI socket");
if (maapi_connect(maapisock, (struct sockaddr*)&addr,
sizeof (struct sockaddr_in)) < 0)
confd_fatal("Failed to confd_connect() to confd \n");
ip.af = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &ip.ip.v4);
if ((maapi_start_user_session(maapisock, "admin", context,groups,
sizeof(groups) / sizeof(*groups),
&ip,
CONFD_PROTO_TCP) != CONFD_OK)) {
confd_fatal("Failed to start user session");
}
if ((thandle = maapi_start_trans(maapisock,CONFD_OPERATIONAL,
CONFD_READ)) < 0) {
confd_fatal("Failed to start trans\n");
}
if(maapi_init_cursor(maapisock, thandle, &mc,
"/ifs:interfaces-state/interface") != CONFD_OK)
confd_fatal("maapi_init_cursor() failed\n");
CONFD_SET_STR(&inkeys[0], &inkey[0]);
if (maapi_find_next(&mc, CONFD_FIND_SAME_OR_NEXT, inkeys, 1) != CONFD_OK)
confd_fatal("maapi_find_next() failed\n");
if(mc.n == 0) {
fprintf(stderr, "Key \"%s\" not found\n", inkey);
exit(0);
}
v = malloc(sizeof(confd_value_t) * values_per_entry * entries_per_request);
nobj = entries_per_request;
if (maapi_get_objects(&mc, v, values_per_entry, &nobj) < 0)
confd_fatal("maapi_get_objects() failed\n");
if(nobj == 0) {
fprintf(stderr, "No entries after \"%s\"\n", inkey);
exit(0);
}
for (i = 0; i < nobj; i++) {
printf("%d: ", i);
print_value_array(&v[i*values_per_entry], values_per_entry);
printf("\n");
}
exit(0);
}
A simple script to generate some test data:
#!/usr/bin/env python
import sys
import time
def print_config(str, str2):
print """<config xmlns="http://tail-f.com/ns/config/1.0">
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">
<interface>
<name>lo1</name>
<type>ianaift:softwareLoopback</type>
<enabled>true</enabled>
</interface>
%s
</interfaces>
<interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">
<interface>
<name>lo1</name>
<type>ianaift:softwareLoopback</type>
<admin-status>up</admin-status>
<oper-status>up</oper-status>
<if-index>1</if-index>
<statistics>
<discontinuity-time>
2019-07-22T03:00:00+00:00
</discontinuity-time>
<in-octets>0</in-octets>
<in-unicast-pkts>0</in-unicast-pkts>
<in-broadcast-pkts>0</in-broadcast-pkts>
<in-multicast-pkts>0</in-multicast-pkts>
<in-discards>0</in-discards>
<in-errors>0</in-errors>
<in-unknown-protos>0</in-unknown-protos>
<out-octets>0</out-octets>
<out-unicast-pkts>0</out-unicast-pkts>
<out-broadcast-pkts>0</out-broadcast-pkts>
<out-multicast-pkts>0</out-multicast-pkts>
<out-discards>0</out-discards>
<out-errors>0</out-errors>
</statistics>
</interface>
%s
</interfaces-state>
</config>
"""%(str,str2)
def gen_data(n):
str = ""
str2 = ""
for i in xrange(0,n):
str += """
<interface>
<name>eth%03d</name>
<type>ianaift:ethernetCsmacd</type>
<enabled>true</enabled>
</interface>
"""%(i)
str2 += """
<interface>
<name>eth%03d</name>
<type>ianaift:ethernetCsmacd</type>
<admin-status>up</admin-status>
<oper-status>up</oper-status>
<if-index>%d</if-index>
<phys-address>00:01:02:03:04:06</phys-address>
<statistics>
<discontinuity-time>
2019-07-22T03:00:00+00:00
</discontinuity-time>
<in-octets>%d</in-octets>
<in-unicast-pkts>%d</in-unicast-pkts>
<in-broadcast-pkts>%d</in-broadcast-pkts>
<in-multicast-pkts>%d</in-multicast-pkts>
<in-discards>%d</in-discards>
<in-errors>%d</in-errors>
<in-unknown-protos>%d</in-unknown-protos>
<out-octets>%d</out-octets>
<out-unicast-pkts>%d</out-unicast-pkts>
<out-broadcast-pkts>%d</out-broadcast-pkts>
<out-multicast-pkts>%d</out-multicast-pkts>
<out-discards>%d</out-discards>
<out-errors>%d</out-errors>
</statistics>
</interface>
"""%(i,i+2,i,i,i,i,i,i,i,i,i,i,i,i,i)
print_config(str, str2)
def parse_num(str):
if str[:2] == '2^':
return pow(2,parse_num(str[2:]))
return int(str)
gen_data(parse_num(sys.argv[1]))
After building the YANG models and applications above you can for example generate some test data
$ ./cdbgen.py 1000 > test.xml
Load it into the CDB operational datastore defined by the IETF interfaces YANG model
$ confd_load -O -dd -m -l test.xml
Run the maapi_get_next() maapi_get_object() client application to trigger the find_get_next()
$ ./maapi-get-obj -k eth100 -e 5
And/or run some netconf-console, confd_load, curl, etc. tests to exercise some some other interfaces and API calls.