RFC1076 - HEMS monitoring and control language

时间:2005-02-12 来源: 作者: 点击:
Network Working Group G. Trewitt Request for Comments: 1076 Stanford University Obsoletes: RFC1023 C. Partridge BBN/NNSC November 1988 HEMS Monitoring and Control Language TABLE OF CONTENTS 1. Status of This Memo 1 Introduction 2 2. Overview and Scop
  Network Working Group G. Trewitt
Request for Comments: 1076 Stanford University
Obsoletes: RFC1023 C. Partridge
BBN/NNSC
November 1988

HEMS Monitoring and Control Language

TABLE OF CONTENTS

1. Status of This Memo 1
Introduction 2
2. Overview and Scope 2
3. Overview of Query Processor Operation 4
4. Encoding of Queries and Responses 5
4.1 Notation Used in This Proposal 5
5. Data Organization 6
5.1 Example Data Tree 7
5.2 Arrays 8
6. Components of a Query 9
7. Reply to a Query 10
8. Query Language 12
8.1 Moving Around in the Data Tree 14
8.2 Retrieving Data 15
8.3 Data Attributes 16
8.4 Examining Memory 18
8.5 Control Operations: Modifying the Data Tree 19
8.6 Associative Data Access: Filters 21
8.7 Terminating a Query 26
9. Extending the Set of Values 27
10. Authorization 27
11. Errors 28
I. ASN.1 Descriptions of Query Language Components 29
I.1 Operation Codes 30
I.2 Error Returns 31
I.3 Filters 33
I.4 Attributes 34
I.5 VendorSpecific 36
II. Implementation Hints 36
III. Obtaining a Copy of the ASN.1 Specification 42

1. STATUS OF THIS MEMO

This RFCspecifies a query language for monitoring and control of
network entities. This RFCsupercedes RFC-1023, extending the query
language and providing more discussion of the underlying issues.

This language is a component of the High-Level Entity Monitoring
System (HEMS) described in RFC-1021 and RFC-1022. Readers may wish
to consult these RFCs when reading this memo. RFC-1024 contains
detailed assignments of numbers and structures used in this system.
Portions of RFC-1024 that define query language structures are
superceded by definitions in this memo. This memo assumes a
knowledge of the ISO data encoding standard, ASN.1.

Distribution of this memo is unlimited.

INTRODUCTION

This RFCspecifies the design of a general-purpose, yet efficient,
monitoring and control language for managing network entities. The
data in the entity is modeled as a hierarchy and specific items are
named by giving the path from the root of the tree. Most items are
read-only, but some can be "set" in order to perform control
operations. Both requests and responses are represented using the
ISO ASN.1 data encoding rules.

2. OVERVIEW AND SCOPE

The basic model of monitoring and control used in this memo is that a
query is sent to a monitored entity and the entity sends back a
response. The term query is used in the database sense -- it may
request information, modify data, or both. We will use gateway-
oriented examples, but it should be understood that this query-
response mechanism is applicable to any IP entity.

In particular, there is no notion of an interactive "conversation" as
in SMTP [RFC-821] or FTP [RFC-959]. A query is a complete request
that stands on its own and elicits a complete response.

In order to design the query language, we had to define a model for
the data to be retrieved by the queries, which required some
understanding of and assumptions to be made about the data. We ended
up with a fairly flexible data model, which places few limits on the
type or size of the data.

Wherever possible, we give motivations for the design decisions or
assumptions that led to particular features or definitions. Some of
the important global considerations and assumptions are:

- The query processor should place as little computational
burden on the monitored entity as possible.

- It should not be necessary for a monitored entity to store
the complete query. Nothing in the query language should

preclude an implementation from being able to process the
query on the fly, producing portions of the response while
the query is still being read and parsed. There may be
other constraints that require large amounts of data to be
buffered, but the query language design must not be one.

- It is assumed that there is some mechanism to transport a
sequence of octets to a query processor within the
monitored entity and that there is some mechanism to return
a sequence of octets to the entity making the query. In
HEMS, this is provided by HEMP and its underlying transport
layer. The query language design is independent of these
details, however, and could be grafted onto some other
protocol.

- The data model must provide organization for the data, so
that it can be conveniently named.

- Much of the data to be monitored will be contained in
tables. Some tables may contain other tables. The query
language should be able to deal with such tables.

- We don't provide capabilities for data reduction in the
query language. We will provide for data selection, for
example, only retrieving certain table entries, but we will
not provide general facilities for processing data, such as
computing averages.

- Because one monitoring center may be querying many
(possibly hetrogenous) hosts, it must be possible to write
generic queries that can be sent to all hosts, and have the
query elicit as much information as is available from each
host. i.e., queries must not be aborted just because they
requested non-existent data.

There were some assumptions that we specifically did not make:

- It is up to the implementation to choose what degree of
concurrency will be allowed when processing queries. By
locking only portions of the database, it should be
possible to achieve good concurrency while still preventing
deadlock.

- This specification makes no statement about the use of the
"definite" and "indefinite" length forms in ASN.1. There
is currently some debate about this usage in the ISO
community; implementors should note the recommendations in
the ASN.1 specification.

Other RFCs associated with HEMS are:

RFC-1021 Overview;
RFC-1022 Transport protocol and message encapsulation;
RFC-1024 Precise data definitions.

The rest of this report is organized as follows:

Section 3 Gives a brief overview of the data model and the
operation of the query processor.

Section 4 Describes the encoding used for queries and
responses, and the notation used to represent them
in this report.

Section 5 Describes how the data is organized in the
monitored entity, and the view provided of it by
the query processor.

Section 6 Describes the basic data types that may be given
to the query processor as input.

Section 7 Describes how a reply to a query is organized.

Section 8 Describes the operations available in the query
language.

Section 9 Describes how the set of data in the tree may be
extended.

Section 10 Describes how authorization issues affect the
execution of a query.

Section 11 Describes how errors are reported, and their
effect on the processing of the query.

Appendix I Gives precise ASN.1 definitions of the data types
used by the query processor.

Appendix II Gives extensive implementation hints for the core
of the query processor.

3. OVERVIEW OF QUERY PROCESSOR OPERATION

In this section, we give an overview of the operation of the query
processor, to provide a framework for the later sections.

The query language models the manageable data as a tree, with each

branch representing a different aspect of the entity, such as
different layers of protocols. Subtrees are further divided to
provide additional structure to the data. The leaves of the tree
contain the actual data.

Given this data representation, the task of the query processor is to
traverse this tree and retrieve (or modify) data specified in a
query. A query consists of instructions to move around in the tree
and to retrieve (or modify) named data. The result of a query is an
exact image of the parts of the tree that the query processor
visited.

The query processor is very simple -- it only understands eight
commands, most of which share the same structure. It is helpful to
think of the query processor as an automaton that walks around in the
tree, directed by commands in the query. As it moves around, it
copies the tree structure it traverses to the query result. Data
that is requested by the query is copied into the result as well.
Data that is changed by a query is copied into the result after the
modification is made.

4. ENCODING OF QUERIES AND RESPONSES

Both queries and responses are encoded using the representation
defined in ISO Standard ASN.1 (Abstract Syntax Notation 1). ASN.1
represents data as sequences of <tag,length,contents> triples that
are encoded as a stream of octets. The data tuples may be
recursively nested to represent structured data such as arrays or
records. For a full description, see the ISO standards IS 8824 and
IS 8825. See appendix for information about obtaining these
documents.

4.1 Notation Used in This Proposal

The notation used in this memo is similar to that used in ASN.1, but
less formal, smaller, and (hopefully) easier to read. We will refer
to a <tag,length,contents> tuple as a "data object". In this RFC, we
will not be concerned with the details of the object lengths. They
exist in the actual ASN.1 encoding, but will be omitted in the
examples here.

Data objects that have no internal ASN.1 structure such as integer or
octet string are referred to as "simple types" or "simple objects".
Objects which are constructed out of other ASN.1 data objects will be
referred to as "composite types" or "composite objects".

The notation
ID(value)
represents a simple object whose tag is "ID" with the given value. A
composite object is represented as
ID{ ... contents ... }
where contents is a sequence of data objects. The contents may
include both simple and structured types, so the structure is fully
recursive.

The difference between simple and composite types is close to the
meaning of the "constructor" bit in ASN.1. For the uses here, the
distinction is made based upon the semantics of the data, not the
representation. Therefore, even though an OctetString can be
represented in ASN.1 using either constructed or non-constructed
forms, it is conceptually a simple type, with no internal structure,
and will always be written as
ID("some arbitrary string")
in this RFC.

There are situations where it is necessary to specify a type but give
no value, such as when referring to the name of the data. In this
situation, the same notation is used, but with the value omitted:
ID or ID() or ID{}
Such objects have zero length and no contents. The latter two forms
are used when a distinction is being made between simple and
composite data, but the difference is just notation -- the
representation is the same.

ASN.1 distinguishes between four "classes" of tags: universal,
application-specific, context-dependent, and reserved. HEMS and this
query language use the first three. Universal tags are assigned in
the ASN.1 standard and its addendums for common types, and are
understood by any application using ASN.1. Application-specific tags
are limited in scope to a particular application. These are used for
"well-known" identifiers that must be recognizable in any context,
such as derived data types. Finally, context-dependent tags are used
for objects whose meaning is dependent upon where they are
encountered. Most tags that identify data are context-dependent.

5. DATA ORGANIZATION

Data in a monitored entity is modeled as a hierarchy.
Implementations are not required to organize the data internally as a
hierarchy, but they must provide this view of the data through the
query language. A hierarchy offers useful structure for the
following operations:

Organization A hierarchy allows related data to be grouped
together in a natural way.

Naming The name of a piece of data is just the path from the
root to the data of interest.

Mapping onto ASN.1
ASN.1 can easily represent a hierarchy by using a
"constructor" type as an envelope for an entire
subtree.

Efficient Representation
Hierarchical structures are compact and can be
traversed quickly.

Safe Locking If it is necessary to lock part of the hierarchy (for
example, when doing an update), locking an entire
subtree can be done efficiently and safely, with no
danger of deadlock.

We will use the term "data tree" to refer to this entire structure.
Note that this internal model is completely independent of the
external ASN.1 representation -- any other suitable representation
would do. For the sake of efficiency, we do make a one-to-one
mapping between ASN.1 tags and the (internal) names of the nodes.
The same could be done for any other external representation.

Each node in the hierarchy must have names for its component parts.
Although we would normally think of names as being ASCII strings such
as "input errors", the actual name is just an ASN.1 tag. Such names
are small integers (typically, less than 30) and so can easily be
mapped by the monitored entity onto its internal representation.

We use the term "dictionary" to mean an internal node in the
hierarchy. Leaf nodes contain the actual data. A dictionary may
contain both leaf nodes and other dictionaries.

5.1 Example Data Tree

Here is a possible organization of the hierarchy in an entity that
has several network interfaces and does IP routing. The exact
organization of data in entities is specified in RFC-1024. This
skeletal data tree will be used throughout this RFCin query
examples.

System {
name -- host name
clock-msec -- msec since boot

interfaces -- # of interfaces
memory
}
Interfaces { -- one per interface
InterfaceData{ address, mtu, netMask, ARP{...}, ... }
InterfaceData{ address, mtu, netMask, ARP{...}, ... }
:
}
IPRouting {
Entry{ ip-addr, interface, cost, ... }
Entry{ ip-addr, interface, cost, ... }
:
}

There are three top-level dictionaries in this hierarchy (System,
Interfaces, and IPRouting) and three other dictionary types
(InterfaceData, Entry, and ARP), each with multiple instances.

The "name" of the clock in this entity would be:
system{ clock-msec }
and the name of a routing table entry's IP address would be:
IPRouting{ Entry{ ip-addr } }.

More than one piece of data can be named by a single ASN.1 object.
The entire collection of system information is named by:
system
and the name of a routing table's IP address and cost would be:
IPRouting{ Entry{ ip-addr, cost } }.

5.2 Arrays

There is one sub-type of a dictionary that is used as the basis for
tables of objects with identical types. We call these dictionaries
arrays. In the example above, the dictionaries for interfaces,
routing tables, and ARP tables are all arrays.

In the examples above, the "ip-addr" and "cost" fields are named. In
fact, these names refer to the field values for ALL of the routing
table entries -- the name doesn't (and can't) specify which routing
table entry is intended. This ambiguity is a problem wherever data
is organized in tables. If there was a meaningful index for such
tables (e.g., "routing table entry #1"), there would be no problem.
Unfortunately, there usually isn't such an index. The solution to
this problem requires that the data be accessed on the basis of some
of its content. Filters, discussed in section 8.6, provide this
mechanism.

The primary difference between arrays and plain dictionaries is that

arrays may contain only one type of item, while dictionaries, in
general, will contain many different types of items. For example,
the dictionary IPRouting (which is an array) will contain only items
of type Entry.

The fact that these objects are viewed externally as arrays or tables
does not mean that they are represented in an implementation as
linear lists of objects. Any collection of same-typed objects is
viewed as an array, even though it might be stored internally in some
other format, for example, as a hash table.

6. COMPONENTS OF A QUERY

A HEMS query consists of a sequence of ASN.1 objects, interpreted by
a simple stack-based interpreter. [Although we define the query
language in terms of the operations of a stack machine, the language
does not require an implementation to use a stack machine. This is a
well-understood model, and is easy to implement.] One ASN.1 tag is
reserved for operation codes; all other tags indicate data that will
eventually be used by an operation. These objects are pushed onto
the stack when received. Opcodes are immediately executed and may
remove or add items to the stack. Because ASN.1 itself provides
tags, very little needs to be done to the incoming ASN.1 objects to
make them suitable for use by the query interpreter.

Each ASN.1 object in a query will fit into one of the following
categories:

Opcode An opcode tells the query interpreter to perform an action.
They are described in detail in section 8. Opcodes are
represented by an application-specific type whose value
determines the operation.

Template These are objects that name one or more items in the data
tree. Named items may be either simple items (leaf nodes)
or entire dictionaries, in which case the entire subtree
"underneath" the dictionary is understood. Templates are
used to select specific data to be retrieved from the data
tree. A template may be either simple or structured,
depending upon what it is naming. A template only names
the data -- there are no values contained in it. Therefore
the leaf objects in a template will all have a length of
zero.

Examples of very simple templates are:
name() or System{}
Each of these is just one ASN.1 data object, with zero
length. The first names a single data item in the "System"

dictionary (and must appear in that context), and the
second names the entire "System" dictionary. A more
complex template such as:
Interfaces{ InterfaceData{ address, netMask, ARP } }
names two simple data items and a dictionary, iterated over
all occurrences of "InterfaceData" within the Interfaces
array.

Path A path is a special case of a template that names only a
single node in the tree. It specifies a path down into the
dictionary tree and names exactly one node in the
dictionary tree.

Value These are used to give data values when needed in a query,
for example, when changing a value in the data tree. A
value can be thought of as either a filled-in template or
as the ASN.1 representation some part of the data tree.

Filter A boolean expression that can be executed in the context of
a particular dictionary that is used to select or not
select items in the dictionary. The expressions consist of
the primitives "equal", "greater-or-equal",
"less-or-equal", and "present" possibly joined by "and",
"or", and "not". (See section 8.6.)

Values, Paths, and Templates usually have names in the context-
dependent class, except for a few special cases, which are in the
application-specific class.

7. REPLY TO A QUERY

The data returned to the monitoring entity is a sequence of ASN.1
data items. Conceptually, the reply is a subset of the data tree,
where the query selects which portions are to be included. This is
exactly true for data retrieval requests, and essentially true for
data modification requests -- the reply contains the data after it
has been modified. The key point is that the data in a reply
represents the state of the data tree immediately after the query was
executed.

The sequence of the data is determined by the sequence of query
language operations and the order of data items within Templates and
Values given as input to these operations. If a query requests data
from two of the top-level dictionaries in the data tree, by giving
two templates such as:

System{ name, interfaces }
Interfaces{

InterfaceData { address, netMask, mtu }
}

then the response will consist of two ASN.1 data objects, as follows:

System {
name("system name"),
interfaces(2)
}
Interfaces {
InterfaceData { address(36.8.0.1),
netMask(FFFF0000),
mtu(1500)
}
InterfaceData { address(10.1.0.1),
mtu(1008),
netMask(FF000000)
}
}

With few exceptions, each of the data items in the hierarchy is named
in the context-specific ASN.1 type space. Because of this, the
returned objects must be fully qualified. For example, the name of
the entity must always be returned encapsulated inside an ASN.1
object for "System". If it were not, there would be no way to tell
if the object that was returned was "name" inside the "System"
dictionary or "address" inside the "interfaces" dictionary (assuming
in this case that "name" and "address" were assigned the same integer
as their ASN.1 tags).

Having fully-qualified data simplifies decoding of the data at the
receiving end and allows the tags to be locally chosen. Definitions
for tags within routing tables won't conflict with definitions for
tags within interfaces. Therefore, the people doing the name
assignments are less constrained. In addition, most of the
identifiers will be fairly small integers, which is an advantage
because ASN.1 can fit tag numbers up to 30 in a one-octet tag field.
Larger numbers require a second octet.

If data is requested that doesn't exist, either because the tag is
not defined, or because an implementation doesn't provide that data
(such as when the data is optional), the response will contain an
ASN.1 object that is empty. The tag will be the same as in the
query, and the object will have a length of zero.

The same response is given if the requested data does exist, but the
invoker of the query does not have authorization to access it. See
section 10 for more discussion of authorization mechanisms.

This allows completely generic queries to be composed without regard
to whether the data is defined or implemented at all of the entities
that will receive the query. All of the available data will be
returned, without generating errors that might otherwise terminate
the processing of the query.

8. QUERY LANGUAGE

The query language is designed to be expressive enough to write
useful queries with, yet simple enough to be easy to implement. The
query processor should be as simple and fast as possible, in order to
avoid placing a burden on the monitored entity, which may be a
critical node such as a gateway.

Although queries are formed in a flexible way using what we term a
"language", this is not a programming language. There are operations
that operate on data, but most other features of programming
languages are not present. In particular:

- Programs are not stored in the query processor.

- The only form of temporary storage is a stack, of limited
depth.

- There are no subroutines.

- There are no explicit control structures defined in the
language.

The central element of the language is the stack. It may contain
templates, (and therefore paths), values, and filters taken from the
query. In addition, it can contain dictionaries (and therefore
arrays) from the data tree. At the beginning of a query, it contains
one item, the root dictionary.

The overall operation consists of reading ASN.1 objects from the
input stream. All objects that aren't opcodes are pushed onto the
stack as soon as they are read. Each opcode is executed immediately
and may remove items from the stack, may generate ASN.1 objects and
send them to the output stream, and may leave items on the stack.
Because each input object is dealt with immediately, portions of the
response may be generated while the query is still being received.

In the descriptions below, operator names are in capital letters,
preceded by the arguments used from the stack and followed by results
left on the stack. For example:

OP a b OP a t
means that the OP operator takes <a> and <b> off of the
stack and leaves <t> on the stack. Most of the operators
in the query language leave the first operand (<a> in this
example) on the stack for future use.

If both <a> and <b> were received as part of the query (as opposed to
being calculated by previous operations), then this part of the query
would have consisted of the sequence:
<a>
<b>
OP
So, like other stack-based languages, the arguments and operators
must be presented in postfix order, with an operator following its
operands.

Here is a summary of all of the operators defined in the query
language. Most of the operators can take several different sets of
operands and behave differently based upon the operand types.
Details and examples are given later.

BEGIN dict1 path BEGIN dict1 dict
array path filter BEGIN array dict
Move down in the data tree, establishing a context for
future operations.

END dict END --
Undo the most recent BEGIN.

GET dict GET dict
dict template GET dict
array template filter GET array
Retrieve data from the data tree.

GET-ATTRIBUTES
dict GET-ATTRIBUTES dict
dict template GET-ATTRIBUTES dict
array template filter GET-ATTRIBUTES array
Retrieve attribute information about data in the data tree.

GET-RANGE dict path start length GET-RANGE dict
Retrieve a subrange of an OctetString. Used for reading
memory.

SET dict value SET dict
array value filter SET array
Change values in the data tree, possibly performing control
functions.

CREATE array value CREATE dict
Create new table entries.

DELETE array filter DELETE array
Delete table entries.

These operators are defined so that it is impossible to generate an
invalid query response. Since a response is supposed to be a
snapshot of a portion (or portions) of the data tree, it is important
that only data that is actually in the tree be put in the response.
Two features of the language help guarantee this:

- Data is put in the response directly from the tree (by
GET-*). Data does not go from the tree to the stack and
then into the response.

- Dictionaries on the stack are all derived from the initial,
root dictionary. The operations that manipulate
dictionaries (BEGIN and END) also update the response with
the new location in the tree.

8.1 Moving Around in the Data Tree

The initial point of reference in the data tree is the root. That
is, operators name data starting at the root of the tree. It is
useful to be able to move to some other dictionary in the tree and
then name data from that point. The BEGIN operator moves down in the
tree and END undoes the last unmatched BEGIN.

BEGIN is used for two purposes:

- By moving to a dictionary closer to the data of interest,
the name of the data can be shorter than if the full name
(from the root) were given.

- It is used to establish a context for filtered operations
to operate in. Filters are discussed in section 8.6.

BEGIN dict1 path BEGIN dict1 dict
Follow <path> down the dictionary starting from <dict1>.
Push the final dictionary named by <path> onto the stack.
<path> must name a dictionary (not a leaf node). At the
same time, produce the beginning octets of an ASN.1 object
corresponding to the new dictionary. It is up to the
implementation to choose between using the "indefinite
length" representation or the "definite length" form and
going back and filling the length in later.

END dict END --
Pop <dict> off of the stack and terminate the open ASN.1
object(s) started by the matching BEGIN. Must be paired
with a BEGIN. If an END operation pops the root dictionary
off of the stack, the query is terminated.

<path> must point to a regular dictionary. If any part of it refers
to a non-existent node, if it points to a leaf node, or if it refers
to a node inside an array-type dictionary, then it is in error, and
the query is terminated immediately.

An additional form of BEGIN, which takes a filter argument, is
described later.

8.2 Retrieving Data

The basic model that all of the data retrieval operations follow is
that they take a template and fill in the leaf nodes of the template
with the appropriate data values.

GET dict template GET dict
Emit an ASN.1 object with the same "shape" as the given
template, except with values filled in for each node. The
first ASN.1 tag of <template> should refer to an object in
<dict>. If a dictionary tag is supplied anywhere in
<template>, the entire dictionary contents are emitted to
the response. Any items in the template that are not in
<dictionary> (or its components) are represented as objects
with a length of zero.

dict GET dict
If there is no template, get all of the items in the
dictionary. This is equivalent to providing a template
that lists all of the items in the dictionary.

An additional form of GET, which takes a filter argument, is
described later.

Here is an example of using the BEGIN operator to move down the data
tree to the TCP dictionary and then using the GET operator to
retrieve 5 data values from the TCP Stats dictionary:

IPTransport{ TCP } BEGIN
Stats{ octetsIn, octetsOut, inputPkts, outputPkts, badtag } GET
END

This might return:

IPTransport{ TCP
Stats{ octetsIn(13255), octetsOut(82323),
inputPkts(9213), outputPkts(12425), badtag() }
}

"badtag" is a tag value that is undefined. No value is returned for
it, indicating that there is no data value associated with it.

8.3 Data Attributes

Although ASN.1 "self-describes" the structure and syntax of the data,
it gives no information about what the data means. For example, by
looking at the raw data, it is possible to tell that an item is of
type [context 5] and is 4 octets long. That does not tell how to
interpret the data (is this an integer, an IP address, or a 4-
character string?) or what the data means (IP address of what?).
Even if the data were "tagged", in ASN.1 parlance, that would only
give the base type (e.g., IP-address or counter) and not the meaning.

Most of the time, this information will come from RFC-1024, which
defines the ASN.1 tags and their precise meaning. When extensions
have been made, it may not be possible to get documentation on the
extensions. (Extensions are discussed in section 9.)

The GET-ATTRIBUTES operator is similar to the GET operator, but
returns a set of attributes describing the data rather than the data
itself. This information is intended to be sufficient to let a human
understand the meaning of the data and to let a sophisticated
application treat the data appropriately. Such an application could
use the attribute information to format the data on a display and
decide whether it is appropriate to subtract one sample from another.

Some of the attributes are textual descriptions to help a human
understand the nature of the data and provide meaningful labels for
it. Extensive descriptions of standard data are optional, since they
are defined in RFC-1024. Complete descriptions of extensions must be
available, even if they are documented in a user's manual. Network
firefighters may not have a current manual handy when the network is
broken.

The format of the attributes is not as simple as the format of the
data itself. It isn't possible to use the data's tag, since that
would look exactly like the data itself. The format is:

Attributes ::= [APPLICATION 3] IMPLICIT SEQUENCE {
tagASN1 [0] IMPLICIT INTEGER,

valueFormat [1] IMPLICIT INTEGER,
longDesc [2] IMPLICIT IA5String OPTIONAL,
shortDesc [3] IMPLICIT IA5String OPTIONAL,
unitsDesc [4] IMPLICIT IA5String OPTIONAL,
precision [5] IMPLICIT INTEGER OPTIONAL,
properties [6] IMPLICIT BITSTRING OPTIONAL,
valueSet [7] IMPLICIT SET OF valueDesc OPTIONAL
}

The GET-ATTRIBUTES operator is similar to the GET operator. The
major difference is that dictionaries named in the template do not
elicit data for the entire subtree.

GET-ATTRIBUTES
dict template GET-ATTRIBUTES dict
Emit a single ASN.1 Attributes object for each of the
objects named in <template>. For each of these, the
tagASN1 field will be set to the corresponding tag from the
template. The rest of the fields are set as appropriate
for the data object. Any items in the template that are
not in <dictionary> (or its components) elicit an
Attributes object with a valueFormat of NULL, and no other
descriptive information.

or
dict GET-ATTRIBUTES dict
If there is no template, emit Attribute objects for all of
the items in the dictionary. This is equivalent to
providing a template that lists all of the items in the
dictionary. This allows a complete list of a dictionary's
contents to be obtained.

An additional form of GET-ATTRIBUTES, which takes a filter argument,
is described later.

Here is an example of using the GET-ATTRIBUTES operator to request
attributes for three objects in the System dictionary:

System{ name, badtag, clock-msec } GET-ATTRIBUTES

"badtag" is some unknown tag. The result might be:

System{
Attributes{
tagASN1(name),
valueFormat(IA5String),
longDesc("The primary hostname."),

shortDesc("hostname")
},
Attributes{
tagASN1(badtag), valueFormat(NULL)
}
Attributes{
tagASN1(clock-msec),
valueFormat(Integer),
longDesc("milliseconds since boot"),
shortDesc("uptime"), unitsDesc("ms")
precision(4294967296),
properties(1)
}

Note that in this example "name" and "clock-msec" are integer values
for the ASN.1 tags for the two data items. "badtag" is an integer
value that has no corresponding name in this context.

There will always be exactly as many Attributes items in the result
as there are objects in the template. Attributes objects for items
which do not exist in the entity will have a valueFormat of NULL and
none of the optional elements will appear.

[ A much cleaner method would be to store the attributes as
sub-components of the data item of interest. For example,
requesting
System{ clock-msec } GET
would normally just get the value of the data. Asking for an
additional layer down the tree would now get its attributes:
System{ clock-msec{ shortDesc, unitsDesc } GET
would get the named attributes. (The attributes would be
named with application-specific tags.) Unfortunately, ASN.1
doesn't provide a notation to describe this type of
organization. So, we're stuck with the GET-ATTRIBUTES
operator. However, if a cleaner organization were possible,
this decision would have been made differently. ]

8.4 Examining Memory

Even with the ability to symbolically access all of this information
in an entity, there will still be times when it is necessary to get
to very low levels and examine memory, as in remote debugging
operations. The building blocks outlined so far can easily be
extended to allow memory to be examined.

Memory is modeled as an ordinary object in the data tree, with an
ASN.1 representation of OctetString. Because of the variety of
addressing architectures in existence, the conversion from the

internal memory model to OctetString is very machine-dependent. The
only simple case is for byte-addressed machines with 8 bits per byte.

Each address space in an entity is represented by one "memory" data
item. In a one-address-space situation, this dictionary will
probably be in "System" dictionary. If each process has its own
address space, then one "memory" item might exist for each process.
Again, this is very machine-dependent.

Although the GET-RANGE operator is provided primarily for the purpose
of retrieving the contents of memory, it can be used on any object
whose base type is OctetString.

GET-RANGE dict path start length GET-RANGE dict
Get <length> elements of the OctetString, starting at
<start>. <start> and <length> are both ASN.1 INTEGER type.
<path>, starting from <dict>, must specify a node
representing memory, or some other OctetString.

The returned data may not be <length> octets long, since it may take
more than one octet to represent one memory location.

Memory items in the data tree are special in that they will not
automatically be returned when the entire contents of a dictionary
are requested. e.g., If memory is part of the "System" dictionary,
then the query
System GET
will emit the entire contents of the System dictionary, but not the
memory item.

8.5 Control Operations: Modifying the Data Tree

All of the operators defined so far only allow data in an entity to
be retrieved. By replacing the template argument used in the GET
operators with a value, data in the entity can be changed. Very few
items in the data tree can be changed; those that can are noted in
RFC-1024.

Values in the data tree can modified in order to change configuration
parameters, patch routing tables, etc. Control functions, such as
bringing an interface "down" or "up", do not usually map directly to
changing a value. In such cases, an item in the tree can be defined
to have arbitrary side-effects when a value is assigned to it.
Control operations then consist of "setting" this item to an
appropriate command code. Reading the value of such an item might
return the current status. Again, details of such data tree items
are given in RFC-1024.

This "virtual command-and-status register" model is very powerful,
and can be extended by an implementation to provide whatever controls
are needed. It has the advantage that the control function is
associated with the controlled object in the data tree. In addition,
no additional language features are required to support control
functions, and the same operations used to locate data for retrieval
are used to describe what is being controlled.

For all of the control and data modification operations, the fill-
in-the-blank model used for data retrieval is extended: the response
to an operation is the affected part of the data tree, after the
operation has been executed. Therefore, for normal execution, SET
and CREATE will return the object given as an argument, and DELETE
will return nothing (because the affected portion was deleted).

SET dict value SET dict
Set the value(s) of data in the entity to the value(s)
given in <value>. The result will be the value of the data
after the SET. Attempting to set a non-settable item will
not produce an error, but will yield a result in the reply
different from what was sent.

CREATE array value CREATE dict
Insert a new entry into <array>. Depending upon the
context, there may be severe restrictions about what
constitutes a valid <value>. The result will be the actual
item added to the <array>. Note that only one item can be
added per CREATE operation.

DELETE array filter DELETE array
Delete the entry(s) in <array> that match <filter>.
Filters are described later in this document. Normally, no
data items will be produced in the response, but if any of
the items that matched the filter could not be deleted,
they will be returned in the response.

An additional form of SET, which takes a filter argument, is
described later.

Here is an example of attempting to use SET to change the number of
interfaces in an entity:
System{ interfaces(5) } SET
Since that is not a settable parameter, the result would be:
System{ interfaces(2) }
giving the old value.

Here is an example of how CREATE would be used to add a routing table
entry for net for 128.89.0.0.

IPRouting BEGIN -- get dictionary
Entries{ DestAddr(128.89.0.0), ... } -- entry to insert
CREATE
END -- finished with dict

The result would be what was added:
IPRouting{ Entries{ DestAddr(128.89.0.0), ... } }

The results in the response of these operators is consistent of the
global model of the response: it contains a subset of the data in
the tree immediately after the query is executed.

Note that CREATE and DELETE only operate on arrays, and then only on
arrays that are specifically intended for it. For example, it is
quite reasonable to add and remove entries from routing tables or ARP
tables, both of which are arrays. However, it doesn't make sense to
add or remove entries in the "Interfaces" dictionary, since the
contents of that array is dictated by the hardware. For each array
in the data tree, RFC-1024 indicates whether CREATE and DELETE are
valid.

CREATE and DELETE are always invalid in non-array contexts. If
DELETE were allowed on monitored data, then the deleted data would
become unmonitorable to the entire world. Conversely, if it were
possible to CREATE arbitrary dictionary entries, there would be no
way to give such entries any meaning. Even with the data in place,
there is nothing that would couple the data to the operation of the
monitored entity. [Creation and deletion would also add considerable
complication to an implementation, because without them, all of the
data structures that represent the data tree are essentially static,
with the exception of dynamic tables such as the ones mentioned,
which already have mechanisms in place for adding and removing
entries.]

8.6 Associative Data Access: Filters

One problem that has not been dealt with was alluded to earlier: When
dealing with array data, how do you specify one or more entries based
upon some value in the array entries? Consider the situation where
there are several interfaces. The data might be organized as:

Interfaces { -- one per interface
InterfaceData { address, mtu, netMask, ARP{...}, ... }
InterfaceData { address, mtu, netMask, ARP{...}, ... }
:
}

If you only want information about one interface (perhaps because

there is an enormous amount of data about each), then you have to
have some way to name it. One possibility would be to just number
the interfaces and refer to the desired interface as
InterfaceData(3)
for the third one.

But this is not sufficient, because interface numbers may change over
time, perhaps from one reboot to the next. It is even worse when
dealing with arrays with many elements, such as routing tables, TCP
connections, etc. Large, changing arrays are probably the more
common case, in fact. Because of the lack of utility of indexing in
this context, there is no general mechanism provided in the language
for indexing.

A better scheme is to select objects based upon some value contained
in them, such as the IP address. The query language uses filters to
select specific table entries that an operator will operate on. The
operators BEGIN, GET, GET-ATTRIBUTES, SET, and DELETE can take a
filter argument that restricts their operation to entries that match
the filter.

A filter is a boolean expression that is executed for each element in
an array. If an array entry "matches" the filter (i.e., if the filter
produces a "true" result), then it is used by the operation. A
filter expression is very restricted: it can only compare data
contained in the array element and the comparisons are only against
constants. Comparisons may be connected by AND, OR and NOT
operators.

The ASN.1 definition of a filter is:

Filter ::= [APPLICATION 2] CHOICE {
present [0] DataPath,
equal [1] DataValue,
greaterOrEqual [2] DataValue,
lessOrEqual [3] DataValue,
and [4] SEQUENCE OF Filter,
or [5] SEQUENCE OF Filter,
not [6] Filter
}

DataPath ::= ANY -- Path with no value

DataValue ::= ANY -- Single data value

This definition is similar to the filters used in the ISO monitoring
protocol (CMIP) and was derived from that specification.

"DataPath" is the name of a single data item; "DataValue" is the
value of a single data item. The three comparisons are all of the
form "data OP constant", where "data" is the value from the tree,
"constant" is the value from the filter expression, and "OP" is one
of equal, greater-than-or-equal, or less-than-or-equal. The last
operation, "present", tests to see if the named item exists in the
data tree. By its nature, it requires no value, so only a path needs
to be given.

Here is an example of a filter that matches an Interface whose IP
address is 10.1.0.1:
Filter{ equal{ address(10.0.0.51) } }
Note that the name of the data to be compared is relative to the
"InterfaceData" dictionary.

Each operator, when given a filter argument, takes an array
(dictionary containing only one type of item) as its first argument.
In the current example, this would be "Interfaces". The items in it
are all of type "InterfaceData". This tag is referred to as the
"iteration tag".

Before a filtered operation is used, BEGIN must be used to put the
array (dictionary) on top of the stack, to establish it as the
context that the filter iterates over. The general operation of a
filtered operation is then:

1. Iterate over the items in the array.

2. For each element in the array, execute the filter.

3. If the filter succeeds, do the requested operation
(GET/SET/etc.) on the matched element, using the
template/value/path as input to the operation. At this
point, the execution of the operation is the same as in
the non-filtered case.

This is a model of operation; actual implementations may take
advantage of whatever lookup techniques are available for the
particular table (array) involved.

Therefore, there are three inputs to a filtered operation:

1. The "current dictionary" on the stack. This is the
array-type dictionary to be searched, set by an earlier
BEGIN.

2. A filter, to test each item in the array. Each path or
value mentioned in the filter must be named in the context

of an item in the array, as if it was the current
dictionary. For example, in the case where a filtered
operation iterates over the set of "InterfaceData" items
in the "Interfaces" array, each value or path in the
filter should name an item in the "InterfaceData"
dictionary, such as "address".

3. A template, path, or value associated with the operation
to be performed. The leading ASN.1 tag in this must match
the iteration tag. In the current example where the
filter is searching the "Interfaces" dictionary, the first
tag in the template/tag/value must be "InterfaceData".

The operators which take filters as arguments are:

BEGIN array path filter BEGIN array dict
Find a dictionary in <array> that matches <filter>. Use
that as the starting point for <path> and push the
dictionary corresponding to <path> onto the stack. If more
than one dictionary matches <filter>, then any of the
matches may be used. This specification does not state how
the choice is made. At least one dictionary must match; it
is an error if there are no matches. (Perhaps it should be
------分隔线----------------------------
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
推荐内容