Skip to content
Snippets Groups Projects
Commit 33ef2314 authored by Taya Snijder's avatar Taya Snijder
Browse files

Merge branch 'L2SS-446-Extend-SNMP-client-to-support-MIB-files' into 'master'

Resolve L2SS-446 "Extend snmp client to support mib files"

Closes L2SS-446

See merge request !288
parents 5b7224f4 f3004a73
No related branches found
No related tags found
1 merge request!288Resolve L2SS-446 "Extend snmp client to support mib files"
Showing
with 1941 additions and 295 deletions
......@@ -35,7 +35,8 @@
"TileBeam": {
"STAT": {
"TileBeam": {
"STAT/TileBeam/1": {}
"STAT/TileBeam/1": {
}
}
}
},
......
......@@ -2,6 +2,8 @@
from tangostationcontrol.clients.comms_client import CommClient
from pysnmp import hlapi
from pysnmp.smi import builder
from os import path
import numpy
import logging
......@@ -12,8 +14,9 @@ __all__ = ["SNMP_client"]
snmp_to_numpy_dict = {
hlapi.Integer32: numpy.int64,
hlapi.Integer: numpy.int64,
hlapi.TimeTicks: numpy.int64,
str: str,
hlapi.OctetString: str,
hlapi.ObjectIdentity: str,
hlapi.Counter32: numpy.int64,
hlapi.Gauge32: numpy.int64,
......@@ -21,6 +24,27 @@ snmp_to_numpy_dict = {
}
class SNMP_comm:
"""
Holds information for communicating with the SNMP server
"""
def __init__(self, community, host, port):
self.port = port
self.engine = hlapi.SnmpEngine()
self.community = hlapi.CommunityData(community)
self.transport = hlapi.UdpTransportTarget((host, port))
# context data sets the version used. Default SNMPv2
self.ctx_data = hlapi.ContextData()
def getter(self, objs):
return next(hlapi.getCmd(self.engine, self.community, self.transport, self.ctx_data, *objs))
def setter(self, objs):
return next(hlapi.getCmd(self.engine, self.community, self.transport, self.ctx_data, *objs))
class SNMP_client(CommClient):
"""
messages to keep a check on the connection. On connection failure, reconnects once.
......@@ -36,26 +60,23 @@ class SNMP_client(CommClient):
super().__init__(fault_func, try_interval)
logger.debug(f"setting up SNMP engine with host: {host} and community: {community}")
self.port = port
self.engine = hlapi.SnmpEngine()
self.community = hlapi.CommunityData(community)
self.transport = hlapi.UdpTransportTarget((host, port))
# context data sets the version used. Default SNMPv2
self.ctx_data = hlapi.ContextData()
self.SNMP_comm = SNMP_comm(community, host, port)
# only sets up the engine, doesn't connect
self.connected = True
def _process_annotation(self, annotation):
def _setup_annotation(self, annotation):
"""
parses the annotation this attribute received for its initialisation.
"""
try:
mib = annotation["mib"]
name = annotation["name"]
wrapper = annotation_wrapper(annotation)
return wrapper
# SNMP has tables that require an index number to access them. regular non-table variable have an index of 0
idx = annotation.get('index', 0)
return mib, name, idx
except KeyError:
raise ValueError(f"SNMP attribute annotation requires a dict argument with both a 'name' and 'mib' key. Instead got: {annotation}")
def setup_value_conversion(self, attribute):
"""
......@@ -78,11 +99,11 @@ class SNMP_client(CommClient):
"""
# process the annotation
wrapper = self._setup_annotation(annotation)
mib, name, idx = self._process_annotation(annotation)
# get all the necessary data to set up the read/write functions from the attribute_wrapper
dim_x, dim_y, dtype = self.setup_value_conversion(attribute)
snmp_attr = snmp_attribute(self, wrapper, dtype, dim_x, dim_y)
snmp_attr = snmp_attribute(self, mib, name, idx, dtype, dim_x, dim_y)
# return the read/write functions
def read_function():
......@@ -93,145 +114,48 @@ class SNMP_client(CommClient):
return read_function, write_function
class snmp_attribute:
class annotation_wrapper:
def __init__(self, annotation):
"""
The SNMP client uses a dict and takes the following keys:
either
oids: Required. An oid string of the object
or
mib: the mib name
name: name of the value to read
index (optional) the index if the value thats being read from is a table.
"""
# values start as None because we have a way too complicated interface
self.oids = None
self.mib = None
self.name = None
self.idx = None
# check if the 'oids' key is used and not the 'mib' and 'name' keys
if 'oids' in annotation and 'mib' not in annotation and 'name' not in annotation:
self.oids = annotation["oids"]
# checks to make sure this isn't present
if 'index' in annotation:
raise ValueError(f"SNMP attribute annotation doesn't support oid type declarations with an index present.")
# check if the 'oids' key is NOT used but instead the 'mib' and 'name' keys
elif 'oids' not in annotation and 'mib' in annotation and 'name' in annotation:
self.mib = annotation["mib"]
self.name = annotation["name"]
# SNMP has tables that require an index number to access them. regular non-table variable have an index of 0
self.idx = annotation.get('index', 0)
def __init__(self, comm: SNMP_comm, mib, name, idx, dtype, dim_x, dim_y):
else:
raise ValueError(
f"SNMP attribute annotation requires a dict argument with either a 'oids' key or both a 'name' and 'mib' key. Not both. Instead got: {annotation}")
self.comm = comm
self.mib = mib
self.name = name
self.idx = idx
self.dtype = dtype
def create_objID(self, x, y):
is_scalar = (x + y) == 1
self.len = self.get_len(dim_x, dim_y)
self.is_scalar = self.len == 1
# if oids are used
if self.oids is not None:
# get a list of str of the oids
self.oids = self._get_oids(x, y, self.oids)
self.objID = self.create_objID()
# turn the list of oids in to a tuple of pysnmp object identities. These are used for the
objID = tuple(hlapi.ObjectIdentity(self.oids[i]) for i in range(len(self.oids)))
def get_len(self, dim_x, dim_y):
"""""Small helper function to not clutter the __init__"""
# if mib + name is used
else:
if dim_x == 0:
dim_x = 1
if dim_y == 0:
dim_y = 1
return dim_x * dim_y
# only scalars can be used at the present time.
if not is_scalar:
# tuple(hlapi.ObjectIdentity(mib, name, idx) for i in range(len(oids)))
def create_objID(self):
raise ValueError(f"MIB + name type attributes can only be scalars, got dimensions of: ({x}, {y})")
else:
if self.is_scalar:
objID = hlapi.ObjectIdentity(self.mib, self.name, self.idx)
return objID
def _get_oids(self, x, y, in_oid):
"""
This function expands oids depending on dimensionality.
if its a scalar its left alone, but if its an array it creates a list of sequential oids if not already provided
scalar "1.1.1.1" -> stays the same
spectrum: "1.1.1.1" -> ["1.1.1.1.1", "1.1.1.1.2, ..."]
"""
if x == 0:
x = 1
if y == 0:
y = 1
is_scalar = (x * y) == 1
nof_oids = x * y
# if scalar
if is_scalar:
if type(in_oid) is str:
# for ease of handling put single oid in a 1 element list
in_oid = [in_oid]
return in_oid
else:
# if we got a single str oid, make a list of sequential oids
if type(in_oid) is str:
return ["{}.{}".format(in_oid, i + 1) for i in range(nof_oids)]
# if its an already expanded list of all oids
elif type(in_oid) is list and len(in_oid) == nof_oids:
return in_oid
# if its a list of attributes with the wrong length.
else:
raise ValueError(
"SNMP oids need to either be a single value or an array the size of the attribute dimensions. got: {} expected: {}x{}={}".format(
len(in_oid), x, y, x * y))
class snmp_attribute:
def __init__(self, client : SNMP_client, wrapper, dtype, dim_x, dim_y):
self.client = client
self.wrapper = wrapper
self.dtype = dtype
self.dim_x = dim_x
self.dim_y = dim_y
self.is_scalar = (self.dim_x + self.dim_y) == 1
self.objID = self.wrapper.create_objID(self.dim_x, self.dim_y)
objID = tuple(hlapi.ObjectIdentity(self.mib, self.name, self.idx + i) for i in range(self.len))
def next_wrap(self, cmd):
"""
This function exists to allow the next(cmd) call to be mocked for unit testing. As the
"""
return next(cmd)
return objID
def read_function(self):
"""
Read function we give to the attribute wrapper
"""
# must be recreated for each read it seems
# we need to remake this every time it seems or things dont work
self.objs = tuple(hlapi.ObjectType(i) for i in self.objID)
# get the thingy to get the values
get_cmd = hlapi.getCmd(self.client.engine, self.client.community, self.client.trasport, self.client.ctx_data, *self.objs)
# dont ask me why 'next' is used to get all of the values
errorIndication, errorStatus, errorIndex, *varBinds = self.next_wrap(get_cmd)
# get all of the values
errorIndication, errorStatus, errorIndex, *varBinds = self.comm.getter(*self.objs)
# get all the values in a list converted to the correct type
val_lst = self.convert(varBinds)
......@@ -250,8 +174,7 @@ class snmp_attribute:
else:
write_obj = tuple(hlapi.ObjectType(self.objID[i], value[i]) for i in range(len(self.objID)))
set_cmd = hlapi.setCmd(self.client.engine, self.client.community, self.client.trasport, self.client.ctx_data, *write_obj)
errorIndication, errorStatus, errorIndex, *varBinds = self.next_wrap(set_cmd)
errorIndication, errorStatus, errorIndex, *varBinds = self.comm.setter(write_obj)
def convert(self, varBinds):
"""
......@@ -259,20 +182,43 @@ class snmp_attribute:
"""
vals = []
if not self.is_scalar:
#just the first element of this single element list
varBinds = varBinds[0]
for varBind in varBinds:
# class 'DisplayString' doesnt want to play along for whatever reason
if "DisplayString" in str(type(varBind[1])):
vals.append(varBind[1].prettyPrint())
elif type(varBind[1]) == hlapi.IpAddress:
# Some MIB's used custom types, some dont. Custom types are merely wrapped base types.
varbind_types = varBind[1].__class__.__bases__ + (type(varBind[1]),)
snmp_type = None
# find if one of the base types is present.
for i in varbind_types:
if i in snmp_to_numpy_dict.keys():
snmp_type = i
if snmp_type is None:
raise TypeError(f"Error: did not find a valid snmp type. Got: {varbind_types}, expected one of: '{snmp_to_numpy_dict.keys()}'")
if snmp_type is hlapi.IpAddress:
# IpAddress values get printed as their raw value but in hex (7F 20 20 01 for 127.0.0.1 for example)
vals.append(varBind[1].prettyPrint())
elif snmp_type is hlapi.Integer32 or snmp_type is hlapi.Integer and self.dtype == str:
# Integers can have 'named values', Where a value can be translated to a specific name. A dict basically
# Example: {1: "other", 2: "invalid", 3: "dynamic", 4: "static",}
if varBind[1].namedValues == {}:
# An empty dict {} means no namedValue's are present.
vals.append(snmp_to_numpy_dict[snmp_type](varBind[1]))
else:
# append the named values string instead of the raw number.
vals.append(varBind[1].prettyPrint())
else:
# convert from the funky pysnmp types to numpy types and then append
vals.append(snmp_to_numpy_dict[type(varBind[1])](varBind[1]))
vals.append(snmp_to_numpy_dict[snmp_type](varBind[1]))
if self.is_scalar:
vals = vals[0]
......@@ -280,3 +226,16 @@ class snmp_attribute:
return vals
class mib_loader:
def __init__(self, mib_dir: str):
self.mibBuilder = builder.MibBuilder()
if not path.isabs(mib_dir):
mib_dir = "/" + mib_dir
mib_source = builder.DirMibSource(mib_dir)
self.mibBuilder.addMibSources(mib_source)
def load_pymib(self, mib_name):
self.mibBuilder.loadModule(mib_name)
......@@ -12,16 +12,14 @@
"""
# PyTango imports
from tango.server import run
from tango.server import device_property
from tango import AttrWriteType
# Additional import
from tangostationcontrol.clients.snmp_client import SNMP_client
from tangostationcontrol.clients.attribute_wrapper import attribute_wrapper
from tangostationcontrol.common.entrypoint import entry
from tangostationcontrol.devices.lofar_device import lofar_device
import numpy
from tango.server import device_property, command
import os
# Additional import
from tangostationcontrol.clients.snmp_client import SNMP_client, mib_loader
import logging
logger = logging.getLogger()
......@@ -39,6 +37,10 @@ class SNMP(lofar_device):
- Type:'DevString'
SNMP_host
- Type:'DevULong'
SNMP_community
- Type:'DevString'
SNMP_rel_mib_dir
- Type:'DevString'
SNMP_timeout
- Type:'DevDouble'
"""
......@@ -57,6 +59,11 @@ class SNMP(lofar_device):
mandatory=True
)
SNMP_rel_mib_dir = device_property(
dtype='DevString',
mandatory=False
)
SNMP_timeout = device_property(
dtype='DevDouble',
mandatory=True
......@@ -66,43 +73,14 @@ class SNMP(lofar_device):
# Attributes
# ----------
# example attributes. mib and name mandatory and index optional.
# octetstring
sysDescr_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysDescr"}, datatype=numpy.str)
sysName_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysName"}, datatype=numpy.str)
# get a table element with the oid
ifDescr31_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.2.2.1.2.31"}, datatype=numpy.str)
# get 10 table elements with the oid and dimension
ifDescr_R = attribute_wrapper(comms_annotation={"oids": "1.3.6.1.2.1.2.2.1.2"}, dims=(10,), datatype=numpy.str)
#timeticks
sysUpTime_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysUpTime"}, datatype=numpy.int64)
# OID
sysObjectID_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysObjectID"}, datatype=numpy.int64)
# integer
sysServices_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysServices"}, datatype=numpy.int64)
tcpRtoAlgorithm_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "tcpRtoAlgorithm"}, datatype=numpy.int64)
snmpEnableAuthenTraps_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "snmpEnableAuthenTraps"}, datatype=numpy.int64)
#gauge
tcpCurrEstab_R = attribute_wrapper(comms_annotation={"mib": "RFC1213-MIB", "name": "tcpCurrEstab"}, datatype=numpy.int64)
#counter32
tcpActiveOpens_R = attribute_wrapper(comms_annotation={"mib": "RFC1213-MIB", "name": "tcpActiveOpens"}, datatype=numpy.int64)
snmpInPkts_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "snmpInPkts"}, datatype=numpy.int64)
#IP address
ipAdEntAddr_R = attribute_wrapper(comms_annotation={"mib": "RFC1213-MIB", "name": "ipAdEntAddr", "index": (127,0,0,1)}, datatype=numpy.str)
ipAdEntIfIndex_R = attribute_wrapper(comms_annotation={"mib": "RFC1213-MIB", "name": "ipAdEntIfIndex", "index": (10, 87, 6, 14)}, datatype=numpy.str)
#str RW attribute
sysContact_obj_R = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysContact"}, datatype=numpy.str)
sysContact_obj_RW = attribute_wrapper(comms_annotation={"mib": "SNMPv2-MIB", "name": "sysContact"}, datatype=numpy.str, access=AttrWriteType.READ_WRITE)
# Reads from a table and returns an array of table entries 1 to 10 (note, tables require an index key and start at 1)
# test_attr1_R = attribute_wrapper(comms_annotation={"mib": "TEST-MIB", "name": "test_attr1", "index": 1}, dims=(10,), datatype=numpy.str)
# indices can also be IP addresses sometimes. Gets a single scalar value
# test_attr2_R = attribute_wrapper(comms_annotation={"mib": "TEST-MIB", "name": "test_attr2", "index": (127,0,0,1)}, datatype=numpy.int64)
# if the attribute doesn't get the value from a table, then no index is needed, or the default of 0 can be supplied.
# test_attr3_R = attribute_wrapper(comms_annotation={"mib": "TEST-MIB", "name": "test_attr3"}, datatype=numpy.int64)
# --------
......@@ -125,6 +103,30 @@ class SNMP(lofar_device):
self.snmp_manager.start()
@command(dtype_out=str)
def get_mib_dir(self):
if not os.path.isabs(self.SNMP_rel_mib_dir):
mib_path = os.path.dirname(__file__) + "/" + self.SNMP_rel_mib_dir
else:
# if the string does start with
mib_path = self.SNMP_rel_mib_dir
return mib_path
def init_device(self):
super().init_device()
# create the mib_loader and set the mib path
loader = mib_loader(self.get_mib_dir())
for i in self.attr_list():
try:
# for all of the attributes attempt to load the pre-compiled MIB. Skips already loaded ones
loader.load_pymib(i.comms_annotation["mib"])
except Exception as e:
raise Exception(f"Failed to load MIB file: {i.comms_annotation.get('mib')} for attribute {i.get_name()}") from e
# --------
# Commands
......@@ -136,8 +138,5 @@ class SNMP(lofar_device):
# ----------
def main(args=None, **kwargs):
"""Main function of the module."""
return entry((SNMP,), args=args, **kwargs)
from tangostationcontrol.common.lofar_logging import configure_logger
configure_logger()
return run((SNMP,), args=args, **kwargs)
Integer32, MibScalar, MibTable, MibTableRow, MibTableColumn, TimeTicks, iso, Gauge32, MibIdentifier, Bits, Counter32 = mibBuilder.importSymbols("SNMPv2-SMI", "Integer32", "MibScalar", "MibTable", "MibTableRow", "MibTableColumn", "TimeTicks", "iso", "Gauge32", "MibIdentifier", "Bits","Counter32")
ConstraintsIntersection, ConstraintsUnion, ValueSizeConstraint, SingleValueConstraint, ValueRangeConstraint = mibBuilder.importSymbols("ASN1-REFINEMENT", "ConstraintsIntersection", "ConstraintsUnion", "ValueSizeConstraint", "SingleValueConstraint", "ValueRangeConstraint")
NamedValues, = mibBuilder.importSymbols("ASN1-ENUMERATION", "NamedValues")
testNamedValue = MibScalar((9, 8, 7, 6, 5, 4, 3, 2, 1), Integer32().subtype(subtypeSpec=ConstraintsUnion(SingleValueConstraint(1, 2, 3, 4))).clone(namedValues=NamedValues(("A", 1), ("B", 2), ("C", 3), ("D", 4)))).setMaxAccess("readonly")
mibBuilder.exportSymbols("TEST-MIB", testNamedValue=testNamedValue)
from tangostationcontrol.test import base
from tangostationcontrol.clients.snmp_client import mib_loader
from pysnmp.smi import view
import pysnmp.hlapi as pysnmp
from pysnmp.smi.rfc1902 import ObjectIdentity
from os import path
class TestMibLoading(base.TestCase):
#name and directory of the pymib file
mib = "TEST-MIB"
# mib file is in a folder that is in the same folder as this test
rel_dir = "SNMP_mib_loading"
def test_content(self):
"""
This file contains a 1 variable named: testNamedValue with oid "9.8.7.6.5.4.3.2.1" with named values: ("A", 1), ("B", 2), ("C", 3), ("D", 4)
In order to confirm that the mib is indeed loaded correctly this test has to get the oids, the values and the named values
"""
abs_dir = path.dirname(__file__) + "/" + self.rel_dir
loader = mib_loader(abs_dir)
loader.load_pymib(self.mib)
# used to view mibs client side
mibView = view.MibViewController(loader.mibBuilder)
# The expected testNamedValue parameters as written in TEST-MIB.py
testNamedValue = "testNamedValue"
testNamedValue_oid = "9.8.7.6.5.4.3.2.1"
testNamedValue_named = "A"
testNamedValue_value = 1
# get testValue and set a value of 1
obj_T = pysnmp.ObjectType(ObjectIdentity(self.mib, testNamedValue), pysnmp.Integer32(1))
obj_T.resolveWithMib(mibView)
# get the oid
self.assertEqual(str(obj_T[0]), testNamedValue_oid)
# get the name format: mib::name
self.assertEqual(obj_T[0].prettyPrint(), f"{self.mib}::{testNamedValue}")
# get the namedValue
self.assertEqual(str(obj_T[1]), testNamedValue_named)
# get the numerical value
self.assertEqual(int(obj_T[1]), testNamedValue_value)
......@@ -5,7 +5,7 @@ from unittest import mock
from tangostationcontrol.test import base
from tangostationcontrol.clients.snmp_client import SNMP_client, snmp_attribute, annotation_wrapper
from tangostationcontrol.clients.snmp_client import SNMP_client, snmp_attribute, SNMP_comm
class server_imitator:
......@@ -13,7 +13,7 @@ class server_imitator:
snmp_to_numpy_dict = {
hlapi.Integer32: numpy.int64,
hlapi.TimeTicks: numpy.int64,
str: str,
hlapi.OctetString: str,
hlapi.Counter32: numpy.int64,
hlapi.Gauge32: numpy.int64,
hlapi.IpAddress: str,
......@@ -35,6 +35,8 @@ class server_imitator:
read_val = (None, snmp_type("1.3.6.1.2.1.1.1.0"))
elif snmp_type is hlapi.IpAddress:
read_val = (None, snmp_type("1.1.1.1"))
elif snmp_type is hlapi.OctetString:
read_val = (None, snmp_type("1"))
else:
read_val = (None, snmp_type(1))
......@@ -48,12 +50,16 @@ class server_imitator:
read_val = []
for _i in range(dims[0]):
read_val.append((None, snmp_type(f"1.1.1.1")))
elif snmp_type is hlapi.OctetString:
read_val = []
for _i in range(dims[0]):
read_val.append((None, snmp_type("1")))
else:
read_val = []
for _i in range(dims[0]):
read_val.append((None, snmp_type(1)))
else:
raise Exception("Image not yet supported :(")
raise Exception("Image not supported :(")
return read_val
......@@ -66,14 +72,14 @@ class server_imitator:
if dims == self.dim_list["scalar"]:
snmp_type_dict = {hlapi.ObjectIdentity:"1.3.6.1.2.1.1.1.0.1",
hlapi.IpAddress: "1.1.1.1",
str: "1"}
hlapi.OctetString: "1"}
check_val = 1
for k,v in snmp_type_dict.items():
if snmp_type is k: check_val = v
elif dims == self.dim_list["spectrum"]:
snmp_type_dict = {hlapi.ObjectIdentity:["1.3.6.1.2.1.1.1.0.1"] * dims[0],
hlapi.IpAddress: ["1.1.1.1"] * dims[0],
str: ["1"] * dims[0]}
hlapi.OctetString: ["1"] * dims[0]}
check_val = check_val = [1] * dims[0]
for k,v in snmp_type_dict.items():
if snmp_type is k: check_val = v
......@@ -84,39 +90,6 @@ class server_imitator:
class TestSNMP(base.TestCase):
def test_annotation_success(self):
"""
unit test for the processing of annotation. Has 2 lists. 1 with things that should succeed and 1 with things that should fail.
"""
client = SNMP_client(community='public', host='localhost', timeout=10, fault_func=None, try_interval=2)
test_list = [
# test name nad MIB type annotation
{"mib": "SNMPv2-MIB", "name": "sysDescr"},
# test name nad MIB type annotation with index
{"mib": "RFC1213-MIB", "name": "ipAdEntAddr", "index": (127, 0, 0, 1)},
{"mib": "random-MIB", "name": "aName", "index": 2},
#oid
{"oids": "1.3.6.1.2.1.2.2.1.2.31"}
]
for i in test_list:
wrapper = client._setup_annotation(annotation=i)
if wrapper.oids is not None:
self.assertEqual(wrapper.oids, i["oids"])
else:
self.assertEqual(wrapper.mib, i["mib"], f"expected mib with: {i['mib']}, got: {wrapper.idx} from: {i}")
self.assertEqual(wrapper.name, i["name"], f"expected name with: {i['name']}, got: {wrapper.idx} from: {i}")
self.assertEqual(wrapper.idx, i.get('index', 0), f"expected idx with: {i.get('index', 0)}, got: {wrapper.idx} from: {i}")
def test_annotation_fail(self):
"""
unit test for the processing of annotation. Has 2 lists. 1 with things that should succeed and 1 with things that should fail.
......@@ -125,56 +98,19 @@ class TestSNMP(base.TestCase):
client = SNMP_client(community='public', host='localhost', timeout=10, fault_func=None, try_interval=2)
fail_list = [
# OIDS cant use the index
{"oids": "1.3.6.1.2.1.2.2.1.2.31", "index": 2},
# mixed annotation is not allowed
{"oids": "1.3.6.1.2.1.2.2.1.2.31", "name": "thisShouldFail"},
# no 'name'
{"mib": "random-MIB", "index": 2},
# no MIB
{"name": "random-name", "index": 2},
]
for i in fail_list:
with self.assertRaises(ValueError):
client._setup_annotation(annotation=i)
def test_oids_scalar(self):
test_oid = "1.1.1.1"
server = server_imitator()
x, y = server.dim_list['scalar']
# we just need the object to call another function
wrapper = annotation_wrapper(annotation = {"oids": "Not None lol"})
# scalar
scalar_expected = [test_oid]
ret_oids = wrapper._get_oids(x, y, test_oid)
self.assertEqual(ret_oids, scalar_expected, f"Expected: {scalar_expected}, got: {ret_oids}")
def test_oids_spectrum(self):
"""
Tests the "get_oids" function, which is for getting lists of sequential oids.
Results should basically be an incrementing list of oids with the final number incremented by 1 each time.
So "1.1" with dims of 3x1 might become ["1.1.1", "1.1.2", "1.1.3"]
"""
server = server_imitator()
test_oid = "1.1.1.1"
x, y = server.dim_list['spectrum']
# we just need the object to call another function
wrapper = annotation_wrapper(annotation={"oids": "Not None lol"})
# spectrum
spectrum_expected = [test_oid + ".1", test_oid + ".2", test_oid + ".3", test_oid + ".4"]
ret_oids = wrapper._get_oids(x, y, test_oid)
self.assertListEqual(ret_oids, spectrum_expected, f"Expected: {spectrum_expected}, got: {ret_oids}")
client._process_annotation(annotation=i)
@mock.patch('pysnmp.hlapi.ObjectIdentity')
@mock.patch('pysnmp.hlapi.ObjectType')
@mock.patch('tangostationcontrol.clients.snmp_client.snmp_attribute.next_wrap')
@mock.patch('tangostationcontrol.clients.snmp_client.SNMP_comm.getter')
def test_snmp_obj_get(self, m_next, m_obj_T, m_obj_i):
"""
Attempts to read a fake SNMP variable and checks whether it got what it expected
......@@ -186,21 +122,24 @@ class TestSNMP(base.TestCase):
for i in server.snmp_to_numpy_dict:
m_next.return_value = (None, None, None, server.get_return_val(i, server.dim_list[j]))
m_client = mock.Mock()
def __fakeInit__(self):
pass
with mock.patch.object(SNMP_comm, '__init__', __fakeInit__):
m_comms = SNMP_comm()
wrapper = annotation_wrapper(annotation={"oids": "1.3.6.1.2.1.2.2.1.2.31"})
snmp_attr = snmp_attribute(client=m_client, wrapper=wrapper, dtype=server.snmp_to_numpy_dict[i], dim_x=server.dim_list[j][0], dim_y=server.dim_list[j][1])
snmp_attr = snmp_attribute(comm=m_comms, mib="test", name="test", idx=0, dtype=server.snmp_to_numpy_dict[i], dim_x=server.dim_list[j][0], dim_y=server.dim_list[j][1])
val = snmp_attr.read_function()
checkval = server.val_check(i, server.dim_list[j])
self.assertEqual(checkval, val, f"Expected: {checkval}, got: {val}")
self.assertEqual(checkval, val, f"During test {j} {i}; Expected: {checkval} of type {i}, got: {val} of type {type(val)}")
@mock.patch('pysnmp.hlapi.ObjectIdentity')
@mock.patch('pysnmp.hlapi.ObjectType')
@mock.patch('pysnmp.hlapi.setCmd')
@mock.patch('tangostationcontrol.clients.snmp_client.snmp_attribute.next_wrap')
def test_snmp_obj_set(self, m_next, m_nextCmd, m_obj_i):
@mock.patch('tangostationcontrol.clients.snmp_client.SNMP_comm.setter')
def test_snmp_obj_set(self, m_next, m_nextCmd, m_obj_T, m_obj_ID):
"""
Attempts to write a value to an SNMP server, but instead intercepts it and compared whether the values is as expected.
"""
......@@ -211,11 +150,15 @@ class TestSNMP(base.TestCase):
for i in server.snmp_to_numpy_dict:
m_next.return_value = (None, None, None, server.get_return_val(i, server.dim_list[j]))
m_client = mock.Mock()
def __fakeInit__(self):
pass
with mock.patch.object(SNMP_comm, '__init__', __fakeInit__):
m_comms = SNMP_comm()
set_val = server.val_check(i, server.dim_list[j])
wrapper = annotation_wrapper(annotation={"oids": "1.3.6.1.2.1.2.2.1.2.31"})
snmp_attr = snmp_attribute(client=m_client, wrapper=wrapper, dtype=server.snmp_to_numpy_dict[i], dim_x=server.dim_list[j][0], dim_y=server.dim_list[j][1])
snmp_attr = snmp_attribute(comm=m_comms, mib="test", name="test", idx=0, dtype=server.snmp_to_numpy_dict[i], dim_x=server.dim_list[j][0], dim_y=server.dim_list[j][1])
res_lst = []
def test(*value):
......@@ -230,8 +173,30 @@ class TestSNMP(base.TestCase):
res_lst = res_lst[0]
checkval = server.val_check(i, server.dim_list[j])
self.assertEqual(checkval, res_lst, f"Expected: {checkval}, got: {res_lst}")
self.assertEqual(checkval, res_lst, f"During test {j} {i}; Expected: {checkval}, got: {res_lst}")
@mock.patch('tangostationcontrol.clients.snmp_client.SNMP_comm.getter')
def test_named_value(self, m_next):
# # {1: "other", 2: "invalid", 3: "dynamic", 4: "static",}
# test_val = hlapi.Integer.withNamedValues(enable=1, disable=0)
# test_val(1)
m_comms = mock.Mock()
snmp_attr = snmp_attribute(comm=m_comms, mib="test", name="test", idx=0, dtype=str, dim_x=1, dim_y=0)
# create a named integer with the values: 'enable' for 1 and 'disable' for 0
test_val = ((None, hlapi.Integer.withNamedValues(enable=1, disable=0)(1)),)
ret_val = snmp_attr.convert(test_val)
# should return 'enable' since we supplied the value 1
self.assertEqual(ret_val, "enable", f"Expected: to get 'enable', got: {ret_val} of type {type(ret_val)}")
# create an unnamed integer with a value of 2
test_val = ((None, hlapi.Integer(2)),)
ret_val = snmp_attr.convert(test_val)
# check to make sure the value is indeed 2
self.assertEqual(ret_val, 2, f"Expected: to get {2}, got: {ret_val} of type {type(ret_val)}")
# -*- coding: utf-8 -*-
#
# This file is part of the LOFAR 2.0 Station Software
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
from tango.test_context import DeviceTestContext
from tangostationcontrol.devices import snmp_device, lofar_device
import mock
from os import path
from tangostationcontrol.test import base
class TestSNMPDevice(base.TestCase):
# some dummy values for mandatory properties
snmp_properties = {'SNMP_community': 'localhost', 'SNMP_host': 161, 'SNMP_rel_mib_dir': "SNMP_mib_loading", 'SNMP_timeout': 5.0}
def setUp(self):
super(TestSNMPDevice, self).setUp()
# Patch DeviceProxy to allow making the proxies during initialisation
# that we otherwise avoid using
for device in [lofar_device]:
proxy_patcher = mock.patch.object(
device, 'DeviceProxy')
proxy_patcher.start()
self.addCleanup(proxy_patcher.stop)
def test_get_mib_dir(self):
with DeviceTestContext(snmp_device.SNMP, properties=self.snmp_properties, process=True) as proxy:
mib_dir = proxy.get_mib_dir()
self.assertEqual(mib_dir, f"{path.dirname(snmp_device.__file__)}/{self.snmp_properties['SNMP_rel_mib_dir']}")
# -*- coding: utf-8 -*-
#
# This file is part of the LOFAR 2.0 Station Software
#
#
#
# Distributed under the terms of the APACHE license.
# See LICENSE.txt for more info.
from tangostationcontrol.test import base
from tangostationcontrol.toolkit.mib_compiler import mib_compiler
import sys
from os.path import isfile
from os import getcwd
from tempfile import TemporaryDirectory
from unittest import mock
class TestCompiler(base.TestCase):
def test_compile(self):
with TemporaryDirectory() as tmpdir:
new_sys_argv = [sys.argv[0], "--mibs", "TEST-MIB",
"--source", f"{getcwd()}/tangostationcontrol/toolkit/mib_compiler/mibs",
"--destination", f"{tmpdir}", "-v"]
with mock.patch.object(mib_compiler.sys, 'argv', new_sys_argv):
with self.assertRaises(SystemExit):
mib_compiler.main()
# check if file was written
self.assertTrue(isfile(f"{tmpdir}/TEST-MIB.py"))
#MIB Compiler
The MIB compiler script 'compiles' .mib files to a custom python representation that pysnmp can load immediately.
In order to compile scripts there must be a valid mib file in the source directory as well as any potential imported files.
You can find out which mib files need to be imported by opening the file and looking at the `IMPORTS` section, where imported mib files are listed.
These mibs may also have subsequent mib files that need to be imported. Alternatively these imports may also be found in the verbose debug log.
This script will also generate pymib files for all the imported mib files.
`--mibs`: A list of mib files that need to be compiled.
`--destination`: The output folder for the compiled mibs. This argument is optional. The default destination folder is `~/output_pymibs`
`--source`: A list of source folders and addresses from where the mib files are sourced. This argument is optional. The default source folder is `~/mibs`
It can be useful to also list a web address as source, as there exist various sites that host mib files.
`--debug`: enable verbose debugging. Useful for figuring out errors.
example usage:
To source the mib TEST-MIB from the default `/mibs` location
`python3 mib_compiler.py --mibs TEST-MIB`
To source the mib TEST-MIB from the default `/mibs` location but to a custom output folder
`python3 mib_compiler.py --mibs TEST-MIB --destination home/user/output`
To source te mib TEST-MIB and all its imports from the path `home/user/mibs` and web address `http://www.net-snmp.org/docs/mibs/`
`python3 mib_compiler.py --mibs TEST-MIB --source home/user/mibs http://www.net-snmp.org/docs/mibs/`
import argparse
import sys
from pysnmp.smi import builder, compiler
from pathlib import Path
from pysmi import debug
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mib_compiler")
def mib_compile(mib_list : list, src, dst):
mibBuilder = builder.MibBuilder()
# set the compiler, the source path and set the www.net-snmp.org site as mib source as well.
compiler.addMibCompiler(mibBuilder, sources=src, destination=dst)
for i in mib_list:
# compile it
try:
mibBuilder.loadModules(i)
logger.debug(f"loaded {i}")
except Exception as e:
raise Exception(f"Something went wrong, try checking whether all the mib fills imported by the provided mib files are present in the source locations ({src}) \r\n (To do this enable debug options and scroll up) ") from e
def main():
abs_path = str(Path().absolute()).replace("\\", "/")
out_path = f"{abs_path}/output_pymibs"
in_path = f"{abs_path}/mibs"
parser = argparse.ArgumentParser(
description='Compiles .mib files in to the easy to load pysnmp format')
parser.add_argument(
'-m', '--mibs', type=str, required=True, nargs='+', help='list of mib names to compile')
parser.add_argument(
'-d', '--destination', type=str, required=False, default=out_path,
help='sets the output directory for the compiled mibs. (default: '
'%(default)s)')
parser.add_argument(
'-s', '--source', type=str, required=False, nargs='+', default=in_path,
help='sets the input paths or addresses to read the .mib files from (default: '
'%(default)s)')
parser.add_argument(
'-v', '--debug', dest='debug', action='store_true', default=False,
help='increase log output')
args = parser.parse_args()
# argparse arguments
mibs = args.mibs
destination = args.destination
source = args.source
debug_option = args.debug
if debug_option:
debug.setLogger(debug.Debug('compiler'))
mib_compile(mib_list=mibs, src=source, dst=destination)
sys.exit(1)
if __name__ == "__main__":
main()
SNMPv2-CONF DEFINITIONS ::= BEGIN
IMPORTS ObjectName, NotificationName, ObjectSyntax
FROM SNMPv2-SMI;
-- definitions for conformance groups
OBJECT-GROUP MACRO ::=
BEGIN
TYPE NOTATION ::=
ObjectsPart
"STATUS" Status
"DESCRIPTION" Text
ReferPart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
ObjectsPart ::=
"OBJECTS" "{" Objects "}"
Objects ::=
Object
| Objects "," Object
Object ::=
value(ObjectName)
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
-- a character string as defined in [2]
Text ::= value(IA5String)
END
-- more definitions for conformance groups
NOTIFICATION-GROUP MACRO ::=
BEGIN
TYPE NOTATION ::=
NotificationsPart
"STATUS" Status
"DESCRIPTION" Text
ReferPart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
NotificationsPart ::=
"NOTIFICATIONS" "{" Notifications "}"
Notifications ::=
Notification
| Notifications "," Notification
Notification ::=
value(NotificationName)
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
-- a character string as defined in [2]
Text ::= value(IA5String)
END
-- definitions for compliance statements
MODULE-COMPLIANCE MACRO ::=
BEGIN
TYPE NOTATION ::=
"STATUS" Status
"DESCRIPTION" Text
ReferPart
ModulePart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
ModulePart ::=
Modules
Modules ::=
Module
| Modules Module
Module ::=
-- name of module --
"MODULE" ModuleName
MandatoryPart
CompliancePart
ModuleName ::=
-- identifier must start with uppercase letter
identifier ModuleIdentifier
-- must not be empty unless contained
-- in MIB Module
| empty
ModuleIdentifier ::=
value(OBJECT IDENTIFIER)
| empty
MandatoryPart ::=
"MANDATORY-GROUPS" "{" Groups "}"
| empty
Groups ::=
Group
| Groups "," Group
Group ::=
value(OBJECT IDENTIFIER)
CompliancePart ::=
Compliances
| empty
Compliances ::=
Compliance
| Compliances Compliance
Compliance ::=
ComplianceGroup
| Object
ComplianceGroup ::=
"GROUP" value(OBJECT IDENTIFIER)
"DESCRIPTION" Text
Object ::=
"OBJECT" value(ObjectName)
SyntaxPart
WriteSyntaxPart
AccessPart
"DESCRIPTION" Text
-- must be a refinement for object's SYNTAX clause
SyntaxPart ::= "SYNTAX" Syntax
| empty
-- must be a refinement for object's SYNTAX clause
WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax
| empty
Syntax ::= -- Must be one of the following:
-- a base type (or its refinement),
-- a textual convention (or its refinement), or
-- a BITS pseudo-type
type
| "BITS" "{" NamedBits "}"
NamedBits ::= NamedBit
| NamedBits "," NamedBit
NamedBit ::= identifier "(" number ")" -- number is nonnegative
AccessPart ::=
"MIN-ACCESS" Access
| empty
Access ::=
"not-accessible"
| "accessible-for-notify"
| "read-only"
| "read-write"
| "read-create"
-- a character string as defined in [2]
Text ::= value(IA5String)
END
-- definitions for capabilities statements
AGENT-CAPABILITIES MACRO ::=
BEGIN
TYPE NOTATION ::=
"PRODUCT-RELEASE" Text
"STATUS" Status
"DESCRIPTION" Text
ReferPart
ModulePart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
Status ::=
"current"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
ModulePart ::=
Modules
| empty
Modules ::=
Module
| Modules Module
Module ::=
-- name of module --
"SUPPORTS" ModuleName
"INCLUDES" "{" Groups "}"
VariationPart
ModuleName ::=
-- identifier must start with uppercase letter
identifier ModuleIdentifier
ModuleIdentifier ::=
value(OBJECT IDENTIFIER)
| empty
Groups ::=
Group
| Groups "," Group
Group ::=
value(OBJECT IDENTIFIER)
VariationPart ::=
Variations
| empty
Variations ::=
Variation
| Variations Variation
Variation ::=
ObjectVariation
| NotificationVariation
NotificationVariation ::=
"VARIATION" value(NotificationName)
AccessPart
"DESCRIPTION" Text
ObjectVariation ::=
"VARIATION" value(ObjectName)
SyntaxPart
WriteSyntaxPart
AccessPart
CreationPart
DefValPart
"DESCRIPTION" Text
-- must be a refinement for object's SYNTAX clause
SyntaxPart ::= "SYNTAX" Syntax
| empty
WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax
| empty
Syntax ::= -- Must be one of the following:
-- a base type (or its refinement),
-- a textual convention (or its refinement), or
-- a BITS pseudo-type
type
| "BITS" "{" NamedBits "}"
NamedBits ::= NamedBit
| NamedBits "," NamedBit
NamedBit ::= identifier "(" number ")" -- number is nonnegative
AccessPart ::=
"ACCESS" Access
| empty
Access ::=
"not-implemented"
-- only "not-implemented" for notifications
| "accessible-for-notify"
| "read-only"
| "read-write"
| "read-create"
-- following is for backward-compatibility only
| "write-only"
CreationPart ::=
"CREATION-REQUIRES" "{" Cells "}"
| empty
Cells ::=
Cell
| Cells "," Cell
Cell ::=
value(ObjectName)
DefValPart ::= "DEFVAL" "{" Defvalue "}"
| empty
Defvalue ::= -- must be valid for the object's syntax
-- in this macro's SYNTAX clause, if present,
-- or if not, in object's OBJECT-TYPE macro
value(ObjectSyntax)
| "{" BitsValue "}"
BitsValue ::= BitNames
| empty
BitNames ::= BitName
| BitNames "," BitName
BitName ::= identifier
-- a character string as defined in [2]
Text ::= value(IA5String)
END
END
SNMPv2-SMI DEFINITIONS ::= BEGIN
-- the path to the root
org OBJECT IDENTIFIER ::= { iso 3 } -- "iso" = 1
dod OBJECT IDENTIFIER ::= { org 6 }
internet OBJECT IDENTIFIER ::= { dod 1 }
directory OBJECT IDENTIFIER ::= { internet 1 }
mgmt OBJECT IDENTIFIER ::= { internet 2 }
mib-2 OBJECT IDENTIFIER ::= { mgmt 1 }
transmission OBJECT IDENTIFIER ::= { mib-2 10 }
experimental OBJECT IDENTIFIER ::= { internet 3 }
private OBJECT IDENTIFIER ::= { internet 4 }
enterprises OBJECT IDENTIFIER ::= { private 1 }
security OBJECT IDENTIFIER ::= { internet 5 }
snmpV2 OBJECT IDENTIFIER ::= { internet 6 }
-- transport domains
snmpDomains OBJECT IDENTIFIER ::= { snmpV2 1 }
-- transport proxies
snmpProxys OBJECT IDENTIFIER ::= { snmpV2 2 }
-- module identities
snmpModules OBJECT IDENTIFIER ::= { snmpV2 3 }
-- Extended UTCTime, to allow dates with four-digit years
-- (Note that this definition of ExtUTCTime is not to be IMPORTed
-- by MIB modules.)
ExtUTCTime ::= OCTET STRING(SIZE(11 | 13))
-- format is YYMMDDHHMMZ or YYYYMMDDHHMMZ
-- where: YY - last two digits of year (only years
-- between 1900-1999)
-- YYYY - last four digits of the year (any year)
-- MM - month (01 through 12)
-- DD - day of month (01 through 31)
-- HH - hours (00 through 23)
-- MM - minutes (00 through 59)
-- Z - denotes GMT (the ASCII character Z)
--
-- For example, "9502192015Z" and "199502192015Z" represent
-- 8:15pm GMT on 19 February 1995. Years after 1999 must use
-- the four digit year format. Years 1900-1999 may use the
-- two or four digit format.
-- definitions for information modules
MODULE-IDENTITY MACRO ::=
BEGIN
TYPE NOTATION ::=
"LAST-UPDATED" value(Update ExtUTCTime)
"ORGANIZATION" Text
"CONTACT-INFO" Text
"DESCRIPTION" Text
RevisionPart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
RevisionPart ::=
Revisions
| empty
Revisions ::=
Revision
| Revisions Revision
Revision ::=
"REVISION" value(Update ExtUTCTime)
"DESCRIPTION" Text
-- a character string as defined in section 3.1.1
Text ::= value(IA5String)
END
OBJECT-IDENTITY MACRO ::=
BEGIN
TYPE NOTATION ::=
"STATUS" Status
"DESCRIPTION" Text
ReferPart
VALUE NOTATION ::=
value(VALUE OBJECT IDENTIFIER)
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
-- a character string as defined in section 3.1.1
Text ::= value(IA5String)
END
-- names of objects
-- (Note that these definitions of ObjectName and NotificationName
-- are not to be IMPORTed by MIB modules.)
ObjectName ::=
OBJECT IDENTIFIER
NotificationName ::=
OBJECT IDENTIFIER
-- syntax of objects
-- the "base types" defined here are:
-- 3 built-in ASN.1 types: INTEGER, OCTET STRING, OBJECT IDENTIFIER
-- 8 application-defined types: Integer32, IpAddress, Counter32,
-- Gauge32, Unsigned32, TimeTicks, Opaque, and Counter64
ObjectSyntax ::=
CHOICE {
simple
SimpleSyntax,
-- note that SEQUENCEs for conceptual tables and
-- rows are not mentioned here...
application-wide
ApplicationSyntax
}
-- built-in ASN.1 types
SimpleSyntax ::=
CHOICE {
-- INTEGERs with a more restrictive range
-- may also be used
integer-value -- includes Integer32
INTEGER (-2147483648..2147483647),
-- OCTET STRINGs with a more restrictive size
-- may also be used
string-value
OCTET STRING (SIZE (0..65535)),
objectID-value
OBJECT IDENTIFIER
}
-- indistinguishable from INTEGER, but never needs more than
-- 32-bits for a two's complement representation
Integer32 ::=
INTEGER (-2147483648..2147483647)
-- application-wide types
ApplicationSyntax ::=
CHOICE {
ipAddress-value
IpAddress,
counter-value
Counter32,
timeticks-value
TimeTicks,
arbitrary-value
Opaque,
big-counter-value
Counter64,
unsigned-integer-value -- includes Gauge32
Unsigned32
}
-- in network-byte order
-- (this is a tagged type for historical reasons)
IpAddress ::=
[APPLICATION 0]
IMPLICIT OCTET STRING (SIZE (4))
-- this wraps
Counter32 ::=
[APPLICATION 1]
IMPLICIT INTEGER (0..4294967295)
-- this doesn't wrap
Gauge32 ::=
[APPLICATION 2]
IMPLICIT INTEGER (0..4294967295)
-- an unsigned 32-bit quantity
-- indistinguishable from Gauge32
Unsigned32 ::=
[APPLICATION 2]
IMPLICIT INTEGER (0..4294967295)
-- hundredths of seconds since an epoch
TimeTicks ::=
[APPLICATION 3]
IMPLICIT INTEGER (0..4294967295)
-- for backward-compatibility only
Opaque ::=
[APPLICATION 4]
IMPLICIT OCTET STRING
-- for counters that wrap in less than one hour with only 32 bits
Counter64 ::=
[APPLICATION 6]
IMPLICIT INTEGER (0..18446744073709551615)
-- definition for objects
OBJECT-TYPE MACRO ::=
BEGIN
TYPE NOTATION ::=
"SYNTAX" Syntax
UnitsPart
"MAX-ACCESS" Access
"STATUS" Status
"DESCRIPTION" Text
ReferPart
IndexPart
DefValPart
VALUE NOTATION ::=
value(VALUE ObjectName)
Syntax ::= -- Must be one of the following:
-- a base type (or its refinement),
-- a textual convention (or its refinement), or
-- a BITS pseudo-type
type
| "BITS" "{" NamedBits "}"
NamedBits ::= NamedBit
| NamedBits "," NamedBit
NamedBit ::= identifier "(" number ")" -- number is nonnegative
UnitsPart ::=
"UNITS" Text
| empty
Access ::=
"not-accessible"
| "accessible-for-notify"
| "read-only"
| "read-write"
| "read-create"
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
IndexPart ::=
"INDEX" "{" IndexTypes "}"
| "AUGMENTS" "{" Entry "}"
| empty
IndexTypes ::=
IndexType
| IndexTypes "," IndexType
IndexType ::=
"IMPLIED" Index
| Index
Index ::=
-- use the SYNTAX value of the
-- correspondent OBJECT-TYPE invocation
value(ObjectName)
Entry ::=
-- use the INDEX value of the
-- correspondent OBJECT-TYPE invocation
value(ObjectName)
DefValPart ::= "DEFVAL" "{" Defvalue "}"
| empty
Defvalue ::= -- must be valid for the type specified in
-- SYNTAX clause of same OBJECT-TYPE macro
value(ObjectSyntax)
| "{" BitsValue "}"
BitsValue ::= BitNames
| empty
BitNames ::= BitName
| BitNames "," BitName
BitName ::= identifier
-- a character string as defined in section 3.1.1
Text ::= value(IA5String)
END
-- definitions for notifications
NOTIFICATION-TYPE MACRO ::=
BEGIN
TYPE NOTATION ::=
ObjectsPart
"STATUS" Status
"DESCRIPTION" Text
ReferPart
VALUE NOTATION ::=
value(VALUE NotificationName)
ObjectsPart ::=
"OBJECTS" "{" Objects "}"
| empty
Objects ::=
Object
| Objects "," Object
Object ::=
value(ObjectName)
Status ::=
"current"
| "deprecated"
| "obsolete"
ReferPart ::=
"REFERENCE" Text
| empty
-- a character string as defined in section 3.1.1
Text ::= value(IA5String)
END
-- definitions of administrative identifiers
zeroDotZero OBJECT-IDENTITY
STATUS current
DESCRIPTION
"A value used for null identifiers."
::= { 0 0 }
END
This diff is collapsed.
TEST-MIB DEFINITIONS ::= BEGIN
--
-- A simple MIB objects for testing
--
IMPORTS
MODULE-IDENTITY, OBJECT-TYPE, Integer32, org FROM SNMPv2-SMI
;
testMib MODULE-IDENTITY
LAST-UPDATED "202004060000Z"
ORGANIZATION "astron"
CONTACT-INFO "astron"
DESCRIPTION "Test mib"
::= { org 2 }
--
-- top level structure
--
TestVal OBJECT IDENTIFIER ::= { testMib 1 }
--
-- Example scalars
--
testValue OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"This is simply a test value."
DEFVAL { 1 }
::= { TestVal 1 }
END
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment