查看源代码 检测函数
每个对象的用户定义的检测函数将托管对象附加到真实资源。此函数在 get
或 set
操作时由代理调用。该函数可以读取一些硬件寄存器,执行计算,或执行实现与概念变量相关的语义所需的任何操作。这些函数必须为标量变量和表编写。它们在关联文件中指定,该文件是一个文本文件。在此文件中,每个托管对象的 OBJECT IDENTIFIER
或符号名称都与 Erlang 元组 {Module,``Function
, ListOfExtraArguments}
相关联。
当在 SNMP 操作中引用托管对象时,将调用关联的 {Module, Function, ListOfExtraArguments}
。该函数应用于一些标准参数(例如,操作类型)和用户提供的额外参数。
检测函数必须为标量变量和表编写 get
和 set
,并且仅为表编写 get-next
。get-bulk
操作将转换为对 get-next
的一系列调用。
检测函数
以下部分描述了如何在 Erlang 中为不同的操作定义检测函数。在以下内容中,RowIndex
是表的键值列表,Column
是列号。
这些函数在 检测函数定义 中详细描述。
新建 / 删除操作
对于标量变量
variable_access(new [, ExtraArg1, ...])
variable_access(delete [, ExtraArg1, ...])
对于表
table_access(new [, ExtraArg1, ...])
table_access(delete [, ExtraArg1, ...])
当 MIB 分别卸载或加载时,将为 MIB 中的每个对象调用这些函数。
Get 操作
对于标量变量
variable_access(get [, ExtraArg1, ...])
对于表
table_access(get,RowIndex,Cols [,ExtraArg1, ...])
Cols
是 Column
的列表。代理将对传入的变量进行排序,以便对同一行(相同索引)的所有操作将同时提供。这样做的原因是数据库通常逐行检索信息。
这些函数必须返回关联变量的当前值。
Set 操作
对于标量变量
variable_access(set, NewValue [, ExtraArg1, ...])
对于表
table_access(set, RowIndex, Cols [, ExtraArg1,..])
Cols
是元组 {Column, NewValue}
的列表。
如果赋值成功,这些函数将返回 noError
,否则返回错误代码。
Is-set-ok 操作
作为 set
操作的补充,可以指定一个测试函数。此函数与上面的 set 操作具有相同的语法,不同之处在于第一个参数是 is_set_ok
而不是 set
。此函数在设置变量之前调用。其目的是确保允许将变量设置为新值。
variable_access(is_set_ok, NewValue [, ExtraArg1, ...])
对于表
table_access(set, RowIndex, Cols [, ExtraArg1,..])
Cols
是元组 {Column, NewValue}
的列表。
撤消操作
使用 is_set_ok
调用的函数将再次被调用,如果没有任何错误,则使用 set
调用,如果发生错误,则使用 undo
调用。这样,可以在 is_set_ok
操作中保留资源,在 undo
操作中释放资源,或者在 set
操作中使其永久化。
variable_access(undo, NewValue [, ExtraArg1, ...])
对于表
table_access(set, RowIndex, Cols [, ExtraArg1,..])
Cols
是元组 {Column, NewValue}
的列表。
GetNext 操作
GetNext 操作只应为表定义,因为代理可以在 MIB 中找到普通变量的下一个实例,并使用 get
操作调用检测。
table_access(get_next, RowIndex, Cols [, ExtraArg1, ...])
Cols
是大于或等于零的整数列表。这表示检测应找到下一个可访问的实例。此函数返回元组 {NextOid, NextValue}
或 endOfTable
。NextOid
应该是表中托管对象的按字典顺序的下一个可访问实例。它应该是一个整数列表,其中第一个整数是列,列表的其余部分是下一行的索引。如果返回 endOfTable
,则代理将继续在其他变量和表中搜索下一个实例。
RowIndex
可以是空列表、未完全指定的行索引或未指定行的索引。
最好用一个示例来描述此操作。
GetNext 示例
一个名为 myTable
的表有五列。前两列是键(不可访问),并且该表有三行。此表的检测函数称为 my_table
。
注意
N/A 表示不可访问。
管理器发出以下 getNext
请求
getNext{ myTable.myTableEntry.3.1.1,
myTable.myTableEntry.5.1.1 }
由于这两个操作都涉及到 1.1 索引,因此将其转换为对 my_table
的一次调用
my_table(get_next, [1, 1], [3, 5])
在此调用中,[1, 1]
是 RowIndex
,其中键 1 的值为 1,键 2 的值为 1,[3, 5]
是请求的列的列表。该函数现在应返回按字典顺序的下一个元素
[{[3, 1, 2], d}, {[5, 1, 2], f}]
这在下表中进行了说明
管理器现在发出以下 getNext
请求
getNext{ myTable.myTableEntry.3.2.1,
myTable.myTableEntry.5.2.1 }
将其转换为对 my_table
的一次调用
my_table(get_next, [2, 1], [3, 5])
该函数现在应返回
[{[4, 1, 1], b}, endOfTable]
这在下表中进行了说明
管理器现在发出以下 getNext
请求
getNext{ myTable.myTableEntry.3.1.2,
myTable.myTableEntry.4.1.2 }
这将转换为对 my_table
的一次调用
my_table(get_next, [1, 2], [3, 4])
该函数现在应返回
[{[3, 2, 1], g}, {[5, 1, 1], c}]
这在下表中进行了说明
管理器现在发出以下 getNext
请求
getNext{ myTable.myTableEntry,
myTable.myTableEntry.1.3.2 }
这将转换为对 my_table
的两次调用
my_table(get_next, [], [0]) and
my_table(get_next, [3, 2], [1])
该函数现在应返回
[{[3, 1, 1], a}] and
[{[3, 1, 1], a}]
在这两种情况下,都应返回表中第一个可访问的元素。由于键列不可访问,这意味着第三列是第一行。
注意
通常,上述函数的行为与所示的完全相同,但它们可以自由执行其他操作。例如,get 请求可能会有副作用,例如设置其他变量,也许是全局
lastAccessed
变量。
使用 ExtraArgument
ListOfExtraArguments
可用于编写通用函数。此列表附加到每个函数的标准参数。考虑一个设备的两个只读变量,ipAdr
和 name
,其对象标识符分别为 1.1.23.4 和 1.1.7。要访问这些变量,可以实现两个 Erlang 函数 ip_access
和 name_access
,它们将位于 MIB 中。可以在文本文件中按如下方式指定这些函数
{ipAdr, {my_module, ip_access, []}}.
% Or using the oid syntax for 'name'
{[1,1,7], {my_module, name_access, []}}.
ExtraArgument
参数是空列表。例如,当代理接收到 ipAdr
变量的 get 请求时,将对 ip_access(get)
进行调用。此函数返回的值是对 get 请求的答案。
如果 ip_access
和 name_access
的实现方式类似,我们可以使用 ListOfExtraArguments
编写 generic_access
函数
{ipAdr, {my_module, generic_access, ['IPADR']}}.
% The mnemonic 'name' is more convenient than 1.1.7
{name, {my_module, generic_access, ['NAME']}}.
当代理接收到与上述相同的 get 请求时,将对 generic_access(get,
'IPADR')
进行调用。
另一种更接近硬件的可能性是
{ipAdr, {my_module, generic_access, [16#2543]}}.
{name, {my_module, generic_access, [16#A2B3]}}.
默认检测
完成 MIB 定义工作后,还剩下两个主要问题。
- 实现 MIB
- 实现管理器应用程序。
实现 MIB 可能是一项繁琐的任务。最有可能的是,在实现所有表和变量之前,需要测试代理。在这种情况下,默认检测函数很有用。该工具包可以为变量以及表生成默认检测函数。因此,无需任何编程即可生成一个正在运行的原型代理,该代理可以处理 set
、get
、get-next
和表操作。
代理将值存储在基于标准模块 ets
的内部易失数据库中。但是,也可以让 MIB 编译器生成使用内部持久数据库或 Mnesia DBMS 的函数。有关详细信息,请参阅 Mnesia 用户指南和参考手册的 SNMP 部分,模块 snmp_generic
。
实现 MIB 的部分内容后,可以重新编译它,并通过使用默认函数继续操作。通过这种方法,可以增量开发 SNMP 代理。
默认检测允许在开发和测试代理的同时,开发和测试管理器端的应用程序。完成 ASN.1 文件后,让 MIB 编译器生成默认实现,并以此开发管理应用程序。
表操作
为表生成默认函数适用于使用 SNMPv2 中定义的 RowStatus
文本约定的表,该约定在 STANDARD-MIB 和 SNMPv2-TC 中定义。
注意
我们强烈建议对每个可以从管理器修改的表使用
RowStatus
约定,即使对于新设计的 SNMPv1 MIB 也是如此。在 SNMPv1 中,每个人都发明了自己的方案来模拟表操作,这导致了许多不一致之处。SNMPv2 中的约定灵活而强大,并且已经过成功测试。如果表是只读的,则不应使用 RowStatus 列。
原子设置
在 SNMP 中,set
操作是原子的。要么更改 set
操作中指定的所有变量,要么都不更改。因此,set
操作分为两个阶段。在第一阶段,根据 MIB 中变量的定义检查每个变量的新值。检查以下定义
- 类型
- 长度
- 范围
- 该变量是可写的并且在 MIB 视图中。
在第一阶段结束时,将为每个标量变量和每组表操作调用用户定义的 is_set_ok
函数。
如果没有发生错误,则执行第二阶段。此阶段将为所有变量调用用户定义的 set
函数。
如果在 is_set_ok
阶段或 set
阶段发生错误,则所有使用 is_set_ok
但未使用 set
调用的函数都将使用 undo
调用。
此事务机制存在限制。如果变量之间存在复杂的依赖关系,例如 month
和 day
之间,则需要另一种机制。通过一种更通用的事务机制可以避免将日期设置为“2 月 31 日”。您可以继续查找更复杂的情况并构建 N 阶段设置机制。此工具包仅包含一个简单的机制。
事务机制最常见的应用是将行操作保持在一起。由于我们的代理对行操作进行排序,因此与 RowStatus(特别是 'createAndWait' 值)结合实现的机制可以优雅地解决大多数问题。