9. Mock WBEM server

New in pywbem 0.12 as experimental and finalized in 1.2.

9.1. Overview

The ‘pywbem_mock’ module of pywbem provides a mock WBEM server that enables using the pywbem client library without a real WBEM server. This is useful for testing the pywbem client library itself as well as for the development and testing of Python programs that use the pywbem client library.

The pywbem_mock.FakedWBEMConnection class establishes an in-process mock WBEM server and represents a faked connection to that mock WBEM server. That class acts as both the client API and as an API for managing the mocked WBEM server.

The pywbem_mock.FakedWBEMConnection class is a subclass of pywbem.WBEMConnection and replaces its internal methods that use HTTP/HTTPS to communicate with a WBEM server with methods that communicate with the mock WBEM server. As a result, the operation methods of FakedWBEMConnection are those inherited from WBEMConnection, so they have exactly the same input parameters, output parameters, return values, and even most of the raised exceptions, as when invoked on a WBEMConnection object against a real WBEM server.

The mock WBEM server has an in-memory repository of CIM objects (the CIM repository). Each FakedWBEMConnection object creates its own CIM repository that contains the same kinds of CIM objects a WBEM server repository contains: CIM classes, CIM instances, and CIM qualifier declarations types, contained in CIM namespaces. Because FakedWBEMConnection operates only on the CIM repository, the class does not have any connection- or security-related constructor parameters.

Like WBEMConnection, FakedWBEMConnection has a default CIM namespace that is created in the CIM repository upon FakedWBEMConnection object creation. Additional namespaces in the CIM repository can be created with add_namespace() .

An Interop namespace can be created by adding it via add_namespace(). The Interop namespace will be initially empty, and the necessary instance(s) of a CIM namespace class will be automatically created when registering the namespace provider CIMNamespaceProvider. See Mocking multiple CIM namespaces for details.

The CIM repository must contain the CIM classes, CIM instances and CIM qualifier declaration types that are needed for the operations that are invoked. This results in a behavior of the mock WBEM server that is close to the behavior of the operations of a real WBEM server. FakedWBEMConnection has methods that provide for adding CIM classes, instances and qualifier types to its CIM repository by providing them as CIM objects, or by compiling MOF. See Building a mocked CIM repository for details.

The following example demonstrates setting up a mock WBEM server, adding several CIM objects defined in a MOF string to its CIM repository, and executing WBEM operations on the faked connection:

import pywbem
import pywbem_mock

# MOF string defining qualifiers, class, and instance
mof = '''
    Qualifier Key : boolean = false,
        Scope(property, reference),
        Flavor(DisableOverride, ToSubclass);
    Qualifier Description : string = null,
        Scope(any),
        Flavor(EnableOverride, ToSubclass, Translatable);
    Qualifier In : boolean = true,
        Scope(parameter),
        Flavor(DisableOverride, ToSubclass);

        [Description ("This is a dumb test class")]
    class CIM_Foo {
            [Key, Description ("This is key prop")]
        string InstanceID;
            [Description ("This is some simplistic data")]
        Uint32 SomeData;
            [Description ("This is a method without parameters")]
        string Fuzzy();
            [Description ("This is a second method with parameter")]
        uint32 Delete([IN, Description('blahblah']
          boolean Immediate);
    };

    instance of CIM_Foo as $I1 { InstanceID = "I1"; SomeData=3 };
    '''

# Create a faked connection including a mock WBEM server with a CIM repo
conn = pywbem_mock.FakedWBEMConnection(default_namespace='root/cimv2')

# Compile the MOF string and add its CIM objects to the default namespace
# of the CIM repository
conn.compile_mof_string(mof)

# Perform a few operations on the faked connection:

# Enumerate top-level classes in the default namespace (without subclasses)
classes = conn.EnumerateClasses();
for cls in classes:
    print(cls.tomof())

# Get the 'Description' qualifier type in the default namespace
qd = conn.GetQualifier('Description')

# Enumerate subclasses of 'CIM_Foo' in the default namespace (without subclasses)
classes2 = conn.EnumerateClasses(classname='CIM_Foo')

# Get 'CIM_Foo' class in the default namespace
my_class = conn.GetClass('CIM_Foo')

# Get a specific instance of 'CIM_Foo' in the default namespace
inst = conn.GetInstance(CIMInstanceName('CIM_Foo', {'InstanceID': "I1"}))

The mock WBEM server supports:

  1. All of the WBEMConnection operation methods that communicate with the WBEM server (see below for the operations supported and their limitations).

  2. Multiple CIM namespaces and a default namespace on the faked connection.

  3. Gathering time statistics and delaying responses for a predetermined time.

  4. WBEMConnection logging except that there are no HTTP entries in the log.

  5. User-defined providers that replace the the default providers specific request methods, CIM classes, and namespaces. See User-defined providers.

The mock WBEM server does NOT support:

  1. CIM-XML protocol security and security constructor parameters of WBEMConnection.

  2. Processing of queries defined for ExecQuery() in languages like CQL and WQL. The mocked operation parses only the very general portions of the query for class/instance name and properties.

  3. Filter processing of FQL (see DSP0212) the Filter Query Language parameter QueryFilter used by the Open… operations because it does not implement the parser/processor for the FQL language. It returns the same data as if the filter did not exist.

  4. Providing data in the trace variables for last request and last reply in WBEMConnection: last_request, last_raw_request, last_reply, last_raw_reply, last_request_len, or last_reply_len.

  5. Log entries for HTTP request and response in the logging support of WBEMConnection, because it does not actually build the HTTP requests or responses.

  6. Generating CIM indications.

  7. Some of the functionality that may be implemented in real WBEM servers such as the __Namespace__ class/provider. Note that such capabilities can be at least partly built on top of the existing capabilities by implementing user-defined providers. Thus the CIM_Namespace class is supported with the user defined provider in the pywbem_mock directory but only registered if the user wants to use the provider.

 +----------------------------------+
 |                                  |
 | +------------------------------+ |           +-------------+
 | |                              | |           |             |
 | | WBEM Server Mock Methods     | |           | Main        |
 | |                              | |           | Provider    |
 | |            All other Requests+------------>+             |
 | |  CreateInstance, ModifyInst  +------+      | Class ,assoc|
 | |  DeleteInstance, InvokeMeth  | |    |      | some inst   |
 | +------------^-----------------+ |    |      | requests    +--+
 |              |                   |    |      +-------------+  |
 | +------------+-----------------+ |    |                       |
 | | WBEM Server Mock Interface   | |    |      +-------------+  |     +-----------+
 | |                              | |    |      |             |  |     | Instance  |
 | +------------^-----------------+ |    |      | Provider    |  |     | Write     |
 |              |                   |    |      | Dispatcher  |  |     | Provider  |
 |              |                   |    |      |             +------->+ (Default) |
 |              |                   |    |      | Dispatches  |  |     +-|---|-----+       +----------+
 | +------------+-----------------+ |    |      | methods to  |  | +-----|   |-------------+ User     |
 | |                              | |    +----->+ default or  +----------------------------> Instance |
 | |  Client API request methods  | |           | registered  |  | |                       | Providers|
 | |  from WBEM Connection        | |           | user        |  | |   +------------+      |          +-----+
 | |                              | |           | providers   |  | |   |            |      +----------+     |
 | |   ..CreateClass, etc.        | |           |             +------->+ Method     |                       |
--->                              | |           |             |  | |   | Provider   |                       |
 | |                              | |           |             |  | |   | (Default)  |                       |
 | |                              | |           +-----------+-+  | |   +-----|------+                       |
 | +------------------------------+ |                       |    | |         |------------------------+     |
 | +------------------------------+ |                       +-------------------------------> User    |     |
 | |  Mock Environment management | |                            | |                        | Method  |     |
 | |  methods                     | |                            | |                        | Providers     |
 | |  * Mof Compiler, add objects | |                            | |                        |         |     |
 | |  * Manage namespaces         | |                            | |                        +---------+     |
 | |  * Register user providers   +---------+    +---------------v-V----------------------------------+     |
 | +------------------------------+ |       |    |                                                    |     |
 |  FakedWBEMConnection             |       +---->          CIM Repository                            <-----+
 +----------------------------------+            |                                                    |
                                                 +----------------------------------------------------+

Diagram of flow of requests operations through mocker

9.2. WBEM operations of a mock WBEM server

The pywbem_mock.FakedWBEMConnection class supports the same WBEM operations that are supported by the pywbem.WBEMConnection class and in addition a set of methods for setting up its mocked CIM repository, and for registering user-defined providers for selected WBEM operations.

These faked WBEM operations generally adhere to the behavior requirements defined in DSP0200 for handling input parameters and returning a result.

The faked WBEM operations get the data to be returned from the CIM repository of the mock WBEM server, and put the data provided in operation parameters that modify objects (create, modify, and delete operations) into the CIM repository.

However, because the pywbem mock support is only a simulation of a WBEM server and intended to be used primarily for testing, there are limitations and differences between the behavior of the faked WBEM operations and a real WBEM server.

The descriptions below describe differences between the faked WBEM operations of the pywbem mock support and the operations of a real WBEM server, and the effects of the operation modes of the CIM repository.

9.2.1. Faked instance operations

The operations that retrieve instances require instances in the repository for the instances to be recovered and that the classes for these instances exist in the repository.

  • GetInstance: Behaves like GetInstance(). Returns an instance defined by the instance name input parameter if that instance exists in the repository.

  • EnumerateInstances: Behaves like EnumerateInstances(), returning all instances of the CIM class defined on input and subclasses of this class filtered by the optional attributes for property names, etc.

  • EnumerateInstanceNames: Behaves like EnumerateInstances(), Returns the instance name of all instances of the pywbem.CIMClass defined on input and subclasses of this class.

  • CreateInstance: Behaves like CreateInstance().

    Creates the defined instance in the CIM repository.

    If there is no user-defined provider (see User-defined providers)for the class defined in the NewInstance parameter operation requires that all key properties be specified in the new instance since the CIM repository has no support for dynamically setting key properties and all key properties are required to get the newly defined instance with other requests. If a user-defined provider exists for the class, the behavior depends on that provider.

  • ModifyInstance: Behaves like ModifyInstance(). Modifies the instance defined by the instance name provided on input if that instance exists in the repository. It modifies only the properties defined by the instance provided and will not modify any key properties. If there is a user-defined provider defined for this operation, that provider may modify the default behavior.

  • DeleteInstance: Behaves like DeleteInstance(). Deletes the instance defined by the instance name provided on input if that instance exists. If there is a user-defined provider defined for this operation, that provider may modify the default behavior.

  • ExecQuery: This operation is not currently implemented.

See User-defined providers for more information on writing a user-defined provider.

9.2.2. Faked association operations

The faked association operations support both instance-level use and class-level requests. Class-level use requires the CIM repository to be in full mode, while instance-level use works in both operation modes of the CIM repository.

  • AssociatorNames: Behaves like AssociatorNames(), with the following requirements: The source, target, and association classes and their subclasses must exist in the CIM repository for both class-level use and instance-level use.

  • Associators: Behaves like Associators(), with the requirements described for AssociatorNames, above.

  • ReferenceNames: Behaves like ReferenceNames(), with the requirements described for AssociatorNames, above.

  • References: Behaves like References(), with the requirements described for AssociatorNames, above.

9.2.3. Faked method invocation operation

The faked method invocation operation (InvokeMethod) behaves like InvokeMethod(), but because of the nature of InvokeMethod, the user must provide an implementation of InvokeMethod based on the API defined in InvokeMethod().

NOTE: InvokeMethod is the method name pywbem and other clients and WBEM servers use for what the DMTF defines as extrinsic methods.

See User-defined providers for more information on writing a user-defined provider.

9.2.4. Faked pull operations

The faked pull operations behave like the pull operations of WBEMConnection, with the following exceptions:

  • The filter query related parameters FilterQuery and FilterQueryLanguage are ignored and no such filtering takes place.

  • The ContinueOnError parameter is ignored because injecting an error into the processing of the pull operations is not supported by the pywbem mock support, so no failures can happen during the processing of the pull operations.

  • The OperationTimeout parameter is currently ignored. As a result, there will be no timeout if the response_delay property is set to a time larger than the OperationTimeout parameter.

The faked pull operations are:

  • OpenEnumerateInstances: Behaves like OpenEnumerateInstances(), with the stated exceptions.

  • OpenEnumerateInstancePaths: Behaves like OpenEnumerateInstancePaths(), with the stated exceptions.

  • OpenAssociatorInstances: Behaves like OpenAssociatorInstances(), with the stated exceptions.

  • OpenAssociatorInstancePaths: Behaves like OpenAssociatorInstancePaths(), with the stated exceptions.

  • OpenReferenceInstances: Behaves like OpenReferenceInstances(), with the stated exceptions.

  • OpenReferenceInstancePaths: Behaves like OpenReferenceInstancePaths(), with the stated exceptions.

  • OpenQueryInstances: Behaves like OpenQueryInstances(), with the stated exceptions.

  • PullInstancesWithPath: Behaves like PullInstancesWithPath(), with the stated exceptions.

  • PullInstancePaths: Behaves like PullInstancePaths(), with the stated exceptions.

  • PullInstances: Behaves like PullInstances(), with the stated exceptions.

  • CloseEnumeration: Behaves like CloseEnumeration(), with the stated exceptions.

9.2.5. Faked iter operations

The iter operations on a faked connection are in fact the iter operations on WBEMConnection, because they do not directly issue requests and responses on the connection, but instead are a layer on top of underlying operations. For example, IterEnumerateInstances invokes either pull operations (i.e. OpenEnumerateInstances followed by PullInstancesWithPath) or traditional operations (i.e. EnumerateInstances). The use of pull vs. traditional operations is controlled via the use_pull_operations init parameter of FakedWBEMConnection.

The iter operations are:

  • IterEnumerateInstances()

  • IterEnumerateInstancePaths()

  • IterAssociatorInstances()

  • IterAssociatorInstancePaths()

  • IterReferenceInstances()

  • IterReferenceInstancePaths()

  • IterQueryInstances()

9.2.6. Faked class operations

Class operations only work if the CIM repository is in full operation mode.

  • GetClass: Behaves like GetClass(). Requires that the class to be returned is in the CIM repository.

  • EnumerateClasses: Behaves like EnumerateClasses(). Requires that the class specified in the ClassName parameter be in the CIM repository.

  • EnumerateClassNames: Behaves like EnumerateClassNames(). Requires that the class specified in the ClassName parameter be in the CIM repository.

  • CreateClass: Behaves like CreateClass(). Requires that the superclass of the new class (if it specifies one) is in the CIM repository.

  • DeleteClass: Behaves like DeleteClass(), with the following difference: This operation additionally deletes all direct and indirect subclasses of the class to be deleted, and all instances of the classes that are being deleted. Requires that the class to be deleted is in the CIM repository.

  • ModifyClass: Behaves like ModifyClass(). The pywbem_mock implementation rejects modifications where the class to modifiy has either subclasses or instances and resolves the various qualifiers, class origin values and propagated values as the the CreateClass operation.

9.2.7. Faked qualifier declaration operations

Qualifier operations declaration include the following.

  • SetQualifier: Behaves like SetQualifier(). Requires that the specified qualifier type is in the CIM repository.

  • GetQualifier: Behaves like GetQualifier(). Requires that the specified qualifier type is in the CIM repository.

  • EnumerateQualifiers: Behaves like EnumerateQualifiers(). Requires that the qualifier types to be returned are in the CIM repository.

  • DeleteQualifier: - Not implemented.

9.3. FakedWBEMConnection class

9.4. Building a mocked CIM repository

The CIM repository of a mock WBEM server needs to contain the CIM namespaces, and within them, the CIM qualifier declarations, CIM classes, and CIM instances required by the user. These are created as part of the setup of any particular pywbem mock environment. Thus, if the user only requires CIM_ComputerSystem in a particular namespace, only that class and its dependent classes and qualifier declarations need be in that namespace in the CIM repository, along with instances of the classes that will satisfy the client methods executed.

The classes FakedWBEMConnection and DMTFCIMSchema provide the tools to build the CIM repository.

CIM namespaces are created in the CIM repository by defining a default namespace for the FakedWBEMConnection object, and by using the add_namespace() method to create additional namespaces.

There are multiple ways to add CIM objects to a target namespace of the CIM repository:

  • From CIM objects, using the add_cimobjects() method.

    The specified CIM objects are added to or updated in a target namespace of the CIM repository. Dependent classes and qualifier types of these objects must already exist in the target namespace.

  • From definitions of the CIM objects in a MOF string or a MOF file, using the compile_mof_string() or compile_mof_file() methods.

    The CIM objects defined in the MOF are added to or updated in a target namespace of the CIM repository. Dependent classes and qualifier types of these objects must already exist in the target namespace.

  • From CIM class names and a schema search path containing the MOF files of one or more schemas, using the compile_schema_classes() method.

    The schema MOF files can either be provided by the user, or the DMTF CIM schema can be automatically downloaded from the DMTF using the DMTFCIMSchema() class.

    The specified CIM classes are added to or updated in a target namespace of the CIM repository, and their dependent classes and qualifier types are added to the target namespace from the schemas in the search path as needed.

    The dependent classes and qualifier types are determined automatically and recursively. This includes superclasses, reference classes (used in reference properties and reference parameters), and embedded classes (i.e. classes referenced through the EmbeddedInstance qualifier). Thus, a user building a CIM repository does not have to track down those dependent classes and qualifier types, and instead only needs to know the schema(s) to be used and the creation classes for any CIM instances. This also means that there is normally no reason to compile the complete schema which is much larger than the classes that are minimally needed.

It may take a combination of all of the above methods to build a CIM repository that satisfies a particular usage requirement. A typical approach for building a CIM repository is:

  1. Establish the MOF subtrees for the schema(s) needed. That can be a downloaded version of the DMTF CIM schema, local modifications to a version of the DMTF CIM schema, user-defined schemas including schemas derived from the DMTF CIM schema, or a combination thereof.

  2. Create the CIM namespaces needed, either as the default namespace or by adding namespaces, including the interop namespace.

  3. For each CIM namespace needed, create the set of needed CIM classes and qualifier types by using the compile_schema_classes() method and specifying the set of creation classes of the CIM instances that are intended to be added, and specifying the pragma MOF files of the schema(s) added in step 1 as a schema search path.

  4. For each CIM namespace needed, create the set of needed CIM instances by defining and compiling instance MOF, or by creating and adding CIMInstance objects, or both. Often defining MOF is easier for this because it simplifies the definition of association instances with the instance alias.

  5. Register user-defined providers such as the CIMNamespaceProvider or user-written providers for the creation classes of the CIM instances that have non-default instance write behavior or that need CIM methods to be supported. See User-defined providers for details.

9.4.1. Example: Set up qualifier types and classes in DMTF CIM schema

This example creates a mock WBEM server using root/interop as the default namespace and compiles the classes defined in the list ‘classes’ from DMTF schema version 2.49.0 into the CIM repository along with all of the qualifier types defined by the DMTF CIM schema and any dependent classes (superclasses, etc.).

import pywbem
import pywbem_mock

conn = pywbem_mock.FakedWBEMConnection(default_namespace='root/interop')

# Leaf classes that are to be compiled along with their dependent classes
leaf_classes = ['CIM_RegisteredProfile',
                'CIM_Namespace',
                'CIM_ObjectManager',
                'CIM_ElementConformsToProfile',
                'CIM_ReferencedProfile']

# Download DMTF CIM schema version 2.49.0 into directory my_schema_dir.
schema = DMTFCIMSchema((2, 49, 0), "my_schema_dir", leaf_classes,
                       verbose=True)

# Compile the leaf classes, looking up dependent classes and qualifier
# types from the downloaded DMTF CIM schema.
conn.compile_schema_classes(leaf_classes, schema.schema_pragma_file
                            verbose=True)

# Display the resulting repository
conn.display_repository()

9.4.2. Example: Set up qualifier types and classes from MOF

This example creates a mock WBEM server and sets up its CIM repository with qualifier types and classes that are defined in a MOF string.

import pywbem
import pywbem_mock

tst_namespace = 'root/blah'
conn = pywbem_mock.FakedWBEMConnection()

# Add some qualifier types and classes to the CIM repo by compiling MOF
mof = '''
    Qualifier Key : boolean = false,
        Scope(property, reference),
        Flavor(DisableOverride, ToSubclass);

    Qualifier Association : boolean,
        Scope(class),
        Flavor(DisableOverride, ToSubclass);

    class TST_Class1 {
          [Key]
        string InstanceID;
        string Prop1;
    };

    class TST_Class2 {
          [Key]
        string InstanceID;
        string Prop2;
    };

      [Association]
    class TST_Association12 {
          [Key]
        TST_Class1 REF Ref1;
          [Key]
        TST_Class2 REF Ref2;
    };
'''
conn.compile_mof_string(mof, tst_namespace)

conn.display_repository()

Here is the output from displaying the CIM repository in the example above:

# ========Mock Repo Display fmt=mof namespaces=all =========


# NAMESPACE root/blah

# Namespace root/blah: contains 2 Qualifier Declarations

Qualifier Association : boolean,
    Scope(class),
    Flavor(DisableOverride, ToSubclass);

Qualifier Key : boolean = false,
    Scope(property, reference),
    Flavor(DisableOverride, ToSubclass);

# Namespace root/blah: contains 3 Classes

   [Association ( true )]
class TST_Association12 {

      [Key ( true )]
   TST_Class1 REF Ref1;

      [Key ( true )]
   TST_Class2 REF Ref2;

};

class TST_Class1 {

      [Key ( true )]
   string InstanceID;

   string Prop1;

};

class TST_Class2 {

      [Key ( true )]
   string InstanceID;

   string Prop2;

};

============End Repository=================

9.4.3. Example: Set up instances from single CIM objects

Based on the CIM repository content of the previous example, this example adds two class instances and one association instance from their CIM objects using the add_cimobjects() method.

c1_key = pywbem.CIMProperty('InstanceID', type='string', value='111')
c1_path = pywbem.CIMInstanceName(
    'TST_Class1',
    keybindings={c1_key.name: c1_key.value},
)
c1 = pywbem.CIMInstance(
    'TST_Class1',
    properties=[
        c1_key,
        pywbem.CIMProperty('Prop1', type='string', value='1'),
    ],
    path=c1_path,
)

c2_key = pywbem.CIMProperty('InstanceID', type='string', value='222')
c2_path = pywbem.CIMInstanceName(
    'TST_Class2',
    keybindings={c2_key.name: c2_key.value},
)
c2 = pywbem.CIMInstance(
    'TST_Class2',
    properties=[
        c2_key,
        pywbem.CIMProperty('Prop2', type='string', value='2'),
    ],
    path=c2_path,
)

a12_key1 = pywbem.CIMProperty('Ref1', type='reference', value=c1_path)
a12_key2 = pywbem.CIMProperty('Ref2', type='reference', value=c2_path)
a12_path = pywbem.CIMInstanceName(
    'TST_Association12',
    keybindings={
        a12_key1.name: a12_key1.value,
        a12_key2.name: a12_key2.value,
    },
)
a12 = pywbem.CIMInstance(
    'TST_Association12',
    properties=[
        a12_key1,
        a12_key2,
    ],
    path=a12_path,
)

conn.add_cimobjects([c1, c2, a12], tst_namespace)

conn.display_repository()

This adds the instances to the repository display of the previous example:

# Namespace root/blah: contains 3 Instances

#  Path=/root/blah:TST_Class1.InstanceID="111"
instance of TST_Class1 {
   InstanceID = "111";
   Prop1 = "1";
};

#  Path=/root/blah:TST_Class2.InstanceID="222"
instance of TST_Class2 {
   InstanceID = "222";
   Prop2 = "2";
};

#  Path=/root/blah:TST_Association12.Ref1="/:TST_Class1.InstanceID=\"111\"",Ref2="/:TST_Class2.InstanceID=\"222\""
instance of TST_Association12 {
   Ref1 = "/:TST_Class1.InstanceID=\"111\"";
   Ref2 = "/:TST_Class2.InstanceID=\"222\"";
};

9.4.4. DMTF CIM schema download support

9.4.5. In-memory CIM repository classes

9.5. Mocking multiple CIM namespaces

The mock WBEM server allows creating multiple CIM namespaces in its CIM repository and adding CIM objects in some or all of those namespaces.

There is a default namespace created when the FakedWBEMConnection object is created from either the value of the default_namespace init parameter or its default value root/cimv2.

However, a working WBEM environment normally includes at least two namespaces:

  1. An interop namespace which contains the parts of the model that deal with the WBEM server itself and with the implemented model (ex. CIM_Namespace, CIM_ObjectManager, CIM_RegisteredProfile, etc.) and which must be publically discoverable without the user knowing what CIM namespaces exist in the WBEM server.

  2. A user namespace containing the CIM objects for the user model.

Pywbem_mock includes a user-defined provider for the CIM_Namespace class that can be enabled by adding code similar to the following to the setup of a mock environment:

conn = pywbem_mock.FakedWBEMConnection(...)

. . .

interop_ns = "interop"   # or "root/interop"
conn.add_namespace(interop_ns)
ns_provider = pywbem_mock.CIMNamespaceProvider(conn.cimrepository)
conn.register_provider(ns_provider, namespaces=interop_ns)

9.6. User-defined providers

Within the mock WBEM server, the response to client requests is normally determined by provider methods that use data in the CIM repository to generate responses (ex. GetInstance gets the instances from the repository, possibly filters properties and returns the instance). However, it is expected that the user may want WBEM operations on specific CIM classes to have side effects, or manage the returns differently than the normal responses.

Thus, for example, the DMTF-defined CIM_Namespace class represents the namespaces in the CIM repository. That means that its provider method for CreateInstance must create a namespace as a side effect, and that its provider methods for GetInstance or EnumerateInstances must inspect the set of namespaces in the CIM repository to generate responses, etc.

Another example of user-defined providers are classes that create their key property values automatically, for example classes that have a single InstanceID key. The provider method for CreateInstance of such classes would ignore the values for the key properties provided in the NewInstance parameter, and determine the key values on its own.

Also, the InvokeMethod operation depends on a specific provider method for the invoked CIM method in the mock WBEM server.

To meet these needs, the capability is provided to users to write user-defined providers that replace the default providers for defined classes and operations.

User-defined providers are written by implementing subclasses of specific provider classes and registering these subclasses as providers using register_provider().

The WBEM operations supported by user-defined providers can be implemented one by one. If a user-defined provider does not implement all WBEM operations, the default implementation will be used.

The following table shows the WBEM operations for which user-defined providers are supported, and the corresponding provider types:

WBEM operation

Provider type

Superclass

Provider description

CreateInstance

instance write

InstanceWriteProvider

User-defined instance write providers

ModifyInstance

instance write

InstanceWriteProvider

User-defined instance write providers

DeleteInstance

instance write

InstanceWriteProvider

User-defined instance write providers

InvokeMethod

method

MethodProvider

User-defined method providers

9.6.1. Creating user-defined providers

A user-defined provider can be created as follows:

  1. Define a subclass of the superclass listed in the table above, with the methods and attributes listed below.

    Example for an instance write provider:

    from pywbem_mock import InstanceWriteProvider
    
    class MyInstanceProvider(InstanceWriteProvider):
    
        . . .
    
    1. Optional: It may have an __init__() method.

      The __init__() method takes as input parameters at least the cimrepository parameter and passes it to the superclass, and may have additional init parameters the user-defined provider requires (that are not passed on to the superclass). Additional init parameters are possible because the user creates the provider object when registering it with register_provider(). Having an __init__() method is optional if no additional init parameters are defined.

      Example for an __init__() method that does not define additional init parameters (and that could therefore be omitted):

      def __init__(self, cimrepository):
          super(MyInstanceWriteProvider, self).__init__(cimrepository)
      
    2. It must have a declaration of the CIM class(es) served by the provider.

      The CIM class or classes served by the provider are declared with a class attribute named provider_classnames. Its value must be a single string or a list/tuple of strings with the CIM class names (in any lexical case).

      provider_classnames = 'CIM_Foo'
      

      or

      provider_classnames = ['CIM_Foo', 'CIM_Foo_blah']
      
    3. It must have an implementation of the Python methods for the WBEM operations that are overwritten by the provider.

      This must be all or a subset of the WBEM operations defined for the provider type. WBEM operations not implemented in the user-defined provider class default to implementations in the superclass. See Python operation methods in user-defined providers for details.

      Example for an CreateInstance method of an instance write provider that just calls superclass method to perform the work (and that could therefore be omitted):

      def CreateInstance(self, namespace, NewInstance):
          return super(MyInstanceWriteProvider, self).CreateInstance(
              namespace, NewInstance)
      
    4. Optional: It may define a post register setup method.

      The provider may override the post_register_setup() method of the provider superclass to do any special setup it needs. That method includes the current connection as a parameter so that WBEM operations on the same or on different classes can be executed. That method will be called during invocation of register_provider(), after the provider registration is successful.

      Example:

      def post_register_setup(self, conn):
        # code that performs post registration setup for the provider
      
  2. Register the user-defined provider using pywbem_mock.FakedWBEMConnection.register_provider().

    This specifies the CIM namespaces for which the user-defined provider will be active. These namespaces must already exist in the CIM repository if the mock WBEM server.

    provider = MyInstanceProvider(self.cimrepository)
    conn.register_provider(provider,
                           namespaces=['root/interop', 'root/cimv2'])
    

9.6.2. Python operation methods in user-defined providers

The Python operation methods (i.e. the Python methods implementing WBEM operations) in user-defined providers may:

  • provide parameter or CIM repository validation in addition to the normal request validation,

  • modify parameters of the request,

  • abort the request with a pywbem.CIMError exception,

  • make modifications to the CIM repository.

The Python operation methods may call the corresponding superclass method to complete the CIM repository modification, or may implement the code to complete the modification. In any case, once a Python operation method returns, the CIM repository needs to reflect any changes on CIM objects the WBEM operation is normally expected to perform.

The Python operation methods have access to:

  • methods defined in the superclass of the provider, including :class:~pywbem_mock.BaseProvider`

  • methods to access the CIM repository using the methods defined in InMemoryRepository

The input parameters for the Python operation methods will have been already validated, including:

  • The input parameters have the correct Python type as per the descriptions in the superclasses.

  • The CIM namespace exists in the CIM repository.

  • The CIM class of a CIM instance or instance path specified as an input parameter exists in that namespace of the CIM repository.

  • The CIM properties of a CIM instance specified as an input parameter are defined in the CIM class of the instance and have the correct CIM types.

  • The CIM instance does not yet exist for CreateInstance and does exist for ModifyInstance.

The Python operation methods should raise any exceptions using pywbem.CIMError using the CIM status codes defined in DSP0200.

9.6.3. User-defined instance write providers

9.6.4. User-defined method providers

9.6.5. CIM_Namespace provider

9.7. Registry for provider dependent files

A faked WBEM connection provides a registry for provider dependent files in its provider_dependent_registry property of type pywbem_mock.ProviderDependentRegistry.

This registry can be used by callers to register and look up the path names of additional dependent files of a mock script, in context of that mock script.

The pywbemtools project makes use of this registry for validating whether its mock cache is up to date w.r.t. additional dependent files a mock script has used.

9.8. Configuration of mocked behavior

Pywbem_mock supports several variables that provide configuration and behavior control of the mock environment.

These configuration variables can be modified by the user directly after importing pywbem. For example:

import pywbem_mock
pywbem_mock.config.SYSTEMNAME = 'MyTestSystemName'

Note that the pywbem source file defining these variables should not be changed by the user. Instead, the technique shown in the example above should be used to modify the configuration variables.

pywbem_mock.config.DEFAULT_MAX_OBJECT_COUNT = 100

Default value for the MaxObjectCount parameter of the Open…() methods of the mock WBEM server, if the value was not provided by the user.

pywbem_mock.config.IGNORE_INSTANCE_ICO_PARAM = True

Use of the IncludeClassOrigin parameter on instance requests is DEPRECATED. A WBEM server may choose to treat the value of IncludeClassOrigin parameter as false for all requests, otherwise the implementation shall support the original behavior as defined in the rest of this paragraph. If the IncludeClassOrigin input parameter is true, the CLASSORIGIN attribute shall be present on all appropriate elements in each returned Instance. If it is false, no CLASSORIGIN attributes are present.

The following variable forces pywbem_mock to ignore the client supplied variable and not return qualifiers on EnumerateInstances and GetInstance responses.

  • True (default): pywbem_mock always removes class origin attributes from instances in responses

  • False: pywbem_mock uses value of input parameter or its default to determine if class origin attributes are to be removed

pywbem_mock.config.IGNORE_INSTANCE_IQ_PARAM = True

Use of the IncludeQualifiers parameter on instance requests is DEPRECATED in DMTF DSP0200. The definition of IncludeQualifiers is ambiguous and when this parameter is set to true, WBEM clients cannot be assured that any qualifiers will be returned. A WBEM client should always set this parameter to false. To minimize the impact of this recommendation on WBEM clients, a WBEM server may choose to treat the value of IncludeQualifiers as false for all requests.

The following variable forces pywbem_mock to ignore the client supplied variable and not return qualifiers on EnumerateInstances and GetInstance responses.

  • True (default): pywbem_mock always removes qualifiers from instances in responses

  • False: pywbem_mock uses value of input parameter or its default to determine if qualifiers are to be removed

pywbem_mock.config.OBJECTMANAGERCREATIONCLASSNAME = 'CIM_ObjectManager'

Value for the CIM_ObjectManagerCreationClassName defined in the CIM_ObjectManager class.

pywbem_mock.config.OBJECTMANAGERNAME = 'FakeObjectManager'

Name for the object manager It is used in defining user-defined provider properties for CIM_Namespace provider and CIM_ObjectManager provider if if they are defined.

pywbem_mock.config.OPEN_MAX_TIMEOUT = 40

Maximum value for the OperationTimeout parameter of the Open…() methods of the mock WBEM server.

pywbem_mock.config.SYSTEMCREATIONCLASSNAME = 'CIM_ComputerSystem'

Name for the property SystemCreationClassname defined a number of CIM classes that are used by the pywbem_mock including CIM_Namespace

pywbem_mock.config.SYSTEMNAME = 'MockSystem_WBEMServerTest'

The name of the mock object. This string value becomes part of the CIM_ObjectNamager instance and CIM_Namespace instances