Realmd CIM Provider

Building
========

Prerequisites:
--------------

To build you'll need these packages installed:

cmake
openlmi-providers-devel
konkretcmpi


To install and run you'll need at a minimum:
--------------------------------------------

tog-pegasus
openlmi-providers


This project uses the same build mechanism as openlmi-provider which
is based on cmake. It's also important specify the same cmake
configuration parameters enforced by RPM.

My short term solution is to use the following shell script. I add the
CFLAGS override to turn on options useful for debugging during
development, you may wish to omit that.

<<<<<<<<<<
#!/bin/sh

export CFLAGS='-g -O0 -DRDCP_DEBUG'

/usr/bin/cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr -DINCLUDE_INSTALL_DIR:PATH=/usr/include -DLIB_INSTALL_DIR:PATH=/usr/lib -DSYSCONF_INSTALL_DIR:PATH=/etc -DSHARE_INSTALL_PREFIX:PATH=/usr/share -DBUILD_SHARED_LIBS:BOOL=ON .

if [ $? -eq 0 ]; then
    make
fi
>>>>>>>>>>

Installing:
-----------

% sudo make install

This copies the mof file, the registration file and the loadable
module to their destination. Then you must register your module with
the Pegasus CIMOM. Note: pegasus MUST be running!

% openlmi-mof-register register /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg

Development Tips:
-----------------

Understanding konkret code generation issues:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

konkret is a tool that reads in a mof file and generates C code. For
every XXX class it will generate a XXX.h and XXXProvider.c file. The
code generation occurs due to CMake macros provided by the
openlmi-providers-devel package. konkret needs to run any time you
modify the mof file. It *always* generates a new XXX.h file because
that's where definitions based on the contents of the mof file are
located. If there is no XXXProvider.c file it will also generate
it. This is a "stub" file in which you will fill in with your
implementation. If XXXProvider.c exits it will not overwrite it,
however it always overwrites the XXX.h file.

Do not put anything into the XXX.h file you'll need to retain.

After editing the mof file the make targets will cause konkret to run
again. You'll get brand new XXX.h files. But your old XXXProvider.c
files may no longer have the correct definitions (e.g. prototypes)
found in the XXX.h file so you may need to hand edit by copying the
function prototype from the XXX.h file into your XXXProvider.c file.

If you've written definitions that logically belong in XXX.h but don't
want them nuked the next time konkret runs my solution was to put them
in someother .h file that's included by the XXXProvider.c file.

Initializing class instances:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The way konkret works is to emit specialized inline functions to
initialize each member of a class. If the class is subclassed you get
different initializers depending on whether the property is in the
parent class or the subclass. You cannot call a parent class property
initializer in a subclass (yuck), you have to use the subclass
initializer for the property inherited from the parent class. This
creates a maintenance problem if the parent class changes, you have
find every place parent class properties are inialized and make
changes. To solve this problem I defined macros that initialize class
properties. The macro takes a "klass" parameter and token pastes it to
generate the class specific property manipulation function call. Using
these macros means anytime a class changes due to a change in the mof
file there is only one place where you need to change the code. These
macros are a good example of what logically belongs in the XXX.h file
but are separated out into a different .h file because konkret will
nuke anything you've added to a XXX.h file.

Modifications to the provider:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

During development if the mof file changes you have to make Pegasus
reload the mof. It's not sufficient to retart cimserver, you have to
unregister the provider and register it again for Pegasus to see the
mof changes. Thus you would use the openlmi-mof-register command above
except pass the unregister option, followed by the above command, e.g.

% openlmi-mof-register unregister /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
% openlmi-mof-register register /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg

If all you've done during devopment is modify the provider (not it's
mof definition) then all you need to do is:

% sudo cimserver -s
% sudo make install
% sudo cimserver

How do I run the Pegasus CIMOM so I can see debug statements?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

% sudo cimserver daemon=false forceProviderProcesses=false

How do I use GDB to debug my provider?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Create the following .gdbinit file where XXX is where you want to break.

<<<<<<<<<<
set breakpoint pending on
b XXX
r daemon=false forceProviderProcesses=false
>>>>>>>>>>

then run gdb like this:

% sudo gdb cimserver

How do I trace what Pegasus is doing?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

% cimserver daemon=false forceProviderProcesses=false logLevel=TRACE traceLevel=5 traceFacility=File traceComponents=All

The trace file is written to:

/var/lib/Pegasus/cache/trace/cimserver.trc

More information about cimserver tracing can be found in the
"OpenPegasus Tracing User Guide" PDF. Google the title to get a
current URL.

FAQ
===

Q: What does the rdcp acronym stand for?

A: Realmd CIM Provider. The "rd" is for Realmd, the "c" is for CIM and
   the "p" is for Provider

Q: What decisions influenced your DBus implementation strategy?

A: There are essentially two supported DBus API's. A very high level
   GDBus and a very low level libdbus.

   GDBus requires you to utilize Gnome's GObject pseudo
   object-orientated framework. If you're not familar with it it's a
   fairly steep learning curve to become proficient. Also if you're
   not proficient in GObject programming it's hard to comprehend code
   which utilizes it thus putting normal C developers at a
   disadvantage. However GDBus gives you a lot of nice support, one of
   the nice features is everything is based on GVariants, a powerful
   data structure.

   On the other hand libdbus is very low level, there is very little
   support for the necessary DBus operations. However it is used
   extensively by other projects so it's not an aberration to use it,
   it's pure C code making it easier to understand and it doesn't pull
   in the whole GObject system. But it's a lot of work to use.

   I took a compromise approach. I didn't have the time to become
   proficient with GObject and I felt the GObject based code was
   difficult for C programmers without GObject experience to read
   and modify. However I recognized the value of expressing most
   things in terms of GVariants, a hallmark of GDBus. So I wrote some
   utility code that supports serializing GVariants into and out of
   libdbus. This allowed me to use the powerful GVariant without
   having to get involved with GObjects and GDBus. If we ever decide
   to port the code to GDBus it shold be fairly straight forward
   because we're already using GVariant's as a fundamental type. This
   seemed to represent a reasonable compromise between libdus and
   GDBus, it avoids the pseudo object-orientated framework of GObject
   in favor of vanilla C code but retains the powerful use of
   GVariants. I guess only time will tell if it was a smart choice or
   not.

ToDo
====

Implement locale in RealmdService.

Utilize CMPI Logging instead of debug printf statements (currently
controlled by the RDCP_DEBUG compile time flag).

Generate indications when realms are added or removed from the Realms
property of the LMI_RealmdService.

Any blocking operations should not block the CIMOM.
(e.g. communicating with DBus). I think the right way to do this is
via the CMPI threading support, but this needs further
investigation. Other openlmi developers would be a good resource for
this issue.

We call DBus to get object properties a lot. There is I believe
support for DBus clients which caches object properties and listens on
the properties change signal to refresh the properties cache. Access
to the properties are then performed via the local properties cache
rather than the via RPC. Obviously this is much more efficient, we
should support it.
