Merge branch 'snmp'

This commit is contained in:
Olof hagsand 2022-07-15 15:54:44 +02:00
commit 1d78241115
47 changed files with 8494 additions and 46 deletions

View file

@ -2,9 +2,9 @@ name: Clixon CI
on: on:
push: push:
branches: [ master, http-data ] branches: [ master, snmp ]
pull_request: pull_request:
branches: [ master, http-data ] branches: [ master, snmp ]
jobs: jobs:
build: build:

View file

@ -35,6 +35,20 @@
* [3.3.2](#332) Aug 27 2017 * [3.3.2](#332) Aug 27 2017
* [3.3.1](#331) June 7 2017 * [3.3.1](#331) June 7 2017
## SNMP branch
* Clixon SNMP frontend
* Support of SNMP for retreiving and setting values via net-snmp using a MIB-YANG mapping defined in RFC6643.
* For details, see [SNMP section of user manual](https://clixon-docs.readthedocs.io/en/latest/snmp.html)
* YANG `clixon-config@2022-03-21.yang` changes:
* Added options:
* `CLICON_SNMP_AGENT_SOCK`
* `CLICON_SNMP_MIB`
* New configure options:
* `--enable-netsnmp`
* `--with-mib-generated-yang-dir=DIR`
* Thanks to Siklu Communications LTD for sponsoring this work
## 5.8.0 ## 5.8.0
Planned: July 2022 Planned: July 2022
@ -121,8 +135,6 @@ Developers may need to change their code
The Clixon 5.7 release introduces (long overdue) NETCONF chunked framing as defined The Clixon 5.7 release introduces (long overdue) NETCONF chunked framing as defined
in RFC 6242. It also introduces a limited http data service and lots of bugfixes. in RFC 6242. It also introduces a limited http data service and lots of bugfixes.
### New features
* Implementation of "chunked framing" according to RFC6242 for Netconf 1.1. * Implementation of "chunked framing" according to RFC6242 for Netconf 1.1.
* First hello is 1.0 EOM framing, then successing rpc is chunked framing * First hello is 1.0 EOM framing, then successing rpc is chunked framing
* See * See

View file

@ -36,6 +36,7 @@ VPATH = @srcdir@
srcdir = @srcdir@ srcdir = @srcdir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
with_restconf = @with_restconf@ with_restconf = @with_restconf@
enable_netsnmp = @enable_netsnmp@
SHELL = /bin/sh SHELL = /bin/sh
@ -47,6 +48,11 @@ SUBDIRS += netconf
ifdef with_restconf ifdef with_restconf
SUBDIRS += restconf SUBDIRS += restconf
endif endif
ifdef enable_netsnmp
ifeq ($(enable_netsnmp),yes)
SUBDIRS += snmp
endif
endif
.PHONY: all clean depend install $(SUBDIRS) .PHONY: all clean depend install $(SUBDIRS)

View file

@ -453,7 +453,7 @@ usage(clicon_handle h,
"\t-p <dir>\tAdd Yang directory path (see CLICON_YANG_DIR)\n" "\t-p <dir>\tAdd Yang directory path (see CLICON_YANG_DIR)\n"
"\t-b <dir>\tSpecify datastore directory\n" "\t-b <dir>\tSpecify datastore directory\n"
"\t-F\t\tRun in foreground, do not run as daemon\n" "\t-F\t\tRun in foreground, do not run as daemon\n"
"\t-z\t\tKill other config daemon and exit\n" "\t-z\t\tKill other backend daemon and exit\n"
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)(default: %s)\n" "\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)(default: %s)\n"
"\t-P <file>\tPid filename (default: %s)\n" "\t-P <file>\tPid filename (default: %s)\n"
@ -1004,7 +1004,7 @@ main(int argc,
goto done; goto done;
/* Write pid-file */ /* Write pid-file */
if ((pid = pidfile_write(pidfile)) < 0) if (pidfile_write(pidfile) < 0)
goto done; goto done;
if (set_signal(SIGTERM, backend_sig_term, NULL) < 0){ if (set_signal(SIGTERM, backend_sig_term, NULL) < 0){

View file

@ -861,7 +861,7 @@ cli_show_options(clicon_handle h,
else else
fprintf(stdout, "%s: NULL\n", keys[i]); fprintf(stdout, "%s: NULL\n", keys[i]);
} }
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree /* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB from config tree
* Since they are lists they are placed in the config tree. * Since they are lists they are placed in the config tree.
*/ */
x = NULL; x = NULL;
@ -876,6 +876,12 @@ cli_show_options(clicon_handle h,
continue; continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x)); fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
} }
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
}
retval = 0; retval = 0;
done: done:
if (keys) if (keys)

134
apps/snmp/Makefile.in Normal file
View file

@ -0,0 +1,134 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the Apache License version 2 or the GPL.
#
# ***** END LICENSE BLOCK *****
#
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LINKAGE = @LINKAGE@
INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
prefix = @prefix@
datarootdir = @datarootdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
sbindir = @sbindir@
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
includedir = @includedir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@
LIBSTATIC_SUFFIX = @LIBSTATIC_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
# Use this clixon lib for linking
ifeq ($(LINKAGE),dynamic)
CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
else
CLIXON_LIB = libclixon$(LIBSTATIC_SUFFIX)
endif
# For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src $(top_srcdir)/lib/src/$(CLIXON_LIB) @LIBS@
CPPFLAGS = @CPPFLAGS@
ifeq ($(LINKAGE),dynamic)
CPPFLAGS += -fPIC
endif
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
# Application
APPL = clixon_snmp
# Common source - not accessible from plugin - independent of restconf package (fcgi|native)
APPSRC =
APPSRC += snmp_main.c
APPSRC += snmp_register.c
APPSRC += snmp_handler.c
APPSRC += snmp_lib.c
APPOBJ = $(APPSRC:.c=.o)
all: $(APPL)
# Dependency of clixon library (LIBDEPS)
$(top_srcdir)/lib/src/$(CLIXON_LIB):
(cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB))
clean:
rm -f *.core $(APPL) $(APPOBJ) *.o
rm -f *.gcda *.gcno *.gcov # coverage
distclean: clean
rm -f Makefile *~ .depend
# Put daemon in bin
# Put other executables in libexec/
install: $(APPL)
install -d -m 0755 $(DESTDIR)$(sbindir)
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(sbindir)
install-include:
uninstall:
rm -f $(DESTDIR)$(sbindir)/$(APPL)
.SUFFIXES:
.SUFFIXES: .c .o
.c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_snmp\" $(CPPFLAGS) $(CFLAGS) -c $<
$(APPL) : $(APPOBJ) $(LIBDEPS)
echo $(APPOBJ)
echo $(LIBDEPS)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPFCGI) $(APPSRC) > .depend
#include .depend

1360
apps/snmp/snmp_handler.c Normal file

File diff suppressed because it is too large Load diff

61
apps/snmp/snmp_handler.h Normal file
View file

@ -0,0 +1,61 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_HANDLER_H_
#define _SNMP_HANDLER_H_
/*
* Prototypes
*/
int clixon_snmp_table_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests);
int clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests);
#endif /* _SNMP_HANDLER_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

1208
apps/snmp/snmp_lib.c Normal file

File diff suppressed because it is too large Load diff

117
apps/snmp/snmp_lib.h Normal file
View file

@ -0,0 +1,117 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_LIB_H_
#define _SNMP_LIB_H_
/*
* Constants
*/
/* Need some way to multiplex SNMP_ and MIB errors on OE_SNMP error handler */
#define CLIXON_ERR_SNMP_MIB 0x1000
#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"
/* Special case/extended Clixon ASN1 types
* Set in type_yang2asn1() if extended is true
* Must be back to proper net-snmp ASN_ types in type_snmp2xml and type_xml2snmp
* before calling netsnmp API
*/
#define CLIXON_ASN_EXTRAS 253 /* Special case clixon address >= this */
#define CLIXON_ASN_PHYS_ADDR 253 /* Special case phy-address */
#define CLIXON_ASN_FIXED_STRING 254 /* RFC2578 Sec 7.7: String-valued, fixed-length */
#define CLIXON_ASN_ROWSTATUS 255
/*
* Types
*/
/* Userdata to pass around in netsmp callbacks
*/
struct clixon_snmp_handle {
clicon_handle sh_h;
yang_stmt *sh_ys; /* Leaf for scalar, list for table */
oid sh_oid[MAX_OID_LEN]; /* OID for debug, may be removed? */
size_t sh_oidlen;
char *sh_default; /* MIB default value leaf only */
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;
/*
* Prototypes
*/
int oid_eq(const oid * objid0, size_t objid0len, const oid * objid1, size_t objid1len);
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);
void *snmp_handle_clone(void *arg);
void snmp_handle_free(void *arg);
int type_yang2asn1(yang_stmt *ys, int *asn1_type, int extended);
int type_snmp2xml(yang_stmt *ys,
int *asn1type,
netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests,
char **valstr);
int type_xml2snmp_pre(char *xmlstr, yang_stmt *ys, char **snmpstr);
int type_xml2snmp(char *snmpstr, int *asn1type, u_char **snmpval, size_t *snmplen, char **reason);
int snmp_yang2xpath(yang_stmt *ys, cvec *keyvec, char **xpath);
int snmp_str2oid(char *str, yang_stmt *yi, oid *objid, size_t *objidlen);
int snmp_oid2str(oid **oidi, size_t *oidilen, yang_stmt *yi, cg_var *cv);
int clixon_snmp_err_cb(void *handle, int suberr, cbuf *cb);
int snmp_xmlkey2val_oid(cxobj *xrow, cvec *cvk_name, cvec **cvk_orig, oid *objidk, size_t *objidklen);
/*========== libnetsnmp-specific code =============== */
int clixon_snmp_api_agent_check(void);
int clixon_snmp_api_agent_cleanup(void);
int clixon_snmp_api_oid_find(oid *oid1, size_t oidlen);
#endif /* _SNMP_LIB_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

550
apps/snmp/snmp_main.c Normal file
View file

@ -0,0 +1,550 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* This is the clixon_snmp daemon
* It assumes a netsnmp damon is running.
* - If netsnmp does not run, clixon_snmp will not start
* - If netsnmp dies, clixon_snmp will exit
* - If netsnmp is restarted, clixon_snmp should also be restarted
* It is possible to be more resilient, such as setting a timer and trying again, in fact, libnetsnmp
* has some such mechanisms but these are NOT implemented
* @see RFC 6643 Translation of Structure of Management Information Version 2 (SMIv2)
* MIB Modules to YANG Modules
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "snmp_lib.h"
#include "snmp_register.h"
/* Command line options to be passed to getopt(3) */
#define SNMP_OPTS "hD:f:l:o:z"
/* Forward */
static int clixon_snmp_input_cb(int s, void *arg);
/*! Return (hardcoded) pid file
*/
static char*
clicon_snmp_pidfile(clicon_handle h)
{
return "/var/tmp/clixon_snmp.pid";
}
/*! Signal terminates process
* Just set exit flag for proper exit in event loop
*/
static void
clixon_snmp_sig_term(int arg)
{
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
__PROGRAM__, __FUNCTION__, getpid(), arg);
/* This should ensure no more accepts or incoming packets are processed because next time eventloop
* is entered, it will terminate.
* However there may be a case of sockets closing rather abruptly for clients
*/
clixon_exit_set(1);
}
/*! Clean and close all state of netconf process (but dont exit).
* Cannot use h after this
* @param[in] h Clixon handle
*/
static int
snmp_terminate(clicon_handle h)
{
yang_stmt *yspec;
cvec *nsctx;
cxobj *x = NULL;
char *pidfile = clicon_snmp_pidfile(h);
snmp_shutdown(__FUNCTION__);
shutdown_agent();
clixon_snmp_api_agent_cleanup();
if (clicon_ptr_get(h, "snmp-rowstatus-tree", (void**)&x) == 0 && x){
xml_free(x);
x = NULL;
}
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
ys_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
ys_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
xpath_optimize_exit();
clixon_event_exit();
clicon_handle_exit(h);
clixon_err_exit();
clicon_log_exit();
if (pidfile)
unlink(pidfile);
return 0;
}
/*! Get which sockets are used from SNMP API, the register single sockets into clixon event system
*
* @param[in] h Clixon handle
* @param[in] register if 1 register snmp sockets with event handler. If 0 close and unregister
* This is a workaround for netsnmps API usiing fdset:s, instead an fdset is created before calling
* the snmp api
* if you use select(), see snmp_select_info() in snmp_api(3)
* snmp_select_info(int *numfds, fd_set *fdset, struct timeval *timeout, int *block)
* @see clixon_snmp_input_cb
*/
static int
clixon_snmp_fdset_register(clicon_handle h,
int regfd)
{
int retval = -1;
int numfds = 0;
fd_set readfds;
struct timeval timeout = { LONG_MAX, 0 };
int block = 0;
int nr;
int s;
FD_ZERO(&readfds);
if ((nr = snmp_sess_select_info(NULL, &numfds, &readfds, &timeout, &block)) < 0){
clicon_err(OE_XML, errno, "snmp_select_error");
goto done;
}
/* eg 4, 6, 8 */
for (s=0; s<numfds; s++){
if (FD_ISSET(s, &readfds)){
clicon_debug(1, "%s %d", __FUNCTION__, s);
if (regfd){
if (clixon_event_reg_fd(s, clixon_snmp_input_cb, h, "snmp socket") < 0)
goto done;
}
else{
if (clixon_event_unreg_fd(s, clixon_snmp_input_cb) < 0)
goto done;
close(s);
}
}
}
retval = 0;
done:
return retval;
}
/*! Callback for single socket
* This is a workaround for netsnmps API usiing fdset:s, instead an fdset is created before calling
* the snmp api
* @param[in] s Read socket
* @param[in] arg Clixon handle
*/
static int
clixon_snmp_input_cb(int s,
void *arg)
{
int retval = -1;
fd_set readfds;
clicon_handle h = (clicon_handle)arg;
int ret;
clicon_debug(1, "%s %d", __FUNCTION__, s);
FD_ZERO(&readfds);
FD_SET(s, &readfds);
(void)snmp_read(&readfds);
if (clixon_event_poll(s) < 0){
if (errno == EBADF){
clicon_err_reset();
/* Close the active socket */
if (clixon_event_unreg_fd(s, clixon_snmp_input_cb) < 0)
goto done;
close(s);
/* and then the others */
if (clixon_snmp_fdset_register(h, 0) < 0)
goto done;
if ((ret = snmp_close_sessions()) != 1){
clicon_err(OE_SNMP, ret, "snmp_close_sessions");
goto done;
}
/* Signal normal exit to upper layers (=event handling)
* One can signal error and return -1, but it is nicer with an orderly exit
*/
clixon_exit_set(1);
}
else {
clicon_err(OE_UNIX, errno, "poll");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Init netsnmp agent connection
* @param[in] h Clixon handle
* @param[in] logdst Log destination, see clixon_log.h
* @see snmp_terminate
*/
static int
clixon_snmp_init_subagent(clicon_handle h,
int logdst)
{
int retval = -1;
char *sockpath = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (logdst == CLICON_LOG_SYSLOG)
snmp_enable_calllog();
else
snmp_enable_stderrlog();
/* 0 if master, 1 if client */
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
/* don't load config and don't load/save persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
/* don't load persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
/* don't save persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE, 1);
if (clicon_debug_get())
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1);
if ((sockpath = clicon_option_str(h, "CLICON_SNMP_AGENT_SOCK")) == NULL){
clicon_err(OE_XML, 0, "CLICON_SNMP_AGENT_SOCK not set");
goto done;
}
/* XXX: This should be configurable. */
netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, sockpath);
/* initialize the agent library */
init_agent(__PROGRAM__);
/* example-demon will be used to read example-demon.conf files. */
init_snmp(__PROGRAM__);
if (!clixon_snmp_api_agent_check()){
clicon_err(OE_DAEMON, 0, "Connection to SNMP agent failed");
goto done;
}
if (set_signal(SIGTERM, clixon_snmp_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGINT, clixon_snmp_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGPIPE, SIG_IGN, NULL) < 0){
clicon_err(OE_UNIX, errno, "Setting SIGPIPE signal");
goto done;
}
/* Workaround for netsnmps API use of fdset:s instead of sockets */
if (clixon_snmp_fdset_register(h, 1) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Usage help routine
* @param[in] h Clixon handle
* @param[in] argv0 command line
*/
static void
usage(clicon_handle h,
char *argv0)
{
fprintf(stderr, "usage:%s\n"
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D <level>\tDebug level\n"
"\t-f <file>\tConfiguration file (mandatory)\n"
"\t-l (e|o|s|f<file>) Log on std(e)rr, std(o)ut, (s)yslog(default), (f)ile\n"
"\t-z\t\tKill other %s daemon and exit\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0, argv0
);
exit(0);
}
int
main(int argc,
char **argv)
{
int retval = -1;
int c;
char *argv0 = argv[0];
clicon_handle h;
int logdst = CLICON_LOG_STDERR;
struct passwd *pw;
yang_stmt *yspec = NULL;
char *str;
uint32_t id;
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
int dbg = 0;
size_t sz;
int pid;
char *pidfile = NULL;
struct stat st;
int zap = 0;
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
return -1;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
/* Set username to clixon handle. Use in all communication to backend */
if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid");
goto done;
}
if (clicon_username_set(h, pw->pw_name) < 0)
goto done;
while ((c = getopt(argc, argv, SNMP_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
usage(h, argv[0]);
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv[0]);
break;
case 'f': /* override config file */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLICON_LOG_FILE &&
strlen(optarg)>1 &&
clicon_log_file(optarg+1) < 0)
goto done;
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL);
/*
* Register error category and error/log callbacks for netsnmp special error handling
*/
if (clixon_err_cat_reg(OE_SNMP, /* category */
h, /* handle (can be NULL) */
clixon_snmp_err_cb /* log fn */
) < 0)
goto done;
yang_init(h);
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0)
goto done;
/* Now rest of options */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, SNMP_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
case 'D' : /* debug */
case 'f': /* config file */
case 'l': /* log */
break; /* see above */
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
case 'z': /* Zap other process */
zap++;
break;
default:
usage(h, argv[0]);
break;
}
argc -= optind;
argv += optind;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
/* Check pid-file, if zap kill the old daemon, else return here */
if ((pidfile = clicon_snmp_pidfile(h)) == NULL){
clicon_err(OE_FATAL, 0, "pidfile not set");
goto done;
}
if (pidfile_get(pidfile, &pid) < 0)
goto done;
if (zap){
if (pid && pidfile_zapold(pid) < 0)
return -1;
if (lstat(pidfile, &st) == 0)
unlink(pidfile);
snmp_terminate(h);
exit(0); /* OK */
}
else if (pid){
clicon_err(OE_DAEMON, 0, "Clixon_snmp daemon already running with pid %d\n(Try killing it with %s -z)",
pid, argv0);
return -1; /* goto done deletes pidfile */
}
/* Here there is either no old process or we have killed it,.. */
if (lstat(pidfile, &st) == 0)
unlink(pidfile);
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
if ((sz = clicon_option_int(h, "CLICON_LOG_STRING_LIMIT")) != 0)
clicon_log_string_limit_set(sz);
/* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
xml_nsctx_namespace_netconf_default(h);
/* Add (hardcoded) netconf features in case ietf-netconf loaded here
* Otherwise it is loaded in netconf_module_load below
*/
if (netconf_module_features(h) < 0)
goto done;
/* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
/* Load Yang modules
* 1. Load a yang module as a specific absolute filename */
if ((str = clicon_yang_main_file(h)) != NULL){
if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
/* 2. Load a (single) main module */
if ((str = clicon_yang_module_main(h)) != NULL){
if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
yspec) < 0)
goto done;
}
/* 3. Load all modules in a directory */
if ((str = clicon_yang_main_dir(h)) != NULL){
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Add netconf yang spec, used by netconf client and as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
if (dbg)
clicon_option_dump(h, dbg);
/* Get session id from backend hello */
clicon_session_id_set(h, getpid());
/* Send hello request to backend to get session-id back
* This is done once at the beginning of the session and then this is
* used by the client, even though new TCP sessions are created for
* each message sent to the backend.
*/
if (clicon_hello_req(h, &id) < 0)
goto done;
clicon_session_id_set(h, id);
/* Init snmp as subagent */
if (clixon_snmp_init_subagent(h, logdst) < 0)
goto done;
/* Init and traverse mib-translated yangs and register callbacks */
if (clixon_snmp_traverse_mibyangs(h) < 0)
goto done;
/* Write pid-file */
if (pidfile_write(pidfile) < 0)
goto done;
/* main event loop */
if (clixon_event_loop(h) < 0)
goto done;
retval = 0;
done:
snmp_terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
return retval;
}

532
apps/snmp/snmp_register.c Normal file
View file

@ -0,0 +1,532 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* See RFC 6643
* Extensions are grouped in some categories, the one I have seen are, example:
* 1. leaf
* smiv2:max-access "read-write";
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:defval "42"; (not always)
* 2. container, list
* smiv2:oid "1.3.6.1.4.1.8072.2.1";
* 3. module level
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
*
* SNMP messages:
* 160 MODE_GETNEXT / SNMP_MSG_GET
* 161 MODE_GET / SNMP_MSG_GETNEXT
* 0 MODE_SET_RESERVE1
* 1 MODE_SET_RESERVE2
* 2 MODE_SET_ACTION
* 3 MODE_SET_COMMIT
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <assert.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "snmp_lib.h"
#include "snmp_register.h"
#include "snmp_handler.h"
/*! Parse smiv2 extensions for YANG leaf
* Typical leaf:
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:max-access "read-write";
* smiv2:defval "42"; (optional)
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node
* @param[in] cvk_orig Vector of untranslated key/index values (eg "foo")
* @param[in] oidk Part of OID thatrepresents key
* @param[in] oidklen Length of oidk
* @retval 0 OK
* @retval -1 Error
* netsnmp_subtree_find(oid1,sz1, 0, 0)
*/
static int
mibyang_leaf_register(clicon_handle h,
yang_stmt *ys,
cvec *cvk_val,
oid *oidk,
size_t oidklen)
{
int retval = -1;
netsnmp_handler_registration *nhreg = NULL;
netsnmp_mib_handler *handler;
int ret;
char *modes_str = NULL;
char *default_str = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t oid1len = MAX_OID_LEN;
int modes;
char *name;
clixon_snmp_handle *sh;
cbuf *cboid = NULL;
if ((cboid = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if ((ret = yangext_oid_get(ys, oid1, &oid1len, NULL)) < 0)
goto done;
if (ret == 0)
goto ok;
if (oid_append(oid1, &oid1len, oidk, oidklen) < 0)
goto done;
/* Check if already registered */
if (clixon_snmp_api_oid_find(oid1, oid1len) == 1)
goto ok;
if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done;
/* Only for sanity check of types initially to fail early */
if (type_yang2asn1(ys, NULL, 0) < 0)
goto done;
/* Get modes (access) read-only, read-write, not-accessible, accessible-for-notify
*/
if (modes_str == NULL)
goto ok;
modes = snmp_access_str2int(modes_str);
/* SMI default value, How is this different from yang defaults?
*/
if (yang_extension_value(ys, "defval", IETF_YANG_SMIV2_NS, NULL, &default_str) < 0)
goto done;
name = yang_argument_get(ys);
/* Stateless function, just returns ptr */
if ((handler = netsnmp_create_handler(name, clixon_snmp_scalar_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
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;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = oid1len;
sh->sh_default = default_str;
if (cvk_val &&
(sh->sh_cvk_orig = cvec_dup(cvk_val)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
/* Stateless function, just returns ptr */
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, oid1len,
modes)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
/* Register our application data and how to free it */
handler->myvoid = (void*)sh;
handler->data_clone = snmp_handle_clone;
handler->data_free = snmp_handle_free;
/*
* XXX: nhreg->agent_data
*/
if ((ret = netsnmp_register_instance(nhreg)) != SNMPERR_SUCCESS){
/* Note MIB_ errors, not regular SNMPERR_ */
clicon_err(OE_SNMP, ret-CLIXON_ERR_SNMP_MIB, "netsnmp_register_instance");
goto done;
}
oid_cbuf(cboid, oid1, oid1len);
clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, cbuf_get(cboid));
ok:
retval = 0;
done:
if (cboid)
cbuf_free(cboid);
return retval;
}
/*! Register table entry handler itself (not column/row leafs)
*
* 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
mibyang_table_register(clicon_handle h,
yang_stmt *ylist)
{
int retval = -1;
netsnmp_handler_registration *nhreg;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t oid1len = 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;
yang_stmt *ys;
if ((ys = yang_parent_get(ylist)) == NULL ||
yang_keyword_get(ys) != Y_CONTAINER){
clicon_err(OE_YANG, EINVAL, "ylist parent is not list");
goto done;
}
/* Get OID from parent container */
if ((ret = yangext_oid_get(ys, oid1, &oid1len, &oidstr)) < 0)
goto done;
if (ret == 0)
goto ok;
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 = ylist;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = oid1len;
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, oid1len,
HANDLER_CAN_RWRITE)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
/* Register our application data and how to free it */
handler->myvoid =(void*)sh;
handler->data_clone = snmp_handle_clone;
handler->data_free = snmp_handle_free;
/* 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) < 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 = 1;
/* 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;
}
sh->sh_table_info = table_info; /* Keep to free at exit */
clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, oidstr);
ok:
retval = 0;
done:
return retval;
}
/*! Register table sub-oid:s of existing entries in clixon
* 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
*/
int
mibyang_table_poll(clicon_handle h,
yang_stmt *ylist)
{
int retval = -1;
cvec *nsc = NULL;
char *xpath = NULL;
cxobj *xt = NULL;
cxobj *xerr;
cxobj *xtable;
cxobj *xrow;
cxobj *xcol;
yang_stmt *y;
cvec *cvk_name;
cvec *cvk_val = NULL; /* vector of index keys: original index */
yang_stmt *ys;
int ret;
oid oidk[MAX_OID_LEN] = {0,};
size_t oidklen = MAX_OID_LEN;
clicon_debug(1, "%s", __FUNCTION__);
if ((ys = yang_parent_get(ylist)) == NULL ||
yang_keyword_get(ys) != Y_CONTAINER){
clicon_err(OE_YANG, EINVAL, "ylist parent is not list");
goto done;
}
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
if (snmp_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 ((cvk_name = 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 ((ret = snmp_xmlkey2val_oid(xrow, cvk_name, &cvk_val, oidk, &oidklen)) < 0)
goto done;
if (ret == 0)
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 (mibyang_leaf_register(h, y, cvk_val, oidk, oidklen) < 0)
goto done;
}
}
}
retval = 0;
done:
if (xpath)
free(xpath);
if (cvk_val)
cvec_free(cvk_val);
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
* traversed before a child.
* Extensions are grouped in some categories, the one I have seen are, example:
* 1. leaf
* smiv2:max-access "read-write";
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:defval "42"; (not always)
* 2. container, list
* smiv2:oid "1.3.6.1.4.1.8072.2.1";
* 3. module level
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
* @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
mibyang_traverse(clicon_handle h,
yang_stmt *yn)
{
int retval = -1;
yang_stmt *ys = NULL;
yang_stmt *yp;
int ret;
clicon_debug(1, "%s %s", __FUNCTION__, yang_argument_get(yn));
switch(yang_keyword_get(yn)){
case Y_LEAF:
if (mibyang_leaf_register(h, yn, NULL, NULL, 0) < 0)
goto done;
break;
case Y_CONTAINER: /* See list case */
break;
case Y_LIST: /* If parent is container -> identify as table */
yp = yang_parent_get(yn);
if (yang_keyword_get(yp) == Y_CONTAINER){
/* Register table entry handler itself (not column/row leafs) */
if (mibyang_table_register(h, yn) < 0)
goto done;
goto ok;
}
break;
default:
break;
}
/* Traverse data nodes in tree (module is special case */
ys = NULL;
while ((ys = yn_each(yn, ys)) != NULL) {
if (!yang_schemanode(ys))
continue;
if ((ret = mibyang_traverse(h, ys)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
}
ok:
retval = 0;
done:
return retval;
}
/*! Init mib-translated yangs and register callbacks by traversing the yang
*
* @þaram[in] h Clixon handle
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_snmp_traverse_mibyangs(clicon_handle h)
{
int retval = -1;
char *modname;
cxobj *x;
yang_stmt *yspec;
yang_stmt *ymod;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
/* Loop over clixon configuration file to find all CLICON_SNMP_MIB, and
* then loop over all those MIBs to register OIDs with netsnmp
*/
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
if ((modname = xml_body(x)) == NULL)
continue;
clicon_debug(1, "%s %s: \"%s\"", __FUNCTION__, xml_name(x), modname);
/* Note, here we assume the Yang is loaded by some other mechanism and
* error if it not found.
* Alternatively, that YANG could be loaded.
* Problem is, if clixon_snmp has not loaded it, has backend done it?
* What happens if backend has not loaded it?
*/
if ((ymod = yang_find(yspec, Y_MODULE, modname)) == NULL){
clicon_err(OE_YANG, 0, "Mib-translated-yang %s not loaded", modname);
goto done;
}
/* Recursively traverse the mib-yang to find extensions */
if (mibyang_traverse(h, ymod) < 0)
goto done;
}
retval = 0;
done:
return retval;
}

55
apps/snmp/snmp_register.h Normal file
View file

@ -0,0 +1,55 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_REGISTER_H_
#define _SNMP_REGISTER_H_
/*
* Prototypes
*/
int mibyang_table_poll(clicon_handle h, yang_stmt *ylist);
int clixon_snmp_traverse_mibyangs(clicon_handle h);
#endif /* _SNMP_REGISTER_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

160
configure vendored
View file

@ -632,12 +632,14 @@ ac_ct_CXX
CXXFLAGS CXXFLAGS
CXX CXX
CPP CPP
MIB_GENERATED_YANG_DIR
YANG_STANDARD_DIR YANG_STANDARD_DIR
YANG_INSTALLDIR YANG_INSTALLDIR
CLIXON_YANG_PATCH CLIXON_YANG_PATCH
with_libxml2 with_libxml2
HAVE_HTTP1 HAVE_HTTP1
HAVE_LIBNGHTTP2 HAVE_LIBNGHTTP2
enable_netsnmp
with_restconf with_restconf
LINKAGE LINKAGE
LIBSTATIC_SUFFIX LIBSTATIC_SUFFIX
@ -722,6 +724,8 @@ enable_publish
with_restconf with_restconf
enable_http1 enable_http1
enable_nghttp2 enable_nghttp2
enable_netsnmp
with_mib_generated_yang_dir
with_configfile with_configfile
with_libxml2 with_libxml2
with_sigaction with_sigaction
@ -1374,6 +1378,7 @@ Optional Features:
only only
--disable-nghttp2 Disable nghttp2 for native restconf http/2, ie --disable-nghttp2 Disable nghttp2 for native restconf http/2, ie
http/1 only http/1 only
--enable-netsnmp Enable net-snmp Clixon YANG mapping
Optional Packages: Optional Packages:
@ -1384,6 +1389,9 @@ Optional Packages:
--with-restconf=fcgi FCGI interface for stand-alone web rev-proxy eg --with-restconf=fcgi FCGI interface for stand-alone web rev-proxy eg
nginx nginx
--without-restconf Disable restconf altogether --without-restconf Disable restconf altogether
--with-mib-generated-yang-dir=DIR
Directory of generated YANG specs (default:
$prefix/share/mib-yangs)
--with-configfile=FILE Set default path to config file --with-configfile=FILE Set default path to config file
--with-libxml2 Use gnome/libxml2 regex engine --with-libxml2 Use gnome/libxml2 regex engine
--without-sigaction Don't use sigaction --without-sigaction Don't use sigaction
@ -3385,7 +3393,8 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
# Set to native or fcgi -> compile apps/restconf # Set to native or fcgi -> compile apps/restconf
# Enable build of apps/snmp
HAVE_LIBNGHTTP2=false HAVE_LIBNGHTTP2=false
# consider using neutral constant such as with-http2 # consider using neutral constant such as with-http2
HAVE_HTTP1=false HAVE_HTTP1=false
@ -3396,6 +3405,8 @@ HAVE_HTTP1=false
# Examples require standard IETF YANGs. You need to provide these for example and tests # Examples require standard IETF YANGs. You need to provide these for example and tests
# SNMP tests require generated YANGs from MIBs
# #
ac_ext=c ac_ext=c
@ -5301,6 +5312,149 @@ if test "${with_restconf+set}" = set; then :
fi fi
# This is for net-snmp
# Check whether --enable-netsnmp was given.
if test "${enable_netsnmp+set}" = set; then :
enableval=$enable_netsnmp;
if test "$enableval" = no; then
enable_netsnmp=no
else
enable_netsnmp=yes
fi
else
enable_netsnmp=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: checking netsnmp is enabled: $enable_netsnmp" >&5
$as_echo "checking netsnmp is enabled: $enable_netsnmp" >&6; }
if test "$enable_netsnmp" = "yes"; then
# All libs are:
# libnetsnmp, libnetsnmpagent, libnetsnmpmibs, libnetsnmptrapd, libnetsnmphelpers
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for init_snmp in -lnetsnmp" >&5
$as_echo_n "checking for init_snmp in -lnetsnmp... " >&6; }
if ${ac_cv_lib_netsnmp_init_snmp+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lnetsnmp $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char init_snmp ();
int
main ()
{
return init_snmp ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_netsnmp_init_snmp=yes
else
ac_cv_lib_netsnmp_init_snmp=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_netsnmp_init_snmp" >&5
$as_echo "$ac_cv_lib_netsnmp_init_snmp" >&6; }
if test "x$ac_cv_lib_netsnmp_init_snmp" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBNETSNMP 1
_ACEOF
LIBS="-lnetsnmp $LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for init_agent in -lnetsnmpagent" >&5
$as_echo_n "checking for init_agent in -lnetsnmpagent... " >&6; }
if ${ac_cv_lib_netsnmpagent_init_agent+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lnetsnmpagent $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char init_agent ();
int
main ()
{
return init_agent ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_netsnmpagent_init_agent=yes
else
ac_cv_lib_netsnmpagent_init_agent=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_netsnmpagent_init_agent" >&5
$as_echo "$ac_cv_lib_netsnmpagent_init_agent" >&6; }
if test "x$ac_cv_lib_netsnmpagent_init_agent" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBNETSNMPAGENT 1
_ACEOF
LIBS="-lnetsnmpagent $LIBS"
fi
for ac_header in net-snmp/net-snmp-config.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "net-snmp/net-snmp-config.h" "ac_cv_header_net_snmp_net_snmp_config_h" "$ac_includes_default"
if test "x$ac_cv_header_net_snmp_net_snmp_config_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NET_SNMP_NET_SNMP_CONFIG_H 1
_ACEOF
else
as_fn_error $? "snmp is missing" "$LINENO" 5
fi
done
# MIB_GENERATED_YANG_DIR is where clixon assumes generated YANGs from MIBs are stored
# This is NOT installed by Clixon and is not needed for core system
# However, it is required by SNMP tests.
# To generate: for i in /usr/share/snmp/mibs/*; do smidump -f yang $i > `basename -s .txt $i`.yang; done
# Check whether --with-mib-generated-yang-dir was given.
if test "${with_mib_generated_yang_dir+set}" = set; then :
withval=$with_mib_generated_yang_dir; MIB_GENERATED_YANG_DIR="$withval"
else
MIB_GENERATED_YANG_DIR="${prefix}/share/mib-yangs"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Generated YANGs from MIB files are expected to be in ${MIB_GENERATED_YANG_DIR}" >&5
$as_echo "Generated YANGs from MIB files are expected to be in ${MIB_GENERATED_YANG_DIR}" >&6; }
fi
# Set default config file location # Set default config file location
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml
@ -5569,7 +5723,7 @@ fi
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/main/example.xml extras/rpm/Makefile docker/Makefile docker/base/Makefile docker/clixon-dev/Makefile docker/main/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile doc/Makefile test/Makefile test/config.sh test/cicd/Makefile test/vagrant/Makefile" ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/snmp/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/main/example.xml extras/rpm/Makefile docker/Makefile docker/base/Makefile docker/clixon-dev/Makefile docker/main/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile doc/Makefile test/Makefile test/config.sh test/cicd/Makefile test/vagrant/Makefile"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@ -6272,6 +6426,7 @@ do
"apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;; "apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;;
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;; "apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;; "apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
"apps/snmp/Makefile") CONFIG_FILES="$CONFIG_FILES apps/snmp/Makefile" ;;
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;; "etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
@ -6878,4 +7033,3 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi fi

View file

@ -114,7 +114,8 @@ AC_SUBST(LIBS)
AC_SUBST(SH_SUFFIX) AC_SUBST(SH_SUFFIX)
AC_SUBST(LIBSTATIC_SUFFIX) AC_SUBST(LIBSTATIC_SUFFIX)
AC_SUBST(LINKAGE) AC_SUBST(LINKAGE)
AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf
AC_SUBST(enable_netsnmp) # Enable build of apps/snmp
AC_SUBST(HAVE_LIBNGHTTP2,false) # consider using neutral constant such as with-http2 AC_SUBST(HAVE_LIBNGHTTP2,false) # consider using neutral constant such as with-http2
AC_SUBST(HAVE_HTTP1,false) AC_SUBST(HAVE_HTTP1,false)
AC_SUBST(with_libxml2) AC_SUBST(with_libxml2)
@ -123,6 +124,8 @@ AC_SUBST(CLIXON_YANG_PATCH)
AC_SUBST(YANG_INSTALLDIR) AC_SUBST(YANG_INSTALLDIR)
# Examples require standard IETF YANGs. You need to provide these for example and tests # Examples require standard IETF YANGs. You need to provide these for example and tests
AC_SUBST(YANG_STANDARD_DIR) AC_SUBST(YANG_STANDARD_DIR)
# SNMP tests require generated YANGs from MIBs
AC_SUBST(MIB_GENERATED_YANG_DIR)
# #
AC_PROG_CC() AC_PROG_CC()
@ -275,6 +278,37 @@ AC_ARG_WITH([restconf],
AC_ARG_WITH([restconf], AC_ARG_WITH([restconf],
AS_HELP_STRING([--without-restconf],[Disable restconf altogether])) AS_HELP_STRING([--without-restconf],[Disable restconf altogether]))
# This is for net-snmp
AC_ARG_ENABLE(netsnmp, AS_HELP_STRING([--enable-netsnmp],[Enable net-snmp Clixon YANG mapping]),[
if test "$enableval" = no; then
enable_netsnmp=no
else
enable_netsnmp=yes
fi
],
[ enable_netsnmp=no])
AC_MSG_RESULT(checking netsnmp is enabled: $enable_netsnmp)
if test "$enable_netsnmp" = "yes"; then
# All libs are:
# libnetsnmp, libnetsnmpagent, libnetsnmpmibs, libnetsnmptrapd, libnetsnmphelpers
AC_CHECK_LIB(netsnmp, init_snmp)
AC_CHECK_LIB(netsnmpagent, init_agent)
AC_CHECK_HEADERS(net-snmp/net-snmp-config.h,[], AC_MSG_ERROR([snmp is missing]))
# MIB_GENERATED_YANG_DIR is where clixon assumes generated YANGs from MIBs are stored
# This is NOT installed by Clixon and is not needed for core system
# However, it is required by SNMP tests.
# To generate: for i in /usr/share/snmp/mibs/*; do smidump -f yang $i > `basename -s .txt $i`.yang; done
AC_ARG_WITH(mib-generated-yang-dir,
[AS_HELP_STRING([--with-mib-generated-yang-dir=DIR],[Directory of generated YANG specs (default: $prefix/share/mib-yangs)])],
[MIB_GENERATED_YANG_DIR="$withval"],
[MIB_GENERATED_YANG_DIR="${prefix}/share/mib-yangs"]
)
AC_MSG_RESULT(Generated YANGs from MIB files are expected to be in ${MIB_GENERATED_YANG_DIR})
fi
# Set default config file location # Set default config file location
CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml CLIXON_DEFAULT_CONFIG=/usr/local/etc/clixon.xml
AC_ARG_WITH([configfile], AC_ARG_WITH([configfile],
@ -352,6 +386,7 @@ AC_OUTPUT(Makefile
apps/backend/Makefile apps/backend/Makefile
apps/netconf/Makefile apps/netconf/Makefile
apps/restconf/Makefile apps/restconf/Makefile
apps/snmp/Makefile
include/Makefile include/Makefile
etc/Makefile etc/Makefile
etc/clixonrc etc/clixonrc
@ -373,4 +408,3 @@ AC_OUTPUT(Makefile
test/cicd/Makefile test/cicd/Makefile
test/vagrant/Makefile test/vagrant/Makefile
) )

View file

@ -39,6 +39,9 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update git make build-base gcc flex bison curl-dev RUN apk add --update git make build-base gcc flex bison curl-dev
# For netsnmp
RUN apk add --update net-snmp net-snmp-dev
WORKDIR /usr/local/share WORKDIR /usr/local/share
# Checkout standard YANG models for tests (note >1G for full repo) # Checkout standard YANG models for tests (note >1G for full repo)
@ -76,7 +79,7 @@ WORKDIR /clixon/clixon
COPY clixon . COPY clixon .
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --without-restconf --with-yang-standard-dir=/usr/local/share/yang/standard RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --without-restconf --with-yang-standard-dir=/usr/local/share/yang/standard --enable-netsnmp --with-mib-generated-yang-dir=/usr/local/share/mib-yangs/
RUN make RUN make
RUN make install RUN make install
@ -103,6 +106,9 @@ WORKDIR /clixon
COPY startsystem.sh startsystem.sh COPY startsystem.sh startsystem.sh
RUN install startsystem.sh /clixon/build/bin/ RUN install startsystem.sh /clixon/build/bin/
# Add our generated YANG files
RUN git clone https://github.com/clicon/mib-yangs.git /usr/local/share/mib-yangs
# #
# Stage 2 # Stage 2
# The second step skips the development environment and builds a runtime system # The second step skips the development environment and builds a runtime system
@ -112,6 +118,16 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update flex bison fcgi-dev RUN apk add --update flex bison fcgi-dev
# For SNMP
RUN apk add --update net-snmp net-snmp-tools
# Some custom configuration for SNMP
RUN echo "master agentx" > /etc/snmp/snmpd.conf
RUN echo "agentaddress 127.0.0.1" >> /etc/snmp/snmpd.conf
RUN echo "rwcommunity public localhost" >> /etc/snmp/snmpd.conf
RUN echo "agentXSocket unix:/var/run/snmp.sock" >> /etc/snmp/snmpd.conf
RUN echo "agentxperms 777 777" >> /etc/snmp/snmpd.conf
# Test-specific (for test scripts) # Test-specific (for test scripts)
RUN apk add --update sudo curl procps grep make bash expect RUN apk add --update sudo curl procps grep make bash expect
@ -120,6 +136,7 @@ RUN adduser -D -H clicon
COPY --from=0 /clixon/build/ /usr/local/ COPY --from=0 /clixon/build/ /usr/local/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/
COPY --from=0 /usr/local/share/mib-yangs/* /usr/local/share/mib-yangs/
# Log to stderr. # Log to stderr.
CMD /usr/local/bin/startsystem.sh CMD /usr/local/bin/startsystem.sh

View file

@ -38,6 +38,9 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update git make build-base gcc flex bison fcgi-dev curl-dev RUN apk add --update git make build-base gcc flex bison fcgi-dev curl-dev
# For netsnmp
RUN apk add --update net-snmp net-snmp-dev
# Checkut standard YANG models for tests (note >1G for full repo) # Checkut standard YANG models for tests (note >1G for full repo)
WORKDIR /usr/local/share WORKDIR /usr/local/share
RUN mkdir yang RUN mkdir yang
@ -77,7 +80,7 @@ RUN adduser -D -H -G www-data www-data
RUN apk add --update nginx RUN apk add --update nginx
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=fcgi --with-yang-standard-dir=/usr/local/share/yang/standard RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=fcgi --with-yang-standard-dir=/usr/local/share/yang/standard --enable-netsnmp --with-mib-generated-yang-dir=/usr/local/share/mib-yangs/
RUN make RUN make
RUN make install RUN make install
@ -104,6 +107,9 @@ WORKDIR /clixon
COPY startsystem_fcgi.sh startsystem.sh COPY startsystem_fcgi.sh startsystem.sh
RUN install startsystem.sh /clixon/build/bin/ RUN install startsystem.sh /clixon/build/bin/
# Add our generated YANG files
RUN git clone https://github.com/clicon/mib-yangs.git /usr/local/share/mib-yangs
# #
# Stage 2 # Stage 2
# The second step skips the development environment and builds a runtime system # The second step skips the development environment and builds a runtime system
@ -113,6 +119,16 @@ MAINTAINER Olof Hagsand <olof@hagsand.se>
# For clixon and cligen # For clixon and cligen
RUN apk add --update flex bison fcgi-dev RUN apk add --update flex bison fcgi-dev
# For SNMP
RUN apk add --update net-snmp net-snmp-tools
# Some custom configuration for SNMP
RUN echo "master agentx" > /etc/snmp/snmpd.conf
RUN echo "agentaddress 127.0.0.1" >> /etc/snmp/snmpd.conf
RUN echo "rwcommunity public localhost" >> /etc/snmp/snmpd.conf
RUN echo "agentXSocket unix:/var/run/snmp.sock" >> /etc/snmp/snmpd.conf
RUN echo "agentxperms 777 777" >> /etc/snmp/snmpd.conf
# Need to add www user manually, but group www-data already exists on Alpine # Need to add www user manually, but group www-data already exists on Alpine
RUN adduser -D -H -G www-data www-data RUN adduser -D -H -G www-data www-data
# nginx adds group www-data # nginx adds group www-data
@ -131,6 +147,7 @@ RUN adduser www-data clicon
COPY --from=0 /clixon/build/ /usr/local/ COPY --from=0 /clixon/build/ /usr/local/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/
COPY --from=0 /usr/local/share/mib-yangs/* /usr/local/share/mib-yangs/
# Manually created # Manually created
RUN mkdir /www-data RUN mkdir /www-data

View file

@ -41,6 +41,9 @@ RUN apk add --update git make build-base gcc flex bison curl-dev
# nghttp2 dependencies # nghttp2 dependencies
RUN apk add --update nghttp2 RUN apk add --update nghttp2
# For netsnmp
RUN apk add --update net-snmp net-snmp-dev
# Checkut models # Checkut models
WORKDIR /usr/local/share/ WORKDIR /usr/local/share/
@ -88,7 +91,7 @@ WORKDIR /clixon/clixon
COPY clixon . COPY clixon .
# Configure, build and install clixon # Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-http1 --with-yang-standard-dir=/usr/local/share/yang/standard RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-restconf=native --enable-nghttp2 --enable-http1 --with-yang-standard-dir=/usr/local/share/yang/standard --enable-netsnmp --with-mib-generated-yang-dir=/usr/local/share/mib-yangs/
RUN make RUN make
RUN make install RUN make install
@ -111,11 +114,17 @@ RUN install *.sh /clixon/build/bin/test
RUN install *.exp /clixon/build/bin/test RUN install *.exp /clixon/build/bin/test
RUN install clixon.png /clixon/build/bin/test RUN install clixon.png /clixon/build/bin/test
RUN install -d /clixon/build/mibs
RUN install mibs/* /clixon/build/mibs
# Copy startscript # Copy startscript
WORKDIR /clixon WORKDIR /clixon
COPY startsystem_native.sh startsystem.sh COPY startsystem_native.sh startsystem.sh
RUN install startsystem.sh /clixon/build/bin/ RUN install startsystem.sh /clixon/build/bin/
# Add our generated YANG files
RUN git clone https://github.com/clicon/mib-yangs.git /usr/local/share/mib-yangs
# #
# Stage 2 # Stage 2
# The second step skips the development environment and builds a runtime system # The second step skips the development environment and builds a runtime system
@ -136,6 +145,16 @@ RUN apk add --update nghttp2
# Test-specific (for test scripts) # Test-specific (for test scripts)
RUN apk add --update sudo curl procps grep make bash expect RUN apk add --update sudo curl procps grep make bash expect
# For SNMP
RUN apk add --update net-snmp net-snmp-tools
# Some custom configuration for SNMP
RUN echo "master agentx" > /etc/snmp/snmpd.conf
RUN echo "agentaddress 127.0.0.1" >> /etc/snmp/snmpd.conf
RUN echo "rwcommunity public localhost" >> /etc/snmp/snmpd.conf
RUN echo "agentXSocket unix:/var/run/snmp.sock" >> /etc/snmp/snmpd.conf
RUN echo "agentxperms 777 777" >> /etc/snmp/snmpd.conf
# Expose https port for restconf # Expose https port for restconf
EXPOSE 80/tcp EXPOSE 80/tcp
EXPOSE 443/tcp EXPOSE 443/tcp
@ -147,6 +166,8 @@ COPY --from=0 /clixon/build/ /usr/local/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/standard/
COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/experimental/ COPY --from=0 /usr/local/share/yang/* /usr/local/share/yang/experimental/
COPY --from=0 /usr/local/share/openconfig/* /usr/local/share/openconfig/ COPY --from=0 /usr/local/share/openconfig/* /usr/local/share/openconfig/
COPY --from=0 /usr/local/share/mib-yangs/* /usr/local/share/mib-yangs/
COPY --from=0 /clixon/build/mibs/* /usr/share/snmp/mibs/
# Start the backend and restconf deamons # Start the backend and restconf deamons
CMD /usr/local/bin/startsystem.sh CMD /usr/local/bin/startsystem.sh

View file

@ -82,6 +82,10 @@ chmod 775 /usr/local/bin/test/site.sh
/usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs /usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs
>&2 echo "clixon_backend started" >&2 echo "clixon_backend started"
# Start snmpd, we need this for the SNMP tests and the app clixon_snmp. Log to stdout, then we can
# use Docker logs to see what's happening.
snmpd -Lo -p /var/run/snmpd.pid -I -ifXTable -I -ifTable -I -system_mib -I -sysORTable -I -snmpNotifyFilterTable -I -snmpNotifyTable -I -snmpNotifyFilterProfileTable
# Alt: let backend be in foreground, but test scripts may # Alt: let backend be in foreground, but test scripts may
# want to restart backend # want to restart backend
/bin/sleep 100000000 /bin/sleep 100000000

View file

@ -130,6 +130,10 @@ chmod g+w /www-data/fastcgi_restconf.sock
/usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs /usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs
>&2 echo "clixon_backend started" >&2 echo "clixon_backend started"
# Start snmpd, we need this for the SNMP tests and the app clixon_snmp. Log to stdout, then we can
# use Docker logs to see what's happening.
snmpd -Lo -p /var/run/snmpd.pid -I -ifXTable -I -ifTable -I -system_mib -I -sysORTable -I -snmpNotifyFilterTable -I -snmpNotifyTable -I -snmpNotifyFilterProfileTable
# Alt: let backend be in foreground, but test scripts may # Alt: let backend be in foreground, but test scripts may
# want to restart backend # want to restart backend
/bin/sleep 100000000 /bin/sleep 100000000

View file

@ -125,6 +125,12 @@ openssl req -x509 -config ./ca.cnf -nodes -newkey rsa:4096 -keyout /etc/ssl/priv
/usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs /usr/local/sbin/clixon_backend -D $DBG -s running -l e # logs on docker logs
>&2 echo "clixon_backend started" >&2 echo "clixon_backend started"
# Start snmpd, we need this for the SNMP tests and the app clixon_snmp. Log to stdout, then we can
# use Docker logs to see what's happening.
snmpd -Lo -p /var/run/snmpd.pid -I -ifXTable -I -ifTable -I -system_mib -I -sysORTable -I -snmpNotifyFilterTable -I -snmpNotifyTable -I -snmpNotifyFilterProfileTable
sleep 3
# Alt: let backend be in foreground, but test scripts may # Alt: let backend be in foreground, but test scripts may
# want to restart backend # want to restart backend
/bin/sleep 100000000 /bin/sleep 100000000

View file

@ -33,7 +33,7 @@
/* Define to 1 if you have the `getresuid' function. */ /* Define to 1 if you have the `getresuid' function. */
#undef HAVE_GETRESUID #undef HAVE_GETRESUID
/* Enable HTTP/1 (default true) */ /* Set to true to enable Native HTTP/1 */
#undef HAVE_HTTP1 #undef HAVE_HTTP1
/* Define to 1 if you have the `inet_aton' function. */ /* Define to 1 if you have the `inet_aton' function. */
@ -60,6 +60,12 @@
/* Define to 1 if you have the `m' library (-lm). */ /* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM #undef HAVE_LIBM
/* Define to 1 if you have the `netsnmp' library (-lnetsnmp). */
#undef HAVE_LIBNETSNMP
/* Define to 1 if you have the `netsnmpagent' library (-lnetsnmpagent). */
#undef HAVE_LIBNETSNMPAGENT
/* Define to 1 if you have the `nghttp2' library (-lnghttp2). */ /* Define to 1 if you have the `nghttp2' library (-lnghttp2). */
#undef HAVE_LIBNGHTTP2 #undef HAVE_LIBNGHTTP2
@ -75,6 +81,9 @@
/* Define to 1 if you have the <memory.h> header file. */ /* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H #undef HAVE_MEMORY_H
/* Define to 1 if you have the <net-snmp/net-snmp-config.h> header file. */
#undef HAVE_NET_SNMP_NET_SNMP_CONFIG_H
/* Define to 1 if you have the <nghttp2/nghttp2.h> header file. */ /* Define to 1 if you have the <nghttp2/nghttp2.h> header file. */
#undef HAVE_NGHTTP2_NGHTTP2_H #undef HAVE_NGHTTP2_NGHTTP2_H

View file

@ -164,4 +164,3 @@
*/ */
#define PROTO_RESTART_RECONNECT #define PROTO_RESTART_RECONNECT

View file

@ -55,7 +55,8 @@
/* /*
* Types * Types
* Add error category here, but must also add an entry in EV variable in clixon_err.c * Add error category here,
* @see EV variable in clixon_err.c but must also add an entry there
*/ */
enum clicon_err{ enum clicon_err{
/* 0 means error not set) */ /* 0 means error not set) */
@ -78,6 +79,7 @@ enum clicon_err{
OE_UNDEF, OE_UNDEF,
/*-- From here error extensions using clixon_err_cat_reg, XXX register dynamically? --*/ /*-- From here error extensions using clixon_err_cat_reg, XXX register dynamically? --*/
OE_SSL, /* Openssl errors, see eg ssl_get_error */ OE_SSL, /* Openssl errors, see eg ssl_get_error */
OE_SNMP , /* Netsnmp error */
OE_NGHTTP2, /* nghttp2 errors, see HAVE_LIBNGHTTP2 */ OE_NGHTTP2, /* nghttp2 errors, see HAVE_LIBNGHTTP2 */
}; };

View file

@ -70,6 +70,8 @@ int xml2xpath(cxobj *x, cvec *nsc, char **xpath);
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p); int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, cxobj *x1); int assign_namespace_body(cxobj *x0, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_valstr2enum(yang_stmt *ytype, char *valstr, char **enumstr);
int yang_enum2valstr(yang_stmt *ytype, char *enumstr, char **valstr);
int yang_enum_int_value(cxobj *node, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val);
int xml_copy_marked(cxobj *x0, cxobj *x1); int xml_copy_marked(cxobj *x0, cxobj *x1);
int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp); int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp);

View file

@ -124,6 +124,7 @@ static struct errvec EV[] = {
{"Undefined", OE_UNDEF}, {"Undefined", OE_UNDEF},
/* From here error extensions using clixon_err_cat_reg */ /* From here error extensions using clixon_err_cat_reg */
{"OpenSSL error", OE_SSL}, {"OpenSSL error", OE_SSL},
{"SNMP error", OE_SNMP},
{"Nghttp2 error", OE_NGHTTP2}, {"Nghttp2 error", OE_NGHTTP2},
{NULL, -1} {NULL, -1}
}; };

View file

@ -133,7 +133,7 @@ static const map_str2int yang_regexp_map[] = {
* @param[in] dbglevel Debug level * @param[in] dbglevel Debug level
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @note CLICON_FEATURE and CLICON_YANG_DIR are treated specially since they are lists * @note CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB are treated specially since they are lists
*/ */
int int
clicon_option_dump(clicon_handle h, clicon_option_dump(clicon_handle h,
@ -161,7 +161,7 @@ clicon_option_dump(clicon_handle h,
else else
clicon_debug(dbglevel, "%s = NULL", keys[i]); clicon_debug(dbglevel, "%s = NULL", keys[i]);
} }
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree /* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_DIR from config tree
* Since they are lists they are placed in the config tree. * Since they are lists they are placed in the config tree.
*/ */
x = NULL; x = NULL;
@ -176,6 +176,12 @@ clicon_option_dump(clicon_handle h,
continue; continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x)); clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
} }
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
}
retval = 0; retval = 0;
done: done:
if (keys) if (keys)
@ -333,7 +339,8 @@ parse_configfile(clicon_handle h,
continue; continue;
/* List options for configure options that are lists or leaf-lists: append to main */ /* List options for configure options that are lists or leaf-lists: append to main */
if (strcmp(name,"CLICON_FEATURE")==0 || if (strcmp(name,"CLICON_FEATURE")==0 ||
strcmp(name,"CLICON_YANG_DIR")==0){ strcmp(name,"CLICON_YANG_DIR")==0 ||
strcmp(name,"CLICON_SNMP_MIB")==0){
if (xml_addsub(xt, xec) < 0) if (xml_addsub(xt, xec) < 0)
goto done; goto done;
continue; continue;
@ -381,6 +388,8 @@ parse_configfile(clicon_handle h,
continue; continue;
if (strcmp(name,"CLICON_YANG_DIR")==0) if (strcmp(name,"CLICON_YANG_DIR")==0)
continue; continue;
if (strcmp(name,"CLICON_SNMP_MIB")==0)
continue;
if (clicon_hash_add(copt, if (clicon_hash_add(copt,
name, name,
body, body,
@ -424,7 +433,8 @@ clicon_option_add(clicon_handle h,
cxobj *x; cxobj *x;
if (strcmp(name, "CLICON_FEATURE")==0 || if (strcmp(name, "CLICON_FEATURE")==0 ||
strcmp(name, "CLICON_YANG_DIR")==0){ strcmp(name, "CLICON_YANG_DIR")==0 ||
strcmp(name, "CLICON_SNMP_MIB")==0){
if ((x = clicon_conf_xml(h)) == NULL){ if ((x = clicon_conf_xml(h)) == NULL){
clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name); clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name);
goto done; goto done;

View file

@ -386,6 +386,8 @@ yang2api_path_fmt_1(yang_stmt *ys,
* @param[in] ys Yang statement * @param[in] ys Yang statement
* @param[in] inclkey If set include key leaf (eg last leaf d in ex) * @param[in] inclkey If set include key leaf (eg last leaf d in ex)
* @param[out] api_path_fmt XML api path. Needs to be freed after use. * @param[out] api_path_fmt XML api path. Needs to be freed after use.
* @retval 0 OK
* @retval -1 Error
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/ */
int int
@ -424,7 +426,7 @@ yang2api_path_fmt(yang_stmt *ys,
* @param[in] api_path_fmt XML key format, eg /aaa/%s/name * @param[in] api_path_fmt XML key format, eg /aaa/%s/name
* @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt * @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt
* @param[out] api_path api_path, eg /aaa/17. Free after use * @param[out] api_path api_path, eg /aaa/17. Free after use
* @param[out] cvvi 1..cvv-len. Index into cvv of last cvv entry used, For example, * @param[out] cvv_i 1..cvv-len. Index into cvv of last cvv entry used, For example,
* if same as len of cvv, all were used, if < some entries were not * if same as len of cvv, all were used, if < some entries were not
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
@ -902,7 +904,7 @@ api_path2xpath(char *api_path,
* @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue * @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[out] xbotp Resulting xml tree * @param[out] xbotp Resulting xml tree
* @param[out] ybotp Yang spec matching xpathp * @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0) * @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf error * @retval 0 Invalid api_path or associated XML, netconf error
@ -1144,7 +1146,7 @@ api_path2xml_vec(char **vec,
* @param[in,out] xtop Incoming XML tree * @param[in,out] xtop Incoming XML tree
* @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue * @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[out] xbotp Resulting xml tree (end of xpath) * @param[out] xbotp Resulting xml tree (end of xpath) (optional)
* @param[out] ybotp Yang spec matching xbotp * @param[out] ybotp Yang spec matching xbotp
* @param[out] xerr Netconf error message (if retval=0) * @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK * @retval 1 OK

View file

@ -186,7 +186,7 @@ clicon_rpc_msg(clicon_handle h,
#ifdef RPC_USERNAME_ASSERT #ifdef RPC_USERNAME_ASSERT
assert(strstr(msg->op_body, "username")!=NULL); /* XXX */ assert(strstr(msg->op_body, "username")!=NULL); /* XXX */
#endif #endif
clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body); clicon_debug(2, "%s request:%s", __FUNCTION__, msg->op_body);
/* Create a socket and connect to it, either UNIX, IPv4 or IPv6 per config options */ /* Create a socket and connect to it, either UNIX, IPv4 or IPv6 per config options */
if (clicon_rpc_msg_once(h, msg, &retdata, &eof, &s) < 0) if (clicon_rpc_msg_once(h, msg, &retdata, &eof, &s) < 0)
goto done; goto done;
@ -215,7 +215,7 @@ clicon_rpc_msg(clicon_handle h,
goto done; goto done;
#endif #endif
} }
clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata); clicon_debug(2, "%s retdata:%s", __FUNCTION__, retdata);
if (retdata){ if (retdata){
/* Cannot populate xret here because need to know RPC name (eg "lock") in order to associate yang /* Cannot populate xret here because need to know RPC name (eg "lock") in order to associate yang
@ -581,7 +581,7 @@ clicon_rpc_get_config(clicon_handle h,
* @note xml arg need to have <config> as top element * @note xml arg need to have <config> as top element
* @code * @code
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, * if (clicon_rpc_edit_config(h, "running", OP_MERGE,
* "<config><a>4</a></config>") < 0) * "<config><a xmlns="urn:example:clixon">4</a></config>") < 0)
* err; * err;
* @endcode * @endcode
*/ */

View file

@ -1810,6 +1810,74 @@ xml_merge(cxobj *x0,
goto done; goto done;
} }
/*! Given a YANG (enum) type node and a value, return the string containing corresponding int str
*
* @param[in] ytype YANG type noden
* @param[in] valstr Integer string value
* @param[out] enumstr Value of enum, dont free
*/
int
yang_valstr2enum(yang_stmt *ytype,
char *valstr,
char **enumstr)
{
int retval = -1;
yang_stmt *yenum = NULL;
yang_stmt *yval;
if (enumstr == NULL){
clicon_err(OE_UNIX, EINVAL, "str is NULL");
goto done;
}
while ((yenum = yn_each(ytype, yenum)) != NULL) {
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done;
if (strcmp(yang_argument_get(yval), valstr) == 0)
break;
}
if (yenum)
*enumstr = yang_argument_get(yenum);
retval = 0;
done:
return retval;
}
/*! Given a YANG (enum) type node and a value, return the string containing corresponding int str
*
* @param[in] ytype YANG type noden
* @param[in] enumstr Value of enum
* @param[out] valstr Corresponding string containing an int (direct pointer, dont free)
* @retval 1 OK, result in valstr
* @retval 0 Invalid, not found
* @retval -1 Error
*/
int
yang_enum2valstr(yang_stmt *ytype,
char *enumstr,
char **valstr)
{
int retval = -1;
yang_stmt *yenum;
yang_stmt *yval;
if (valstr == NULL){
clicon_err(OE_UNIX, EINVAL, "valstr is NULL");
goto done;
}
if ((yenum = yang_find(ytype, Y_ENUM, enumstr)) == NULL)
goto fail;
/* Should assign value if yval not found */
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done;
*valstr = yang_argument_get(yval);
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Get integer value from xml node from yang enumeration /*! Get integer value from xml node from yang enumeration
* @param[in] node XML node in a tree * @param[in] node XML node in a tree
* @param[out] val Integer value returned * @param[out] val Integer value returned
@ -1832,9 +1900,8 @@ yang_enum_int_value(cxobj *node,
yang_stmt *ys; yang_stmt *ys;
yang_stmt *ytype; yang_stmt *ytype;
yang_stmt *yrestype; /* resolved type */ yang_stmt *yrestype; /* resolved type */
yang_stmt *yenum;
yang_stmt *yval;
char *reason = NULL; char *reason = NULL;
char *intstr = NULL;
if (node == NULL) if (node == NULL)
goto done; goto done;
@ -1853,20 +1920,16 @@ yang_enum_int_value(cxobj *node,
} }
if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration")) if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
goto done; goto done;
if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL) if (yang_enum2valstr(yrestype, xml_body(node), &intstr) < 0)
goto done;
/* Should assign value if yval not found */
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done; goto done;
/* reason is string containing why int could not be parsed */ /* reason is string containing why int could not be parsed */
if (parse_int32(yang_argument_get(yval), val, &reason) < 0) if (parse_int32(intstr, val, &reason) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1 /*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
* Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE * Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
* *

View file

@ -1364,7 +1364,7 @@ yang_find_prefix_by_namespace(yang_stmt *ys,
goto done; goto done;
} }
/*! Given a yang statement and local prefi valid in module , find namespace /*! Given a yang statement and local prefix valid in module, find namespace
* *
* @param[in] ys Yang statement in module tree (or module itself) * @param[in] ys Yang statement in module tree (or module itself)
* @param[in] prefix Local prefix to access module with (direct pointer) * @param[in] prefix Local prefix to access module with (direct pointer)
@ -3661,7 +3661,7 @@ yang_anydata_add(yang_stmt *yp,
/*! Find extension argument and return if extension exists and its argument value /*! Find extension argument and return if extension exists and its argument value
* *
* @param[in] ys Yang statement where unknown statement may occur referncing to extension * @param[in] ys Yang statement where unknown statement may occur referencing to extension
* @param[in] name Name of the extension * @param[in] name Name of the extension
* @param[in] ns The namespace of the module where the extension is defined * @param[in] ns The namespace of the module where the extension is defined
* @param[out] exist The extension exists. * @param[out] exist The extension exists.
@ -3678,7 +3678,7 @@ yang_anydata_add(yang_stmt *yp,
* // use extension value * // use extension value
* } * }
* @endcode * @endcode
* @see ys_populate_unknown Called when parsing YANGo * @see ys_populate_unknown Called when parsing YANG
*/ */
int int
yang_extension_value(yang_stmt *ys, yang_extension_value(yang_stmt *ys,

View file

@ -474,7 +474,7 @@ ys_do_refine(yang_stmt *yr,
return retval; return retval;
} }
/*! Yang node yg is a leaf in yang node list yn /*! Check if Yang node y is a leaf in yang node list yp
* Could be made to a generic function used elsewhere as well * Could be made to a generic function used elsewhere as well
* @param[in] y Yang leaf * @param[in] y Yang leaf
* @param[in] yp Yang list parent * @param[in] yp Yang list parent
@ -504,7 +504,6 @@ ys_iskey(yang_stmt *y,
return 0; return 0;
} }
/*! Helper function to yang_expand_grouping /*! Helper function to yang_expand_grouping
* @param[in] yn Yang parent node of uses ststement * @param[in] yn Yang parent node of uses ststement
* @param[in] ys Uses statement * @param[in] ys Uses statement
@ -790,7 +789,10 @@ yang_parse_str(char *str,
if (yang_scan_exit(&yy) < 0) if (yang_scan_exit(&yy) < 0)
goto done; goto done;
} }
ymod = yy.yy_module; if ((ymod = yy.yy_module) == NULL){
clicon_err(OE_YANG, 0, "No module in YANG %s", name);
goto done;
}
/* Add filename for debugging and errors, see also ys_linenum on (each symbol?) */ /* Add filename for debugging and errors, see also ys_linenum on (each symbol?) */
if (yang_filename_set(ymod, name) < 0) if (yang_filename_set(ymod, name) < 0)
goto done; goto done;

34
scripts/mib_to_yang.sh Executable file
View file

@ -0,0 +1,34 @@
#/usr/bin/env bash
# Traverses a directory ($MIBDIR) and tries to convert each file from SMI format
# to YANG and preserve directory structure etc.
# This script can be used like this: ./mib_to_yang /usr/share/snmp/mibs yang/
MIBDIR=$1
YANGDIR=$2
function parse_mibs(){
indir=$1
outdir=$2
for i in $indir/*; do
outfile="$outdir`basename $i | cut -d"." -f1`.yang"
SMIPATH=$MIBDIR smidump -f yang -k $i > $outfile
done
}
if [ $# -ne 2 ]; then
echo "Usage: $0 <MIB directory, usually /usr/share/snmp/mibs> <out directory, for example yang/>"
exit
fi
for i in `find $MIBDIR -type d -or -type l`; do
if [ `basename $i` == `basename $MIBDIR` ]; then
outdir=$YANGDIR
else
outdir="$YANGDIR`basename $i`/"
fi
mkdir -p $outdir
parse_mibs $i $outdir
done

View file

@ -67,6 +67,8 @@ You can prefix a test with `BE=0` if you want to run your own backend.
You can prefix a test with `RC=0` if you want to run your own restconf process. You can prefix a test with `RC=0` if you want to run your own restconf process.
You can prefix a test with `SN=0` if you want to run your own SNMP process (in combination with `BE=0`)
To run with debug flags, use the `DBG=<number>` environment variable. To run with debug flags, use the `DBG=<number>` environment variable.
Other variables include: Other variables include:
@ -150,6 +152,26 @@ If you do not have them, generate self-signed certs, eg as follows:
There are also client-cert tests, eg `test_ssl_certs.sh` There are also client-cert tests, eg `test_ssl_certs.sh`
## SNMP
Clixon snmp frontend tests require a running netsnmpd and converted YANG files from MIB.
Netsnmpd is 5.9 or later and can be started via systemd. For the tests
to run, the systems IFMIB should be disabled: `-I -ifTable,ifNumber,ifXTable,`, etc.
One way to start snmpd on Ubuntu, known to be working for the tests are: snmpd -Lo -p /var/run/snmpd.pid -I -ifXTable -I -ifTable -I -system_mib -I -sysORTable -I -snmpNotifyFilterTable -I -snmpNotifyTable -I -snmpNotifyFilterProfileTable
Converted YANG files are available at `https://github.com/clicon/mib-yangs` or alternatively use `smidump` version 0.5 or later. Clixon expects them to be at `/usr/local/share/mib-yangs/` by default, or configured by `--with-mib-generated-yang-dir=DIR`.
You also need to configure a unix socket for agent. Example of /etc/snmp/snmpd.conf:
```
master agentx
agentaddress 127.0.0.1,[::1]
rwcommunity public localhost
agentXSocket unix:/var/run/snmp.sock
agentxperms 777 777
```
## Known issues ## Known issues
[Workaround: Unicode double-quote in iana-if-type@2022-03-07.yang](https://github.com/clicon/clixon/issues/315) [Workaround: Unicode double-quote in iana-if-type@2022-03-07.yang](https://github.com/clicon/clixon/issues/315)

View file

@ -52,6 +52,9 @@ HAVE_HTTP1=@HAVE_HTTP1@
# use it you need to set Clixon config option CLICON_YANG_REGEXP to libxml2 # use it you need to set Clixon config option CLICON_YANG_REGEXP to libxml2
WITH_LIBXML2=@with_libxml2@ WITH_LIBXML2=@with_libxml2@
# Check if we have support for Net-SNMP enabled or not.
ENABLE_NETSNMP=@enable_netsnmp@
# C++ compiler # C++ compiler
CXX=@CXX@ CXX=@CXX@
@ -92,3 +95,4 @@ YANG_STANDARD_DIR=@YANG_STANDARD_DIR@
YANG_INSTALLDIR=@YANG_INSTALLDIR@ YANG_INSTALLDIR=@YANG_INSTALLDIR@
MIB_GENERATED_YANG_DIR=@MIB_GENERATED_YANG_DIR@

View file

@ -53,6 +53,7 @@ testname=
# 1: Start valgrind at every new testcase. Check result every next new # 1: Start valgrind at every new testcase. Check result every next new
# 2: Start valgrind every new backend start. Check when backend stops # 2: Start valgrind every new backend start. Check when backend stops
# 3: Start valgrind every new restconf start. Check when restconf stops # 3: Start valgrind every new restconf start. Check when restconf stops
# 4: Start valgrind every new snmp start. Check when snmp stops
# #
: ${valgrindtest=0} : ${valgrindtest=0}
@ -73,6 +74,9 @@ testname=
# eg logging to a file: RCLOG="-l f/www-data/restconf.log" # eg logging to a file: RCLOG="-l f/www-data/restconf.log"
: ${RCLOG:=} : ${RCLOG:=}
# If set to 0, override starting of clixon_snmp in test (you bring your own)
: ${SN:=1}
# Namespace: netconf base # Namespace: netconf base
BASENS='urn:ietf:params:xml:ns:netconf:base:1.0' BASENS='urn:ietf:params:xml:ns:netconf:base:1.0'
@ -182,6 +186,13 @@ BUSER=clicon
: ${clixon_backend:=clixon_backend} : ${clixon_backend:=clixon_backend}
: ${clixon_snmp:=$(type -p clixon_snmp)}
: ${clixon_snmp_pidfile:="/var/tmp/clixon_snmp.pid"}
# Temporary debug var, set to trigger remaining snmp errors
: ${snmp_debug:=false}
# Source the site-specific definitions for test script variables, if site.sh # Source the site-specific definitions for test script variables, if site.sh
# exists. The variables defined in site.sh override any variables of the same # exists. The variables defined in site.sh override any variables of the same
# names in the environment in the current execution. # names in the environment in the current execution.
@ -204,6 +215,76 @@ if [ ! -z ${YANG_STANDARD_DIR} ]; then
: ${IETFRFC=$YANG_STANDARD_DIR/ietf/RFC} : ${IETFRFC=$YANG_STANDARD_DIR/ietf/RFC}
fi fi
: ${SNMPCHECK:=true}
if $SNMPCHECK; then
snmpget="$(type -p snmpget) -On -c public -v2c localhost "
snmpbulkget="$(type -p snmpbulkget) -On -c public -v2c localhost "
snmpset="$(type -p snmpset) -On -c public -v2c localhost "
snmpgetstr="$(type -p snmpget) -c public -v2c localhost "
snmpgetnext="$(type -p snmpgetnext) -On -c public -v2c localhost "
snmpgetnextstr="$(type -p snmpgetnext) -c public -v2c localhost "
snmptable="$(type -p snmptable) -c public -v2c localhost "
snmpwalk="$(type -p snmpwalk) -c public -v2c localhost "
snmptranslate="$(type -p snmptranslate) "
if [ "${ENABLE_NETSNMP}" == "yes" ]; then
pgrep snmpd > /dev/null
if [ $? != 0 ]; then
echo -e "\e[31m\nenable-netsnmp set but snmpd not running, start with:"
echo "systemctl start snmpd"
echo ""
echo "snmpd must be configured to use a Unix socket for agent communication"
echo "and have a rwcommunity configured, make sure the following lines are"
echo "added to /etc/snmp/snmpd.conf:"
echo ""
echo " rwcommunity public localhost"
echo " agentXSocket unix:/var/run/snmp.sock"
echo " agentxperms 777 777"
echo ""
echo "If you don't rely on systemd you can configure the lines above"
echo "and start snmpd manually with 'snmpd -Lo -p /var/run/snmpd.pid'."
echo -e "\e[0m"
exit -1
fi
fi
function validate_oid(){
oid=$1
oid2=$2
type=$3
value=$4
result=$5
name="$($snmptranslate $oid)"
name2="$($snmptranslate $oid2)"
if [[ $oid =~ ^([0-9]|\.)+$ ]]; then
get=$snmpget
getnext=$snmpgetnext
else
get=$snmpgetstr
getnext=$snmpgetnextstr
fi
if [ $oid == $oid2 ]; then
if [ -z "$result" ]; then
result="$oid = $type: $value"
fi
new "Validating OID: $oid2 = $type: $value"
expectpart "$($get $oid)" 0 "$result"
else
if [ -z "$result" ]; then
result="$oid2 = $type: $value"
fi
new "Validating next OID: $oid2 = $type: $value"
expectpart "$($getnext $oid)" 0 "$result"
fi
}
fi
# Check sanity between --with-restconf setting and if nginx is started by systemd or not # Check sanity between --with-restconf setting and if nginx is started by systemd or not
# This check is optional because some installs, such as vagrant make a non-systemd/direct # This check is optional because some installs, such as vagrant make a non-systemd/direct
# start # start
@ -413,6 +494,31 @@ function chunked_framing()
printf "\n#%s\n%s\n##\n" ${length} "${str}" printf "\n#%s\n%s\n##\n" ${length} "${str}"
} }
# Start clixon_snmp
function start_snmp(){
cfg=$1
rm -f ${clixon_snmp_pidfile}
$clixon_snmp -f $cfg -D $DBG &
if [ $? -ne 0 ]; then
err
fi
}
# Stop clixon_snmp and Valgrind if needed
function stop_snmp(){
if [ $valgrindtest -eq 4 ]; then
pkill -f clixon_snmp
sleep 1
checkvalgrind
else
killall -q clixon_snmp
fi
rm -f ${clixon_snmp_pidfile}
}
# Start backend with all varargs. # Start backend with all varargs.
# If valgrindtest == 2, start valgrind # If valgrindtest == 2, start valgrind
function start_backend(){ function start_backend(){
@ -544,6 +650,19 @@ function wait_restconf_stopped(){
fi fi
} }
# Use pidfile to check snmp started. pidfile is created after init in clixon_snmp
function wait_snmp()
{
let i=0;
while [ ! -f ${clixon_snmp_pidfile} ]; do
if [ $i -ge $DEMLOOP ]; then
err1 "snmp timeout $DEMWAIT seconds"
fi
sleep $DEMSLEEP
let i++;
done
}
# End of test, final tests before normal exit of test # End of test, final tests before normal exit of test
# Note this is a single test started by new, not a total test suite # Note this is a single test started by new, not a total test suite
function endtest() function endtest()

View file

@ -18,6 +18,7 @@ function memonce(){
clixon_netconf= clixon_netconf=
clixon_backend= clixon_backend=
clixon_restconf= clixon_restconf=
clixon_snmp=
case "$what" in case "$what" in
'cli') 'cli')
valgrindtest=1 valgrindtest=1
@ -43,8 +44,15 @@ function memonce(){
clixon_restconf="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile clixon_restconf" clixon_restconf="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile clixon_restconf"
;; ;;
'snmp')
valgrindtest=4 # This means snmp valgrind test
sudo chmod 660 $valgrindfile
: ${DEMWAIT:=15} # valgrind snmp needs some time to get up
clixon_snmp="/usr/bin/valgrind --num-callers=50 --leak-check=full --show-leak-kinds=all --suppressions=./valgrind-clixon.supp --track-fds=yes --trace-children=no --child-silent-after-fork=yes --log-file=$valgrindfile clixon_snmp"
;;
*) *)
echo "usage: $0 cli|netconf|restconf|backend" # valgrind memleak checks echo "usage: $0 cli|netconf|restconf|backend|snmp" # valgrind memleak checks
rm -f $valgrindfile rm -f $valgrindfile
exit -1 exit -1
;; ;;
@ -86,16 +94,16 @@ function println(){
} }
if [ -z "$*" ]; then if [ -z "$*" ]; then
cmds="backend restconf cli netconf" cmds="backend restconf cli netconf snmp"
else else
cmds=$* cmds=$*
fi fi
# First run sanity # First run sanity
for c in $cmds; do for c in $cmds; do
if [ $c != cli -a $c != netconf -a $c != restconf -a $c != backend ]; then if [ $c != cli -a $c != netconf -a $c != restconf -a $c != backend -a $c != snmp ]; then
echo "c:$c" echo "c:$c"
echo "usage: $0 [cli|netconf|restconf|backend]+" echo "usage: $0 [cli|netconf|restconf|backend|snmp]+"
echo " with no args run all" echo " with no args run all"
exit -1 exit -1
fi fi

View file

@ -0,0 +1,450 @@
CLIXON-TYPES-MIB DEFINITIONS ::= BEGIN
--
-- Example MIB objects for agent module example implementations
--
IMPORTS
MODULE-IDENTITY, OBJECT-TYPE, Integer32,
TimeTicks, Counter32, Gauge32, Counter64,
NOTIFICATION-TYPE, IpAddress FROM SNMPv2-SMI
SnmpAdminString FROM SNMP-FRAMEWORK-MIB
netSnmp FROM NET-SNMP-MIB
TruthValue, TimeStamp,
RowStatus, StorageType FROM SNMPv2-TC
InetAddressType, InetAddress FROM INET-ADDRESS-MIB
IANAifType FROM IANAifType-MIB
;
clixonExamples MODULE-IDENTITY
LAST-UPDATED "200406150000Z"
ORGANIZATION "www.net-snmp.org"
CONTACT-INFO
"postal: Wes Hardaker
P.O. Box 382
Davis CA 95617
email: net-snmp-coders@lists.sourceforge.net"
DESCRIPTION
"Example MIB objects for agent module example implementations"
REVISION "200406150000Z"
DESCRIPTION
"Corrected notification example definitions"
REVISION "200202060000Z"
DESCRIPTION
"First draft"
::= { netSnmp 200 }
--
-- top level structure
--
clixonExampleScalars OBJECT IDENTIFIER ::= { clixonExamples 1 }
clixonExampleTables OBJECT IDENTIFIER ::= { clixonExamples 2 }
clixonExampleNotifications OBJECT IDENTIFIER ::= { clixonExamples 3 }
clixonTables OBJECT IDENTIFIER ::= { clixonExamples 4 }
clixonInetTables OBJECT IDENTIFIER ::= { clixonExamples 5 }
clixonExampleNotificationPrefix OBJECT IDENTIFIER
::= { clixonExampleNotifications 0 }
clixonExampleNotificationObjects OBJECT IDENTIFIER
::= { clixonExampleNotifications 2 }
-- clixonTutorial OBJECT IDENTIFIER ::= { clixonExamples 4 }
--
-- Example scalars
--
clixonExampleInteger OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"This is a simple object which merely houses a writable
integer. It's only purposes is to hold the value of a single
integer. Writing to it will simply change the value for
subsequent GET/GETNEXT/GETBULK retrievals.
This example object is implemented in the
agent/mibgroup/examples/scalar_int.c file."
DEFVAL { 42 }
::= { clixonExampleScalars 1 }
clixonExampleSleeper OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"This is a simple object which is a basic integer. It's value
indicates the number of seconds that the agent will take in
responding to requests of this object. This is implemented
in a way which will allow the agent to keep responding to
other requests while access to this object is blocked. It is
writable, and changing it's value will change the amount of
time the agent will effectively wait for before returning a
response when this object is manipulated. Note that SET
requests through this object will take longer, since the
delay is applied to each internal transaction phase, which
could result in delays of up to 4 times the value of this
object.
This example object is implemented in the
agent/mibgroup/examples/delayed_instance.c file."
DEFVAL { 1 }
::= { clixonExampleScalars 2 }
clixonExampleString OBJECT-TYPE
SYNTAX SnmpAdminString
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"This is a simple object which merely houses a writable
string. It's only purposes is to hold the value of a single
string. Writing to it will simply change the value for
subsequent GET/GETNEXT/GETBULK retrievals.
This example object is implemented in the
agent/mibgroup/examples/watched.c file."
DEFVAL { "So long, and thanks for all the fish!" }
::= { clixonExampleScalars 3 }
ifTableLastChange OBJECT-TYPE
SYNTAX TimeTicks
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of sysUpTime at the time of the last creation or
deletion of an entry in the ifTable. If the number of
entries has been unchanged since the last re-initialization
of the local network management subsystem, then this object
contains a zero value."
::= { clixonExampleScalars 4 }
ifType OBJECT-TYPE
SYNTAX IANAifType
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The type of interface. Additional values for ifType are
assigned by the Internet Assigned Numbers Authority (IANA),
through updating the syntax of the IANAifType textual
convention."
::= { clixonExampleScalars 5 }
ifSpeed OBJECT-TYPE
SYNTAX Gauge32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An estimate of the interface's current bandwidth in bits
per second. For interfaces which do not vary in bandwidth
or for those where no accurate estimation can be made, this
object should contain the nominal bandwidth. If the
bandwidth of the interface is greater than the maximum value
reportable by this object then this object should report its
maximum value (4,294,967,295) and ifHighSpeed must be used
to report the interace's speed. For a sub-layer which has
no concept of bandwidth, this object should be zero."
::= { clixonExampleScalars 6 }
ifAdminStatus OBJECT-TYPE
SYNTAX INTEGER {
up(1), -- ready to pass packets
down(2),
testing(3) -- in some test mode
}
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"The desired state of the interface. The testing(3) state
indicates that no operational packets can be passed. When a
managed system initializes, all interfaces start with
ifAdminStatus in the down(2) state. As a result of either
explicit management action or per configuration information
retained by the managed system, ifAdminStatus is then
changed to either the up(1) or testing(3) states (or remains
in the down(2) state)."
::= { clixonExampleScalars 7}
ifInOctets OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The total number of octets received on the interface,
including framing characters.
Discontinuities in the value of this counter can occur at
re-initialization of the management system, and at other
times as indicated by the value of
ifCounterDiscontinuityTime."
::= { clixonExampleScalars 8}
ifHCInOctets OBJECT-TYPE
SYNTAX Counter64
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The total number of octets received on the interface,
including framing characters. This object is a 64-bit
version of ifInOctets.
Discontinuities in the value of this counter can occur at
re-initialization of the management system, and at other
times as indicated by the value of
ifCounterDiscontinuityTime."
::= { clixonExampleScalars 9}
ifPromiscuousMode OBJECT-TYPE
SYNTAX TruthValue
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"This object has a value of false(2) if this interface only
accepts packets/frames that are addressed to this station.
This object has a value of true(1) when the station accepts
all packets/frames transmitted on the media. The value
true(1) is only legal on certain types of media. If legal,
setting this object to a value of true(1) may require the
interface to be reset before becoming effective.
The value of ifPromiscuousMode does not affect the reception
of broadcast and multicast packets/frames by the interface."
::= { clixonExampleScalars 10 }
ifCounterDiscontinuityTime OBJECT-TYPE
SYNTAX TimeStamp
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of sysUpTime on the most recent occasion at which
any one or more of this interface's counters suffered a
discontinuity. The relevant counters are the specific
instances associated with this interface of any Counter32 or
Counter64 object contained in the ifTable or ifXTable. If
no such discontinuities have occurred since the last re-
initialization of the local management subsystem, then this
object contains a zero value."
::= { clixonExampleScalars 11 }
ifStackStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The status of the relationship between two sub-layers.
Changing the value of this object from 'active' to
'notInService' or 'destroy' will likely have consequences up
and down the interface stack. Thus, write access to this
object is likely to be inappropriate for some types of
interfaces, and many implementations will choose not to
support write-access for any type of interface."
::= { clixonExampleScalars 12 }
ifIpAddr OBJECT-TYPE
SYNTAX IpAddress
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"IP address example."
::= { clixonExampleScalars 13 }
--
-- Example Tables
--
clixonIETFWGTable OBJECT-TYPE
SYNTAX SEQUENCE OF ClixonIETFWGEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This table merely contains a set of data which is otherwise
useless for true network management. It is a table which
describes properies about a IETF Working Group, such as the
names of the two working group chairs.
This example table is implemented in the
agent/mibgroup/examples/data_set.c file."
::= { clixonExampleTables 1 }
clixonIETFWGEntry OBJECT-TYPE
SYNTAX ClixonIETFWGEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A row describing a given working group"
INDEX { nsIETFWGName }
::= {clixonIETFWGTable 1 }
ClixonIETFWGEntry ::= SEQUENCE {
nsIETFWGName INTEGER,
nsIETFWGChair1 OCTET STRING,
nsIETFWGChair2 OCTET STRING
}
nsIETFWGName OBJECT-TYPE
SYNTAX INTEGER (1..2147483647)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The name of the IETF Working Group this table describes."
::= { clixonIETFWGEntry 1 }
nsIETFWGChair1 OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"One of the names of the chairs for the IETF working group."
::= { clixonIETFWGEntry 2 }
nsIETFWGChair2 OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The other name, if one exists, of the chairs for the IETF
working group."
::= { clixonIETFWGEntry 3 }
clixonTable OBJECT-TYPE
SYNTAX SEQUENCE OF ClixonEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This table merely contains a set of data which is otherwise
useless for true network management. It is a table which
describes properies about a IETF Working Group, such as the
names of the two working group chairs.
This example table is implemented in the
agent/mibgroup/examples/data_set.c file."
::= { clixonTables 1 }
clixonEntry OBJECT-TYPE
SYNTAX ClixonEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A row describing a given working group"
INDEX { nsName }
::= {clixonTable 1 }
ClixonEntry ::= SEQUENCE {
nsName INTEGER,
nsChair1 OCTET STRING,
nsChair2 OCTET STRING
}
nsName OBJECT-TYPE
SYNTAX INTEGER (1..2147483647)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The name of the IETF Working Group this table describes."
::= { clixonEntry 1 }
nsChair1 OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"One of the names of the chairs for the IETF working group."
::= { clixonEntry 2 }
nsChair2 OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The other name, if one exists, of the chairs for the IETF
working group."
::= { clixonEntry 3 }
--
-- A table used in a table_iterator example
-- (agent/mibgroup/examples/clixonHostsTable*.[ch])
--
clixonHostsTable OBJECT-TYPE
SYNTAX SEQUENCE OF ClixonHostsEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An example table that implements a wrapper around the
/etc/hosts file on a machine using the iterator helper API."
::= { clixonExampleTables 2 }
clixonHostsEntry OBJECT-TYPE
SYNTAX ClixonHostsEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A host name mapped to an ip address"
INDEX { clixonHostName }
::= { clixonHostsTable 1 }
ClixonHostsEntry ::= SEQUENCE {
clixonHostName OCTET STRING,
clixonHostAddressType InetAddressType,
clixonHostAddress InetAddress,
clixonHostStorage StorageType,
clixonHostRowStatus RowStatus
}
clixonHostName OBJECT-TYPE
SYNTAX OCTET STRING (SIZE(0..64))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A host name that exists in the /etc/hosts (unix) file."
::= { clixonHostsEntry 1 }
clixonHostAddressType OBJECT-TYPE
SYNTAX InetAddressType
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The address type of then given host."
::= { clixonHostsEntry 2 }
clixonHostAddress OBJECT-TYPE
SYNTAX InetAddress
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The address of then given host."
::= { clixonHostsEntry 3 }
clixonHostStorage OBJECT-TYPE
SYNTAX StorageType
MAX-ACCESS read-create
STATUS current
DESCRIPTION "The storage type for this conceptual row."
DEFVAL { nonVolatile }
::= { clixonHostsEntry 4 }
clixonHostRowStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION "The status of this conceptual row."
::= { clixonHostsEntry 5 }
clixonObjectID OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The vendor's authoritative identification of the
network management subsystem contained in the entity.
This value is allocated within the SMI enterprises
subtree (1.3.6.1.4.1) and provides an easy and
unambiguous means for determining `what kind of box' is
being managed. For example, if vendor `Flintstones,
Inc.' was assigned the subtree 1.3.6.1.4.1.424242,
it could assign the identifier 1.3.6.1.4.1.424242.1.1
to its `Fred Router'."
::= { netSnmp 3 }
clixonInetTable OBJECT-TYPE
SYNTAX SEQUENCE OF ClixonInetEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This table merely contains a set of data which is otherwise
useless for true network management. It is a table which
describes properies about a IETF Working Group, such as the
names of the two working group chairs.
This example table is implemented in the
agent/mibgroup/examples/data_set.c file."
::= { clixonExampleTables 3 }
clixonInetEntry OBJECT-TYPE
SYNTAX ClixonInetEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A row describing a given working group"
INDEX { clixonAddress }
::= {clixonInetTable 1 }
ClixonInetEntry ::= SEQUENCE {
clixonAddress InetAddress,
clixonString OCTET STRING
}
clixonAddress OBJECT-TYPE
SYNTAX InetAddress
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The name of the IETF Working Group this table describes."
::= { clixonInetEntry 2 }
clixonString OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"One of the names of the chairs for the IETF working group."
::= { clixonInetEntry 3 }
END

1411
test/mibs/ENTITY-MIB Normal file

File diff suppressed because it is too large Load diff

View file

@ -208,6 +208,12 @@ wait_backend
new "expand identityref 1st level" new "expand identityref 1st level"
expectpart "$(echo "set identityrefs identityref ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 "ex:des" "ex:des2" "ex:des3" expectpart "$(echo "set identityrefs identityref ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 "ex:des" "ex:des2" "ex:des3"
# XXX something wrong sometimes in this test on docker.
# Expected:
# <name>
# CLI syntax error: "set leafrefs leafref": Incomplete command
echo "set leafrefs leafref ?" | $clixon_cli -f $cfg -o CLICON_CLI_EXPAND_LEAFREF=false
new "expand leafref 1st level" new "expand leafref 1st level"
expectpart "$(echo "set leafrefs leafref ?" | $clixon_cli -f $cfg -o CLICON_CLI_EXPAND_LEAFREF=false 2> /dev/null)" 0 "<name>" --not-- "91" "92" "93" expectpart "$(echo "set leafrefs leafref ?" | $clixon_cli -f $cfg -o CLICON_CLI_EXPAND_LEAFREF=false 2> /dev/null)" 0 "<name>" --not-- "91" "92" "93"

516
test/test_snmp_entity.sh Executable file
View file

@ -0,0 +1,516 @@
#!/usr/bin/env bash
# SNMP "smoketest" Basic snmpget test for a scalar
# 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."
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 <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>ENTITY-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import ENTITY-MIB {
prefix "entity-mib";
}
}
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 <<EOF > $fstate
<ENTITY-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:ENTITY-MIB">
<entPhysicalTable>
<entPhysicalEntry>
<entPhysicalIndex>1</entPhysicalIndex>
<entPhysicalDescr>Entity 1</entPhysicalDescr>
<entPhysicalVendorType>1.3.6.1.2.1.4</entPhysicalVendorType>
<entPhysicalContainedIn>9</entPhysicalContainedIn>
<entPhysicalClass>powerSupply</entPhysicalClass>
<entPhysicalParentRelPos>123</entPhysicalParentRelPos>
<entPhysicalName>ABCD1234</entPhysicalName>
<entPhysicalHardwareRev>REV 099</entPhysicalHardwareRev>
<entPhysicalFirmwareRev>REV 123</entPhysicalFirmwareRev>
<entPhysicalSoftwareRev>Clixon Version XXX.YYY year ZZZ</entPhysicalSoftwareRev>
<entPhysicalSerialNum>1234-1234-ABCD-ABCD</entPhysicalSerialNum>
<entPhysicalMfgName>Olof Hagsand Datakonsult AB</entPhysicalMfgName>
<entPhysicalModelName>Model AA.BB</entPhysicalModelName>
<entPhysicalAlias>Alias 123</entPhysicalAlias>
<entPhysicalAssetID>Asset 123</entPhysicalAssetID>
<entPhysicalIsFRU>true</entPhysicalIsFRU>
<entPhysicalMfgDate>11111111</entPhysicalMfgDate>
<!-- <entPhysicalUris></entPhysicalUris>-->
<!-- <entPhysicalUUID></entPhysicalUUID> -->
</entPhysicalEntry>
<entPhysicalEntry>
<entPhysicalIndex>2</entPhysicalIndex>
<entPhysicalDescr>Entity 2</entPhysicalDescr>
<entPhysicalVendorType>1.3.6.1.2.1.4</entPhysicalVendorType>
<entPhysicalContainedIn>4</entPhysicalContainedIn>
<entPhysicalClass>powerSupply</entPhysicalClass>
<entPhysicalParentRelPos>999</entPhysicalParentRelPos>
<entPhysicalName>XXZZ11994</entPhysicalName>
<entPhysicalHardwareRev>REV 100</entPhysicalHardwareRev>
<entPhysicalFirmwareRev>REV 234</entPhysicalFirmwareRev>
<entPhysicalSoftwareRev>Clixon Version XXX.YYY year ZZZ</entPhysicalSoftwareRev>
<entPhysicalSerialNum>2345-2345-ABCD-ABCD</entPhysicalSerialNum>
<entPhysicalMfgName>Olof Hagsand Datakonsult AB</entPhysicalMfgName>
<entPhysicalModelName>Model CC.DD</entPhysicalModelName>
<entPhysicalAlias>Alias 456</entPhysicalAlias>
<entPhysicalAssetID>Asset 456</entPhysicalAssetID>
<entPhysicalIsFRU>false</entPhysicalIsFRU>
<entPhysicalMfgDate>22222222</entPhysicalMfgDate>
<!-- <entPhysicalUris></entPhysicalUris> -->
<!-- <entPhysicalUUID></entPhysicalUUID> -->
</entPhysicalEntry>
</entPhysicalTable>
</ENTITY-MIB>
EOF
function testinit(){
new "test params: -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
}
ENTITY_OID=".1.3.6.1.2.1.47.1.1.1"
OID1="${ENTITY_OID}.1.1.1"
OID2="${ENTITY_OID}.1.1.2"
OID3="${ENTITY_OID}.1.2.1"
OID4="${ENTITY_OID}.1.2.2"
OIDX="${ENTITY_OID}.1.3.1"
OIDY="${ENTITY_OID}.1.3.2"
OID5="${ENTITY_OID}.1.4.1"
OID6="${ENTITY_OID}.1.4.2"
OID7="${ENTITY_OID}.1.5.1"
OID8="${ENTITY_OID}.1.5.2"
OID9="${ENTITY_OID}.1.6.1"
OID10="${ENTITY_OID}.1.6.2"
OID11="${ENTITY_OID}.1.7.1"
OID12="${ENTITY_OID}.1.7.2"
OID13="${ENTITY_OID}.1.8.1"
OID14="${ENTITY_OID}.1.8.2"
OID15="${ENTITY_OID}.1.9.1"
OID16="${ENTITY_OID}.1.9.2"
OID17="${ENTITY_OID}.1.10.1"
OID18="${ENTITY_OID}.1.10.2"
OID19="${ENTITY_OID}.1.11.1"
OID20="${ENTITY_OID}.1.11.2"
OID21="${ENTITY_OID}.1.12.1"
OID22="${ENTITY_OID}.1.12.2"
OID23="${ENTITY_OID}.1.13.1"
OID24="${ENTITY_OID}.1.13.2"
OID25="${ENTITY_OID}.1.14.1"
OID26="${ENTITY_OID}.1.14.2"
OID27="${ENTITY_OID}.1.15.1"
OID28="${ENTITY_OID}.1.15.2"
OID29="${ENTITY_OID}.1.16.1"
OID30="${ENTITY_OID}.1.16.2"
OID31="${ENTITY_OID}.1.17.1"
OID32="${ENTITY_OID}.1.17.2"
NAME1="ENTITY-MIB::entPhysicalIndex.1"
NAME2="ENTITY-MIB::entPhysicalIndex.2"
NAME3="ENTITY-MIB::entPhysicalDescr.1"
NAME4="ENTITY-MIB::entPhysicalDescr.2"
NAMEX="ENTITY-MIB::entPhysicalVendorType.1"
NAMEY="ENTITY-MIB::entPhysicalVendorType.2"
NAME5="ENTITY-MIB::entPhysicalContainedIn.1"
NAME6="ENTITY-MIB::entPhysicalContainedIn.2"
NAME7="ENTITY-MIB::entPhysicalClass.1"
NAME8="ENTITY-MIB::entPhysicalClass.2"
NAME9="ENTITY-MIB::entPhysicalParentRelPos.1"
NAME10="ENTITY-MIB::entPhysicalParentRelPos.2"
NAME11="ENTITY-MIB::entPhysicalName.1"
NAME12="ENTITY-MIB::entPhysicalName.2"
NAME13="ENTITY-MIB::entPhysicalHardwareRev.1"
NAME14="ENTITY-MIB::entPhysicalHardwareRev.2"
NAME15="ENTITY-MIB::entPhysicalFirmwareRev.1"
NAME16="ENTITY-MIB::entPhysicalFirmwareRev.2"
NAME17="ENTITY-MIB::entPhysicalSoftwareRev.1"
NAME18="ENTITY-MIB::entPhysicalSoftwareRev.2"
NAME19="ENTITY-MIB::entPhysicalSerialNum.1"
NAME20="ENTITY-MIB::entPhysicalSerialNum.2"
NAME21="ENTITY-MIB::entPhysicalMfgName.1"
NAME22="ENTITY-MIB::entPhysicalMfgName.2"
NAME23="ENTITY-MIB::entPhysicalModelName.1"
NAME24="ENTITY-MIB::entPhysicalModelName.2"
NAME25="ENTITY-MIB::entPhysicalAlias.1"
NAME26="ENTITY-MIB::entPhysicalAlias.2"
NAME27="ENTITY-MIB::entPhysicalAssetID.1"
NAME28="ENTITY-MIB::entPhysicalAssetID.2"
NAME29="ENTITY-MIB::entPhysicalIsFRU.1"
NAME30="ENTITY-MIB::entPhysicalIsFRU.2"
NAME31="ENTITY-MIB::entPhysicalMfgDate.1"
NAME32="ENTITY-MIB::entPhysicalMfgDate.2"
NAME33="ENTITY-MIB::entPhysicalUris.1"
NAME34="ENTITY-MIB::entPhysicalUris.2"
NAME35="ENTITY-MIB::entPhysicalUris.2"
new "SNMP system tests"
testinit
new "Get index, $OID1"
validate_oid $OID1 $OID1 "INTEGER" "1"
validate_oid $NAME1 $NAME1 "INTEGER" "1"
new "Get next $OID1"
validate_oid $OID1 $OID2 "INTEGER" "2"
validate_oid $NAME1 $NAME2 "INTEGER" "2"
new "Get index, $OID2"
validate_oid $OID2 $OID2 "INTEGER" "2"
validate_oid $NAME2 $NAME2 "INTEGER" "2"
new "Get next $OID2"
validate_oid $OID2 $OID3 "STRING" "\"Entity 1\""
validate_oid $NAME2 $NAME3 "STRING" "Entity 1"
new "Get index, $OID3"
validate_oid $OID3 $OID3 "STRING" "\"Entity 1\""
validate_oid $NAME3 $NAME3 "STRING" "Entity 1"
new "Get next $OID4"
validate_oid $OID3 $OID4 "STRING" "\"Entity 2\""
validate_oid $NAME3 $NAME4 "STRING" "Entity 2"
new "Get index, $OID4"
validate_oid $OID4 $OID4 "STRING" "\"Entity 2\""
validate_oid $NAME4 $NAME4 "STRING" "Entity 2"
new "Get next $OID4"
validate_oid $OID4 $OIDX "OID" ".1.3.6.1.2.1.4"
validate_oid $NAME4 $NAMEX "OID" "IP-MIB::ip"
new "Get $NAMEX"
validate_oid $OIDX $OIDX "OID" ".1.3.6.1.2.1.4"
validate_oid $NAMEX $NAMEX "OID" "IP-MIB::ip"
new "Get next $NAMEX"
validate_oid $OIDX $OIDY "OID" ".1.3.6.1.2.1.4"
validate_oid $NAMEX $NAMEY "OID" "IP-MIB::ip"
new "Get $NAMEY"
validate_oid $OIDY $OIDY "OID" ".1.3.6.1.2.1.4"
validate_oid $NAMEY $NAMEY "OID" "IP-MIB::ip"
new "Get next $NAMEY"
validate_oid $OIDY $OID5 "INTEGER" 9
validate_oid $NAMEY $NAME5 "INTEGER" 9
new "Get container, $OID5"
validate_oid $OID5 $OID5 "INTEGER" "9"
validate_oid $NAME5 $NAME5 "INTEGER" "9"
new "Get next container, $OID5"
validate_oid $OID5 $OID6 "INTEGER" "4"
validate_oid $NAME5 $NAME6 "INTEGER" "4"
new "Get container, $OID6"
validate_oid $OID6 $OID6 "INTEGER" "4"
validate_oid $NAME6 $NAME6 "INTEGER" "4"
new "Get next container, $OID6"
validate_oid $OID6 $OID7 "INTEGER" "6"
validate_oid $NAME6 $NAME7 "INTEGER" "powerSupply(6)"
new "Get container, $OID7"
validate_oid $OID7 $OID7 "INTEGER" "6"
validate_oid $NAME7 $NAME7 "INTEGER" "powerSupply(6)"
new "Get next container, $OID7"
validate_oid $OID7 $OID8 "INTEGER" "6"
validate_oid $NAME7 $NAME8 "INTEGER" "powerSupply(6)"
new "Get container, $OID8"
validate_oid $OID8 $OID8 "INTEGER" "6"
validate_oid $NAME8 $NAME8 "INTEGER" "powerSupply(6)"
new "Get next container, $OID8"
validate_oid $OID8 $OID9 "INTEGER" 123
validate_oid $NAME8 $NAME9 "INTEGER" 123
new "Get name, $OID9"
validate_oid $OID9 $OID9 "INTEGER" 123
validate_oid $NAME9 $NAME9 "INTEGER" 123
new "Get next, $OID9"
validate_oid $OID9 $OID10 "INTEGER" 999
validate_oid $NAME9 $NAME10 "INTEGER" 999
new "Get name, $OID10"
validate_oid $OID10 $OID10 "INTEGER" 999
validate_oid $NAME10 $NAME10 "INTEGER" 999
new "Get name, $OID11"
validate_oid $OID11 $OID11 "STRING" "\"ABCD1234\""
validate_oid $NAME11 $NAME11 "STRING" "ABCD1234"
new "Get next, $OID11"
validate_oid $OID11 $OID12 "STRING" "\"XXZZ11994\""
validate_oid $NAME11 $NAME12 "STRING" "XXZZ11994"
new "Get name, $OID12"
validate_oid $OID12 $OID12 "STRING" "\"XXZZ11994\""
validate_oid $NAME12 $NAME12 "STRING" "XXZZ11994"
new "Get next, $OID12"
validate_oid $OID12 $OID13 "STRING" "\"REV 099\""
validate_oid $NAME12 $NAME13 "STRING" "REV 099"
new "Get rev, $OID13"
validate_oid $OID13 $OID13 "STRING" "\"REV 099\""
validate_oid $NAME13 $NAME13 "STRING" "REV 099"
new "Get next hw rev, $OID13"
validate_oid $OID13 $OID14 "STRING" "\"REV 100\""
validate_oid $NAME13 $NAME14 "STRING" "REV 100"
new "Get hw rev, $OID14"
validate_oid $OID14 $OID14 "STRING" "\"REV 100\""
validate_oid $NAME14 $NAME14 "STRING" "REV 100"
new "Get next hw rev, $OID14"
validate_oid $OID14 $OID15 "STRING" "\"REV 123\""
validate_oid $NAME14 $NAME15 "STRING" "REV 123"
new "Get fw rev, $OID15"
validate_oid $OID15 $OID15 "STRING" "\"REV 123\""
validate_oid $NAME15 $NAME15 "STRING" "REV 123"
new "Get next fw rev, $OID15"
validate_oid $OID15 $OID16 "STRING" "\"REV 234\""
validate_oid $NAME15 $NAME16 "STRING" "REV 234"
new "Get fw rev, $OID16"
validate_oid $OID16 $OID16 "STRING" "\"REV 234\""
validate_oid $NAME16 $NAME16 "STRING" "REV 234"
new "Get next fw rev, $OID16"
validate_oid $OID16 $OID17 "STRING" "\"Clixon Version XXX.YYY year ZZZ\""
validate_oid $NAME16 $NAME17 "STRING" "Clixon Version XXX.YYY year ZZZ"
new "Get sw rev, $OID7"
validate_oid $OID17 $OID17 "STRING" "\"Clixon Version XXX.YYY year ZZZ\""
validate_oid $NAME17 $NAME17 "STRING" "Clixon Version XXX.YYY year ZZZ"
new "Get next sw rev, $OID17"
validate_oid $OID17 $OID18 "STRING" "\"Clixon Version XXX.YYY year ZZZ\""
validate_oid $NAME17 $NAME18 "STRING" "Clixon Version XXX.YYY year ZZZ"
new "Get sw rev, $OID18"
validate_oid $OID18 $OID18 "STRING" "\"Clixon Version XXX.YYY year ZZZ\""
validate_oid $NAME18 $NAME18 "STRING" "Clixon Version XXX.YYY year ZZZ"
new "Get next sw rev, $OID18"
validate_oid $OID18 $OID19 "STRING" "\"1234-1234-ABCD-ABCD\""
validate_oid $NAME18 $NAME19 "STRING" "1234-1234-ABCD-ABCD"
new "Get serial, $OID19"
validate_oid $OID19 $OID19 "STRING" "\"1234-1234-ABCD-ABCD\""
validate_oid $NAME19 $NAME19 "STRING" "1234-1234-ABCD-ABCD"
new "Get next serial, $OID19"
validate_oid $OID19 $OID20 "STRING" "\"2345-2345-ABCD-ABCD\""
validate_oid $NAME19 $NAME20 "STRING" "2345-2345-ABCD-ABCD"
new "Get serial, $OID20"
validate_oid $OID20 $OID20 "STRING" "\"2345-2345-ABCD-ABCD\""
validate_oid $NAME20 $NAME20 "STRING" "2345-2345-ABCD-ABCD"
new "Get next serial, $OID20"
validate_oid $OID20 $OID21 "STRING" "\"Olof Hagsand Datakonsult AB\""
validate_oid $NAME20 $NAME21 "STRING" "Olof Hagsand Datakonsult AB"
new "Get manufacturer, $OID21"
validate_oid $OID21 $OID21 "STRING" "\"Olof Hagsand Datakonsult AB\""
validate_oid $NAME21 $NAME21 "STRING" "Olof Hagsand Datakonsult AB"
new "Get next manufacturer, $OID21"
validate_oid $OID21 $OID22 "STRING" "\"Olof Hagsand Datakonsult AB\""
validate_oid $NAME21 $NAME22 "STRING" "Olof Hagsand Datakonsult AB"
new "Get manufacturer, $OID22"
validate_oid $OID22 $OID22 "STRING" "\"Olof Hagsand Datakonsult AB\""
validate_oid $NAME22 $NAME22 "STRING" "Olof Hagsand Datakonsult AB"
new "Get next manufacturer, $OID22"
validate_oid $OID22 $OID23 "STRING" "\"Model AA.BB\""
validate_oid $NAME22 $NAME23 "STRING" "Model AA.BB"
new "Get model, $OID23"
validate_oid $OID23 $OID23 "STRING" "\"Model AA.BB\""
validate_oid $NAME23 $NAME23 "STRING" "Model AA.BB"
new "Get next model, $OID23"
validate_oid $OID23 $OID24 "STRING" "\"Model CC.DD\""
validate_oid $NAME23 $NAME24 "STRING" "Model CC.DD"
new "Get model, $OID24"
validate_oid $OID24 $OID24 "STRING" "\"Model CC.DD\""
validate_oid $NAME24 $NAME24 "STRING" "Model CC.DD"
new "Get next model, $OID24"
validate_oid $OID24 $OID25 "STRING" "\"Alias 123\""
validate_oid $NAME24 $NAME25 "STRING" "Alias 123"
new "Get alias, $OID25"
validate_oid $OID25 $OID25 "STRING" "\"Alias 123\""
validate_oid $NAME25 $NAME25 "STRING" "Alias 123"
new "Get next alias, $OID25"
validate_oid $OID25 $OID26 "STRING" "\"Alias 456\""
validate_oid $NAME25 $NAME26 "STRING" "Alias 456"
new "Get alias, $OID26"
validate_oid $OID26 $OID26 "STRING" "\"Alias 456\""
validate_oid $NAME26 $NAME26 "STRING" "Alias 456"
new "Get next alias, $OID26"
validate_oid $OID26 $OID27 "STRING" "\"Asset 123\""
validate_oid $NAME26 $NAME27 "STRING" "Asset 123"
new "Get asset, $OID27"
validate_oid $OID27 $OID27 "STRING" "\"Asset 123\""
validate_oid $NAME27 $NAME27 "STRING" "Asset 123"
new "Get next asset, $OID27"
validate_oid $OID27 $OID28 "STRING" "\"Asset 456\""
validate_oid $NAME27 $NAME28 "STRING" "Asset 456"
new "Get asset, $OID28"
validate_oid $OID28 $OID28 "STRING" "\"ASSET 456\""
validate_oid $NAME28 $NAME28 "STRING" "ASSET 456"
new "Get next asset, $OID28"
validate_oid $OID28 $OID29 "INTEGER" "1"
validate_oid $NAME28 $NAME29 "INTEGER" "true(1)"
new "Get fru, $OID29"
validate_oid $OID29 $OID29 "INTEGER" "1"
validate_oid $NAME29 $NAME29 "INTEGER" "true(1)"
new "Get next fru, $OID29"
validate_oid $OID29 $OID30 "INTEGER" "0"
validate_oid $NAME29 $NAME30 "INTEGER" "0"
new "Get fru 2, $OID30"
validate_oid $OID30 $OID30 "INTEGER" "0"
validate_oid $NAME30 $NAME30 "INTEGER" "0"
new "Get next fru 2, $OID30"
validate_oid $NAME30 $NAME31 "STRING" "12593-49-49,49:49:49.49"
new "Get mfg date, $OID31"
validate_oid $NAME31 $NAME31 "STRING" "12593-49-49,49:49:49.49"
new "Get next mfg date, $OID31"
validate_oid $NAME31 $NAME32 "STRING" "12850-50-50,50:50:50.50"
new "Get mfg date, $OID32"
validate_oid $NAME32 $NAME32 "STRING" "12850-50-50,50:50:50.50"
new "Validate snmpwalk"
expectpart "$($snmpwalk $ENTITY_OID)" 0 "SNMPv2-SMI::mib-2.47.1.1.1.1.1.1 = INTEGER: 1" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.1.2 = INTEGER: 2" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.2.1 = STRING: \"Entity 1\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.2.2 = STRING: \"Entity 2\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.3.1 = OID: IP-MIB::ip" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.3.2 = OID: IP-MIB::ip" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.4.1 = INTEGER: 9" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.4.2 = INTEGER: 4" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.5.1 = INTEGER: 6" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.5.2 = INTEGER: 6" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.6.1 = INTEGER: 123" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.6.2 = INTEGER: 999" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.7.1 = STRING: \"ABCD1234\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.7.2 = STRING: \"XXZZ11994\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.8.1 = STRING: \"REV 099\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.8.2 = STRING: \"REV 100\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.9.1 = STRING: \"REV 123\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.9.2 = STRING: \"REV 234\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.10.1 = STRING: \"Clixon Version XXX.YYY year ZZZ\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.10.2 = STRING: \"Clixon Version XXX.YYY year ZZZ\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.11.1 = STRING: \"1234-1234-ABCD-ABCD\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.11.2 = STRING: \"2345-2345-ABCD-ABCD\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.12.1 = STRING: \"Olof Hagsand Datakonsult AB\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.12.2 = STRING: \"Olof Hagsand Datakonsult AB\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.13.1 = STRING: \"Model AA.BB\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.13.2 = STRING: \"Model CC.DD\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.14.1 = STRING: \"Alias 123\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.14.2 = STRING: \"Alias 456\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.15.1 = STRING: \"Asset 123\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.15.2 = STRING: \"Asset 456\"" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.16.1 = INTEGER: 1" \
"SNMPv2-SMI::mib-2.47.1.1.1.1.16.2 = INTEGER: 0" \
new "Cleaning up"
# testexit
new "endtest"
endtest

286
test/test_snmp_get.sh Executable file
View file

@ -0,0 +1,286 @@
#!/usr/bin/env bash
# SNMP "smoketest" Basic snmpget test for a scalar
# 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."
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 <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>CLIXON-TYPES-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import CLIXON-TYPES-MIB {
prefix "clixon-types";
}
}
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 <<EOF > $fstate
<CLIXON-TYPES-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:CLIXON-TYPES-MIB">
<clixonExampleScalars>
<clixonExampleInteger>0x7fffffff</clixonExampleInteger>
<clixonExampleSleeper>-1</clixonExampleSleeper>
<clixonExampleString>This is not default</clixonExampleString>
<ifTableLastChange>12345</ifTableLastChange>
<ifType>modem</ifType>
<ifSpeed>123123123</ifSpeed>
<ifAdminStatus>testing</ifAdminStatus>
<ifInOctets>123456</ifInOctets>
<ifHCInOctets>4294967296</ifHCInOctets>
<ifPromiscuousMode>true</ifPromiscuousMode>
<ifCounterDiscontinuityTime>1234567890</ifCounterDiscontinuityTime>
<ifStackStatus>active</ifStackStatus>
</clixonExampleScalars>
<clixonIETFWGTable>
<clixonIETFWGEntry>
<nsIETFWGName>42</nsIETFWGName>
<nsIETFWGChair1>Name1</nsIETFWGChair1>
<nsIETFWGChair2>Name2</nsIETFWGChair2>
</clixonIETFWGEntry>
</clixonIETFWGTable>
<clixonHostsTable>
<clixonHostsEntry>
<clixonHostName>test</clixonHostName>
<clixonHostAddressType>ipv4</clixonHostAddressType>
<clixonHostAddress>10.20.30.40</clixonHostAddress>
<clixonHostStorage>permanent</clixonHostStorage>
<clixonHostRowStatus>active</clixonHostRowStatus>
</clixonHostsEntry>
</clixonHostsTable>
<clixonInetTable>
<clixonInetEntry>
<clixonAddress>1.2.3.4</clixonAddress>
<clixonString>foo</clixonString>
</clixonInetEntry>
<clixonInetEntry>
<clixonAddress>2.2.2.2</clixonAddress>
<clixonString>bar</clixonString>
</clixonInetEntry>
</clixonInetTable>
</CLIXON-TYPES-MIB>
EOF
function testinit(){
new "test params: -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
}
new "SNMP tests"
testinit
MIB=".1.3.6.1.4.1.8072.200"
OID1="${MIB}.1.1" # netSnmpExampleInteger
OID2="${MIB}.1.2" # netSnmpExampleSleeper
OID3="${MIB}.1.3" # netSnmpExampleString
OID4="${MIB}.1.4" # ifTableLastChange 12345678
OID5="${MIB}.1.5" # ifType modem(48)
OID6="${MIB}.1.6" # ifSpeed 123123123
OID7="${MIB}.1.7" # ifAdminStatus testing(3)
OID8="${MIB}.1.8" # ifInOctets 123456
OID9="${MIB}.1.9" # ifHCInOctets 4294967296
OID10="${MIB}.1.10" # ifPromiscuousMode true(1)
OID11="${MIB}.1.11" # ifCounterDiscontinuityTime 1234567890 TimeStamp
OID12="${MIB}.1.12" # ifStackStatus active(1)
OID13="${MIB}.2.1" # netSnmpIETFWGTable
OID14="${MIB}.2.1.1" # netSnmpIETFWGEntry
OID15="${MIB}.2.1.1.1.42" # nsIETFWGName
OID16="${MIB}.2.1.1.2.42" # nsIETFWGChair1
OID17="${MIB}.2.1.1.3.42" # nsIETFWGChair2
OID18="${MIB}.2.2" # netSnmpHostsTable
OID19="${MIB}.2.2.1.1.4.116.101.115.116" # netSnmpHostName
OID20="${MIB}.2.2.1.2.4.116.101.115.116" # netSnmpHostAddressType
OID21="${MIB}.2.2.1.3" # netSnmpHostAddress
OID22="${MIB}.2.2.1.4" # netSnmpHostStorage
OID23="${MIB}.2.2.1.5" # netSnmpHostRowStatus
NAME1="CLIXON-TYPES-MIB::clixonExampleInteger"
NAME2="CLIXON-TYPES-MIB::clixonExampleSleeper"
NAME3="CLIXON-TYPES-MIB::clixonExampleString"
NAME4="CLIXON-TYPES-MIB::ifTableLastChange"
NAME5="CLIXON-TYPES-MIB::ifType"
NAME6="CLIXON-TYPES-MIB::ifSpeed"
NAME7="CLIXON-TYPES-MIB::ifAdminStatus"
NAME8="CLIXON-TYPES-MIB::ifInOctets"
NAME9="CLIXON-TYPES-MIB::ifHCInOctets"
NAME10="CLIXON-TYPES-MIB::ifPromiscuousMode"
NAME11="CLIXON-TYPES-MIB::ifCounterDiscontinuityTime"
NAME12="CLIXON-TYPES-MIB::ifStackStatus"
NAME13="CLIXON-TYPES-MIB::netSnmpIETFWGTable"
NAME14="CLIXON-TYPES-MIB::netSnmpIETFWGEntry"
NAME15="CLIXON-TYPES-MIB::nsIETFWGName"
NAME16="CLIXON-TYPES-MIB::nsIETFWGChair1"
NAME17="CLIXON-TYPES-MIB::nsIETFWGChair2"
NAME18="CLIXON-TYPES-MIB::netSnmpHostsTable"
NAME19="CLIXON-TYPES-MIB::netSnmpHostName"
NAME20="CLIXON-TYPES-MIB::netSnmpHostAddressType"
NAME21="CLIXON-TYPES-MIB::netSnmpHostAddress"
NAME22="CLIXON-TYPES-MIB::netSnmpHostStorage"
NAME23="CLIXON-TYPES-MIB::netSnmpHostRowStatus"
new "$snmpget"
new "Get netSnmpExampleInteger"
validate_oid $OID1 $OID1 "INTEGER" 2147483647
validate_oid $OID1 $OID2 "INTEGER" -1
validate_oid $NAME1 $NAME1 "INTEGER" 2147483647
validate_oid $NAME1 $NAME2 "INTEGER" -1
new "Get netSnmpExampleSleeper"
validate_oid $OID2 $OID2 "INTEGER" -1
validate_oid $OID2 $OID3 "STRING" "\"This is not default\""
validate_oid $NAME2 $NAME2 "INTEGER" -1
validate_oid $NAME2 $NAME3 "STRING" "This is not default"
new "Get netSnmpExampleString"
validate_oid $OID3 $OID3 "STRING" "\"This is not default\""
validate_oid $OID3 $OID4 "Timeticks" "(12345) 0:02:03.45"
validate_oid $NAME3 $NAME3 "STRING" "This is not default"
validate_oid $NAME3 $NAME4 "Timeticks" "(12345) 0:02:03.45"
new "Get ifTableLastChange"
validate_oid $OID4 $OID4 "Timeticks" "(12345) 0:02:03.45"
validate_oid $OID4 $OID5 "INTEGER" 48
validate_oid $NAME4 $NAME4 "Timeticks" "(12345) 0:02:03.45"
validate_oid $NAME4 $NAME5 "INTEGER" "modem(48)"
new "Get ifType"
validate_oid $OID5 $OID5 "INTEGER" 48
validate_oid $OID5 $OID6 "Gauge32" 123123123
validate_oid $NAME5 $NAME5 "INTEGER" "modem(48)"
validate_oid $NAME5 $NAME6 "Gauge32" 123123123
new "Get ifSpeed"
validate_oid $OID6 $OID6 "Gauge32" 123123123
validate_oid $OID6 $OID7 "INTEGER" 3
validate_oid $NAME6 $NAME6 "Gauge32" 123123123
validate_oid $NAME6 $NAME7 "INTEGER" "testing(3)"
new "Get ifAdminStatus"
validate_oid $OID7 $OID7 "INTEGER" 3
validate_oid $OID7 $OID8 "Counter32" 123456
validate_oid $NAME7 $NAME7 "INTEGER" "testing(3)"
validate_oid $NAME7 $NAME8 "Counter32" 123456
new "Get ifInOctets"
validate_oid $OID8 $OID8 "Counter32" 123456
validate_oid $OID8 $OID9 "Counter64" 4294967296
validate_oid $NAME8 $NAME8 "Counter32" 123456
validate_oid $NAME8 $NAME9 "Counter64" 4294967296
new "Get ifInHCOctets"
validate_oid $OID9 $OID9 "Counter64" 4294967296
validate_oid $OID9 $OID10 "INTEGER" 1
validate_oid $NAME9 $NAME9 "Counter64" 4294967296
validate_oid $NAME9 $NAME10 "INTEGER" "true(1)"
new "Get ifPromiscuousMode"
validate_oid $OID10 $OID10 "INTEGER" 1
validate_oid $OID10 $OID11 "Timeticks" "(1234567890) 142 days, 21:21:18.90"
validate_oid $NAME10 $NAME10 "INTEGER" "true(1)"
validate_oid $NAME10 $NAME11 "Timeticks" "(1234567890) 142 days, 21:21:18.90"
new "Get ifCounterDiscontinuityTime"
validate_oid $OID11 $OID11 "Timeticks" "(1234567890) 142 days, 21:21:18.90"
validate_oid $OID11 $OID12 "INTEGER" 1
validate_oid $NAME11 $NAME11 "Timeticks" "(1234567890) 142 days, 21:21:18.90"
validate_oid $NAME11 $NAME12 "INTEGER" "active(1)"
new "Get ifStackStatus"
validate_oid $OID12 $OID12 "INTEGER" 1
validate_oid $NAME12 $NAME12 "INTEGER" "active(1)"
new "Get bulk OIDs"
expectpart "$($snmpbulkget $OID1)" 0 "$OID2 = INTEGER: -1" "$OID3 = STRING: \"This is not default\"" "$OID4 = Timeticks: (12345) 0:02:03.45" "$OID5 = INTEGER: 48" "$OID6 = Gauge32: 123123123" "$OID7 = INTEGER: 3" "$OID8 = Counter32: 123456" "$OID9 = Counter64: 4294967296" "$OID10 = INTEGER: 1" "$OID11 = Timeticks: (1234567890) 142 days, 21:21:18.90"
new "Test SNMP getnext netSnmpIETFWGTable"
validate_oid $OID15 $OID15 "INTEGER" 42
new "Test SNMP get nsIETFWGName"
validate_oid $OID15 $OID15 "INTEGER" 42
new "Test SNMP getnext nsIETFWGName"
expectpart "$($snmpgetnext $OID15)" 0 "Hex-STRING: 4E 61 6D 65 31 00"
new "Test SNMP getnext netSnmpHostsTable"
expectpart "$($snmpgetnext $OID18)" 0 "$OID19 = Hex-STRING: 74 65 73 74 00"
new "Test SNMP get netSnmpHostName"
expectpart "$($snmpget $OID19)" 0 "$OID19 = Hex-STRING: 74 65 73 74 00"
new "Test SNMP getnext netSnmpHostName"
expectpart "$($snmpgetnext $OID19)" 0 "$OID20 = INTEGER: 1"
new "Cleaning up"
testexit
new "endtest"
endtest

469
test/test_snmp_ifmib.sh Executable file
View file

@ -0,0 +1,469 @@
#!/usr/bin/env bash
# SNMP "smoketest" Basic snmpget test for a scalar
# 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."
if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi
snmpd=$(type -p snmpd)
snmpget="$(type -p snmpget) -On -c public -v2c localhost "
snmpgetnext="$(type -p snmpgetnext) -On -c public -v2c localhost "
snmptable="$(type -p snmptable) -c public -v2c localhost "
snmpwalk="$(type -p snmpwalk) -c public -v2c localhost "
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 <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>IF-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>false</CLICON_VALIDATE_STATE_XML>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import IF-MIB {
prefix "if-mib";
}
}
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 <<EOF > $fstate
<IF-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:IF-MIB">
<interfaces>
<ifNumber>1</ifNumber>
</interfaces>
<ifMIBObjects>
<ifTableLastChange>0</ifTableLastChange>
<ifStackLastChange>0</ifStackLastChange>
</ifMIBObjects>
<ifTable>
<ifEntry>
<ifIndex>1</ifIndex>
<ifDescr>Test</ifDescr>
<ifType>ethernetCsmacd</ifType>
<ifMtu>1500</ifMtu>
<ifSpeed>10000000</ifSpeed>
<ifPhysAddress>aa:bb:cc:dd:ee:ff</ifPhysAddress>
<ifAdminStatus>testing</ifAdminStatus>
<ifOperStatus>up</ifOperStatus>
<ifLastChange>0</ifLastChange>
<ifInOctets>123</ifInOctets>
<ifInUcastPkts>124</ifInUcastPkts>
<ifInNUcastPkts>124</ifInNUcastPkts>
<ifInDiscards>125</ifInDiscards>
<ifInErrors>126</ifInErrors>
<ifInUnknownProtos>127</ifInUnknownProtos>
<ifOutOctets>128</ifOutOctets>
<ifOutUcastPkts>129</ifOutUcastPkts>
<ifOutNUcastPkts>129</ifOutNUcastPkts>
<ifOutDiscards>130</ifOutDiscards>
<ifOutErrors>131</ifOutErrors>
<ifOutQLen>132</ifOutQLen>
<ifSpecific>0.0</ifSpecific>
</ifEntry>
<ifEntry>
<ifIndex>2</ifIndex>
<ifDescr>Test 2</ifDescr>
<ifType>ethernetCsmacd</ifType>
<ifMtu>1400</ifMtu>
<ifSpeed>1000</ifSpeed>
<ifPhysAddress>11:22:33:44:55:66</ifPhysAddress>
<ifAdminStatus>down</ifAdminStatus>
<ifOperStatus>down</ifOperStatus>
<ifLastChange>0</ifLastChange>
<ifInOctets>111</ifInOctets>
<ifInUcastPkts>222</ifInUcastPkts>
<ifInNUcastPkts>333</ifInNUcastPkts>
<ifInDiscards>444</ifInDiscards>
<ifInErrors>555</ifInErrors>
<ifInUnknownProtos>666</ifInUnknownProtos>
<ifOutOctets>777</ifOutOctets>
<ifOutUcastPkts>888</ifOutUcastPkts>
<ifOutNUcastPkts>999</ifOutNUcastPkts>
<ifOutDiscards>101010</ifOutDiscards>
<ifOutErrors>111111</ifOutErrors>
<ifOutQLen>111</ifOutQLen>
<ifSpecific>1.2.3</ifSpecific>
</ifEntry>
</ifTable>
<ifRcvAddressTable>
<ifRcvAddressEntry>
<ifIndex>1</ifIndex>
<ifRcvAddressAddress>11:bb:cc:dd:ee:ff</ifRcvAddressAddress>
<ifRcvAddressStatus>active</ifRcvAddressStatus>
<ifRcvAddressType>other</ifRcvAddressType>
</ifRcvAddressEntry>
<ifRcvAddressEntry>
<ifIndex>2</ifIndex>
<ifRcvAddressAddress>aa:22:33:44:55:66</ifRcvAddressAddress>
<ifRcvAddressStatus>createAndGo</ifRcvAddressStatus>
<ifRcvAddressType>volatile</ifRcvAddressType>
</ifRcvAddressEntry>
</ifRcvAddressTable>
</IF-MIB>
EOF
# This is the expected result from snmpwalk:
# $ snmpwalk -cpublic -v2c localhost IF-MIB::ifTable # .1.3.6.1.2.1.2.2
# IF-MIB::ifIndex.1 = INTEGER: 1
# IF-MIB::ifDescr.1 = STRING: Test
# IF-MIB::ifType.1 = INTEGER: ethernetCsmacd(6)
# IF-MIB::ifMtu.1 = INTEGER: 1500
# IF-MIB::ifSpeed.1 = Gauge32: 10000000
# IF-MIB::ifPhysAddress.1 = STRING: aa:bb:cc:dd:ee:ff
# IF-MIB::ifAdminStatus.1 = INTEGER: up(1)
# IF-MIB::ifOperStatus.1 = INTEGER: up(1)
# IF-MIB::ifLastChange.1 = Timeticks: (0) 0:00:00.00
# IF-MIB::ifInOctets.1 = Counter32: 123
# IF-MIB::ifInUcastPkts.1 = Counter32: 123
# IF-MIB::ifInNUcastPkts.1 = Counter32: 123
# IF-MIB::ifInDiscards.1 = Counter32: 123
# IF-MIB::ifInErrors.1 = Counter32: 123
# IF-MIB::ifInUnknownProtos.1 = Counter32: 123
# IF-MIB::ifOutOctets.1 = Counter32: 123
# IF-MIB::ifOutUcastPkts.1 = Counter32: 123
# IF-MIB::ifOutNUcastPkts.1 = Counter32: 123
# IF-MIB::ifOutDiscards.1 = Counter32: 123
# IF-MIB::ifOutErrors.1 = Counter32: 123
# IF-MIB::ifOutQLen.1 = Gauge32: 123
# IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero
function testinit(){
new "test params: -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
}
new "SNMP tests"
testinit
# IF-MIB::interfaces
MIB=".1.3.6.1.2.1"
for (( i=1; i<23; i++ )); do
eval OID${i}="${MIB}.2.2.1.$i.1"
done
OID24=".1.3.6.1.2.1.31.1.4.1.1.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
OID25=".1.3.6.1.2.1.31.1.4.1.1.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
OID26=".1.3.6.1.2.1.31.1.4.1.2.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
OID27=".1.3.6.1.2.1.31.1.4.1.2.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
OID28=".1.3.6.1.2.1.31.1.4.1.3.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
OID29=".1.3.6.1.2.1.31.1.4.1.3.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
NAME1="IF-MIB::ifIndex"
NAME2="IF-MIB::ifDescr"
NAME3="IF-MIB::ifType"
NAME4="IF-MIB::ifMtu"
NAME5="IF-MIB::ifSpeed"
NAME6="IF-MIB::ifPhysAddress"
NAME7="IF-MIB::ifAdminStatus"
NAME8="IF-MIB::ifOperStatus"
NAME9="IF-MIB::ifLastChange"
NAME10="IF-MIB::ifInOctets"
NAME11="IF-MIB::ifInUcastPkts"
NAME12="IF-MIB::ifInNUcastPkts"
NAME13="IF-MIB::ifInDiscards"
NAME14="IF-MIB::ifInErrors"
NAME15="IF-MIB::ifInUnknownProtos"
NAME16="IF-MIB::ifOutOctets"
NAME17="IF-MIB::ifOutUcastPkts"
NAME18="IF-MIB::ifOutNUcastPkts"
NAME19="IF-MIB::ifOutDiscards"
NAME20="IF-MIB::ifOutErrors"
NAME21="IF-MIB::ifOutQLen"
NAME22="IF-MIB::ifSpecific"
NAME24="IF-MIB::ifRcvAddressAddress.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
NAME25="IF-MIB::ifRcvAddressAddress.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
NAME26="IF-MIB::ifRcvAddressStatus.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
NAME27="IF-MIB::ifRcvAddressStatus.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
NAME28="IF-MIB::ifRcvAddressType.1.17.49.49.58.98.98.58.99.99.58.100.100.58.101.101.58.102.102"
NAME29="IF-MIB::ifRcvAddressType.2.17.97.97.58.50.50.58.51.51.58.52.52.58.53.53.58.54.54"
new "$snmpget"
new "Test SNMP get all entries in ifTable"
new "Test $OID1 ifIndex"
validate_oid $OID1 $OID1 "INTEGER" "1"
validate_oid "$NAME1.1" "$NAME1.1" "INTEGER" 1
validate_oid "$NAME1.1" "$NAME1.2" "INTEGER" 2
new "Test $OID2 ifDescr"
validate_oid $OID2 $OID2 "STRING" "Test"
validate_oid $NAME2.1 $NAME2.1 "STRING" "Test"
validate_oid $NAME2.2 $NAME2.2 "STRING" "Test"
new "Test $OID3 ifType"
validate_oid $OID3 $OID3 "INTEGER" "ethernetCsmacd(6)"
validate_oid $NAME3.1 $NAME3.1 "INTEGER" "ethernetCsmacd(6)"
validate_oid $NAME3.2 $NAME3.2 "INTEGER" "ethernetCsmacd(6)"
new "Test $OID4 ifMtu"
validate_oid $OID4 $OID4 "INTEGER" "1500"
validate_oid $NAME4.1 $NAME4.1 "INTEGER" 1500
validate_oid $NAME4.2 $NAME4.2 "INTEGER" 1400
new "Test $OID5 ifSpeed"
validate_oid $OID5 $OID5 "Gauge32" "10000000"
validate_oid $NAME5.1 $NAME5.1 "Gauge32" 10000000
validate_oid $NAME5.2 $NAME5.2 "Gauge32" 1000
new "Test $OID6 ifPhysAddress yang:phys-address"
validate_oid $OID6 $OID6 "STRING" "aa.bb:cc:dd:ee:ff"
validate_oid $NAME6.1 $NAME6.1 "STRING" "aa.bb:cc:dd:ee:ff"
validate_oid $NAME6.2 $NAME6.2 "STRING" "11:22:33:44:55:66"
new "Test $OID7 ifAdminStatus"
validate_oid $OID7 $OID7 "INTEGER" "testing(3)"
validate_oid $NAME7.1 $NAME7.1 "INTEGER" "testing(3)"
validate_oid $NAME7.2 $NAME7.2 "INTEGER" "down(2)"
new "Test $OID8 ifOperStatus"
validate_oid $OID8 $OID8 "INTEGER" "up(1)"
validate_oid $NAME8.1 $NAME8.1 "INTEGER" "up(1)"
validate_oid $NAME8.2 $NAME8.2 "INTEGER" "down(2)"
new "Test $OID9 ifLastChange"
validate_oid $OID9 $OID9 "Timeticks" "(0) 0:00:00.00"
validate_oid $NAME9.1 $NAME9.1 "Timeticks" "(0) 0:00:00.00"
validate_oid $NAME9.2 $NAME9.2 "Timeticks" "(0) 0:00:00.00"
new "Test $OID10 ifInOctets"
validate_oid $OID10 $OID10 "Counter32" 123
validate_oid $NAME10.1 $NAME10.1 "Counter32" 123
validate_oid $NAME10.2 $NAME10.2 "Counter32" 111
new "Test $OID11 ifInUcastPkts"
validate_oid $OID11 $OID11 "Counter32" 124
validate_oid $NAME11.1 $NAME11.1 "Counter32" 124
validate_oid $NAME11.2 $NAME11.2 "Counter32" 222
new "Test $OID12 ifInNUcastPkts"
validate_oid $OID12 $OID12 "Counter32" 124
validate_oid $NAME12.1 $NAME12.1 "Counter32" 124
validate_oid $NAME12.2 $NAME12.2 "Counter32" 333
new "Test $OID13 ifInDiscards"
validate_oid $OID13 $OID13 "Counter32" 125
validate_oid $NAME13.1 $NAME13.1 "Counter32" 125
validate_oid $NAME13.2 $NAME13.2 "Counter32" 444
new "Test $OID14 ifInErrors"
validate_oid $OID14 $OID14 "Counter32" 126
validate_oid $NAME14.1 $NAME14.1 "Counter32" 126
validate_oid $NAME14.2 $NAME14.2 "Counter32" 555
new "Test $OID15 ifInUnknownProtos"
validate_oid $OID15 $OID15 "Counter32" 127
validate_oid $NAME15.1 $NAME15.1 "Counter32" 127
validate_oid $NAME15.2 $NAME15.2 "Counter32" 666
new "Test $OID16 ifOutOctets"
validate_oid $OID16 $OID16 "Counter32" 128
validate_oid $NAME16.1 $NAME16.1 "Counter32" 128
validate_oid $NAME16.2 $NAME16.2 "Counter32" 777
new "Test $OID17 ifOutUcastPkts"
validate_oid $OID17 $OID17 "Counter32" 129
validate_oid $NAME17.1 $NAME17.1 "Counter32" 129
validate_oid $NAME17.2 $NAME17.2 "Counter32" 888
new "Test $OID18 ifOutNUcastPkts"
validate_oid $OID18 $OID18 "Counter32" 129
validate_oid $NAME18.1 $NAME18.1 "Counter32" 129
validate_oid $NAME18.2 $NAME18.2 "Counter32" 999
new "Test $OID19 ifOutDiscards"
validate_oid $OID19 $OID19 "Counter32" 130
validate_oid $NAME19.1 $NAME19.1 "Counter32" 130
validate_oid $NAME19.2 $NAME19.2 "Counter32" 101010
new "Test $OID20 ifOutErrors"
validate_oid $OID20 $OID20 "Counter32" 131
validate_oid $NAME20.1 $NAME20.1 "Counter32" 131
validate_oid $NAME20.2 $NAME20.2 "Counter32" 111111
new "Test $OID21 ifOutQLen"
validate_oid $OID21 $OID21 "Gauge32" 132
validate_oid $NAME21.1 $NAME21.1 "Gauge32" 132
validate_oid $NAME21.2 $NAME21.2 "Gauge32" 111
new "Test $OID22 ifSpecific"
validate_oid $OID22 $OID22 "OID" ".0.0"
validate_oid $NAME22.1 $NAME22.1 "OID" "SNMPv2-SMI::zeroDotZero"
validate_oid $NAME22.2 $NAME22.2 "OID" "iso.2.3"
new "Test ifTable"
expectpart "$($snmptable IF-MIB::ifTable)" 0 "Test 2" "1400" "1000" "11:22:33:44:55:66" "down" "111" "222" "333" "444" "555" "666" "777" "888" "999" "101010" "111111" "111"
new "Walk the walk..."
expectpart "$($snmpwalk IF-MIB::ifTable)" 0 "IF-MIB::ifIndex.1 = INTEGER: 1" \
"IF-MIB::ifIndex.2 = INTEGER: 2" \
"IF-MIB::ifDescr.1 = STRING: Test." \
"IF-MIB::ifDescr.2 = STRING: Test 2." \
"IF-MIB::ifType.1 = INTEGER: ethernetCsmacd(6)" \
"IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)" \
"IF-MIB::ifMtu.1 = INTEGER: 1500" \
"IF-MIB::ifMtu.2 = INTEGER: 1400" \
"IF-MIB::ifSpeed.1 = Gauge32: 10000000" \
"IF-MIB::ifSpeed.2 = Gauge32: 1000" \
"IF-MIB::ifPhysAddress.1 = STRING: aa:bb:cc:dd:ee:ff" \
"IF-MIB::ifPhysAddress.2 = STRING: 11:22:33:44:55:66" \
"IF-MIB::ifAdminStatus.1 = INTEGER: testing(3)" \
"IF-MIB::ifAdminStatus.2 = INTEGER: down(2)" \
"IF-MIB::ifOperStatus.1 = INTEGER: up(1)" \
"IF-MIB::ifOperStatus.2 = INTEGER: down(2)" \
"IF-MIB::ifLastChange.1 = Timeticks: (0) 0:00:00.00" \
"IF-MIB::ifLastChange.2 = Timeticks: (0) 0:00:00.00" \
"IF-MIB::ifInOctets.1 = Counter32: 123" \
"IF-MIB::ifInOctets.2 = Counter32: 111" \
"IF-MIB::ifInUcastPkts.1 = Counter32: 124" \
"IF-MIB::ifInUcastPkts.2 = Counter32: 222" \
"IF-MIB::ifInNUcastPkts.1 = Counter32: 124" \
"IF-MIB::ifInNUcastPkts.2 = Counter32: 333" \
"IF-MIB::ifInDiscards.1 = Counter32: 125" \
"IF-MIB::ifInDiscards.2 = Counter32: 444" \
"IF-MIB::ifInErrors.1 = Counter32: 126" \
"IF-MIB::ifInErrors.2 = Counter32: 555" \
"IF-MIB::ifInUnknownProtos.1 = Counter32: 127" \
"IF-MIB::ifInUnknownProtos.2 = Counter32: 666" \
"IF-MIB::ifOutOctets.1 = Counter32: 128" \
"IF-MIB::ifOutOctets.2 = Counter32: 777" \
"IF-MIB::ifOutUcastPkts.1 = Counter32: 129" \
"IF-MIB::ifOutUcastPkts.2 = Counter32: 888" \
"IF-MIB::ifOutNUcastPkts.1 = Counter32: 129" \
"IF-MIB::ifOutNUcastPkts.2 = Counter32: 999" \
"IF-MIB::ifOutDiscards.1 = Counter32: 130" \
"IF-MIB::ifOutDiscards.2 = Counter32: 101010" \
"IF-MIB::ifOutErrors.1 = Counter32: 131" \
"IF-MIB::ifOutErrors.2 = Counter32: 111111" \
"IF-MIB::ifOutQLen.1 = Gauge32: 132" \
"IF-MIB::ifOutQLen.2 = Gauge32: 111" \
"IF-MIB::ifSpecific.1 = OID: SNMPv2-SMI::zeroDotZero" \
"IF-MIB::ifSpecific.2 = OID: iso.2.3"
new "Test $OID24"
validate_oid $OID24 $OID24 "STRING" "11:bb:cc:dd:ee:ff"
validate_oid $NAME24 $NAME24 "STRING" "11:bb:cc:dd:ee:ff" "IF-MIB::ifRcvAddressAddress.1.\"11:bb:cc:dd:ee:ff\" = STRING: 11:bb:cc:dd:ee:ff"
new "Get next $OID24"
validate_oid $OID24 $OID25 "STRING" "aa:22:33:44:55:66"
validate_oid $NAME24 $NAME25 "STRING" "aa:22:33:44:55:66" "IF-MIB::ifRcvAddressAddress.2.\"aa:22:33:44:55:66\" = STRING: aa:22:33:44:55:66"
new "Get $NAME25"
validate_oid $OID25 $OID25 "STRING" "aa:22:33:44:55:66"
validate_oid $NAME25 $NAME25 "STRING" "aa:22:33:44:55:66" "IF-MIB::ifRcvAddressAddress.2.\"aa:22:33:44:55:66\" = STRING: aa:22:33:44:55:66"
new "Get next $OID25"
validate_oid $OID25 $OID26 "INTEGER" "active(1)"
validate_oid $NAME25 $NAME26 "INTEGER" "active(1)" "IF-MIB::ifRcvAddressStatus.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: active(1)"
new "Get $OID26"
validate_oid $OID26 $OID26 "INTEGER" "active(1)"
validate_oid $NAME26 $NAME26 "INTEGER" "active(1)" "IF-MIB::ifRcvAddressStatus.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: active(1)"
new "Get next $OID26"
validate_oid $OID26 $OID27 "INTEGER" "createAndGo(4)"
validate_oid $NAME26 $NAME27 "INTEGER" "createAndGo(4)" "IF-MIB::ifRcvAddressStatus.2.\"aa:22:33:44:55:66\" = INTEGER: createAndGo(4)"
new "Get $OID27"
validate_oid $OID27 $OID27 "INTEGER" "createAndGo(4)"
validate_oid $NAME27 $NAME27 "INTEGER" "createAndGo(4)" "IF-MIB::ifRcvAddressStatus.2.\"aa:22:33:44:55:66\" = INTEGER: createAndGo(4)"
new "Get next $OID27"
validate_oid $OID27 $OID28 "INTEGER" "other(1)"
validate_oid $NAME27 $NAME28 "INTEGER" "other(1)" "IF-MIB::ifRcvAddressType.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: other(1)"
new "Get $OID28"
validate_oid $OID28 $OID28 "INTEGER" "other(1)"
validate_oid $NAME28 $NAME28 "INTEGER" "other(1)" "IF-MIB::ifRcvAddressType.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: other(1)"
new "Get next $OID28"
validate_oid $OID28 $OID29 "INTEGER" "volatile(2)"
validate_oid $NAME28 $NAME29 "INTEGER" "volatile(2)" "IF-MIB::ifRcvAddressType.2.\"aa:22:33:44:55:66\" = INTEGER: volatile(2)"
new "Test ifTable"
expectpart "$($snmptable IF-MIB::ifRcvAddressTable)" 0 "SNMP table: IF-MIB::ifRcvAddressTable" "ifRcvAddressStatus" "ifRcvAddressType" "active" "other" "createAndGo" "volatile"
new "Walk ifRcvTable"
expectpart "$($snmpwalk IF-MIB::ifRcvAddressTable)" 0 "IF-MIB::ifRcvAddressAddress.1.\"11:bb:cc:dd:ee:ff\" = STRING: 11:bb:cc:dd:ee:ff" \
"IF-MIB::ifRcvAddressAddress.2.\"aa:22:33:44:55:66\" = STRING: aa:22:33:44:55:66" \
"IF-MIB::ifRcvAddressStatus.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: active(1)" \
"IF-MIB::ifRcvAddressStatus.2.\"aa:22:33:44:55:66\" = INTEGER: createAndGo(4)" \
"IF-MIB::ifRcvAddressType.1.\"11:bb:cc:dd:ee:ff\" = INTEGER: other(1)" \
"IF-MIB::ifRcvAddressType.2.\"aa:22:33:44:55:66\" = INTEGER: volatile(2)"
testexit
new "endtest"
endtest

226
test/test_snmp_rowstatus.sh Executable file
View file

@ -0,0 +1,226 @@
#!/usr/bin/env bash
# SNMP table rowstatus tests
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
# XXX skip for now
if [ ${ENABLE_NETSNMP} != "yes" ]; then
echo "Skipping test, Net-SNMP support not enabled."
if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi
snmpd=$(type -p snmpd)
snmpget="$(type -p snmpget) -c public -v2c localhost "
snmpset="$(type -p snmpset) -c public -v2c localhost "
cfg=$dir/conf.xml
fyang=$dir/clixon-example.yang
# AgentX unix socket
SOCK=/var/run/snmp.sock
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>SNMP-NOTIFICATION-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import SNMP-NOTIFICATION-MIB {
prefix "snmp-notification";
}
deviation "/snmp-notification:SNMP-NOTIFICATION-MIB" {
deviate replace {
config true;
}
}
}
EOF
cat <<EOF > $dir/startup_db
<${DATASTORE_TOP}>
</${DATASTORE_TOP}>
EOF
function testinit(){
new "test params: -f $cfg"
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 startup -f $cfg
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 testrun_createAndGo()
{
index=go
new "Configuring a value without a row is a failure"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\' = 2 2>&1)" 2 "Reason: inconsistentValue"
new "Set RowStatus to CreateAndGo and set tag"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = createAndGo SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: createAndGo(4)"
new "Check rowstatus is active"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: active(1)"
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'$index' = STRING: 2"
new "Get tag via netconf: candidate"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/sn:SNMP-NOTIFICATION-MIB/sn:snmpNotifyTable/sn:snmpNotifyEntry[sn:snmpNotifyName='$index']/sn:snmpNotifyTag\" xmlns:sn=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><SNMP-NOTIFICATION-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"><snmpNotifyTable><snmpNotifyEntry><snmpNotifyName>$index</snmpNotifyName><snmpNotifyTag>2</snmpNotifyTag></snmpNotifyEntry></snmpNotifyTable></SNMP-NOTIFICATION-MIB></data></rpc-reply>"
new "Get tag via netconf: running"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><running/></source><filter type=\"xpath\" select=\"/sn:SNMP-NOTIFICATION-MIB/sn:snmpNotifyTable/sn:snmpNotifyEntry[sn:snmpNotifyName='$index']/sn:snmpNotifyTag\" xmlns:sn=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><SNMP-NOTIFICATION-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"><snmpNotifyTable><snmpNotifyEntry><snmpNotifyName>$index</snmpNotifyName><snmpNotifyTag>2</snmpNotifyTag></snmpNotifyEntry></snmpNotifyTable></SNMP-NOTIFICATION-MIB></data></rpc-reply>"
new "set storage type"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'$index\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'$index' = INTEGER: other(1)"
}
function testrun_createAndWait()
{
index=wait
new "Configuring a value without a row is a failure"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\' = 2 2>&1)" 2 "Reason: inconsistentValue"
new "Set RowStatus to CreateAndWait and set tag"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = createAndWait SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: createAndWait(5)"
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'$index' = STRING: 2"
new "Get tag via netconf: candidate expect fail"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/sn:SNMP-NOTIFICATION-MIB/sn:snmpNotifyTable/sn:snmpNotifyEntry[sn:snmpNotifyName='$index']/sn:snmpNotifyTag\" xmlns:sn=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: notInService(2)"
new "Set storagetype"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'$index\' = 1)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'$index' = INTEGER: other(1)"
new "Set rowstatus to active/ commit"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: active(1)"
new "Set storagetype again"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.\'$index\' = 5)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyStorageType.'$index' = INTEGER: readOnly(5)"
new "Set rowstatus to createAndWait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: createAndWait(5)"
new "Set second rowstatus to createAndGo"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'${index}2\' = createAndGo)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'${index}2' = INTEGER: createAndGo(4)"
new "Set third rowstatus to createAndWait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'${index}3\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'${index}3' = INTEGER: createAndWait(5)"
new "Set third rowstatus to active"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'${index}3\' = active)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'${index}3' = INTEGER: active(1)"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: notInService(2)"
}
function testrun_removeRows()
{
index=remove
new "Set RowStatus to CreateAndGo and set tag"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = createAndGo SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\' = 2)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: createAndGo(4)"
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'$index' = STRING: 2"
new "Get tag via netconf"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/sn:SNMP-NOTIFICATION-MIB/sn:snmpNotifyTable/sn:snmpNotifyEntry[sn:snmpNotifyName='$index']/sn:snmpNotifyTag\" xmlns:sn=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data><SNMP-NOTIFICATION-MIB xmlns=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"><snmpNotifyTable><snmpNotifyEntry><snmpNotifyName>$index</snmpNotifyName><snmpNotifyTag>2</snmpNotifyTag></snmpNotifyEntry></snmpNotifyTable></SNMP-NOTIFICATION-MIB></data></rpc-reply>"
new "Set rowstatus to destroy"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = destroy)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: destroy(6)"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = No Such Instance currently exists at this OID"
# Default value is ""
new "Get tag"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyTag.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyTag.'$index' = STRING: " --not-- "= STRING: 2"
new "Get tag via netconf: candidate expect fail"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/sn:SNMP-NOTIFICATION-MIB/sn:snmpNotifyTable/sn:snmpNotifyEntry[sn:snmpNotifyName='$index']/sn:snmpNotifyTag\" xmlns:sn=\"urn:ietf:params:xml:ns:yang:smiv2:SNMP-NOTIFICATION-MIB\"/></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
new "Set rowstatus to createandwait"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = createAndWait)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index'"
new "Set rowstatus to destroy"
expectpart "$($snmpset SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\' = destroy)" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = INTEGER: destroy(6)"
new "Get rowstatus"
expectpart "$($snmpget SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.\'$index\')" 0 "SNMP-NOTIFICATION-MIB::snmpNotifyRowStatus.'$index' = No Such Instance currently exists at this OID"
}
function testexit()
{
stop_snmp
}
new "SNMP tests"
testinit
new "createAndGo"
testrun_createAndGo
new "createAndWait"
testrun_createAndWait
new "removeRows"
testrun_removeRows
new "Cleaning up"
testexit
new "endtest"
endtest

248
test/test_snmp_set.sh Executable file
View file

@ -0,0 +1,248 @@
#!/usr/bin/env bash
# snmpset. This requires deviation of MIB-YANG to make write operations
# Get default value, set new value via SNMP and check it, set new value via NETCONF and check
# Selected types from CLIXON/IF-MIB/ENTITY mib
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
# XXX skip for now
if [ ${ENABLE_NETSNMP} != "yes" ]; then
echo "Skipping test, Net-SNMP support not enabled."
if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi
snmpd=$(type -p snmpd)
snmpget="$(type -p snmpget) -On -c public -v2c localhost "
snmpset="$(type -p snmpset) -On -c public -v2c localhost "
cfg=$dir/conf.xml
fyang=$dir/clixon-example.yang
# AgentX unix socket
SOCK=/var/run/snmp.sock
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>CLIXON-TYPES-MIB</CLICON_SNMP_MIB>
<CLICON_SNMP_MIB>IF-MIB</CLICON_SNMP_MIB>
<CLICON_SNMP_MIB>ENTITY-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import CLIXON-TYPES-MIB {
prefix "clixon-types";
}
import IF-MIB {
prefix "if-mib";
}
import ENTITY-MIB {
prefix "entity-mib";
}
deviation "/clixon-types:CLIXON-TYPES-MIB" {
deviate replace {
config true;
}
}
deviation "/if-mib:IF-MIB" {
deviate replace {
config true;
}
}
deviation "/entity-mib:ENTITY-MIB" {
deviate replace {
config true;
}
}
}
EOF
if true; then # Dont start with a state (default)
cat <<EOF > $dir/startup_db
<${DATASTORE_TOP}>
</${DATASTORE_TOP}>
EOF
else # Start with a state (debug)
cat <<EOF > $dir/startup_db
<${DATASTORE_TOP}>
<CLIXON-TYPES-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:CLIXON-TYPES-MIB">
<clixonExampleScalars>
<clixonExampleInteger>42</clixonExampleInteger>
<ifIpAddr>4.3.2.1</ifIpAddr>
</clixonExampleScalars>
</CLIXON-TYPES-MIB>
<IF-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:IF-MIB">
<ifStackTable>
<ifStackEntry>
<ifStackHigherLayer>9</ifStackHigherLayer>
<ifStackLowerLayer>9</ifStackLowerLayer>
</ifStackEntry>
</ifStackTable>
<ifTable>
<ifEntry>
<ifIndex>1</ifIndex>
<ifPhysAddress>aa:bb:cc:dd:ee:ff</ifPhysAddress>
</ifEntry>
</ifTable>
</IF-MIB>
</${DATASTORE_TOP}>
EOF
fi
function testinit(){
new "test params: -f $cfg"
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 startup -f $cfg
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
}
# Set value via SNMP, read value via SNMP and CLI
# Args:
# 1: name
# 2: type
# 3: value SNMP value
# 4: value2 SNMP value2 (as shown "after" snmpset)
# 5: xvalue XML/Clixon value
# 6: OID
function testrun()
{
name=$1
type=$2
value=$3
value2=$4
xvalue=$5
oid=$6
# Type from man snmpset
case $type in
"INTEGER")
set_type="i"
;;
"STRING")
set_type="s"
;;
"HEX STRING")
set_type="x"
;;
"TIMETICKS")
set_type="t"
;;
"IPADDRESS")
set_type="a"
;;
"OBJID")
set_type="o"
;;
"BITS")
set_type="b"
;;
*)
set_type="s"
;;
esac
new "Set $name via SNMP"
if [ $type == "STRING" ]; then
echo "$snmpset $oid $set_type $value"
expectpart "$($snmpset $oid $set_type $value)" 0 "$type:" "$value"
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
expectpart "$($snmpget $oid)" 0 "$type:" "$value"
else
expectpart "$($snmpget $oid)" 0 "$type: $value2"
fi
new "Check $name via CLI"
expectpart "$($clixon_cli -1 -f $cfg show config xml)" 0 "<$name>$xvalue</$name>"
}
function testexit(){
stop_snmp
}
new "SNMP tests"
testinit
MIB=".1.3.6.1.4.1.8072.200"
IFMIB=".1.3.6.1.2.1"
ENTMIB=".1.3.6.1.2.1.47.1.1.1"
testrun clixonExampleInteger INTEGER 1234 1234 1234 ${MIB}.1.1
testrun clixonExampleSleeper INTEGER -1 -1 -1 ${MIB}.1.2
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
# Inline testrun for rowstatus complicated logic
name=ifStackStatus
type=INTEGER
oid=${IFMIB}.31.1.2.1.3.5.9
new "Set $name via SNMP"
expectpart "$($snmpset $oid i 4)" 0 "$type: createAndGo(4)"
new "Check $name via SNMP"
expectpart "$($snmpget $oid)" 0 "$type: active(1)"
new "Check $name via CLI"
expectpart "$($clixon_cli -1 -f $cfg show config xml)" 0 "<$name>active</$name>"
new "Cleaning up"
testexit
new "endtest"
endtest

239
test/test_snmp_system.sh Executable file
View file

@ -0,0 +1,239 @@
#!/usr/bin/env bash
# SNMP system MIB test
# 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."
if [ "$s" = $0 ]; then exit 0; else return 0; fi
fi
snmpd=$(type -p snmpd)
snmpget="$(type -p snmpget) -On -c public -v2c localhost "
snmpwalk="$(type -p snmpwalk) -On -c public -v2c localhost "
snmpwalkstr="$(type -p snmpwalk) -c public -v2c localhost "
snmpgetnext="$(type -p snmpgetnext) -On -c public -v2c localhost "
snmptable="$(type -p snmptable) -c public -v2c localhost "
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 <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${YANG_STANDARD_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>${MIB_GENERATED_YANG_DIR}</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/var/tmp/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_SNMP_AGENT_SOCK>unix:$SOCK</CLICON_SNMP_AGENT_SOCK>
<CLICON_SNMP_MIB>SNMPv2-MIB</CLICON_SNMP_MIB>
<CLICON_VALIDATE_STATE_XML>true</CLICON_VALIDATE_STATE_XML>
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-example{
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
import SNMPv2-MIB {
prefix "snmpv2-mib";
}
}
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 <<EOF > $fstate
<SNMPv2-MIB xmlns="urn:ietf:params:xml:ns:yang:smiv2:SNMPv2-MIB">
<system>
<sysName>Test</sysName>
<sysContact>clixon@clicon.com</sysContact>
<sysLocation>Clixon HQ</sysLocation>
<sysDescr>System description</sysDescr>
<sysUpTime>11223344</sysUpTime>
<sysServices>72</sysServices>
</system>
<sysORTable>
<sysOREntry>
<sysORIndex>1</sysORIndex>
<sysORID>1.3.6.1.2.1.4</sysORID>
<sysORDescr>Entry 1 description</sysORDescr>
<sysORUpTime>11223344</sysORUpTime>
</sysOREntry>
<sysOREntry>
<sysORIndex>2</sysORIndex>
<sysORID>1.3.6.1.2.1.2.2</sysORID>
<sysORDescr>Entry 2 description</sysORDescr>
<sysORUpTime>1122111111</sysORUpTime>
</sysOREntry>
</sysORTable>
</SNMPv2-MIB>
EOF
function testinit(){
new "test params: -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
}
new "SNMP tests"
testinit
OID_SYS=".1.3.6.1.2.1.1"
OID_DESCR="${OID_SYS}.1"
OID_UPTIME="${OID_SYS}.3"
OID_CONTACT="${OID_SYS}.4"
OID_SYSNAME="${OID_SYS}.5"
OID_LOCATION="${OID_SYS}.6"
OID_SERVICES="${OID_SYS}.7"
OID_ORTABLE="${OID_SYS}.9"
OID_ORTABLE1_IDX="${OID_SYS}.9.1.1.1"
OID_ORTABLE2_IDX="${OID_SYS}.9.1.1.2"
OID_ORTABLE1="${OID_SYS}.9.1.3.1"
OID_ORTABLE2="${OID_SYS}.9.1.3.2"
NAME_DESCR="SNMPv2-MIB::sysDescr"
NAME_UPTIME="SNMPv2-MIB::sysUpTime"
NAME_CONTACT="SNMPv2-MIB::sysContact"
NAME_SYSNAME="SNMPv2-MIB::sysName"
NAME_LOCATION="SNMPv2-MIB::sysLocation"
NAME_SERVICES="SNMPv2-MIB::sysServices"
NAME_ORTABLE="SNMPv2-MIB::sysORTable"
NAME_ORTABLE1_IDX="SNMPv2-MIB::sysORIndex.1"
NAME_ORTABLE2_IDX="SNMPv2-MIB::sysORIndex.2"
NAME_ORTABLE1="SNMPv2-MIB::sysORDescr.1"
NAME_ORTABLE2="SNMPv2-MIB::sysORDescr.2"
new "Get description, $OID_DESCR"
validate_oid $OID_DESCR $OID_DESCR "STRING" "System description"
validate_oid $NAME_DESCR $NAME_DESCR "STRING" "System description"
new "Get next $OID_DESCR"
validate_oid $OID_DESCR $OID_UPTIME "Timeticks" "(11223344) 1 day, 7:10:33.44"
validate_oid $NAME_DESCR $NAME_UPTIME "Timeticks" "(11223344) 1 day, 7:10:33.44"
new "Get contact, $OID_CONTACT"
validate_oid $OID_CONTACT $OID_CONTACT "STRING" "clixon@clicon.com"
validate_oid $NAME_CONTACT $NAME_CONTACT "STRING" "clixon@clicon.com"
new "Get next OID after contact $OID_CONTACT"
validate_oid $OID_CONTACT $OID_SYSNAME "STRING" "Test"
validate_oid $NAME_CONTACT $NAME_SYSNAME "STRING" "Test"
new "Get sysName $OID_SYSNAME"
validate_oid $OID_SYSNAME $OID_SYSNAME "STRING" "Test"
validate_oid $NAME_SYSNAME $NAME_SYSNAME "STRING" "Test"
new "Get next OID after sysName $OID_SYSNAME"
validate_oid $OID_SYSNAME $OID_LOCATION "STRING" "Clixon HQ"
validate_oid $NAME_SYSNAME $NAME_LOCATION "STRING" "Clixon HQ"
new "Get sysLocation $OID_LOCATION"
validate_oid $OID_LOCATION $OID_LOCATION "STRING" "Clixon HQ"
validate_oid $NAME_LOCATION $NAME_LOCATION "STRING" "Clixon HQ"
new "Get next OID after sysLocation $OID_LOCATION"
validate_oid $OID_LOCATION $OID_SERVICES "INTEGER" 72
validate_oid $NAME_LOCATION $NAME_SERVICES "INTEGER" 72
new "Get sysServices $OID_SERVICES"
validate_oid $OID_SERVICES $OID_SERVICES "INTEGER" "72"
validate_oid $NAME_SERVICES $NAME_SERVICES "INTEGER" "72"
new "Get next OID after sysServices $OID_SERVICES"
validate_oid $OID_SERVICES $OID_ORTABLE1_IDX "INTEGER" 1
validate_oid $NAME_SERVICES $NAME_ORTABLE1_IDX "INTEGER" 1
new "Get first index of OR table $OID_ORTABLE1_IDX"
validate_oid $OID_ORTABLE1_IDX $OID_ORTABLE1_IDX "INTEGER" 1
validate_oid $NAME_ORTABLE1_IDX $NAME_ORTABLE1_IDX "INTEGER" 1
new "Get next OID after index $OID_ORTABLE1_IDX"
validate_oid $OID_ORTABLE1_IDX $OID_ORTABLE2_IDX "INTEGER" 2
validate_oid $NAME_ORTABLE1_IDX $NAME_ORTABLE2_IDX "INTEGER" 2
new "Get second index $OID_ORTABLE2_IDX"
validate_oid $OID_ORTABLE2_IDX $OID_ORTABLE2_IDX "INTEGER" 2
validate_oid $NAME_ORTABLE2_IDX $NAME_ORTABLE2_IDX "INTEGER" 2
new "Get sysORTable, entry 1 $OID_ORTABLE1"
validate_oid $OID_ORTABLE1 $OID_ORTABLE1 "STRING" "Entry 1 description"
validate_oid $NAME_ORTABLE1 $NAME_ORTABLE1 "STRING" "Entry 1 description"
new "Get sysORTable, entry 2 $OID_ORTABLE2"
validate_oid $OID_ORTABLE2 $OID_ORTABLE2 "STRING" "Entry 2 description"
validate_oid $NAME_ORTABLE2 $NAME_ORTABLE2 "STRING" "Entry 2 description"
new "Get table sysORTable $OID_ORTABLE"
expectpart "$($snmptable $OID_ORTABLE)" 0 ".*Entry 1 description.*" "IP-MIB::ip" "1:7:10:33.44"
expectpart "$($snmptable $OID_ORTABLE)" 0 ".*Entry 2 description.*" "IF-MIB::ifTable" "129:20:58:31.11"
expectpart "$($snmptable $NAME_ORTABLE)" 0 ".*Entry 1 description.*" "IP-MIB::ip" "1:7:10:33.44"
expectpart "$($snmptable $NAME_ORTABLE)" 0 ".*Entry 2 description.*" "IF-MIB::ifTable" "129:20:58:31.11"
new "Walk the tabbles..."
expectpart "$($snmpwalkstr system)" 0 "SNMPv2-MIB::sysDescr = STRING: System description." \
"SNMPv2-MIB::sysUpTime = Timeticks: (11223344) 1 day, 7:10:33.44" \
"SNMPv2-MIB::sysContact = STRING: clixon@clicon.com." \
"SNMPv2-MIB::sysName = STRING: Test." \
"SNMPv2-MIB::sysLocation = STRING: Clixon HQ." \
"SNMPv2-MIB::sysServices = INTEGER: 72" \
"SNMPv2-MIB::sysORIndex.1 = INTEGER: 1" \
"SNMPv2-MIB::sysORIndex.2 = INTEGER: 2" \
"SNMPv2-MIB::sysORID.1 = OID: IP-MIB::ip" \
"SNMPv2-MIB::sysORID.2 = OID: IF-MIB::ifTable" \
"SNMPv2-MIB::sysORDescr.1 = STRING: Entry 1 description." \
"SNMPv2-MIB::sysORDescr.2 = STRING: Entry 2 description." \
"SNMPv2-MIB::sysORUpTime.1 = Timeticks: (11223344) 1 day, 7:10:33.44" \
"SNMPv2-MIB::sysORUpTime.2 = Timeticks: (1122111111) 129 days, 20:58:31.11"
new "Cleaning up"
testexit
new "endtest"
endtest

View file

@ -1132,5 +1132,27 @@ module clixon-config {
0 means no limit"; 0 means no limit";
} }
leaf-list CLICON_SNMP_MIB {
description
"Names of MIBs that are used by clixon_snmp.
For each MIB M, a YANG file M.yang is expected to be found.
If not found, an error is genereated.
The YANG file M.yang is typically generated from the source MIB but can also
be handcrafted. An example of such a script is scripts/mib_to_yang.sh.
A list of these options should be in the configuration.";
type string;
}
leaf CLICON_SNMP_AGENT_SOCK {
type string;
default "unix:/tmp/clixon_snmp.sock";
description
"String description of AgentX socket that clixon_snmp listens to.
For example, for net-snmpd, the socket is created by using the following:
--agentXSocket=unix:<path>
This string currently only supports UNIX socket path.
Note also that the user should consider setting permissions appropriately
XXX: This should be in later yang revision and documented as added when
merged with master";
}
} }
} }