A Practical Approach to WBEM[s]CIM Management

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:

Instance Provider

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

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.

// ******************************************** // 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; }

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.

// ******************************************** // 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; }

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.

// ******************************************** // 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; }

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.

// ******************************************************** // 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; }

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 .

// ******************************************* // 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; }

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:

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.

// ******************************************** // 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(); }

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.

// ******************************************* // 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; }

Figure 12.11: C++ Code for doReset()

// ******************************************** // 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; }

Figure 12.12: C++ Code for setTheIndicator()

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

Категории