Novell Home

AppNote: Modeling and Writing a Common Information Model (CIM) Provider

Novell Cool Solutions: AppNote
By Ramandeep Kaur

Digg This - Slashdot This

Posted: 10 May 2005
 

Ramandeep Kaur
Senior Software Engineer
rkaur@novell.com

This How-to AppNote discusses CIM and also explains the approach to be taken to develop a CIM Provider.

Contents
Introduction
WBEM Architecture
CIM Management Schema
CIM Elements
Modeling a CIM Provider
Developing and Testing a CIM Provider
Conclusion
Topics CIM Introduction, modeling and writing a CIM Provider
Products OES 1.0, SLES 9.0
Audience Developers
Level Intermediate

Introduction

Distributed enterprise computing has drastically changed the way users work. It allows people to access various types of data anytime and anywhere. The persistent problem is for managing the same as various industry standards viz. SNMP , DMI are prevalent. The solution to this problem is to use a technology which can provide optimal management for most of the entities in the enterprise network and can co exist with the existing technologies.

Common Information Model (CIM) is an information model driven by Web Based Enterprise Management (WBEM). WBEM is an initiative spearheaded by the Distributed Management Task Force (DMTF) to develop a core set of standard technologies for managing enterprise computing environments. CIM provides a unified method for managing an enterprise network without requiring an overhaul of the existing network management infrastructure.

Open Enterprise Server (OES) from Novell® provides management for various services running on the NetWare and Linux Servers through CIM. This includes the Health Monitoring Service, Samba etc. All these services are managed by writing a provider for each of them which interacts with the managed resource. For example:
The Samba provider interfaces with the samba configuration file to facilitate its management through CIM.

This article covers the following topics:

  • Basics of CIM/ WBEM
  • Art of modeling and writing the CIM providers

WBEM Architecture

Let us first acclimatize with WBEM, its components and architecture. The main components of WBEM are the WBEM Server or CIM Object Manager (CIMOM), the WBEM Providers, and WBEM clients. As shown in the figure below, a WBEM Provider can be seen as the interface between the managed resource which can be a file, a device , a system etc. and the CIMOM while a WBEM Client can be seen as the interface between the manager and the CIMOM.

The WBEM Client queries (add/modify/delete requests) the CIMOM for the managed resource and is unaware of the very existence of the provider. The CIMOM is responsible for calling the correct provider(s) in response to the client?s requests which then processes the incoming request and returns the response to the CIMOM. The CIMOM then passes on the response to the WBEM client.


Fig: WBEM Architecture

CIM Management Schema

In Order to model a CIM Provider, the knowledge of CIM Schema is required. CIM Schema defines a set of classes with properties and associations that provide a framework within which it is possible to organize the available information about the managed environment. CIM is structured into three layers:

  1. Core Model: This contains concepts applicable to management of anything which can be managed from a small device to a very big network. It classifies basic elements like and the associations of any managed environment. The CIM Schema's class hierarchy begins with the Managed Element class which is abstract and the Managed System Element, the Product related classes, Setting and Configuration, Collection and the Statistical Information classes are derived from it. More information about these classes can be found at www.dmtf.org/education.

  2. Common Model: This is an information model that captures notions that are common to particular management areas, but independent of a particular technology or implementation.

  3. Extension Schemas: This represents the technology specific extensions of the common models.

CIM Elements

A CIM model can be expressed either graphically using the Unified Modeling Language (UML) or textually using Management Object Format (MOF). CIM includes expressions for common elements like classes, objects, methods, attributes, qualifiers (meta-data) and associations etc. that must be clearly presented to management applications in order to model the CIM provider.

Let us now discuss the UML representation for some of the basic CIM elements:

  1. Class: A class is a collection of instances all of which support a common type, that is, a set of properties and methods. It is a basic unit of management. The figure below shows the general structure of a class in UML which is a rectangle divided into three parts wherein the top most division contains the Class name, the middle subdivision contains the set of properties which the last subdivision lists the methods that the class supports.

  2. Property: It has a data type and a value used to denote a characteristic of a class. Some properties are designated as Keys, which means that they are required to be unique across all the instances of the class.

  3. Method: It is an action that denotes the behaviour of the class.


  4. Fig: UML representation of a Class

  5. Association: An association is a mechanism for providing an explicit mapping between classes. The differences between association classes and other classes are that they have ASSOCIATION qualifier (Meta data) and that they are allowed to have reference properties. Reference properties are properties that refer to other CIM classes. Although association is a class, still it is not represented using the rectangular box shown in the figure above. An association is represented by a line between the classes it associates. The star at the ends of the association indicated that any number from 0 upwards may be the part of the association.

Fig: UML Representation of an association

A Visual model can be written textually in the form of Managed Object Format (mof). Mof is the textual language to describe CIM models. All CIM components, such as classes, properties, methods, associations and instances, can be specified in the MOF syntax.

  1. Class

class ClassName
{
	data type PropertyName;
	data type MethodName();
};

Here we have a class with name ClassName, a property, PropertyName, and a method, MethodName.
  1. Instance
instance of ClassName {
	PropertyName=value;
};

It is not possible to implement methods in Mof. It is up to the WBEM providers to implement the methods.

  1. Qualifiers are meta-data i.e they describe the data about the class. Qualifiers can be put before class, property and method declarations. They are enclosed with square brackets ("["and "]") and each class, property or method can have several qualifiers separated by commas (",").
[Description ("This class is an example")]
class ClassName

{
	[Key, Minvalue(5), Maxvalue(10)]
	 data type PropertyName;
	[Description ("This method is an example")]
	data type MethodName();
};


Here we use the four different qualifiers DESCRIPTION, KEY, MINVALUE and MAXVALUE. These qualifiers are standard CIM qualifiers. It is also possible to declare new qualifiers if needed.

CIM Interfaces:

In the WBEM architecture, the WBEM listeners and the WBEM clients are not aware of the existence of the providers and they have no knowledge about the manner in which the data is requested and retrieved from the managed resource.

The two kinds of interfaces which have the WBEM server at one end are:

  1. Client/Server Interface: This encapsulates the user's requests and passes them to the WBEM server for the action. This is shown in the figure below.


Fig: Client-Server Interface

  1. Listener Interface: This receives information about the indications that have occurred in the managed resource. Here the WBEM Listener may or may not have any understanding of CIM.

Modeling a CIM provider

A provider model can never be right or wrong. Each designer can model a managed resource in his/her own way. In this section, we will discuss the various approaches for modeling. To make modeling interesting, let us take an example of a configuration file to be instrumented by a user. A bare minimum implementation of any configuration file will have few settings and their corresponding values.

These settings can be just a simple name-value pairs. For example:

name= value
    or 
<name value>

They can even be nested within one another. For example:

<name1 value1>
	<name2 value2>
	</name2 value2>
</name1 value1>

Having acquired the knowledge about a generic configuration file, let us go through the steps involved in modeling a simple configuration file like /etc/hosts which is present on any Linux system. This file describes the number of hostname to address mappings. It has settings as follows:

# /etc/hosts

#IP-Address		Full-Qualified-Hostname
127.0.0.1		localhost

<a.b.c.d>		www.demo-one.com
<e.f.g.h>		www.demo.two.com

Where <a.b.c.d> & <e.f.g.h> are IP addresses

Steps to be followed while modeling a provider:

  1. Naming the extension schema: As the schema name must be unique, let us name the schema as "Novell_Hosts".

  2. Identify classes: Now that, we are dealing with a configuration file which contains the settings i.e. name-value pairs, let's look into CIM classes that depict the name-value pairs in the CIM v2.8 Schema. Settings and Configuration related classes are defined in Core28_Settings.mof. The first and the second class in this file are CIM_SettingData and CIM_Setting respectively and either of them can be used to depict the name of the settings. Extension of the schema is done to include the value of the setting in the derived class as shown in the mof in Step 5.
    To include scalability in the design, let's us consider, if these simple name-value pairs are nested within another name-value pair. The description of CIM_Configuration class fits the scenario of grouping individual settings in a configuration. In the /etc/hosts file, as we do not have any nesting of settings, to illustrate the same we shall consider a logical anchor which holds all the individual settings. This logical anchoring class is CIM_Configuration.
    The identification process gets simplified by reading the description of various CIM schema classes. For example: if we have to instrument a process running on a system, we will take CIM_Process class into consideration. The documentation of the CIM Schema classes can be found at www.dmtf.org.

  3. Identify Keys: A Key is indicated by the presence of "Key" qualifier. Key acts as an identifier for an instance of a class. Therefore, it has to be unique. Each class has some properties (attributes) and references which can be used as keys.

  4. Identify associations, class hierarchies, and additional semantics for the classes in the design:
    Let us try to find an association class which has references to both CIM_Setting and
    CIM_Configuration objects. In Core_Settings28.mof, we find an association class
    CIM_SettingContext which satisfies the requirement. This class clearly states that the
    CIM_Configuration object is an aggregate of CIM_Settings object.

Fig: Visual Model for /etc/hosts provider
    Here CIM_Configuration was extended to form Novell_HostsConfiguration. We then derived CIM_Setting to form Novell_HostsSetting and finally to have an association between the extension schema we form Novell_HostsSettingContext which is derived from CIM_SettingContext.

  1. Now that the visual modeling is through, let us take a step forward into the textual modeling of the provider. A mof file has to be written for the same.
            // This pragma specifies the language and the country of the system
             #pragma locale ("en_US")

            // Namespace is a boundary within which the classes and their keys should be unique
            #pragma namespace ("root/cimv2")

            [Description ("Class representing logical configuration which groups individual settings of /etc/hosts file.")]
	class Novell_HostsConfiguration : CIM_Configuration
	{
	[ Description ("Key Value"), key ]
	string Name = "etc_hosts";
	[Description ("Dummy method to demonstrate user defined functions")]
	string getTime();
	};

	[Description ("Class representing the settings and their values of /etc/hosts")]
	class Novell_HostsSetting : CIM_Setting
	{
	[Description ("Name of the setting representing IPAddress"), key ]
	string IPAddress;
		
	[Description ("Fully qualified HostName"), required ]
	string FullHostname;	
	
	};

	[Association]
	class Novell_HostsSettingContext : CIM_SettingContext
	{
	[Override("Context"), Aggregate, key ]
	Novell_HostsConfiguration REF Context;

	[Override("Setting"), key ]
	Novell_HostsSetting REF Setting;
	};

Save the above file as Novell_hosts.mof.

Amongst the various available WBEM implementation, we have chosen OpenWbem to demonstrate the development of the provider. OpenWbem package can be downloaded from www.openwbem.org/#Download. Build the package and then start the OpenWbem daemon called owcimomd.

The CIMOM has to be made aware of the existence of the extension schema. In order to facilitate this, the Novell_hosts.mof file needs to be compiled/imported into the CIMOM. OpenWbem provides command line utilities for manipulating namespaces, compiling the mof etc. Follow the given steps in order to import the mof file.

  1. Create the "/root/cimv2" by executing owcreatenamespace utility with the URL as http://localhost and namespace as "/root/cimv2".

  2. Compile the base CIM Schema which can be found in openwbem/schemas/cim28 directory. Compilation of the CIM 2.8 schema is done by executing owmofc CIM_Schema28.mof. With this the CIMOM is aware of the classes in CIM 2.8 Schema.

  3. As shown in Step 2, compile Novell_hosts.mof.

  4. A CIM browser is used to browse the Base CIM Schema classes and the extension classes. For downloading SNIA CIM browser, go to http://www.novell.com/coolsolutions/cooldev/assets/cimbrowser.tar.gz.

  5. On running the cimbrowser.sh, the following user interface is visible. Select the root/cimv2 namespace to browse through all the CIM 2.8 and extension classes.

Developing and Testing CIM Providers

A WBEM provider is an entity which interfaces with the managed resource and facilitates the instrumentation of the same. WBEM server supports many types of providers viz method provider, instance provider, associator provider, property provider etc. For our purpose, we need to know about the following types of providers as they will be implemented in the provider code:

  1. Method Provider: We need to implement this type of provider as it handles calls to the extrinsic methods (user defined methods). The getTime() method of Novell_HostsConfiguration class is an extrinsic method which will be implemented in the provider.

  2. Instance Provider: We need to manipulate (add/modify/delete) setting name-value pairs of the /etc/hosts file. Each of name-value pairs is depicted by a Novell_HostsSetting object. Therefore, NovellHostsProvider should implement an instance provider as it handles the enumeration, creation, modification and deletion of the instances of a particular class. Intrinsic methods (which are built into the WBEM server and manipulate the model) such as enumerateInstances (), enumerateInstanceNames(), createInstance(), modifyInstance() and deleteInstance() are implemented by the instance provider.

  3. Association Provider: Novell_HostsConfiguration and Novell_HostsSetting classes as associated by a Novell_HostsSettingConfext class. In order to manipulate this association class, an association provider is implemented, which handles the creation, deletion of associations between different classes. An associator provider implements intrinsic methods such as associators(), associatorNames(), references() and referenceNames().

Having gained the knowledge about the types of providers to be implemented, let us go through the steps involved in developing and testing the provider.

  1. Implementing intrinsic methods for the method, instance, and association provider.

  2. Once the Provider Code is written, a factory function has to be provided which the CIMOM uses to load the provider. The following macro is provided for the same.

    OW_PROVIDERFACTORY (NovellHostsProvider, novellhostsprovider);

    The first argument is the C++ class name. The second argument needs to correspond to the name of the shared library (.so) which in this case is libnovellhostsprovider.so.

  3. The C++ class representing the provider will need to be compiled into a shared library (.so). The shared object (.so) file has to be put in the directory specified by the cppprovifc.prov location configuration item in openwbem.conf.

  4. The provider is now fully functional and can be tested with a CIM browser.

Writing the Novell_HostsProvider

Let us now go through the C++ code for intrinsic methods of the Novell_HostsProvider. The definitions of all these methods are given in the developer documentation at www.openwbem.org. As we are implementing Instance, Method and Associator provider, intrinsic methods of CppInstanceProviderIFC class, CppMethodProviderIFC class and CppAssocitorProviderIFC class respectively needs to be implemented.

  1. enumInstanceNames: This operation is used to enumerate the names of the instances of a CIM Class in the given Namespace.

virtual void enumInstanceNames( const ProviderEnvironmentIFCRef& env,
		const String& ns, const String& className,
		CIMObjectPathResultHandlerIFC& result,
		const CIMClass& cimClass)
{
	/* Comparing class names with the classes defined in Novell_hosts.mof */
 
	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		/* Create the CIMObjectPath and set the Name Property */
		CIMObjectPath cop("Novell_HostsConfiguration", ns);
		cop.setKeyValue("Name", CIMValue(String("etchosts")));
		result.handle(cop);
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		/* There are more settings, therefore iterate through all of them
		* and set the IPAddress property */
		CIMObjectPath cop("Novell_HostsSetting", ns);
		
		/* getSettings is parser method which returns the settings
		*  and their values from /etc/hosts file */ 
		SettingsMap sm = getSettings();
		SettingsMap::iterator smit = sm.begin();
		while(smit != sm.end())
		{
			cop.setKeyValue("IPaddress", CIMValue(smit->first));
			result.handle(cop);
			smit++;
		}
	}		
}

To test this method, select either Novell_HostsSetting or Novell_HostsConfiguration and then select the Show option from the Instances menu, one can see the Instances of the respective class as shown below.

  1. enumInstances: This operation is used to enumerate instances ( all properties) of a CIM Class in the target Namespace.
virtual void enumInstances( const ProviderEnvironmentIFCRef& env,
	const String& ns, const String& className, 						CIMInstanceResultHandlerIFC& result, ELocalOnlyFlag localOnly, 
	EDeepFlag deep, EIncludeQualifiersFlag includeQualifiers, 
	EIncludeClassOriginFlag includeClassOrigin,
	const StringArray* propertyList, const CIMClass& requestedClass,
	const CIMClass& cimClass )
{
	/* Comparing class names with the classes defined in Novell_hosts.mof */

 	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		/* Create the CIMInstance and set the "Name" Property */
		CIMInstance ci = cimClass.newInstance();
		ci.setProperty("Name", CIMValue(String("etchosts")));
		result.handle(ci.clone(localOnly, includeQualifiers, includeClassOrigin,
				propertyList));
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		/* There are more settings, therefore iterate through all of them
		* and set the IPAddress and FullHostName property in the newly
		* created CIMInstance */
		CIMInstance ci = cimClass.newInstance();
		SettingsMap sm = getSettings();
		SettingsMap::iterator smit = sm.begin();
		while(smit != sm.end())
		{
			ci.setProperty("IPAddress", CIMValue(smit->first));
			ci.setProperty("FullHostName", CIMValue(smit->second));
			result.handle(ci.clone(localOnly, includeQualifiers, 						includeClassOrigin,propertyList));
			smit++;
		}
	}	
}

Testing of this operation can be done by running the owexecwql utility which comes along with the openWbem package.

  1. getInstance: This operation is used to return a single CIM Instance from the target Namespace.
virtual CIMInstance getInstance( const ProviderEnvironmentIFCRef& env,
	const String& ns, const CIMObjectPath& instanceName,
	ELocalOnlyFlag localOnly, EIncludeQualifiersFlag includeQualifiers, 
	EIncludeClassOriginFlag includeClassOrigin, const StringArray* propertyList, 
	const CIMClass& cimClass )
{
	String className = cimClass.getName();
	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		/* getStringKey is a parser method which retrieves the
		* the key/property from the CIMObjectPath 
		* which in this case is "Name" property*/
		String name = getStringKey(instanceName, "Name");
		if(name != "etchosts")
		{
			OW_THROWCIMMSG(CIMException::NOT_FOUND,
				("Object not found: %1",
					instanceName.toString()).c_str());
		}
		CIMInstance ci = cimClass.newInstance();
		ci.setProperty("Name", CIMValue(String("etchosts")));
		return ci.clone(localOnly, includeQualifiers, includeClassOrigin,
			propertyList);
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		String key = getStringKey(instanceName, "IPAddress");
		if(key.length() == 0)
		{
			OW_THROWCIMMSG									(CIMException::INVALID_PARAMETER,
				("Invalid Object path: %1",
						instanceName.toString()).c_str());
		}

		/* getStringValue is a parser method which retrieves the value of 				 * the  key/property */
		String value = getSettingValue(key);
		if(value.length() == 0)
		{
			OW_THROWCIMMSG(CIMException::NOT_FOUND,
			("Object not found: %1",
						instanceName.toString()).c_str());
		}
		/* Create a new Instance and set the IPAddress and FullHostName 			 * properties and return the clone of this instance */	
		CIMInstance ci = cimClass.newInstance();
		ci.setProperty("IPAddress", CIMValue(key));
		ci.setProperty("FullHostName", CIMValue(value));
		return ci.clone(localOnly, includeQualifiers, includeClassOrigin,
				propertyList);
	}
	else
	{
		OW_THROWCIMMSG(CIMException::NOT_FOUND,
		("Object not found: %1",
		instanceName.toString()).c_str());
	}
	return CIMInstance(CIMNULL);		
}

To test this operation, select one of the instances of a given class in the CIMBrowser. One can find the values of all the properties on the right hand side as shown below.

  1. createInstance: This operation is used to create a single CIM Instance in the target Namespace. The instance must not already exist.
virtual CIMObjectPath createInstance( const ProviderEnvironmentIFCRef& env,
	const String& ns, const CIMInstance& cimInstance )
{
	String className = cimInstance.getClassName();
	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		OW_THROWCIMMSG(CIMException::NOT_SUPPORTED,
				"Instances of this class cant not be created");
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		/* getStringProp is a parser method used to check the existence of a 			 * property in a given CIMInstance */
		String name = getStringProp(cimInstance, "IPAddress", true);
		String value = getSettingValue(name);
		if(value.length() != 0)
		{
			OW_THROWCIMMSG(CIMException::ALREADY_EXISTS,
					("Object already exists: %1", name).c_str());
		}
		value = getStringProp(cimInstance, "FullHostName", true);
		/* setSetting is a parser method to set(add/modify)
		 *  name value pair in the configuration file */ 
		setSetting(name, value);
		CIMObjectPath cop("Novell_HostsSetting", ns);
		cop.setKeyValue("IPAddress", CIMValue(name));
		return cop;
	}
	else
	{
		OW_THROWCIMMSG(CIMException::FAILED,
			("NovellHostsProvider does not support creation of"
				" %1 objects", className).c_str());
	}	

}

An instance can be created in CIM Browser's Instance Editor by selecting Object->Create New Instance menu which is shown below.

The user needs to fill up the values of the New Instance by selecting the Properties tab in the right hand side and then select Object->SaveInstance as shown below.

  1. modifyInstance: This operation is used to modify an existing CIM Instance in the target Namespace. The instance must already exist.
virtual void modifyInstance( const ProviderEnvironmentIFCRef& env,
	const String& ns, const CIMInstance& modifiedInstance,
	const CIMInstance& previousInstance, EIncludeQualifiersFlag includeQualifiers,
	const StringArray* propertyList, const CIMClass& theClass)
{
	String className = theClass.getName();
	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		OW_THROWCIMMSG(CIMException::NOT_SUPPORTED,
			"Instances of this class can not be modified");
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		CIMInstance mci = modifiedInstance.createModifiedInstance(
			previousInstance, includeQualifiers, propertyList,
			theClass);

		String name = getStringProp(mci, "IPAddress", true);
		String value = getSettingValue(name);
		if(value.length() == 0)
		{
			OW_THROWCIMMSG(CIMException::NOT_FOUND,
				("Object not found for modification: %1",
					name).c_str());
		}
		value = getStringProp(mci, "FullHostName", true);
		if(value.length() == 0)
		{
			OW_THROWCIMMSG										(CIMException::INVALID_PARAMETER, 
					"FullHostName property must be given");
		}
		setSetting(name, value);
	}
	else
	{
		OW_THROWCIMMSG(CIMException::FAILED,
			("NovellHostsProvider does not support modification of"
				" %1 objects", className).c_str());
	}
}

Modification of an instance is done by changing the values of the properties of an instance and then saving that instance.

  1. DeleteInstance: This operation is used to delete a single CIM instance from the target Namespace.
virtual void deleteInstance( const ProviderEnvironmentIFCRef& env,
	const String& ns, const CIMObjectPath& cop)
{
	String className = cop.getClassName();
	if(className.equalsIgnoreCase("Novell_HostsConfiguration"))
	{
		OW_THROWCIMMSG(CIMException::NOT_SUPPORTED,
			"You can't delete instances of this class");
	}
	else if(className.equalsIgnoreCase("Novell_HostsSetting"))
	{
		String key = getStringKey(cop, "IPAddress");
		if(key.length() == 0)
		{
			OW_THROWCIMMSG										(CIMException::INVALID_PARAMETER,
				("Invalid Object path: %1", cop.toString()).c_str());
		}
		String value = getSettingValue(key);
		if(value.length() == 0)
		{
			OW_THROWCIMMSG(CIMException::NOT_FOUND,
				("Object not found: %1",
					cop.toString()).c_str());
		}
		/* deleteSettings is a parser method which deletes the key(IPAddress) from 		* the configuration file */
		deleteSetting(key);
	}
	else
	{
		OW_THROWCIMMSG(CIMException::FAILED,
			("Novell_HostsProvider does not support deletion of"
				" %1 objects", className).c_str());
	}	
}

An instance is deleted by selecting the Object->Delete Instance in the Instance Editor.

  1. Associators: This operation is used to enumerate CIM objects (classes or instances) that are associated to a particular source CIM object.
virtual void associators( const ProviderEnvironmentIFCRef& env,
	CIMInstanceResultHandlerIFC& result, const String& ns,
	const CIMObjectPath& objectName, const String& assocClass,
	const String& resultClass, const String& role,
	const String& resultRole, EIncludeQualifiersFlag includeQualifiers,
	EIncludeClassOriginFlag includeClassOrigin, const StringArray* propertyList)
{
	/* check if the association class is "Novell_HostsSettingContext" */
	if(assocClass.equalsIgnoreCase("Novell_HostsSettingContext"))
	{
		CIMOMHandleIFCRef lch = env->getCIMOMHandle();
		String objClass = objectName.getClassName();
		/* Check if the objectClass is "Novell_HostsConfiguration" */
		if(objClass.equalsIgnoreCase("Novell_HostsConfiguration"))
		{
			/* Return the instances of "Novell_HostsSetting" */
			CIMClass cc = lch->getClass(ns, "Novell_HostsSetting",
				E_NOT_LOCAL_ONLY, E_INCLUDE_QUALIFIERS,
				E_INCLUDE_CLASS_ORIGIN);
			enumInstances(env, ns, "Novell_HostsSetting", result,
				E_NOT_LOCAL_ONLY, E_DEEP, includeQualifiers,
				includeClassOrigin, propertyList, cc, cc);
		}
		/* Check if the objectClass is "Novell_HostsSetting" */
		else if(objClass.equalsIgnoreCase("Novell_HostsSetting"))
		{
			/* Return the instances of "Novell_HostsConfiguration" */
			CIMClass cc = lch->getClass(ns, "Novell_HostsConfiguration",
				E_NOT_LOCAL_ONLY, E_INCLUDE_QUALIFIERS,
				E_INCLUDE_CLASS_ORIGIN);
			enumInstances(env, ns, "Novell_HostsConfiguration", result,
				E_NOT_LOCAL_ONLY, E_DEEP, includeQualifiers,
				includeClassOrigin, propertyList, cc, cc);
		}
	}	
}
  1. AssociatorNames: This operation is used to enumerate the names of CIM objects (classes or instances) that are associated to a particular source CIM object.
virtual void associatorNames( const ProviderEnvironmentIFCRef& env,
	CIMObjectPathResultHandlerIFC& result, const String& ns,
	const CIMObjectPath& objectName, const String& assocClass,
	const String& resultClass, const String& role, const String& resultRole)
{
	/* check if the association class is "Novell_HostsSettingContext" */
	if(assocClass.equalsIgnoreCase("Novell_HostsSettingContext"))
	{
		CIMOMHandleIFCRef lch = env->getCIMOMHandle();
		String objClass = objectName.getClassName();

		/* Check if the objectClass is "Novell_HostsConfiguration" */
		if(objClass.equalsIgnoreCase("Novell_HostsConfiguration"))
		{
			/* Return the names of the instances of "Novell_HostsSetting" */
			CIMClass cc = lch->getClass(ns, "Novell_HostsSetting",
				E_NOT_LOCAL_ONLY, E_INCLUDE_QUALIFIERS,
				E_INCLUDE_CLASS_ORIGIN);
			enumInstanceNames(env, ns, "Novell_HostsSetting", result, cc);
		}
		/* Check if the objectClass is "Novell_HostsSetting" */
		else if(objClass.equalsIgnoreCase("Novell_HostsSetting"))
		{
			/* Return the names of the instances of 							 * "Novell_HostsConfiguration" */
			CIMClass cc = lch->getClass(ns, "Novell_HostsConfiguration",
				E_NOT_LOCAL_ONLY, E_INCLUDE_QUALIFIERS,
				E_INCLUDE_CLASS_ORIGIN);
			enumInstanceNames(env, ns, "Novell_HostsConfiguration", result, 				cc);
		}
	}		
}

Associators for a particular instance can be tested by selecting the "Related objects" tab in the right hand side of the CIM browser.

  1. References: This operation is used to enumerate the association objects that refer to a particular target CIM object (class or instance).
virtual void references( const ProviderEnvironmentIFCRef& env,
	CIMInstanceResultHandlerIFC& result, const String& ns,
	const CIMObjectPath& objectName, const String& resultClass,
	const String& role, EIncludeQualifiersFlag includeQualifiers,
	EIncludeClassOriginFlag includeClassOrigin, const StringArray* propertyList)
{
	/* check if the resultClass (association) is Novell_HostsSettingContext" */
	if(resultClass.equalsIgnoreCase("Novell_HostsSettingContext"))
	{
		CIMOMHandleIFCRef lch = env->getCIMOMHandle();
		CIMClass cc = lch->getClass(ns, "Novell_HostsSettingContext",
			E_NOT_LOCAL_ONLY, E_INCLUDE_QUALIFIERS,
			E_INCLUDE_CLASS_ORIGIN);

		CIMInstance ci = cc.newInstance();
		
		/* Create a COP for Novell_HostsConfiguration and set this as a 		 		* Context property  of the Novell_HostsSettingContext instance */
		CIMObjectPath configOp("Novell_HostsConfiguration", ns);
		configOp.setKeyValue("Name", CIMValue(String("etchosts")));
		ci.setProperty("Context", CIMValue(configOp));
				
		String objClass = objectName.getClassName();
		if(objClass.equalsIgnoreCase("Novell_HostsConfiguration"))
		{
			/* Create a COP for Novell_HostsSetting and set this as a 		 			* Setting property of the Novell_HostsSettingContext instance*/
			CIMObjectPath settingOp("Novell_HostsSetting", ns);
			SettingsMap sm = getSettings();
			SettingsMap::iterator smit = sm.begin();
			while(smit != sm.end())
			{
				settingOp.setKeyValue("IPAddress", CIMValue(smit-					>first));
				ci.setProperty("Setting", CIMValue(settingOp));
				result.handle(ci.clone(E_NOT_LOCAL_ONLY, 						includeQualifiers, includeClassOrigin, propertyList));
				smit++;
			}
		}
		else if(objClass.equalsIgnoreCase("Novell_HostsSetting"))
		{
			String key = getStringKey(objectName, "IPAddess");
			if(key.length() == 0)
			{
				OW_THROWCIMMSG									(CIMException::INVALID_PARAMETER,
				("Invalid Object path: %1", objectName.toString()).c_str());
			}
			ci.setProperty("Setting", CIMValue(objectName));
			result.handle(ci.clone(E_NOT_LOCAL_ONLY, includeQualifiers, 			includeClassOrigin, propertyList));
		}
	}
}
  1. ReferenceNames: This operation is used to enumerate the association objects that refer to a particular target CIM object (class or instance).
virtual void referenceNames( const ProviderEnvironmentIFCRef& env,
	CIMObjectPathResultHandlerIFC& result, const String& ns,
	const CIMObjectPath& objectName, const String& resultClass,
	const String& role)
{
	if(resultClass.equalsIgnoreCase("Novell_HostsSettingContext"))
	{
		CIMObjectPath resultOp("Novell_HostsSettingContext", ns);
		CIMObjectPath configOp("Novell_HostsConfiguration", ns);
		configOp.setKeyValue("Name", CIMValue(String("etchosts")));
		resultOp.setKeyValue("Context", CIMValue(configOp));
			
		String objClass = objectName.getClassName();
		if(objClass.equalsIgnoreCase("Novell_HostsConfiguration"))
		{
			CIMObjectPath settingOp("Novell_HostsSetting", ns);
			SettingsMap sm = getSettings();
			SettingsMap::iterator smit = sm.begin();
			while(smit != sm.end())
			{
				settingOp.setKeyValue("IPAddress", CIMValue(smit->first));
				resultOp.setKeyValue("Setting", CIMValue(settingOp));
				result.handle(resultOp);
				smit++;
			}
		}
		else if(objClass.equalsIgnoreCase("Novell_HostsSetting"))
		{
			String key = getStringKey(objectName, "IPAddress");
			if(key.length() == 0)
			{
				OW_THROWCIMMSG									(CIMException::INVALID_PARAMETER,
				("Invalid Object path: %1", objectName.toString()).c_str());
			}
			resultOp.setKeyValue("Setting", CIMValue(objectName));
			result.handle(resultOp);
		}
	}
}

ReferenceNames can be tested by selecting the Associations tab on the right hand side of the CIM browser.

  1. InvokeMethod: This operation is used to implement the user defined methods defined in the Mof file.
virtual CIMValue invokeMethod( const ProviderEnvironmentIFCRef& env,
	const String& ns, const CIMObjectPath& path, const String& methodName,
	const CIMParamValueArray& in, CIMParamValueArray& out )
{
	if(methodName.equalsIgnoreCase("gettime"))
	{
		DateTime dt(::time(NULL));
		CIMDateTime cdt(dt);
		return CIMValue(cdt.toString());
	}
	else
		OW_THROWCIMMSG(CIMException::FAILED,
		("Provider does not support method: %1", methodName).c_str());

	}
}

A user defined method can be executed by selecting the Methods tab for the class and then selecting Instances->Execute method as shown below.


In order to simplify the source code for an Associator Provider, a single method: doReferences () can be implemented instead of implementing associators (), associatorNames (), references (), referenceNames (). This method is from CppSimpleInstanceProviderIFC class and is a modified version of references (). In this method, all the instances of the association class have to be returned. These instances are filtered on the input parameters passed to doReferences (). The base class then converts this to the expected response for references () (with no conversion required), referenceNames() (converting instances to ObjectPaths), associators() (calling a getInstance() on the appropriate REF property), and associatorNames() (returning the appropriate REF properties). In this way, implementation of all the four associator provider methods is avoided.

Conclusion

This is how a CIM Provider is developed and tested. This provider helps to abstract the implementation details about managing a resource from the WBEM client.

To summarize, in order to write a CIM Provider:

  1. Identify classes and the associated semantics.

  2. Write the Mof file to depict the Schema extension and the association between the classes.

  3. Identify the Types of provider to be implemented.

  4. Write the source code for the provider.

  5. Write the provider factory method along with the source code.

  6. Create a shared object and place in the location which is given in the openwbem.conf

  7. Test the provider from the CIM Client.

Supplemental material:

  1. Novell_hosts.mof

  2. Novell_HostsProvider.cpp

  3. An Introduction to WBEM and OpenWBEM in SUSE Linux at http://www.novell.com/coolsolutions/feature/14625.html

  4. More information can be found at www.dmtf.org and www.openwbem.org


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell