[virt-tools-list] libosinfo - RFCv3

Arjun Roy arroy at redhat.com
Fri Oct 30 04:10:47 UTC 2009


Hi all,

This is a third draft of the proposed libosinfo API.
The basic idea remains:

1. OS Data would be stored in several XML files.
2. The data would have a flat, no hierarchy layout.
3. The API for querying data would not be tied to the choice of backing data
representation.

Previous suggestions included changing the osi_put methods into osi_free, and
replacing distro with os. I will incorporate those, even though I haven't in this
email. So don't bring that up again please :)

Changes for round 3:

1. Device drivers will be handled in the following fashion:

A new type will be added to the library and the backing data, osi_device_t. It
will represent information for a given device, as follows:

   <device rdf:about="http://pci-ids.ucw.cz/read/PC/1274/5000">
      <class>audio</class>
      <bus-type>pci</bus-type>
      <vendor>0x1274</vendor>
      <product>0x5000</product>
    </device>

There will be an N-N mapping between devices and hypervisors, where a device
can be supported by multiple hypervisors, and a hypervisor can support many
devices.

    <hypervisor rdf:about="http://qemu.org/qemu-kvm-0.11.0">
      <name>KVM</name>
      <version>0.11.0</version>
      <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1002/4382" />
      <device rdf:resource="http://pci-ids.ucw.cz/read/PC/a727/0013" />
      <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1274/5000" />
      ....
    </hypervisor>

The above means that the KVM hypervisor supports/exposes the devices listed. The
name of the driver could change depending on the operating system.

Operating systems, hypervisors and devices would be related in the following fashion:

  <distro rdf:about="http://fedoraproject.org/fedora-11">
   <upgrades rdf:about="http://fedoraproject.org/fedora-10">
   <short-id>fedora11</short-id>
   <name>Fedora 11</name>

    <hypervisor rdf:about="http://qemu.org/qemu-kvm-0.11.0">
        <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1274/5000">ac97</device> -- an audio device, the preferred one for this hypervisor/OS combo, uses the "ac97" driver
        <device rdf:resource="http://pci-ids.ucw.cz/read/PC/a727/0013">3com</device> -- a wireless card, perhaps the only one, definitely the preferred one, uses the "3com" driver
        <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1002/4382">es1391</device> -- another audio device supported by the hypervisor, not preferred, uses "es1391" driver
        ...
        ...  -- some subset of all the devices exposed by this HV 
        ...  -- are also supported by this OS
        ...
    </hypervisor>

    -- If our HV does not have its own section for the OS, use this default list

    <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1002/4382">es1391</device> -- an audio device, preferred for generic HV/this OS combo
    <device rdf:resource="http://pci-ids.ucw.cz/read/PC/1274/5000">ac97</device> -- another audio device
    ...
  </distro>

In the above case, for the KVM 0.11 hypervisor, device .../5000 and .../4382 are
supported for audio. Since both are devices of the same class, that means
.../5000 is the preferred device. For any other hypervisor (ie. any other that
doesn't have a special section like KVM 0.11) it will use the generic device
support list. In the above example, .../4382 is the preferred device for audio
compared to .../5000 for a generic HV. 

The given OS/given HV driver support list must be a subset of the devices exposed
by HV list. It is assumed the generic HV supports every possible device.


________________________________________

API
________________________________________


The library would be implemented in C, and define a few major types:

osi_lib_t : an opaque handle to a library instance.
osi_hypervisor_t : represents a hypervisor

osi_distro_t : represents a distro object.
osi_distro_id_t : represents the unique ID for a distro.
osi_distro_list_t : a list of osi_distro_t objects.
osi_filter_t: filter for searching through distros.

osi_device_t : represents a device
osi_device_id_t : represents a device ID
osi_device_list_t : represents a list of devices

And a bunch of methods:

osi_lib_t osi_get_lib_handle();

/* Setting parameters like libvirt version, etc. */
int osi_set_lib_param(osi_lib_t lib, char* key, char* val);
int osi_get_lib_param(osi_lib_t lib, char* key);
int osi_set_hypervisor(osi_lib_t lib, char* hvname, char* hvversion);
osi_hypervisor_t osi_get_hypervisor(osi_lib_t lib);

/* Initializing and closing down the library */
int osi_init_lib(osi_lib_t lib);
int osi_close_lib(osi_lib_t lib);

/* Getting parameters for hypervisor */
char* osi_get_hv_property_pref_value(osi_hypervisor_t hv, char* propname);
char** osi_get_hv_property_all_values(osi_hypervisor_t hv, char* propname, int* num);
char** osi_get_all_property_keys(osi_hypervisor_t hv, int* num);

/* Managing lists of devices */
osi_device_list_t osi_hypervisor_all_devices(osi_hypervisor_t hv);
osi_device_list_t osi_distro_all_devices(osi_distro_t distro);
osi_device_list_t osi_match_devices(osi_distro_t distro, char* property, char* value);
int osi_put_devices_list(osi_device_list_t devices);
int osi_devices_list_length(osi_device_list_t devices);
osi_device_t osi_get_device_by_index(osi_device_list_t devices, int index);
osi_device_t osi_get_device_by_id(osi_device_id_t device_id);
osi_device_t osi_get_preferred_device(osi_distro_t distro, char* property, char* value);

/* Getting properties for a device */
osi_device_id_t osi_device_id(osi_device_t device);
char* osi_get_device_property_value(osi_device_t device, char* property);
char* osi_get_device_driver(osi_device_t device, osi_distro_t distro);
char** osi_get_device_property_all_values(osi_device_t device, char* property, int* num);
char** osi_get_all_property_keys(osi_device_t device, int* num);

/* Querying for distros and iterating over them */
osi_distro_list_t osi_get_distros_list(osi_lib_t lib, osi_distro_filter_t filter);
int osi_put_distros_list(osi_distro_list_t distros);
int osi_distro_list_length(osi_distro_list_t distros);

/* Methods for filtering results when querying distros */
osi_filter_t osi_get_filter(osi_lib_t lib);
int osi_put_filter(osi_filter_t filter);
int osi_add_constraint(osi_filter_t filter, char* propname, char* propval);
int osi_add_relation_constraint(osi_filter_t filter, enum_t relationship, os_distro_t distro);
int osi_clear_constraint(osi_filter_t filter, char* propname);
int osi_clear_relation_constraint(osi_filter_t filter, enum_t relationship);
int osi_clear_all_constraints(osi_filter_t filter);

/* Get a single distro, either from a list or by ID */
osi_distro_t osi_get_distro_by_index(osi_distro_list_t distros, int index);
osi_distro_t osi_get_distro_by_id(osi_lib_t lib, osi_distro_id_t id);

/* Query Properties for a given distro */
osi_distro_id_t osi_get_distro_id(osi_distro_t distro);
osi_distro_t osi_get_related_distro(osi_distro_t distro, enum_t relationship);
char* osi_get_property_pref_value(osi_distro_t distro, char* propname);
char** osi_get_property_all_values(osi_distro_t distro, char* propname, int* num);
char** osi_get_all_property_keys(osi_distro_t distro, int* num);

/* Query unique values for a given property */
char** osi_unique_property_values(osi_lib_t lib, char* propname, int* num);
os_distro_list_t osi_unique_relationship_values(osi_lib_t lib, enum_t relationship);

Here is a sample program that sets libvirt version, hypervisor type and version,
opens the library, gets a handle to RHEL5.4's representation, finds all the 
distros deriving from it, and lists the audio drivers available for each.

#include <stdio.h>
#include "libosinfo.h"

int main(void)
{
  int i, ret, count;
  osi_lib_t lib = osi_get_lib_handle();

  ret = osi_set_lib_param(lib, "libvirt-version", "3.4");
  if (ret != 0) {
    printf("Error: Could not set libvirt version!\n");
    exit(1);
  }

  ret = osi_set_hypervisor(lib, "kvm", "1.2");
  if (ret != 0) {
    printf("Error: Could not set hypervisor!\n");
    exit(1);
  }

  ret = osi_init_lib(lib);
  if (ret != 0) {
    printf("Error: Could not set initialize libosinfo!\n");
    exit(1);
  }

  osi_filter_t filter = osi_get_filter(lib);
  ret = osi_add_constraint(filter, "short-id", "rhel5.4");
  if (ret != 0) {
    printf("Error: Could not set constraint!\n");
    exit(1);
  }

  osi_distro_list_t results = osi_get_distros_list(lib, filter);
  if (osi_bad_object(results))
    printf("Bad result list!\n");
    exit(1);
  }

  if (osi_distro_list_length(results) == 0) {
    printf("No results. Quitting...\n");
    exit(0);
  }
  else if (osi_distro_list_length(results) > 1) {
    printf("Failed sanity check. 'short-id' should be unique...\n");
    exit(1);
  }

  osi_distro_t rhel = osi_get_distro_by_index(results, 0);

  // Now that we have a handle to rhel5.4, we can free the results list
  // that we used to get to it. The handle to the single distro is still
  // valid though, and we use it for the next step
  ret = osi_put_distros_list(results); // Done with that list so get rid of it
  if (ret != 0) {
    printf("Error freeing distro list!\n");
    exit(1);
  }

  // We shall reuse the filter
  ret = osi_clear_all_constraints(filter);
  if (ret != 0) {
    printf("Error clearing constraints!\n");
    exit(1);
  }

  if (osi_add_constraint(filter, "kernel", "linux") != 0 ||
      osi_add_constraint(filter, "kernel-version", "2.6.30") != 0 ||
      osi_add_relation_constraint(filter, DERIVES_FROM, rhel) != 0)
  {
    printf("Error adding constraints!\n");
    exit(1);
  }

  osi_distro_list_t more_results = osi_get_distros_list(lib, filter);
  if (osi_bad_object(more_results))
    printf("Bad result list!\n");
    exit(1);
  }

  // For each distro:
  count = osi_distro_list_length(more_results);
  for (i = 0; i < count; i++) {
    int j, num;
    osi_distro_t distro = osi_distro_by_index(more_results, i);
    char* distroname = osi_get_property_pref_value(distro, "name");

    osi_device_list_t audio_devices = osi_match_devices(distro, "class", "audio");
    num = osi_devices_list_length(audio_devices);

    // For each audio device:
    for (j = 0; j < num; j++) {
      osi_device_t device = osi_get_device_by_index(audio_devices, j);
      printf("Audio device for %s:\n", distroname);
      printf("\tBus Type: %s Vendor: %s Product: %s\n", 
                 osi_get_device_property_value(device, "bus-type"),
                 osi_get_device_property_value(device, "vendor"),
                 osi_get_device_property_value(device, "product"));
      printf("\tDriver is: %s\n", osi_get_device_driver(device, distro));
    }

    // And free the distroname and list of audio devices for this distro
    free(distroname);
    ret = osi_put_devices_list(audio_devices);
    if (ret != 0) {
        printf("Error freeing devices list!\n");
        exit(1);
    }
  }

  ret = osi_put_distros_list(more_results); // Done with that list
  if (ret != 0) {
    printf("Error freeing distro list!\n");
    exit(1);
  }

  ret = osi_put_filter(filter); // Done with the filter
  if (ret != 0) {
    printf("Error freeing filter!\n"):
    exit(1);
  }

  ret = osi_close_lib(lib);
  if (ret != 0) {
    printf("Error cleaning up library handle!\n");
    exit(1);
  }

  printf("Done.\n");
  return 0;
}

Awaiting comments/concerns/criticisms and anything really. Thanks for the read.

-Arjun




More information about the virt-tools-list mailing list