I've lost count of how many times I've had to re-learn how to create a bootable image from a filesystem tree (such as a tarball of a root filesystem, or a Xen partition image). If for no other reason than recording it for next time, here it is.
Create an image file (lets assume 4MB will do it), partition it, mount the partition, and copy in the root filesystem
This is then thrown around way too much, using an enumeration called CMPIType to indicate which interpretation of CMPIValue you intend to use. For example, to set a property of an instance, you would do something like this for an integer:
Makes sense, right? The above usage in the string case is, of course, the scenario intended by the interface designer, and it is (in my experience) almost exclusively used that way. However, assuming you want to do something a little more complicated, such as write a CIMXML instance parser, you might want to pass around their CMPIValue and CMPIType objects.
if (/* value looks like an integer */) type = parse_integer(string, &value); else type = parse_string(string, &value);
CMSetProperty(inst, name, &value, type); }
That's all fine, right? We set the CMPIValue in our handler functions, and then we pass a pointer to our value to CMSetProperty(). Obviously nothing to see here, CMSetProperty() takes a CMPIValue *......doesn't it?
The answer is a big fat "NO, no it doesn't". If you were paying close attention before, we passed in the pointer to a CMPIValue stack variable in the integer case, but in the string case, we passed in a pointer to an actual string buffer. Thus, if we put that string pointer in the CMPIValue itself, and then pass a pointer to it, you get a double pointer, the equivalent of a char **. Because the type is correct, the compiler doesn't complain, but the CIMOM explodes.
I'm really shocked and horrified that the CMPI interface includes this semantic difference in the setProperty() broker call. To verify that I wasn't just hallucinating on a Friday afternoon, I tracked the call down to the following bit of truly spectacular OpenPegasus code:
CIMValue value2CIMValue(const CMPIValue* data, ...) { . . . else if (type==CMPI_chars) { if (data) v.set(String((char*)data)); else return CIMValue(CIMTYPE_STRING,false); }
Right there, they take the CMPIValue and cast it directly to a char * if you tell them that you're passing in CMPI_chars! Just to illustrate how the interpretation of the CMPIValue parameter fundamentally changes depending on the type you passed in, the following appears directly below:
else if (type==CMPI_charsptr) { if (data && *(char**)data) v.set(String(*(char**)data)); else return CIMValue(CIMTYPE_STRING,false); }
So, if you pass in CMPI_chars, then it expects a char*. If you pass in CMPI_charsptr, then it expects a char ** (which would actually cast a the example above to the correct thing). IMHO, it would have been better for them to just call it a void * and be done with it. They think they have made it cleaner, but they've actually made it worse.
If you know me, you know that I don't often acknowledge the existence of Windows as an OS worth my attention. However, I'm working on some code for a group of people who are predominantly Windows users that, aside from their choice of OS, are pretty quality individuals. Since I would certainly not spend my time writing a native win32 application, I decided to use Python, which is one of my preferred languages for Linux.
I knew that Python ran on Windows, but I didn't realize how extensive the list of necessary add-on packages would be; I'm spoiled by modern Linux distributions. For this app, I would need Python, pyGTK, and pySerial. Those packages require the GTK runtime, the win32all package, pygobject, and pycairo. Thus, any poor Windows user would be stuck installing a raft of packages totalling about 50MB (download size) before they could run the app.
I figured there had to be a better way. I had considered getting the source for all of the python code and putting it into a single tree and rebuilding the installer to install them all at once, but that wouldn't cover GTK and would be a bit of a pain. After a bit of searching, I came across py2exe.
This nifty little tool extends the python distutils and gives you a new build target. During the build process, it determines all of the required Python packages that your app will need and copies them into a distribution directory. It then copies in the Python runtime itself, as well as any necessary DLLs to run. Finally, it turns your toplevel .py file into a .exe. Zip that up and post it. All someone needs to do is extract it and run your .exe file. How nice is that?
Given that pyGTK is rather large and complex, it didn't go as smoothly as it should have. I had to do two extra things: Add a list of required packages to assist the detection and copy in the GTK share files. My setup.py ended up like this:
After building, you get a nice "dist/" (or, in the backwards Windows world, "\dist") directory. To complete the GTK distribution, I copied the share, etc, and lib directories from C:\GTK into dist. To cut down on the extra crap, I also deleted everything but "en" from the "share/locale" directory. The result is a nice and neat little package that OS-challenged people can handle. I think I'll also need to figure out how MacOS distributes such things at some point, but I'm sure there will be less groaning and eye-rolling during that process.
I've never had to debug a multi-process program with GDB before. Mostly because I do a lot of my debugging with printf() and elbow-grease. However, a CIM provider is in an environment where a crash happen within the broker, which means you can't (easily) add print statements to the appropriate place. If you pass something broken to a CBFoo() call, the CIMOM will crash a ways down the line.
With Pegsus, you can turn off provider processes, which forces thread-only mode. This means that the whole CIMOM is vulnerable to a bad provider, but it also means that running the cimserver in gdb is easy and straightforward. Doing the same in SFCB, however, is not as simple. Your bad provider ends up in a child of the main process, which crashes without GDB's full attention, which isn't very helpful.
While helping to debug a particularly nasty crash with an Indication provider, I learned a (relatively) easy way to catch the crash with GDB. First, I started SFCB and subscribed to the indication. This forked off a process to host the provider. While in the simple case, the PID of the new process will be the highest-numbered sfcbd process, you can also find out which one it is with something like this:
% PROV=libVirtComputerSystemIndication % for i in $(ps ax | grep sfcbd | awk '{print $1}'); do > grep -q $PROV /proc/$i/maps && echo $i; > done
That checks all of the currently running sfcbd processes to see which has your provider loaded and prints the PID. Next, I attached to the process with gdb and allowed it to continue:
% S gdb /usr/local/sbin/sfcbd GNU gdb 6.6 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "x86_64-suse-linux"... Using host libthread_db library "/lib64/libthread_db.so.1". (gdb) attach 5832 <snip> (gdb) c
Next, I triggered the crash and GDB caught it, allowing me to get a stack trace and examime the situation a little. After that, figuring out the issue was easy.
Recently a colleague pointed out that our VirtualSystemManagementService's RemoveResourceSettings method (in libvirt-cim) was marked with the wrong type. This was done early on, and probably was just a mistake, given that AddResourceSettings and ModifyResourceSettings both took the same type: an EmbeddedObject. RemoveResourceSettings takes a reference, since anything you could possibly delete would be representable as a reference. It's the right way, but annoying, given that I had already built a common resource management infrastructure around the idea of having a list of resource objects to (Add,Modify,Remove).
Anyway, I coded up the change to the provider itself and set out to test it. I quickly realized that I didn't know what the CIMXML for a method call with an array of references looked like. Up to this point, I have been unit testing the method providers by just hacking up a CIMXML template and stuffing it into wbemexec or wbemcat. It sucks, but it works.
However, this time I was stuck. I didn't have an example, and some guessing didn't work in a short amount of time. I decided to try to work up something in pywbem to see if I could get not only the method call made for testing, but also a glimpse at what the CIMXML for it looked like. I ended up with this very small bit of python code that was extremely helpful:
from pywbem import WBEMConnection, CIMInstanceName
c = WBEMConnection("http://localhost", ("root", "password"), "root/virt") c.debug = True
r = CIMInstanceName("Xen_DiskResourceAllocationSettingData", keybindings={"InstanceID":"pv0/xvda"})
res = c.InvokeMethod("RemoveResourceSettings", "Xen_VirtualSystemManagementService", ResourceSettings=[r])
print c.last_request
This connects to localhost, creates a reference, and makes the method call with that reference as a single-element array. After the operation, the "last_request" field contains the CIMXML it sent to the server, but only if the "debug" property is set to True (something the documentation conveniently leaves out). The result (with the boiler-plate cut out) looks like this:
Not that I really need it at this point, given that the pywbem case was so easy to write. However, it provides a good example of how to not only use pywbem to figure out the proper CIMXML for an operation, but also debug it if it's wrong.