This page last changed on Feb 16, 2010 by kgiusti@redhat.com.
Proposal for "Next Generation" QMF API
Goals
- Use new QPID Messaging API
- Implement QMF protocol via QPID Map messages
- Improve thread safety by minimizing the callback notification mechanism.
- Move to a work-queue based event model.
Component Addressing
QMF uses AMQP messaging as the means for communications between Console and Agent components. Thus instances of Agents and Consoles must have addresses which uniquely identify them within the AMQP messaging domain.
QMF uses AMQP string types to represent Agent and Console addresses. These strings are assigned by the application, and their contents are opaque to the QMF implementation.
A QMF address is composed of two parts - an optional domain string, and a mandatory name string. The domain string is used to construct the name of the AMQP exchange to which the component's name string will be bound. If not supplied, the value of the domain defaults to "default". Both Agents and Components must belong to the same domain in order to communicate.
When a Console or Agent is instantiated, it will create a receiving endpoint (source) on the AMQP message domain. The endpoint's address will be sent as the reply_to field for all messages originating from that Console or Agent. The address of the endpoint is created from the value of the domain and name strings, using the following format:
Data Model
Representation of Managment Data
The QMF data model supports two methods for representing management data:
- arbitrarily structured data
- data with a formally defined structure
Arbitrarily structured data is the simpliest method for representing data under QMF. It consists of a set of named data values. Each data value is represented by a primitive AMQP data type. The data is accessed using the data's name as a key. QMF represents arbitrarily structured data as a map of AMQP data types indexed by a name string.
Data that has a formally defined structure extends the data representation by associating the data with a Schema. The Schema describes the structure of all instances of data that are based on that Schema. This is akin to a record type in database design.
Both types of data representations can be managed by an agent. Managed data includes:
- an object identifier
- object lifecycle state
An object identifier uniquely addresses a data object within the domain of its managing agent. QMF represents the object identifier as an opaque string. An object identifier can be assigned to the object by the agent when the object is instantiated. Alternatively, a schema can define an object identifier by defining an ordered list of names of data items. In this case, the object identifier string is built by concatenating the string representations of the value of each named data item. This approach is similar to defining index fields within a database record.
For example, assume a managed object with the following map of data item values:
and assume the data item list defined by the managed object's schema is:
The identifier for this data object would be:
QmfData Class
QMF defines the QmfData class to represent an atomic unit of managment data. The QmfData class defines a collection of named data values. Optionally, a string tag, or "sub-type" may be associated with each data item. This tag may be used by an application to annotate the data item.
When representing formally defined data, a QmfData instance is assigned a Schema.
When representing managed data, a QmfData instance is assigned an object identifier (either explicitly, or via the Schema).
QMF uses a map encoding to represent a QmfData class in an AMQP message. The map encoding of an instance of the QmfData Class is made up of the following elements:
Index |
Optional |
Type |
Description |
"_values" |
N |
map |
Map containing all the "name"=<AMQP Type> pairs for this object. |
"_subtype" |
Y |
map |
Map containing all "name"=<AMQP String> subtype entries for this object. |
"_tag" |
Y |
Any AMQP-supported type |
Application-specific tag for this object. |
"_object_id" |
Y |
string |
Unique identifier for this data object. |
"_schema_id" |
Y |
map |
Map containing the SchemaClassId for this object. |
QmfEvent Class
QMF supports event management functionality. An event is a notification that is sent by an Agent to alert Console(s) of a change in some aspect of the system under management. Like QMF Data, Events may be formally defined by a Schema or not. Unlike QMF Data, Events are not manageable entities - they have no lifecycle. Events simply indicate a point in time where some interesting action occurred.
An instance of an event is represented by the QmfEvent class.
An AMQP timestamp value is associated with each QmfEvent instance. It indicates the moment in time the event occurred. This timestamp is mandatory.
A severity level may be associated with each QmfEvent instance. The following severity levels are supported:
- "emerg" - system is unusable
- "alert" - action must be taken immediately
- "crit" - the system is in a critical condition
- "err" - there is an error condition
- "warning" - there is a warning condition
- "notice" - a normal but significant condition
- "info" - a purely informational message
- "debug" - messages generated to debug the application
The default severity is "notice".
The map encoding of an instance of the QmfEvent class extends the map of the parent class with the following class properites:
Index |
Optional |
Type |
Description |
"_timestamp" |
N |
AMQP Timestamp |
Time the event occurred. |
"_severity" |
Y |
string |
Event severity |
Schema
Schemas are used by QMF to describe the structure of management data and events. The use of Schemas is optional.
Schema Types
There are two classes (or types) of Schema - those that describe data objects, and those that describe event objects.
These types may be represented by the strings "_data" and "_event",
respectively.
Schema Identifier
Schema are identified by a combination of their package name and class
name. A hash value over the body of the schema provides a revision
identifier. The class SchemaClassId represents this Schema
identifier.
If the hash value is not supplied, then the value of the hash string will be set to None. This will be the case when a SchemaClass is being dynamically constructed, and a proper hash is not yet available.
The map encoding of a SchemaClassId:
Index |
Optional |
Type |
Description |
"_package_name" |
N |
string |
The name of the associated package. |
"_class_name" |
N |
string |
The name of the class within the package. |
"_type" |
N |
string |
The type of schema, either "data" or "event". |
"_hash_str" |
Y |
string |
The MD5 hash of the schema, in the format "%08x-%08x-%08x-%08x" |
Schema For Describing The Properties of a Data Item
The SchemaProperty class describes a single data item in a QmfData object. A SchemaProperty is a list of named attributes and their values. QMF defines a set of primitive attributes. An application can extend this set of attributes with application-specific attributes.
QMF reserved attribute names all start with the underscore character ("_"). Do not create an application-specific attribute with a name starting with an underscore.
Once instantiated, the SchemaProperty is immutable.
The map encoding of a SchemaProperty's body:
Index |
Optional |
Type |
Description |
"_qmf_type" |
N |
integer |
The QMF type code indicating the value's data type. |
"_access" |
N |
string |
The access allowed to this property, default "RO" |
"_optional" |
N |
boolean |
True if this property is optional, default False |
"_unit" |
Y |
string |
Description of the units used to express this property. |
"_min" |
Y |
integer |
The minimum allowed value for this property |
"_max" |
Y |
integer |
The maximun allowed value for this property |
"_maxlen" |
Y |
integer |
For string types, this is the maximum length in bytes required to represent the longest permittable instance of this string. |
"_desc" |
Y |
string |
Human-readable description of this property. |
"_dir" |
Y |
string |
Direction for an argument when passed to a Method call: "I", "O", "IO", default value: "I" |
"_subtype" |
Y |
string |
Type information for use by the application. |
"_polled" |
Y |
boolean |
True if this property can only be queried/polled. Default False. |
"_reference" |
Y |
string |
unknown? |
"_parent_ref" |
Y |
boolean |
True if this property references an object in which this object is in a child-parent relationship. Default False |
"x-"<varies> |
Y |
Any AMQP type |
An application-defined attribute. |
Schema For Describing Method Calls
The SchemaMethod class describes a method call's parameter list. The parameter list is represented by an unordered map of SchemaProperty entries indexed by parameter name.
The map encoding of a SchemaMethod:
Index |
Optional |
Type |
Description |
"_desc" |
Y |
string |
Description of the method. |
"_arguments" |
Y |
map |
Map of "name":<SchemaProperty> values, one for each argument to the method. |
Note that the "dir" SchemaProperty attribute applies to each
argument. If "dir" is not specified, it is assumed to be "I".
Schema for Data Objects and Events
The structure of QmfData objects is formally defined by the class SchemaObjectClass.
The structure of QmfEvent objects is formally defined by the class SchemaEventClass.
Both of these classes derive from the virtual base SchemaClass.
Agent applications may dynamically construct instances of these objects by adding properties and methods at run time. However, once the Schema is made public, it must be considered immutable, as the hash value must be constant once the Schema is in use.
QMF defines the following classes to represent data and event schema:
The map encoding of an instance of the SchemaClass class extends the map of its parent class with elements for the following class properties.
Index |
Optional |
Type |
Description |
"_schema_id" |
N |
map |
Map containing the SchemaClassId for this object. |
The map encoding of an instance of the SchemaObjectClass class extends the map of its parent class with elements for the following class properties.
Index |
Optional |
Type |
Description |
"_primary_key" |
Y |
list |
Order list of property names used to construct the primary key for objects defined by this schema |
The map encoding of a SchemaEventClass instance uses the same format as the map for the SchemaClass.
Data Management
QMF allows data managment via the Query, Subscription, and Method Call actions.
Queries
A Query is a mechanism for interrogating the management database. A Query represents a selector which is sent to an Agent. The Agent applies the Query against its management database, and returns those objects which meet the constraints described in the query.
A Query must specify the class of information it is selecting. This class of information is considered the target of the query. Any data objects selected by the query will be of the type indicated by the target.
A Query may also specify a selector which is used as a filter against the set of all target instances. Only those instances accepted by the filter will be returned in response to the query.
Queries are expressed using the following map syntax:
Where:
The value of the "what" key is a map that specifies the target for the Query. The Agent will return a list of instances of target types that match the query.
<target> is implemented as a map with a single element in the format:
QMF defines the following values for <target name string>:
Target |
Description |
"schema_package" |
Query against the set of known packages. Returns a list of package name strings. |
"schema_id" |
Query against the set of SchemaClass objects, return a list of SchemaClassId instances for the objects that match. |
"schema" |
Query against the set of SchemaClass objects, and return a list of matched SchemaClass instances. |
"object_id" |
Query against the set of managed QmfData objects, return a list of name strings for all matching instances. |
"object" |
Query against the set of managed objects, return a list of matching QmfData instances. |
"agent" |
Query against all agents within the current QMF domain, return a list of name strings for each matching agent. Used only by the "agent-locate" message. |
more tbd ... |
... |
The value of the <target name string> map entry is ignored for now, its use is TBD.
The selector_type is optional. If it is not present, then the Query has no filter. The Agent will return every instance of type target it has. If selector_type is present, it may be one of the following supported values:
- "id" - an exact match against the target's unique identifier.
- "where" - a logical expression to apply to the target.
- ...more tbd...
Only one selector_type is allowed in the Query at a time.
"id" Selector
A Query with an id selector is used to find one particular instance of a given target type. The value of the "id" keyword is a type that suitably represents an exact identifier for the target type. The Agent will return the first instance that matches the identifier.
id selectors are only valid for the following subset of targets:
Target |
Selector Description |
"schema" |
A SchemaClassId |
"object" |
String containing the object identifier |
"agent" |
String containing the name of the agent |
The Agent will return no matches should a Query using an id selector specify a target name not in the above subset.
"Where" Selector
A Query with a where selector describes a logical expression that is used to filter the target set. This logical expression is built from a set of boolean operations that are applied against the target's data. These boolean operations may be combined using logical operations.
The Agent will apply the logical expression against every instance of the target type, and return the set of instances for which the expression evaluates as True.
The value of the "where" keyword is a list representation of a predicate expression. QMF will support the following syntax for predicate expressions:
In the above expressions, the left and right braces indicate lists. For example, BOOL-EXP is a list containing a BOOL-OP followed by BOOL-ARG.
A BOOL-EXP is a boolean test that is applied against the target. BOOL-OP defines the boolean operation that is performed on the arguments. QMF will define a set of string literals representing the supported boolean operations. At minimum, the following boolean operators are defined:
- "eq" - equality
- "ne" - inequality
- "lt" - arithmetical/lexical less-than
- "le" - arithmetical/lexical less-than-or-equal
- "gt" - arithmetical/lexical greater-than
- "ge" - arithmetical/lexical greater-than-or-equal
- "re_match" - string regular expression match (one argument)
- "exists" - True if named value is present in target, else False (one argument)
- "true" - always true (no arguments)
- "false" - always false (no arguments)
- <more tbd>
All operators are binary unless otherwise noted.
LOGIC-OP defines the logical operation that is applied to its arguments. QMF will define a set of string literals representing supported logical operations. At minimum, the following logical operators will be defined:
- "and" - logical AND, all arguments must evaluate to True.
- "or" - logical OR, at least one argument must evaluate to True
- "not" - logical NOT, all arguments must evaluate to False
- <more tbd>
Logic operations are "short-circuiting". That is, evaluation ceases as soon as the truth value of the expression is determined. For example, the evaluation of an "and" expression stops when the first argument that evaluates as False is found.
QMF considers string arguments in boolean expressions to be names of data values in the target object. When evaluating a predicate expression, QMF will fetch the value of the named data item from each candidate target object. The value is then used in the boolean expression. In other words, QMF considers string arguments to be variables in the expression. In order to indicate that a string should be treated as a literal instead, the string must be quoted using the "quote" expression.
For example, the following boolean expression contains the data item named "employee" against the string literal "Joey Jojo":
In implementation, predicate expressions are nested lists. The first element of all lists is the operator keyword string. The remaining list elements are arguments to the operator.
Examples:
Assume a QmfData type defines fields named "name", "address" and "town". The following predicate expression matches any instance with a name field set to "tross", or any instance where the name field is "jross", the address field is "1313 Spudboy Lane" and the town field is "Utopia":
Assume a QmfData type with fields "name" and "age". A predicate to find all instances with name matching the regular expression "?ross" with an optional age field that is greater than the value 29 or less than 12 would be:
The valid set of <name> values in an predicate expression is determined by the target of the Query. QMF reserves a set of <name> values it recognizes. The tables below list the set of name strings allowed for each type of target, and what these names evaluate to from the target instance.
Target "schema_package" valid names |
Description |
"_package_name" |
Evaluates to the schema's package name string. |
Target "schema_id" and "schema" valid names |
Description |
"_package_name" |
Evaluates to the schema's package name string. |
"_class_name" |
Evaluates to the schema's class name string. |
"_type" |
Evaluates to the schema's type string ("_data" or "_event"). |
"_hash_str" |
Evaluates to the schema's hash string value. |
"_schema_id" |
Evaluates to the schema's full identifier (SchemaClassId). |
<property-name> |
Name of a property defined by the schema. Evaluates to the name string, or None if the property is not defined. |
<method-name> |
Name of a method defined by the schema. Evaluates to the name string, or None if the method is not defined. |
Target "agent_info" valid names |
Description |
"_name" |
Evaluates to the agent's name string. |
Target "object_id" and "object" valid names |
Description |
"_package_name" |
If schema assigned, evaluates to the schema's package name string, else None. |
"_class_name" |
If schema assigned, evaluates to the schema's class name string, else None. |
"_hash_str" |
If schema assigned, evaluates to the schema's hash string value, else None. |
"_schema_id" |
If schema assigned, evaluates to the schema's full identifier (SchemaClassId), else None. |
"_object_id" |
Evaluates to the identifying name string. |
"_update_ts" |
Evaluates to the last update timestamp. |
"_create_ts" |
Evaluates to the creation timestamp. |
"_delete_ts" |
Evaluates to the deletion timestamp. |
<value-name> |
Specifies the name of a data item in the QmfData data object. Evaluates to the value of the data item. |
QMF reserved <name> values all start with the underscore character "_". Do not create value or method names starting with an underscore.
The QmfQuery class represents a query:
The map encoding of a QmfQuery:
Index |
Optional |
Type |
Description |
"what" |
N |
map |
The target map. |
"id" |
Y |
map |
The identifier, map format determined by target type. |
"where" |
Y |
map |
The predicate map. |
Example Queries
With the above syntax, QMF queries can be constructed using AMQP maps and lists. For example, a query for all known schema identifiers:
Note that the absence of a "where" clause acts as a "match all"
directive. This query will return a list of all known SchemaId's.
A query for all schema identifiers whose package names are equal to the string "myPackage":
Query for the particular QmfData data object with the identifier "Agent007":
Query for the particular SchemaObjectClass instance that has the package name "MyPackage", class name "MyClass", that has a hash string of 'b49cda2d-bbe53b97-9f6ee5d1-485ea3da':
Query for all SchemaClass objects that match a given package and class
name:
Query all managed objects belonging to the "myPackage" package, of the class "myClass", whose object_id matches a given regular expression. Return a list of matching object identifiers:
Query for all QmfData objects from a particular schema, whose "temperature" property is greater or equal to 50:
In the previous example, the agent will convert the value 50 to a type compatible with the type given by the "temperature" property's schema in order to do the comparison.
Query for all objects that match the given schema, which have a property named "state" which does not match the regular expression "$Error*", or whose "owner" property is not set to "Cartman".
Subscriptions
A subscription allows a Console application to monitor specific management data for changes in state. A Console creates a subscription with an Agent based on a Query. The Query specifies the set of management data that is to be monitored. The Agent will periodically publish updates to the subscribing Console(s). The update contains a snapshot of the of the monitored data.
A subscription remains in effect for a predetermined amount of time. Once the subscription expires, no further updates are published. A console may elect to refresh a subscription prior to its expiration. Alternatively, a Console may explicitly cancel the subscription when the data no longer needs to be monitored.
Invoking Methods
QMF allows a Console application to perform a "remote procedure call" on the Agent. The procedure - or method - call executes on the Agent. On completion a result is passed back to the Console. Method calls can be associated with an instance of a data object, or applied to the Agent as a whole.
The structure of a method call may be described by the schema associated with the object. The schema can define a name for the method and a description of its input and output parameters. The SchemaMethod class is used for this purpose.
The value(s) returned to the Console when the method call completes are represented by the MethodResult class. The MethodResult class indicates whether the method call succeeded or not, and, on success, provides access to all data returned by the method call. Returned data is provided in a map indexed by the name of the parameter. The map contains only those parameters that are classified as "output" by the SchemaMethod.
Should a method call result in a failure, this failure is indicated by the presence of an error object in the MethodResult. This object is represented by a QmfData object. The structure of this QmfData object is application-defined, but should contain a description of the reason for the failure. There are no returned parameters when a method call fails.
A successful method invokation is indicated by the absence of the QmfData error object in the MethodResult.
Management Events
An event is a notification that is sent by an Agent to alert Console(s) of a change in some aspect of the system under management. Agents publish events asynchronously. Consoles have the option of receiving events from a given Agent.
To publish an event, the Agent application must call the raise_event() method, passing an instance of a QmfEvent object. The Agent publishes the QmfEvent instance.
To receive events, the Console application must enable event reception on a per-agent basis. The Console application does this by calling the enable_events() method on the desired Agent instance. Published events from the Agent will then appear on the Console's work-queue. The Console application may disable events by invoking the Agent's disable_events() method.
Work-Queue Event Model.
The original QMF API defined a set of callback methods that a Console or Agent application needed to provide in order to process asynchronous QMF events. Each asynchonous event defined its own callback method.
The new API replaces this callback model with a work-queue approach. All asynchronous events are represented by a WorkItem object. When a QMF event occurs it is translated into a WorkItem object and placed in a FIFO queue. It is left to the application to drain this queue as needed.
This new API does require the application to provide a single callback. The callback is used to notify the application that WorkItem object(s) are pending on the work queue. This callback is invoked by QMF when one or more new WorkItem objects are added to the queue. To avoid any potential threading issues, the application is not allowed to call any QMF API from within the context of the callback. The purpose of the callback is to notify the application to schedule itself to drain the work queue at the next available opportunity.
For example, a console application may be designed using a select() loop. The application waits in the select() for any of a number of different descriptors to become ready. In this case, the callback could be written to simply make one of the descriptors ready, and then return. This would cause the application to exit the wait state, and start processing pending events.
The callback is represented by the Notifier virtual base class. This base class contains a single method. An application derives a custom notification handler from this class, and makes it available to the Console or Agent object.
The WorkItem class represents a single notification event that is read from the work queue:
Console and Agent-specific WorkItem types are defined.
Console Application Model
This section describes the API that is specific to Console components.
A QMF console component is represented by a Console class. This class is the topmost object of the console application's object model.
A Console is composed of the following objects:
- a connection to the AMQP bus
- a queue of inbound work items
- a collection of all known schemas
- a list of all known remote Agents
- a cache of known data object proxies
The connection to the AMQP bus is used to communicate with remote Agents. The queue is used as a source for notifications coming from remote Agents.
QmfConsoleData Class
The QmfData class is subclassed to provide a Console specific representation of management data.
The Console application represents a managed data object by the QmfConsoleData class. The Console has "read only" access to the data values in the data object via this class. The Console can also invoke the methods defined by the object via this class. The actual data stored in this object is cached from the Agent. In order to update the cached values, the Console invokes the instance's refresh() method.
Note that the refresh() and invoke_method() methods require communication with the remote Agent. As such, they may block. For these two methods, the Console has the option of blocking in the call until the call completes. Optionally, the Console can receive a notification asynchronously when the operation is complete. See below for more detail regarding synchronous and asynchronous API calls.
Asychronous Event Model.
The Console application must support the following WorkItem types:
- AGENT_ADDED
- AGENT_DELETED
- NEW_PACKAGE
- NEW_CLASS
- OBJECT_UPDATE
- EVENT_RECEIVED
- AGENT_HEARTBEAT
- SUBSCRIBE_RESPONSE
- RESUBSCRIBE_RESPONSE
- SUBSCRIPTION_INDICATION
These WorkItem types are described in detail below:
AGENT_ADDED
When the QMF Console receives the first heartbeat from an Agent, an AGENT_ADDED WorkItem is pushed onto the work-queue. The WorkItem's get_param() call returns a map which contains a reference to the new Console Agent instance. The reference is indexed from the map using the key string "agent". There is no handle associated with this WorkItem.
Note: If a new Agent is discovered as a result of the Console find_agent() method, then no AGENT_ADDED WorkItem is generated for that Agent.
AGENT_DELETED
When a known Agent stops sending heartbeat messages, the Console will time out that Agent. On Agent timeout, an AGENT_DELETED WorkItem is pushed onto the work-queue. The WorkItem's get_param() call returns a map which contains a reference to the Agent instance that has been deleted. The reference is indexed from the map using the key string "agent". There is no handle associated with this WorkItem.
The Console application must release all saved references to the Agent before returning the WorkItem.
NEW_PACKAGE
TBD.
NEW_CLASS
TBD.
OBJECT_UPDATE
TBD.
EVENT_RECEIVED
TBD
SUBSCRIBE_RESPONSE
The SUBSCRIBE_RESPONSE WorkItem returns the result of a subscription request made by this Console. This WorkItem is generated when the Console's create_subscription() is called in an asychronous manner, rather than pending for the result.
The get_params() method of a SUBSCRIBE_RESPONSE WorkItem will return an instance of the following object:
The SubscriptionId object must be used when the subscription is refreshed or cancelled - it must be passed to the Console's refresh_subscription() and cancel_subscription() methods. The value of the SubscriptionId does not change over the lifetime of the subscription.
The console handle will be provided by the Agent on each data indication event that corresponds to this subscription. It should not change for the lifetime of the subscription.
The get_handle() method returns the reply handle provided to the create_subscription() method call. This handle is merely the handle used for the asynchronous response, it is not associated with the subscription in any other way.
Once a subscription is created, the Agent that maintains the subscription will periodically issue updates for the subscribed data. This update will contain the current values of the subscribed data, and will appear as the first SUBSCRIPTION_INDICATION WorkItem for this subscription.
SUBSCRIPTION_INDICATION
The SUBSCRIPTION_INDICATION WorkItem signals the arrival of an update to subscribed data from the Agent.
The get_params() method of a SUBSCRIPTION_INDICATION WorkItem will return an instance of the following object:
The get_handle() method returns None.
RESUBSCRIBE_RESPONSE
The RESUBSCRIBE_RESPONSE WorkItem is generated in response to a subscription refresh request made by this Console. This WorkItem is generated when the Console's refresh_subscription() is called in an asychronous manner, rather than pending for the result.
The get_params() method of a RESUBSCRIBE_RESPONSE WorkItem will return an instance of the following object:
The get_handle() method returns the reply handle provided to the refresh_subscription() method call. This handle is merely the handle used for the asynchronous response, it is not associated with the subscription in any other way.
Local representation of a remote Agent.
The console application maintains a list of all known remote Agents. Each Agent is represented by the Agent class:
The Console Object.
The Console class is the top-level object used by a console application. All QMF console functionality is made available by this object. A console application must instatiate one of these objects.
As noted below, some Console methods require contacting a remote Agent. For these methods, the caller has the option to either block for a (non-infinite) timeout waiting for a reply, or to allow the method to complete asynchonously. When the asynchronous approach is used, the caller must provide a unique handle that identifies the request. When the method eventually completes, a WorkItem will be placed on the work queue. The WorkItem will contain the handle that was provided to the corresponding method call.
All blocking calls are considered thread safe - it is possible to have a multi-threaded implementation have multiple blocking calls in flight simultaineously.
If a name is supplied, it must be unique across all Consoles attached to the AMQP bus under the given domain. If no name is supplied, a unique name will be synthesized in the format: "qmfc-<hostname>.<pid>"
Example Console Application
The following pseudo-code performs a blocking query for a particular agent.
The following pseudo-code performs a non-blocking query for all
agents. It completes when at least one agent is found.
Agent Application Model
This section describes the API that is specific to Agent components.
A QMF agent component is represented by a instance of the Agent class. This class is the topmost object of the agent application's object model. Associated with a particular agent are:
- the set of objects managed by that agent
- the set of schema that describes the structured objects owned by the agent
- a collection of consoles that are interfacing with the agent
The Agent class communicates with the application using the same work-queue model as the console. The agent maintains a work-queue of pending requests. Each pending request is associated with a handle. When the application is done servicing the work request, it passes the response to the agent along with the handle associated with the originating request.
QmfAgentData Class
The Agent manages the data it represents by the QmfAgentData class - a derivative of the QmfData class. The Agent is responsible for managing the values of the properties within the object, as well as servicing the object's method calls. Unlike the Console, the Agent has full control of the state of the object.
An agent can support one of two different models for managing its database of QmfAgentData objects: internal or external store.
Internal Object Store
An agent that implements internal object store gives full responsibility for managing its data objects to the QMF infrastructure. In this model, the application passes a reference for each managed object to the QMF agent. The agent manages the set of objects internally, directly accessing the contents of the object in order to service console requests.
With this model, the application's complexity is reduced. The application needs to instantiate the object and register it with the agent. The application also maintains a reference to the object, as the application is responsible for updating the object's properties as necessary.
However, the application must still service method calls. The agent notifies the application when a method call has been requested by a console. The application services the method call, passing the result of the method back to the agent. The agent then relays the response to the originating console.
The application may decide to delete an object instance. The application does this by invoking the destroy() method on the object. This notifies the agent, which will mark the object as deleted in its database. Once the application invokes the destroy() method on an object, it must no longer access the object. The agent will clean up the object at a later point in time.
Internal object store is the default model for agent object managment.
Data Consistency
The internal object store requires sharing of the managed data between the agent and the application. The application is responsible for keeping the data up to date, while the agent is responsible for providing the data to client consoles. It is likely that these components may be implemented in separate execution contexts. This raises the possibility that a data item could be in the process of being written to by the application at the same moment the agent attempts to read it. This could result in invalid data being read.
To prevent this from occuring, the QmfAgentObject class provides accessors for all data in the object. These accessors provide atomic access to the underlying data. Therefore, both the agent and the application code must use these accessors to manipulate a shared object's data.
External Object Store
An alternate agent implementation allows the application to take full responsibility for managing the objects. With this model, all instances of managed objects exist external to the agent. When a console requests an action against an object, that action is transferred from the agent to the application. The application then must process the request, and send the result to the agent. The agent then sends a reply to the requesting console.
The model gives full control of the managed objects to the application, but usually requires more application development work.
Agent Class
The base class for the agent object is the Agent class. This base
class represents a single agent implementing internal store.
AgentExternal Class
The AgentExternal class must be used by those applications that implement the external store model. The AgentExternal class extends the Agent class by adding interfaces that notify the application when it needs to service a request for management operations from the agent.
Asychronous Event Model.
The Agent uses the same notification driven work-queue model as the Console. In the Agent case, the following set of WorkItem types are supported:
- METHOD_CALL
- QUERY
- SUBSCRIBE_REQUEST
- RESUBSCRIBE_REQUEST
- UNSUBSCRIBE_REQUEST
Note Well: In the case of an internal store agent implementation, only the METHOD_CALL work item is generated. An external store agent must support all work item types.
METHOD_CALL
The METHOD_CALL WorkItem describes a method call that must be serviced by the application on behalf of this agent.
The get_params() method of a METHOD_CALL WorkItem will return an instance of the following object:
On completion of the method call, the application must provide the result of the call to the Agent. This is done by invoking the Agent's method_response() method. The method_response() method must be passed the handle from the METHOD_CALL WorkItem.
On successful completion of a method call, any output arguments from the method call must be passed in the out_args map parameter, in name=<value> pairs. The error parameter must be set to None.
If the method call fails the application must indicate the failure by passing a QmfData instance via the error parameter. The structure of this QmfData is application-specific, and meant to provide a description of the failure to the console.
QUERY
The QUERY WorkItem describes a query that the application must service. The application should call the query_response() method for each object that satisfies the query. When complete, the application must call the query_complete() method. If a failure occurs, the application should indicate the error to the agent by calling the query_complete() method with a description of the error.
SUBSCRIBE_REQUEST
The SUBSCRIBE_REQUEST WorkItem provides a query that the agent application must periodically publish until the subscription is cancelled or expires. On receipt of this WorkItem, the application should call the Agent::subscription_response() method to acknowledge the request. On each publish interval, the application should call Agent::subscription_indicate(), passing a list of the objects that satisfy the query. The subscription remains in effect until an UNSUBSCRIBE_REQUEST WorkItem for the subscription is received, or the subscription expires.
The get_params() method call of the SUBSCRIBE_REQUEST WorkItem returns an instance of the following object:
The Agent application must call the AgentExternal::subscription_response() method in response to this WorkItem.
The get_handle() WorkItem method returns the reply handle which should be passed to the Agent's subscription_response() method.
RESUBSCRIBE_REQUEST
The RESUBSCRIBE_REQUEST is sent by a Console to renew an existing subscription. The Console may request a new duration for the subscription, otherwise the previous lifetime interval is repeated.
The get_params() method call of the RESUBSCRIBE_REQUEST WorkItem returns an instance of the following object:
The Agent application must call the AgentExternal:subscription_reponse method in response to this WorkItem.
The get_handle() WorkItem method returns the reply handle which should be passed to the Agent's subscription_reponse() method
UNSUBSCRIBE_REQUEST
The UNSUBSCRIBE_REQUEST is sent by a Console to terminate an existing subscription.
The get_params() method call returns the subscription identifier assigned by the Agent when the subscription is created.
The Agent application should terminate the given subscription if it exists, and cancel sending any further updates against it.
|