RFC 4011
Policy Based Management MIB
RFC 4011: Policy Based Management MIB ๐
okay bestie, this one is the Policy Based Management MIB ๐ It's an Internet standards track spec from March 2005, so it's officially on the proper standards path ๐ฅ It's from the Network Working Group and it's by S. Waldbusser (Nextbeacon), J. Saperia (JDS Consulting, Inc.), and T. Hongal (Riverstone Networks, Inc.) ๐ซถ The document is the Internet Society's, copyright 2005 โจ
Status of This Memo โจ
this doc is an Internet standards track protocol spec for the Internet community, and it's basically asking for discussion and suggestions to make it better ๐ฆ if you want the official standardization state of this protocol, go check the current "Internet Official Protocol Standards" (STD 1) ๐ and yes, distribution of this memo is unlimited ๐
Copyright Notice ๐
Copyright (C) The Internet Society (2005).
Abstract ๐ธ
this memo defines a piece of the Management Information Base (MIB) for network management protocols in TCP/IP-based internets ๐ง specifically, it has objects that let you do policy-based monitoring and management of Simple Network Management Protocol (SNMP) infrastructures, plus a scripting language and a script execution environment ๐ it's giving full toolkit energy ๐ญ
Table of Contents ๐
1. The Internet-Standard Management Framework .................. 3
2. Overview .................................................... 4
3. Policy-Based Management Architecture ........................ 4
4. Policy-Based Management Execution Environment ............... 10
4.1. Terminology ........................................... 10
4.2. Execution Environment - Elements of Procedure ......... 10
4.3. Element Discovery ..................................... 11
4.3.1. Implementation Notes .......................... 12
4.4. Element Filtering ..................................... 13
4.4.1. Implementation Notes .......................... 13
4.5. Policy Enforcement .................................... 13
4.5.1. Implementation Notes .......................... 14
5. The PolicyScript Language ................................... 14
5.1. Formal Definition ..................................... 15
5.2. Variables ............................................. 18
5.2.1. The Var Class ................................. 19
5.3. PolicyScript QuickStart Guide ......................... 23
5.3.1. Quickstart for C Programmers .................. 25
5.3.2. Quickstart for Perl Programmers ............... 25
5.3.3. Quickstart for TCL Programmers ................ 25
5.3.4. Quickstart for Python Programmers ............. 26
5.3.5. Quickstart for JavaScript/ECMAScript/JScript
Programmers ................................... 26
5.4. PolicyScript Script Return Values ..................... 26
6. Index Information for `this element' ........................ 27
7. Library Functions ........................................... 28
8. Base Function Library ....................................... 29
8.1. SNMP Library Functions ................................ 29
8.1.1. SNMP Operations on Non-Local Systems .......... 30
8.1.2. Form of SNMP Values ........................... 32
8.1.3. Convenience SNMP Functions .................... 34
8.1.3.1. getVar() ............................ 34
8.1.3.2. exists() ............................ 34
8.1.3.3. setVar() ............................ 35
8.1.3.4. searchColumn() ...................... 36
8.1.3.5. setRowStatus() ...................... 38
8.1.3.6. createRow() ......................... 39
8.1.3.7. counterRate() ....................... 42
8.1.4. General SNMP Functions ........................ 44
8.1.4.1. newPDU() ............................ 45
8.1.4.2. writeVar() .......................... 45
8.1.4.3. readVar() ........................... 46
8.1.4.4. snmpSend() .......................... 47
8.1.4.5. readError() ......................... 48
8.1.4.6. writeBulkParameters() ............... 48
8.1.5. Constants for SNMP Library Functions .......... 49
8.2. Policy Library Functions .............................. 51
8.2.1. elementName() ................................. 51
8.2.2. elementAddress() .............................. 51
8.2.3. elementContext() .............................. 52
8.2.4. ec() .......................................... 52
8.2.5. ev() .......................................... 52
8.2.6. roleMatch() ................................... 52
8.2.7. Scratchpad Functions .......................... 53
8.2.8. setScratchpad() ............................... 55
8.2.9. getScratchpad() ............................... 56
8.2.10. signalError() ................................. 57
8.2.11. defer() ....................................... 57
8.2.12. fail() ........................................ 58
8.2.13. getParameters() ............................... 58
8.3. Utility Library Functions ............................. 59
8.3.1. regexp() ...................................... 59
8.3.2. regexpReplace() ............................... 60
8.3.3. oidlen() ...................................... 60
8.3.4. oidncmp() ..................................... 60
8.3.5. inSubtree() ................................... 60
8.3.6. subid() ....................................... 61
8.3.7. subidWrite() .................................. 61
8.3.8. oidSplice() ................................... 61
8.3.9. parseIndex() .................................. 62
8.3.10. stringToDotted() .............................. 63
8.3.11. integer() ..................................... 64
8.3.12. string() ...................................... 64
8.3.13. type() ........................................ 64
8.3.14. chr() ......................................... 64
8.3.15. ord() ......................................... 64
8.3.16. substr() ...................................... 65
8.4. General Functions ..................................... 65
9. International String Library ................................ 65
9.1. stringprep() .......................................... 66
9.1.1. Stringprep Profile ............................ 66
9.2. utf8Strlen() .......................................... 67
9.3. utf8Chr() ............................................. 68
9.4. utf8Ord() ............................................. 68
9.5. utf8Substr() .......................................... 68
10. Schedule Table .............................................. 69
11. Definitions ................................................. 70
12. Relationship to Other MIB Modules ........................... 113
13. Security Considerations ..................................... 114
14. IANA Considerations ......................................... 117
15. Acknowledgements ............................................ 118
16. References .................................................. 118
16.1. Normative References .................................. 118
16.2. Informative References ................................ 119
Authors' Addresses .............................................. 120
Full Copyright Statement ........................................ 121
1. The Internet-Standard Management Framework ๐ฆ
if you want the full scoop on the documents that describe the current Internet-Standard Management Framework, check out section 7 of RFC 3410 [16] ๐
managed objects get accessed through a virtual information store called the Management Information Base, or MIB ๐ง you usually reach MIB objects through the Simple Network Management Protocol (SNMP) ๐ the objects themselves are defined using the mechanisms in the Structure of Management Information (SMI) โจ this memo specifies a MIB module that's compliant with SMIv2, which is described in STD 58, RFC 2578 [2], STD 58, RFC 2579 [3], and STD 58, RFC 2580 [4] ๐
2. Overview ๐
okay so big IT orgs have come up with management strategies to handle how massive and complicated today's networks are ๐ญ instead of managing device by device (where you can make orders of magnitude more decisions, and more mistakes, lol ๐), they try to configure the whole network by describing and implementing high-level business policies ๐
here are some examples of "business policies" ๐
- All routers will run code version 6.2.
- On-site contractors will only be connected to ports that are configured with special security restrictions.
- All voice over cable ports in California must provide free local calling.
- Apply special forwarding to all ports whose customers have paid for premium service.
each one of these could be an action applied to hundreds of thousands of variables ๐คฏ
to automate all this, customers need software tools that carry out business policies across their networks, plus standard protocols that make sure policies can be applied to all their devices, no matter the vendor โจ this practice is called Policy-Based Management, and this document defines the managed objects for SNMP that get used to distribute policies in a common form throughout the network ๐
3. Policy-Based Management Architecture โจ
okay so policy-based management is basically applying management operations globally across ALL the managed elements that share certain attributes ๐ it's giving "set it once, it hits everything that matches" energy ๐ซถ
Policies are meant to express this vibe:
if (an element has certain characteristics) then (apply an
operation to that element)
and they take this normal form:
if (policyCondition) then (policyAction)
A policyCondition is a script that results in a boolean ๐ฅ it decides whether an element is a member of the set of elements that an action should be performed on. A policyAction is an operation performed on an element or a set of elements.
These policies are most often run on or near the managed devices, where the elements live ๐ that way their characteristics can be inspected easily, and the operations on those elements get performed right there. The management station is responsible for distributing an organization's policies to all the managed devices in the infrastructure ๐ฌ and the pmPolicyTable gives you managed objects for representing a policy on a managed device ๐
An element is an instance of a physical or logical entity, and it's embodied by a group of related MIB variables (like all the variables for interface 7 ๐). This makes policies way more efficient and concise, and elements can also model circuits, CPUs, queues, processes, systems, etc. ๐
Conceptually, policies run like this:
for each element for which policyCondition returns true, execute
policyAction on that element
For example:
If (interface is fast ethernet) then (apply full-duplex mode)
If (interface is access) then (apply security filters)
If (circuit w/gold service paid for) then (apply special queuing)
Each unique combo of policy and element is called an execution context โจ inside one, the phrase 'this element' is used to refer to the associated element, since most policy operations get applied to 'this element'. The address of 'this element' holds the object identifier of any attribute of the element, the SNMP context the element was discovered in, and the address of the system it was discovered on ๐ฆ
Policies can manage elements on the same system:
-----------------------------------------------------
| |
| Managed System |
| |
| |
| ------------------ Managed Elements |
| | | interfaces |
| | Policy Manager | manages... circuits |
| | | queues |
| ------------------ processes |
| ... |
| |
-----------------------------------------------------
or they can manage elements on other systems:
--------------------------
| Managed System |
-------------------------- | Managed Elements |
| | | interfaces |
| Management Station or | | circuits |
| Mid-Level Manager | | ... |
| | --------------------------
| ------------------ | manages...
| | Policy Manager | | --------------------------
| ------------------ | | Managed System |
| | | Managed Elements |
-------------------------- | interfaces |
| circuits |
| ... |
--------------------------
...
PolicyConditions can do comparison operations on SNMP variables, logical expressions, and other functions ๐ง a bunch of device characteristics are already defined in MIB Modules and are easy to drop into policyCondition expressions, like ifType == ethernet or frCircuitCommittedBurst < 128K ๐ BUT (and this is the tea โ) there are important characteristics that aren't in MIB objects yet, and it's not current practice to store that info on managed devices. So this document defines MIB objects for that info. To meet today's needs, there are three missing areas: roles, capabilities, and time ๐
Roles ๐
A role is an administratively specified characteristic of a managed element. As a selector for policies, it decides whether a policy applies to a particular managed element ๐
Roles are things like political, financial, legal, geographical, or architectural characteristics, which usually can't be directly derived from the info stored on the managed system. So "paid for premium service" or "is plugged into a UPS" are roles, but "percent utilization of a link" is not, because you can just read that one ๐
Here are the kinds of info you'd put into a role:
- political - describes the role of a person or group of people, or of a service that a group of people uses. Examples: executive, sales, outside-contractor, customer.
If (attached user is executive) then (apply higher bandwidth)
If (attached user is outside-contractor) then (restrict access)
- financial/legal - describes what financial consideration was received. Could also include contractual or legal considerations. Examples: paid, gold, free, trial, demo, lifeline.
If (gold service paid for) then (apply special queuing)
- geographical - describes the location of an element. Examples: California, Headquarters, insecure conduit.
If (interface leaves the building) then (apply special security)
- architectural - describes the network architects "intent" for an element. Examples: backup, trunk.
If (interface is backup) then (set ifAdminStatus = down)
Roles in this model are human-defined strings that policy code can reference ๐ง the role table in this MIB may be used to assign role strings to elements and to view all role string assignments. Implementation-specific mechanisms may also be used to assign role strings, however, those assignments must be visible in the role table ๐ Multiple roles can be assigned to each element.
Here's the tea: policy code can already read MIB objects that show the current state of the system, while role strings are more static. So it is recommended that role strings not duplicate information available in MIB objects ๐ โโ๏ธ role strings generally should be used to describe info that isn't accessible in MIB objects.
Policy scripts may inspect role assignments to make decisions based on whether an element has a particular role assigned to it ๐ฆ
The pmRoleTable lets a management station learn what roles exist on a managed system. The management station may choose not to install policies that depend on a role that doesn't exist on any elements in the system. It can also register for notifications of new roles, and when it receives a pmNewRoleNotification, it may choose to install new policies that make use of that new role ๐
Capabilities ๐ธ
The capabilities table lets a management station learn what capabilities exist on a managed system. The management station may choose not to install policies that depend on a capability that doesn't exist on any elements in the system. It can then register for notifications of new capabilities ๐ซถ and upon receipt of a pmNewCapabilityNotification, it may choose to install new policies that make use of that new capability โจ
Time โฐ
Managers may wish to define policies that are meant to apply for certain periods of time ๐ฐ๏ธ this might mean a policy is installed and dormant for a while, becomes ready, and then later goes dormant again. Sometimes these time periods are regular (Monday-Friday 9-5), and sometimes they're ad hoc. This MIB provides a schedule table that can schedule when a policy is ready and when it's dormant ๐
A policy manager contains the following pieces ๐งฉ
-------------------------------------------------------
| Policy Manager |
| |
| ---------------------------------------- |
| | Agent | |
| | | |
| | --------------------------------- | |
| | | Policy Download and Control | | |
| | | pmPolicyTable | | |
| | | pmElementTypeRegTable | | |
| | | pmSchedTable | | |
| | --------------------------------- | |
| | | |
| | --------------------------------- | |
| | | Policy Environment Control | | |
| | | pmRoleTable | | |
| | | pmCapabilitiesTables | | |
| | --------------------------------- | |
| | | |
| | --------------------------------- | |
| | | Policy Monitoring | | |
| | | pmTrackingTables | | |
| | | pmDebuggingTable | | |
| | --------------------------------- | |
| ---------------------------------------- |
| |
| -------------------------------- |
| | Execution Environment | |
| | | |
| | ----------------------- | |
| | | Policy Scheduler | | |
| | ----------------------- | |
| | ----------------------- | |
| | | Language | | |
| | ----------------------- | |
| | ----------------------- | |
| | | Function Library | | |
| | ----------------------- | |
| -------------------------------- |
-------------------------------------------------------
4. Policy-Based Management Execution Environment ๐
4.1. Terminology ๐
Active Schedule ๐๏ธ - okay so a schedule lists the times it's considered active, and it's active during exactly those times. Simple, iconic. ๐
Valid Policy โ - a valid policy is one that's fully configured and enabled to run. It can run, unless it's linked to a schedule entry that says it's not active right now. Not the vibe, but it's the rule. ๐
Ready Policy ๐ - a ready policy is a valid policy that either has no schedule at all, or is linked to a schedule that's active at this moment. Ready = good to go, bestie. โจ
Precedence Group ๐ - you can put multiple policies into one precedence group. The result is that for each element, out of the ready policies that match the condition, only the one with the highest precedence value gets to be active. Main character energy, one policy wins. ๐ For example, say there's a default bronze policy that applies to any interface, and a special policy for gold interfaces. The higher precedence of the gold policy means it runs on the gold ports, and the bronze policy doesn't. ๐ฅ
Active Execution Context ๐ฏ - this is a pairing of a ready policy with an element that matches both the element type filter and the policy condition. If there are multiple policies in the precedence group, no higher precedence policy in that group can match the policy condition too. It's a whole lineup, and only the top match gets the spotlight. ๐ธ
Run-Time Exception (RTE) ๐ - a run-time exception is a fatal error caused in language or function processing. If one happens while a script is being invoked, that script is immediately terminated, no cap. If a policyCondition hits a run-time exception while processing an element, that element is not matched by the condition, and the associated action won't be run on it. An RTE can also add an entry to the pmDebuggingTable, and it shows up in the pmTrackingPEInfo object. ๐ญ
4.2. Execution Environment - Elements of Procedure ๐ง
Okay, so there are three steps the environment goes through, in order, to execute policies:
- Element Discovery
- Element Filtering
- Policy Enforcement
4.3. Element Discovery ๐
An element is an instance of a physical or logical entity. Examples include interfaces, circuits, queues, CPUs, and processes. Sometimes different attributes of one entity get described through tables in several standard and proprietary MIB Modules. As long as the indexing is consistent across those tables, the entity can be modeled as one element. For example, the ifTable and the dot3Stats table both contain attributes of interfaces and share the same index (ifIndex), so they can be modeled as one element type. It's giving teamwork. ๐ซถ
The Element Type Registration table lets the manager learn which element types the system is managing, and register new types if needed. An element type gets registered by providing the OID of an SNMP object (without the instance). Each SNMP instance that exists under that object is a distinct element. The index part of the discovered OID gets supplied to policy conditions and actions, so that code can inspect and configure the element. The agent can figure out the index portion of discovered OIDs from the length of the pmElementTypeRegOIDPrefix, for the part of the MIB being retrieved. For example, if the OIDPrefix is 'ifEntry', which has 9 subids, the index starts at the 11th subid (skipping the subidentifier for the column; e.g., ifSpeed). Math that's actually serving. ๐งฎ
For each element that gets discovered, the policy condition is called with the element's name as an argument, to see whether the element is a member of the set the policy acts upon. ๐
Note that agents may automatically configure entries in this table for frequently used element types (interfaces, circuits, etc.). In particular, they may configure elements for which discovery is optimized in one or both of these ways:
- The agent may discover elements by scanning internal data structures, as opposed to issuing local SNMP requests. It is possible to recreate the exact semantics described in this table even if local SNMP requests are not issued.
- The agent may receive asynchronous notification of new elements (for example, "card inserted") and use that information to create elements instantly, rather than through polling. A similar feature might be available for the deletion of elements.
Note that upon restart, the disposition of agent-installed entries is described by the pmPolicyStorageType object. ๐
A special element type "0.0" represents the "system element". "0.0" represents the single instance of the system itself, and provides an execution context for policies to operate on "the system" and on MIB objects modeled as scalars. For example, "0.0" gives an execution context for policy-based selection of the operating system code version (likely modeled as a scalar MIB object). The element type "0.0" always exists. As a consequence, no actual discovery takes place, and the pmElementTypeRegMaxLatency object has no effect for the "0.0" element type. However, if the "0.0" element type is not registered in the table, policies will not be executed on the "0.0" element. The system element is the main character who's always there. ๐
If the agent is discovering elements by polling, it should check for new elements no less frequently than pmElementTypeRegMaxLatency would dictate. When an element is first discovered, all policyConditions are run immediately, and any policyConditions that match will have the associated policyAction run immediately. After that, the policyCondition gets run regularly for the element, with no more than pmPolicyConditionMaxLatency milliseconds elapsing between each invocation. Note that if an implementation can be alerted immediately when a particular type of element is created, it is urged to discover that type of element this way rather than through polling, so the discovered element gets configured instantly. โก
4.3.1. Implementation Notes ๐ ๏ธ
Although the external behavior of this registration process is defined in terms of walking MIB tables, implementation strategies may differ. For example, commonly used element types (such as interface) may have purpose-built element discovery capability built in, and advertised to managers through an entry in the pmElementTypeRegTable. ๐ฆ
Before registering an element type, a manager is responsible for inspecting the table to see whether it's already registered, either by the agent or by another manager. Note that entries that differ only in the last subid (which specifies which object is an entry) are effectively duplicates, and should be treated as such by the manager. Double entries are not it. ๐ โโ๏ธ
The system that implements the Policy-Based Management MIB may not know the format of object identifiers in other MIB Modules. So it's inappropriate for it to check these OIDs for errors. It's the management station's job to register well-formed object identifiers. For example, if an extra sub-identifier is supplied when the ifTable is registered, no elements will be discovered. Similarly, if a sub-identifier is missing, every element will be discovered numerous times (once per column), and none of the element addresses will be well formed. Read your OIDs carefully, bestie. ๐
4.4. Element Filtering ๐งน
The first step in executing a policy is to see whether the policy is ready to run based on its schedule. If the pmPolicySchedule object is equal to zero, there's no schedule defined, and the policy is always ready. If the pmPolicySchedule object is non-zero, then the policy is ready only if the referenced schedule group contains at least one valid schedule entry that's active at the current time. ๐๏ธ
If the policy is ready, the next step is to see which elements match the policy condition. The policy condition is called once for each element, and runs to completion. The element's name is the only argument passed to the condition code for each invocation. No state is remembered within the policy script from the previous invocation of 'this element', or from the previous invocation of the policy condition, except for state accessible through library functions. Two notable examples are the scratchpad functions, which explicitly provide for storing state, and the SNMP functions, which can store state in local or remote MIB objects. If any run-time exception occurs, the condition terminates immediately for 'this element'. If the condition returns non-zero, the corresponding policy action gets executed for 'this element'. ๐ฏ
If an element matches a condition and it hadn't matched that condition the last time it was checked (or if it's a newly discovered element), the associated policyAction gets executed immediately. If the element matched the condition at the last check, it stays in the set of elements whose policyAction will be run within the policyActionMaxLatency. ๐
4.4.1. Implementation Notes ๐
Whether policy conditions are multi-tasked is an implementation-dependent matter. Each condition/element combination is conceptually its own process, and can be scheduled sequentially, or two or more could run simultaneously. Choose your fighter, the implementation decides. ๐คทโโ๏ธ
4.5. Policy Enforcement ๐จ
For each element that has returned non-zero from the policy condition, the corresponding policy action is called. The element's name is the only argument passed to the policy action for each invocation. Except for state accessible from library functions, no state is remembered from the policy condition evaluation, from the previous condition/action invocation of 'this element', or from the previous invocation of the policy condition or action on any other element. If any run-time exception occurs, the action terminates immediately for 'this element'. ๐
4.5.1. Implementation Notes ๐
How policy actions are multi-tasked is an implementation-dependent matter. Each condition/element combination is conceptually its own process, and can be scheduled sequentially, or two or more could be run simultaneously. Same energy as the conditions, different spotlight. โจ
5. The PolicyScript Language โจ
okay so, policy conditions and policy actions are written in the PolicyScript language ๐ it's meant to be a small interpreted language that's simple to understand and simple to implement, because it's built for tiny scripts that make up conditions and actions. it's giving lightweight and focused ๐ฆ
PolicyScript is meant to feel familiar to programmers who already know a common language like Perl or C ๐ธ nominally, policyScript is a subset of C. but the authors wanted C++'s operator overloading (mostly just to make the docs easier to write) ๐ so PolicyScript is formally defined as a subset of C++, where a lot of the operators are overloaded as part of the "var" class. heads up though: a PolicyScript program can't overload operators itself, because the syntax for doing that isn't part of PolicyScript. the subset exists so that low-cost interpreters are easy to build, and so the C/C++ quirks get left behind ๐ง the goal is that both C and Perl people will recognise what's allowed.
some C/C++ features are NOT available, like function definitions, pointer variables, structures, enums, typedefs, floating point, and preprocessor functions (except for comments). no cap, it's a streamlined vibe โจ
this language is formally defined as a subset of ISO C++ [10], but it only allows constructs that can be expressed in the Extended Backus-Naur Form (EBNF) written down here. EBNF doesn't fully pin down syntax (it allows some things that are actually invalid) and it doesn't cover semantics, but it's enough to define the subset you need for conformance ๐ unless it's explicitly described here, the meaning of any EBNF construct is found by looking at the ISO C++ standard.
comments and newlines are allowed and actually encouraged, since they make PolicyScript code easier to read ๐ซถ comments start with '/' and end with '/', or start with '//' and run to the end of the line.
one thing EBNF can't express: all variables inside one instance of a PolicyScript script share the same scope. so variables defined in a block between '{' and '}' are NOT in their own separate scope from the enclosing block. wild, right? ๐ญ
PolicyScript code must be written in the ASCII character set ๐
in the EBNF used here, terminals are character set members (on their own or in a sequence) wrapped in single quotes, or described as a phrase between '<' and '>'. nonterminals are sequences of letters and underscores. a colon (:) after a nonterminal introduces its definition (its production). inside a production, '|' separates alternatives, and '(' and ')' group things together. '[' and ']' mean the enclosed items are optional, and so does a '?' after an item. a '*' after an item means zero or more repeats, and a '+' means one or more. and '--' starts a comment that runs to the end of the line ๐
5.1. Formal Definition ๐
The PolicyScript language follows the syntax and semantics of ISO C++ [10], but it's limited to what can be expressed in the EBNF below.
the following keywords are reserved words and can't be used in any policy script ๐ this stops someone from using a keyword from another language as an identifier and accidentally changing what a script means. the reserved words are:
auto, case, char, const, default, do, double, enum, extern, float,
goto, inline, int, long, register, short, signed, sizeof, static,
struct, switch, typedef, union, unsigned, void, and volatile.
any syntax error, use of a reserved keyword, reference to an unknown identifier, wrong number of function arguments, error in coercing an argument to the proper type, going over the local limit on string length, or going over the local limit on total storage used by local variables will cause an RTE ๐
PolicyScript allows comments using the comment delimiters '/' to '/', or the start-of-comment symbol '//' โจ
-- Lexical Grammar
letter: '_' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
| 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'
| 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't'
| 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
| 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
| 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T'
| 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'
digit: '0' | '1' | '2' | '3' | '4'
| '5' | '6' | '7' | '8' | '9'
non_zero: '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
oct_digit: '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7'
hex_digit: digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
escape_seq: '\'' | '\"' | '\?' | '\\'
| '\a' | '\b' | '\f' | '\n'
| '\r' | '\t' | '\v'
| '\' oct_digit+ | '\x' hex_digit+
non_quote: Any character in the ASCII character set
except single quote ('), double quote ("),
backslash ('\'), or newline.
c_char: non_quote | '"' | escape_seq
string_literal: '"' s_char* '"'
s_char: non_quote | ''' | escape_seq
char_constant: ''' c_char '''
decimal_constant: non_zero digit*
octal_constant: '0' oct_digit*
hex_constant: ( '0x' | '0X' ) hex_digit+
integer_constant: decimal_constant | octal_constant | hex_constant
identifier: letter ( letter | digit )*
-- Phrase Structure Grammar
-- Expressions
primary_expr: identifier | integer_constant | char_constant
| string_literal | '(' expression ')'
postfix_expr: primary_expr
| identifier '(' argument_expression_list? ')'
| postfix_expr '++'
| postfix_expr '--'
| postfix_expr '[' expression ']'
argument_expression_list:
assignment_expr
| argument_expression_list ',' assignment_expr
unary_expr: postfix_expr | unary_op unary_expr
unary_op: '+' | '-' | '~' | '!' | '++' | '--'
binary_expr: unary_expr | binary_expr binary_op unary_expr
binary_op: '||' | '&&' | '|' | '^' | '&' | '!='
| '==' | '>=' | '<=' | '>' | '<' | '>>'
| '<<' | '-' | '+' | '%' | '/' | '*'
assignment_expr: binary_expr
| unary_expr assignment_op assignment_expr
assignment_op: '=' | '*=' | '/=' | '%=' | '+=' | '-='
| '<<=' | '>>=' | '&=' | '^=' | '|='
expression: assignment_expr | expression ',' assignment_expr
-- Declarations
declaration: 'var' declarator_list ';'
declarator_list: init_declarator
| declarator_list ',' init_declarator
init_declarator: identifier [ '=' assignment_expr ]
-- Statements
statement: declaration
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
compound_statement: '{' statement* '}'
expression_statement: expression? ';'
selection_statement:
'if' '(' expression ')' statement
| 'if' '(' expression ')' statement 'else' statement
iteration_statement:
'while' '(' expression ')' statement
| 'for' '(' expression? ';' expression? ';' expression? ')'
statement
jump_statement: 'continue' ';'
| 'break' ';'
| 'return' expression? ';'
-- Root production
PolicyScript: statement*
5.2. Variables ๐
to keep scripts short and easy to write, PolicyScript gives you a loosely typed data class called "var" ๐ฆ it can hold both integer and string values. so the native C++ types (char, int, and so on) aren't needed, and they didn't make it into this subset. the semantics of the "var" type are modeled on ECMAScript [17] ๐
for example:
var number = 0, name = "IETF";
this language runs in an environment where the following typedef is declared. (note that this typedef won't be visible in the policyCondition or policyAction code ๐)
typedef ... var;
even though this is written here as a typedef, the 'typedef' keyword itself is NOT available to use in PolicyScript code ๐ โโ๏ธ
5.2.1. The Var Class ๐
okay so bestie, a value is an entity that is always one of two types: string or integer ๐
The String type is every finite ordered sequence of zero or more 8-bit unsigned integer values (we call each one an "element") ๐ธ Strings can hold text AND binary data, so they're super versatile. Each element sits at a position in the sequence, and positions are numbered with nonnegative integers. The first element (if there is one) is at position 0, the next at 1, and so on. The length of a string is how many elements (8-bit values) it has. The empty string has length 0, so it has no elements at all ๐ซถ
The integer type is every integer from -9223372036854775808 (-2^63) up to 18446744073709551615 (2^64-1) ๐ฅ If an integer operation overflows in the positive direction, the result is taken modulo 2^64. If it underflows in the negative direction, the result is undefined (yikes ๐). Integer division rounds toward zero.
Before a var object gets initialized, it has type String and a length of zero ๐ง
The policy script runtime does automatic type conversion whenever it's needed โจ To make the semantics of some constructs easier to explain, the spec defines a set of conversion operators: ToInteger(), ToString(), ToBoolean(), and Type(). These are NOT part of the language itself, they just exist on paper to help describe how things behave ๐ They're polymorphic, meaning they accept a value of any standard type ๐ฆ
ToInteger
ToInteger converts its argument to a value of type Integer, following this table:
Integer The result equals the input argument
(no conversion).
String See grammar and note below.
integer_constant The result equals the input argument
(no conversion).
string_literal See grammar and note below.
char_constant See grammar and note below.
ToInteger Applied to Strings
For string_literal and char_constant inputs, ToInteger runs the grammar below over the input. If the grammar can't read the string as an expansion of numeric_string, then an RTE gets generated ๐ Also, a numeric_string that's empty or only whitespace converts to 0. Lowkey a fun little fact โจ
-- EBNF for numeric_string
numeric_string : white_space* numeric? white_space*
white_space : <TAB> | <SP> | <NBSP> | <FF> | <VT>
| <CR> | <LF> | <LS> | <PS> | <USP>
numeric : signed_decimal | hex_constant | octal_constant |
enum_decimal
signed_decimal: [ '-' | '+' ] decimal_constant
enum_decimal: [ letter | digit | '-' ]* '(' decimal_constant ')'
-- decimal_constant, hex_constant, and octal_constant are defined
-- in the PolicyScript EBNF described earlier.
Now the enum_decimal form is the main vibe ๐
When it converts, everything before the parenthesis AND the parentheses themselves get completely ignored, and only the decimal_constant inside the parentheses gets converted. So "frame-relay(32)" turns into the integer 32 ๐ฆ
Writing 32 directly would be more readable, sure, but the code writer has to be accurate, because "ethernet-csmacd(32)" and "frame-relay(999)" will ALSO be accepted ๐ญ Be careful, bestie.
ToString
ToString converts its argument to a value of type String, following this table:
Integer Return the string containing the decimal
representation of the input argument in
the form of signed_decimal, except that
no leading '+' will be used.
String Return the input argument (no conversion)
integer_constant Return the string containing the decimal
representation of the input argument in the
form of signed_decimal except that no
leading '+' will be used.
string_literal Return the input argument (no conversion)
char_constant Return the string of length one containing
the value of the input argument.
ToBoolean
ToBoolean converts its argument to a value of type Integer, following this table:
Integer The result is 0 if the argument is 0.
Otherwise the result is 1.
String The results is 0 if the argument is the
empty string. Otherwise the result is 1.
integer_constant The result is 0 if the argument is 0.
Otherwise the result is 1.
string_literal The result is 0 if the argument is the
empty string. Otherwise the result is 1.
char_constant The result is 1.
Operators
These rules spell out how the type conversions work for each operator ๐ Read them carefully, they're the heart of the whole thing ๐ซถ
A++: A = ToInteger(A); A++;
A--: A = ToInteger(A); A--;
++A: A = ToInteger(A); ++A;
--A: A = ToInteger(A); --A;
+A: ToInteger(A);
-A: -1 * ToInteger(A);
~A: ToInteger(A);
!A: !ToBoolean(A);
A * B, A - B, A & B, A ^ B , A | B, A << B, A >> B:
ToInteger(A) <operator> ToInteger(B)
A / B, A % B:
if (ToInteger(B) == 0)
RTE, terminate;
else
ToInteger(A) <operator> ToInteger(B)
A + B:
if (Type(A) == String || Type(B) == String)
ToString(A) concatenated with ToString(B)
else
A + B
Compound Assignment (<operator>=):
Simply follow rules above. Note that type of LHS (Left
Hand Side) may be changed as a result.
A < B, A > B, A <= B, A >= B, A == B, A != B:
if (Type(A) == String && Type(B) == String)
lexically compare strings with strcmp() logic
else
ToInteger(A) <operator> ToInteger(B)
A && B:
if (ToBoolean(A))
ToBoolean(B);
else
false;
A || B:
if (ToBoolean(A))
true;
else
ToBoolean(B);
if(A):
if (ToBoolean(A))
while(A):
while(ToBoolean(A))
for(...; A; ...):
for(...; ToBoolean(A); ...)
A[B] as a RHS (Right Hand Side) value:
if (Type(A) != String
|| ToInteger(B) >= strlen(A))
RTE, terminate;
A[ ToInteger(B) ]
The contents are returned as a string of length one
A[B] = C as a LHS value:
if (Type(A) != String
|| ToInteger(B) >= strlen(A))
RTE, terminate;
if (strlen(ToString(C)) == 0)
RTE, terminate
A[ ToInteger(B) ] = First octet of ToString(C)
Note that this is only applicable in a simple assignment.
Here's a little example to see it all click โจ Take this expression:
"getVar("ifSpeed.1") < 128000"
getVar ALWAYS returns a string, and '128000' gets implicitly treated as an integer ๐ง The < rules say that if either argument is an integer, you do a "numeric less than" on ToInteger(A) and ToInteger(B).
So if "getVar("ifSpeed.1")" returns "64000", the expression can be translated like this:
ToInteger("64000") < ToInteger(128000); or,
64000 < 128000; or,
True
5.3. PolicyScript QuickStart Guide ๐
okay so PolicyScript is built so that programmers who already know other languages can jump in and start writing scripts right away ๐ Honestly it's giving approachable ๐ธ
The fastest way to get a language is to just see it in action โจ This totally nonsensical script uses most of the PolicyScript constructs (it skips some usage options and a bunch of arithmetic operators though ๐):
var x, index = 7, str = "Hello World", oid = "ifSpeed.";
x = 0;
while(x < 10){
if (str < "Goodbye") /* string comparison */
continue;
else
break;
x++;
}
if (oidlen(oid) == 10)
oid += "." + index; // append index to oid
for(x = 0; x < 7; x++){
str += "a";
var y = 12;
index = ((x * 7) + y) % 3;
if (str[6] == 'W')
return index;
}
return;
Now for some more practical stuff ๐ฆ
For a condition:
// Return 1 if this is an interface and it is tagged
// with the role "gold"
return (inSubtree(elementName(), "ifEntry")
&& roleMatch("gold"))
A condition/action pair: first, you register the Host Resources MIB hrSWRunEntry as a new element in the pmElementTypeRegTable. This makes the policy run for every process on the system ๐ The token $* gets replaced by the script interpreter with a process index (see Section 7 for a definition of the $* token).
The condition:
// if it's a process and it's an application and it's
// consumed more than 5 minutes of CPU time
return (inSubtree(elementName(), "hrSWRunEntry")
&& getVar("hrSWRunType.$*") == 4 // app, not OS or driver
&& getVar("hrSWRunPerfCPU.$*") > 30000) // 300 seconds
The action:
// Kill it
setVar("hrSWRunStatus.$*", 4, Integer); // invalid(4) kills it
A more substantial action, to start an RMON2 host table on the interfaces that match the condition ๐ง
var pdu, index;
pdu = newPDU();
writeVar(pdu, 0, "hlHostControlDataSource.*",
"ifIndex." + ev(0), Oid);
writeVar(pdu, 1, "hlHostControlNlMaxDesiredEntries.*", 1000,
Integer);
writeVar(pdu, 2, "hlHostControlAlMaxDesiredEntries.*", 1000,
Integer);
writeVar(pdu, 3, "hlHostControlOwner.*", "policy", String);
writeVar(pdu, 4, "hlHostControlStatus.*", "active(1)", Integer);
if (createRow(pdu, 5, 4, 20, 65535, index) == 0
|| index == -1)
return;
Because PolicyScript is a least common denominator, it has nothing that would shock programmers who know C, C++, Perl, Tcl, JavaScript, or Python ๐ญ A new programmer might try language constructs that PolicyScript doesn't have, but they should still be able to understand any existing PolicyScript, and they'll probably know how to use anything that IS valid in it. The lists below quickly point out the notable changes for people coming from particular languages. They don't describe the unavailable constructs, but you can easily tell what's available from the definition above ๐
5.3.1. Quickstart for C Programmers ๐
- Character constants (i.e.,
'c') are treated as one-character strings, not as integers. So operations such as('M' - 'A')or(x + 'A')will not perform as expected ๐ - Functions can change the value of arguments even though they are not pointers (or called like
&arg). ๐ - All variables are in the same scope. ๐ซถ
5.3.2. Quickstart for Perl Programmers ๐ธ
- Comments are
/* comment */and// till end of line, not#. โจ - No need to put a
$in front of variables. ๐ - Strings are compared with
==,<=,<, etc. (details in Sec. 6.2.1). ๐ฆ - Strings are concatenated with
+(details in Sec. 6.2.1). ๐ - No variable substitution in
""strings.''strings are 1 char only. ๐ - Variables must be declared before use (but no type is necessary). ๐ง
- All variables are in the same scope. iconic ๐
5.3.3. Quickstart for TCL Programmers ๐
okay bestie, TCL people, this one's for you ๐ง here's what's different:
- Comments are
/* comment */and// till end of line, not#๐ - No need to put a
$in front of variables โจ - Function calls are
func-name(arg1, arg2, ...)๐ซถ - Square braces
[]don't interpret their contents ๐ - Double quotes
""surround a string, but no substitutions are performed (""is like{ }in TCL ) ๐ - Statements are terminated by a semicolon (
;) ๐ธ - Instead of
"Set a b", use"b = a;"๐ - Strings are concatenated with
+(details in Sec. 6.2.1) ๐ฆ - All variables are in the same scope ๐
5.3.4. Quickstart for Python Programmers ๐
Python girlies, read this part ๐ซถ the vibes are a little different:
- Comments are
/* comment */and// till end of line, not#๐ - Single quotes can be used only for single-character strings (
'a') ๐ธ - Indentation doesn't matter. Braces
{ }define blocks โจ - Variables must be declared before use (but no type is necessary) ๐ฆ
- The expressions for if and while are always surrounded by parenthesis, as in
"if (x < 5)"๐ forsyntax is"for(expression; expression; expression)"(see EBNF) ๐- All variables are in the same scope ๐
5.3.5. Quickstart for JavaScript/ECMAScript/JScript Programmers ๐
- Variables must be declared before use ๐
- Functions can change the value of arguments ๐ฅ
- All variables are in the same scope ๐
5.4. PolicyScript Script Return Values ๐
okay so a PolicyScript script usually ends one of two ways: either it hits a return statement, or the flow of execution reaches the end of the final statement in the script ๐ a normal script execution always returns a Boolean value โจ if there's no explicit value in the return statement, or the flow goes right through the end of the script, the return value is implicitly zero ๐ and if you do give the return statement an expression, that expression gets evaluated, and its result is implicitly converted with the ToBoolean operator before it goes back to the script execution environment ๐ซถ
the return value of a policyCondition script decides whether its policyAction script runs ๐ฆ if the returned value is zero, the policyAction script is not executed ๐ if the returned value is one, the policyAction script will be executed ๐ฅณ
the return value of a policyAction script is ignored ๐คทโโ๏ธ
and heads up, an RTE or an invocation of the fail() function sets the return value of the script to zero ๐ญ but! running defer() or fail() might set the defer attribute, so the lower precedence script may get executed anyway ๐ that part has nothing to do with the return value of the policy script execution, so keep them separate in your head ๐ง
6. Index Information for 'this element' ๐งญ
PolicyScript code needs a convenient way to grab the components of the index for 'this element', so it can do SNMP operations on it or on related elements ๐ there are two mechanisms for this, and they're both super useful ๐ซถ
- For all OID input parameters to all SNMP Library Functions (but not OID utility functions), the token
"$n"($followed by an integer between 0 and 128) can be used in place of any decimal sub-identifier โจ the agent expands this token at execution time to contain the nth subid of the index for the current element ๐ฆ for example, if the element is interface 7, and the objectIdentifier is"1.3.6.1.2.1.2.2.1.3.$0", it gets expanded to"1.3.6.1.2.1.2.2.1.3.7"๐ธ the special token"$*"expands to contain all of the subidentifiers of the index of the current element, separated by.characters ๐
It is an RTE if a token is specified that is beyond the length of the index for the current element ๐ฌ
Note that the "$n" convention is only active within strings ๐
- The ec() and ev() functions let you access the components of the index for 'this element' ๐ ec() takes no argument and returns the number of index components that exist ๐ ev() takes an integer argument specifying which component of the index (numbered starting at 0), and returns an integer containing the value of the n'th subidentifier โจ refer to the Library functions section for the complete definition of ec() and ev() ๐
For example, if 'this element' is frCircuitDLCI.5.57
(ifIndex = 5, DLCI = 57)
then ec() returns 2
ev(0) returns 5
ev(1) returns 57
This is helpful when one wishes to address a related element ๐ง extending the previous example, to find the port speed of the port, the circuit (above) runs over:
portSpeed = getVar("ifSpeed." + ev(0));
A script may check the type of 'this element' by calling the elementName() function ๐ it's possible to write a script that works with different types of elements, but honestly, lots of scripts will assume one particular element type. if they get used on a different element type, they'll work incorrectly ๐ญ so be careful, bestie ๐
7. Library Functions ๐
Library functions are built-in functions, mainly there to give you access to information on the local system or to manipulate that information more efficiently โจ a group of functions is organized into a library, and the library is the unit of conformance for function implementation ๐ซถ to claim conformance to a library, an implementation must implement all functions in that library to the specifications of the library ๐
so a management station or a condition or action can figure out whether a certain library is implemented ๐ต๏ธโโ๏ธ each library has a name that it registers in the role table as a characteristic of the system element ("0.0") in the default SNMP context ๐ that means you can test conformance to a library with the roleMatch library function (in the base library) with the call roleMatch ("libraryName", "0.0") ๐
in the descriptions of these functions below, the function prototype describes the type of argument expected ๐ even though variables aren't declared with a particular type, their contents must be appropriate for each function argument ๐ค if the type is variable, the keyword 'var' will be used. if only a string is appropriate, the keyword 'string' will be used. if only an integer is appropriate, the keyword 'integer' will be used ๐ฆ if the argument is declared as 'string' or 'integer' and a value of a different type is passed, the argument will be coerced with ToInteger() or ToString() ๐ any failure of this coercion will cause an RTE (in particular for ToInteger(), which will fail if its string-valued argument is not a well-formed integer) ๐
in the function prototype, if the '&' character precedes the identifier for an argument, that argument may be modified by the function (e.g., "integer &result, ...)") โ๏ธ arguments without the '&' character cannot be modified by the function ๐ in a script, modifiable arguments don't have to be preceded by a '&' ๐
it is an RTE if a constant is passed to a modifiable function argument (regardless of whether the function actually writes to the argument) ๐ฑ
in the function prototype, the '[' and ']' characters surround arguments that are optional ๐ in PolicyScript code, the optional argument may only be included if all optional arguments to the left of it are included ๐ง the function may place restrictions on when an optional argument must, or must not, be included ๐ซ
in the function prototype, if a type is listed before the name of the function, the function returns a value of that type ๐ if no type is listed, the function returns no value ๐ซฅ
8. Base Function Library ๐ธ
a standard base library of functions is available to all systems that implement this specification ๐ this library is registered with the name "pmBaseFunctionLibrary" โจ the specification of this library is modularized into 4 separate sections, but conformance to the library requires implementation of all functions in all sections ๐ซถ the sections are:
- SNMP library functions ๐
- Policy library functions ๐ฆ
- Utility functions ๐งฐ
- Library Functions ๐
8.1. SNMP Library Functions ๐ก
there are two sets of SNMP Library functions, each one built for a different situation ๐ง
- Convenience SNMP Functions
these are all about keeping simple things simple โจ they're easy to use and the code is easy to understand ๐ they'll suffice for the majority of situations, where a single variable is referenced and the desired error recovery is simply (and immediately) to give up and move to the next policy-element combination ๐ in more complex cases, the General SNMP Functions can be used, at the cost of several times the code complexity ๐ฎโ๐จ
the convenience SNMP functions are getVar, exists, setVar, setRowStatus, createRow, counterRate, and searchColumn ๐
- General SNMP Functions
the General SNMP functions let you generate nearly any legal SNMP Message, including ones with multiple varbinds, getNext operations, notifications, and messages with explicit addressing or security specifications ๐ฅ they're the heavy hitters ๐
the general SNMP functions are writeVar, readVar, snmpSend, readError, and writeBulkParameters ๐
8.1.1. SNMP Operations on Non-Local Systems โจ
Okay so sometimes a script has to do something on a different SNMP system than the one 'this element' lives on ๐. Sometimes it also needs different security parameters. For that, the SNMP library functions get some extra optional arguments ๐ :
snmp-function(...[, integer mPModel,
string tDomain, string tAddress,
integer secModel, string secName,
integer secLevel, string contextEngineID
])
For example:
getVar("sysDescr.0", "", SNMPv3, "transportDomainUdpIpv4",
"192.168.1.1:161", USM, "joe", NoAuthNoPriv);
When a function uses these, its definition calls them 'NonLocalArgs' ๐ฆ. Here's what each one means:
- 'mPModel' is the integer value of the SnmpMessageProcessingModel to use for this operation.
- 'tDomain' is a string containing an ASCII dotted-decimal object identifier representing the transport domain to use for this operation.
- 'tAddress' is a string containing the transport address formatted according to the 'tDomain' argument. The ASCII formats for each 'tDomain' value come from the DISPLAY-HINT of a TEXTUAL-CONVENTION that represents an address of that type. The DISPLAY-HINTs used are:
tDomain Source of DISPLAY-HINT [5] [11]
------- ----------------------
transportDomainUdpIpv4 TransportAddressIPv4
transportDomainUdpIpv6 TransportAddressIPv6
transportDomainUdpDns TransportAddressDns
snmpCLNSDomain snmpOSIAddress
snmpCONSDomain snmpOSIAddress
snmpDDPDomain snmpNBPAddress
snmpIPXDomain snmpIPXAddress
rfc1157Domain snmpUDPAddress
Other Use DISPLAY-HINT "1x:"
- 'secModel' is the integer value of the SnmpSecurityModel to use for this operation.
- 'secName' is a string value representing the SnmpSecurityName to use for this operation.
- 'secLevel' is the integer value of the SnmpSecurityLevel to use for this operation.
Now the important bit ๐จ. An SNMP operation gets sent to the target system using security parameters pulled from a local configuration datastore, based on 'secModel', 'secName', and 'secLevel'. The agent is responsible for making sure that sensitive info in that local datastore gets used on behalf of the correct principals. Those are whoever is identified by the security credentials of the last entity to modify the pmPolicyAdminStatus for a policy.
Let's do an example ๐ง. 'joe' installs a policy on 'PMAgent' that periodically configures objects on 'TargetAgent' using the credentials of 'Operator'. For this policy to run with the right privileges, all of these must be true:
- 'Operator's security credentials for TargetAgent must be installed in PMAgent's local configuration datastore (e.g., usmUserTable [6]) indexed by TargetAgent's engineID and 'Operator'.
- VACM [9] must be configured on PMAgent so that 'joe' has access to the above entry in the appropriate MIB for the local configuration datastore (e.g., usmUserTable).
- 'joe' must be the last user to modify the pmPolicyAdminStatus object for the policy.
See the Security Considerations section for more information. Honestly, read it ๐.
For convenience, constants for 'mPModel', 'secModel', and 'secLevel' are defined in the "Constants" section below.
'contextEngineID' is a string representing the contextEngineID of the SNMP entity targeted by this operation. It's encoded as a pair of hex digits for each octet of the contextEngineID (upper- and lowercase are both valid). If 'tDomain' and 'tAddress' are provided but 'contextEngineID' isn't, the operation goes to the SNMP entity reachable at 'tDomain' and 'tAddress'.
Okay, the rules for stacking these args ๐: for PolicyScript code to use any of these arguments, all optional arguments to the left must be included. 'mPModel', 'tDomain', 'tAddress', 'secModel', 'secName', and 'secLevel' must be used as a group. If one is specified, they must all be specified. 'contextEngineID' may only be specified if all others are specified.
Also, a function that uses NonLocalArgs must provide a parameter for the contextName, which is required whenever the NonLocalArgs are present. Lots of functions follow this logic:
ContextName NonLocalArgs
Supplied Supplied
No No Addressed to default context on
local system.
Yes No Addressed to named context on
local system.
Yes Yes Addressed to named context on
potentially remote system.
No Yes Not allowed.
8.1.2. Form of SNMP Values
Lots of library functions have input or output parameters that can be one of the many SMI data types ๐. The actual type isn't encoded in the value. It's specified somewhere else, sometimes just by the situation it's used in. Here's exactly how input and output work for each one:
Any Integer value (INTEGER, Integer32, Counter32, Counter64, Gauge32, Unsigned32, TimeTicks, Counter64):
- On input: an Integer, or a String that can be successfully coerced to an Integer with the ToInteger() operator. It's an RTE if a string is passed that ToInteger() can't convert into an integer.
A string of the form
enum_decimal: [ letter | digit | '-' ]* '(' decimal_constant ')'
is also accepted. In that case, the characters before the parentheses and the parentheses themselves are completely ignored, and the decimal_constant inside the parentheses gets converted. So "frame-relay(32)" becomes the integer 32 โจ.
- On output: an Integer containing the returned value.
Octet String
- On input: either a String or an Integer. If it's an Integer, it gets coerced to a String with the ToString() function. This string is used as an unencoded representation of the octet string value.
- On output: a String containing the unencoded value of the octet string.
Object Identifier
- On input and on output: a String containing a decimal ASCII encoded object identifier of this form:
oid: subid [ '.' subid ]* [ '.' ]
subid: '0' | decimal_constant
It's an RTE if an Object Identifier argument isn't in the form above. A trailing '.' is acceptable and just gets ignored. (BUT ๐ค, a trailing dot could make a strncmp() comparison of two otherwise-identical OIDs fail. Use oidncmp() instead.)
ASCII descriptors (e.g., "ifIndex") are never used in these encodings "over the wire". They're never returned from library functions, and library functions never accept them. NMS user interfaces are encouraged to let humans view object identifiers with ASCII descriptors. But they must translate those descriptors to dotted-decimal format before sending them in MIB objects to policy agents.
Null
- On input: the input is ignored.
- On output: a zero length string.
8.1.3. Convenience SNMP Functions
8.1.3.1. getVar()
The getVar() function retrieves the value of an SNMP MIB object instance ๐ฆ.
string getVar(string oid [, string contextName, NonLocalArgs])
- 'oid' is a string containing an ASCII dotted-decimal representation of an object identifier (e.g., "1.3.6.1.2.1.1.1.0").
- The optional 'contextName' argument contains the SNMP context to operate on. If 'contextName' isn't present, the contextName of 'this element' gets used. If 'contextName' is the zero-length string, the default context is used.
- The optional 'NonLocalArgs' provide addressing and security info to perform an SNMP operation on a system different from 'this element'.
It's an RTE if the queried object identifier value doesn't exist ๐.
This function returns a string containing the returned value, encoded according to the returned type. No actual SNMP PDU has to be generated and parsed when the policy MIB agent lives on the same system as the managed elements, so that's a nice bonus ๐.
It is recommended that NMS user interfaces display and allow input of MIB object names by their descriptor values, followed by the index in dotted-decimal form (e.g., "ifType.7") ๐.
8.1.3.2. exists()
The exists() function checks whether an SNMP MIB object instance exists ๐.
integer exists(string oid [, string contextName, NonLocalArgs])
- 'oid' is a string containing an ASCII dotted-decimal representation of an object identifier (e.g., "1.3.6.1.2.1.1.1.0").
- The optional 'contextName' argument contains the SNMP context to operate on. If 'contextName' isn't present, the contextName of 'this element' gets used. If 'contextName' is the zero-length string, the default context is used.
- The optional 'NonLocalArgs' provide addressing and security info to perform an SNMP operation on a system different from 'this element'.
This function returns the value 1 if the SNMP instance exists, and 0 if it doesn't. Again, no actual SNMP PDU has to be generated and parsed when the policy MIB agent is on the same system as the managed elements ๐ง .
It is recommended that NMS user interfaces display and allow input of MIB object names by their descriptor values, followed by the index in dotted-decimal form (e.g., "ifType.7") ๐ธ.
8.1.3.3. setVar() ๐
okay so setVar() is how you SET a MIB object instance to a specific value ๐ It's only valid inside policyActions, so keep it there, bestie ๐ซถ
setVar(string oid, var value, integer type
[, string contextName, NonLocalArgs] )
oidis a string holding an ASCII dotted-decimal version of an object identifier, like"1.3.6.1.2.1.1.1.0"โจvalueis a string encoded in whatever format matches thetypeparameter. The agent then sets the variable named byoidto thatvalue๐typeis the type of thevalueparameter, and it's one of the DataType Constants ๐ฆcontextNameis optional and holds the SNMP context to work on. If you leave it out, the contextName of 'this element' gets used. If it's the zero-length string, the default context is used ๐ธNonLocalArgsis optional too, and it's giving addressing and security info so you can do an SNMP operation on a system that isn't 'this element' ๐ Also, no actual SNMP PDU has to be generated and parsed when the policy MIB agent lives on the same system as the managed elements. Iconic efficiency ๐ญ
If the set hits any error at all, it's an RTE. Not me getting an RTE in my own script ๐
It's recommended that NMS user interfaces show and accept MIB object names by their descriptor values, followed by the index in dotted-decimal form, like "ifType.7" ๐
8.1.3.4. searchColumn() ๐
integer searchColumn(string columnoid, string &oid,
string pattern, integer mode
[, string contextName, NonLocalArgs])
okay this one is a whole vibe ๐ง searchColumn does an SNMP walk on part of the MIB and looks for objects whose values equal the pattern parameter ๐
columnoidlimits the search to variables that share the same OID prefix. Basically, anything living beneath it in the OID tree ๐ณ- A getnext request gets sent for the object identifier
oid. Ifoidis an empty string, the value ofcolumnoidgets sent instead ๐ - Every value that comes back in a response packet gets turned into a string. First it's put into the form dictated by the "Form of SNMP Values" rules, and then the ToString() function runs on it. That result is called 'SearchString' ๐ฆ
modedecides what kind of match to do against that 'SearchString'. There are 6 possibilities, and the constants for them live in the 'Constants' section below โจ
Mode Search Action
ExactMatch Case sensitive exact match of 'pattern'
and 'SearchString'.
ExactCaseMatch Case insensitive exact match of 'pattern'
and 'SearchString'.
SubstringMatch Case sensitive substring match, finding
'pattern' in 'SearchString'.
SubstringCaseMatch Case insensitive substring match, finding
'pattern' in 'SearchString'.
RegexpMatch Case sensitive regular expression match,
searching 'SearchString' for the regular
expression given in 'pattern'.
RegexpCaseMatch Case insensitive regular expression match,
searching 'SearchString' for the regular
expression given in 'pattern'.
searchColumn uses the POSIX extended regular expressions defined in POSIX 1003.2 ๐
contextNameis optional and holds the SNMP context to work on. If it's left out, the contextName of 'this element' is used. If it's the zero-length string, the default context is used ๐ธNonLocalArgsis optional, and it provides addressing and security info so you can run SNMP operations on a system that isn't 'this element' ๐
If a match shows up, oid gets set to the OID of the matched value and you get back 1. If the search goes past columnoid, or it hits an error without finding anything, you get back zero and oid stays untouched ๐ฅบ
To find the first match, the caller sets oid to the empty string. To find more matches, later calls to searchColumn should have oid set to the OID of the last match. searchColumn does that bit automatically for you, bestie ๐
For example, to find an ethernet interface:
oid = "";
searchColumn("ifType", oid, "6", 0);
This sends a getnext request for ifType and keeps walking the tree until it finds a value matching 6, or until it returns a variable that isn't in the 'ifType' subtree ๐ญ
To find the next ethernet interface, assuming interface 3 was the first one found:
oid = "ifType.3";
searchColumn("ifType", oid, "6", 0);
And if you want all the ethernet interfaces in a loop, it looks like this:
oid = "";
while(searchColumn("ifType", oid, "6", 0)){
/* Do something with oid */
}
Quick note: "ifType" is just a notational convenience in those examples. The actual code downloaded to the policy MIB agent has to use the string "1.3.6.1.2.1.2.2.1.3", because there might not be a MIB compiler (or MIB file) on the policy MIB agent ๐
Also, if columnoid is too short and references too much of the object identifier tree (like "1.3.6"), then searchColumn could end up searching a huge number of variables. If the value was "1.3.6", it would search ALL variables on the agent ๐ It's the caller's job to make sure columnoid is set appropriately ๐ซถ
8.1.3.5. setRowStatus() ๐ง
integer setRowStatus(string oid, integer maxTries
[, integer freeOnException , integer seed
, string contextName, NonLocalArgs])
setRowStatus automates finding an unused row in a read-create table that uses RowStatus, where the index has an arbitrary integer component for uniqueness ๐ฆ So it's basically doing the hunt for you โจ
oidis a string holding an ASCII dotted-decimal version of an object identifier, with one of the subids swapped out for a '' character. For example, "1.3.6.1.3.1.99.1.2.1.9." ๐ธoidhas to reference an 'instance' of the RowStatus object, and the '*' has to stand in for any integer index item that can be set to some random value ๐
setRowStatus picks a number for the selected index item and tries to create the instance with the createAndWait state. If that attempt fails, it tries again with a different random index value. It'll try no more than maxTries times ๐ฅ
freeOnExceptionis optional. If it's present and equal to 1, the agent frees this row by setting RowStatus to 'destroy' if, later in the same script invocation, the script dies with a run-time exception or by a call to fail() ๐ Note that this doesn't apply to exceptions in later invocations of the script ๐seedis optional. If it's present, the initial index gets set toseed. Otherwise it's random ๐seedmay not be present if thefreeOnExceptionargument isn't present either ๐ฅบcontextNameis optional and holds the SNMP context to work on. If it's left out, the contextName of 'this element' is used. If it's the zero-length string, the default context is used ๐ธNonLocalArgsis optional, and it provides addressing and security info for doing an SNMP operation on a system that isn't 'this element' ๐
setRowStatus returns the successful integer value for the index. If it's unsuccessful after maxTries, or if zero or more than one '*' is in OID, -1 gets returned ๐ญ
The createRow function can also be used when adding rows to tables. createRow has more functionality, but setRowStatus might be preferable in certain situations, like when you want the chance to inspect the default values the agent created ๐ง๐
8.1.3.6. createRow() ๐
integer createRow(integer reqPDU, integer reqNumVarbinds,
integer statusColumn, integer maxTries,
integer indexRange,
integer &respPDU, integer &respNumVarbinds,
integer &index
[, integer freeOnException, string contextName,
NonLocalArgs])
okay so createRow is the helper that does the annoying work of making a row in a read-create table whose index has a random integer in it ๐ฆ It basically wraps up the createAndWait or createAndGo mechanism AND the hunt for an unused row, so you don't have to. It is not useful for tables whose indexes don't have an arbitrary integer component, so don't reach for it there ๐
createRow sends 'reqPDU' and drops the results into 'respPDU'. Both of those must have been allocated with newPDU first ๐ธ They're allowed to be the same PDU handle too, in which case 'reqPDU' gets sent and then swapped out for whatever the received PDU contains. Iconic recycling โจ
'reqNumVarbinds' is an integer bigger than zero that says which varbinds in the PDU get used. The first 'reqNumVarbinds' ones are the ones that count. Each of those varbinds has to be a special form, where one of the subids in the object name is replaced with a * character. For example, "1.3.6.1.3.1.99.1.2.1.9.*". The subid that gets the * is an integer index item that can be set to some random value, and you should pick the same subid in every varbind in the PDU. Consistency is everything bestie ๐ซถ
'respNumVarbinds' gets updated to hold the number of varbinds received in the last response PDU ๐
'statusColumn' tells you which varbind in 'pdu' should be treated as the RowStatus column, where 0 is the 1st varbind ๐
So here's the vibe. createRow comes up with a random integer index value and swaps it in for the * subid in each varbind. Then it sets the RowStatus column to pick the createAndGo mechanism and runs the set. If that fails because createAndGo isn't available, it retries with createAndWait selected ๐ฅ If it fails because that index is already taken, it tries again with a different random index. It keeps going like this until it succeeds, until it has made 'maxTries' attempts, or until it runs into an error. Honestly, 'maxTries' should be picked high enough that, as the table fills up, creating a new entry doesn't 'collide' too often and fail. Nobody wants a row that keeps getting ghosted ๐
Every random index value has to land between 1 and 'indexRange', inclusive. That way it never tries an index outside that index's restricted range, like 1..65535 ๐
If the optional 'freeOnException' argument is present and equal to 1, the agent will free this row by setting RowStatus to destroy if, later in the same script invocation, the script dies with a run-time exception or with a call to fail(). Just know this doesn't apply to exceptions that happen in later invocations of the script ๐
The optional 'contextName' argument holds the SNMP context to operate on. If 'contextName' isn't present, the contextName of 'this element' gets used. If 'contextName' is the zero-length string, the default context is used โจ
The optional 'NonLocalArgs' give you the addressing and security info needed to run an SNMP operation on a system that is different from 'this element' ๐
Here's a plot twist. No actual SNMP PDU has to be generated or parsed when the policy MIB agent lives on the same system as the managed elements. But if no PDU gets generated, the agent must correctly simulate the behavior of the SNMP Response PDU, especially when there's an error. Main character energy means not skipping the fake response ๐ง
This function returns zero unless an error happens. If one does, it returns the proper SNMP Error Constant. When there's an error, respPDU holds the last response PDU as received from the agent, unless no response PDU came back at all, in which case respNumVarbinds will be 0. Either way you can call readError on the PDU to get the error info for the transaction ๐ญ
The 'index' parameter hands back the chosen index. If it works, 'index' gets set to the successful integer index ๐ฆ If there's no SNMP error but the operation still doesn't succeed, 'index' gets set to -1, and that happens for these reasons:
- Unsuccessful after
'maxTries'. - An object name had no
*in it. - An object name had more than one
*in it.
Here's how you'd use createRow():
var index, pdu = newPDU(), nVars = 0;
writeVar(pdu, nVars++, "hlHostControlDataSource.*",
"ifIndex." + ev(0), Oid);
writeVar(pdu, nVars++, "hlHostControlNlMaxDesiredEntries.*",
1000, Integer);
writeVar(pdu, nVars++, "hlHostControlAlMaxDesiredEntries.*",
1000, Integer);
writeVar(pdu, nVars++, "hlHostControlOwner.*", "policy",
String);
writeVar(pdu, nVars++, "hlHostControlStatus.*", "active(1)",
Integer);
if (createRow(pdu, nVars, 4, 20, 65535,
pdu, nVars, index) != 0
|| index == -1)
return;
// index now contains index of new row
8.1.3.7. counterRate() ๐
When a policy wants to make a decision based on the rate of a counter, it runs into a couple of problems ๐ค
- It may have to run every X minutes but need to make decisions on rates calculated over at least Y minutes, where Y > X. That would require the complexity of managing a queue of old counter values.
- The policy script has no control over exactly when it will run.
The counterRate() function is built to surmount these problems easily. Slay ๐
integer counterRate(string oid, integer minInterval
[, integer 64bit,
string discOid, integer discMethod,
string contextName, NonLocalArgs])
'counterRate' retrieves the variable specified by oid once per invocation. It keeps track of timestamped values retrieved on previous invocations by this execution context, so it can calculate a rate over a period longer than the time since the last invocation ๐ฆ
'oid' is the object identifier of the counter value that will be retrieved. The most recent previously saved value of that same object identifier that is at least 'minInterval' seconds old gets subtracted from the newly retrieved value, giving you a delta. If 'minInterval' is zero, that delta is returned. Otherwise, the delta gets divided by the number of seconds elapsed between the two retrievals, and the integer-valued result is returned (rounding down when necessary) ๐
If there was no previously saved retrieval older than 'minInterval' seconds, then -1 is returned. It is an RTE if the query returns noSuchName, noSuchInstance, or noSuchObject, or an object that is not of type Counter32 or Counter64 ๐
The delta calculation allows for 32-bit counter semantics if it runs into a rollover between the two retrievals. The exception is when the optional argument '64bit' is present and equal to 1, in which case it allows for 64-bit counter semantics ๐ธ
'discOid' and 'discMethod' may only be present together. 'discOid' contains an object identifier of a discontinuity indicator value that is retrieved at the same time as each counter value:
- If
'discMethod'is equal to 1 and the discontinuity indicator is less than the last one retrieved, then a discontinuity is indicated. - If
'discMethod'is equal to 2 and the discontinuity indicated is different from the last one retrieved, then a discontinuity is indicated.
If this value indicates a discontinuity, this counter value (and its timestamp) gets stored. All previously stored counter values get invalidated, and -1 is returned ๐ญ
The implementation has to store a number of timestamped counter values. It must keep all values that are newer than minInterval seconds, plus the newest value that is older than minInterval seconds. Apart from that one older value, the implementation should discard any older values ๐ง
Here are some examples:
Policy that executes every 60 seconds:
rate = counterRate("ifInOctets.$*", 300);
if (rate > 1000000)
...
Another example, with a discontinuity indicator:
Policy that executes every 60 seconds:
rate = counterRate("ifInOctets.$*", 300, 0,
"sysUpTime.0", 1);
if (rate > 1000000)
...
Another example, with zero minInterval:
Policy that executes every 60 seconds:
delta = counterRate("ifInErrors.$*", 0);
if (delta > 100)
...
The optional 'contextName' argument holds the SNMP context to operate on. If 'contextName' isn't present, the contextName of 'this element' gets used. If 'contextName' is the zero-length string, the default context is used ๐
8.1.4. General SNMP Functions ๐ธ
okay so a general SNMP interface really should be able to poke at a bunch of variables in one go, and let multiple varbind lists exist at the same time ๐ง newPDU, readVar, and writeVar are here to make that happen in a language with no pointers, no arrays, and no memory allocators ๐
newPDU lets you allocate a PDU and get back an integer handle for it. PDUs get freed automatically when the script exits and you can reuse them during execution, so there is no freePDU() ๐ซถ
readVar and writeVar work on a variable-length varbind list inside a PDU. Every readVar and writeVar call needs the PDU handle and the index of the variable inside that PDU. Once you've fully set up a PDU with one or more writeVar calls, you pass it to snmpSend (by referencing the handle) along with the number of varbinds to include. When the response comes back, its contents land in another PDU, and you read them out with one or more readVar calls. Error info comes from the readError function ๐ And since GetBulk PDUs carry extra info in the SNMP header, the writeBulkParameters function lets you configure those parameters ๐
Varbinds in this data store get created automatically whenever they're written by any writeVar or snmpSend operation โจ
Here's an example:
var pdu = newPDU();
var nVars = 0, oid, type, value;
writeVar(pdu, nVars++, "sysDescr.0", "", Null);
writeVar(pdu, nVars++, "sysOID.0", "", Null);
writeVar(pdu, nVars++, "ifNumber.0", "", Null);
if (snmpSend(pdu, nVars, Get, pdu, nVars))
return;
readVar(pdu, 0, oid, value, type);
readVar(pdu, 1, oid, value, type);
readVar(pdu, 2, oid, value, type);
...
or,
var pdu = newPDU();
var nVars = 0, oid1, oid2;
writeVar(pdu, nVars++, "ifIndex", "", Null);
writeVar(pdu, nVars++, "ifType", "", Null);
while(!done){
if (snmpSend(pdu, nVars, Getnext, pdu, nVars))
continue;
readVar(pdu, 0, oid1, value, type);
readVar(pdu, 1, oid2, value, type);
/* leave OIDs alone, now PDU #0 is set up for next step
in table walk. */
if (oidncmp(oid1, "ifIndex", oidlen("ifIndex")))
done = 0;
...
}
Quick heads-up: in those examples, ifType and sysDescr are only used in object identifiers as a notational convenience ๐ The actual code downloaded to the policy MIB agent must use dotted decimal notation only, because there might be no MIB compiler (or MIB file) on the policy MIB agent ๐ค
To conform to this spec, implementations must allow each policy script invocation to allocate at least 5 PDUs with at least 64 varbinds per list ๐ It's suggested that implementations cap the total number of PDUs per invocation so one malfunctioning script (like one that calls newPDU() in a loop) can't wreck everyone else's scripts ๐
8.1.4.1. newPDU()
integer newPDU()
newPDU will allocate a new PDU and return a handle to it ๐ฆ If it can't allocate a PDU, -1 gets returned. The PDU's initial values of nonRepeaters and maxRepetitions are zero.
8.1.4.2. writeVar()
writeVar(integer pdu, integer varBindIndex,
string oid, var value, integer type)
writeVar stores 'oid', 'value', and 'type' in the specified varbind ๐
'pdu' is the handle to a PDU that newPDU() allocated.
'varBindIndex' is a non-negative integer that identifies which varbind inside the specified PDU this call modifies. The first varbind is number 0.
'oid' is a string holding an ASCII dotted-decimal representation of an object identifier (e.g., "1.3.6.1.2.1.1.1.0").
'value' is the value to be stored, and it has to be a type that matches the 'type' parameter.
'type' is the type of the value parameter, and it gets set to one of the values from DataType Constants.
It's an RTE if any of the parameters don't follow the rules above. Bestie, don't skip this one ๐ญ
8.1.4.3. readVar()
readVar(integer pdu, integer varBindIndex, string &oid,
var &value, integer &type)
readVar pulls the oid, the value, and its type out of the specified varbind โจ
'pdu' is the handle to a PDU that newPDU() allocated.
'varBindIndex' is a non-negative integer that identifies which varbind inside the specified PDU this call reads. The first varbind is number 0.
The object identifier of the referenced varbind gets copied into the 'oid' parameter, formatted as an ASCII dotted-decimal representation (e.g., "1.3.6.1.2.1.1.1.0").
'value' is the value retrieved, and it's a type that matches the 'type' parameter.
'type' is the type of the value parameter, and it gets set to one of the values from DataType Constants.
It's an RTE if 'pdu' doesn't reference a valid PDU or if 'varBindIndex' doesn't reference a valid varbind ๐ โโ๏ธ
8.1.4.4. snmpSend()
integer snmpSend(integer reqPDU, integer reqNumVarbinds,
integer opcode,
integer &respPDU, integer &respNumVarbinds,
[, string contextName , NonLocalArgs] )
snmpSend performs an SNMP operation by sending 'reqPDU' and dropping the results into 'respPDU' ๐ Both 'reqPDU' and 'respPDU' have to have been allocated with newPDU first. They're allowed to be the same PDU handle, in which case 'reqPDU' gets sent and then replaced with the contents of the received PDU. If the opcode is a Trap or V2trap, 'respPDU' won't be modified.
'reqNumVarbinds' is an integer greater than zero that says which varbinds in the PDU get used in this operation. The first 'reqNumVarbinds' varbinds in the PDU are the ones used. 'respNumVarbinds' gets updated to hold the number of varbinds in the response PDU, which, for a GetBulk or an error, can be wildly different from reqNumVarbinds ๐
'opcode' is the type of SNMP operation to perform, and it must be one of the values from the SNMP Operation Constants listed in the 'Constants' section below.
The optional 'contextName' argument holds the SNMP context to operate on. If 'contextName' isn't there, the contextName of 'this element' gets used. If 'contextName' is the zero-length string, the default context is used ๐ธ
Heads up: no actual SNMP PDU has to be generated and parsed when the policy MIB agent lives on the same system as the managed elements ๐ง If no PDU gets generated, the agent must correctly simulate the behavior of the SNMP Response PDU, especially in the case of an error.
This function returns zero unless an error happens, in which case it returns the proper SNMP Error Constant. If an error occurred, respPDU holds the response PDU as received from the agent, unless no response PDU came back, in which case respNumVarbinds will be 0. Either way, you can call readError on the PDU to find out the error info for the transaction ๐
If an SNMP Version 1 trap is requested (the opcode is Trap(4)), then SNMP Version 2 trap parameters are supplied and converted according to the rules of RFC 3584 [8], section 3.2. The first variable binding must be sysUpTime.0, and the second must be snmpTrapOID.0, as per RFC 3416 [7], section 4.2.6. Any variable bindings after that get copied into the SNMP Version 1 trap PDU in the usual fashion ๐ฆ
8.1.4.5. readError()
readError(integer pdu, integer numVarbinds, integer &errorStatus,
integer &errorIndex, integer &hasException)
Returns the error information in a PDU ๐
'errorStatus' holds the error-status field from the response PDU, or a local error constant if the error was generated locally. If no error was experienced, or no PDU was ever copied into this PDU, this value will be 0.
'errorIndex' holds the error-index field from the response PDU. If no PDU was ever copied into this PDU, this value will be 0.
'hasException' will be 1 if any of the first 'numVarbinds' varbinds in the PDU contain an exception (Nosuchobject, Nosuchinstance, Endofmibview). Otherwise it will be 0 ๐
It's an RTE if 'pdu' doesn't reference a valid PDU, or if 'numVarbinds' references varbinds that aren't valid.
8.1.4.6. writeBulkParameters()
writeBulkParameters(integer pdu, integer nonRepeaters,
integer maxRepetitions)
This modifies the parameters in a PDU for any later GetBulk operation sent by that PDU ๐ 'nonRepeaters' gets copied into the PDU's non-repeaters field, and 'maxRepetitions' gets copied into the max-repetitions field.
writeBulkParameters can be called before or after writeVar adds varbinds to the PDU, but it must be called before the PDU is sent. Otherwise it won't have any effect. A new PDU starts with nonRepeaters set to zero and maxRepetitions set to zero. If a Bulk PDU gets sent before writeBulkParameters is called, those default values get used. If writeBulkParameters is called to modify a PDU, it's okay if that PDU is later sent as a type other than bulk. The writeBulkParameters call only affects later sends of Bulk PDUs. If a PDU is used to receive the contents of a response, the values of nonRepeaters and maxRepetitions are never modified ๐
8.1.5. Constants for SNMP Library Functions โจ
okay so these are the constants you can use with all the SNMP Library Functions ๐ policy code runs somewhere these are already declared, but heads up: the declarations themselves won't be visible in policyCondition or policyAction code ๐. They're reserved words, so you can't name any variable or function with them. Also, even though they're written as C consts here, the const keyword itself isn't available in policy code. bestie, just use the values ๐
// Datatype Constants
// From RFC 2578 [2]
const integer Integer = 2;
const integer Integer32 = 2;
const integer String = 4;
const integer Bits = 4;
const integer Null = 5;
const integer Oid = 6;
const integer IpAddress = 64;
const integer Counter32 = 65;
const integer Gauge32 = 66;
const integer Unsigned32 = 66;
const integer TimeTicks = 67;
const integer Opaque = 68;
const integer Counter64 = 70;
// SNMP Exceptions from RFC 3416 [7]
const integer NoSuchObject = 128;
const integer NoSuchInstance = 129;
const integer EndOfMibView = 130;
// SNMP Error Constants from RFC 3416 [7]
const integer NoError = 0;
const integer TooBig = 1;
const integer NoSuchName = 2;
const integer BadValue = 3;
const integer ReadOnly = 4;
const integer GenErr = 5;
const integer NoAccess = 6;
const integer WrongType = 7;
const integer WrongLength = 8;
const integer WrongEncoding = 9;
const integer WrongValue = 10;
const integer NoCreation = 11;
const integer InconsistentValue = 12;
const integer ResourceUnavailable = 13;
const integer CommitFailed = 14;
const integer UndoFailed = 15;
const integer AuthorizationError = 16;
const integer NotWritable = 17;
const integer InconsistentName = 18;
// "Local" Errors
// These are also possible choices for errorStatus returns
// For example: unknown PDU, maxVarbinds is bigger than number
// written with writeVar, unknown opcode, etc.
const integer BadParameter = 1000;
// Request would have created a PDU larger than local limitations
const integer TooLong = 1001;
// A response to the request was received but errors were encountered
// when parsing it.
const integer ParseError = 1002;
// Local system has complained of an authentication failure
const integer AuthFailure = 1003;
// No valid response was received in a timely fashion
const integer TimedOut = 1004;
// General local failure including lack of resources
const integer GeneralFailure = 1005;
// SNMP Operation Constants from RFC 3416 [7]
const integer Get = 0;
const integer Getnext = 1;
const integer Set = 3;
const integer Trap = 4;
const integer Getbulk = 5;
const integer Inform = 6;
const integer V2trap = 7;
// Constants from RFC 3411 [1] for SnmpMessageProcessingModel
const integer SNMPv1 = 0;
const integer SNMPv2c = 1;
const integer SNMPv3 = 3;
// Constants from RFC 3411 [1] for SnmpSecurityModel
const integer SNMPv1 = 1;
const integer SNMPv2c = 2;
const integer USM = 3;
// SnmpSecurityLevel Constants from RFC 3411 [1]
const integer NoAuthNoPriv = 1;
const integer AuthNoPriv = 2;
const integer AuthPriv = 3;
// Constants for use with searchColumn
const integer ExactMatch = 0;
const integer ExactCaseMatch = 1;
const integer SubstringMatch = 2;
const integer SubstringCaseMatch = 3;
const integer RegexpMatch = 4;
const integer RegexpCaseMatch = 5;
8.2. Policy Library Functions ๐
okay now we're in the policy library era ๐ these functions give you info that's specifically about running policies, so they're basically your cheat sheet for the policy itself ๐ง
8.2.1. elementName() ๐ฆ
elementName() tells you what the current element is ๐ and it can also give you info about what type of element it is and how it's indexed. it's giving detective energy, no cap.
string elementName()
elementName returns a string holding an ASCII dotted-decimal representation of an object identifier (e.g., 1.3.6.1.2.1.1.1.0) ๐ that OID identifies an instance of a MIB object that's an attribute of 'this element'. โจ
8.2.2. elementAddress() ๐ธ
elementAddress(&tDomain, &tAddress)
elementAddress finds a domain/address pair you can use to reach 'this element', and it hands the values back in 'tDomain' and 'tAddress'. bestie that's literally how you find your way to the element ๐บ๏ธ๐
8.2.3. elementContext() ๐
string elementContext()
elementContext() returns a string with the SNMP contextName of 'this element'. simple, iconic, done ๐
8.2.4. ec() ๐
okay so ec() and ev() are the element count and element value functions, and they're super handy for getting at the pieces of the index for 'this element' ๐ซถ they're especially useful when you're building the index for other, related elements.
integer ec()
ec() returns an integer count of how many index subidentifiers exist in the index for 'this element'. โจ
8.2.5. ev() ๐ง
integer ev(integer n)
ev() returns the value of the nth subidentifier in the index for 'this element' ๐ heads up, the first subidentifier is at index 0, not 1 ๐ and if n points past the last subidentifier, that's an RTE. ๐ญ
8.2.6. roleMatch() ๐
roleMatch() is how you check whether an element has been given a particular role. it's main character energy ๐ฅ
integer roleMatch(string roleString [, string element,
string contextName, string contextEngineID])
'roleString' is a string. the optional 'element' argument holds the OID name of an element, and if you don't pass it, it defaults to the current element ๐ if roleString exactly matches (content and length, no cap) any role assigned to the specified element, the function returns 1. if no roles match, it returns 0. โจ
the optional 'contextName' argument is the SNMP context you want to operate on ๐ฆ if you leave it out, the contextName of 'this element' gets used. if 'contextName' is the zero-length string, the default context is used. โจ
'contextEngineID' holds the contextEngineID of the remote system where 'element' lives ๐ it's encoded as a pair of hex digits for each octet of the contextEngineID, and upper- and lowercase are both valid. if you skip it, the contextEngineID of 'this element' gets used. and here's the rule, periodt: 'contextEngineID' can only be present if the 'element' and 'context' arguments are also present. ๐ซถ
8.2.7. Scratchpad Functions ๐ง
okay so every maxLatency period, every policy runs once for each element ๐ซถ when setScratchpad runs, it stores a value under a string name, and getScratchpad() can still grab it even after that policy code has exited โจ this is how a condition and an action can share data, or two conditions running on different elements can, or even two totally different policies ๐ฅ
the scope decides which policy/element combos can retrieve a 'varName'/'value' pair ๐
here are the scope options:
Global The 'varName'/'value' pair is available in the condition or action of any policy, while that policy is running on any element ๐ but heads up bestie: anything stored here is visible to every other script on the system, no matter whose authority they have ๐ญ sensitive info should not be put in global scratchpad variables.
Policy The pair is available in any future run of the current policy's condition or action, on whatever element it's running on ๐ if the policy gets deleted, or its condition or action code gets modified, every value in its 'Policy' scope is deleted ๐๏ธ
PolicyElement The pair is available in future runs of the current policy's condition or action, but only when that policy is running on the current element ๐ฆ same deal: deleting the policy or editing its code wipes its 'PolicyElement' values. the agent may also periodically delete 'PolicyElement' values if the element doesn't exist anymore. so if an element vanishes and then comes back, those values may or may not still be there ๐
setScratchpad's 'storageType' argument controls how long a variable lives in the scratchpad ๐ if storageType is the constant 'volatile', the variable must be deleted on a reboot. if it's 'nonVolatile', the variable should be stored in non-volatile storage, so it sticks around after a reboot โจ if the 'storageType' argument isn't there at all, the variable is volatile and gets erased on reboot.
if the optional 'freeOnException' argument is there and equals 1, the agent frees the variable when, later in that same script run, the script dies with a run-time exception or by calling fail() ๐ (this doesn't apply to exceptions in later runs of the script, just to be clear ๐ซถ)
there may be implementation-specific limits on how many scratchpad variables you can make ๐ธ the limit can be different for each scope or storageType. it's suggested that implementations cap the total number of scratchpad variables per script, so one broken script can't mess up everyone else ๐ก๏ธ also, compliant implementations must support at least 50 Global variables, 5 Policy variables per policy, and 5 PolicyElement variables per policy-element pair ๐
Scratchpad Usage Examples ๐
Policy Element Action
A ifIndex.1 setScratchpad(Global, "foo", "55")
A ifIndex.1 getScratchpad(Global, "foo", val) --> 55
A ifIndex.2 getScratchpad(Global, "foo", val) --> 55
B ifIndex.2 getScratchpad(Global, "foo", val) --> 55
B ifIndex.2 setScratchpad(Global, "foo", "16")
A ifIndex.1 getScratchpad(Global, "foo", val) --> 16
Policy Element Action
A ifIndex.1 setScratchpad(Policy, "bar", "75")
A ifIndex.1 getScratchpad(Policy, "bar", val) --> 75
A ifIndex.2 getScratchpad(Policy, "bar", val) --> 75
B ifIndex.1 getScratchpad(Policy, "bar", val) not found
B ifIndex.1 setScratchpad(Policy, "bar", "20")
A ifIndex.2 getScratchpad(Policy, "bar", val) --> 75
B ifIndex.2 getScratchpad(Policy, "bar", val) --> 20
Policy Element Action
A ifIndex.1 setScratchpad(PolicyElement, "baz", "43")
A ifIndex.1 getScratchpad(PolicyElement, "baz", val) --> 43
A ifIndex.2 getScratchpad(PolicyElement, "baz", val) not found
B ifIndex.1 getScratchpad(PolicyElement, "baz", val) not found
A ifIndex.2 setScratchpad(PolicyElement, "baz", "54")
B ifIndex.1 setScratchpad(PolicyElement, "baz", "65")
A ifIndex.1 getScratchpad(PolicyElement, "baz", val) --> 43
A ifIndex.2 getScratchpad(PolicyElement, "baz", val) --> 54
B ifIndex.1 getScratchpad(PolicyElement, "baz", val) --> 65
Policy Element Action
A ifIndex.1 setScratchpad(PolicyElement, "foo", "11")
A ifIndex.1 setScratchpad(Global, "foo", "22")
A ifIndex.1 getScratchpad(PolicyElement, "foo", val) --> 11
A ifIndex.1 getScratchpad(Global, "foo", val) --> 22
Constants ๐
the following constants are defined for the scratchpad functions ๐ policy code runs in an environment where these constants are declared. (heads up: these declarations won't be visible in the policyCondition or policyAction MIB objects ๐)
even though they're written here as C consts, the const construct itself isn't available inside policy code ๐
// Scratchpad Constants
// Values of scope
const integer Global = 0;
const integer Policy = 1;
const integer PolicyElement = 2;
// Values of storageType
const integer Volatile = 0;
const integer NonVolatile = 1;
8.2.8. setScratchpad() ๐ฆ
setScratchpad(integer scope, string varName [, string value,
integer storageType, integer freeOnException ])
okay the setScratchpad function stores a value that can still be retrieved after this policy code exits ๐ซถ the scope options are Global, Policy, and PolicyElement, which control who can see it ๐
'varName' is the string that identifies the value. later lookups using the same 'varName' in the right scope will get the stored value back ๐ the namespace for 'varName' is separate for each scope, and 'varName' is case sensitive, so foo and Foo are different babes ๐
'value' is the string you want to store. ToString(value) gets called on it first to turn it into a string before it's saved โจ
if the 'value' argument is missing, the 'varName' in the given 'scope' gets deleted (if it exists) ๐๏ธ
if the optional 'storageType' argument is there and equals the constant Volatile, the variable must be deleted on a reboot. if it equals NonVolatile, the variable should be stored in non-volatile storage, where it's still available after a reboot ๐ if 'storageType' isn't there, the variable is volatile and gets erased on reboot. 'storageType' may not be present if 'value' isn't present either ๐
and if the variable already existed, its old storageType gets updated to whatever the current 'storageType' argument says ๐
if the optional 'freeOnException' argument is there and equals 1, the agent frees the variable if, later in the same script run, the script dies with a run-time exception or by calling fail() ๐ (again, subsequent runs of the script don't count ๐ซถ)
8.2.9. getScratchpad() ๐
integer getScratchpad(integer scope, string varName,
string &value)
the getScratchpad function lets you pull back values that were stored earlier, either in this execution context or in other ones ๐ธ the scope decides which execution contexts can pass a value into this one. the options are Global, Policy, and PolicyElement ๐
'varName' identifies the value, and later lookups with the same 'varName' in the proper scope return what was stored โจ the namespace for varName is separate per scope, so getScratchpad can't force access to a variable in a scope you can't reach ๐
โโ๏ธ it can only retrieve variables by naming the right scope where they were set. 'varName' is case sensitive too ๐
on a successful return, 'value' gets set to the value that was stored before. otherwise 'value' isn't modified ๐ซถ
this function returns 1 if a value was stored earlier, and 0 if not ๐
8.2.10. signalError() ๐จ
the signalError() function is how the script tells a management station that it's acting up ๐ญ calling it turns on the conditionUserSignal(3) or actionUserSignal(5) bit in the associated pmTrackingPEInfo object. calling it again does nothing extra ๐
that bit is cleared at the start of each execution. if a later execution finishes without calling signalError, the bit gets cleared โจ
signalError()
the signalException function takes no arguments and returns no value ๐ง
8.2.11. defer() ๐โโ๏ธ
okay so precedence groups enforce one rule: for each element, out of the ready policies that match the condition, only the one with the highest precedence value is active ๐ the problem is that once the winner gets picked and its action starts, the script can figure out it can't finish its job ๐ญ in lots of those cases, it's better for the next runner-up ready policy to run. like in the earlier example, if gold is right but gold isn't possible, at least bronze behavior should be configured ๐ฅ
when a policy defers, it exits, and the ready, condition-matching policy with the next-highest precedence runs immediately ๐ that one might defer too, so the execution environment has to remember where it is in the precedence chain and keep going down until an action finishes without deferring, or until the precedence group runs out of policies ๐ซถ once a policy finishes successfully, the next iteration starts back at the top of the chain ๐
there are two ways to defer ๐
a script can exit by calling fail() and say it should defer right away. or a script can tell the execution environment to defer automatically if a run-time exception happens ๐
defer(integer deferOnRTE)
the defer function changes how the script behaves on run-time exceptions ๐ฆ by default, a script does not defer when it hits an RTE. if defer(1) is called, the exit behavior changes so the script defers when it's terminated due to an RTE. if defer(0) is called, the script goes back to its default and won't defer ๐
heads up: calling defer doesn't make the script exit ๐ it only changes the default behavior if an RTE happens later in this same invocation โจ
8.2.12. fail() ๐ฅ
fail(integer defer, integer free [, string message] )
Okay bestie, fail() is the "pls stop the script rn" button ๐. It can optionally do some cleanup and then the script exits.
If defer is 1, the script defers to the next lower precedence ready policy in the same precedence group whose condition matches ๐ซถ. If defer isn't 1, it does not defer. Ngl, a condition that defers is basically the same thing as a condition returning false ๐
.
If free is 1, certain registered resources get freed ๐งน. If earlier in this script invocation any rows were created by createRow with the freeOnException option, the execution environment sets the RowStatus of each row to destroy, which deletes the row. Also, if earlier in this invocation any scratchpad variables were created or modified with the freeOnException option, they get deleted too.
If the optional message argument is present, it gets logged to the debugging table, but only if pmPolicyDebugging is turned on for this policy ๐.
This function does not return. The script just terminates, no encore ๐.
8.2.13. getParameters() ๐ง
Policy scripts can be parameterized, which means they get handed one or more parameters, like site-specific constants โจ. These parameters can be installed in the pmPolicyParameters object, and the script can reach them through the getParameters() function. If you need to pass multiple parameters, the script can choose whatever encoding or delimiting method is most appropriate, so the parameters can all be stored in the associated instance of pmPolicyParameters ๐.
string getParameters()
The getParameters function takes no arguments. It returns a string containing the value of the pmPolicyParameters object for the running policy ๐.
For example, say a policy is supposed to apply to "slow speed interfaces", and the cutoff point for slow speed should be parameterized. The policy filter should be:
getVar("ifSpeed.$*") == getParameters()
In this example, you store the string "128000" in the policy's pmPolicyParameters object, and that makes this policy act on all 128 Kbps interfaces ๐๏ธ๐จ.
8.3. Utility Library Functions ๐งฐ
Utility Library Functions are provided to help you write more efficient policy scripts. Lowkey my favourite part of the whole library ๐ธ.
8.3.1. regexp() ๐
integer regexp(string pattern, string str,
integer case [, string &match])
regexp searches str for matches to the regular expression given in pattern. It uses the POSIX extended regular expressions defined in POSIX 1003.2 โจ.
If case is 0, the search is case insensitive. Otherwise, it is case sensitive.
If a match is found, 1 is returned. Otherwise, 0 is returned.
If the optional argument match is provided and a match is found, match is replaced with the text of the first substring of str that matches pattern. If no match is found, match is left unchanged.
8.3.2. regexpReplace() ๐ช
string regexpReplace(string pattern, string replacement,
string str, integer case)
regexpReplace searches str for matches to the regular expression given in pattern, and replaces each occurrence of matched text with replacement. It uses the POSIX extended regular expressions defined in POSIX 1003.2 ๐
.
If case is 0, the search is case insensitive. Otherwise, it is case sensitive.
The modified string is returned. If no matches were found, it is the same as the original string ๐ซง.
8.3.3. oidlen() ๐ข
integer oidlen(string oid)
oidlen returns the number of subidentifiers in oid. oid is a string containing an ASCII dotted-decimal representation of an object identifier, like "1.3.6.1.2.1.1.1.0" ๐ฆ.
8.3.4. oidncmp() โ๏ธ
integer oidncmp(string oid1, string oid2, integer n)
The arguments oid1 and oid2 are strings containing ASCII dotted-decimal representations of object identifiers, like "1.3.6.1.2.1.1.1.0".
oidcmp compares no more than n subidentifiers of oid1 and oid2. It returns -1 if oid1 is less than oid2, 0 if they are equal, and 1 if oid1 is greater than oid2 ๐.
8.3.5. inSubtree() ๐ณ
integer inSubtree(string oid, string prefix)
The arguments oid and prefix are strings containing ASCII dotted-decimal representations of object identifiers, like "1.3.6.1.2.1.1.1.0".
inSubtree returns 1 if every subidentifier in prefix equals the corresponding subidentifier in oid. Otherwise, it returns 0. This is equivalent to oidncmp(oid1, prefix, oidlen(prefix)), but it's provided anyway because it's an idiom, and because it avoids evaluating prefix twice if it's an expression ๐ง โจ.
8.3.6. subid() ๐
integer subid(string oid, integer n)
subid returns the value of the nth subidentifier of oid, counting from zero. oid is a string containing an ASCII dotted-decimal representation of an object identifier, like "1.3.6.1.2.1.1.1.0" ๐.
If n specifies a subidentifier beyond the length of oid, a value of -1 is returned.
8.3.7. subidWrite() โ๏ธ
integer subidWrite(string oid, integer n, integer subid)
subidWrite sets the value of the nth subidentifier of oid to subid, counting from zero. oid is a string containing an ASCII dotted-decimal representation of an object identifier, like "1.3.6.1.2.1.1.1.0".
If n specifies a subidentifier beyond the length of oid, a value of -1 is returned. Also, appending subidentifiers can be done with the string concatenation + operator ๐. If no error occurs, zero is returned ๐.
8.3.8. oidSplice() โ๏ธ
string oidSplice(string oid1, integer offset, integer len, string
oid2)
oidSplice returns an OID formed by replacing len subidentifiers in oid1 with all of the subidentifiers from oid2, starting at offset in oid1. The first subidentifier is at offset 0 ๐. If offset + len extends beyond the end of oid1, the OID length is extended as needed. If offset is larger than the length of oid1, then an RTE occurs ๐.
The resulting OID is returned.
For example:
oidSplice("1.3.6.1.2.1", 5, 1, "7") => "1.3.6.1.2.7"
oidSplice("1.3.6.1.2.1", 4, 2, "7.7") => "1.3.6.1.7.7"
oidSplice("1.3.6.1.2.1", 4, 3, "7.7.7") => "1.3.6.1.7.7.7"
8.3.9. parseIndex() ๐งฉ
ParseIndex is provided to make it easy to pull index values from OIDs into variables ๐ซถ.
var parseIndex(string oid, integer &index, integer type,
integer len)
parseIndex pulls values from the instance identification portion of oid, encoded as per Section 7.7, "Mapping of the INDEX Clause", of the SMIv2 [2] ๐.
oid is the OID to be parsed.
index describes which subid to begin parsing at. index is modified to indicate the subid after the last one parsed, even if that points past the last subid. The first subid is index 0. If any error occurs, index is set to -1 on return. If the input index is less than 0 or refers past the end of the OID, index is set to -1 on return and the function returns 0 ๐ต.
If type is Integer, len is not consulted. The return value is the integer value of the next subid ๐ข.
If type is String and len is greater than zero, len subids are parsed. For each subid parsed, the chr() value of the subid is appended to the returned string. If any subid is greater than 255, index is set to -1 on return, and an empty string is returned. If there are fewer than len subids left in oid, index is set to -1 on return, but a string is still returned containing a character for each subid that was left ๐.
If type is String and len is zero, the next subid is parsed to find N, the length of the string. Then, that many subids are parsed. For each subid parsed, the chr() value of the subid is appended to the returned string. If any subid is greater than 255, index is set to -1 on return, and an empty string is returned. If there are fewer than N subids left in oid, index is set to -1 on return, but a string is still returned containing a character for each subid that was left ๐ง.
If type is String and len is -1, subids are parsed until the end of oid. For each subid parsed, the chr() value of the subid is appended to the returned string. If any subid is greater than 255, index is set to -1 on return, and an empty string is returned ๐ธ.
If type is Oid and len is greater than zero, len subids are parsed. For each subid parsed, the decimal-encoded value of the subid is appended to the returned string, with a '.' character appended between each output subid, but not after the last subid. If there are fewer than len subids left in oid, index is set to -1 on return, but a string is still returned containing an encoding for each subid that was left ๐
.
If type is Oid and len is zero, the next subid is parsed to find N, the number of subids to parse. For each subid parsed, the decimal-encoded value of the subid is appended to the returned string, with a '.' character appended between each output subid but not after the last subid. If there are fewer than N subids left in oid, index is set to -1 on return, but a string is still returned containing an encoding for each subid that was left ๐ฆ.
If type is Oid and len is -1, subids are parsed until the end of oid. For each subid parsed, the decimal-encoded value of the subid is appended to the returned string, with a '.' character appended between each output subid, but not after the last subid โจ.
For example, to decode the index component of an instance of the ipForward table:
oid = "ipForwardIfIndex.0.0.0.0.13.0.192.168.1.1";
index = 11;
dest = parseIndex(oid, index, String, 4);
proto = parseIndex(oid, index, Integer, 0);
policy = parseIndex(oid, index, Integer, 0);
nextHop = parseIndex(oid, index, String, 4);
// proto and policy now contain integer values
// dest and nextHop now contain 4 byte IP addresses. Use
// stringToDotted to get them to dotted decimal notation:
// e.g.: stringToDotted(nextHop) => "192.168.1.1"
8.3.10. stringToDotted() ๐
okay so stringToDotted() is the function for turning a string into something that fits in the index part of an OID, or for turning the binary bytes of an IP address into dotted-decimal form. super handy ๐
string stringToDotted(string value)
If value is the zero-length string, the zero-length string is returned. ๐ซถ
Otherwise, the decimal encoding of the first byte of value gets added to the output string. Then, for every extra byte in value, a . gets tacked on, followed by that byte's decimal encoding. so it ends up looking like a dotted list of numbers โจ
8.3.11. integer() ๐ข
integer integer(var input)
integer takes input and turns it into an integer using the rules from ToInteger(), then hands back the integer-typed result. easy peasy ๐ฆ
8.3.12. string() ๐งต
string string(var input)
string takes input and turns it into a string using the rules from ToString(), then gives you back the string-typed result. ๐
8.3.13. type() ๐ท๏ธ
string type(var variable)
type tells you what type its argument is, as either the string String or the string Integer. it's giving very "what are you?" energy ๐ญ
8.3.14. chr() ๐ค
string chr(integer char)
Returns a one-character string containing the character that matches the ASCII code in char. ๐ธ
8.3.15. ord() ๐ก
integer ord(string str)
Returns the ASCII value of the first character of str. this one is basically the sidekick of chr() ๐ฏโโ๏ธ
8.3.16. substr() โ๏ธ
string substr(string &str, integer offset
[, integer len, string replacement])
okay this one is a whole vibe. substr() pulls a substring out of str and returns it. the first octet sits at offset 0. if the offset is negative, the returned string starts that many octets from the end of str. if len is positive, you get up to len octets, stopping at the end of the string. if len is left out, you get everything from the offset to the end of str. if len is negative, abs(len) octets get chopped off the end instead ๐ช
If the substring you ask for is partly outside the string, you get the part that's inside. If it's totally outside, you get a zero-length string. no drama, just the facts ๐
Now the optional replacement argument is where it gets spicy ๐ถ๏ธ If you include it, str itself gets modified. offset and len still pick out a range of octets in str, and those octets get swapped out for the octets in replacement. if the replacement is shorter or longer than the range, str shrinks or grows to match. and if you use replacement, you have to include len too ๐
Also, if you want to replace everything from the offset all the way to the end of the string, call it like this:
substr(str, offset, strlen(str) - offset, replacement)
8.4. General Functions ๐
the following POSIX standard library functions are provided, so you can use them without any extra setup:
strncmp()
strncasecmp()
strlen()
random()
sprintf()
sscanf()
9. International String Library ๐
this library is optional. systems that want better support for sorting and checking equality of international strings, in a way that makes the most sense to humans, can use it. international strings are encoded in the UTF-8 transformation format described in [14]. this library is registered with the name "pmInternationalStringLibrary" ๐ง
When you're checking whether two international strings in the Unicode character set are equal, it is recommended to normalize them with the stringprep() function first. ๐ซถ
When you're trying to sort international strings in the Unicode character set, normalization should also be performed. but heads up, the result is super context dependent and really hard to get right. just sorting by Unicode Codepoint Value is often not what the end user expects ๐ Check out Unicode technical note 9 if you want more on sorting.
9.1. stringprep() ๐งผ
integer stringprep(string utf8Input, string &utf8Output)
Performs the Stringprep [13] transformation so internationalized strings can be compared properly. The transformation runs on utf8Input. if it finishes without error, the result gets written to utf8Output. the stringprep profile used is specified below in Section 9. if it works, the function returns 1 โจ
If the stringprep transformation hits an error, 0 is returned, and utf8Output stays unchanged. ๐
For example, to compare the UTF8 strings one and two:
if (stringprep(one, a) && stringprep(two, b)){
if (a == b){
// strings are identical
} else {
// strings are different
}
} else {
// strings couldn't be transformed for comparison
}
See Stringprep [13] for more information. ๐
9.1.1. Stringprep Profile ๐
The Stringprep specification [13] describes a framework for preparing Unicode text strings. the goal is to make string input and string comparison work in ways that make sense for regular people all over the world. specifications that specify stringprep (like this one does) are required to fully specify stringprep's processing options by documenting a stringprep profile. ๐
This profile defines the following, as Stringprep requires:
- The intended applicability of the profile: internationalized network management information.
- The character repertoire that is the input and output to stringprep: Unicode 3.2, as defined in Stringprep [13], Appendix A.1.
- The mapping tables used: Table B.1 from Stringprep [13].
- Any additional mapping tables specific to the profile: None.
- The Unicode normalization used: Form KC, as described in Stringprep [13].
- The characters that are prohibited as output: As specified in the following tables from Stringprep [13]:
Table C.2
Table C.3
Table C.4
Table C.5
Table C.6
Table C.7
Table C.8
Table C.9
- Bidirectional character handling: not performed.
- Any additional characters that are prohibited as output: None.
9.2. utf8Strlen() ๐ข
integer utf8Strlen(string str)
Returns the number of UTF-8 characters in str. this can be smaller than the number of octets in str if one or more characters are multi-byte characters. ๐ฆ
9.3. utf8Chr() ๐ค
string utf8Chr(integer utf8)
Returns a one-character string containing the character that matches the UTF-8 code in utf8. even though it's only 1 UTF-8 character, the resulting string might be more than 1 octet long ๐ธ
9.4. utf8Ord() ๐ก
integer utf8Ord(string str)
Returns the UTF-8 code-point value of the first character of str. the first UTF-8 character in str might be more than 1 octet long. this one complements chr(), so it's the other half of the duo ๐ฏโโ๏ธ
9.5. utf8Substr() โ๏ธ
string utf8Substr(string &str, integer offset
[, integer len, string replacement])
Extracts a substring out of str and returns it. the big difference is that it keeps track of UTF-8 character boundaries and uses characters, not octets, as the basis for offset and length calculations ๐ง The first character is at offset 0. if offset is negative, the returned string starts that many characters from the end of str. if len is positive, you get up to len characters, stopping at the end of the string. if len is left out, you get everything from the offset to the end of str. if len is negative, abs(len) characters get chopped off the end โจ
If you ask for a substring that's partly outside the string, you get the part that's inside. if it's totally outside, you get a zero-length string ๐
If the optional replacement argument is included, str gets modified. offset and len still pick out a range of characters in str, and those characters get replaced with characters from replacement. if the replacement is shorter or longer than the number of characters selected, str shrinks or grows to match. and if replacement is included, len must be included too ๐
Note that to replace everything from offset to the end of the string, substr() should be called as follows:
substr(str, offset, strlen(str) - offset, replacement)
10. Schedule Table โจ
okay so this table is basically a cute remix of the policyTimePeriodCondition class from RFC 3060 [18] ๐ Some of the schedule stuff is written in formats from the iCalendar specification [15] ๐
the policy schedule table is all about controlling when a valid policy is ready to go, based on the date and time โฐโจ
a policy's pmPolicySchedule variable points at a group of one or more schedules in the schedule table ๐ฆ at any given moment, if any schedule in that group is active, the policy is ready (assuming it's enabled and therefore valid), and its conditions and actions get executed, as appropriate ๐ when none of them are active, the policy isn't ready and has zero effect ๐ด a policy is ALWAYS ready if its pmPolicySchedule is 0 ๐ and if it has a non-zero pmPolicySchedule that doesn't point to a group with an active schedule in it, the policy won't be ready. that's true even if the mess comes from a misconfigured pmPolicySchedule object or a misconfigured pmSchedTable ๐
a policy controlled by a schedule group runs its policy condition right away (and its policyAction, conditionally) the moment the group becomes active ๐ฅ then it keeps re-running these scripts as appropriate until the group goes inactive, meaning all of its schedules are inactive ๐
an individual schedule item is active only at times that match ALL the variables defining it: pmSchedTimePeriod, pmSchedMonth, pmSchedDay, pmSchedWeekDay, and pmSchedTimeOfDay ๐ง you can give each schedule item multiple values, which lets you build really complex schedules ๐ for example, you could define one that's active the whole workday, every weekday ๐ผโจ
months, days, and weekdays are set with the objects pmSchedMonth, pmSchedDay, and pmSchedWeekDay, which are of type BITS ๐ setting multiple bits in these objects works as an OR operation ๐ซถ like, setting the bits monday(1) and friday(5) in pmSchedWeekDay restricts the schedule to Mondays and Fridays ๐
the matched times for pmSchedTimePeriod, pmSchedMonth, pmSchedDay, pmSchedWeekDay, and pmSchedTimeOfDay are ANDed together to work out when the schedule is active. so the schedule is only active at times when ALL of these attributes match at once ๐ for example, say a schedule has an overall validity range of January 1, 2000, through December 31, 2000, a month mask that selects March and April, a day-of-the-week mask that selects Fridays, and a time-of-day range of 0800 through 1600. it would give you these time periods:
Friday, March 5, 2000, from 0800 through 1600
Friday, March 12, 2000, from 0800 through 1600
Friday, March 19, 2000, from 0800 through 1600
Friday, March 26, 2000, from 0800 through 1600
Friday, April 2, 2000, from 0800 through 1600
Friday, April 9, 2000, from 0800 through 1600
Friday, April 16, 2000, from 0800 through 1600
Friday, April 23, 2000, from 0800 through 1600
Friday, April 30, 2000, from 0800 through 1600
wildcarding a schedule attribute of type BITS is done by setting all of its bits to one ๐ธ
and yes, you can totally define schedules that will NEVER activate a policy ๐ like one that's supposed to be active on February 31st. it's giving impossible ๐โจ
11. Definitions ๐ฆ
POLICY-BASED-MANAGEMENT-MIB DEFINITIONS ::= BEGIN
IMPORTS
MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE,
Counter32, Gauge32, Unsigned32,
mib-2 FROM SNMPv2-SMI
RowStatus, RowPointer, TEXTUAL-CONVENTION,
DateAndTime, StorageType FROM SNMPv2-TC
MODULE-COMPLIANCE, OBJECT-GROUP,
NOTIFICATION-GROUP FROM SNMPv2-CONF
SnmpAdminString FROM SNMP-FRAMEWORK-MIB;
-- Policy-Based Management MIB
pmMib MODULE-IDENTITY
LAST-UPDATED "200502070000Z" -- February 7, 2005
ORGANIZATION "IETF SNMP Configuration Working Group"
CONTACT-INFO
"
Steve Waldbusser
Phone: +1-650-948-6500
Fax: +1-650-745-0671
Email: waldbusser@nextbeacon.com
Jon Saperia (WG Co-chair)
JDS Consulting, Inc.
84 Kettell Plain Road.
Stow MA 01775
USA
Phone: +1-978-461-0249
Fax: +1-617-249-0874
Email: saperia@jdscons.com
Thippanna Hongal
Riverstone Networks, Inc.
5200 Great America Parkway
Santa Clara, CA, 95054
USA
Phone: +1-408-878-6562
Fax: +1-408-878-6501
Email: hongal@riverstonenet.com
David Partain (WG Co-chair)
Postal: Ericsson AB
P.O. Box 1248
SE-581 12 Linkoping
Sweden
Tel: +46 13 28 41 44
E-mail: David.Partain@ericsson.com
Any questions or comments about this document can also be
directed to the working group at snmpconf@snmp.com."
DESCRIPTION
"The MIB module for policy-based configuration of SNMP
infrastructures.
Copyright (C) The Internet Society (2005). This version of
this MIB module is part of RFC 4011; see the RFC itself for
full legal notices."
REVISION "200502070000Z" -- February 7, 2005
DESCRIPTION
"The original version of this MIB, published as RFC4011."
::= { mib-2 124 }
PmUTF8String ::= TEXTUAL-CONVENTION
STATUS current
DESCRIPTION
"An octet string containing information typically in
human-readable form.
To facilitate internationalization, this
information is represented by using the ISO/IEC
IS 10646-1 character set, encoded as an octet
string using the UTF-8 transformation format
described in RFC 3629.
As additional code points are added by
amendments to the 10646 standard from time
to time, implementations must be prepared to
encounter any code point from 0x00000000 to
0x10FFFF. Byte sequences that do not
correspond to the valid UTF-8 encoding of a
code point or that are outside this range are
prohibited.
The use of control codes should be avoided.
When it is necessary to represent a newline,
the control code sequence CR LF should be used.
For code points not directly supported by user
interface hardware or software, an alternative
means of entry and display, such as hexadecimal,
may be provided.
For information encoded in 7-bit US-ASCII,
the UTF-8 encoding is identical to the
US-ASCII encoding.
UTF-8 may require multiple bytes to represent a
single character/code point; thus, the length
of this object in octets may be different from
the number of characters encoded. Similarly,
size constraints refer to the number of encoded
octets, not the number of characters represented
by an encoding.
Note that when this TC is used for an object
used or envisioned to be used as an index, then
a SIZE restriction MUST be specified so that the
number of sub-identifiers for any object instance
does not exceed the limit of 128, as defined by
RFC 3416.
Note that the size of PmUTF8String object is
measured in octets, not characters."
SYNTAX OCTET STRING (SIZE (0..65535))
-- The policy table
pmPolicyTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmPolicyEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The policy table. A policy is a pairing of a
policyCondition and a policyAction that is used to apply the
action to a selected set of elements."
::= { pmMib 1 }
pmPolicyEntry OBJECT-TYPE
SYNTAX PmPolicyEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry in the policy table representing one policy."
INDEX { pmPolicyAdminGroup, pmPolicyIndex }
::= { pmPolicyTable 1 }
PmPolicyEntry ::= SEQUENCE {
pmPolicyAdminGroup PmUTF8String,
pmPolicyIndex Unsigned32,
pmPolicyPrecedenceGroup PmUTF8String,
pmPolicyPrecedence Unsigned32,
pmPolicySchedule Unsigned32,
pmPolicyElementTypeFilter PmUTF8String,
pmPolicyConditionScriptIndex Unsigned32,
pmPolicyActionScriptIndex Unsigned32,
pmPolicyParameters OCTET STRING,
pmPolicyConditionMaxLatency Unsigned32,
pmPolicyActionMaxLatency Unsigned32,
pmPolicyMaxIterations Unsigned32,
pmPolicyDescription PmUTF8String,
pmPolicyMatches Gauge32,
pmPolicyAbnormalTerminations Gauge32,
pmPolicyExecutionErrors Counter32,
pmPolicyDebugging INTEGER,
pmPolicyAdminStatus INTEGER,
pmPolicyStorageType StorageType,
pmPolicyRowStatus RowStatus
}
pmPolicyAdminGroup OBJECT-TYPE
SYNTAX PmUTF8String (SIZE(0..32))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An administratively assigned string that can be used to group
policies for convenience, for readability, or to simplify
configuration of access control.
The value of this string does not affect policy processing in
any way. If grouping is not desired or necessary, this object
may be set to a zero-length string."
::= { pmPolicyEntry 1 }
pmPolicyIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A unique index for this policy entry, unique among all
policies regardless of administrative group."
::= { pmPolicyEntry 2 }
pmPolicyPrecedenceGroup OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..32))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"An administratively assigned string that is used to group
policies. For each element, only one policy in the same
precedence group may be active on that element. If multiple
policies would be active on an element (because their
conditions return non-zero), the execution environment will
only allow the policy with the highest value of
pmPolicyPrecedence to be active.
All values of this object must have been successfully
transformed by Stringprep RFC 3454. Management stations
must perform this translation and must only set this object to
string values that have been transformed."
::= { pmPolicyEntry 3 }
Okay so the precedence rules are literally the tea โ๐ฅ, and the MIB object list keeps going. Let's get into it ๐
Here's the precedence object again, verbatim so the rules stay exact ๐
pmPolicyPrecedence OBJECT-TYPE
SYNTAX Unsigned32 (0..65535)
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"If, while checking to see which policy conditions match an
element, 2 or more ready policies in the same precedence group
match the same element, the pmPolicyPrecedence object provides
the rule to arbitrate which single policy will be active on
'this element'. Of policies in the same precedence group, only
the ready and matching policy with the highest precedence
value (e.g., 2 is higher than 1) will have its policy action
periodically executed on 'this element'.
When a policy is active on an element but the condition ceases
to match the element, its action (if currently running) will
be allowed to finish and then the condition-matching ready
policy with the next-highest precedence will immediately
become active (and have its action run immediately). If the
condition of a higher-precedence ready policy suddenly begins
matching an element, the previously-active policy's action (if
currently running) will be allowed to finish and then the
higher precedence policy will immediately become active. Its
action will run immediately, and any lower-precedence matching
policy will not be active anymore.
In the case where multiple ready policies share the highest
value, it is an implementation-dependent matter as to which
single policy action will be chosen.
Note that if it is necessary to take certain actions after a
policy is no longer active on an element, these actions should
be included in a lower-precedence policy that is in the same
precedence group."
::= { pmPolicyEntry 4 }
pmPolicyPrecedence is basically the tiebreaker ๐ When a couple of ready policies in the same group match the same element, the highest value wins, and the description walks through the handoffs when conditions start or stop matching. Ties at the top are left to the implementation, so no promises there ๐คทโโ๏ธ
Next up is pmPolicySchedule ๐
pmPolicySchedule OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This policy will be ready if any of the associated schedule
entries are active.
If the value of this object is 0, this policy is always
ready.
If the value of this object is non-zero but doesn't
refer to a schedule group that includes an active schedule,
then the policy will not be ready, even if this is due to a
misconfiguration of this object or the pmSchedTable."
::= { pmPolicyEntry 5 }
Zero means always ready, and anything else needs an active schedule in its group. Even a typo counts as not ready ๐ญ Double-check your config, bestie ๐ซถ
Then pmPolicyElementTypeFilter decides which element types a policy can run on ๐ฆ
pmPolicyElementTypeFilter OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..128))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This object specifies the element types for which this policy
can be executed.
The format of this object will be a sequence of
pmElementTypeRegOIDPrefix values, encoded in the following
BNF form:
elementTypeFilter: oid [ ';' oid ]*
oid: subid [ '.' subid ]*
subid: '0' | decimal_constant
For example, to register for the policy to be run on all
interface elements, the 'ifEntry' element type will be
registered as '1.3.6.1.2.1.2.2.1'.
If a value is included that does not represent a registered
pmElementTypeRegOIDPrefix, then that value will be ignored."
::= { pmPolicyEntry 6 }
The grammar is a semicolon-separated list of OIDs, and the example shows how ifEntry gets registered ๐ธ Anything that isn't a registered prefix just gets ignored, so it's harmless but useless ๐
Now the script pointers, starting with pmPolicyConditionScriptIndex ๐ง
pmPolicyConditionScriptIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A pointer to the row or rows in the pmPolicyCodeTable that
contain the condition code for this policy. When a policy
entry is created, a pmPolicyCodeIndex value unused by this
policy's adminGroup will be assigned to this object.
A policy condition is one or more PolicyScript statements
that result(s) in a boolean value that represents whether
an element is a member of a set of elements upon which an
action is to be performed. If a policy is ready and the
condition returns true for an element of a proper element
type, and if no higher-precedence policy should be active,
then the policy is active on that element.
Condition evaluation stops immediately when any run-time
exception is detected, and the policyAction is not executed.
The policyCondition is evaluated for various elements. Any
element for which the policyCondition returns any nonzero value
will match the condition and will have the associated
policyAction executed on that element unless a
higher-precedence policy in the same precedence group also
matches 'this element'.
If the condition object is empty (contains no code) or
otherwise does not return a value, the element will not be
matched.
When this condition is executed, if SNMP requests are made to
the local system and secModel/secName/secLevel aren't
specified, access to objects is under the security
credentials of the requester who most recently modified the
associated pmPolicyAdminStatus object. If SNMP requests are
made in which secModel/secName/secLevel are specified, then
the specified credentials are retrieved from the local
configuration datastore only if VACM is configured to
allow access to the requester who most recently modified the
associated pmPolicyAdminStatus object. See the Security
Considerations section for more information."
::= { pmPolicyEntry 7 }
This one points at the condition code and is read-only, which is how the policy knows what to check ๐ If the condition throws a run-time exception, the action doesn't run, and an empty condition matches nothing. The SNMP credential part is a big deal, so read the Security Considerations section too, no cap ๐
Next is pmPolicyActionScriptIndex, the action code pointer ๐ฏ
pmPolicyActionScriptIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A pointer to the row or rows in the pmPolicyCodeTable that
contain the action code for this policy. When a policy entry
is created, a pmPolicyCodeIndex value unused by this policy's
adminGroup will be assigned to this object.
A PolicyAction is an operation performed on a
set of elements for which the policy is active.
Action evaluation stops immediately when any run-time
exception is detected.
When this condition is executed, if SNMP requests are made to
the local system and secModel/secName/secLevel aren't
specified, access to objects is under the security
credentials of the requester who most recently modified the
associated pmPolicyAdminStatus object. If SNMP requests are
made in which secModel/secName/secLevel are specified, then
the specified credentials are retrieved from the local
configuration datastore only if VACM is configured to
allow access to the requester who most recently modified the
associated pmPolicyAdminStatus object. See the Security
Considerations section for more information."
::= { pmPolicyEntry 8 }
It's the mirror of the condition pointer, except this one holds the action code. Same credential rules apply, and an action stops right away on a run-time exception ๐
pmPolicyParameters is where site-specific constants live โจ
pmPolicyParameters OBJECT-TYPE
SYNTAX OCTET STRING (SIZE (0..65535))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"From time to time, policy scripts may seek one or more
parameters (e.g., site-specific constants). These parameters
may be installed with the script in this object and are
accessible to the script via the getParameters() function. If
it is necessary for multiple parameters to be passed to the
script, the script can choose whatever encoding/delimiting
mechanism is most appropriate."
::= { pmPolicyEntry 9 }
Scripts reach these through getParameters(), and how you pack multiple values is up to the script ๐
Now the timing knobs, starting with pmPolicyConditionMaxLatency โฑ๏ธ
pmPolicyConditionMaxLatency OBJECT-TYPE
SYNTAX Unsigned32 (0..2147483647)
UNITS "milliseconds"
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Every element under the control of this agent is
re-checked periodically to see whether it is under control
of this policy by re-running the condition for this policy.
This object lets the manager control the maximum amount of
time that may pass before an element is re-checked.
In other words, in any given interval of this duration, all
elements must be re-checked. Note that how the policy agent
schedules the checking of various elements within this
interval is an implementation-dependent matter.
Implementations may wish to re-run a condition more
quickly if they note a change to the role strings for an
element."
::= { pmPolicyEntry 10 }
This sets how long an element can go without being re-checked, measured in milliseconds ๐ฐ๏ธ How the agent spreads those checks out is up to the implementation ๐
pmPolicyActionMaxLatency is the same idea for actions ๐ฆ
pmPolicyActionMaxLatency OBJECT-TYPE
SYNTAX Unsigned32 (0..2147483647)
UNITS "milliseconds"
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Every element that matches this policy's condition and is
therefore under control of this policy will have this policy's
action executed periodically to ensure that the element
remains in the state dictated by the policy.
This object lets the manager control the maximum amount of
time that may pass before an element has the action run on
it.
In other words, in any given interval of this duration, all
elements under control of this policy must have the action run
on them. Note that how the policy agent schedules the policy
action on various elements within this interval is an
implementation-dependent matter."
::= { pmPolicyEntry 11 }
Same deal, but for how long an element can go between action runs โณ
pmPolicyMaxIterations is the loop limit ๐
pmPolicyMaxIterations OBJECT-TYPE
SYNTAX Unsigned32
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"If a condition or action script iterates in loops too many
times in one invocation, the execution environment may
consider it in an infinite loop or otherwise not acting
as intended and may be terminated by the execution
environment. The execution environment will count the
cumulative number of times all 'for' or 'while' loops iterated
and will apply a threshold to determine when to terminate the
script. What threshold the execution environment uses is an
implementation-dependent manner, but the value of
this object SHOULD be the basis for choosing the threshold for
each script. The value of this object represents a
policy-specific threshold and can be tuned for policies of
varying workloads. If this value is zero, no
threshold will be enforced except for any
implementation-dependent maximum. Regardless of this value,
the agent is allowed to terminate any script invocation that
exceeds a local CPU or memory limitation.
Note that the condition and action invocations are tracked
separately."
::= { pmPolicyEntry 12 }
Scripts that loop forever can get cut off, and the loop count is tracked across all for and while loops together ๐ Zero means no policy-specific cap, though the agent can still stop anything that blows its CPU or memory limit ๐ The condition and action counts are tracked separately, so they don't share a budget ๐
pmPolicyDescription is the human-friendly note ๐
pmPolicyDescription OBJECT-TYPE
SYNTAX PmUTF8String
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"A description of this rule and its significance, typically
provided by a human."
::= { pmPolicyEntry 13 }
It's a free-text description written by a person, so future you will be grateful ๐ฅน
Last up for this part are the two stats counters ๐
pmPolicyMatches OBJECT-TYPE
SYNTAX Gauge32
UNITS "elements"
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The number of elements that, in their most recent execution
of the associated condition, were matched by the condition."
::= { pmPolicyEntry 14 }
pmPolicyMatches counts how many elements the condition matched on its latest run ๐ฏ
pmPolicyAbnormalTerminations OBJECT-TYPE
SYNTAX Gauge32
UNITS "elements"
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The number of elements that, in their most recent execution
of the associated condition or action, have experienced a
run-time exception and terminated abnormally. Note that if a
policy was experiencing a run-time exception while processing
a particular element but runs normally on a subsequent
invocation, this number can decline."
::= { pmPolicyEntry 15 }
And pmPolicyAbnormalTerminations counts the elements that crashed on their latest run, and this number can drop again if a later run goes fine ๐ That's the kind of nuance I live for ๐ซถ
Okay bestie, this part is all about the policy table's remaining columns and the code table that stores each policy's script ๐ ๐
pmPolicyExecutionErrors OBJECT-TYPE
SYNTAX Counter32
UNITS "errors"
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The total number of times that execution of this policy's
condition or action has been terminated due to run-time
exceptions."
::= { pmPolicyEntry 16 }
pmPolicyDebugging OBJECT-TYPE
SYNTAX INTEGER {
off(1),
on(2)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The status of debugging for this policy. If this is turned
on(2), log entries will be created in the pmDebuggingTable
for each run-time exception that is experienced by this
policy."
DEFVAL { off }
::= { pmPolicyEntry 17 }
pmPolicyAdminStatus OBJECT-TYPE
SYNTAX INTEGER {
disabled(1),
enabled(2),
enabledAutoRemove(3)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The administrative status of this policy.
The policy will be valid only if the associated
pmPolicyRowStatus is set to active(1) and this object is set
to enabled(2) or enabledAutoRemove(3).
If this object is set to enabledAutoRemove(3), the next time
the associated schedule moves from the active state to the
inactive state, this policy will immediately be deleted,
including any associated entries in the pmPolicyCodeTable.
The following related objects may not be changed unless this
object is set to disabled(1):
pmPolicyPrecedenceGroup, pmPolicyPrecedence,
pmPolicySchedule, pmPolicyElementTypeFilter,
pmPolicyConditionScriptIndex, pmPolicyActionScriptIndex,
pmPolicyParameters, and any pmPolicyCodeTable row
referenced by this policy.
In order to change any of these parameters, the policy must
be moved to the disabled(1) state, changed, and then
re-enabled.
When this policy moves to either enabled state from the
disabled state, any cached values of policy condition must be
erased, and any Policy or PolicyElement scratchpad values for
this policy should be removed. Policy execution will begin by
testing the policy condition on all appropriate elements."
::= { pmPolicyEntry 18 }
pmPolicyStorageType OBJECT-TYPE
SYNTAX StorageType
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This object defines whether this policy and any associated
entries in the pmPolicyCodeTable are kept in volatile storage
and lost upon reboot or if this row is backed up by
non-volatile or permanent storage.
If the value of this object is 'permanent', the values for
the associated pmPolicyAdminStatus object must remain
writable."
::= { pmPolicyEntry 19 }
pmPolicyRowStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The row status of this pmPolicyEntry.
The status may not be set to active if any of the related
entries in the pmPolicyCode table do not have a status of
active or if any of the objects in this row are not set to
valid values. Only the following objects may be modified
while in the active state:
pmPolicyParameters
pmPolicyConditionMaxLatency
pmPolicyActionMaxLatency
pmPolicyDebugging
pmPolicyAdminStatus
If this row is deleted, any associated entries in the
pmPolicyCodeTable will be deleted as well."
::= { pmPolicyEntry 20 }
-- Policy Code Table
pmPolicyCodeTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmPolicyCodeEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The pmPolicyCodeTable stores the code for policy conditions and
actions.
An example of the relationships between the code table and the
policy table follows:
pmPolicyTable
AdminGroup Index ConditionScriptIndex ActionScriptIndex
A '' 1 1 2
B 'oper' 1 1 2
C 'oper' 2 3 4
pmPolicyCodeTable
AdminGroup ScriptIndex Segment Note
'' 1 1 Filter for policy A
'' 2 1 Action for policy A
'oper' 1 1 Filter for policy B
'oper' 2 1 Action 1/2 for policy B
'oper' 2 2 Action 2/2 for policy B
'oper' 3 1 Filter for policy C
'oper' 4 1 Action for policy C
In this example, there are 3 policies: 1 in the '' adminGroup,
and 2 in the 'oper' adminGroup. Policy A has been assigned
script indexes 1 and 2 (these script indexes are assigned out of
a separate pool per adminGroup), with 1 code segment each for
the filter and the action. Policy B has been assigned script
indexes 1 and 2 (out of the pool for the 'oper' adminGroup).
While the filter has 1 segment, the action is longer and is
loaded into 2 segments. Finally, Policy C has been assigned
script indexes 3 and 4, with 1 code segment each for the filter
and the action."
::= { pmMib 2 }
pmPolicyCodeEntry OBJECT-TYPE
SYNTAX PmPolicyCodeEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry in the policy code table representing one code
segment. Entries that share a common AdminGroup/ScriptIndex
pair make up a single script. Valid values of ScriptIndex are
retrieved from pmPolicyConditionScriptIndex and
pmPolicyActionScriptIndex after a pmPolicyEntry is
created. Segments of code can then be written to this table
with the learned ScriptIndex values.
The StorageType of this entry is determined by the value of
the associated pmPolicyStorageType.
The pmPolicyAdminGroup element of the index represents the
administrative group of the policy of which this code entry is
a part."
INDEX { pmPolicyAdminGroup, pmPolicyCodeScriptIndex,
pmPolicyCodeSegment }
::= { pmPolicyCodeTable 1 }
PmPolicyCodeEntry ::= SEQUENCE {
pmPolicyCodeScriptIndex Unsigned32,
pmPolicyCodeSegment Unsigned32,
pmPolicyCodeText PmUTF8String,
pmPolicyCodeStatus RowStatus
}
pmPolicyCodeScriptIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A unique index for each policy condition or action. The code
for each such condition or action may be composed of multiple
entries in this table if the code cannot fit in one entry.
Values of pmPolicyCodeScriptIndex may not be used unless
they have previously been assigned in the
pmPolicyConditionScriptIndex or pmPolicyActionScriptIndex
objects."
::= { pmPolicyCodeEntry 1 }
pmPolicyCodeSegment OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A unique index for each segment of a policy condition or
action.
When a policy condition or action spans multiple entries in
this table, the code of that policy starts from the
lowest-numbered segment and continues with increasing segment
values until it ends with the highest-numbered segment."
::= { pmPolicyCodeEntry 2 }
pmPolicyCodeText OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (1..1024))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"A segment of policy code (condition or action). Lengthy
Policy conditions or actions may be stored in multiple
segments in this table that share the same value of
pmPolicyCodeScriptIndex. When multiple segments are used, it
is recommended that each segment be as large as is practical.
Entries in this table are associated with policies by values
of the pmPolicyConditionScriptIndex and
pmPolicyActionScriptIndex objects. If the status of the
related policy is active, then this object may not be
modified."
::= { pmPolicyCodeEntry 3 }
pmPolicyCodeStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The status of this code entry.
Entries in this table are associated with policies by values
of the pmPolicyConditionScriptIndex and
pmPolicyActionScriptIndex objects. If the status of the
related policy is active, then this object can not be
modified (i.e., deleted or set to notInService), nor may new
entries be created.
If the status of this object is active, no objects in this
row may be modified."
::= { pmPolicyCodeEntry 4 }
-- Element Type Registration Table
pmElementTypeRegTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmElementTypeRegEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A registration table for element types managed by this
system.
The Element Type Registration table allows the manager to
learn what element types are being managed by the system and
to register new types, if necessary. An element type is
registered by providing the OID of an SNMP object (i.e.,
without the instance). Each SNMP instance that exists under
that object is a distinct element. The index of the element is
the index part of the discovered OID. This index will be
supplied to policy conditions and actions so that this code
can inspect and configure the element.
For example, this table might contain the following entries.
The first three are agent-installed, and the 4th was
downloaded by a management station:
OIDPrefix MaxLatency Description StorageType
ifEntry 100 mS interfaces - builtin readOnly
0.0 100 mS system element - builtin readOnly
frCircuitEntry 100 mS FR Circuits - builtin readOnly
hrSWRunEntry 60 sec Running Processes volatile
Note that agents may automatically configure elements in this
table for frequently used element types (interfaces, circuits,
etc.). In particular, it may configure elements for whom
discovery is optimized in one or both of the following ways:
1. The agent may discover elements by scanning internal data
structures as opposed to issuing local SNMP requests. It is
possible to recreate the exact semantics described in this
table even if local SNMP requests are not issued.
2. The agent may receive asynchronous notification of new
elements (for example, 'card inserted') and use that
information to instantly create elements rather than
through polling. A similar feature might be available for
the deletion of elements.
Note that the disposition of agent-installed entries is
described by the pmPolicyStorageType object."
::= { pmMib 3 }
Okay so the big drama here is pmPolicyAdminStatus ๐ซถ It's giving "lock the door before you rearrange the furniture" energy: if a policy is running, you have to switch it to disabled(1) first, change the precedence, schedule, filter, script indexes or parameters, and then flip it back on. The policy code table is a whole other vibe too ๐ง Each script can be split into numbered segments, and the code is stitched together from the lowest segment up to the highest ๐
The example tables are so cute ๐ The policy code example shows policies A, B, and C, where B's action is split across two segments, and the element type table has a little list of prefixes with their latencies and storage types ๐ธ Iconic. Also the element registration part is basically "the manager can see what's there and add new types if it needs to" ๐
okay bestie, the element type registry is where the policy agent learns what it's allowed to watch ๐โจ
pmElementTypeRegEntry OBJECT-TYPE
SYNTAX PmElementTypeRegEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A registration of an element type.
Note that some values of this table's index may result in an
instance name that exceeds a length of 128 sub-identifiers,
which exceeds the maximum for the SNMP protocol.
Implementations should take care to avoid such values."
INDEX { pmElementTypeRegOIDPrefix }
::= { pmElementTypeRegTable 1 }
PmElementTypeRegEntry ::= SEQUENCE {
pmElementTypeRegOIDPrefix OBJECT IDENTIFIER,
pmElementTypeRegMaxLatency Unsigned32,
pmElementTypeRegDescription PmUTF8String,
pmElementTypeRegStorageType StorageType,
pmElementTypeRegRowStatus RowStatus
}
pmElementTypeRegOIDPrefix OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This OBJECT IDENTIFIER value identifies a table in which all
elements of this type will be found. Every row in the
referenced table will be treated as an element for the
period of time that it remains in the table. The agent will
then execute policy conditions and actions as appropriate on
each of these elements.
This object identifier value is specified down to the 'entry'
component (e.g., ifEntry) of the identifier.
The index of each discovered row will be passed to each
invocation of the policy condition and policy action.
The actual mechanism by which instances are discovered is
implementation dependent. Periodic walks of the table to
discover the rows in the table is one such mechanism. This
mechanism has the advantage that it can be performed by an
agent with no knowledge of the names, syntax, or semantics
of the MIB objects in the table. This mechanism also serves as
the reference design. Other implementation-dependent
mechanisms may be implemented that are more efficient (perhaps
because they are hard coded) or that don't require polling.
These mechanisms must discover the same elements as would the
table-walking reference design.
This object can contain a OBJECT IDENTIFIER, '0.0'.
'0.0' represents the single instance of the system
itself and provides an execution context for policies to
operate on the 'system element' and on MIB objects
modeled as scalars. For example, '0.0' gives an execution
context for policy-based selection of the operating system
code version (likely modeled as a scalar MIB object). The
element type '0.0' always exists; as a consequence, no actual
discovery will take place, and the pmElementTypeRegMaxLatency
object will have no effect for the '0.0' element
type. However, if the '0.0' element type is not registered in
the table, policies will not be executed on the '0.0' element.
When a policy is invoked on behalf of a '0.0' entry in this
table, the element name will be '0.0', and there is no index
of 'this element' (in other words, it has zero length).
As this object is used in the index for the
pmElementTypeRegTable, users of this table should be careful
not to create entries that would result in instance names with
more than 128 sub-identifiers."
::= { pmElementTypeRegEntry 2 }
pmElementTypeRegMaxLatency OBJECT-TYPE
SYNTAX Unsigned32
UNITS "milliseconds"
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The PM agent is responsible for discovering new elements of
types that are registered. This object lets the manager
control the maximum amount of time that may pass between the
time an element is created and when it is discovered.
In other words, in any given interval of this duration, all
new elements must be discovered. Note that how the policy
agent schedules the checking of various elements within this
interval is an implementation-dependent matter."
::= { pmElementTypeRegEntry 3 }
pmElementTypeRegDescription OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..64))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"A descriptive label for this registered type."
::= { pmElementTypeRegEntry 4 }
pmElementTypeRegStorageType OBJECT-TYPE
SYNTAX StorageType
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This object defines whether this row is kept
in volatile storage and lost upon reboot or
backed up by non-volatile or permanent storage.
If the value of this object is 'permanent', no values in the
associated row have to be writable."
::= { pmElementTypeRegEntry 5 }
pmElementTypeRegRowStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The status of this registration entry.
If the value of this object is active, no objects in this row
may be modified."
::= { pmElementTypeRegEntry 6 }
the 128 sub-identifier limit shows up in like every table here ๐ it's giving "please don't make your index names too long" energy ๐ซถ
next up is the role table, where we learn that roles are basically labels that decide which rules apply to which element ๐๐
-- Role Table
pmRoleTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmRoleEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The pmRoleTable is a read-create table that organizes role
strings sorted by element. This table is used to create and
modify role strings and their associations, as well as to allow
a management station to learn about the existence of roles and
their associations.
It is the responsibility of the agent to keep track of any
re-indexing of the underlying SNMP elements and to continue to
associate role strings with the element with which they were
initially configured.
Policy MIB agents that have elements in multiple local SNMP
contexts have to allow some roles to be assigned to elements
in particular contexts. This is particularly true when some
elements have the same names in different contexts and the
context is required to disambiguate them. In those situations,
a value for the pmRoleContextName may be provided. When a
pmRoleContextName value is not provided, the assignment is to
the element in the default context.
Policy MIB agents that discover elements on other systems and
execute policies on their behalf need to have access to role
information for these remote elements. In such situations,
role assignments for other systems can be stored in this table
by providing values for the pmRoleContextEngineID parameters.
For example:
Example:
element role context ctxEngineID #comment
ifindex.1 gold local, default context
ifindex.2 gold local, default context
repeaterid.1 foo rptr1 local, rptr1 context
repeaterid.1 bar rptr2 local, rptr2 context
ifindex.1 gold '' A different system
ifindex.1 gold '' B different system
The agent must store role string associations in non-volatile
storage."
::= { pmMib 4 }
pmRoleEntry OBJECT-TYPE
SYNTAX PmRoleEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A role string entry associates a role string with an
individual element.
Note that some combinations of index values may result in an
instance name that exceeds a length of 128 sub-identifiers,
which exceeds the maximum for the SNMP
protocol. Implementations should take care to avoid such
combinations."
INDEX { pmRoleElement, pmRoleContextName,
pmRoleContextEngineID, pmRoleString }
::= { pmRoleTable 1 }
PmRoleEntry ::= SEQUENCE {
pmRoleElement RowPointer,
pmRoleContextName SnmpAdminString,
pmRoleContextEngineID OCTET STRING,
pmRoleString PmUTF8String,
pmRoleStatus RowStatus
}
pmRoleElement OBJECT-TYPE
SYNTAX RowPointer
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The element with which this role string is associated.
For example, if the element is interface 3, then this object
will contain the OID for 'ifIndex.3'.
If the agent assigns new indexes in the MIB table to
represent the same underlying element (re-indexing), the
agent will modify this value to contain the new index for the
underlying element.
As this object is used in the index for the pmRoleTable,
users of this table should be careful not to create entries
that would result in instance names with more than 128
sub-identifiers."
::= { pmRoleEntry 1 }
pmRoleContextName OBJECT-TYPE
SYNTAX SnmpAdminString (SIZE (0..32))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"If the associated element is not in the default SNMP context
for the target system, this object is used to identify the
context. If the element is in the default context, this object
is equal to the empty string."
::= { pmRoleEntry 2 }
pmRoleContextEngineID OBJECT-TYPE
SYNTAX OCTET STRING (SIZE (0 | 5..32))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"If the associated element is on a remote system, this object
is used to identify the remote system. This object contains
the contextEngineID of the system for which this role string
assignment is valid. If the element is on the local system
this object will be the empty string."
::= { pmRoleEntry 3 }
pmRoleString OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..64))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The role string that is associated with an element through
this table. All role strings must have been successfully
transformed by Stringprep RFC 3454. Management stations
must perform this translation and must only set this object
to string values that have been transformed.
A role string is an administratively specified characteristic
of a managed element (for example, an interface). It is a
selector for policy rules, that determines the applicability of
the rule to a particular managed element."
::= { pmRoleEntry 4 }
pmRoleStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The status of this role string.
If the value of this object is active, no object in this row
may be modified."
::= { pmRoleEntry 5 }
and last but not least, the capabilities table, which is how managers figure out what an agent can actually do before they install anything ๐ง๐ธ
-- Capabilities table
pmCapabilitiesTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmCapabilitiesEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The pmCapabilitiesTable contains a description of
the inherent capabilities of the system so that
management stations can learn of an agent's capabilities and
differentially install policies based on the capabilities.
Capabilities are expressed at the system level. There can be
variation in how capabilities are realized from one vendor or
model to the next. Management systems should consider these
differences before selecting which policy to install in a
system."
::= { pmMib 5 }
pmCapabilitiesEntry OBJECT-TYPE
SYNTAX PmCapabilitiesEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A capabilities entry holds an OID indicating support for a
particular capability. Capabilities may include hardware and
software functions and the implementation of MIB
Modules. The semantics of the OID are defined in the
description of pmCapabilitiesType.
Entries appear in this table if any element in the system has
a specific capability. A capability should appear in this
table only once, regardless of the number of elements in the
system with that capability. An entry is removed from this
table when the last element in the system that has the
capability is removed. In some cases, capabilities are
dynamic and exist only in software. This table should have an
entry for the capability even if there are no current
instances. Examples include systems with database or WEB
services. While the system has the ability to create new
databases or WEB services, the entry should exist. In these
cases, the ability to create these services could come from
other processes that are running in the system, even though
there are no currently open databases or WEB servers running.
Capabilities may include the implementation of MIB Modules
but need not be limited to those that represent MIB Modules
with one or more configurable objects. It may also be
valuable to include entries for capabilities that do not
include configuration objects, as that information, in
combination with other entries in this table, might be used
by the management software to determine whether to
install a policy.
Vendor software may also add entries in this table to express
capabilities from their private branch.
Note that some values of this table's index may result in an
instance name that exceeds a length of 128 sub-identifiers,
which exceeds the maximum for the SNMP
protocol. Implementations should take care to avoid such
values."
INDEX { pmCapabilitiesType }
::= { pmCapabilitiesTable 1 }
PmCapabilitiesEntry ::= SEQUENCE {
pmCapabilitiesType OBJECT IDENTIFIER
}
pmCapabilitiesType OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"There are three types of OIDs that may be present in the
pmCapabilitiesType object:
1) The OID of a MODULE-COMPLIANCE macro that represents the
highest level of compliance realized by the agent for that
MIB Module. For example, an agent that implements the OSPF
MIB Module at the highest level of compliance would have the
value of '1.3.6.1.2.1.14.15.2' in the pmCapabilitiesType
object. For software that realizes standard MIB
Modules that do not have compliance statements, the base OID
of the MIB Module should be used instead. If the OSPF MIB
Module had not been created with a compliance statement, then
the correct value of the pmCapabilitiesType would be
'1.3.6.1.2.1.14'. In the cases where multiple compliance
statements in a MIB Module are supported by the agent, and
where one compliance statement does not by definition include
the other, each of the compliance OIDs would have entries in
this table.
MIB Documents can contain more than one MIB Module. In the
case of OSPF, there is a second MIB Module
that describes notifications for the OSPF Version 2 Protocol.
If the agent also realizes these functions, an entry will
also exist for those capabilities in this table.
2) Vendors should install OIDs in this table that represent
vendor-specific capabilities. These capabilities can be
expressed just as those described above for MIB Modules on
the standards track. In addition, vendors may install any
OID they desire from their registered branch. The OIDs may be
at any level of granularity, from the root of their entire
branch to an instance of a single OID. There is no
restriction on the number of registrations they may make,
though care should be taken to avoid unnecessary entries.
3) OIDs that represent one capability or a collection of
capabilities that could be any collection of MIB Objects or
hardware or software functions may be created in working
groups and registered in a MIB Module. Other entities (e.g.,
vendors) may also make registrations. Software will register
these standard capability OIDs, as well as vendor specific
OIDs.
If the OID for a known capability is not present in the
table, then it should be assumed that the capability is not
implemented.
As this object is used in the index for the
pmCapabilitiesTable, users of this table should be careful
not to create entries that would result in instance names
with more than 128 sub-identifiers."
::= { pmCapabilitiesEntry 1 }
-- Capabilities override table
pmCapabilitiesOverrideTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmCapabilitiesOverrideEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The pmCapabilitiesOverrideTable allows management stations
to override pmCapabilitiesTable entries that have been
registered by the agent. This facility can be used to avoid
situations in which managers in the network send policies to
a system that has advertised a capability in the
pmCapabilitiesTable but that should not be installed on this
particular system. One example could be newly deployed
equipment that is still in a trial state in a trial state or
resources reserved for some other administrative reason.
This table can also be used to override entries in the
pmCapabilitiesTable through the use of the
pmCapabilitiesOverrideState object. Capabilities can also be
declared available in this table that were not registered in
the pmCapabilitiesTable. A management application can make
an entry in this table for any valid OID and declare the
capability available by setting the
pmCapabilitiesOverrideState for that row to valid(1)."
::= { pmMib 6 }
pmCapabilitiesOverrideEntry OBJECT-TYPE
SYNTAX PmCapabilitiesOverrideEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry in this table indicates whether a particular
capability is valid or invalid.
Note that some values of this table's index may result in an
instance name that exceeds a length of 128 sub-identifiers,
which exceeds the maximum for the SNMP
protocol. Implementations should take care to avoid such
values."
INDEX { pmCapabilitiesOverrideType }
::= { pmCapabilitiesOverrideTable 1 }
PmCapabilitiesOverrideEntry ::= SEQUENCE {
pmCapabilitiesOverrideType OBJECT IDENTIFIER,
pmCapabilitiesOverrideState INTEGER,
pmCapabilitiesOverrideRowStatus RowStatus
}
pmCapabilitiesOverrideType OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This is the OID of the capability that is declared valid or
invalid by the pmCapabilitiesOverrideState value for this
row. Any valid OID, as described in the pmCapabilitiesTable,
is permitted in the pmCapabilitiesOverrideType object. This
means that capabilities can be expressed at any level, from a
specific instance of an object to a table or entire module.
There are no restrictions on whether these objects are from
standards track MIB documents or in the private branch of the
MIB.
If an entry exists in this table for which there is a
corresponding entry in the pmCapabilitiesTable, then this entry
shall have precedence over the entry in the
pmCapabilitiesTable. All entries in this table must be
preserved across reboots.
As this object is used in the index for the
pmCapabilitiesOverrideTable, users of this table should be
careful not to create entries that would result in instance
names with more than 128 sub-identifiers."
::= { pmCapabilitiesOverrideEntry 1 }
pmCapabilitiesOverrideState OBJECT-TYPE
SYNTAX INTEGER {
invalid(1),
valid(2)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"A pmCapabilitiesOverrideState of invalid indicates that
management software should not send policies to this system
for the capability identified in the
pmCapabilitiesOverrideType for this row of the table. This
behavior is the same whether the capability represented by
the pmCapabilitiesOverrideType exists only in this table
(that is, it was installed by an external management
application) or exists in this table as well as the
pmCapabilitiesTable. This would be the case when a manager
wanted to disable a capability that the native management
system found and registered in the pmCapabilitiesTable.
An entry in this table that has a pmCapabilitiesOverrideState
of valid should be treated as though it appeared in the
pmCapabilitiesTable. If the entry also exists in the
pmCapabilitiesTable in the pmCapabilitiesType object, and if
the value of this object is valid, then the system shall
operate as though this entry did not exist and policy
installations and executions will continue in a normal
fashion."
::= { pmCapabilitiesOverrideEntry 2 }
pmCapabilitiesOverrideRowStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The row status of this pmCapabilitiesOverrideEntry.
If the value of this object is active, no object in this row
may be modified."
::= { pmCapabilitiesOverrideEntry 3 }
-- The Schedule Group
pmSchedLocalTime OBJECT-TYPE
SYNTAX DateAndTime (SIZE (11))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The local time used by the scheduler. Schedules that
refer to calendar time will use the local time indicated
by this object. An implementation MUST return all 11 bytes
of the DateAndTime textual-convention so that a manager
may retrieve the offset from GMT time."
::= { pmMib 7 }
--
-- The schedule table that controls the scheduler.
--
pmSchedTable OBJECT-TYPE
SYNTAX SEQUENCE OF PmSchedEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"This table defines schedules for policies."
::= { pmMib 8 }
pmSchedEntry OBJECT-TYPE
SYNTAX PmSchedEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry describing a particular schedule.
Unless noted otherwise, writable objects of this row can be
modified independently of the current value of pmSchedRowStatus,
pmSchedAdminStatus and pmSchedOperStatus. In particular, it
is legal to modify pmSchedWeekDay, pmSchedMonth, and
pmSchedDay when pmSchedRowStatus is active."
INDEX { pmSchedIndex }
::= { pmSchedTable 1 }
Okay so the capabilities entry is basically the "menu" of what a box can do, bestie ๐ Each capability shows up once, no matter how many elements have it, and it disappears when the last element with it is gone. But dynamic stuff like database or WEB services can keep an entry even when nothing is running yet, because the system can still make them ๐ Vendors can add their own private-branch entries too, and any OID can be at any level of granularity โจ Just watch out for the SNMP 128 sub-identifier limit, it's a whole thing ๐
pmCapabilitiesType is the actual OID that says what the capability is ๐ฆ There are three flavours. First, MODULE-COMPLIANCE OIDs for the highest compliance level the agent hits (like OSPF's '1.3.6.1.2.1.14.15.2'), or the base MIB OID if there's no compliance statement (like '1.3.6.1.2.1.14'), with a separate entry for each compliance statement the agent supports that doesn't include the others. Second, vendor-specific OIDs, which can be from the whole registered branch, down to a single instance ๐ Third, capability OIDs that working groups or other folks register in a MIB Module. And if a known capability's OID isn't in the table? It's giving "not implemented" ๐
Capabilities override table ๐
The override table is the manager's power move ๐ฅ It lets management stations override what the agent registered in pmCapabilitiesTable, so policies don't get sent to a box that shouldn't get them. Think brand-new gear still in a trial state, or resources reserved for some other admin reason ๐ธ Managers can also declare a capability available here even if the agent never registered it, just by setting the state to valid(1) for any valid OID.
Each row says whether a capability is valid or invalid ๐ง The override type is the capability's OID, and it can be any valid OID at any level, from one instance all the way up to a whole module, standards track or private branch. If a row here matches a row in pmCapabilitiesTable, the override takes precedence, and every row in this table has to survive reboots ๐ Same 128 sub-identifier caution applies, obviously.
The state is where the magic happens โจ invalid(1) means management software shouldn't send policies for that capability, even if the native system registered it, and that's the case whether the row is brand-new or a disable of something already found ๐ valid(2) means the row gets treated like it was in pmCapabilitiesTable. If the same capability is in both tables and the override says valid, the system acts like the duplicate never existed and policy installs carry on as normal ๐ซถ
The row status is read-create, and when it's active, nothing else in that row can be changed ๐
The Schedule Group ๐๏ธ
The local time object is the scheduler's clock, and any calendar-based schedule uses it ๐ฐ๏ธ The big rule here is that an implementation MUST return all 11 bytes of the DateAndTime textual convention, so the manager can work out the offset from GMT โญ No shortchanging the offset, bestie ๐ญ
Then there's the schedule table itself, which just defines schedules for policies ๐ง Each entry describes one schedule, and its writable objects can be changed independently of pmSchedRowStatus, pmSchedAdminStatus and pmSchedOperStatus, unless it's noted otherwise. That includes pmSchedWeekDay, pmSchedMonth and pmSchedDay, which are totally fine to modify even when the row is active ๐ The index is pmSchedIndex.
okay so this is where the schedule entries get spicy ๐ every one of these objects is a little knob for when a policy is allowed to switch on, and the group stuff is lowkey the cutest part ๐
PmSchedEntry ::= SEQUENCE {
pmSchedIndex Unsigned32,
pmSchedGroupIndex Unsigned32,
pmSchedDescr PmUTF8String,
pmSchedTimePeriod PmUTF8String,
pmSchedMonth BITS,
pmSchedDay BITS,
pmSchedWeekDay BITS,
pmSchedTimeOfDay PmUTF8String,
pmSchedLocalOrUtc INTEGER,
pmSchedStorageType StorageType,
pmSchedRowStatus RowStatus
}
pmSchedIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The locally unique, administratively assigned index for this
scheduling entry."
::= { pmSchedEntry 1 }
okay so the index is the entry's own ID card ๐ชช it's locally unique and administratively assigned, and you can't poke it directly (not-accessible) โจ
pmSchedGroupIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The locally unique, administratively assigned index for the
schedule group this scheduling entry belongs to.
To assign multiple schedule entries to the same group, the
pmSchedGroupIndex of each entry in the group will be set to
the same value. This pmSchedGroupIndex value must be equal to
the pmSchedIndex of one of the entries in the group. If the
entry whose pmSchedIndex equals the pmSchedGroupIndex
for the group is deleted, the agent will assign a new
pmSchedGroupIndex to all remaining members of the group.
If an entry is not a member of a group, its pmSchedGroupIndex
must be assigned to the value of its pmSchedIndex.
Policies that are controlled by a group of schedule entries
are active when any schedule in the group is active."
::= { pmSchedEntry 2 }
ngl the group index is giving friendship bracelets ๐ง everyone in the group points at the same index, and that index has to match the pmSchedIndex of one of the members. if that anchor gets deleted, the agent picks a new one for the rest of the crew ๐ฆ and a lone entry just points at itself. a policy tied to a group is active whenever any schedule in that group is active ๐
pmSchedDescr OBJECT-TYPE
SYNTAX PmUTF8String
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The human-readable description of the purpose of this
scheduling entry."
DEFVAL { ''H }
::= { pmSchedEntry 3 }
pmSchedTimePeriod OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..31))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The overall range of calendar dates and times over which this
schedule is active. It is stored in a slightly extended version
of the format for a 'period-explicit' defined in RFC 2445.
This format is expressed as a string representing the
starting date and time, in which the character 'T' indicates
the beginning of the time portion, followed by the solidus
character, '/', followed by a similar string representing an
end date and time. The start of the period MUST be before the
end of the period. Date-Time values are expressed as
substrings of the form 'yyyymmddThhmmss'. For example:
20000101T080000/20000131T130000
January 1, 2000, 0800 through January 31, 2000, 1PM
The 'Date with UTC time' format defined in RFC 2445 in which
the Date-Time string ends with the character 'Z' is not
allowed.
This 'period-explicit' format is also extended to allow two
special cases in which one of the Date-Time strings is
replaced with a special string defined in RFC 2445:
1. If the first Date-Time value is replaced with the string
'THISANDPRIOR', then the value indicates that the schedule
is active at any time prior to the Date-Time that appears
after the '/'.
2. If the second Date-Time is replaced with the string
'THISANDFUTURE', then the value indicates that the schedule
is active at any time after the Date-Time that appears
before the '/'.
Note that although RFC 2445 defines these two strings, they are
not specified for use in the 'period-explicit' format. The use
of these strings represents an extension to the
'period-explicit' format."
::= { pmSchedEntry 4 }
okay the description is cute, but the time period is the big calendar window for the whole schedule ๐
it's a string in the yyyymmddThhmmss/yyyymmddThhmmss style, borrowed from RFC 2445 and then stretched a bit. the start has to come before the end, and the Z UTC version is a no-go ๐
โโ๏ธ there are also two extra special words, THISANDPRIOR and THISANDFUTURE, which RFC 2445 defines but doesn't actually use for this format, so this is a little extension of its own โจ
pmSchedMonth OBJECT-TYPE
SYNTAX BITS {
january(0),
february(1),
march(2),
april(3),
may(4),
june(5),
july(6),
august(7),
september(8),
october(9),
november(10),
december(11)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Within the overall time period specified in the
pmSchedTimePeriod object, the value of this object specifies
the specific months within that time period when the schedule
is active. Setting all bits will cause the schedule to act
independently of the month."
DEFVAL { { january, february, march, april, may, june, july,
august, september, october, november, december } }
::= { pmSchedEntry 5 }
pmSchedDay OBJECT-TYPE
SYNTAX BITS {
d1(0), d2(1), d3(2), d4(3), d5(4),
d6(5), d7(6), d8(7), d9(8), d10(9),
d11(10), d12(11), d13(12), d14(13), d15(14),
d16(15), d17(16), d18(17), d19(18), d20(19),
d21(20), d22(21), d23(22), d24(23), d25(24),
d26(25), d27(26), d28(27), d29(28), d30(29),
d31(30),
r1(31), r2(32), r3(33), r4(34), r5(35),
r6(36), r7(37), r8(38), r9(39), r10(40),
r11(41), r12(42), r13(43), r14(44), r15(45),
r16(46), r17(47), r18(48), r19(49), r20(50),
r21(51), r22(52), r23(53), r24(54), r25(55),
r26(56), r27(57), r28(58), r29(59), r30(60),
r31(61)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Within the overall time period specified in the
pmSchedTimePeriod object, the value of this object specifies
the specific days of the month within that time period when
the schedule is active.
There are two sets of bits one can use to define the day
within a month:
Enumerations starting with the letter 'd' indicate a
day in a month relative to the first day of a month.
The first day of the month can therefore be specified
by setting the bit d1(0), and d31(30) means the last
day of a month with 31 days.
Enumerations starting with the letter 'r' indicate a
day in a month in reverse order, relative to the last
day of a month. The last day in the month can therefore
be specified by setting the bit r1(31), and r31(61)
means the first day of a month with 31 days.
Setting multiple bits will include several days in the set
of possible days for this schedule. Setting all bits starting
with the letter 'd' or all bits starting with the letter 'r'
will cause the schedule to act independently of the day of the
month."
DEFVAL { { d1, d2, d3, d4, d5, d6, d7, d8, d9, d10,
d11, d12, d13, d14, d15, d16, d17, d18, d19, d20,
d21, d22, d23, d24, d25, d26, d27, d28, d29, d30,
d31, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10,
r11, r12, r13, r14, r15, r16, r17, r18, r19, r20,
r21, r22, r23, r24, r25, r26, r27, r28, r29, r30,
r31 } }
::= { pmSchedEntry 6 }
pmSchedWeekDay OBJECT-TYPE
SYNTAX BITS {
sunday(0),
monday(1),
tuesday(2),
wednesday(3),
thursday(4),
friday(5),
saturday(6)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Within the overall time period specified in the
pmSchedTimePeriod object, the value of this object specifies
the specific days of the week within that time period when
the schedule is active. Setting all bits will cause the
schedule to act independently of the day of the week."
DEFVAL { { sunday, monday, tuesday, wednesday, thursday,
friday, saturday } }
::= { pmSchedEntry 7 }
the calendar bits are where the mood board really comes together ๐ month is just a checklist of twelve months. day-of-month has two tracks: d counts forward from the 1st (so d1 is the 1st), and r counts backward from the last day (so r1 is the last day) ๐
and if you set every d bit or every r bit, the schedule stops caring about the day of the month. weekday is the same deal, sunday through saturday, and setting all of them means it doesn't care what day of the week it is ๐
pmSchedTimeOfDay OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..15))
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"Within the overall time period specified in the
pmSchedTimePeriod object, the value of this object specifies
the range of times in a day when the schedule is active.
This value is stored in a format based on the RFC 2445 format
for 'time': The character 'T' followed by a 'time' string,
followed by the solidus character, '/', followed by the
character 'T', followed by a second time string. The first time
indicates the beginning of the range, and the second time
indicates the end. Thus, this value takes the following
form:
'Thhmmss/Thhmmss'.
The second substring always identifies a later time than the
first substring. To allow for ranges that span midnight,
however, the value of the second string may be smaller than
the value of the first substring. Thus, 'T080000/T210000'
identifies the range from 0800 until 2100, whereas
'T210000/T080000' identifies the range from 2100 until 0800 of
the following day.
When a range spans midnight, by definition it includes parts
of two successive days. When one of these days is also
selected by either the MonthOfYearMask, DayOfMonthMask, and/or
DayOfWeekMask, but the other day is not, then the policy is
active only during the portion of the range that falls on the
selected day. For example, if the range extends from 2100
until 0800, and the day of week mask selects Monday and
Tuesday, then the policy is active during the following three
intervals:
From midnight Sunday until 0800 Monday
From 2100 Monday until 0800 Tuesday
From 2100 Tuesday until 23:59:59 Tuesday
Setting this value to 'T000000/T235959' will cause the
schedule to act independently of the time of day."
DEFVAL { '543030303030302F54323335393539'H } -- T000000/T235959
::= { pmSchedEntry 8 }
time of day is the clock part of the schedule โฐ it's a Thhmmss/Thhmmss range, and if the second time is earlier than the first, the range wraps past midnight ๐ the tricky bit is the midnight crossover: the policy only switches on for the bits of each day that are actually selected by the month, day, and weekday masks. the example in the description walks through a Sunday-to-Tuesday one, which is so iykyk level detailed ๐ the default T000000/T235959 basically means "any time of day, bestie" ๐ซถ
pmSchedLocalOrUtc OBJECT-TYPE
SYNTAX INTEGER {
localTime(1),
utcTime(2)
}
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This object indicates whether the times represented in the
TimePeriod object and in the various Mask objects represent
local times or UTC times."
DEFVAL { utcTime }
::= { pmSchedEntry 9 }
pmSchedStorageType OBJECT-TYPE
SYNTAX StorageType
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"This object defines whether this schedule entry is kept
in volatile storage and lost upon reboot or
backed up by non-volatile or permanent storage.
Conceptual rows having the value 'permanent' must allow write
access to the columnar objects pmSchedDescr, pmSchedWeekDay,
pmSchedMonth, and pmSchedDay.
If the value of this object is 'permanent', no values in the
associated row have to be writable."
DEFVAL { volatile }
::= { pmSchedEntry 10 }
pmSchedRowStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-create
STATUS current
DESCRIPTION
"The status of this schedule entry.
If the value of this object is active, no object in this row
may be modified."
::= { pmSchedEntry 11 }
the last few are the housekeeping crew ๐งน pmSchedLocalOrUtc says whether the times are local or UTC, and it defaults to UTC ๐ธ pmSchedStorageType decides whether the entry lives in volatile memory (gone after a reboot) or gets backed up in permanent storage, and permanent rows have to stay writable for a few columns. pmSchedRowStatus is the row's on/off switch, and once it's active, nothing in that row can be changed ๐
okay the policy tracking part is next, and it's giving a whole new chapter ๐ฆ the table starts with the "policy to element" (PE) table and the "element to policy" (EP)
-- table track the status of execution contexts grouped by policy and -- element respectively.
pmTrackingPETable OBJECT-TYPE SYNTAX SEQUENCE OF PmTrackingPEEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "The pmTrackingPETable describes what elements are active (under control of) a policy. This table is indexed in order to optimize retrieval of the entire status for a given policy." ::= { pmMib 9 }
pmTrackingPEEntry OBJECT-TYPE SYNTAX PmTrackingPEEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pmTrackingPETable. The pmPolicyIndex in the index specifies the policy tracked by this entry.
Note that some combinations of index values may result in an instance name that exceeds a length of 128 sub-identifiers, which exceeds the maximum for the SNMP protocol. Implementations should take care to avoid such combinations." INDEX { pmPolicyIndex, pmTrackingPEElement, pmTrackingPEContextName, pmTrackingPEContextEngineID } ::= { pmTrackingPETable 1 }
PmTrackingPEEntry ::= SEQUENCE { pmTrackingPEElement RowPointer, pmTrackingPEContextName SnmpAdminString, pmTrackingPEContextEngineID OCTET STRING, pmTrackingPEInfo BITS }
pmTrackingPEElement OBJECT-TYPE SYNTAX RowPointer MAX-ACCESS not-accessible STATUS current DESCRIPTION "The element that is acted upon by the associated policy.
As this object is used in the index for the pmTrackingPETable, users of this table should be careful not to create entries that would result in instance names with more than 128 sub-identifiers." ::= { pmTrackingPEEntry 1 }
pmTrackingPEContextName OBJECT-TYPE SYNTAX SnmpAdminString (SIZE (0..32)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "If the associated element is not in the default SNMP context for the target system, this object is used to identify the context. If the element is in the default context, this object is equal to the empty string." ::= { pmTrackingPEEntry 2 }
pmTrackingPEContextEngineID OBJECT-TYPE SYNTAX OCTET STRING (SIZE (0 | 5..32)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "If the associated element is on a remote system, this object is used to identify the remote system. This object contains the contextEngineID of the system on which the associated element resides. If the element is on the local system, this object will be the empty string." ::= { pmTrackingPEEntry 3 }
pmTrackingPEInfo OBJECT-TYPE SYNTAX BITS { actionSkippedDueToPrecedence(0), conditionRunTimeException(1), conditionUserSignal(2), actionRunTimeException(3), actionUserSignal(4) } MAX-ACCESS read-only STATUS current DESCRIPTION "This object returns information about the previous policy script executions.
If the actionSkippedDueToPrecedence(1) bit is set, the last execution of the associated policy condition returned non-zero, but the action is not active, because it was trumped by a matching policy condition in the same precedence group with a higher precedence value.
If the conditionRunTimeException(2) bit is set, the last execution of the associated policy condition encountered a run-time exception and aborted.
If the conditionUserSignal(3) bit is set, the last execution of the associated policy condition called the signalError() function.
If the actionRunTimeException(4) bit is set, the last execution of the associated policy action encountered a run-time exception and aborted.
If the actionUserSignal(5) bit is set, the last execution of the associated policy action called the signalError() function.
Entries will only exist in this table of one or more bits are set. In particular, if an entry does not exist for a particular policy/element combination, it can be assumed that the policy's condition did not match 'this element'." ::= { pmTrackingPEEntry 4 }
-- Element to Policy Table
pmTrackingEPTable OBJECT-TYPE SYNTAX SEQUENCE OF PmTrackingEPEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "The pmTrackingEPTable describes what policies are controlling an element. This table is indexed in order to optimize retrieval of the status of all policies active for a given element."
::= { pmMib 10 }
pmTrackingEPEntry OBJECT-TYPE SYNTAX PmTrackingEPEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pmTrackingEPTable. Entries exist for all element/policy combinations for which the policy's condition matches and only if the schedule for the policy is active.
The pmPolicyIndex in the index specifies the policy tracked by this entry.
Note that some combinations of index values may result in an instance name that exceeds a length of 128 sub-identifiers, which exceeds the maximum for the SNMP protocol. Implementations should take care to avoid such combinations." INDEX { pmTrackingEPElement, pmTrackingEPContextName, pmTrackingEPContextEngineID, pmPolicyIndex } ::= { pmTrackingEPTable 1 }
PmTrackingEPEntry ::= SEQUENCE { pmTrackingEPElement RowPointer, pmTrackingEPContextName SnmpAdminString, pmTrackingEPContextEngineID OCTET STRING, pmTrackingEPStatus INTEGER }
pmTrackingEPElement OBJECT-TYPE SYNTAX RowPointer MAX-ACCESS not-accessible STATUS current DESCRIPTION "The element acted upon by the associated policy.
As this object is used in the index for the pmTrackingEPTable, users of this table should be careful not to create entries that would result in instance names with more than 128 sub-identifiers." ::= { pmTrackingEPEntry 1 }
pmTrackingEPContextName OBJECT-TYPE SYNTAX SnmpAdminString (SIZE (0..32)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "If the associated element is not in the default SNMP context for the target system, this object is used to identify the context. If the element is in the default context, this object is equal to the empty string." ::= { pmTrackingEPEntry 2 }
pmTrackingEPContextEngineID OBJECT-TYPE SYNTAX OCTET STRING (SIZE (0 | 5..32)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "If the associated element is on a remote system, this object is used to identify the remote system. This object contains the contextEngineID of the system on which the associated element resides. If the element is on the local system, this object will be the empty string." ::= { pmTrackingEPEntry 3 }
pmTrackingEPStatus OBJECT-TYPE SYNTAX INTEGER { on(1), forceOff(2) } MAX-ACCESS read-write STATUS current DESCRIPTION "This entry will only exist if the calendar for the policy is active and if the associated policyCondition returned 1 for 'this element'.
A policy can be forcibly disabled on a particular element by setting this value to forceOff(2). The agent should then act as though the policyCondition failed for 'this element'. The forceOff(2) state will persist (even across reboots) until this value is set to on(1) by a management request. The forceOff(2) state may be set even if the entry does not previously exist so that future policy invocations can be avoided.
Unless forcibly disabled, if this entry exists, its value will be on(1)." ::= { pmTrackingEPEntry 4 }
-- Policy Debugging Table
pmDebuggingTable OBJECT-TYPE SYNTAX SEQUENCE OF PmDebuggingEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "Policies that have debugging turned on will generate a log entry in the policy debugging table for every runtime exception that occurs in either the condition or action code.
The pmDebuggingTable logs debugging messages when policies experience run-time exceptions in either the condition or action code and the associated pmPolicyDebugging object has been turned on.
The maximum number of debugging entries that will be stored and the maximum length of time an entry will be kept are an implementation-dependent manner. If entries must be discarded to make room for new entries, the oldest entries must be discarded first.
If the system restarts, all debugging entries may be deleted." ::= { pmMib 11 }
pmDebuggingEntry OBJECT-TYPE SYNTAX PmDebuggingEntry MAX-ACCESS not-accessible STATUS current DESCRIPTION "An entry in the pmDebuggingTable. The pmPolicyIndex in the index specifies the policy that encountered the exception that led to this log entry.
Note that some combinations of index values may result in an instance name that exceeds a length of 128 sub-identifiers, which exceeds the maximum for the SNMP protocol. Implementations should take care to avoid such combinations." INDEX { pmPolicyIndex, pmDebuggingElement, pmDebuggingContextName, pmDebuggingContextEngineID, pmDebuggingLogIndex } ::= { pmDebuggingTable 1 }
PmDebuggingEntry ::= SEQUENCE { pmDebuggingElement RowPointer, pmDebuggingContextName SnmpAdminString, pmDebuggingContextEngineID OCTET STRING, pmDebuggingLogIndex Unsigned32, pmDebuggingMessage PmUTF8String } pmDebuggingElement OBJECT-TYPE SYNTAX RowPointer MAX-ACCESS not-accessible STATUS current DESCRIPTION "The element the policy was executing on when it encountered the error that led to this log entry.
For example, if the element is interface 3, then this object will contain the OID for 'ifIndex.3'.
As this object is used in the index for the pmDebuggingTable, users of this table should be careful not to create entries that would result in instance names with more than 128 sub-identifiers." ::= { pmDebuggingEntry 1 }
pmDebuggingContextName OBJECT-TYPE SYNTAX SnmpAdminString (SIZE (0..32)) MAX-ACCESS not-accessible STATUS current DESCRIPTION "If the associated element is not in the default SNMP context for the target system, this object is used to identify the context. If the element is in the default context, this object is equal to the empty string." ::= { pmDebuggingEntry 2 }
pmDebuggingContextEngineID OBJECT-TYPE
SYNTAX OCTET STRING (SIZE (0 | 5..32))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"If the associated element is on a remote system, this object
is used to identify the remote system. This object contains
the contextEngineID of the system on which the associated
element resides. If the element is on the local system,
this object will be the empty string."
::= { pmDebuggingEntry 3 }
pmDebuggingLogIndex OBJECT-TYPE
SYNTAX Unsigned32 (1..4294967295)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A unique index for this log entry among other log entries
for this policy/element combination."
::= { pmDebuggingEntry 4 }
pmDebuggingMessage OBJECT-TYPE
SYNTAX PmUTF8String (SIZE (0..128))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An error message generated by the policy execution
environment. It is recommended that this message include the
time of day when the message was generated, if known."
::= { pmDebuggingEntry 5 }
-- Notifications
pmNotifications OBJECT IDENTIFIER ::= { pmMib 0 }
pmNewRoleNotification NOTIFICATION-TYPE
OBJECTS { pmRoleStatus }
STATUS current
DESCRIPTION
"The pmNewRoleNotification is sent when an agent is configured
with its first instance of a previously unused role string
(not every time a new element is given a particular role).
An instance of the pmRoleStatus object is sent containing
the new roleString in its index. In the event that two or
more elements are given the same role simultaneously, it is an
implementation-dependent matter as to which pmRoleTable
instance will be included in the notification."
::= { pmNotifications 1 }
pmNewCapabilityNotification NOTIFICATION-TYPE
OBJECTS { pmCapabilitiesType }
STATUS current
DESCRIPTION
"The pmNewCapabilityNotification is sent when an agent
gains a new capability that did not previously exist in any
element on the system (not every time an element gains a
particular capability).
An instance of the pmCapabilitiesType object is sent containing
the identity of the new capability. In the event that two or
more elements gain the same capability simultaneously, it is an
implementation-dependent matter as to which pmCapabilitiesType
instance will be included in the notification."
::= { pmNotifications 2 }
pmAbnormalTermNotification NOTIFICATION-TYPE
OBJECTS { pmTrackingPEInfo }
STATUS current
DESCRIPTION
"The pmAbnormalTermNotification is sent when a policy's
pmPolicyAbnormalTerminations gauge value changes from zero to
any value greater than zero and no such notification has been
sent for that policy in the last 5 minutes.
The notification contains an instance of the pmTrackingPEInfo
object where the pmPolicyIndex component of the index
identifies the associated policy and the rest of the index
identifies an element on which the policy failed."
::= { pmNotifications 3 }
-- Compliance Statements
pmConformance OBJECT IDENTIFIER ::= { pmMib 12 }
pmCompliances OBJECT IDENTIFIER ::= { pmConformance 1 }
pmGroups OBJECT IDENTIFIER ::= { pmConformance 2 }
pmCompliance MODULE-COMPLIANCE
STATUS current
DESCRIPTION
"Describes the requirements for conformance to
the Policy-Based Management MIB"
MODULE -- this module
MANDATORY-GROUPS { pmPolicyManagementGroup, pmSchedGroup,
pmNotificationGroup }
::= { pmCompliances 1 }
pmPolicyManagementGroup OBJECT-GROUP
OBJECTS { pmPolicyPrecedenceGroup, pmPolicyPrecedence,
pmPolicySchedule, pmPolicyElementTypeFilter,
pmPolicyConditionScriptIndex, pmPolicyActionScriptIndex,
pmPolicyParameters,
pmPolicyConditionMaxLatency, pmPolicyActionMaxLatency,
pmPolicyMaxIterations,
pmPolicyDescription, pmPolicyMatches,
pmPolicyAbnormalTerminations,
pmPolicyExecutionErrors, pmPolicyDebugging,
pmPolicyStorageType, pmPolicyAdminStatus,
pmPolicyRowStatus, pmPolicyCodeText, pmPolicyCodeStatus,
pmElementTypeRegMaxLatency, pmElementTypeRegDescription,
pmElementTypeRegStorageType, pmElementTypeRegRowStatus,
pmRoleStatus,
pmCapabilitiesType, pmCapabilitiesOverrideState,
pmCapabilitiesOverrideRowStatus,
pmTrackingPEInfo,
pmTrackingEPStatus,
pmDebuggingMessage }
STATUS current
DESCRIPTION
"Objects that allow for the creation and management of
configuration policies."
::= { pmGroups 1 }
pmSchedGroup OBJECT-GROUP
OBJECTS { pmSchedLocalTime, pmSchedGroupIndex,
pmSchedDescr, pmSchedTimePeriod,
pmSchedMonth, pmSchedDay, pmSchedWeekDay,
pmSchedTimeOfDay, pmSchedLocalOrUtc, pmSchedStorageType,
pmSchedRowStatus
}
STATUS current
DESCRIPTION
"Objects that allow for the scheduling of policies."
::= { pmGroups 2 }
pmNotificationGroup NOTIFICATION-GROUP
NOTIFICATIONS { pmNewRoleNotification,
pmNewCapabilityNotification,
pmAbnormalTermNotification }
STATUS current
DESCRIPTION
"Notifications sent by an Policy MIB agent."
::= { pmGroups 3 }
pmBaseFunctionLibrary OBJECT IDENTIFIER ::= { pmGroups 4 }
END
12. Relationship to Other MIB Modules โจ
okay so if you're using policy-based management specifically for (policy-based) configuration, the "Configuring Networks and Devices With SNMP" RFC 3512 [19] document is literally the one to read ๐. It covers configuration management practices, the terminology, and an example of a MIB Module that may be helpful to anyone building or using this tech ๐ฆ.
The Policy MIB is the one doing the watching ๐. It accesses system instrumentation for policy evaluation, control, notification, monitoring, and error reporting, and managers can see all of that through MIB objects ๐ฅ. Changes to system configuration also happen through the Policy MIB, but the actual objects that get changed are defined in other MIB Modules ๐ธ.
Managers pull the operational and configuration details of a system by getting to the specific MIB objects on a network element ๐ . So honestly, the Policy MIB can work with any standard or vendor-defined object that lives on a managed system ๐ง. That includes vendor-specific objects that are instance-specific, like BGP timeout parameters and specific interface counters ๐, which is giving total main character energy ๐.
13. Security Considerations ๐โจ
okay bestie, this MIB has no objects where reading them would leak sensitive info ๐ซถ that's the good news.
the not-so-cute part: there are a bunch of objects with a MAX-ACCESS of read-write and/or read-create. those can be sensitive or vulnerable depending on the network, and doing SET operations in a non-secure environment without proper protection can seriously mess up network operations ๐ญ
with a few exceptions (pmPolicyDescription, pmPolicyDebugging, pmElementTypeRegDescription, and pmSchedDescr), EVERY read-create and read-write object in this MIB should be considered sensitive ๐ because if an unauthorized user could manipulate these, they could make the Policy MIB system use the stored credentials of an authorized user to do unauthorized and potentially harmful stuff. no thank you ๐ โโ๏ธ
there are no read-only objects in this MIB that contain sensitive information, so that's a W ๐
SNMP versions before SNMPv3 did not include adequate security. even if the network itself is secure (like, using IPSec), there's still no control over who on that secure network is allowed to access and GET/SET (read/change/create/delete) the objects in this MIB module. so the network being locked down isn't the whole story ๐
it is RECOMMENDED that implementers consider the security features provided by the SNMPv3 framework (see [16], section 8), including full support for the SNMPv3 cryptographic mechanisms (for authentication and privacy) ๐ฅ
further, deployment of SNMP versions prior to SNMPv3 is NOT RECOMMENDED. instead, it is RECOMMENDED to deploy SNMPv3 and enable cryptographic security. then it's a customer/operator responsibility to make sure the SNMP entity giving access to an instance of this MIB module is properly configured, so the objects are only accessible to principals (users) who actually have legitimate rights to GET or SET (change/create/delete) them ๐
an implementation must ensure that access control rules are applied when SNMP operations are performed in policy scripts. to make that happen, an implementation must record and maintain the security credentials of the last entity to modify each policy's pmPolicyAdminStatus object. the credentials to store are the securityModel, securityName, and securityLevel, and they'll be used as input parameters for isAccessAllowed from the Architecture for Describing SNMP Management Frameworks [1]. this mechanism was first introduced in the DISMAN-SCHEDULE-MIB [12] ๐ง
SNMP requests made when secModel, secName, and secLevel are specified use credentials stored in the local configuration datastore. access to those credentials depends on the security credentials of the last entity to modify the policy's pmPolicyAdminStatus object. to figure out whether the credentials can be accessed, the isAccessAllowed abstract service interface defined in RFC 3411 [1] gets called:
statusInformation = -- success or errorIndication
isAccessAllowed(
IN securityModel -- Security Model used
IN securityName -- principal who wants to access
IN securityLevel -- Level of Security used
IN viewType -- write
IN contextName -- context containing variableName
IN variableName -- OID for an object in the proper
-- LCD entry
)
the securityModel, securityName, and securityLevel parameters are set to the values that were recorded when the policy was modified. the viewType is set to write, and the contextName and variableName are set to select any read-create object in the appropriate LCD entry ๐ฆ
proper configuration of VACM requires that write access to an LCD entry not be given to entities that aren't authorized to use the credentials therein ๐ซถ
access control for SNMP requests made to the local system where secModel, secName, and secLevel aren't specified depends on the security credentials of the last entity to modify the policy's pmPolicyAdminStatus object. to figure out whether the operation should succeed, the isAccessAllowed abstract service interface defined in RFC 3411 [1] gets called:
statusInformation = -- success or errorIndication
isAccessAllowed(
IN securityModel -- Security Model in use
IN securityName -- principal who wants to access
IN securityLevel -- Level of Security
IN viewType -- read, write, or notify view
IN contextName -- context as specified
IN variableName -- OID for the managed object
)
the securityModel, securityName, and securityLevel parameters are set to the values that were recorded when the policy was modified. the viewType, contextName, and variableName parameters are set as appropriate for the requested SNMP operation ๐ธ
unless all users who have write access to the pmPolicyTable and pmPolicyCodeTable have equivalent access to the managed system, policy scripts could be used by one user to gain the privileges of another user ๐ฑ so when policy users have different access, access control should be applied so that a user's policies can't be modified by another user. to make this easier, a user can put all of their policies in the same pmPolicyAdminGroup so that a single access control view can cover all of them.
some policies may be designed to keep a network secure. if those policies haven't been installed yet, waiting on a role or capability to show up, there will be some delay in activating them once that role or capability appears, because a responsible manager has to notice the change and install the policy. that delay may expose the device or network to unacceptable security vulnerabilities. if the role or capability shows up during a time of network stress, or when the management station is unavailable, the delay could be extensive, making the exposure even worse ๐ฅบ it is recommended that management stations install any security-related policies that might ever be needed on a particular managed device, even if a nonexistent role or capability suggests it isn't needed at the moment.
this MIB allows delegation of access rights, so a user ("Joe") can tell a Policy MIB agent to run remote operations on his behalf, authorized by keys Joe stored in the usmUserTable. care needs to be taken to make sure unauthorized users can't configure their policies to use Joe's keys ๐ซ although there are theoretically many ways to configure SNMP security, users are advised to follow the most straightforward way outlined below, to keep things simple and cut down on the chance of mistakes.
assume that Joe has credentials that give him authority to manage agents A, B, and C, as well as the Policy MIB agent "P". Joe will store credentials for Joe@A, Joe@B, and Joe@C in the usmUserTable of the Policy MIB agent. then the following VACM configuration will be used:
VACM securityToGroupTable
A single entry mapping user Joe@P to group JoesGroup
VACM accessTable
A single entry mapping group JoesGroup to write view JoesView
VACM viewTreeFamilyTable
ViewName Subtree Type
JoesView points to Joe@A in usmUserTable included
JoesView points to Joe@B in usmUserTable included
JoesView points to Joe@C in usmUserTable included
in the preceding examples, the notation Joe@A represents the entry indexed by usmUserEngineID and usmUserName, where the SnmpEngineID is that of system A and the usmUserName is "Joe" ๐
14. IANA Considerations ๐
this is a profile of stringprep. it has been registered by the IANA in the stringprep profile registry, which lives at http://www.iana.org/assignments/stringprep-profiles ๐
Name of this profile: Policy MIB Stringprep.
RFC in which the profile is defined: This document.
Indicator whether this is the newest version of the profile: This is the first version of Policy MIB Stringprep.
15. Acknowledgements ๐ซถ
the authors gratefully acknowledge the significant contributions to this work made by Jeff Case, Patrik Falstrom, Joel Halpern, Pablo Halpern, Bob Moore, Steve Moulton, David Partain, and Walter Weiss ๐
this MIB uses a security delegation mechanism that was first introduced in the DISMAN-SCHEDULE-MIB [12]. the Schedule table of this MIB borrows heavily from the PolicyTimePeriodCondition of the Policy Core Information Model (PCIM) [18] and from the DISMAN-SCHEDULE-MIB [12] โญ
16. References ๐
16.1. Normative References
- [1] Harrington, D., Presuhn, R., and B. Wijnen, "An Architecture for Describing Simple Network Management Protocol (SNMP) Management Frameworks", STD 62, RFC 3411, December 2002.
- [2] McCloghrie, K., Perkins, D., and J. Schoenwaelder, "Structure of Management Information Version 2 (SMIv2)", STD 58, RFC 2578, April 1999.
- [3] McCloghrie, K., Perkins, D., and J. Schoenwaelder, "Textual Conventions for SMIv2", STD 58, RFC 2579, April 1999.
- [4] McCloghrie, K., Perkins, D., and J. Schoenwaelder, "Conformance Statements for SMIv2", STD 58, RFC 2580, April 1999.
- [5] Presuhn, R., "Transport Mappings for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3417, December 2002.
- [6] Blumenthal, U. and B. Wijnen, "User-based Security Model (USM) for version 3 of the Simple Network Management Protocol (SNMPv3)", STD 62, RFC 3414, December 2002.
- [7] Presuhn, R., "Version 2 of the Protocol Operations for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3416, December 2002.
- [8] Frye, R., Levi, D., Routhier, S., and B. Wijnen, "Coexistence between Version 1, Version 2, and Version 3 of the Internet-standard Network Management Framework", BCP 74, RFC 3584, August 2003.
- [9] Wijnen, B., Presuhn, R., and K. McCloghrie, "View-based Access Control Model (VACM) for the Simple Network Management Protocol (SNMP)", STD 62, RFC 3415, December 2002.
- [10] International Standards Organization, "Information Technology - Programming Languages - C++", ISO/IEC 14882-1998
- [11] Daniele, M. and J. Schoenwaelder, "Textual Conventions for Transport Addresses", RFC 3419, December 2002.
- [12] Levi, D. and J. Schoenwaelder, "Definitions of Managed Objects for Scheduling Management Operations", RFC 3231, January 2002.
- [13] Hoffman, P. and M. Blanchet, "Preparation of Internationalized Strings ("stringprep")", RFC 3454, December 2002.
- [14] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.
- [15] Dawson, F. and D. Stenerson, "Internet Calendaring and Scheduling Core Object Specification (iCalendar)", RFC 2445, November 1998.
16.2. Informative References ๐
okay so these are the background reads ๐ค useful for context, but you don't need them to implement anything here, so no pressure bestie ๐
[16] Case, J., Mundy, R., Partain, D., and B. Stewart, "Introduction and Applicability Statements for Internet-Standard Management Framework", RFC 3410, December 2002.
[17] ECMA, "ECMAScript Language Specification", ECMA-262, December 1999
[18] Moore, B., Ellesson, E., Strassner, J., and A. Westerinen, "Policy Core Information Model -- Version 1 Specification", RFC 3060, February 2001.
[19] MacFaden, M., Partain, D., Saperia, J., and W. Tackabury, "Configuring Networks and Devices with Simple Network Management Protocol (SNMP)", RFC 3512, April 2003.
Author's Addresses ๐
Steve Waldbusser ๐ธ
Phone: +1-650-948-6500
Fax: +1-650-745-0671
EMail: waldbusser@nextbeacon.com
Jon Saperia (WG Co-chair) ๐
JDS Consulting, Inc.
84 Kettell Plain Road.
Stow MA 01775
USA
Phone: +1-978-461--0249
Fax: +1-617-249-0874
EMail: saperia@jdscons.com
Thippanna Hongal ๐ฆ
Riverstone Networks, Inc.
5200 Great America Parkway
Santa Clara, CA, 95054
USA
Phone: +1-408-878-6562
Fax: +1-408-878-6501
EMail: hongal@riverstonenet.com
Full Copyright Statement ๐
Copyright (C) The Internet Society (2005).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
this is the legal part, so I kept the wording exactly as written, no vibes-based edits here ๐โ๏ธ
Intellectual Property ๐ฅ
The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.
Acknowledgement ๐
Funding for the RFC Editor function is currently provided by the Internet Society. ๐
๐ฆ where this page comes from
- ietf: rfc4011 2005-03
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms