Subject: Comments on draft-ietf-snmpconf-pm-02

From: Frank Strauss <[email-masked]>
Date: 14 Sep 2000 17:39:07 +0200

I've just read the Policy-Based Management MIB draft.  Here are some
comments and questions on it. Some are just minor corrections, but
some are major design issues.

Several *Index object types have integer range restrictions
(0..65535). Why at all? Why 64k?

Why is pmPolicyFilter restricted to a maximum size of 65535 octets?
If there's a good reason for this size, why is pmPolicyAction not
restricted?

Why is pmPolicyFilterMaxLatency of type Integer32 (signed) but restricted
to 0..2147483647? Why not just Unsigned32?

What is the purpose of pmPolicyActionMaxLatency? At first sight, it
seems like an action is called exactly when a filter is evaluated being
true. Thus the action latency simply depends on pmPolicyFilterMaxLatency
and the result of the filter evaluation.

The pmPolicyPrecedence DESCRIPTION confuses me completely. ;-) Furthermore,
it says `These values must be unique on the local policy system...' which
is difficult to ensure, I think.

It is not possible to lookup pmPolicyTable rows by pmPolicyGroup,
since pmPolicyGroup is no INDEX element. However this might be useful.

The pmPolicyMatches DESCRIPTION says `The number of policies ...' while
it means the number of elements. However, I do not see much value in
this object type. When a manager reads this variable, it does not know
at which stage in policy evaluation this value is current.

The pmElementTypeRegOIDPrefix has a DESCRIPTION that explains the
separation of the prefix and the instance identifying part, which
consists of N sub-identifiers. This is what I guess is
correct. However, the text describes that "$1" can be used in place of
any (single?!) decimal sub-identifier, e.g. in section 6.2.1.

I think "$1" is not a good name, since there cannot be a "$2". How
about "*"?

It's explicitly stated that `no state is remembered from the previous
invocation' in filters and actions. So: how can counters be evaluated
by a filter in a meaningful way?

The DESCRIPTION of pmElementTypeRegName says it's a `description'.
I guess it should be a `name'.

The MIB module contains various comments. This is no good idea, since
this information is not accessible. Important information should be
places in DESCRIPTIONs. Unimportent information should be removed.

The pmCapabilitiesTable represents a new way to express agent
capabilities, but with a limited scope. I think it is not a good idea.

pmCapabilitiesType is of type OID. The DESCRIPTION says that IANA will
publish a list of valid values. Presuming this is a good idea at all,
it should be an enumeration type.

The comment in front of the pmCapabilitiesTable and the DESCRIPTION
of the pmCapabilitiesSubType talk about `base OIDs' of MIB modules.
What is this? The MODULE-IDENTITY node? E.g. what is the `base OID'
of IF-MIB?

The DESCRIPTION of pmCapabilitiesEntry talks about some
pmCapabilitiesRestrict* objects, while their names are
pmCapabilitiesModification*.

Some terminology is used not very consequently and not explained
clearly. Sections 5.2 and 5.3 introduce the term `script', while this
mean `filter' and `action'. The term `address' is introduced in 5.1
but not used at other places, e.g. where "$1" is explained in 6.2.1
or in the pmElementTypeRegOIDPrefix DESCRIPTION.

The grammar in section 6.1 containes weired quotes for the
unary_operator and char rules.

Section 6.2.1 contains the conditional sentence `...when the policy
MIB module resides on the same system as the manages elements.' This
conflicts with section 4 which says `These policies are executed on
managed devices, where the objects live [...] and where operations on
these objects will be performed.

getstring() (section 6.2.2) returns a string without a length
(probably NULL terminated). This means that no arbitrary octet strings
(containing zero bytes) can be handled.

Section 6.2.5 is named strcmp() but describes strncmp().

Accessor function names look quite arbitrary to me. Some use only
lowercase letters, others are mixed. Some use `_', others don't.
strcmp() is named after a well-known libc function, lc_strcmp()
is not (strcasecmp()).

What happens if "$1" appears in an action, but not in the filter?

Examples of useful filter/action pairs should be added.

---

From: Steve Waldbusser <[email-masked]>
Date: Wed, 20 Sep 2000 01:23:08 -0700

Frank,

  Thanks for the questions and feedback. My comments are inline.

> (0..65535). Why at all? Why 64k?

2 reasons:

  1) SMICng requires range restrictions on index objects. If I didn't
     put them there people would complain that it didn't compile
     cleanly. Sometimes it's better to just submit :-)
  2) It's useful to declare that 0 is a valid value. Otherwise this
     would be an FAQ.

Unfortunately, when you put in a range, sometimes you have to make an
arbitrary decision on one end of the range. That's what the 65535
represents (a number larger than reasonably possible). One of the
chief benefits of policy-based management is that it reduces the
number of management decisions that need to be made (or in plain
english, there won't be that many policies) , so a limit of 64K
seems reasonable.

> If there's a good reason for this size, why is pmPolicyAction not

Because the SMI restricts octet strings to 64K. It was my intent to be
consistent with both objects but I missed policyAction.

> to 0..2147483647? Why not just Unsigned32?

Good idea.

> and the result of the filter evaluation.

This architecture allows the agent to implement them coupled (more
obvious and simpler to implement) or decoupled (provides more
scalability for larger systems).
  Where coupled means "if (filter) then (action)"
  Where decoupled means:
        Once every filterMaxLatency period
          Evaluate filter and keep a list of matching elements
        Once every actionMaxLatency period
          Evaluate action for every element currently in matching list

Here's the argument for the decoupled mode:

  Usually we think of the actions being more complex and costly to run
  than the filters because they execute set requests, but sometimes
  the filter is more expensive than the action. For example:
     "Select the several trunk ports on a high-density access
     concentrator and configure the line protocol"
    In this case, I might be able to re-assert the correct line
    protocol every 30 seconds since it only touches several ports but
    I'd be unwilling to search through all ports more than once every 30
    minutes.

So, treat FilterMaxLatency as the maximum amount of time you are
willing to wait for an element to be "discovered" as a target of a
policy (or to be discarded if it no longer matches).
Treat ActionMaxLatency as the maximum amount of time you are willing
to leave an element without re-asserting the desired state.

When implementing coupled mode, you simply run the
if/then statement no less frequently than dictated by
MIN(filterMaxLatency, actionMaxLatency).

> since pmPolicyGroup is no INDEX element. However this might be useful.

At the last interim meeting we agreed that each of the several
proposals for grouping and priority (these objects were one) were
flawed and that we were taking them off the table until we could get
it right.

> at which stage in policy evaluation this value is current.

Yes, it should say "The number of elements ...".

Once filterMaxLatency time has passed, you know this value will be
current.

Regarding how valuable this is, this object is intended to:
  A) Be a first level Quality Control indicator for the policy author
    (did it match roughly the number of elements expected?)
  B) be helpful to a technician (how much effect will disabling this
     policy have?)

> I think "$1" is not a good name, since there cannot be a "$2". How

This is work in progress. One way is to have each subid be a separate
parameter $X. The other way is for them to be one parameter and supply
tools for breaking them apart. I have to try some real-world examples,
but I think that $1, $2, $3, ... will be vastly preferable.

> by a filter in a meaningful way?

I'll answer "how" below, but first I'd like to discuss why we
don't want to remember state - the reasons are complicated and
vitally important.

In this discussion, let's say that we have 3 policies (1-3) and 5
elements (A-E). A particular execution context would be a pairing of
one of each (policy 2 on element D is 2D). In the most obvious case,
we would evaluate 1A, 1B, ..., 1E, 2A, ..., 2E, 3A, ..., 3F, one at a
time and in order. However this might no always be the case.

First of all, we want the scheduler to be free to execute these
contexts in any order. This freedom provides simplicity and greater
scalability because it can execute multiple policies in parallel or
suspend a blocked execution while others continue. Therefore
context 2C can't assume the previous invocation was context 2B.
  (This is the rationale for not using static variables).

About the only thing we COULD rationally do is have context 2C keep
state from the last execution of context 2C.

Each combination of policy and element is a separate thread of
execution, so we could use threads, but we don't want to have to
support P*E threads (we must allow P*E to be a very large number,
say P=100 and E=10,000). So threads are out.

We could automatically remember the last state of all automatic
(local) variables, but this could be a huge burden, especially
considering that many scripts will have multiple local variables but
few will need to remember any from invocation to invocation.

However, there may be times that we need to remember state from one
context to another. Here are 3 ways: (sorry for the shorthand)

A) Scratchpad MIB:
   Sparse table with one read-create column

   scratchPadPolicyIndex Integer32,
   scratchPadElement     RowPointer,
   scratchpadVariable    Integer32, -- allows multiple vars per context
   scratchpadValue       OCTET STRING  -- read-create,
                                       -- the only accessible column
   INDEX { scratchpadPolicyIndex, scratchpadElementIndex,
           scratchpadVariable }
   scratchpadValue.policyIndex.elementIndex.variableIndex

   if for ifIndex #17, we retrieved inoctets = 49 and outoctets = 35
   for policy #3, we could store:

     scratchpadValue.3.`ifIndex.17`.1 = 49
     scratchpadValue.3.`ifIndex.17`.2 = 35
   
B) Scratchpad accessor function
   
   void setscratchpad(int varIndex, char *value)
   char *getscratchpad(int varIndex, char *value)

   This function implicitly knows the policyIndex and element and
   will store them internally associated with this context.

   varIndex lets you save multiple variables per context

C) Counter specific accessor function

   int deltaValue(int counterValue)

   This accessor function remembers the countervalue last passed to it
   by this context and performs a delta with the last value,
   returning the delta.

Finally, let's keep some rigor in our requirements analysis (i.e. a
little skeptecism). I can't imagine a filter or action script making a
meaningful policy-based decision based on the 'instantaneous' delta
value derived from 2 counter values (whether they were collected 2
minutes apart or 2 hours apart). It seems that at least some smoothing
is required.
  What type of policies would depend on traffic or error rates?
  What types of rates would they need? smoothed? peak?
  Where is the best place to get those rates? (another MIB perhaps?)

> I guess it should be a `name'.

I think you're right.

> places in DESCRIPTIONs. Unimportent information should be removed.

Well, the way it is now it is best for someone reading the MIB (and
admittedly leaves some high-level stuff out for someone reading MIB
objects through a MIB browser). Personally I think the latter is a
dying breed.

We can:
1. Leave it how it is.
2. Move it into the table description
3. Duplicate it into both comment and table description

I don't care that much - let's see what the consensus is.

> capabilities, but with a limited scope. I think it is not a good idea.

Agent capabilities describes the capabilities of the instrumentation
(i.e. SNMP Agent), is static, and is not accessible by SNMP
retrievals.

The capabilities describe the capabilities of the managed system, they
are dynamic, and they are accessible by SNMP retrievals (as well as by
the capMatch accessor function).

In particular, you need this functionality if you want your policy
to know if diffserv is currently supported by the card plugged into
slot #3.

Jon wrote the current iteration of the capabilities table so I'll let
him field your questions about it.

Regarding the rest of your valuable feedback below, I'll fix those
items in the next draft.

> Examples of useful filter/action pairs should be added.

Thanks,
Steve

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Wed, 20 Sep 2000 10:55:57 +0200

>>>>> Steve Waldbusser writes:

Steve> 2 reasons:

Steve>  1) SMICng requires range restrictions on index objects. If I
Steve> didn't put them there people would complain that it didn't
Steve> compile cleanly. Sometimes it's better to just submit :-)

The problem why SMICng complains is that you use Integer32 and
negative values are certainly illegal in an INDEX. So the range
restriction is indeed required (as long as you use Integer32).

Steve>  2) It's useful to declare that 0 is a valid value. Otherwise
Steve> this would be an FAQ.

I think it is not useful to allow 0 as a valid value. Disallowing 0
has the advantage that you can have "pointers" to the index which may
use 0 to point to a guaranteed non-existing row. This concept has been
used successfully in various other MIBs. (In fact, introducing TCs
once you need to have references is an even better approach, see the
evolution of the IF-MIB.)

Steve> Unfortunately, when you put in a range, sometimes you have to
Steve> make an arbitrary decision on one end of the range. 

No. If you have no architectural reasons to limit the range, then you
can always revert to the upper limit imposed by the data type you are
using. So in this case, since you use Interger32, 2147483647 would be
the right choice.

So putting things together, these indexes should actually be defined
as Integer32 (1..2147483647) or even Unsigned32 (1..4294967295), which
is basically what the SPPI now uses to identify all the instances in
PIBs.

/js

-- 
Juergen Schoenwaelder      Technical University Braunschweig
<[email-masked]>  Dept. Operating Systems & Computer Networks
Phone: +49 531 391 3289    Bueltenweg 74/75, 38106 Braunschweig, Germany
Fax:   +49 531 391 5936    <URL:http://www.ibr.cs.tu-bs.de/~schoenw/>

---

From: Frank Strauss <[email-masked]>
Date: 20 Sep 2000 11:43:31 +0200

Steve> 2 reasons:

Steve>   1) SMICng requires range restrictions on index objects. If I didn't
Steve>      put them there people would complain that it didn't compile
Steve>      cleanly. Sometimes it's better to just submit :-)
Steve>   2) It's useful to declare that 0 is a valid value. Otherwise this
Steve>      would be an FAQ.

I'd suggest using Unsigned32 without restrictions. The reason why you
need range restrictions for Integer32 is that an index sub-identifier
must not be negative. SMICng is doing right.

Steve> Unfortunately, when you put in a range, sometimes you have to
Steve> make an arbitrary decision on one end of the range. That's what
Steve> the 65535 represents (a number larger than reasonably
Steve> possible).

Then you should specify the maximum value.

Steve> One of the chief benefits of policy-based management is that it
Steve> reduces the number of management decisions that need to be made
Steve> (or in plain english, there won't be that many policies) , so a
Steve> limit of 64K seems reasonable.

Well, it seems just arbitrary to me. ;-)

>> If there's a good reason for this size, why is pmPolicyAction not

Steve> Because the SMI restricts octet strings to 64K. It was my
Steve> intent to be consistent with both objects but I missed
Steve> policyAction.

Yes. The SMI *is* already restricted to this size. See SNMPv2-SMI:

        -- OCTET STRINGs with a more restrictive size
        -- may also be used
        string-value
            OCTET STRING (SIZE (0..65535)),

>> and the result of the filter evaluation.

Steve> This architecture allows the agent to implement them coupled (more
Steve> obvious and simpler to implement) or decoupled (provides more
Steve> scalability for larger systems). [...]

Ok. I think, I understand these two ways of operation.  There are
significant differences. E.g. in the decoupled case, it might happen
that a filter is evaluated true, then again false and the action gets
never executed. How does a manager recognize, how the agent works?

>> at which stage in policy evaluation this value is current.

Steve> Yes, it should say "The number of elements ...".

Steve> Once filterMaxLatency time has passed, you know this value will be
Steve> current.

May be a note should be added, that the agent must not update
pmPolicyMatches for each row while it runs through the policy table.

>> by a filter in a meaningful way?

Steve> I'll answer "how" below, but first I'd like to discuss why we
Steve> don't want to remember state - the reasons are complicated and
Steve> vitally important. [...]

Steve> About the only thing we COULD rationally do is have context 2C keep
Steve> state from the last execution of context 2C.

Yes. This is what you need for counter evaluation.

Steve> Each combination of policy and element is a separate thread of
Steve> execution, so we could use threads, but we don't want to have to
Steve> support P*E threads (we must allow P*E to be a very large number,
Steve> say P=100 and E=10,000). So threads are out.

Well, let's say it's an implementation issue.

Steve> We could automatically remember the last state of all automatic
Steve> (local) variables, but this could be a huge burden, especially
Steve> considering that many scripts will have multiple local variables but
Steve> few will need to remember any from invocation to invocation.

Steve> However, there may be times that we need to remember state from one
Steve> context to another. Here are 3 ways: (sorry for the shorthand)

Steve> A) Scratchpad MIB: [...]

You are kidding!? ;-)

Steve> B) Scratchpad accessor function [...]
   
Sounds to be the most reasonable solution of these three proposals.

Steve> C) Counter specific accessor function [...]

Counters were just one example. Another one would be the detection of
state changes, where states are represented by values of an enumeration
type object. So this counter specific solution would not be generic
enough, I guess.

Steve> Finally, let's keep some rigor in our requirements analysis (i.e. a
Steve> little skeptecism). I can't imagine a filter or action script making a
Steve> meaningful policy-based decision based on the 'instantaneous' delta
Steve> value derived from 2 counter values (whether they were collected 2
Steve> minutes apart or 2 hours apart). It seems that at least some smoothing
Steve> is required.

Yes. I can easily imagine policies that take actions when too many
errors occur. This means you need guaranteed time deltas or some
implicit evaluation (in case of counters).

Steve>   What type of policies would depend on traffic or error rates?

Policies that limit an ISP customers traffic volume per days.

Policies that adapt routing for `gold' customers in case of many
errors on the cheaper routes.

Steve>   What types of rates would they need? smoothed? peak?

Well, every computed rate is `smoothed' somehow. What people - well at
least I ;-) - would expect (except from discontinuities and wraps) is:

            value-of-this-eval - value-of-last-eval
  value  =  ---------------------------------------
                     this-time - last-time

Steve>   Where is the best place to get those rates? (another MIB perhaps?)

My assumption is: by reading from the relevant MIB objects and computing
in the filter script using some kind of inter-evaluation persistent data
(script variables or accessor functions?).

>> capabilities, but with a limited scope. I think it is not a good idea.

Steve> Agent capabilities describes the capabilities of the instrumentation
Steve> (i.e. SNMP Agent), is static, and is not accessible by SNMP
Steve> retrievals.

Steve> The capabilities describe the capabilities of the managed system, they
Steve> are dynamic, and they are accessible by SNMP retrievals (as well as by
Steve> the capMatch accessor function).

Steve> In particular, you need this functionality if you want your policy
Steve> to know if diffserv is currently supported by the card plugged into
Steve> slot #3.

Steve> Jon wrote the current iteration of the capabilities table so I'll let
Steve> him field your questions about it.

I agree that a way to retrieve agent capabilities by SNMP is a good
thing. But I don't think it's a good idea to do this just in the PM
context.

---

From: Jon Saperia <[email-masked]>
Date: Wed, 20 Sep 2000 10:10:07 -0400

> him field your questions about it.

Frank, this is a response to your excellent comments to the PM module. I
have created a new subject line so that everyone does not have to read all
of the other comments. The short answer is that Steve said it well. I have
more detail below.

The capabilities that we use here are not the same thing as what people have
come to understand as capabilities in the AGENT-CAPABILITIES sense. Nor
could this be accomplished with the sysORTable. A much richer set of
conditions needed expression than either of these mechanisms allowed.

There were some questions posed by Andrew Smith some months ago on this list
that led to the current capabilities table in the current form. What the
capabilities table is designed to do is express what a 'system' not an agent
can do. It is also designed to allow the system to express restrictions as
well. For example, if I had a policy that required that parameters be set at
a specific value and the system did not support values in that range, this
could be determined via this table. Please also note that these restrictions
need not be static, that is to say that I can imagine cases where
restrictions might be temporary or change as new software is added or
removed from the system.  For example, we need these capabilities to
express:

    1. Mechanism specific abilities such as queuing methods supported in the
case of DiffServ or Encapsulating Security Protocol in the case of security.
These objects may not always be visible at the OID level.

    2. Restrictions, or or enhancements to basic capabilities. For example
if a standard supported mechanisms 1, 2, and 3 and a vendor had a version
that supported their own mechanism type 4, this would be the way for the
system to 'advertise' this ability. This would be the way that a policy
management application would come to know that a system had
implementation-specific enhancements (or restrictions) Once again, these
abilities are not always going to be static and values in this table could
change.

Hope this helps.

/jon

I have attached the exchange between Andrew and myself on this topic. It was
sent to the list on 6/27 so it may have faded from our collective memory.
:-)

>> on 06/13/2000 6:09 PM, Andrew Smith at [email-masked] wrote:
>> 
>>> Jon,
>>> 
>>> There are several levels of granularity on which this could work:
>>> 
>>> 1. Lowest as in "I support the diffServMeterTable", "I do not support the
>>> diffServAlgDropperTable".
>> 
>> I think the mods above support this.
>>> 
>>> 2. Slightly higher as in "I support head droppers but not tail droppers"
>> 
>> Depends on the layout, but I think this would be handled.
>>> 
>>> 3. Higher as in "I support head droppers that feed into strict-priority
>>> schedulers but not into WRR schedulers"
>> 
>> I doubt very much that this would be correctly dealt with in the objects I
>> wrote. At some point, humans will have to be involved. I would be happy to
>> say we can not deal with this type of condition and that the person writing
>> the 'rule' would have to take this into consideration. Of course this is not
>> an excuse for the agent not doing good error checking :-)
>>> 
>>> There's also a "number of instances" dimension to it: "I support 3 strict
>>> priority scheduled queues per interface", "I support 3 strict priority OR 2
>>> WRR scheduled queues per interface but not both at the same time".
>> 
>> This in my view is a 'capacity' question. A valid one but dealt with
>> elsewhere. We have yet to have a full discussion on this topic, though I
>> look forward to it.
>>> 
>>> There could also be an even more abstract notion of "I support AF but not
>>> EF" but that's somewhat different (since we don't have an AF or EF MIB -
>>> just one that hacks on the individual components that work together to
>>> provide AF or EF).
>> 
>> Since this is not a capability at this level. I am not sure how to deal with
>> this. Of course we could create mechanism specific objects for things like
>> this and they could be modified by this table.
>>> 
>>> This could get quite fun ... but the more information like this that an
>>> agent can export, the easier will be the job of the manager and the more
>>> multi-vendor this whole config thing can become.
>> 
>> Agreed. Of course we have to also make it implementable - some people may
>> feel this table with the 7 objects is already to hard.  Others may feel it
>> does not go far enough.
>> 
>> Opinions?
>> 
>> /jon
>

---

From: Frank Strauss <[email-masked]>
Date: 20 Sep 2000 18:58:24 +0200

Hi!

Thanks, Jon, for the detailed answer!

I think, do understand that the capabilities handled by the PM MIB are
different from what the (broken) AC concept can do. But I'm convinced
that it's not a good idea to do what the pmCapabilitiesTable does in
the current way and scope.

The basic reason for my scepticism: Policy management per se as
represented by the PM MIB should be independent from domains and
mechanisms (as defined in the diffserv I-D).

[I'll be on vacation from now on until the end of this month. So I'll
 probably not be able to follow this list during the next days.]

 Frank

---

From: Jon Saperia <[email-masked]>
Date: Thu, 21 Sep 2000 07:36:50 -0400

> mechanisms (as defined in the diffserv I-D).
We very much agree. The policy module is at a layer of abstraction above the
mechanism and implementation-specific details. It is independent, that is
why the mechanism specific MIB Modules (or their equivalent objects in other
MIB Modules) must  have their capabilities registered into the Policy MIB
Module. The capabilities are far from the details which are below the level
of the Policy MIB Module. To use your DiffServ example, the base OID of the
DiffServ objects is all that would be in the capabilities table.
Enhancements or restrictions would also be registered. This avoids the
problem of the manager having to 'poke' around the entire device for
information such as does this device support DiffServ and what are its
restrictions. The capabilities table is a list of pointers to the details,
not the details.

The manager must know what the 'capabilities' of the system are to
efficiently send policies that are relevant to managed devices. Otherwise
all policies would be sent to all devices, far from efficient. The mechanism
and implementation specifics are dealt with in modules outside of the Policy
MIB Module as in the case with our Policy MIB Module (or its equivalent
objects if integrated into the current DiffServ Module).

> probably not be able to follow this list during the next days.]
have a good vacation

/jon

---

From: Frank Strauss <[email-masked]>
Date: 22 Sep 2000 16:27:16 +0200

>> mechanisms (as defined in the diffserv I-D).

Jon> We very much agree. The policy module is at a layer of
Jon> abstraction above the mechanism and implementation-specific
Jon> details. It is independent, that is why the mechanism specific
Jon> MIB Modules (or their equivalent objects in other MIB Modules)
Jon> must have their capabilities registered into the Policy MIB
Jon> Module. The capabilities are far from the details which are below
Jon> the level of the Policy MIB Module. To use your DiffServ example,
Jon> the base OID of the DiffServ objects is all that would be in the
Jon> capabilities table.  Enhancements or restrictions would also be
Jon> registered. This avoids the problem of the manager having to
Jon> 'poke' around the entire device for information such as does this
Jon> device support DiffServ and what are its restrictions. The
Jon> capabilities table is a list of pointers to the details, not the
Jon> details.

Yes, but it *is* domain specific knowledge. Two questions:

1. What is a `base OID' of a MIB (or of the DiffServ objects)?

2. Who or what has to register capabilities? - Is it the PM MIB sub-agent
   itself or has it to provide an interface that allows other sub-agents
   to register their capabilities?
   The latter is the only way to keep the PM MIB sub-agent independent
   from domains and mechanisms, but it is not described at all in the
   document.