Step 2: Write the Provider Code

Step 2: Write the Provider Code

The second step is to write the code for the providers. We must first decide which providers are required ”we will clearly need at least an instance provider and a method provider. It would be possible to imagine other providers but, once you have written one type, the others are very simple and so I only give these two examples in detail. The open -Pegasus release comes with examples of all types of providers and these should be the basis of your code. We will write two C++ programs:

  • PbxMain. cpp which simply acts as the main program for the providers and creates instances of the Instance Provider and Method Provider classes when requested by the WBEM server. This program is trivial; the code is given in Figure 12.4 and I do not discuss it further below.

    start figure
     #include <Pegasus/Common/Config.h> #include <Pegasus/Common/String.h> #include "PbxTelephoneModule.h" PEGASUS_NAMESPACE_BEGIN extern "C" PEGASUS_EXPORT CIMProvider *     PegasusCreateProvider(const String & name)     {     // if he's looking for one of our classes,     // then return it. Otherwise return 0.     if (String::equalNoCase(name,"PbxInstanceProvider"))         return(new PbxInstanceProvider());     if (String::equalNoCase(name, "PbxMethodProvider"))         return(new PbxMethodProvider());     return(0);     } PEGASUS_NAMESPACE_END 
    end figure

    Figure 12.4: C++ Code to Create Providers ” PbxMain.cpp
  • PbxTelephoneModule.cpp (and the associated PbxTelephoneModule.h header file) which provides the functions necessary for the Instance and Method Providers in two classes: PbxInstanceProvider and PbxMethodProvider .

Instance Provider

The instance provider will need to support a number of interfaces:

  • getInstance(): Pretend to access the logical device on the PBX and create and return an instance of the ACNE_PBXTelephoneInterface class from the information it discovers.

  • enumerateInstances(): Pretend to access the hardware and return all available instances.

  • enumerateInstanceNames(): Do the same as enumerateInstances() but return only a list of the names of the instances.

  • modifyInstance(): Determine whether or not the client is allowed to modify the properties (is it writable?) and, if so, implement the change.

  • createInstance(): Always return an exception since instances of this class may not be created by the client, only by having a card plugged into a slot where they are detected by the PBX software.

  • deleteInstance(): Also always return an exception ”instances are deleted only by the appropriate card being removed from the system.

The easiest methods to code are those which do nothing! As we have seen, createInstance () and deleteInstance () should actually only return an error as the operator is not allowed to create or delete instances of the PbxTelephoneModule class ”instances being created and deleted by the physical plugging in of cards.

The code for createInstance() is given in Figure 12.5 and, since the code for deleteInstance() is identical, I have not given it.

start figure
 // ******************************************** //  method  createlnstance //  purpose create a new PbxTelephoneModule //          instance //  note    this operation is not allowed so //          this method simply throws an //          exception // ******************************************** void PbxInstanceProvider::createInstance(         const OperationContext & context,         const CIMObjectPath & instanceReference,         const CIMInstance & instanceObject,         ObjectPathResponseHandler & handler)     {     // operator is not allowed to create     // an instance: instances are created     // implicitly by plugging the modules     // into the shelf.     throw CIMNotSupportedException(               "Modules may not be created manually");     return;     } 
end figure

Figure 12.5: C++ Code for CreateInstance()

You may query the choice of exception: CIMNotSupportedException does not really describe the problem ”the createInstance() method is forbidden rather than unsupported. Unfortunately the CIM specification (DSP0200) includes only a limited number of exceptions that may be thrown [1] and has no facility for you to add more. It therefore often arises that you will have to make the best of a bad job with the choice of exception and redeem yourself with the accompanying message. The poverty of exceptions has been recognised by the DMTF and a new class, CIM_Error, was introduced in version 2.8 of the CIM schema. The idea is that, in the future, an instance of this class, which has properties such as ErrorType, OwningEntity, PerceivedSeverity, ProbableCause, will accompany the exception.

The methods that actually do something are not much more difficult and I will only consider enumerateInstanceNames() and getInstance() in this book as the other routines are very similar and are largely built from components contained in these two.

Consider enumerateInstanceNames() . Because we have no real hardware, I have invented three instances of the modules by defining them as constants ”in real life, of course, the code would go to the hardware to find out what was really installed. The three instances are defined in PbxTelephoneModule.h as follows and you must accept the lie that they have been created in this form by some program which scanned the hardware:

 const unsigned int numberInstances = 3; const char * const instances[numberInstances] = { "ACNE_PBXTelephoneModule."         "SystemCreationClassName=\"ACNE_PBX\","         "SystemName=\"XYZCompanyPBX1\","         "CreationClassName=\"ACNE_PBXTelephoneModule\","         "DeviceID=\"1\"",  "ACNE_PBXTelephoneModule."         "SystemCreationClassName=\"ACNE_PBX\","         "SystemName=\"XYZCompanyPBX1\","         "CreationClassName=\"ACNE_PBXTelephoneModule\","         "DeviceID=\"3\"",  "ACNE_PBXTelephoneModule."         "SystemCreationClassName=\"ACNE_PBX\","         "SystemName=\"XYZCompanyPBX1\","         "CreationClassxName=\"ACNE_PBXTelephoneModule\","         "DeviceID=\"6\"" }; 

Note that these keys align with those described earlier and that I have made use of the C++ trick of breaking a string across two lines by using trailing and leading quotation marks. Once you have worked out the quotation marks, you will find that each of the three entries is an Object Path (see page 67) in the form

 <classname>.<key>=<value>,<key>=<value> .... 

The code for enumerateInstanceNames() is now easy to write; see Figure 12.6.

start figure
 // ******************************************** //  method  enumerateInstanceNames //  purpose return names of all instances // ******************************************** void PbxInstanceProvider::enumerateInstanceNames(           const OperationContext & context,           const CIMObjectPath & classReference,           ObjectPathResponseHandler & handler)     {     // begin processing the request     handler.processing();     // pretend that we have scanned the hardware to     // get the instances: actually just get them     // from our table     for (unsigned int i=0;i<numberInstances;i++)         {         // create an Object Path from the instances         // that we have found from the hardware         CIMObjectPath inst(instances[i]);         // give the instance to the WBEM server         handler.deliver(inst);         }     // complete processing the request     handler.complete();     return;     } 
end figure

Figure 12.6: C++ Code for EnumeratelnstanceNames()

The pattern for writing a provider for an intrinsic method is surely now becoming clear to you. I will finish with one further example: getInstance() . Again, as we have no real hardware, I have written a helper function to pretend to build the necessary instance. This is illustrated in Figure 12.7 where the addProperty() method of the CIMInstance class is repeatedly invoked to create an instance. I have not taken the trouble to set values for every property of ACNE_PBXTelephoneModule but I have set most of them.

start figure
 // ******************************************** //  method  buildInstance //  purpose build a dummy instance pretending //          to read the real hardware // ******************************************** CIMInstance PbxInstanceProvider::buildInstance(                String deviceId)     {     // create an instance of the correct class     CIMInstance inst("ACNE_PBXTelephoneModule");     // then add the properties     inst.addProperty(CIMProperty("SystemCreationClassName",                           String("ACNE_PBX")));     inst.addProperty(CIMProperty("SystemName",                           String("XYZCompanyPBX1")));     inst.addProperty(CIMProperty("CreationClassName",                           String("ACNE_PBXTelephoneModule")));     inst.addProperty(CIMProperty("DeviceID",deviceId));     inst.addProperty(CIMProperty("Protocol",1));     inst.addProperty(CIMProperty("ModuleNumber",4));     inst.addProperty(CIMProperty("Name",                           String("Telephone Module")));     return inst;     } 
end figure

Figure 12.7: Artificial C++ Code to Build a Dummy Instance

There is one further helper function which getInstance() uses: checkKeys , which I have listed in Figure 12.8. It simply checks whether an Object Path is a valid key for our ACNE_PBXTelephoneModule class. This routine, like the buildInstance() routine above, is not strictly provider code, but I have included it for completeness and to expose you to a few more openPegasus functions, particularly the support for arrays.

start figure
 // ******************************************************** //  method  checkKeys //  purpose check whether a requested instance //          has a valid key //  output  throw exception if not valid, otherwise return //          a string containing the deviceID // ******************************************************** String PbxInstanceProvider::checkKeys(const CIMObjectPath & ref)     {     String  deviceId;     CIMName keyName;     int keyCount = 4;     Array<CIMKeyBinding> keys = ref.getKeyBindings();     if ((unsigned int)keys.size() != (unsigned int)keyCount)         throw CIMInvalidParameterException("Must be 4 keys");     for (unsigned int i = 0; i < keys.size(); i++)         {         keyName = keys[i].getName();         if ((keyName.equal("DeviceID")) && (deviceId.size() == 0))             {             deviceId = keys[i].getValue();             keyCount--;             }         else             {             if (keyName.equal("SystemCreationClassName") &&                 String::equal(keys[i].getValue(), "ACNE_PBX"))                 {                 keyCount--;             else                 .....etc for CreationClassName and SystemName.....             }         }     if (keyCount != 0)         throw CIMInvalidParameterException("Too few parameters");     return deviceId;     } 
end figure

Figure 12.8: C++ Code for checkKeys()

The actual code of getInstance() uses these two routines to return an instance: I give the code in Figure 12.9. The actual work is done in the three calls to handler .

start figure
 // ******************************************* //  method  getInstance //  purpose return a particular instance // (if it exists) //  input // ******************************************* void PbxInstanceProvider::getInstance(         const OperationContext & context,         const CIMObjectPath & ref,         const Boolean includeQualifiers,         const Boolean includeClassOrigin,         const CIMPropertyList & propertyList,         InstanceResponseHandler & handler)     {     CIMInstance instance;     String deviceId;     // use the helper function to decide whether the     // keys that we have been given are OK or not     deviceId = checkKeys(ref);     // now go away to get the instance requested (if it exists)     // Of course, for this example it just returns an instance     // read from a table but in the real world it would return     // something genuine     handler.processing();     instance = buildInstance(deviceId);     handler.deliver(instance);     handler.complete();     return;     } 
end figure

Figure 12.9: C++ Code for getInstance()

Method Provider

There are four extrinsic methods which need to be provided, three of which our class has inherited from CIM_LogicalDevice:

  • uint32 Reset() which will cause us to reset the card by pretending to write into a particular register on a particular integrated circuit on the card. The return value is zero to indicate that the reset was completed successfully.

  • uint32 SaveProperties() and RestoreProperties() which should throw an exception because I have assumed that there is no state to save and restore.

  • Boolean setlndicator (Boolean newValue) which will cause us to pretend to write into a particular register on the card and return the old value of the indicator.

Apart from constructors, destructors and other start-up and closedown routines, a method provider is expected to provide a single function: invokeMethod() to which all incoming extrinsic method invocations are forwarded. Our code for this is contained in Figure 12.10.

start figure
 // ******************************************** //  method  invokeMethod //  purpose look to see which method should //          be invoked and invoke it // ******************************************** void PbxMethodProvider::invokeMethod(             const OperationContext & context,             const CIMObjectPath & objectReference,             const CIMName & methodName,             const Array<CIMParamValue> & inParameters,             MethodResultResponseHandler & handler)     {     // convert a fully qualified reference into a local reference     // (class name and keys only).     CIMObjectPath localReference = CIMObjectPath(                                 String(),                                 String(),                                 objectReference.getClassName(),                                 objectReference.getKeyBindings());     handler.processing();     if (objectReference.getClassName().equal(                                   "ACNE_PBXTelephoneModule"))         {         if (methodName.equal("setIndicator"))             {             Boolean retVal = setTheIndicator(localReference,                                      inParameters);             handler.deliver(retVal);             }         if (methodName.equal("reset"))             {             Uint32 retVal = doReset(localReference);             handler.deliver(retVal);             }         }     handler.complete();     } 
end figure

Figure 12.10: C++ Code for InvokeMethod()

As you can see, this code simply checks the name of the extrinsic function ( setIndicator or reset in this case) and invokes a private function to do the work. Notice again the call to handler.processing() followed by a number (here one) call to handler.deliver() followed by a final call to handler.complete() . This is the same as for all openPegasus providers.

Although strictly you have now seen everything about the method provider interface, I have included the private functions in Figure 12.11 and Figure 12.12 as this gives me an opportunity to showcase a few more openPegasus calls: those to manipulate CIMObjectPaths and CIMKeyBindings. Again, remember that there is no real PBX hardware so they only pretend to do something.

start figure
 // ******************************************* //  method  doReset //  purpose pretend to do a RESET on the module //  input   instance Object Path //  output  0 if all OK, 2 otherwise // ******************************************* Uint32 PbxMethodProvider::doReset(CIMObjectPath obj)     {     // OK, we'll pretend that we've done the reset     return 0;     } 
end figure

Figure 12.11: C++ Code for doReset()
start figure
 // ******************************************** //  method  setTheIndicator //  purpose pretend to set the indicator LED //  input   instance Object Path //          input parameters //  output  previous value of the indicator // ******************************************** Boolean PbxMethodProvider::setTheIndicator(CIMObjectPath obj,             const Array<CIMParamValue> & inParameters)     {     CIMName keyName;     String deviceId;     // we must first retrieve the value of DeviceID     Array<CIMKeyBinding> keys = obj.getKeyBindings();     for (Uint32 i=0;i < keys.size();i++)         {         keyName = keys[i].getName();         if (keyName.equal("DeviceID"))             deviceId = keys[i].getValue();         }     if (deviceId.size() == 0)         throw CIMInvalidParameterException("DeviceID missing");     // and then get the parameter     if (inParameters.size() != 1)         throw CIMInvalidParameterException(                      "setIndicator needs 1 param");     CIMValue paramVal = inParameters[0].getValue();     if (paramVal.getType() != CIMTYPE_BOOLEAN)         throw CIMInvalidParameterException(             "setIndicator param must be Boolean");     Boolean inValue;     paramVal.get(inValue);     // send back the opposite value from the one input     if (inValue == true)         return false;     else         return true;     } 
end figure

Figure 12.12: C++ Code for setTheIndicator()

[1] In C++, exceptions are "thrown" and "caught" rather than being "raised."

A Practical Approach to WBEM[s]CIM Management
A Practical Approach to WBEM[s]CIM Management
ISBN: 849323061
Year: 2006
Pages: 152 © 2008-2017.
If you may any questions please contact us: