SNMP: rowstatus for go and wait

Remaining: destroy and internal state
This commit is contained in:
Olof hagsand 2022-07-09 16:35:21 +02:00
parent d79d0aa933
commit 714e41c627
5 changed files with 274 additions and 45 deletions

View file

@ -309,11 +309,22 @@ snmp_scalar_get(clicon_handle h,
}
/*! Scalar handler, get a value from clixon
* @param[in] h Clixon handle
* @param[in] ys Yang node
* @param[in] cvk Vector of index/Key variables, if any
* @param[in] db Clixon datastore, typically "candidate"
* @param[in] rowstatus Special case: transform createAndGo -> active, createAndWait -> notInService
* @param[in] reqinfo
* @param[in] requestvb SNMP variables
* @retval 0 OK
* @retval -1 Error
*/
static int
snmp_scalar_set(clicon_handle h,
yang_stmt *ys,
cvec *cvk,
char *db,
int rowstatus,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
@ -333,7 +344,6 @@ snmp_scalar_set(clicon_handle h,
int asn1_type;
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
@ -366,6 +376,25 @@ snmp_scalar_set(clicon_handle h,
goto done;
if (ret == 0)
goto ok;
/* Special case translation of rowstatus values:
* createAndGo -> active, createAndWait -> notInService
*/
if (rowstatus){
if (strcmp(valstr, "createAndGo") == 0){
free(valstr);
if ((valstr = strdup("active")) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
else if (strcmp(valstr, "createAndWait") == 0){
free(valstr);
if ((valstr = strdup("notInService")) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
}
if (xml_value_set(xb, valstr) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
@ -374,7 +403,7 @@ snmp_scalar_set(clicon_handle h,
}
if (clixon_xml2cbuf(cb, xtop, 0, 0, -1, 0) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate", OP_MERGE, cbuf_get(cb)) < 0)
if (clicon_rpc_edit_config(h, db, OP_MERGE, cbuf_get(cb)) < 0)
goto done;
ok:
retval = 0;
@ -440,7 +469,7 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
case MODE_SET_RESERVE2: /* 1 */
break;
case MODE_SET_ACTION: /* 2 */
if (snmp_scalar_set(sh->sh_h, sh->sh_ys, NULL, reqinfo, requests) < 0)
if (snmp_scalar_set(sh->sh_h, sh->sh_ys, NULL, "candidate", 0, reqinfo, requests) < 0)
goto done;
break;
case MODE_SET_COMMIT: /* 3 */
@ -460,6 +489,79 @@ clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
return retval;
}
/*! Specialized get-config to get value of row-status, if any
*
* @param[in] h Clixon handle
* @param[in] ys Yang node
* @param[in] cvk Vector of index/Key variables, if any
* @param[out] rowstatus Enmu rowstatus: 0 invalid, 1 active, etc
* @retval 0 OK
* @retval -1 Error
*/
static int
snmp_table_rowstatus_get(clicon_handle h,
yang_stmt *ys,
yang_stmt *yrestype,
cvec *cvk,
int32_t *rowstatus)
{
int retval = -1;
cvec *nsc = NULL;
cxobj *xt = NULL;
cxobj *xerr;
char *xpath = NULL;
cxobj *xr;
int ret;
char *body;
char *intstr;
char *reason = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* Prepare backend call by constructing namespace context */
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
/* Create xpath from yang */
if (snmp_yang2xpath(ys, cvk, &xpath) < 0)
goto done;
/* Do the backend call */
if (clicon_rpc_get_config(h, NULL, "candidate", xpath, nsc, &xt) < 0)
goto done;
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clixon_netconf_error(xerr, "clicon_rpc_get_config", NULL);
goto done;
}
if ((xr = xpath_first(xt, nsc, "%s", xpath)) != NULL &&
(body = xml_body(xr)) != NULL) {
if ((ret = yang_enum2valstr(yrestype, body, &intstr)) < 0)
goto done;
if (ret == 0){
clicon_debug(1, "%s %s invalid or not found", __FUNCTION__, body);
*rowstatus = 0;
}
else {
if ((ret = parse_int32(intstr, rowstatus, &reason)) < 0)
goto done;
if (ret == 0){
clicon_debug(1, "%s parse_int32: %s", __FUNCTION__, reason);
*rowstatus = 0;
}
}
}
else
*rowstatus = 0;
retval = 0;
done:
if (xt)
xml_free(xt);
if (xpath)
free(xpath);
if (nsc)
xml_nsctx_free(nsc);
if (reason)
free(reason);
return retval;
}
/*! Create xpath from YANG table OID + 1 + n + cvk/key = requestvb->name
* Get yang of leaf from first part of OID
* Create xpath with right keys from later part of OID
@ -605,8 +707,11 @@ snmp_table_set(clicon_handle h,
size_t oidleaflen = MAX_OID_LEN;
oid *oidi;
size_t oidilen;
yang_stmt *yi;
yang_stmt *ys;
yang_stmt *yrowst;
yang_stmt *yk;
yang_stmt *yrestype = NULL;
char *xpath = NULL;
cvec *cvk_orig;
cvec *cvk_val;
@ -615,27 +720,44 @@ snmp_table_set(clicon_handle h,
int ret;
int asn1_type;
netsnmp_variable_list *requestvb;
int rowstatus = 0;
char *origtype;
/* Get OID from table /list */
if ((ret = yangext_oid_get(yt, oidt, &oidtlen, NULL)) < 0)
goto done;
if (ret == 0)
goto done;
/* Get yang of leaf from first part of OID */
/* Get yang of leaf from first part of OID
* and also leaf with rowstatus type
*/
ys = NULL;
while ((ys = yn_each(yt, ys)) != NULL) {
if (yang_keyword_get(ys) != Y_LEAF)
yrowst = NULL;
yi = NULL;
while ((yi = yn_each(yt, yi)) != NULL) {
if (yang_keyword_get(yi) != Y_LEAF)
continue;
/* reset oid */
oidleaflen = MAX_OID_LEN;
if ((ret = yangext_oid_get(ys, oidleaf, &oidleaflen, NULL)) < 0)
if ((ret = yangext_oid_get(yi, oidleaf, &oidleaflen, NULL)) < 0)
goto done;
if (ret == 0)
goto done;
if (oidtlen + 1 != oidleaflen) /* Indexes may be from other OID scope, skip those */
continue;
if (oids[oidleaflen-1] == oidleaf[oidleaflen-1])
break;
if (oids[oidleaflen-1] == oidleaf[oidleaflen-1]){
ys = yi;
}
origtype = NULL;
if (snmp_yang_type_get(yi, NULL, &origtype, &yrestype, NULL) < 0)
goto done;
if (strcmp(origtype, "RowStatus") == 0){
yrowst = yi;
}
if (origtype){
free(origtype);
origtype = NULL;
}
}
if (ys == NULL){
/* No leaf with matching OID */
@ -679,11 +801,67 @@ snmp_table_set(clicon_handle h,
clicon_err(OE_YANG, 0, "Expected oidlen 0 but is %zu", oidilen);
goto fail;
}
if (ys == yrowst){
if (snmp_scalar_set(h, ys,
cvk_val,
"candidate",
1,
reqinfo,
requests) < 0)
goto done;
}
else{
if (yrowst){
/* Check rowstatus */
if ((ret = snmp_table_rowstatus_get(h,
yrowst,
yrestype,
cvk_val,
&rowstatus)) < 0)
goto done;
}
else{
/* If no rowstatus object, default to active */
rowstatus = 1;
}
switch (rowstatus){
case 0: // not found, invalid
if ((ret = netsnmp_request_set_error(requests, SNMP_ERR_INCONSISTENTVALUE)) != SNMPERR_SUCCESS){
clicon_err(OE_SNMP, ret, "netsnmp_request_set_error");
goto ok;
}
break;
case 1: // active
case 4: // createAndGo
if (snmp_scalar_set(h, ys,
cvk_val,
"candidate",
0,
reqinfo,
requests) < 0)
goto done;
break;
case 2: // notInService
case 5: // createAndWait
if (snmp_scalar_set(h, ys,
cvk_val,
"candidate",
0,
reqinfo,
requests) < 0)
goto done;
break;
case 3: // notReady
if ((ret = netsnmp_request_set_error(requests, SNMP_ERR_INCONSISTENTVALUE)) != SNMPERR_SUCCESS){
clicon_err(OE_SNMP, ret, "netsnmp_request_set_error");
goto ok;
}
break;
case 6: // destroy
// XXX NYI
break;
}
}
ok:
retval = 1;
done:
@ -691,6 +869,8 @@ snmp_table_set(clicon_handle h,
cvec_free(cvk_val);
if (xpath)
free(xpath);
if (origtype)
free(origtype);
return retval;
fail:
retval = 0;
@ -826,8 +1006,8 @@ snmp_table_getnext(clicon_handle h,
*
* build_new_oid
*/
int
clixon_snmp_table_handler(netsnmp_mib_handler *handler,
static int
clixon_snmp_table_handler1(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
@ -920,3 +1100,31 @@ clixon_snmp_table_handler(netsnmp_mib_handler *handler,
xml_nsctx_free(nsc);
return retval;
}
/*! Top level request handler, loop over individual requests
*/
int
clixon_snmp_table_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int retval = -1;
netsnmp_request_info *req;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
for (req = requests; req; req = req->next){
ret = clixon_snmp_table_handler1(handler, nhreg, reqinfo, req);
if (ret != SNMP_ERR_NOERROR){
retval = ret;
goto done;
break;
}
}
retval = SNMP_ERR_NOERROR;
done:
return retval;
}

View file

@ -261,7 +261,7 @@ oid_print(FILE *f,
/*! Variant of yang_type_get that follows leafrefs
*/
static int
int
snmp_yang_type_get(yang_stmt *ys,
yang_stmt **yrefp,
char **origtypep,
@ -1000,10 +1000,9 @@ snmp_oid2str(oid **oidi,
cprintf(enc, "%c", (char)((*oidi)[i]&0xff));
}
break;
case CLIXON_ASN_FIXED_STRING: // XXX
for (; i<7; i++){
case CLIXON_ASN_FIXED_STRING:
for (; i < *oidilen; i++)
cprintf(enc, "%c", (char)((*oidi)[i]&0xff));
}
break;
default:
break;

View file

@ -63,7 +63,6 @@ struct clixon_snmp_handle {
cvec *sh_cvk_orig; /* Index/Key variable values (original) */
netsnmp_table_registration_info *sh_table_info; /* To mimic table-handler in libnetsnmp code
* save only to free properly */
};
typedef struct clixon_snmp_handle clixon_snmp_handle;
@ -74,6 +73,7 @@ int oid_eq(const oid * objid0, size_t objid0len, const oid * objid1, size_t o
int oid_append(const oid *objid0, size_t *objid0len, const oid *objid1, size_t objid1len);
int oid_cbuf(cbuf *cb, const oid *objid, size_t objidlen);
int oid_print(FILE *f, const oid *objid, size_t objidlen);
int snmp_yang_type_get(yang_stmt *ys, yang_stmt **yrefp, char **origtypep, yang_stmt **yrestypep, char **restypep);
int yangext_oid_get(yang_stmt *yn, oid *objid, size_t *objidlen, char **objidstr);
int snmp_access_str2int(char *modes_str);
const char *snmp_msg_int2str(int msg);

View file

@ -102,43 +102,56 @@ function testrun_createAndGo()
new "createAndGo"
new "Configuring a value without a row is a failure"
echo "$snmpset SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\' = 2"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\' = 2)" 0 "Error in packet."
new "RowStatus is active after createAndGo; can configure additional values afterwards"
echo "$snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndGo SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = 2"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\' = 2 2>&1)" 2 "Reason: inconsistentValue"
new "Set RowStatus to CreateAndGo and set tag"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndGo SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: createAndGo(4)"
new "Rowstatus is active"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: active(1)"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = 2"
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = STRING: 2"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = 1"
new "set storage type"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = INTEGER: other(1)"
}
function testrun_createAndWait()
{
new "createAndWait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndWait SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = createAndWait"
new "Set RowStatus to CreateAndWait and set tag"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndWait SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: createAndWait(5)"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = 2"
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'notify1' = STRING: 2"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: notInService(2)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = 1"
new "Set storagetype"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = INTEGER: other(1)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = active"
new "Set rowstatus to active/ commit"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: active(1)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 5)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = 5"
new "Set storagetype again"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'notify1\' = 5)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'notify1' = INTEGER: readOnly(5)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = createAndWait"
new "Set rowstatus to createAndWait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: createAndWait(5)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify2' = createAndGo)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify2' = createAndGo"
new "Set second rowstatus to createAndGo"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify2\' = createAndGo)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify2' = INTEGER: createAndGo(4)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = createAndWait"
new "Set third rowstatus to createAndWait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify3\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = INTEGER: createAndWait(5)"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = active"
new "Set third rowstatus to active"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify3\' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify3' = INTEGER: active(1)"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = INTEGER: notInService(2)"
}
@ -146,12 +159,16 @@ function testrun_removeRows()
{
new "removeRows"
new "Set rowstatus to createandgo"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = createAndGo)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1'"
new "Set rowstatus to destroy"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\' = destroy)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1' = destroy"
new "Set rowstatus to destroy"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'notify1'"
new "get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'notify1\')" 0 "No Such Instance currently exists at this OID)"
}
@ -163,9 +180,12 @@ function testexit()
new "SNMP tests"
testinit
if $snmp_debug; then
testrun_createAndGo
testrun_createAndWait
if $snmp_debug; then
# NYI
testrun_removeRows
fi

View file

@ -197,10 +197,9 @@ function testrun()
else
echo "$snmpset $oid $set_type $value"
expectpart "$($snmpset $oid $set_type $value)" 0 "$type: $value2"
fi
new "Check $name via SNMP"
if [ $type == "STRING" ]; then
if [ "$type" == "STRING" ]; then
expectpart "$($snmpget $oid)" 0 "$type:" "$value"
else
expectpart "$($snmpget $oid)" 0 "$type: $value2"
@ -227,7 +226,10 @@ testrun clixonExampleString STRING foobar foobar foobar ${MIB}.1.3
testrun ifPromiscuousMode INTEGER 1 1 true ${MIB}.1.10 # boolean
testrun ifIpAddr IPADDRESS 1.2.3.4 1.2.3.4 1.2.3.4 ${MIB}.1.13 # InetAddress
testrun ifPhysAddress STRING ff:ee:dd:cc:bb:aa ff:ee:dd:cc:bb:aa ff:ee:dd:cc:bb:aa ${IFMIB}.2.2.1.6.1
testrun ifStackStatus INTEGER 4 "createAndGo(4)" createAndGo ${IFMIB}.31.1.2.1.3.5.9
if $snmp_debug; then # rowstatus
testrun ifStackStatus INTEGER 4 "createAndGo(4)" active ${IFMIB}.31.1.2.1.3.5.9
fi
new "Cleaning up"
testexit