[virt-tools-list] [PATCH libosinfo] Add a 'osinfo-query' command line tool

Daniel P. Berrange berrange at redhat.com
Mon Mar 12 17:25:12 UTC 2012


From: "Daniel P. Berrange" <berrange at redhat.com>

Enable end users to search the database with a new osinfo-query
command. For example

$ osinfo-query --fields=short-id,name os vendor="Fedora Project"
 Short ID             | Name
----------------------+------------------
 fedora1              | Fedora Core 1
 fedora2              | Fedora Core 2
 fedora3              | Fedora Core 3
 fedora4              | Fedora Core 4
 fedora5              | Fedora Core 5
 fedora6              | Fedora Core 6
 ...
---
 libosinfo.spec.in         |    1 +
 mingw32-libosinfo.spec.in |    1 +
 tools/Makefile.am         |    9 +-
 tools/osinfo-query.c      |  707 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 716 insertions(+), 2 deletions(-)
 create mode 100644 tools/osinfo-query.c

diff --git a/libosinfo.spec.in b/libosinfo.spec.in
index 28f4804..9bfeff8 100644
--- a/libosinfo.spec.in
+++ b/libosinfo.spec.in
@@ -100,6 +100,7 @@ rm -fr %{buildroot}
 %{_bindir}/osinfo-usbids-convert
 %{_bindir}/osinfo-detect
 %{_bindir}/osinfo-db-validate
+%{_bindir}/osinfo-query
 %dir %{_datadir}/libosinfo/
 %dir %{_datadir}/libosinfo/data/
 %dir %{_datadir}/libosinfo/schemas/
diff --git a/mingw32-libosinfo.spec.in b/mingw32-libosinfo.spec.in
index 0ec510c..7da0736 100644
--- a/mingw32-libosinfo.spec.in
+++ b/mingw32-libosinfo.spec.in
@@ -63,6 +63,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_mingw32_bindir}/osinfo-usbids-convert
 %{_mingw32_bindir}/osinfo-detect.exe
 %{_mingw32_bindir}/osinfo-db-validate.exe
+%{_mingw32_bindir}/osinfo-query.exe
 %{_mingw32_bindir}/libosinfo-1.0-0.dll
 %{_mingw32_libdir}/libosinfo-1.0.dll.a
 %{_mingw32_libdir}/libosinfo-1.0.la
diff --git a/tools/Makefile.am b/tools/Makefile.am
index ddee9be..29ef078 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -6,9 +6,9 @@ AM_CFLAGS = $(GOBJECT_CFLAGS) \
 	    -I$(top_srcdir) \
             $(NULL)
 
-bin_PROGRAMS = osinfo-detect osinfo-db-validate
+bin_PROGRAMS = osinfo-detect osinfo-db-validate osinfo-query
 
-man1_MANS = osinfo-db-validate.1 osinfo-detect.1
+man1_MANS = osinfo-db-validate.1 osinfo-detect.1 osinfo-query.1
 
 POD2MAN = pod2man -c "Virtualization Support" -r "$(PACKAGE)-$(VERSION)"
 
@@ -26,3 +26,8 @@ osinfo_db_validate_LDADD = $(GOBJECT_LIBS)   \
 		      $(GIO_LIBS)     	     \
 		      $(LIBXML_LIBS)  	     \
 		      $(top_builddir)/osinfo/libosinfo-1.0.la
+
+osinfo_query_SOURCES = osinfo-query.c
+osinfo_query_LDADD = $(GOBJECT_LIBS) 	     \
+		      $(GIO_LIBS)     	     \
+		      $(top_builddir)/osinfo/libosinfo-1.0.la
diff --git a/tools/osinfo-query.c b/tools/osinfo-query.c
new file mode 100644
index 0000000..cfb807e
--- /dev/null
+++ b/tools/osinfo-query.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc
+ *
+ * osinfo-query: query the contents of the database
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors:
+ *   Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <osinfo/osinfo.h>
+
+
+struct OsinfoLabel {
+    const gchar *prop;
+    const gchar *label;
+    gboolean enabled;
+    gsize width;
+};
+
+typedef OsinfoList * (*osinfo_list_func)(OsinfoDb *db);
+
+struct OsinfoType {
+    const gchar *name;
+    osinfo_list_func listFunc;
+    GType entityType;
+    GType filterType;
+    GType listType;
+    struct OsinfoLabel *labels;
+};
+
+static struct OsinfoLabel os_labels[] = {
+    { OSINFO_PRODUCT_PROP_SHORT_ID,
+      "Short ID", TRUE, 20 },
+    { OSINFO_PRODUCT_PROP_NAME,
+      "Name", TRUE, 50 },
+    { OSINFO_PRODUCT_PROP_VERSION,
+      "Version", TRUE, 8 },
+    { OSINFO_OS_PROP_FAMILY,
+      "Family", TRUE, 10 },
+    { OSINFO_PRODUCT_PROP_VENDOR,
+      "Vendor", TRUE, 25 },
+    { OSINFO_PRODUCT_PROP_RELEASE_DATE,
+      "Release date", FALSE, 12 },
+    { OSINFO_PRODUCT_PROP_EOL_DATE,
+      "End of life", FALSE, 12 },
+    { OSINFO_PRODUCT_PROP_CODENAME,
+      "Code name", FALSE, 10 },
+    { OSINFO_ENTITY_PROP_ID,
+      "ID", TRUE, 40 },
+    { NULL, NULL, 0 },
+};
+
+static struct OsinfoLabel platform_labels[] = {
+    { OSINFO_PRODUCT_PROP_SHORT_ID,
+      "Short ID", TRUE, 20 },
+    { OSINFO_PRODUCT_PROP_NAME,
+      "Name", TRUE, 50 },
+    { OSINFO_PRODUCT_PROP_VERSION,
+      "Version", TRUE, 8 },
+    { OSINFO_PRODUCT_PROP_VENDOR,
+      "Vendor", TRUE, 25 },
+    { OSINFO_PRODUCT_PROP_RELEASE_DATE,
+      "Release date", FALSE, 12 },
+    { OSINFO_PRODUCT_PROP_EOL_DATE,
+      "End of life", FALSE, 12 },
+    { OSINFO_PRODUCT_PROP_CODENAME,
+      "Code name", FALSE, 10 },
+    { OSINFO_ENTITY_PROP_ID,
+      "ID", TRUE, 40 },
+    { NULL, NULL, 0 },
+};
+
+static struct OsinfoLabel device_labels[] = {
+    { OSINFO_DEVICE_PROP_VENDOR,
+      "Vendor", TRUE, 20 },
+    { OSINFO_DEVICE_PROP_VENDOR_ID,
+      "Vendor ID", TRUE, 12 },
+    { OSINFO_DEVICE_PROP_PRODUCT,
+      "Product", TRUE, 20 },
+    { OSINFO_DEVICE_PROP_PRODUCT_ID,
+      "Product ID", TRUE, 12 },
+    { OSINFO_PRODUCT_PROP_NAME,
+      "Name", TRUE, 14 },
+    { OSINFO_DEVICE_PROP_CLASS,
+      "Class", TRUE, 15 },
+    { OSINFO_DEVICE_PROP_BUS_TYPE,
+      "Bus", TRUE, 8 },
+    { OSINFO_ENTITY_PROP_ID,
+      "ID", TRUE, 40 },
+    { NULL, NULL, 0 },
+};
+
+static struct OsinfoLabel deployment_labels[] = {
+    { OSINFO_ENTITY_PROP_ID,
+      "ID", TRUE, 40 },
+    { NULL, NULL, 0 },
+};
+
+
+static gboolean toggle_fields(struct OsinfoLabel *labels,
+                              const gchar *fieldStr,
+                              GError **error)
+{
+    gboolean ret = FALSE;
+    gchar **fields;
+    gsize i, j;
+
+    if (!fieldStr)
+        return TRUE;
+
+    fields = g_strsplit(fieldStr, ",", 0);
+
+    for (j = 0 ; labels[j].prop ; j++) {
+        labels[j].enabled = FALSE;
+    }
+
+    for (i = 0 ; fields[i] != NULL ; i++) {
+        gboolean found = FALSE;
+        for (j = 0 ; labels[j].prop ; j++) {
+            if (g_str_equal(fields[i], labels[j].prop)) {
+                labels[j].enabled = TRUE;
+                found = TRUE;
+            }
+        }
+        if (!found) {
+            g_set_error(error, 0, 0,
+                        "Unknown property name %s", fields[i]);
+            goto cleanup;
+        }
+    }
+
+
+    ret = TRUE;
+
+ cleanup:
+    g_strfreev(fields);
+    return ret;
+}
+
+static gboolean build_filter(struct OsinfoLabel *labels,
+                             OsinfoFilter *filter,
+                             gint argc, char **argv,
+                             GError **error)
+{
+    gboolean ret = FALSE;
+    gsize i, j;
+
+    for (i = 0 ; i < argc ; i++) {
+        const gchar *tmp = strchr(argv[i], '=');
+        if (!tmp) {
+            g_set_error(error, 0, 0, "%s", "Syntax error in condition, expecting KEY=VALUE");
+            goto cleanup;
+        }
+        gchar *key = g_strndup(argv[i], tmp-argv[i]);
+        gchar *val = g_strdup(tmp+1);
+        gboolean found = FALSE;
+
+        for (j = 0 ; labels[j].prop != NULL ; j++) {
+            if (g_str_equal(key, labels[j].prop))
+                found = TRUE;
+        }
+
+        if (!found) {
+            g_set_error(error, 0, 0,
+                        "Unknown property name %s", key);
+            goto cleanup;
+        }
+
+        osinfo_filter_add_constraint(filter, key, val);
+        g_free(key);
+        g_free(val);
+    }
+
+    ret = TRUE;
+ cleanup:
+    return ret;
+}
+
+
+static gint sort_entity(gconstpointer a,
+                        gconstpointer b,
+                        gpointer data)
+{
+    OsinfoEntity *entityA = OSINFO_ENTITY(a);
+    OsinfoEntity *entityB = OSINFO_ENTITY(b);
+    gchar *key = data;
+    const gchar *valA;
+    const gchar *valB;
+
+    valA = osinfo_entity_get_param_value(entityA, key);
+    valB = osinfo_entity_get_param_value(entityB, key);
+
+    if (!valA && !valB)
+        return 0;
+
+    if (!valA && valB)
+        return 1;
+
+    if (valA && !valB)
+        return 1;
+
+    return strcmp(valA, valB);
+}
+
+
+static gboolean print_entity_text(OsinfoEntity *entity,
+                                  const struct OsinfoLabel *labels)
+{
+    gsize i;
+    gboolean first = TRUE;
+    for (i = 0 ; labels[i].prop != NULL ; i++) {
+        gsize pad;
+        gchar *padstr;
+        const gchar *val = osinfo_entity_get_param_value(entity, labels[i].prop);
+        if (!labels[i].enabled)
+            continue;
+
+        if (first)
+            g_print(" ");
+        else
+            g_print(" | ");
+        first = FALSE;
+
+        if (val && (strlen(val) > labels[i].width))
+            pad = 0;
+        else
+            pad = labels[i].width - (val ? strlen(val) : 0);
+
+        padstr = g_new0(gchar, pad+1);
+        memset(padstr, ' ', pad);
+        padstr[pad] = '\0';
+
+        g_print("%s%s",
+                val ? val : "", padstr);
+        g_free(padstr);
+    }
+    g_print("\n");
+
+    return FALSE;
+}
+
+static gboolean print_results_text(OsinfoList *list,
+                                   const struct OsinfoLabel *labels,
+                                   const gchar *sortKey)
+{
+    gboolean ret = FALSE;
+    GList *entities = osinfo_list_get_elements(list);
+    GList *tmp;
+    gsize i;
+    gboolean first = TRUE;
+
+    tmp = entities = g_list_sort_with_data(entities, sort_entity,
+                                           (gchar*)(sortKey ? sortKey :
+                                                    labels[0].prop));
+
+    for (i = 0 ; labels[i].prop != NULL ; i++) {
+        gsize pad;
+        gchar *padstr;
+        if (!labels[i].enabled)
+            continue;
+
+        if (first)
+            g_print(" ");
+        else
+            g_print(" | ");
+        first = FALSE;
+
+        if (strlen(labels[i].label) > labels[i].width)
+            pad = 0;
+        else
+            pad = labels[i].width - strlen(labels[i].label);
+
+        padstr = g_new0(gchar, pad+1);
+        memset(padstr, ' ', pad);
+        padstr[pad] = '\0';
+
+        g_print("%s%s",
+                labels[i].label, padstr);
+        g_free(padstr);
+    }
+    g_print("\n");
+
+    first = TRUE;
+    for (i = 0 ; labels[i].prop != NULL ; i++) {
+        gchar *padstr;
+        if (!labels[i].enabled)
+            continue;
+
+        if (first)
+            g_print("-");
+        else
+            g_print("-+-");
+        first = FALSE;
+
+        padstr = g_new0(gchar, labels[i].width+1);
+        memset(padstr, '-', labels[i].width);
+        padstr[labels[i].width] = '\0';
+
+        g_print("%s", padstr);
+        g_free(padstr);
+    }
+    g_print("\n");
+
+    while (tmp) {
+        OsinfoEntity *entity = OSINFO_ENTITY(tmp->data);
+
+        print_entity_text(entity, labels);
+        tmp = tmp->next;
+    }
+
+
+    g_list_free(entities);
+    ret = TRUE;
+    // cleanup:
+    return ret;
+}
+
+gint main(gint argc, gchar **argv)
+{
+    GOptionContext *context;
+    GError *error = NULL;
+    gint ret = EXIT_FAILURE;
+    const gchar *type = NULL;
+    OsinfoLoader *loader = NULL;
+    OsinfoDb *db = NULL;
+    OsinfoList *entities = NULL;
+    OsinfoFilter *filter = NULL;
+    OsinfoList *results = NULL;
+    struct OsinfoLabel *labels = NULL;
+    gsize i;
+    const gchar *sortKey = NULL;
+    const gchar *fields = NULL;
+
+    g_type_init();
+
+    struct OsinfoType types[] = {
+        { "os",
+          (osinfo_list_func)osinfo_db_get_os_list,
+          OSINFO_TYPE_OS,
+          OSINFO_TYPE_PRODUCTFILTER,
+          OSINFO_TYPE_OSLIST,
+          os_labels },
+        { "platform",
+          (osinfo_list_func)osinfo_db_get_platform_list,
+          OSINFO_TYPE_PLATFORM,
+          OSINFO_TYPE_PRODUCTFILTER,
+          OSINFO_TYPE_PLATFORMLIST,
+          platform_labels },
+        { "device",
+          (osinfo_list_func)osinfo_db_get_device_list,
+          OSINFO_TYPE_DEVICE,
+          OSINFO_TYPE_FILTER,
+          OSINFO_TYPE_DEVICELIST,
+          device_labels },
+        { "deployment",
+          (osinfo_list_func)osinfo_db_get_deployment_list,
+          OSINFO_TYPE_DEPLOYMENT,
+          OSINFO_TYPE_FILTER,
+          OSINFO_TYPE_DEPLOYMENTLIST,
+          deployment_labels },
+    };
+
+    GOptionEntry entries[] = {
+        { "sort", 's', 0, G_OPTION_ARG_STRING, &sortKey,
+          "Sort column", NULL },
+        { "fields", 'f', 0, G_OPTION_ARG_STRING, &fields,
+          "Display fields", NULL },
+        { NULL, 0, 0, 0, NULL, NULL, NULL }
+    };
+
+
+    context = g_option_context_new("- Query the OS info database");
+
+    g_option_context_add_main_entries(context, entries, NULL);
+
+    if (!g_option_context_parse(context, &argc, &argv, &error)) {
+        g_printerr("Error while parsing options: %s\n", error->message);
+        g_printerr("%s\n", g_option_context_get_help(context, FALSE, NULL));
+        goto error;
+    }
+
+    if (argc < 2) {
+        g_printerr("Missing data type parameter\n");
+        goto error;
+    }
+
+    type = argv[1];
+
+    loader = osinfo_loader_new();
+    osinfo_loader_process_default_path(loader, &error);
+    if (error != NULL) {
+        g_printerr("Error loading OS data: %s\n", error->message);
+        goto error;
+    }
+
+    db = osinfo_loader_get_db(loader);
+
+
+    for (i = 0 ; i < (sizeof(types)/sizeof(types[0])) ; i++) {
+        if (g_str_equal(types[i].name, type)) {
+            entities = types[i].listFunc(db);
+            filter = g_object_new(types[i].filterType, NULL);
+            results = g_object_new(types[i].listType,
+                                   "element-type", types[i].entityType,
+                                   NULL);
+            labels = types[i].labels;
+        }
+    }
+
+    if (!entities) {
+        g_printerr("Unknown type '%s' requested\n", type);
+        goto error;
+    }
+
+    if (!build_filter(labels, filter, argc-2, argv+2, &error)) {
+        g_printerr("Unable to construct filter: %s\n", error->message);
+        goto error;
+    }
+
+    if (!toggle_fields(labels, fields, &error)) {
+        g_printerr("Unable to set field visibility: %s\n", error->message);
+        goto error;
+    }
+
+    osinfo_list_add_filtered(results, entities, filter);
+
+    print_results_text(results, labels, sortKey);
+
+    ret = EXIT_SUCCESS;
+
+ error:
+    g_clear_error(&error);
+    g_option_context_free(context);
+
+    if (entities)
+        g_object_unref(entities);
+    if (filter)
+        g_object_unref(filter);
+    if (results)
+        g_object_unref(results);
+    if (loader)
+        g_object_unref(loader);
+
+    return ret;
+}
+
+/*
+=pod
+
+=head1 NAME
+
+osinfo-query - Query information in the database
+
+=head1 SYNOPSIS
+
+osinfo-query [OPTIONS...] TYPE [CONDITION-1 [CONDITION-2 ...]]
+
+=head1 DESCRIPTION
+
+The C<osinfo-query> command allows extraction of information from the
+database. B<TYPE> can be one of C<os>, C<platform>, C<device>, or
+C<deployment>. With no conditions specified, all entities of the given
+type will be listed.
+
+  # List all operating systems
+  $ osinfo-query os
+   Short ID             | Name       ...
+  ----------------------+-----------
+   centos-6.0           | CentOS 6.0 ...
+   centos-6.1           | CentOS 6.1 ...
+   ...
+
+Conditions allow filtering based on specific properties of an entity.
+For example, to filter only distros from the Fedora Project, use
+
+  # List all operating systems
+  $ osinfo-query os vendor="Fedora Project"
+   Short ID             | Name          ...
+  ----------------------+--------------
+   fedora1              | Fedora Core 1 ...
+   fedora2              | Fedora Core 2 ...
+   ...
+
+The set of fields which are printed can be controlled using the C<--fields>
+command line argument:
+
+  # List all operating systems
+  $ osinfo-query --fields=short-id,version os vendor="Fedora Project"
+   Short ID             | Version
+  ----------------------+----------
+   fedora1              | 1
+   fedora2              | 2
+   ...
+
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-s PROPERTY>, B<--sort-key PROPERTY>
+
+Set the data sorting key. Defaults sorting the first column
+
+=item B<-f PROPERTY1,PROPERTY2,...>, B<--fields PROPERTY1,PROPERTY2,...>
+
+Set the visibility of properties in output
+
+=back
+
+=head1 PROPERTY NAMES
+
+=head2 OS
+
+Valid property names for the C<os> type are:
+
+=over 4
+
+=item B<short-id>
+
+The short OS identifier
+
+=item B<name>
+
+The long OS name
+
+=item B<version>
+
+The OS version string
+
+=item B<family>
+
+The OS kernel family
+
+=item B<vendor>
+
+The OS vendor
+
+=item B<release-date>
+
+The OS release date
+
+=item B<eol-date>
+
+The OS end-of-life date
+
+=item B<codename>
+
+The OS code name
+
+=item B<id>
+
+The OS identifier
+
+=back
+
+
+=head2 PLATFORM
+
+Valid property names for the C<platform> type are:
+
+=over 4
+
+=item B<short-id>
+
+The short platform identifier
+
+=item B<name>
+
+The long platform name
+
+=item B<version>
+
+The platform version string
+
+=item B<vendor>
+
+The platform vendor
+
+=item B<release-date>
+
+The platform release date
+
+=item B<eol-date>
+
+The platform end-of-life date
+
+=item B<codename>
+
+The platform code name
+
+=item B<id>
+
+The platform identifier
+
+=back
+
+
+=head2 DEVICE
+
+Valid property names for the C<device> type are:
+
+=over 4
+
+=item B<name>
+
+The device name
+
+=item B<product>
+
+The device product name
+
+=item B<product-id>
+
+The device product ID string
+
+=item B<vendor>
+
+The device vendor name
+
+=item B<vendor-id>
+
+The device vendor ID string
+
+=item B<class>
+
+The device type class
+
+=item B<bus>
+
+The device bus type
+
+=item B<id>
+
+The device identifier
+
+=back
+
+
+=head2 DEPLOYMENT
+
+Valid property names for the C<deployment> type are:
+
+=over 4
+
+=item B<id>
+
+The deployment identifier
+
+=back
+
+
+=head1 EXIT STATUS
+
+The exit status will be 0 if matching entries were found,
+or 1 if not matches were found
+
+=head1 SEE ALSO
+
+C<osinfo-db-validate(1)>, C<osinfo-detect(1)>
+
+=head1 AUTHORS
+
+Daniel P. Berrange <berrange at redhat.com>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 Red Hat, Inc.
+
+=head1 LICENSE
+
+C<osinfo-query> is distributed under the termsof the GNU LGPL v2+
+license. This is free software; see the source for copying conditions.
+There is NO warranty; not even for MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE
+
+=cut
+*/
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ */
-- 
1.7.7.6




More information about the virt-tools-list mailing list