Subject: snmpconf A non-trivial policyAction example

From: Steve Waldbusser <[email-masked]>
Date: Thu, 09 Nov 2000 00:52:54 -0800

There are many, many simple examples of policyActions that can be
expressed in only 1 or 2 lines of code. (turn off a port, set a
configuration option, etc.)

In order to see if the current version of the language is up to some
more complex tasks, I started writing some examples. I figured an
RMON2 example would satisfy the non-triviality test.

The example below sets up RMON2 AlMatrix monitoring on an interface
(for the RMON-ignorant: basically this will keep track of
application conversations). This is done by creating and configuring
the hlMatrixControlEntry.

An action like this might be associated with a filter such as
'roleMatch("trunk")' which would be used to automatically set up
AlMatrix monitoring only on trunk ports (not on access ports). This is
a good configuration practice because the trunk port is a more
efficient place to sniff the traffic rather than double-counting it on
each access port.

Enough RMON2 background, here's the script:

    char oid[STRINGLEN], value[STRINGLEN];
    int index, pdu;

    pdu = 0; /* Use PDU #0 */
    if (!(index = setRowStatus("hlMatrixControlStatus.*", 20)))
        return; /* couldn't find a free entry */

    sprintf(oid, "hlMatrixControlDataSource.%d", index);
    sprintf(value, "ifIndex.%d", $1);
    writeVarbind(pdu, 0, oid, TYPE_OBJECT_IDENTIFIER, value, 0);

    sprintf(oid, "hlMatrixControlNlMaxDesiredEntries.%d", index);
    writeVarbind(pdu, 1, oid, TYPE_INTEGER, "1000", 0);

    sprintf(oid, "hlMatrixControlAlMaxDesiredEntries.%d", index);
    writeVarbind(pdu, 2, oid, TYPE_INTEGER, "1000", 0);

    sprintf(oid, "hlMatrixControlOwner.%d", index);
    writeVarbind(pdu, 3, oid, TYPE_OCTET_STRING, "policy manager", 14);

    sprintf(oid, "hlMatrixControlStatus.%d", index);
    writeVarbind(pdu, 4, oid, TYPE_INTEGER, "1", 0);

    snmpsend(0, 5, OP_SET);

It's pretty straightforward. Basically it uses setRowStatus to find
and reserve an unused row, and then creates a single PDU with all of
the column values filled in and sends the SET.

This is useful to illustrate how reasonable it is to do some fairly
complex stuff, but it has a flaw which I'll identify and then fix.
Scripts run periodically so that they can re-assert whatever state the
policy is expected to enforce. The script above would create a control
entry for every iteration.

The fix is to see if a control entry already exists and, if so,
exit. The first time we create an entry we store the dataSource in the
scratchpad and then every time we execute, we retrieve it from the
scratchpad (if it's there), check to see if it still represents a
matrix on "this" interface, and if so, return immediately.

Here's the complete example. The new code is lines 4-9 and 18:

    char oid[STRINGLEN], value[STRINGLEN];
    int index, length, pdu;

    /* retrieve dataSource and make sure it exists */
    if (getScratchpad(0, oid, &length) && exists(oid)){
        getvar(oid, value, &length);
        if (subid(value, 10) == $1) /* still points to this interface */

            return;
    }

    if (!(index = setRowStatus("hlMatrixControlStatus.*", 20)))
        return; /* couldn't find a free entry */

    pdu = 0; /* Use PDU #0 */
    sprintf(oid, "hlMatrixControlDataSource.%d", index);
    sprintf(value, "ifIndex.%d", $1);
    writeVarbind(pdu, 0, oid, TYPE_OBJECT_IDENTIFIER, value,
strlen(value));
    setScratchpad(0, oid, strlen(oid));

    sprintf(oid, "hlMatrixControlNlMaxDesiredEntries.%d", index);
    writeVarbind(pdu, 1, oid, TYPE_INTEGER, "1000", 0);

    sprintf(oid, "hlMatrixControlAlMaxDesiredEntries.%d", index);
    writeVarbind(pdu, 2, oid, TYPE_INTEGER, "1000", 0);

    sprintf(oid, "hlMatrixControlOwner.%d", index);
    writeVarbind(pdu, 3, oid, TYPE_OCTET_STRING, "policy manager", 14);

    sprintf(oid, "hlMatrixControlStatus.%d", index);
    writeVarbind(pdu, 4, oid, TYPE_INTEGER, "1", 0);

    snmpsend(0, 5, OP_SET);

I'm pretty happy how these examples turned out.

By the way, there are a couple of subtle differences in the API that are
reflected here but are not yet shown in the draft. The most obvious is
the inclusion of sprintf.

Steve

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Thu, 9 Nov 2000 10:43:36 +0100

>>>>> Steve Waldbusser writes:

[...]

Steve> Enough RMON2 background, here's the script:

[...]

Steve> It's pretty straightforward. Basically it uses setRowStatus to
Steve> find and reserve an unused row, and then creates a single PDU
Steve> with all of the column values filled in and sends the SET.

FYI, here is the same script in Tcl using the Tnm extension as you can
run and execute it today.

proc create_hlMatrixControlEntry {s ifIndex} {

    if {[catch {setRowStatus hlMatrixControlStatus} index]} {
        return
    }

    lappend vbl [list hlMatrixControlDataSource.$index ifIndex.$ifIndex]
    lappend vbl [list hlMatrixControlNlMaxDesiredEntries.$index 1000]
    lappend vbl [list hlMatrixControlAlMaxDesiredEntries.$index 1000]
    lappend vbl [list hlMatrixControlOwner.$index "policy manager"]
    lappend vbl [list hlMatrixControlStatus.$index 1]

    $s set $vbl
}

This example makes it very obvious to me that actions will be complex
over time and that a mechanism is needed to write "procedures" (like
the one above) which you can call and reuse in actions. (I can easily
imagine cases where a policy rule action instantiates many rows and
the hlMatrixControlEntry is just one of them.)

Steve> This is useful to illustrate how reasonable it is to do some
Steve> fairly complex stuff, but it has a flaw which I'll identify and
Steve> then fix.  Scripts run periodically so that they can re-assert
Steve> whatever state the policy is expected to enforce. The script
Steve> above would create a control entry for every iteration.

Steve> The fix is to see if a control entry already exists and, if so,
Steve> exit. The first time we create an entry we store the dataSource
Steve> in the scratchpad and then every time we execute, we retrieve
Steve> it from the scratchpad (if it's there), check to see if it
Steve> still represents a matrix on "this" interface, and if so,
Steve> return immediately.

Steve> Here's the complete example. The new code is lines 4-9 and 18:

[...]

The fix is flawed as well since the oid in the scratchpad does not say
which SNMP agent the action is executed on. If you want to use the
action on multiple RMON2 boxes, bad things will happen. I am also
concerned about the error handling - what happens if e.g. snmpsend(0,
5, OP_SET); fails with an SNMP error? Will the policy repeat in a loop
to fire this action regularily??

/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: Steve Waldbusser <[email-masked]>
Date: Thu, 09 Nov 2000 10:35:17 -0800

>     lappend vbl [list hlMatrixControlStatus.$index 1]

This API won't work in our environment because agents don't have MIB
compilers and therefore don't know the type of a variable. For example, in
the maxDesiredEntries varbinds above, it's not clear whether to send the
integer 1000 or the 4 character string "1000".

I'm definitely interested in continuing to work on the API/language to make
scripts shorter and easier to write.

> the hlMatrixControlEntry is just one of them.)

Interesting, I had the opposite reaction. I was struck by the fact that the
function was well within a manageable size and that no functional
decomposition seemed obvious.

> which SNMP agent the action is executed on.

This is handled automatically by the execution environment which knows the
address of "this element".

> I am also

Yes, and I think this is desired behavior. A policy is trying to enforce a
certain behavior. If the enforcement fails temporarily I think that it
should continue to try. In this case, the maxLatency won't be too high
(maybe run once per hour) and the (computational/operational) cost of
failing is low. In another circumstance where the cost of failing was high,
we could set something in the scratchpad to disable enforcement on this
interface.

Steve

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Thu, 9 Nov 2000 21:35:12 +0100

>>>>> Steve Waldbusser writes:

Steve> This API won't work in our environment because agents don't
Steve> have MIB compilers and therefore don't know the type of a
Steve> variable. For example, in the maxDesiredEntries varbinds above,
Steve> it's not clear whether to send the integer 1000 or the 4
Steve> character string "1000".

Sure, I can write the same script using OIDs and having type
information in there as well. The Tnm API allows you to do both.  I
decided to go with the short version since you used descriptors rather
than OIDs in your example as well. :-)

>> This example makes it very obvious to me that actions will be
>> complex over time and that a mechanism is needed to write
>> "procedures" (like the one above) which you can call and reuse in
>> actions. (I can easily imagine cases where a policy rule action
>> instantiates many rows and the hlMatrixControlEntry is just one of
>> them.)

Steve> Interesting, I had the opposite reaction. I was struck by the
Steve> fact that the function was well within a manageable size and
Steve> that no functional decomposition seemed obvious.

We are working on a language which will have primitives on a much
higher level, e.g. a primitive to 'ensure that the following row
exists in a table'.  Your code (and my code as well) was more part of
the assembler code for what I consider to be a primitive in a decent
policy language. But I know, we have different opinions on this.

>> The fix is flawed as well since the oid in the scratchpad does not
>> say which SNMP agent the action is executed on.

Steve> This is handled automatically by the execution environment
Steve> which knows the address of "this element".

Address? EngineID? EngineID + ContextName? Is it specified anywhere
what it is?

>> I am also concerned about the error handling - what happens if
>> e.g. snmpsend(0, 5, OP_SET); fails with an SNMP error? Will the
>> policy repeat in a loop to fire this action regularily??

Steve> Yes, and I think this is desired behavior. A policy is trying
Steve> to enforce a certain behavior. If the enforcement fails
Steve> temporarily I think that it should continue to try. In this
Steve> case, the maxLatency won't be too high (maybe run once per
Steve> hour) and the (computational/operational) cost of failing is
Steve> low. In another circumstance where the cost of failing was
Steve> high, we could set something in the scratchpad to disable
Steve> enforcement on this interface.

There are errors where retrying simply does not make any sense (e.g.
a VACM configuration which does not give you access to the target).
Sure, it is much easier to ignore errors. But my experience is that
systems that ignore errors are not useful once something goes wrong.
Probably another point where we just have very different opinions.

/js

---

From: Steve Waldbusser <[email-masked]>
Date: Thu, 09 Nov 2000 20:48:48 -0800

> than OIDs in your example as well. :-)

Sorry, I assumed that the API didn't specify the type since you didn't
specify the type in the example.

As a side note, the use of descriptors in my examples is part of a
convention described and encouraged in the PM draft in which humans read and
write scripts with descriptors and then management stations translate them
into dotted decimal before shipping them to agents.

Another side note after looking at the TNM API: I see there is some
optionality allowed for the parameters of varbind lists, but I'm surprised
to see the middle parameter of the 3-tuple be omitted. It would seem to be
very difficult to parse correctly. Was this just a mistake in your example?

> policy language. But I know, we have different opinions on this.

I'm not sure we differ at all on this. If you look at the type of routines
I've added to the library, we seem to be on the same wavelength (i.e.
setRowStatus, searchColumn). In other words, I believe that there are
several high-level library routines that can immensely simplify script
writing.

One thing I brought up at the meeting was that it may be possible to further
simplify the addition of rows with a library function specifically designed
for this purpose. I may make a proposal soon.

> Address? EngineID? EngineID + ContextName? Is it specified anywhere

       "The agent will retrieve the instance in the same SNMP context
        in which the element resides. Note that no actual SNMP PDU
        needs to be generated and parsed when the policy MIB module
        resides on the same system as the managed elements."

This text can stand a bit of improvement, but I think it is basically clear.
Also, note that the last published draft has this text for 5 of the 6 SNMP
functions but doesn't have it for the snmpsend function. You'll see this fix
in the next draft.

> Probably another point where we just have very different opinions.

I hope it was clear that I was just talking about this RMON2 example. There
are certainly situations where the cost of failure is high (too much CPU or
other operational impact) or where a remedial operation is desired. If error
recovery is mandated, then it is easy to code it in. I just don't feel it is
warranted in this case.

Nevertheless, I'm not sure your VACM example would make me change my mind.
From an operational point of view, if and when the VACM configuration on an
interface was fixed, I'd like the alMatrix entry to be created because it
moves me closer to my policy which is "all trunk ports are monitored with
alMatrix".

If we were to disable a policy on error (never to retry again), would a
momentary misconfiguration (of security, say) cause the element to forever
be out of policy? I don't think this is the right behavior.

The issue of error handling comes up when (1) failure is expensive or (2)
recovery was an option.

(1) An interesting example of expensive failure is when a log message is
generated whenever an alMatrix creation fails. The cost here is operational
(confusion, time, etc), not computational (CPU, disk, etc). In such a case,
I'd want to make sure the policy didn't retry more than once a day or so
after failure. I could accomplish this with a scratchpad variable that
recorded the last failure time and didn't try again within some window of
that time.

(2) Sometimes recovery is an option when it might be possible to modify the
request and try again or use another mechanism entirely. In such
circumstances I see nothing wrong with adding error recovery code. In this
RMON2 example, there isn't anything about the request that the agent could
complain about.

Steve

---

From: David Partain <[email-masked]>
Date: Fri, 10 Nov 2000 09:48:55 +0100

Greetings,

I just have one quick clarifying question:

Jürgen writes:

> action on multiple RMON2 boxes, bad things will happen.

Is the implication of this statement that you are expecting
the execution environment to send SNMP set requests to multiple
other devices in a mid-level manager kind of way?

I see the "sets" in Steve's example as internal within a device,
not something that's sent to other devices.

Cheers,

--
David Partain                  [email-masked]
Ericsson Radio Systems AB      Tel:    +46 13 28 41 44
Research and Innovation        Mobile: +46 70 633 0515
P.O. Box 1248                  Fax:    +46 13 28 75 67
SE-581 12  Linköping, Sweden

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Fri, 10 Nov 2000 14:45:32 +0100

>>>>> David Partain writes:

David> I just have one quick clarifying question:

David> Jürgen writes:

Thanks for the umlaut. :-)

>> say which SNMP agent the action is executed on. If you want to use
>> the action on multiple RMON2 boxes, bad things will happen.

David> Is the implication of this statement that you are expecting the
David> execution environment to send SNMP set requests to multiple
David> other devices in a mid-level manager kind of way?

David> I see the "sets" in Steve's example as internal within a
David> device, not something that's sent to other devices.

Well, I have not been at the interim and so I am only aware of what is
in the documents. And <draft-ietf-snmpconf-pm-03.txt> says in section
8.1:

:  - General SNMP Functions
:
:    The General SNMP functions allow nearly any legal SNMP Message to
:    be generated, including those with multiple varbinds, getNext
:    operations, notifications, and messages with explicit addressing
:    or security specifications.

To me "messages with explicit addressing or security specifications"
sounds like you are able to talk to other SNMP engines. But even if
this WG restricts itself to a single local engine, then the question
remains which context (perhaps you want to restrict yourself to a
specific context in an SNMP engine?) and which set of security
parameters you use (unless you propose to bypass VACM completely -
which I hope you do not plan to do).

I personally would like to use a policy configuration engine with
existing deployed SNMP devices and MIBs. The assumption that each 
and every device implements the policy MIB is in my view not very
convincing.

/js

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Fri, 10 Nov 2000 15:37:08 +0100

>>>>> Steve Waldbusser writes:

Steve> Another side note after looking at the TNM API: I see there is
Steve> some optionality allowed for the parameters of varbind lists,
Steve> but I'm surprised to see the middle parameter of the 3-tuple be
Steve> omitted. It would seem to be very difficult to parse
Steve> correctly. Was this just a mistake in your example?

I do not find it hard to determine the number of arguments in a list
and to parse the rest accordingly. In fact, Tmn even allows to omit
the value when you do read requests. And you can write OIDs in many
different ways. (But I guess this is not really relevant here.)

>>  Address? EngineID? EngineID + ContextName? Is it specified
>> anywhere what it is?

Steve>        "The agent will retrieve the instance in the same SNMP
Steve> context in which the element resides. Note that no actual SNMP
Steve> PDU needs to be generated and parsed when the policy MIB module
Steve> resides on the same system as the managed elements."

Steve> This text can stand a bit of improvement, but I think it is
Steve> basically clear.  Also, note that the last published draft has
Steve> this text for 5 of the 6 SNMP functions but doesn't have it for
Steve> the snmpsend function. You'll see this fix in the next draft.

I think it is not clear since it is conditional. What happens if the
MIB module does not reside on the same system as the managed elements?
Note also that the terminology is not precise. Does the phrase "same
system as the managed elements" mean it is the same context with the
same SNMP engine? (We now have this great architecture to clearly
say what we mean and we should thus use the right terminology.)

Steve> If we were to disable a policy on error (never to retry again),
Steve> would a momentary misconfiguration (of security, say) cause the
Steve> element to forever be out of policy? I don't think this is the
Steve> right behavior.

Steve> The issue of error handling comes up when (1) failure is
Steve> expensive or (2) recovery was an option.

Ignoring the problem is not an answer. You need to report and handle
errors somehow. A policy system which shows all lights turned to green
while at the same time the lower layers do not install any policies
due to e.g. VACM configuration errors is IMHO useless.

My experience so far is that programming distributed systems is always
80 % time spend on getting error handling right and 20 % spend on the
functional stuff. I would be surprised if this rule does not apply
here as well.

/js

---

From: Steve Waldbusser <[email-masked]>
Date: Fri, 10 Nov 2000 13:51:14 -0800

> and every device implements the policy MIB is in my view not very

Let me try to shed some light on all of this. One important point that will
be made in the following paragraphs is that talking to other systems (over
the network) is, in fact, orthogonal to using explicit addressing or
security specifications.

The architecture specifically allows for the policy MIB to talk to other
systems. One reason this is done is to allow proxy architectures where a
local policy agent can enforce policy on a workgroup of managed systems that
don't support the policy MIB.

The policy agent gathers a set of elements of various types and executes
policies on those elements. The policy agent is free to gather those
elements from any context on the local system and also from remote systems.
It is the job of the execution environment to forward a policy's management
requests to the element. When the execution environment executes a policy on
a non-local element, it sends those requests in SNMP PDUs to the address and
context which are known by the execution environment. In other words, for
this situation, whether elements are local or non-local, the policy code is
the same (devoid of addressing information).

This situation is complete for communications with "this element" and for
other elements related to "this element". However, sometimes in a policy I
will want to read or write information that is in a different context in the
same system. I also can't rule out the need to read or write information on
another system. So for these purposes the PM draft talks about the ability
to address messages to any system/context. This functionality has not yet
been added to the API, but it will be.

Finally, regarding the example code that led up to all of this: the RMON2
"Add alMatrix" policy action code will add an alMatrix entry to any
interface element (on any context/system) managed by the PM agent and
selected by the corresponding policyFilter. It doesn't need addressing
information, even if the PM agent is "proxying" for other systems.

Steve

---

From: Steve Waldbusser <[email-masked]>
Date: Fri, 10 Nov 2000 14:23:50 -0800

> different ways. (But I guess this is not really relevant here.)

I just though that if I omit the middle of 3 arguments (leaving two), it is
difficult to tell which of the last 2 arguments was left off. For example,
if I send { 1.3.6.1.4.1.2.3.4.5, integer }, do I interpret that as sending
an unspecified integer value, or as sending a string whose value is
"integer".

Most varargs schemes allow you to optionally remove items from the end of
the sequence only.

> say what we mean and we should thus use the right terminology.)

Your first question is answered in the email I just sent.

Regarding your second question, it doesn't mean "same context" (context is
really more of a modelling concept than a physical concept anyway). And I
don't think that there is any terminology in our architecture that helps.
It's simply pointing out that if the PM agent and the target agent are on
the same system, you can getint() without issuing a PDU. And if they are in
different subagents on the same system, you can (probably) getint() without
issuing a PDU. And if they are on different systems you probably have to
send an SNMP PDU on the wire between the two systems.

I think the "same system" phrase is pretty good but am willing to use any
better terminology.

> functional stuff. I would be surprised if this rule does not apply

You think I'm saying that error handling is not important in the general
case. I'm not. Please re-read my comments and you'll see.

Steve

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Sat, 11 Nov 2000 16:01:03 +0100

>>>>> David Partain asked:

[...]

[...]

Steve> It is the job of the execution
Steve> environment to forward a policy's management requests to the
Steve> element. When the execution environment executes a policy on a
Steve> non-local element, it sends those requests in SNMP PDUs to the
Steve> address and context which are known by the execution
Steve> environment. In other words, for this situation, whether
Steve> elements are local or non-local, the policy code is the same
Steve> (devoid of addressing information).

[...]

So your answer to David's question is: 

   Yes, the policy execution environment can act as a mid-level manager.

Please correct me if I am wrong.

/js

---

From: Juergen Schoenwaelder <[email-masked]>
Date: Sat, 11 Nov 2000 16:17:47 +0100

>>>>> Steve Waldbusser writes:

Steve> I just though that if I omit the middle of 3 arguments (leaving
Steve> two), it is difficult to tell which of the last 2 arguments was
Steve> left off. For example, if I send { 1.3.6.1.4.1.2.3.4.5, integer
Steve> }, do I interpret that as sending an unspecified integer value,
Steve> or as sending a string whose value is "integer".

The Tmn API is clear about this.

three list elements:

      1 oid (or something that resolves to an oid)
      2 type name
      3 value

two list elements:

      1 oid (or something that resolves to an oid)
      2 value

one list element:

      1 oid (or something that resolves to an oid)

Steve> Most varargs schemes allow you to optionally remove items from
Steve> the end of the sequence only.

See, these are lists and not varargs. ;-)

Steve> Regarding your second question, it doesn't mean "same context"
Steve> (context is really more of a modelling concept than a physical
Steve> concept anyway).

He? SNMPv3 has scoped PDUs - is that not physical enough?

Steve> And I don't think that there is any terminology in our
Steve> architecture that helps. It's simply pointing out that if the
Steve> PM agent and the target agent are on the same system, you can
Steve> getint() without issuing a PDU. And if they are in different
Steve> subagents on the same system, you can (probably) getint()
Steve> without issuing a PDU. And if they are on different systems you
Steve> probably have to send an SNMP PDU on the wire between the two
Steve> systems.

For me, it would be easier to understand the complete picture if
things were described based on the SNMP architecture and its
terminology. But given the fact that you stated (as I understood it)
that an execution engine can act as a mid-level manager, then the
distinction between a "local getint()" and a "remote getint()" does
not really matter and you may as well remove that text.

[...]

Steve> You think I'm saying that error handling is not important in
Steve> the general case. I'm not. Please re-read my comments and
Steve> you'll see.

<soap>
When I started to write the scotty package, I introduced a job command
which allows to schedule the execution of Tcl procedures regularily
with the capability to suspend and resume jobs. However, I did not
give jobs full control how they should behave on error situations.  In
fact, the scheduler was just scheduling them again.  Experience with
real world code later told me that this was a design mistake and there
are now job properties which give complete control over what happens
if things go crazy. This was needed to write robust code.
</soap>

/js

---

From: "Wayne F. Tackabury" <[email-masked]>
Date: Sat, 11 Nov 2000 10:58:07 -0500

>Please correct me if I am wrong.

It is my understanding that the policy environment can *absolutely* act as 
a mid-level manager.  In my vision, this is a transitional product strategy 
for allowing a Policy MIB environment get deployed in a world with no 
Policy MIB-savvy managed element agent environments initially.

In any event, there are plenty of text fragments through the current MIB 
which indicate this capability unambiguously (even if we haven't discussed 
explicit considerations for it very much if at all).

Thanks for forcing the issue, Juergen, since it's an important one.

Wayne

---

From: Steve Waldbusser <[email-masked]>
Date: Sat, 11 Nov 2000 14:01:01 -0800

> He? SNMPv3 has scoped PDUs - is that not physical enough?

I meant something else.

The decision to place an object in a different context on the same agent is
based on information modeling desires. When an object is on another system
it simply reflects physical realities. The point is that architecturally
speaking, moving an object to another context on the same agent does not
make it harder to communicate with.

> ... But given the fact that you stated (as I understood it)

I disagree - I believe the text is helpful to the implementor and should
remain:

"Note that no actual SNMP PDU needs to be generated and parsed when the
policy MIB module resides on the same system as the managed elements."

> if things go crazy. This was needed to write robust code.

Thanks for the story. It is abundantly clear that we agree one hundred
percent.

Steve

---

From: Steve Waldbusser <[email-masked]>
Date: Sat, 11 Nov 2000 14:00:36 -0800

>    Yes, the policy execution environment can act as a mid-level manager.

Yes, it can.

The cool thing about this is that it came for free - we didn't do anything
explicit to make this possible and script witers don't need to write their
script any differently to allow their scripts to work when a network
separates them from the target agent.

Steve

---

From: Pablo Halpern <[email-masked]>
Date: Wed, 15 Nov 2000 10:59:10 -0500

At 12:52 AM 11/9/00 -0800, Steve Waldbusser wrote:

In my never-ending crusade to introduce a native string type, let me 
rewrite the above script using such a native type based on the following 
assumptions:

         a quoted string is of type "string"
         Strings can be assigned using the '=' operator
         Strings can be concatenated using the '+' infix
                 operator (as in C++)
         Using the + operator to add an integer to a string
                 converts the integer to a string first.

Below is the original script interleaved with my revisions

 >    char oid[STRINGLEN], value[STRINGLEN];

/* The declarations of oid and value are not necessary in the revised example,
  * but for exposition, here is what they would look like:
  */
string oid, value;

 >        return; /* couldn't find a free entry */

The above code would be unchanged.

 >    writeVarbind(pdu, 0, oid, TYPE_OBJECT_IDENTIFIER, value, 0);

/* No need to pass a length argument */
writeVarbind(pdu, 0, "hlMatrixControlDataSource." + index,
              TYPE_OBJECT_IDENTIFIER, "ifIndex." + $1);

 >    writeVarbind(pdu, 4, oid, TYPE_INTEGER, "1", 0);

writeVarbind(pdu, 1,
                    "hlMatrixControlNlMaxDesiredEntries." + index,
                    TYPE_INTEGER, "1000");
writeVarbind(pdu, 2,
                    "hlMatrixControlAlMaxDesiredEntries." + index,
                    TYPE_INTEGER, "1000");
writeVarbind(pdu, 3, "hlMatrixControlOwner." + index,
                    TYPE_OCTET_STRING, "policy manager");
writeVarbind(pdu, 4,
                    "hlMatrixControlStatus." + index,TYPE_INTEGER,
                    "1");

 >    snmpsend(0, 5, OP_SET);

/* The a snmpsend line could be unchanged, although I
  * would prefer the following:
  */
snmpset(pdu, 5);

- - - - - -
The above example does not fully show the benefits of the string type. An 
example using get would be more interesting from the string standpoint. 
Here is an off-the-cuff fragment:

Using character arrays (no string type):
=========================================

char oid[STRINGLEN], value[STRINGLEN];
int index, pdu, valueLen;

sprintf(oid, "hlMatrixControlDataSource.%d", index);
writeVarbind(pdu, 0, oid, TYPE_OBJECT_IDENTIFIER, "", 0);
snmpsend(pdu, 1, OP_GET);

/* Error if retrieved oid or value don't fit in buffer */
readVarbind(pdu, 0, oid, TYPE_OBJECT_IDENTIFIER,
                   value, &valueLen);

=========================================================
Using a built-in string type:
=============================

string value, oid;
int index, pdu;

sprintf(oid, "hlMatrixControlDataSource.%d", index);
writeVarbind(pdu, 0, "hlMatrixControlDataSource." + index,
                    TYPE_OBJECT_IDENTIFIER, "");
snmpsend(pdu, 1, OP_GET); /* or snmpget(pdu, 1); */

/* oid and value will grow to required length */
value = readVarbind(pdu, 0, &oid);

The above example is basic, but with enough code, the simplification 
provided by the string type becomes more compelling. Also, the reduced 
chance of error results in reduced need for error handling, which is always 
a big win.

There are also several other simplifications possible once strings are part 
of the language. For example, the readVarbind function could be split and 
simplified, avoiding the need for declaring dummy variables to hold unused 
results:

oid = readVarbindOid(pdu, 0);
value = readVarbindValue(pdu, 0);

Note that either of the above two calls could be eliminated if one or the 
other result is not needed.

- Pablo
---------------------------------------------------------------------
Pablo Halpern                                    [email-masked]
http://www.halpernwightsoftware.com