diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index b3f57b12..5043daa3 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -155,10 +155,63 @@ static const map_str2str yang_snmp_types[] = { {"uint16", "uint32"}, { NULL, NULL} /* if not found */ }; -char* yang_type_to_snmp(char* yang_type) + + /* A function that checks that all subtypes of the union are the same + * @param[in] ytype Yang resolved type (a union in this case) + * @param[out] cb Buffer where type of subtypes is written + * @retval 1 - true(All subtypes are the same), 0 - false + */ +int is_same_subtypes_union(yang_stmt *ytype, cbuf *cb) { - char* ret = clicon_str2str(yang_snmp_types, yang_type); - return (NULL == ret) ? yang_type : ret; + int retval = 0; + yang_stmt *y_sub_type = NULL; + yang_stmt *y_resolved_type = NULL; /* resolved type */ + char *resolved_type_str; /* resolved type */ + char *type_str = NULL; + + int options = 0; + cvec *cvv = NULL; + cvec *patterns = NULL; + uint8_t fraction_digits = 0; + + /* Loop over all sub-types in the resolved union type, note these are + * not resolved types (unless they are built-in, but the resolve call is + * made in the union_one call. + */ + while ((y_sub_type = yn_each(ytype, y_sub_type)) != NULL) + { + if (yang_keyword_get(y_sub_type) != Y_TYPE) + continue; + + if (yang_type_resolve(ytype, ytype, y_sub_type, + &y_resolved_type, &options, + &cvv, patterns, NULL, &fraction_digits) < 0 || ( NULL == y_resolved_type) ) + break; + if( (NULL == (resolved_type_str = yang_argument_get(y_resolved_type))) ) + break; + if( NULL == type_str || strcmp(type_str, resolved_type_str) == 0) + type_str = resolved_type_str; + else + break; + } + if( NULL == y_sub_type && NULL != type_str ) + { + cbuf_append_str(cb, resolved_type_str); + retval = 1; + } + return retval; +} +char* yang_type_to_snmp(yang_stmt *ytype, char* yang_type_str) +{ + char* type_str = yang_type_str; + if (yang_type_str && strcmp(yang_type_str, "union") == 0) + { + cbuf *cb = cbuf_new(); + if( is_same_subtypes_union(ytype, cb) > 0) + type_str = cbuf_get(cb); + } + char* ret = clicon_str2str(yang_snmp_types, type_str); + return (NULL == ret) ? type_str : ret; } /*! Translate from snmp string to int representation @@ -296,7 +349,7 @@ snmp_yang_type_get(yang_stmt *ys, if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0) goto done; restype = yrestype?yang_argument_get(yrestype):NULL; - restype = yang_type_to_snmp(restype); + restype = yang_type_to_snmp(restype, restype); if (strcmp(restype, "leafref")==0){ if ((ypath = yang_find(yrestype, Y_PATH, NULL)) == NULL){ clicon_err(OE_YANG, 0, "No path in leafref"); diff --git a/test/test_snmp_union.sh b/test/test_snmp_union.sh new file mode 100644 index 00000000..7eba648a --- /dev/null +++ b/test/test_snmp_union.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# SNMP test for yang union type with are same types of subtypes + + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +# Re-use main example backend state callbacks +APPNAME=example + +if [ ${ENABLE_NETSNMP} != "yes" ]; then + echo "Skipping test, Net-SNMP support not enabled." + rm -rf $dir + if [ "$s" = $0 ]; then exit 0; else return 0; fi +fi + +cfg=$dir/conf_startup.xml +fyang=$dir/clixon-example.yang +fstate=$dir/state.xml + +# AgentX unix socket +SOCK=/var/run/snmp.sock + +cat < $cfg + + $cfg + ${YANG_INSTALLDIR} + ${YANG_STANDARD_DIR} + ${MIB_GENERATED_YANG_DIR} + $fyang + $dir/$APPNAME.sock + /usr/local/lib/$APPNAME/backend + /var/tmp/$APPNAME.pidfile + $dir + unix:$SOCK + ENTITY-MIB + true + +EOF + +cat < $fyang +module clixon-example{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + + import ietf-yang-smiv2 { + prefix smiv2; + } + typedef first { + type string{ + pattern + "first"; + } + description "first string"; + } + typedef second { + type string{ + pattern + "second"; + } + description "second string"; + } + typedef third { + type string{ + pattern + "third"; + } + description "third string"; + } + + /* Generic config data */ + container table{ + smiv2:oid "1.3.6.1.2.1.47.1.1.1"; + list parameter{ + smiv2:oid "1.3.6.1.2.1.47.1.1.1.1"; + key name; + cx-snmp:table-key "value"; + leaf name{ + type union{ + type ex:first; + type ex:second; + type ex:third; + } + description "name"; + smiv2:oid "1.3.6.1.2.1.47.1.1.1.1.1"; + } + } + } +} +EOF + +# This is state data written to file that backend reads from (on request) + +cat < $fstate + + + first + + + second + + + third + +
+EOF +function testinit(){ + new "test params: -s init -f $cfg -- -sS $fstate" + if [ $BE -ne 0 ]; then + # Kill old backend and start a new one + new "kill old backend" + sudo clixon_backend -zf $cfg + if [ $? -ne 0 ]; then + err "Failed to start backend" + fi + + sudo pkill -f clixon_backend + + new "Starting backend" + start_backend -s init -f $cfg -- -sS $fstate + fi + + new "wait backend" + wait_backend + + if [ $SN -ne 0 ]; then + # Kill old clixon_snmp, if any + new "Terminating any old clixon_snmp processes" + sudo killall -q clixon_snmp + + new "Starting clixon_snmp" + start_snmp $cfg & + fi + + new "wait snmp" + wait_snmp +} + +function testexit(){ + stop_snmp + + if [ $BE -ne 0 ]; then + new "Kill backend" + # Check if premature kill + pid=$(pgrep -u root -f clixon_backend) + if [ -z "$pid" ]; then + err "backend already dead" + fi + # kill backend + stop_backend -f $cfg + fi +} + +ENTITY_OID=".1.3.6.1.2.1.47.1.1.1" + +# first string, value=first +OID_FIRST="${ENTITY_OID}.1.1.1" +# second string, value=second +OID_SECOND="${ENTITY_OID}.1.1.2" +# third string, value=third +OID_THIRD="${ENTITY_OID}.1.1.3" + +new "SNMP system tests" +testinit + +new "Get index, $OID_FIRST" +validate_oid $OID_FIRST $OID_FIRST "STRING" "first" +new "Get next $OID_FIRST" +validate_oid $OID_FIRST $OID_SECOND "STRING" "second" +new "Get index, $OID_SECOND" +validate_oid $OID_SECOND $OID_SECOND "STRING" "second" +new "Get next $OID_SECOND" +validate_oid $OID_SECOND $OID_THIRD "STRING" "third" +new "Get index, $OID_THIRD" +validate_oid $OID_THIRD $OID_THIRD "STRING" "third" + +new "Cleaning up" +testexit + +rm -rf $dir + +new "endtest" +endtest \ No newline at end of file