查看源代码 检测函数

每个对象的用户定义的检测函数将托管对象附加到真实资源。此函数在 getset 操作时由代理调用。该函数可以读取一些硬件寄存器,执行计算,或执行实现与概念变量相关的语义所需的任何操作。这些函数必须为标量变量和表编写。它们在关联文件中指定,该文件是一个文本文件。在此文件中,每个托管对象的 OBJECT IDENTIFIER 或符号名称都与 Erlang 元组 {Module,``Function, ListOfExtraArguments} 相关联。

当在 SNMP 操作中引用托管对象时,将调用关联的 {Module, Function, ListOfExtraArguments}。该函数应用于一些标准参数(例如,操作类型)和用户提供的额外参数。

检测函数必须为标量变量和表编写 getset,并且仅为表编写 get-nextget-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, ...])

ColsColumn 的列表。代理将对传入的变量进行排序,以便对同一行(相同索引)的所有操作将同时提供。这样做的原因是数据库通常逐行检索信息。

这些函数必须返回关联变量的当前值。

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}endOfTableNextOid 应该是表中托管对象的按字典顺序的下一个可访问实例。它应该是一个整数列表,其中第一个整数是列,列表的其余部分是下一行的索引。如果返回 endOfTable,则代理将继续在其他变量和表中搜索下一个实例。

RowIndex 可以是空列表、未完全指定的行索引或未指定行的索引。

最好用一个示例来描述此操作。

GetNext 示例

一个名为 myTable 的表有五列。前两列是键(不可访问),并且该表有三行。此表的检测函数称为 my_table

Contents of 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 from [3,1,1] and [5,1,1].

管理器现在发出以下 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 from [3,2,1] and [5,2,1].

管理器现在发出以下 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 from [3,1,2] and [4,1,2].

管理器现在发出以下 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 可用于编写通用函数。此列表附加到每个函数的标准参数。考虑一个设备的两个只读变量,ipAdrname,其对象标识符分别为 1.1.23.4 和 1.1.7。要访问这些变量,可以实现两个 Erlang 函数 ip_accessname_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_accessname_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 可能是一项繁琐的任务。最有可能的是,在实现所有表和变量之前,需要测试代理。在这种情况下,默认检测函数很有用。该工具包可以为变量以及表生成默认检测函数。因此,无需任何编程即可生成一个正在运行的原型代理,该代理可以处理 setgetget-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 调用。

此事务机制存在限制。如果变量之间存在复杂的依赖关系,例如 monthday 之间,则需要另一种机制。通过一种更通用的事务机制可以避免将日期设置为“2 月 31 日”。您可以继续查找更复杂的情况并构建 N 阶段设置机制。此工具包仅包含一个简单的机制。

事务机制最常见的应用是将行操作保持在一起。由于我们的代理对行操作进行排序,因此与 RowStatus(特别是 'createAndWait' 值)结合实现的机制可以优雅地解决大多数问题。