Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
HP WBEM Services Software Developer's Kit for HP-UX Provider and Client Developer's Guide > Chapter 4 Provider Implementation

Provider Design Considerations

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

Flow Of A Client Request

The following is a high-level description of the control flow of a client request:

  • A client issues a request for data or an operation; the request is encoded in xmlCIM by the Client API (if used) and sent to the CIM server.

  • The CIM server receives the xmlCIM request, decodes it, and authenticates it by determining whether the username and password are valid (authentication is automatic for local requests - see the connectLocal() client function).

  • The CIM server determines whether the user is authorized to perform the requested operation in the specified namespace.

  • The CIM server searches the provider registration information to determine which provider can service the request.

  • The CIM server loads the appropriate shared library (if it’s not already loaded), and calls the appropriate provider.

  • The provider has the responsibility to determine whether the client is authorized to issue the request if necessary (in addition to the simple namespace authorization already performed by the CIM server).

  • The provider performs whatever operations are required to satisfy the request, and delivers information requested, as appropriate, to the CIM server.

  • The CIM server encodes the information received from the provider into xmlCIM and returns it in its reply to the client.

Provider Execution Context

A provider module is implemented as a shared library. The CIM Server loads the shared library and initializes the provider on demand (on the first access of an object served by the provider). In HP WBEM Services for HP-UX, the provider functions are called in the process context of the CIM Server. However, the provider must not assume that it is running in the same process as the CIM Server.

The process context in which a provider runs is determined by the CIM Server, and is not configurable. This means that it is not possible to set environment variables, working directory, permission mask, or any other environment parameters before a provider is activated. Furthermore, the provider must not set any of these during operation. If it is necessary to do so for correct operation, the provider should create a separate process with the needed environment, and communicate with this process by any suitable means of interprocess communication (for example, pipes, shared memory, files, or another mechanism).

Providers run under the root user ID in HP WBEM Services for HP-UX. However, developers must not assume that this will continue to be true in future releases. Please refer to the section on “Security Architecture” for more information.

Providers built for IPF deployment should be compiled as native programs. They will not work if compiled in emulation mode.

Provider Registration and Naming

Provider source code is compiled and linked to build a shared library. The CIM Server will call the functions in this shared library when a client requests an operation on a CIM object that the provider instruments. The CIM Server finds the shared library containing the provider for a particular CIM object by searching the Provider Registrations that it was supplied when providers were installed. Provider Registration consists of CIM instances of three classes of objects, all defined in the root/PG_InterOp namespace:

  • PG_ProviderModule

  • PG_Provider

  • PG_ProviderCapabilities

PG_ProviderModule

The PG_ProviderModule object corresponds to a single shared library. Example 4-1 shows its definition, in a MOF declaration:

Example 4-1 PG_ProviderModule: An abridged MOF declaration

class PG_ProviderModule : CIM_LogicalElement
{
[ Key ] string Name;
string Vendor;
string Version;
string InterfaceType;
string InterfaceVersion;
string Location;
uint16 OperationalStatus[];
string OtherStatusDescription;


uint32 start();
uint32 stop();
};

A shared library (provider module) may contain more than one provider. This can simplify sharing code among several providers that must use the same system services to perform their operations. The name of the shared library file must be of the form lib<library-name>.sl where <library-name> is the string specified in the Location property of an instance of PG_ProviderModule.

The shared library file may reside in any directory on the system, but a symbolic link to the file must exist in /opt/wbem/providers/lib in order for the CIM Server to find it.

The Name property of PG_ProviderModule is an arbitrary string that will be referenced in instances of PG_Provider and PG_ProviderCapabilities. Like the Location property (and the shared library name), it is advisable to use the word Module in the string.

Example 4-2 PG_ProviderModule: Instance shown in MOF

instance of PG_ProviderModule
{
// Properties inherited from CIM_ManagedElement
Caption = "Dynamic Information Provider Module";    // not required

Description = "Module containing several providers";// not required
// Properties inherited from CIM_ManagedSystemElement
InstallDate = "20020517140341.000000-420";          // not required

// Properties local to PG_ProviderModule
Name = "DynamicInfoModule"; // required
Location = "DynamicInfoModule"; // required
Vendor = "ACME Computer Corp.";
Version = "2.0.0";
InterfaceType = "C++Default";        // required
InterfaceVersion = "2.1.0";          // required
};

The Location property is used to construct the path of the shared library containing the provider module as described in Figure 4-2, above.

The extension is either .sl or .so, depending on the platform:

  • On HP-UX PA, the path in this example would be:

    /opt/wbem/providers/lib/libDynamicInfoModule.sl 
  • On HP-UX IA, the path in this example would be:

    /opt/wbem/providers/lib/libDynamicInfoModule.so 

The InterfaceType and InterfaceVersion properties are required with the values exactly as shown in order to allow possible future implementations to support different provider interface protocols and versions.

PG_Provider

The PG_Provider object identifies a single provider and the PG_ProviderModule in which it can be found. Example 4-3 shows its definition in a MOF declaration:

Example 4-3 PG_Provider: an abridged MOF declaration

class PG_Provider : CIM_LogicalElement
{
[ Key, Propagated("PG_ProviderModule.Name") ]
string ProviderModuleName;
[ Key ]
string Name;
};

The ProviderModuleName property is a string whose value must be the same (case insensitive) as the Name property of an instance of PG_ProviderModule.

The Name property in PG_Provider is the string the CIM Server will pass to the PegasusCreateProvider function when it needs a pointer to the provider (the first time it calls a function in the provider). Example 4-4 below shows a source fragment with an example of C++ provider class declarations and an implementation of the PegasusCreateProvider function. Example 4-5 shows how an instance of PG_Provider associates a provider name with a provider module corresponding to a shared library.

Example 4-4 Provider source fragment showing provider class declarations and implementation of the PegasusCreateProvider entry point

// This module contains two providers
// ProcessStatusProvider
// MemoryInfoProvider

// class declaration for the Process Status Provider
class ProcessStatusProvider : public CIMInstanceProvider
{
...
};

// class declaration for the Memory Info Provider
class MemoryInfoProvider : public CIMInstanceProvider
{
...
};

// This module (shared library) contains two providers.
// PegasusCreateProvider returns a pointer to the provider
// that was requested by the CIM Server. For each provider, it
// will create an instance of the class on the heap
// (i.e., with "new") and return the pointer.
//
// The string passed to PegasusCreateProvider is the
// PG_Provider.Name, and does not need to match the
// name of the C++ provider class declared above.
//
// extern "C" tells the C++ compiler to generate a
// pure C symbol name for PegasusCreateProvider, rather
// than a "mangled" C++ symbol name
//
extern "C" CIMProvider *
PegasusCreateProvider(const String &providerName)
{
// create new provider instance on heap and return its
// address. will require the provider's terminate() function
// to use "delete this;" to deallocate
if String::equalNoCase( providerName, "ProcessStatusProvider" )
return new ProcessStatusProvider;

if String::equalNoCase( providerName, "MemoryInfoProvider" )
return new MemoryInfoProvider;

// If neither name was recognized, there could be an
// error in a registration instance
return 0;
}

Example 4-5 PG_Provider: A MOF showing an instance

instance of PG_Provider
{
// Properties inherited from CIM_ManagedElement
  Caption = "Process Status Provider";                //not required
Description = "Instruments several classes in the
"CIM_LogicalElement";                             //not required

// Properties inherited from CIM_ManagedSystemElement
  InstallDATE = “20020517140341.000000-420”;   // not required

// Properties local to PG_Provider
ProviderModuleName = "DynamicInfoModule"; // required
Name = "ProcessStatusProvider"; // required
};

In Example 4.5, the instance of PG_Provider informs the CIM Server that the provider named ProcessStatusProvider will be found in the Provider Module named DynamicInfoModule. For our example, there would be another instance of PG_Provider to tell the CIM Server that the MemoryInfoProvider is also in the DynamicInfoModule.

PG_ProviderCapabilities

The class PG_ProviderCapabilities is used to associate a provider with the class for which it supplies instances, methods, indications, or associations in the specified namespace. Example 4-6 shows the definition of PG_ProviderCapabilities in a MOF declaration:

Example 4-6 PG_ProviderCapabilities: An abridged MOF declaration

class PG_ProviderCapabilities : CIM_ManagedElement
{
[ Key, Propagated ("PG_Provider.ProviderModuleName") ]
string ProviderModuleName;
[ Key, Propagated ("PG_Provider.Name") ]
string ProviderName;
[ Key ]
string CapabilityID;
string ClassName;
string namespaces[];
[ ArrayType ("Indexed"),
ValueMap { "2", "3", "4", "5" },
Values {"Instance","Association","Indication","Method"}]
uint16 ProviderType[];
string SupportedProperties[];
string SupportedMethods[];
};

As in the PG_Provider object (Example 4-3), the ProviderModuleName tells the CIM Server which module contains the provider that this capabilities instance describes. It must be the same as the Name property of an instance of PG_ProviderModule. Likewise, the ProviderName property must be the same as the Name property of an instance of PG_Provider.

The CapabilityID key makes it possible to have several instances naming the same provider and module, but with different capabilities. They might differ in the class that they instrument, or they may name different sets of properties or implement different provider types. However, there can be no more than one Instance Provider for each class in a namespace! There may be more than one Method Provider as long as they do not claim service for the same method.

The following descriptions are taken from the complete MOF definition of PG_ProviderCapabilities:

  • SupportedProperties

    lists the properties supported by this provider. If this array is NULL, the provider MUST support all of the properties defined in the class. If the provider does not support all of the properties, the properties supported MUST be included in the array.

  • SupportedMethods

    lists the methods supported by this provider. If this array is NULL, the provider MUST support all the methods defined in the class. If the provider does not support all the methods, the methods supported MUST be included in the array.

MOF Example 4-7, below, shows a class definition. Example 4-8, following that, shows an instance of PG_ProviderCapabilities that associates the provider with the newly created class:

Example 4-7 MOF declaration of the class ACME_ComputerSystem

  [ Description (
"The class ACME_ComputerSystem extends CIM_ComputerSystem "
"to add several properties containing additional owner "
"contact information." ),
Version( "1.2" ) ]
class ACME_ComputerSystem : CIM_ComputerSystem{
[ Description(
"Additional contact information for the primary "
"owner for this computer system. This is intended "
"to be the telephone number of a pager." ) ]
string PrimaryOwnerPager;
...
};

Example 4-8 PG_ProviderCapabilities: A MOF instance

instance of PG_ProviderCapabilities
{
// Properties inherited from CIM_ManagedElement
Caption = "ComputerSystemProvider Capabilities"; // not required
Description = "First capability description for the "
"Computer System Provider"; // not required

// Properties local to PG_ProviderCapabilities
ProviderModuleName = "ComputerSystemModule"; // required
ProviderName = "ACME_ComputerSystemProvider"; // required
CapabilityID = "1"; // required
ClassName = "ACME_ComputerSystem"; // required
namespaces = { "root/cimv2" }; // required
ProviderType = { 2 }; // 2=InstanceProvider // required
SupportedProperties = NULL; // NULL=All properties
SupportedMethods = NULL; // NULL=All methods
};

The instance of PG_ProviderCapabilities in Example 4-8 informs the CIM Server as follows:

  • This capability instance is for a provider named ACME_ComputerSystemProvider in the module ComputerSystemModule.

  • There can be more than one instance for the same provider; this one has a unique CapabilityID of 1. A different set of capabilities might specify a different class instrumented by this provider, or the same (or different) classes in different namespaces, possibly with different characteristics.

  • The ClassName property identifies the class instrumented by the provider. A single provider can support more than one class, but each Capabilities instance describes the provider's capabilities for a single class. If, for example, the provider will respond to operations at different levels of the model hierarchy, additional Capabilities instances would be required. In this example, we could have another Capabilities instance specifying that the same provider supports operations on CIM_ComputerSystem, the parent class of ACME_ComputerSystem.

  • The provider instruments ACME_ComputerSystem only in the root/cimv2 namespace. It is possible to support the same class in several namespaces with the same provider. Note that the definition of the instrumented class itself (ACME_ComputerSystem, in this example) must be declared in the specified namespace.

  • This provider implements the Instance Provider interface, supporting instance manipulation operations. The values for the ProviderType property are:

    • Instance

    • Method

    A provider may implement more than one interface, but there can be no more than one Instance Provider for a class in a namespace! Often, it is useful to implement several interfaces in the same provider, as this allows the platform-specific code that accesses system resources to be shared.

  • The SupportedProperties property is an array of strings, each of which name a property of the instrumented class. If the value is NULL, or if SupportedProperties is absent, all properties are supported. If SupportedProperties is an empty set ({ }), no properties are supported. Note that declaring support for all properties of a class does not obligate the provider to actually supply (or accept) all properties. It is permissible to throw CIMNotSupportedException for properties that are not supported. The declaration of support for a property informs the CIM Server that this provider accepts responsibility for the property.

  • Like SupportedProperties, the SupportedMethods property specifies methods that are supported. The provider must register as a Method Provider, by placing a “5” in the ProviderType array, for the SupportedMethods to be meaningful. While there can be more than one method provider for a given class, only one method provider may be registered for the same method of a class.

The files containing MOF fragments shown above must be loaded into the CIM Server's repository: the files containing provider registration instances must be loaded into the root/PG_InterOp namespace, and the file containing the definition of the ACME_ComputerSystem class must be loaded into a normal, non-system namespace (please refer to the section on “Installing and Running a Provider”). When a client requests an operation to be performed on a class, the CIM Server will examine these instances to determine which provider(s) to call.

A Version qualifier should be used on the class definition for ACME_ComputerSystem. This allows a provider installation procedure to determine whether an existing schema definition is a lower version (and can therefore be upgraded) or equal to or greater than the version supported by the new provider (in which case it should not be modified).

Provider Naming Conventions

There is no required relationship between the name of the provider (PG_Provider.Name), the name of the module (PG_ProviderModule.Name), the name of the shared library into which it is built (PG_ProviderModule.Location), and the schema element with which it is associated (PG_ProviderCapabilities.ClassName). However, for clarity, it is useful to choose names that help to identify the element. The conventions evident in the previous example show how this may be done.

Key Values: Uniquely Specifying an Instance

Most providers manipulate instances or instance references (also called object paths) in some way. The set of all keys of an instance defines the unique identity of an instance within a namespace of managed objects. For example, there are possibly many users whose Name is "smith", but on any given UNIX computer system, there can be only one called "smith." In order to know exactly which user "smith" we are interested in (to address email, for example), we must know the name of the system in question. Therefore, for a user account, a unique identification must include the SystemName as well as the Name (just as an Internet email address is specified as username@domain-name). In this example of a class representing a user account, the SystemName and Name, together, form a set of key properties that uniquely define each instance. In this example, the SystemName is said to specify the context (or scope) in which the Name will be unique. For many classes in the CIM model in which keys are defined, there will be at least one key property to specify the scope of the class's other keys.

Keys that specify necessary scope are always propagated from another class of object, as mentioned in the section on Common Properties in Chapter 3, under General Schema Use Cases. The SystemName key present in many CIM classes is propagated from the Name property of the CIM_System class (or one of its subclasses). When scoping keys exist in a class definition, the classes from which the scoping keys are propagated are also specified as key properties. So if there is a SystemName key, there must be a property specifying the name of the class from which the SystemName is obtained. This key is the SystemCreationClassName. For a typical class in the hierarchy of CIM_ManagedSystemElement, (CIM_LogicalDevice, for example), the full set of key properties might be:

  • SystemCreationClassName

    The name of the class which has an instance from which the value of the SystemName property is obtained. This could be, for example, CIM_ComputerSystem.

  • SystemName

    The Name property in the instance of the system that identified the object in question. A typical value could be a fully qualified IP hostname, such as idsys.hp.com. The values returned for these properties by different providers must be consistent. That is, all providers that return the SystemCreationClassName and SystemName keys must return consistent values for these keys when referring to the same scope.

  • CreationClassName

    The name of the class to which the instance belongs, usually the class that the provider is instrumenting. Clients can request an enumeration of instances of a higher class in the hierarchy, so this property identifies which class the instance is from. This is important, because there may be several subclasses of a given class on which a client requests an enumeration, and all instances returned must have a unique set of key values. Consider, for example, the classes CIM_TapeDrive and CIM_DisketteDrive, both subclasses of CIM_MediaAccessDevice. The providers for these classes may have been implemented to return a simple integer for their DeviceID property, so that the first tape drive's DeviceID is "0" (zero), and the first diskette drive's DeviceID is also "0". Without the CreationClassName key, a client enumerating the superclass CIM_MediaAccessDevice would receive two instances with identical sets of keys. Only the CreationClassName key guarantees that the full set of key values will be unique.

    When a provider is registered to serve more than one level of the hierarchy, as in the example above, it must always return the same value for CreationClassName, regardless of which class it is responding to. This is essential so that clients will always receive the same set of keys for a given instance. In general, it is useful to return the name of the highest class served by the provider, because this affords the greatest portability (clients will generally "know" about more general classes, and may not know about extension classes on specific platforms).

  • Name (or analogous)

    The unique name of the instance within the scope defined by the previous keys. In many classes, this non-propagated key will be called Name, DeviceID, Handle or something that allows the user to understand that it will contain a value that is unique within that class of object.

It is entirely up to the provider to determine the value of a Name (or analogous) key. The primary requirement is that there may be only one instance of a class with a given key value. If this were not the case, it would be impossible to distinguish between different instances in a given context. Although not a requirement, the value can be chosen to be representative of what a client would expect to see. For example, the name of a disk partition on HP-UX, as displayed by the df command, is often something like /dev/vg00/lvol3, so this would be an appropriate value for the Name property of a disk partition object.

The values for SystemCreationClassName and SystemName must be chosen with equal care, since the provider must coexist with other providers on the same platform (indeed, in the same namespace). The values of these properties must be chosen to ensure consistency and avoid conflicts. When the meaning of SystemName is clearly a system's IP hostname (as it will often be), the provider must supply a standard fully qualified Internet hostname. This value is not reliably obtained from the gethostname() library function, but rather from gethostbyname(). The following code fragment illustrates a suitable means to obtain this value:

Example 4-9 Code Fragment to obtain value for SystemName

#include <sys/param.h>
#include <netdb.h>
#include <Pegasus/Common/String.h>
...

struct hostent *he;
char hn[MAXHOSTNAMELEN];

// fill in hn with what this system thinks is its name
gethostname(hn,MAXHOSTNAMELEN);

// find out what the nameservices think is its full name
if (he=gethostbyname(hn)) return String(he->h_name);

// but if that failed, return what gethostname said
else return String(hn);
...

Use of Empty String Key Values

While the full set of keys is necessary to uniquely identify an instance within a namespace, it is useful to allow clients to specify empty strings for key values when there is no ambiguity. For example, when a client submits a getInstance() operation to a CIM Server, it is clear that the value of the SystemName key will normally be the same as the system to which the client has connected. This is especially useful when managing a multi-homed system (a system that may have more than one IP address or DNS name), since the client need not guess the value of SystemName when there can be several different hostnames for the same system. Unless the provider has a reason to require a value for SystemName, it should accept an empty string for this key without returning an error. As a general rule, providers should accept empty strings for any keys that they do not actually require to identify an instance from among those they manage. However, the provider should always supply values for all keys when returning data to the client.

Multi-Threading for Concurrent Requests

The CIM Server can process several client requests in different threads concurrently in the same provider. Therefore, provider code must be 100% thread-reentrant and must avoid/prevent concurrent access of any shared resources. If shared (global) resources are used, access must be serialized by the use of a suitable semaphore (or mutex) to protect the shared resource against modification that could lead to erroneous behavior. This is particularly important in provider interface methods that alter platform behavior, such as modifyInstance(), createInstance(), and deleteInstance(), but applies to any code that manipulates a globally accessible resource or may have a side effect. Information on developing thread-safe applications can be found online in documentation for the aCC C++ compiler at http://www.docs.hp.com.

Global Symbols

While all providers implement functions of the same names (for example, getInstance()), there is no duplication of symbol names for symbols defined within a C++ provider classes. The C++ compiler generates unique symbols, because the functions are declared in differently named provider classes. However, it is possible to declare global symbols (technically, named ::<name>), and these may indeed "collide" with symbols of the same name. This issue is not unique to C++ nor to HP WBEM Services for HP-UX providers. Care should be taken to avoid the use of global symbol names whenever possible. When necessary, names should be chosen with a component, such as a module name, that will guarantee, or at least increase the probability of, uniqueness.

Security Architecture

User Authentication

Access to WBEM Services is restricted to users with valid accounts on the system being managed. Requests from remote clients contain a username/password pair that the CIM Server will authenticate. Requests may also be received through a local connection using the connectLocal() function in the Client API. This function does not take username or password arguments. The user ID for a local client request is that of the process issuing the request.

namespace Authorization

In addition to user authentication, if the feature is enabled, the CIM Server performs namespace authorization (this is disabled when the product is installed, but can be enabled with the cimconfig command). There are several namespaces serviced by the CIM Server. Each namespace has an associated list of users who are authorized to access its objects, and what level of access is permitted (read, write, or read+write). The namespace authorization database is managed with the cimauth command, described in the cimauth man page.

The section on “Provider Registration and Naming” describes how providers can register to serve classes in multiple namespaces. namespaces are also discussed in Appendix A, CIM Naming Guidelines.

Execution Context

Once a client request has been authenticated and authorized, the username is passed to the provider in the OperationContext parameter present in all function calls. No password is passed to the provider.

As previously mentioned, providers run under the root user ID. Providers must use the username to determine whether the user has permission to perform the requested operation. This determination must be made in addition to the namespace authorization that the CIM Server may have performed. The provider must not perform any operation that would be unauthorized for the user on whose behalf it is executing the request. While it may seem technically possible, setuid() must never be called in the provider process, since other requests may be running concurrently in other threads (in the same provider or in others that may be loaded in the same process context). If done in a thread-safe manner, it is permissible to create a separate process under a specific user ID. This may be an appropriate design strategy in cases where it is the most or only reliable means of ensuring secure operation. Care should be taken to consider performance and resource utilization.

If needed for additional authorization, the namespace of the target object can be obtained from the object identification parameter of the request, as in the API documentation of the HP WBEM Services SDK, in the /opt/wbem/html directory.

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 2000-2003 Hewlett-Packard Development Company, L.P.