linux学习记录
分类:
2008-09-25 15:20:39
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.
ContentsTopics | CIM Introduction, modeling and writing a CIM Provider |
Products | OES 1.0, SLES 9.0 |
Audience | Developers |
Level | Intermediate |
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:
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.
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:
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:
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.
class ClassName
{
data type PropertyName;
data type MethodName();
};
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.
[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();
};
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:
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
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
Where& are IP addresses
Steps to be followed while modeling a provider:
// 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 . 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.
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:
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.
OW_PROVIDERFACTORY (NovellHostsProvider, novellhostsprovider);
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 . As we are implementing Instance, Method and Associator provider, intrinsic methods of CppInstanceProviderIFC class, CppMethodProviderIFC class and CppAssocitorProviderIFC class respectively needs to be implemented.
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.
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.
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.
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.
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.
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.
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);
}
}
}
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.
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));
}
}
}
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.
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.
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:
Supplemental material: