diff --git a/apps/snmp/snmp_lib.c b/apps/snmp/snmp_lib.c index b3f57b125..c95777fa6 100644 --- a/apps/snmp/snmp_lib.c +++ b/apps/snmp/snmp_lib.c @@ -155,10 +155,61 @@ 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) + * @retval 0 - false + */ +int is_same_subtypes_union(yang_stmt *ytype, cbuf *cb) +{ + 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* ret = clicon_str2str(yang_snmp_types, yang_type); - return (NULL == ret) ? yang_type : ret; + 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 +347,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(yrestype, 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 000000000..49fab3f40 --- /dev/null +++ b/test/test_snmp_union.sh @@ -0,0 +1,186 @@ +#!/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 + +# Relies on example_backend.so for $fstate file handling + +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 + clixon-example + true + +EOF + +cat < $fyang +module clixon-example{ + yang-version 1.1; + namespace "urn:example:clixon"; + prefix ex; + import ietf-yang-smiv2 { + prefix smiv2; + } + /* 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 Index; + + leaf Index{ + type int32; + smiv2:oid "1.3.6.1.2.1.47.1.1.1.1.1"; + smiv2:max-access "read-only"; + } + leaf Union_exm{ + description "Union with same subtypes"; + config false; + type union + { + type int32; + type int32; + } + smiv2:oid "1.3.6.1.2.1.47.1.1.1.1.2"; + smiv2:max-access "read-only"; + } + } + } +} +EOF + +# This is state data written to file that backend reads from (on request) +# integer and string have values, sleeper does not and uses default (=1) + +cat < $fstate + + + 2 + 4 + + + 12 + 14 + +
+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" + +# name, value=2 +OID1="${ENTITY_OID}.1.1.2" +# name, value=12 +OID2="${ENTITY_OID}.1.1.12" +# value, value=2 +OID3="${ENTITY_OID}.1.2.2" +# value, value=12 +OID4="${ENTITY_OID}.1.2.12" +# stat, value=2 +OIDX="${ENTITY_OID}.1.3.2" +# stat, value=12 +OIDY="${ENTITY_OID}.1.3.12" + + +new "SNMP system tests" +testinit + +new "Get index, $OID1" +validate_oid $OID1 $OID1 "INTEGER" "2" + +new "Get next $OID1" +validate_oid $OID1 $OID2 "INTEGER" "12" + +new "Get index, $OID2" +validate_oid $OID2 $OID2 "INTEGER" "12" +new "Get next $OID2" +validate_oid $OID2 $OID3 "INTEGER" "4" + +new "Get index, $OID3" +validate_oid $OID3 $OID3 "INTEGER" "4" + +new "Get next $OID4" +validate_oid $OID3 $OID4 "INTEGER" "14" + +new "Get index, $OID4" +validate_oid $OID4 $OID4 "INTEGER" "14" + +new "Cleaning up" +testexit + +rm -rf $dir + +new "endtest" +endtest \ No newline at end of file