SNMP frontend, new preli table handling code

Skipped data set and use table-helper only
Specialized SNMP error handling
This commit is contained in:
Olof hagsand 2022-05-26 17:47:28 +02:00
parent 1a2edb7a2a
commit b2be7e9e1b
15 changed files with 750 additions and 363 deletions

View file

@ -81,91 +81,6 @@
#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"
/*! Parse smiv2 extensions for YANG container/list
*
* Typical table:
* container x {
* smiv2:oid "1.3.6.1.4.1.8072.2.2.1";
* list y{
*
* }
* }
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node
* @retval 0 OK
* @retval -1 Error
*/
static int
mib_yang_table(clicon_handle h,
yang_stmt *ys)
{
int retval = SNMP_ERR_GENERR;
netsnmp_handler_registration *nhreg;
netsnmp_table_data_set *table;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t sz1 = MAX_OID_LEN;
char *name;
clixon_snmp_handle *sh;
int ret;
/* Get OID from parent container */
if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0)
goto done;
if (oidstr == NULL)
goto ok;
if (snmp_parse_oid(oidstr, oid1, &sz1) == NULL){
clicon_err(OE_SNMP, 0, "snmp_parse_oid");
goto done;
}
name = yang_argument_get(ys);
if ((table = netsnmp_create_table_data_set(name)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_create_table_data_set");
goto done;
}
/* Userdata to pass around in netsmp callbacks
* XXX: not deallocated
*/
if ((sh = malloc(sizeof(*sh))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(sh, 0, sizeof(*sh));
sh->sh_h = h;
sh->sh_ys = ys;
sh->sh_table = table;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1;
if (clixon_table_create(table, ys, h) < 0)
goto done;
if ((nhreg = netsnmp_create_handler_registration(name,
snmp_table_handler,
oid1,
sz1,
HANDLER_CAN_RWRITE)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_create_handler_registration");
goto done;
}
nhreg->my_reg_void = sh;
if ((ret = netsnmp_register_table_data_set(nhreg, table, NULL)) != SNMPERR_SUCCESS){
//XXX err return? clicon_err(OE_SNMP, ret, "netsnmp_register_table_data_set");
// goto done;
}
(void)netsnmp_register_auto_data_table(table, NULL);
clicon_debug(1, "%s %s registered", __FUNCTION__, oidstr);
ok:
retval = 0;
done:
return retval;
}
/*! Parse smiv2 extensions for YANG leaf
* Typical leaf:
@ -174,12 +89,14 @@ mib_yang_table(clicon_handle h,
* smiv2:defval "42"; (optional)
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node
* @param[in] cvk Vector of key/index values. NB: not for scalars, only tables
* @retval 0 OK
* @retval -1 Error
*/
static int
mib_yang_leaf(clicon_handle h,
yang_stmt *ys)
yang_stmt *ys,
cvec *cvk)
{
int retval = -1;
netsnmp_handler_registration *nhreg = NULL;
@ -193,23 +110,36 @@ mib_yang_leaf(clicon_handle h,
int modes;
char *name;
clixon_snmp_handle *sh;
cg_var *cvi;
cbuf *cboid = NULL;
/* Get OID from leaf */
if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0)
goto done;
if (oidstr == NULL)
goto ok;
if (snmp_parse_oid(oidstr, oid1, &sz1) == NULL){
clicon_err(OE_SNMP, 0, "snmp_parse_oid");
/* Append sub-keys to original oidstr, use cligen-buf
*/
if ((cboid = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cboid, "%s", oidstr);
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL)
cprintf(cboid, ".%s", cv_string_get(cvi));
if (snmp_parse_oid(cbuf_get(cboid), oid1, &sz1) == NULL){
clicon_err(OE_XML, 0, "snmp_parse_oid(%s)", cbuf_get(cboid));
// goto done;
goto ok; // XXX skip
}
if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done;
#if 1 /* Sanity check of types */
/* Sanity check of types */
if (type_yang2asn1(ys, NULL) < 0)
goto done;
#endif
/* Get modes (access) read-only, read-write, not-accessible, oaccessible-for-notify
/* Get modes (access) read-only, read-write, not-accessible, accessible-for-notify
*/
if (modes_str == NULL)
goto ok;
@ -222,8 +152,8 @@ mib_yang_leaf(clicon_handle h,
name = yang_argument_get(ys);
if ((handler = netsnmp_create_handler(name, snmp_scalar_handler)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_create_handler");
if ((handler = netsnmp_create_handler(name, clixon_snmp_scalar_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
}
@ -240,29 +170,249 @@ mib_yang_leaf(clicon_handle h,
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1;
sh->sh_default = default_str;
if (cvk &&
(sh->sh_cvk = cvec_dup(cvk)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, sz1,
modes)) == NULL){
clicon_err(OE_SNMP, errno, "netsnmp_handler_registration_create");
oid1, sz1,
modes)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
nhreg->my_reg_void =(void*)sh;
handler->myvoid =(void*)sh;
/*
* XXX: nhreg->agent_data
*/
if ((ret = netsnmp_register_instance(nhreg)) < 0){
if ((ret = netsnmp_register_instance(nhreg)) != SNMPERR_SUCCESS){
/* XXX Failures are MIB_REGISTRATION_FAILED and MIB_DUPLICATE_REGISTRATION. */
clicon_err(OE_SNMP, ret, "netsnmp_register_instance");
goto done;
}
clicon_debug(1, "%s %s registered", __FUNCTION__, cbuf_get(cboid));
ok:
retval = 0;
done:
if (cboid)
cbuf_free(cboid);
return retval;
}
/*! Parse smiv2 extensions for YANG container/list
*
* Typical table:
* container x {
* smiv2:oid "1.3.6.1.4.1.8072.2.2.1";
* list y{
*
* }
* }
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node (container)
* @param[in] yl Mib-Yang node (list)
* @retval 0 OK
* @retval -1 Error
*/
static int
mib_yang_table(clicon_handle h,
yang_stmt *ys,
yang_stmt *ylist)
{
int retval = -1;
netsnmp_handler_registration *nhreg;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t sz1 = MAX_OID_LEN;
char *name;
clixon_snmp_handle *sh;
int ret;
netsnmp_mib_handler *handler;
netsnmp_table_registration_info *table_info=NULL;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
yang_stmt *yleaf;
int asn1type;
/* Get OID from parent container */
if (yang_extension_value(ys, "oid", IETF_YANG_SMIV2_NS, NULL, &oidstr) < 0)
goto done;
if (oidstr == NULL)
goto ok;
if (snmp_parse_oid(oidstr, oid1, &sz1) == NULL){
clicon_err(OE_XML, errno, "snmp_parse_oid");
goto done;
}
name = yang_argument_get(ys);
/* Userdata to pass around in netsmp callbacks
* XXX: not deallocated
*/
if ((sh = malloc(sizeof(*sh))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(sh, 0, sizeof(*sh));
sh->sh_h = h;
sh->sh_ys = ys;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = sz1;
if ((handler = netsnmp_create_handler(name, clixon_snmp_table_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
}
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, sz1,
HANDLER_CAN_RWRITE)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
handler->myvoid =(void*)sh;
/* See netsnmp_register_table_data_set */
if ((table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL){
clicon_err(OE_UNIX, errno, "SNMP_MALLOC_TYPEDEF");
goto done;
}
/* Keys, go through keys */
if ((cvk = yang_cvec_get(ylist)) == NULL){
clicon_err(OE_YANG, 0, "No keys");
goto done;
}
cvi = NULL;
/* Iterate over individual keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((yleaf = yang_find(ylist, Y_LEAF, keyname)) == NULL){
clicon_err(OE_XML, 0, "List statement \"%s\" has no key leaf \"%s\"",
yang_argument_get(ylist), keyname);
goto done;
}
if (type_yang2asn1(yleaf, &asn1type) < 0)
// goto done;
goto ok; // XXX skip
if (snmp_varlist_add_variable(&table_info->indexes,
NULL, // oid name
0, // oid len
asn1type,
NULL, // value
0) == NULL){
clicon_err(OE_XML, errno, "snmp_varlist_add_variable");
goto done;
}
}
table_info->min_column = cvec_len(cvk);
/* Count columns */
yleaf = NULL;
table_info->max_column = 0;
while ((yleaf = yn_each(ylist, yleaf)) != NULL) {
if (yang_keyword_get(yleaf) == Y_LEAF)
table_info->max_column++;
}
if ((ret = netsnmp_register_table(nhreg, table_info)) != SNMPERR_SUCCESS){
clicon_err(OE_SNMP, ret, "netsnmp_register_table");
goto done;
}
clicon_debug(1, "%s %s registered", __FUNCTION__, oidstr);
ok:
retval = 0;
done:
return retval;
}
/*! Register table sub-oid:s
* This assumes a table contains a set of keys and a list of leafs only
* The function makes a query to the datastore and registers all table entries that
* currently exists. This means it registers for a static table. If new rows or columns
* are created or deleted this will not change the OID registration.
* That is, the table registration is STATIC
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node (container)
* @param[in] ylist Mib-Yang node (list)
* @retval 0 OK
* @retval -1 Error
*/
static int
mib_traverse_table(clicon_handle h,
yang_stmt *ys,
yang_stmt *ylist)
{
int retval = -1;
cvec *nsc = NULL;
char *xpath;
cxobj *xt = NULL;
cxobj *xerr;
cxobj *xtable;
cxobj *xrow;
cxobj *xcol;
yang_stmt *y;
cvec *cvk0;
cg_var *cv0;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cv;
int i;
cxobj *xi;
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
if (yang2xpath(ys, NULL, &xpath) < 0)
goto done;
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clixon_netconf_error(xerr, "clicon_rpc_get", NULL);
goto done;
}
if ((xtable = xpath_first(xt, nsc, "%s", xpath)) != NULL) {
/* Make a clone of key-list, but replace names with values */
if ((cvk0 = yang_cvec_get(ylist)) == NULL){
clicon_err(OE_YANG, 0, "No keys");
goto done;
}
xrow = NULL;
while ((xrow = xml_child_each(xtable, xrow, CX_ELMNT)) != NULL) {
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if ((cvk = cvec_dup(cvk0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
for (i=0; i<cvec_len(cvk0); i++){
cv0 = cvec_i(cvk0, i);
cv = cvec_i(cvk, i);
if ((xi = xml_find_type(xrow, NULL, cv_string_get(cv0), CX_ELMNT)) == NULL)
break;
cv_string_set(cv, xml_body(xi));
}
if (i<cvec_len(cvk0))
continue; /* skip row, not all indexes */
xcol = NULL;
while ((xcol = xml_child_each(xrow, xcol, CX_ELMNT)) != NULL) {
if ((y = xml_spec(xcol)) == NULL)
continue;
if (mib_yang_leaf(h, y, cvk) < 0)
goto done;
}
}
}
retval = 0;
done:
if (cvk)
cvec_free(cvk);
if (xt)
xml_free(xt);
if (nsc)
xml_nsctx_free(nsc);
return retval;
}
/*! Traverse mib-yang tree, identify scalars and tables, register OID and callbacks
*
* The tree is traversed depth-first, which at least guarantees that a parent is
@ -278,10 +428,11 @@ mib_yang_leaf(clicon_handle h,
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
* @param[in] h Clixon handle
* @param[in] yn yang node
* @retval 0 OK, all nodes traversed
* @retval -1 Error, aborted at first error encounter
* @param[in] h Clixon handle
* @param[in] yn yang node
* @param[in] table Yang node is within table
* @retval 0 OK, all nodes traversed
* @retval -1 Error, aborted at first error encounter
*/
static int
mib_traverse(clicon_handle h,
@ -291,12 +442,10 @@ mib_traverse(clicon_handle h,
yang_stmt *ys = NULL;
yang_stmt *yp;
int ret;
enum rfc_6020 keyw;
keyw = yang_keyword_get(yn);
switch(keyw){
switch(yang_keyword_get(yn)){
case Y_LEAF:
if (mib_yang_leaf(h, yn) < 0)
if (mib_yang_leaf(h, yn, NULL) < 0)
goto done;
break;
case Y_CONTAINER: /* See list case */
@ -304,9 +453,12 @@ mib_traverse(clicon_handle h,
case Y_LIST: /* If parent is container -> identify as table */
yp = yang_parent_get(yn);
if (yang_keyword_get(yp) == Y_CONTAINER){
if (mib_yang_table(h, yp) < 0)
/* Specialize table traversal */
if (mib_yang_table(h, yp, yn) < 0)
goto done;
goto ok; /* Dont traverse child leafs further */
if (mib_traverse_table(h, yp, yn) < 0)
goto done;
goto ok;
}
break;
default:
@ -314,15 +466,16 @@ mib_traverse(clicon_handle h,
}
/* Traverse data nodes in tree (module is special case */
ys = NULL;
if (yang_schemanode(yn) || keyw == Y_MODULE|| keyw == Y_SUBMODULE)
while ((ys = yn_each(yn, ys)) != NULL) {
if ((ret = mib_traverse(h, ys)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
while ((ys = yn_each(yn, ys)) != NULL) {
if (!yang_schemanode(ys))
continue;
if ((ret = mib_traverse(h, ys)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
}
ok:
retval = 0;
done: