diff options
author | Christophe Fergeau <cfergeau@redhat.com> | 2013-03-19 23:11:47 +0100 |
---|---|---|
committer | Christophe Fergeau <cfergeau@redhat.com> | 2013-04-01 21:15:25 +0200 |
commit | c579f979e2921bf24ecaef4c2ef108fc6c56ea08 (patch) | |
tree | 5b990d65ddea61c87e7b98c18da53b8cf0482c43 | |
parent | 8a9c4128bec29757c44c4c8d7ef29b19b024476e (diff) |
virtxml: Add GVirDesigner structure
Experiments to see if I can get a high-level API to generate
VMs...
-rw-r--r-- | examples/Makefile.am | 5 | ||||
-rw-r--r-- | examples/virtoptions.c | 818 | ||||
-rw-r--r-- | examples/virtoptions.h | 66 | ||||
-rw-r--r-- | examples/virtxml.c | 582 |
4 files changed, 904 insertions, 567 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am index ae2667e..71f029e 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -18,6 +18,11 @@ virtxml_LDFLAGS = \ $(LIBVIRT_GCONFIG_LIBS) \ $(LIBVIRT_GOBJECT_LIBS) +virtxml_SOURCES = \ + virtoptions.c \ + virtoptions.h \ + virtxml.c + POD2MAN = pod2man -c "Virtualization Support" -r "$(PACKAGE)-$(VERSION)" %.1: %.c Makefile diff --git a/examples/virtoptions.c b/examples/virtoptions.c new file mode 100644 index 0000000..9f57945 --- /dev/null +++ b/examples/virtoptions.c @@ -0,0 +1,818 @@ +/* + * virtoptions.c: generate a GvirDesignerDomain from cmdline options + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Christophe Fergeau <cfergeau@redhat.com> + * Author: Michal Privoznik <mprivozn@redhat.com> + */ + +/* FIXME: GVirDesigner is not useful and should be killed in a refactoring, + * it exists because of the way the code evolved from the monolithic + * virtxml.c code + */ +#include <config.h> + +#include <libvirt-gobject/libvirt-gobject.h> + +#include <string.h> +#include <sys/stat.h> + +#include "virtoptions.h" + +OsinfoDb *db = NULL; + +typedef struct _GVirDesigner GVirDesigner; + +static gboolean print_oses(const gchar *option_name, const gchar *value, + gpointer data, GError **error); +static gboolean print_platforms(const gchar *option_name, const gchar *value, + gpointer data, GError **error); +static void add_disk(gpointer data, gpointer user_data); +static gboolean add_disk_str(const gchar *option_name, const gchar *value, + gpointer data, GError **error); +static void add_iface(gpointer data, gpointer user_data); +static gboolean add_iface_str(const gchar *option_name, const gchar *value, + gpointer data, GError **error); +static OsinfoOs *find_os(const gchar *os_str); +static OsinfoOs *find_os_by_short_id(const char *short_id); +static OsinfoOs *guess_os_from_disk(GList *disk_list); +static OsinfoPlatform *find_platform(const char *platform_str); +static OsinfoPlatform *guess_platform_from_connect(GVirConnection *conn); +static OsinfoPlatform *find_platform_by_short_id(const char *short_id); +static gboolean gvir_designer_guess_os (GVirDesigner *designer, + GVirDesignerOptions *options); +static gboolean gvir_designer_guess_platform (GVirDesigner *designer, + GVirDesignerOptions *options); +static gboolean gvir_designer_guess_resources (GVirDesigner *designer, + GVirDesignerOptions *options); +static gboolean gvir_designer_create_domain (GVirDesigner *designer, + GVirDesignerOptions *options, + GError **error); + + +#define SET_OPTION_STR(field_name) \ + static gboolean set_##field_name##_str(const gchar *option_name, \ + const gchar *value, \ + gpointer data, \ + GError **error) \ + { \ + GVirDesignerOptions *options = (GVirDesignerOptions *)data; \ +\ + g_free(options->field_name); \ + options->field_name = g_strdup (value); \ +\ + return TRUE; \ + } + + +struct _GVirDesigner { + GVirConnection *conn; + GVirConfigCapabilities *caps; + OsinfoOs *os; + OsinfoPlatform *platform; + GVirDesignerDomain *domain; +}; + + +static void gvir_designer_free (GVirDesigner *designer) +{ + g_return_if_fail (designer != NULL); + + if (designer->conn != NULL) + g_object_unref (G_OBJECT(designer->conn)); + if (designer->caps != NULL) + g_object_unref (G_OBJECT(designer->caps)); + if (designer->os != NULL) + g_object_unref (G_OBJECT(designer->os)); + if (designer->platform != NULL) + g_object_unref (G_OBJECT(designer->platform)); + if (designer->domain != NULL) + g_object_unref (G_OBJECT(designer->domain)); + + g_free (designer); +} + + +static GVirDesigner *gvir_designer_new (void) +{ + return g_new0 (GVirDesigner, 1); +} + +static GVirDesigner *gvir_designer_new_for_uri (const char *connect_uri, GError **error) +{ + GError *local_error = NULL; + GVirDesigner *designer; + GVirConnection *conn; + GVirConfigCapabilities *caps; + + g_return_val_if_fail (connect_uri != NULL, NULL); + conn = gvir_connection_new(connect_uri); + gvir_connection_open(conn, NULL, &local_error); + if (local_error != NULL) { + g_propagate_error (error, local_error); + return NULL; + } + + caps = gvir_connection_get_capabilities(conn, &local_error); + if (local_error != NULL) { + g_object_unref (G_OBJECT (conn)); + g_propagate_error (error, local_error); + return NULL; + } + + designer = gvir_designer_new (); + designer->conn = conn; + designer->caps = caps; + + return designer; +} + +struct _GVirDesignerOptions { + char *arch; + char *connect_uri; + char *os; + char *platform; + char *resources; + + GList *disks; + GList *net_interfaces; +}; + +SET_OPTION_STR(arch) +SET_OPTION_STR(connect_uri) +SET_OPTION_STR(os) +SET_OPTION_STR(platform) +SET_OPTION_STR(resources) + + +GVirDesignerOptions *gvir_designer_options_new (void) +{ + return g_new0 (GVirDesignerOptions, 1); +} + +void gvir_designer_options_free (GVirDesignerOptions *options) +{ + g_return_if_fail (options != NULL); + + g_free (options->arch); + g_free (options->connect_uri); + g_free (options->os); + g_free (options->platform); + g_free (options->resources); + g_list_free_full (options->disks, g_free); + g_list_free_full (options->net_interfaces, g_free); + g_free (options); +} + +GVirDesignerDomain *gvir_designer_domain_new_from_options (GVirDesignerOptions *options, GError **err) +{ + GVirDesigner *designer; + GVirDesignerDomain *domain; + GError *error = NULL; + + g_return_val_if_fail (options != NULL, NULL); + + designer = gvir_designer_new_for_uri (options->connect_uri, &error); + CHECK_ERROR; + if (designer == NULL) { + print_error("Could not create connection to libvirt daemon"); + goto cleanup; + } + + if (!gvir_designer_guess_os (designer, options)) { + print_error("Operating system could not be found or guessed"); + goto cleanup; + } + + if (!gvir_designer_guess_platform (designer, options)) { + print_error("Platform was not specified or could not be guessed"); + goto cleanup; + } + + gvir_designer_create_domain (designer, options, &error); + CHECK_ERROR; + + if (!gvir_designer_guess_resources (designer, options)) { + print_error("Resources could not be set or guessed"); + goto cleanup; + } + + domain = g_object_ref (G_OBJECT (designer->domain)); + gvir_designer_free (designer); + + return domain; + +cleanup: + if (designer != NULL) + gvir_designer_free (designer); + + if (error != NULL) + g_propagate_error (err, error); + + return NULL; +} + + +GOptionGroup *gvir_designer_options_get_option_group (GVirDesignerOptions *options) +{ + GOptionGroup *option_group; + + static GOptionEntry entries[] = + { + {"connect", 'c', 0, G_OPTION_ARG_CALLBACK, set_connect_uri_str, + "libvirt connection URI used for querying capabilities", "URI"}, + {"list-os", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_oses, + "list IDs of known OSes", NULL}, + {"list-platform", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_platforms, + "list IDs of known hypervisors", NULL}, + {"os", 'o', 0, G_OPTION_ARG_CALLBACK, set_os_str, + "set domain OS", "OS"}, + {"platform", 'p', 0, G_OPTION_ARG_CALLBACK, set_platform_str, + "set hypervisor under which domain will be running", "PLATFORM"}, + {"architecture", 'a', 0, G_OPTION_ARG_CALLBACK, set_arch_str, + "set domain architecture", "ARCH"}, + {"disk", 'd', 0, G_OPTION_ARG_CALLBACK, add_disk_str, + "add disk to domain with PATH being source and FORMAT its format", "PATH[,FORMAT]"}, + {"interface", 'i', 0, G_OPTION_ARG_CALLBACK, add_iface_str, + "add interface with NETWORK source. Possible ARGs: mac, link={up,down}", "NETWORK[,ARG=VAL]"}, + {"resources", 'r', 0, G_OPTION_ARG_CALLBACK, set_resources_str, + "Set minimal or recommended values for cpu count and RAM amount", "{minimal|recommended}"}, + {NULL} + }; + + option_group = g_option_group_new (NULL, NULL, NULL, + options, NULL); + g_option_group_add_entries (option_group, entries); + g_option_group_set_translation_domain (option_group, GETTEXT_PACKAGE); + + return option_group; +} + +static gboolean gvir_designer_guess_os (GVirDesigner *designer, + GVirDesignerOptions *options) +{ + OsinfoOs *os; + + g_return_val_if_fail (designer != NULL, FALSE); + + if (options->os) { + os = find_os(options->os); + if (!os) + os = find_os_by_short_id(options->os); + } else { + os = guess_os_from_disk(options->disks); + } + + if (designer->os != NULL) + g_object_unref (designer->os); + designer->os = os; + + return (os != NULL); +} + +static gboolean gvir_designer_guess_platform (GVirDesigner *designer, + GVirDesignerOptions *options) +{ + OsinfoPlatform *platform; + + g_return_val_if_fail (designer != NULL, FALSE); + + if (options->platform) { + platform = find_platform(options->platform); + if (!platform) + platform = find_platform_by_short_id(options->platform); + } else { + platform = guess_platform_from_connect(designer->conn); + } + + if (designer->platform != NULL) + g_object_unref (designer->platform); + designer->platform = platform; + + return (platform != NULL); +} + +static gboolean gvir_designer_guess_resources (GVirDesigner *designer, + GVirDesignerOptions *options) +{ + GVirDesignerDomainResources resources; + GError *error = NULL; + + g_return_val_if_fail (designer != NULL, FALSE); + + if (options->resources) { + if (g_str_equal(options->resources, "minimal") || + g_str_equal(options->resources, "min")) + resources = GVIR_DESIGNER_DOMAIN_RESOURCES_MINIMAL; + else if (g_str_equal(options->resources, "recommended") || + g_str_equal(options->resources, "rec")) + resources = GVIR_DESIGNER_DOMAIN_RESOURCES_RECOMMENDED; + else { + print_error("Unknown value '%s' for resources", options->resources); + return FALSE; + } + gvir_designer_domain_setup_resources (designer->domain, resources, &error); + if (error != NULL) { + g_clear_error (&error); + return FALSE; + } + } else { + gvir_designer_domain_setup_resources(designer->domain, + GVIR_DESIGNER_DOMAIN_RESOURCES_RECOMMENDED, + &error); + } + + return TRUE; +} + + +static gboolean gvir_designer_create_domain (GVirDesigner *designer, + GVirDesignerOptions *options, + GError **error) +{ + GVirDesignerDomain *domain; + unsigned int i; + GError *local_error = NULL; + + domain = gvir_designer_domain_new(db, + designer->os, + designer->platform, + designer->caps); + gvir_designer_domain_setup_machine(domain, &local_error); + if (local_error != NULL) { + g_object_unref (G_OBJECT(domain)); + g_propagate_error(error, local_error); + return FALSE; + } + g_object_unref(gvir_designer_domain_add_graphics(domain, + GVIR_DESIGNER_DOMAIN_GRAPHICS_SPICE, + &local_error)); + if (local_error != NULL) + goto error; + for (i = 0; i < 4; i++) { + g_object_unref(gvir_designer_domain_add_usb_redir(domain, &local_error)); + if (local_error != NULL) + goto error; + } + g_object_unref(gvir_designer_domain_add_smartcard(domain, &local_error)); + if (local_error != NULL) + goto error; + g_object_unref(gvir_designer_domain_add_video(domain, &local_error)); + if (local_error != NULL) + goto error; + + if (options->arch) { + gvir_designer_domain_setup_container_full(domain, options->arch, &local_error); + g_object_unref (G_OBJECT(domain)); + g_propagate_error(error, local_error); + return FALSE; + } + + g_list_foreach(options->disks, add_disk, domain); + g_list_foreach(options->net_interfaces, add_iface, domain); + + designer->domain = domain; + + return TRUE; + +error: + g_object_unref (G_OBJECT(domain)); + g_propagate_error(error, local_error); + return FALSE; +} + + +static gboolean +load_osinfo(void) +{ + GError *err = NULL; + gboolean ret = FALSE; + OsinfoLoader *loader = NULL; + + loader = osinfo_loader_new(); + osinfo_loader_process_default_path(loader, &err); + if (err) { + print_error("Unable to load default libosinfo DB: %s", err->message); + g_clear_error(&err); + } + + db = osinfo_loader_get_db(loader); + g_object_ref(db); + ret = TRUE; + + g_object_unref(loader); + return ret; +} + +static gint +entity_compare(gconstpointer a, gconstpointer b) +{ + const gchar *id_a = osinfo_entity_get_param_value(OSINFO_ENTITY(a), + OSINFO_ENTITY_PROP_ID); + const gchar *id_b = osinfo_entity_get_param_value(OSINFO_ENTITY(b), + OSINFO_ENTITY_PROP_ID); + return g_strcmp0(id_a, id_b); +} + +static gboolean +print_oses(const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + OsinfoOsList *list = NULL; + GList *oses = NULL; + GList *os_iter; + int ret = EXIT_FAILURE; + + if (!db && !load_osinfo()) + goto cleanup; + + printf(" Operating System ID\n" + "-----------------------\n"); + + list = osinfo_db_get_os_list(db); + if (!list) + goto cleanup; + oses = osinfo_list_get_elements(OSINFO_LIST(list)); + oses = g_list_sort(oses, entity_compare); + for (os_iter = oses; os_iter; os_iter = os_iter->next) { + OsinfoOs *os = OSINFO_OS(os_iter->data); + const char *id = osinfo_entity_get_param_value(OSINFO_ENTITY(os), + OSINFO_ENTITY_PROP_ID); + + printf("%s\n", id); + } + + ret = EXIT_SUCCESS; + +cleanup: + if (list) + g_object_unref(list); + g_list_free(oses); + + exit(ret); + return TRUE; +} + +static gboolean +print_platforms(const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + OsinfoPlatformList *list = NULL; + GList *platforms = NULL; + GList *platform_iter; + int ret = EXIT_FAILURE; + + if (!db && !load_osinfo()) + goto cleanup; + + printf(" Platform ID\n" + "---------------\n"); + + list = osinfo_db_get_platform_list(db); + if (!list) + goto cleanup; + platforms = osinfo_list_get_elements(OSINFO_LIST(list)); + platforms = g_list_sort(platforms, entity_compare); + for (platform_iter = platforms; platform_iter; platform_iter = platform_iter->next) { + OsinfoPlatform *platform = OSINFO_PLATFORM(platform_iter->data); + const char *id = osinfo_entity_get_param_value(OSINFO_ENTITY(platform), + OSINFO_ENTITY_PROP_ID); + + printf("%s\n", id); + } + + ret = EXIT_SUCCESS; + +cleanup: + if (list) + g_object_unref(list); + g_list_free(platforms); + + exit(ret); + return TRUE; +} + +static void +add_disk(gpointer data, + gpointer user_data) +{ + GVirDesignerDomain *domain = (GVirDesignerDomain *) user_data; + GVirConfigDomainDisk *disk; + char *path = (char *) data; + char *format = NULL; + struct stat buf; + GError *error = NULL; + + format = strchr(path, ','); + if (format) { + *format = '\0'; + format++; + } + + if (!path || !strlen(path)) { + print_error("No path provided"); + exit(EXIT_FAILURE); + } + + if (!stat(path, &buf) && + !S_ISREG(buf.st_mode)) { + disk = gvir_designer_domain_add_disk_device(domain, path, &error); + } else { + disk = gvir_designer_domain_add_disk_file(domain, path, format, &error); + } + if (disk) + g_object_unref(G_OBJECT(disk)); + + if (error) { + print_error("%s", error->message); + exit(EXIT_FAILURE); + } +} + +static gboolean +add_disk_str(const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + GVirDesignerOptions *options = (GVirDesignerOptions *)data; + + options->disks = g_list_append(options->disks, g_strdup(value)); + + return TRUE; +} + +static void +add_iface(gpointer data, + gpointer user_data) +{ + GVirDesignerDomain *domain = (GVirDesignerDomain *) user_data; + char *network = (char *) data; + char *param = NULL; + GVirConfigDomainInterface *iface = NULL; + GError *error = NULL; + + param = strchr(network, ','); + if (param) { + *param = '\0'; + param++; + } + + iface = gvir_designer_domain_add_interface_network(domain, network, &error); + if (error) { + print_error("%s", error->message); + exit(EXIT_FAILURE); + } + + while (param && *param) { + char *key = param; + char *val; + GVirConfigDomainInterfaceLinkState link; + + /* move to next token */ + param = strchr(param, ','); + if (param) { + *param = '\0'; + param++; + } + + /* parse token */ + val = strchr(key, '='); + if (!val) { + print_error("Invalid format: %s", key); + exit(EXIT_FAILURE); + } + + *val = '\0'; + val++; + + if (g_str_equal(key, "mac")) { + gvir_config_domain_interface_set_mac(iface, val); + } else if (g_str_equal(key, "link")) { + if (g_str_equal(val, "up")) { + link = GVIR_CONFIG_DOMAIN_INTERFACE_LINK_STATE_UP; + } else if (g_str_equal(val, "down")) { + link = GVIR_CONFIG_DOMAIN_INTERFACE_LINK_STATE_DOWN; + } else { + print_error("Unknown value: %s", val); + exit(EXIT_FAILURE); + } + gvir_config_domain_interface_set_link_state(iface, link); + } else { + print_error("Unknown key: %s", key); + exit(EXIT_FAILURE); + } + } +} + +static gboolean +add_iface_str(const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + GVirDesignerOptions *options = (GVirDesignerOptions *)data; + + options->net_interfaces = g_list_append(options->net_interfaces, + g_strdup(value)); + + return TRUE; +} + +static OsinfoEntity * +find_entity_by_short_id(OsinfoList *ent_list, + const char *short_id) +{ + OsinfoEntity *ret = NULL; + GList *list, *list_iterator; + + list = osinfo_list_get_elements(ent_list); + for (list_iterator = list; list_iterator; list_iterator = list_iterator->next) { + const char *id = osinfo_entity_get_param_value(list_iterator->data, + OSINFO_PRODUCT_PROP_SHORT_ID); + + if (!g_strcmp0(id, short_id)) { + ret = list_iterator->data; + g_object_ref(ret); + break; + } + } + + g_list_free(list); + return ret; +} + +static OsinfoOs * +find_os(const gchar *os_str) +{ + OsinfoOs *ret = NULL; + + if (!db && !load_osinfo()) + return NULL; + + ret = osinfo_db_get_os(db, os_str); + + return ret; +} + +static OsinfoOs * +find_os_by_short_id(const char *short_id) +{ + OsinfoOs *ret = NULL; + OsinfoOsList *oses = NULL; + OsinfoEntity *found = NULL; + + if (!db && !load_osinfo()) + return NULL; + + oses = osinfo_db_get_os_list(db); + + if (!oses) + goto cleanup; + + found = find_entity_by_short_id(OSINFO_LIST(oses), short_id); + + if (!found) + goto cleanup; + ret = OSINFO_OS(found); + +cleanup: + if (oses) + g_object_unref(oses); + return ret; +} + +static OsinfoOs * +guess_os_from_disk(GList *disk_list) +{ + OsinfoOs *ret = NULL; + GList *list_it = NULL; + + if (!db && !load_osinfo()) + return NULL; + + for (list_it = disk_list; list_it; list_it = list_it->next) { + char *path = (char *) list_it->data; + char *sep = strchr(path, ','); + OsinfoMedia *media = NULL; + + if (sep) + path = g_strndup(path, sep-path); + + media = osinfo_media_create_from_location(path, NULL, NULL); + + if (sep) + g_free(path); + + if (!media) + continue; + + if (osinfo_db_identify_media(db, media)) { + g_object_get(G_OBJECT(media), "os", &ret, NULL); + break; + } + } + + return ret; +} + +static OsinfoPlatform * +find_platform(const char *platform_str) +{ + OsinfoPlatform *ret = NULL; + + if (!db && !load_osinfo()) + return NULL; + + ret = osinfo_db_get_platform(db, platform_str); + + return ret; +} + +static OsinfoPlatform * +find_platform_by_short_id(const char *short_id) +{ + OsinfoPlatform *ret = NULL; + OsinfoPlatformList *platforms = NULL; + OsinfoEntity *found = NULL; + + if (!db && !load_osinfo()) + goto cleanup; + + platforms = osinfo_db_get_platform_list(db); + + if (!platforms) + goto cleanup; + + found = find_entity_by_short_id(OSINFO_LIST(platforms), short_id); + + if (!found) + goto cleanup; + + ret = OSINFO_PLATFORM(found); + +cleanup: + if (platforms) + g_object_unref(platforms); + return ret; +} + +static OsinfoPlatform * +guess_platform_from_connect(GVirConnection *conn) +{ + OsinfoPlatform *ret = NULL; + gchar *hv_type = NULL; + gulong ver, major, minor, release; + char *short_id = NULL, *type = NULL; + + hv_type = gvir_connection_get_hypervisor_name(conn, NULL); + ver = gvir_connection_get_version(conn, NULL); + + if (!hv_type || !ver) { + print_error("Unable to get hypervisor and its version"); + exit(EXIT_FAILURE); + } + + /* do some mappings: + * QEMU -> qemu-kvm + * Xen -> xen + */ + type = g_ascii_strdown(hv_type, -1); + if (g_str_equal(type, "qemu") && ver <= 1002000) { + g_free(type); + type = g_strdup("qemu-kvm"); + } + + major = ver / 1000000; + ver %= 1000000; + minor = ver / 1000; + release = ver % 1000 ; + + short_id = g_strdup_printf("%s-%lu.%lu.%lu", type, major, minor, release); + + ret = find_platform_by_short_id(short_id); + + g_free(short_id); + g_free(hv_type); + g_free(type); + return ret; +} diff --git a/examples/virtoptions.h b/examples/virtoptions.h new file mode 100644 index 0000000..f14b04f --- /dev/null +++ b/examples/virtoptions.h @@ -0,0 +1,66 @@ +/* + * virtxml.c: produce an domain XML + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Christophe Fergeau <cfergeau@redhat.com> + */ + + +#ifndef __VIRT_OPTIONS_H__ +#define __VIRT_OPTIONS_H__ + +#include <libvirt-designer/libvirt-designer.h> + +typedef struct _GVirDesignerOptions GVirDesignerOptions; + +GVirDesignerDomain *gvir_designer_domain_new_from_options (GVirDesignerOptions *options, GError **err); +GVirDesignerOptions *gvir_designer_options_new (void); +void gvir_designer_options_free (GVirDesignerOptions *options); +GOptionGroup *gvir_designer_options_get_option_group (GVirDesignerOptions *options); + + +#define print_error(...) \ + print_error_impl(__FUNCTION__, __LINE__, __VA_ARGS__) + +#define CHECK_ERROR \ + if (error) { \ + print_error("%s", error->message); \ + goto cleanup; \ + } + +static void print_error_impl(const char *funcname, + size_t linenr, + const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); + +static void +print_error_impl(const char *funcname, + size_t linenr, + const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "Error in %s:%zu ", funcname, linenr); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr,"\n"); +} + + +#endif /* __VIRT_OPTIONS_H__ */ diff --git a/examples/virtxml.c b/examples/virtxml.c index a7a1a0f..ab29ce2 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -27,589 +27,43 @@ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> -#include <string.h> #include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> #include <glib/gprintf.h> -GList *disk_str_list = NULL; -GList *iface_str_list = NULL; -OsinfoDb *db = NULL; - -#define print_error(...) \ - print_error_impl(__FUNCTION__, __LINE__, __VA_ARGS__) - -static void -print_error_impl(const char *funcname, - size_t linenr, - const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); - -static void -print_error_impl(const char *funcname, - size_t linenr, - const char *fmt, ...) -{ - va_list args; - - fprintf(stderr, "Error in %s:%zu ", funcname, linenr); - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr,"\n"); -} - -static gboolean -load_osinfo(void) -{ - GError *err = NULL; - gboolean ret = FALSE; - OsinfoLoader *loader = NULL; - - loader = osinfo_loader_new(); - osinfo_loader_process_default_path(loader, &err); - if (err) { - print_error("Unable to load default libosinfo DB: %s", err->message); - g_clear_error(&err); - } - - db = osinfo_loader_get_db(loader); - g_object_ref(db); - ret = TRUE; - - g_object_unref(loader); - return ret; -} - -static gint -entity_compare(gconstpointer a, gconstpointer b) -{ - const gchar *id_a = osinfo_entity_get_param_value(OSINFO_ENTITY(a), - OSINFO_ENTITY_PROP_ID); - const gchar *id_b = osinfo_entity_get_param_value(OSINFO_ENTITY(b), - OSINFO_ENTITY_PROP_ID); - return g_strcmp0(id_a, id_b); -} - -static gboolean -print_oses(const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - OsinfoOsList *list = NULL; - GList *oses = NULL; - GList *os_iter; - int ret = EXIT_FAILURE; - - if (!db && !load_osinfo()) - goto cleanup; - - printf(" Operating System ID\n" - "-----------------------\n"); - - list = osinfo_db_get_os_list(db); - if (!list) - goto cleanup; - oses = osinfo_list_get_elements(OSINFO_LIST(list)); - oses = g_list_sort(oses, entity_compare); - for (os_iter = oses; os_iter; os_iter = os_iter->next) { - OsinfoOs *os = OSINFO_OS(os_iter->data); - const char *id = osinfo_entity_get_param_value(OSINFO_ENTITY(os), - OSINFO_ENTITY_PROP_ID); - - printf("%s\n", id); - } - - ret = EXIT_SUCCESS; - -cleanup: - if (list) - g_object_unref(list); - g_list_free(oses); - - exit(ret); - return TRUE; -} - -static gboolean -print_platforms(const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - OsinfoPlatformList *list = NULL; - GList *platforms = NULL; - GList *platform_iter; - int ret = EXIT_FAILURE; - - if (!db && !load_osinfo()) - goto cleanup; - - printf(" Platform ID\n" - "---------------\n"); - - list = osinfo_db_get_platform_list(db); - if (!list) - goto cleanup; - platforms = osinfo_list_get_elements(OSINFO_LIST(list)); - platforms = g_list_sort(platforms, entity_compare); - for (platform_iter = platforms; platform_iter; platform_iter = platform_iter->next) { - OsinfoPlatform *platform = OSINFO_PLATFORM(platform_iter->data); - const char *id = osinfo_entity_get_param_value(OSINFO_ENTITY(platform), - OSINFO_ENTITY_PROP_ID); - - printf("%s\n", id); - } - - ret = EXIT_SUCCESS; - -cleanup: - if (list) - g_object_unref(list); - g_list_free(platforms); - - exit(ret); - return TRUE; -} - -static void -add_disk(gpointer data, - gpointer user_data) -{ - GVirDesignerDomain *domain = (GVirDesignerDomain *) user_data; - GVirConfigDomainDisk *disk; - char *path = (char *) data; - char *format = NULL; - struct stat buf; - GError *error = NULL; - - format = strchr(path, ','); - if (format) { - *format = '\0'; - format++; - } - - if (!path || !strlen(path)) { - print_error("No path provided"); - exit(EXIT_FAILURE); - } - - if (!stat(path, &buf) && - !S_ISREG(buf.st_mode)) { - disk = gvir_designer_domain_add_disk_device(domain, path, &error); - } else { - disk = gvir_designer_domain_add_disk_file(domain, path, format, &error); - } - if (disk) - g_object_unref(G_OBJECT(disk)); - - if (error) { - print_error("%s", error->message); - exit(EXIT_FAILURE); - } -} - -static gboolean -add_disk_str(const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - disk_str_list = g_list_append(disk_str_list, g_strdup(value)); - return TRUE; -} - -static void -add_iface(gpointer data, - gpointer user_data) -{ - GVirDesignerDomain *domain = (GVirDesignerDomain *) user_data; - char *network = (char *) data; - char *param = NULL; - GVirConfigDomainInterface *iface = NULL; - GError *error = NULL; - - param = strchr(network, ','); - if (param) { - *param = '\0'; - param++; - } - - iface = gvir_designer_domain_add_interface_network(domain, network, &error); - if (error) { - print_error("%s", error->message); - exit(EXIT_FAILURE); - } - - while (param && *param) { - char *key = param; - char *val; - GVirConfigDomainInterfaceLinkState link; - - /* move to next token */ - param = strchr(param, ','); - if (param) { - *param = '\0'; - param++; - } - - /* parse token */ - val = strchr(key, '='); - if (!val) { - print_error("Invalid format: %s", key); - exit(EXIT_FAILURE); - } - - *val = '\0'; - val++; - - if (g_str_equal(key, "mac")) { - gvir_config_domain_interface_set_mac(iface, val); - } else if (g_str_equal(key, "link")) { - if (g_str_equal(val, "up")) { - link = GVIR_CONFIG_DOMAIN_INTERFACE_LINK_STATE_UP; - } else if (g_str_equal(val, "down")) { - link = GVIR_CONFIG_DOMAIN_INTERFACE_LINK_STATE_DOWN; - } else { - print_error("Unknown value: %s", val); - exit(EXIT_FAILURE); - } - gvir_config_domain_interface_set_link_state(iface, link); - } else { - print_error("Unknown key: %s", key); - exit(EXIT_FAILURE); - } - } -} - -static gboolean -add_iface_str(const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - iface_str_list = g_list_append(iface_str_list, g_strdup(value)); - return TRUE; -} - -static OsinfoEntity * -find_entity_by_short_id(OsinfoList *ent_list, - const char *short_id) -{ - OsinfoEntity *ret = NULL; - GList *list, *list_iterator; - - list = osinfo_list_get_elements(ent_list); - for (list_iterator = list; list_iterator; list_iterator = list_iterator->next) { - const char *id = osinfo_entity_get_param_value(list_iterator->data, - OSINFO_PRODUCT_PROP_SHORT_ID); - - if (!g_strcmp0(id, short_id)) { - ret = list_iterator->data; - g_object_ref(ret); - break; - } - } - g_list_free(list); - - return ret; -} - -static OsinfoOs * -find_os(const gchar *os_str) -{ - OsinfoOs *ret = NULL; - - if (!db && !load_osinfo()) - return NULL; - - ret = osinfo_db_get_os(db, os_str); - - return ret; -} - -static OsinfoOs * -find_os_by_short_id(const char *short_id) -{ - OsinfoOs *ret = NULL; - OsinfoOsList *oses = NULL; - OsinfoEntity *found = NULL; - - if (!db && !load_osinfo()) - return NULL; - - oses = osinfo_db_get_os_list(db); - - if (!oses) - goto cleanup; - - found = find_entity_by_short_id(OSINFO_LIST(oses), short_id); - - if (!found) - goto cleanup; - ret = OSINFO_OS(found); - -cleanup: - if (oses) - g_object_unref(oses); - return ret; -} - -static OsinfoOs * -guess_os_from_disk(GList *disk_list) -{ - OsinfoOs *ret = NULL; - GList *list_it = NULL; - - if (!db && !load_osinfo()) - return NULL; - - for (list_it = disk_list; list_it; list_it = list_it->next) { - char *path = (char *) list_it->data; - char *sep = strchr(path, ','); - OsinfoMedia *media = NULL; - - if (sep) - path = g_strndup(path, sep-path); - - media = osinfo_media_create_from_location(path, NULL, NULL); - - if (sep) - g_free(path); - - if (!media) - continue; - - if (osinfo_db_identify_media(db, media)) { - g_object_get(G_OBJECT(media), "os", &ret, NULL); - break; - } - } - - return ret; -} - -static OsinfoPlatform * -find_platform(const char *platform_str) -{ - OsinfoPlatform *ret = NULL; - - if (!db && !load_osinfo()) - return NULL; - - ret = osinfo_db_get_platform(db, platform_str); - - return ret; -} - -static OsinfoPlatform * -find_platform_by_short_id(const char *short_id) -{ - OsinfoPlatform *ret = NULL; - OsinfoPlatformList *platforms = NULL; - OsinfoEntity *found = NULL; - - if (!db && !load_osinfo()) - goto cleanup; - - platforms = osinfo_db_get_platform_list(db); - - if (!platforms) - goto cleanup; - - found = find_entity_by_short_id(OSINFO_LIST(platforms), short_id); - - if (!found) - goto cleanup; - - ret = OSINFO_PLATFORM(found); - -cleanup: - if (platforms) - g_object_unref(platforms); - return ret; -} - -static OsinfoPlatform * -guess_platform_from_connect(GVirConnection *conn) -{ - OsinfoPlatform *ret = NULL; - gchar *hv_type = NULL; - gulong ver, major, minor, release; - char *short_id = NULL, *type = NULL; - - hv_type = gvir_connection_get_hypervisor_name(conn, NULL); - ver = gvir_connection_get_version(conn, NULL); - - if (!hv_type || !ver) { - print_error("Unable to get hypervisor and its version"); - exit(EXIT_FAILURE); - } - - /* do some mappings: - * QEMU -> qemu-kvm - * Xen -> xen - */ - type = g_ascii_strdown(hv_type, -1); - if (g_str_equal(type, "qemu") && ver <= 1002000) { - g_free(type); - type = g_strdup("qemu-kvm"); - } - - major = ver / 1000000; - ver %= 1000000; - minor = ver / 1000; - release = ver % 1000 ; - - short_id = g_strdup_printf("%s-%lu.%lu.%lu", type, major, minor, release); - - ret = find_platform_by_short_id(short_id); - - g_free(short_id); - g_free(hv_type); - g_free(type); - return ret; -} - -#define CHECK_ERROR \ - if (error) { \ - print_error("%s", error->message); \ - goto cleanup; \ - } +#include "virtoptions.h" int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; GError *error = NULL; - OsinfoOs *os = NULL; - OsinfoPlatform *platform = NULL; - GVirConfigCapabilities *caps = NULL; GVirConfigDomain *config = NULL; - GVirDesignerDomain *domain = NULL; - GVirConnection *conn = NULL; gchar *xml = NULL; - static char *os_str = NULL; - static char *platform_str = NULL; - static char *arch_str = NULL; - static char *connect_uri = NULL; - static char *resources_str = NULL; - GVirDesignerDomainResources resources; GOptionContext *context = NULL; - unsigned int i; - - static GOptionEntry entries[] = - { - {"connect", 'c', 0, G_OPTION_ARG_STRING, &connect_uri, - "libvirt connection URI used for querying capabilities", "URI"}, - {"list-os", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_oses, - "list IDs of known OSes", NULL}, - {"list-platform", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_platforms, - "list IDs of known hypervisors", NULL}, - {"os", 'o', 0, G_OPTION_ARG_STRING, &os_str, - "set domain OS", "OS"}, - {"platform", 'p', 0, G_OPTION_ARG_STRING, &platform_str, - "set hypervisor under which domain will be running", "PLATFORM"}, - {"architecture", 'a', 0, G_OPTION_ARG_STRING, &arch_str, - "set domain architecture", "ARCH"}, - {"disk", 'd', 0, G_OPTION_ARG_CALLBACK, add_disk_str, - "add disk to domain with PATH being source and FORMAT its format", "PATH[,FORMAT]"}, - {"interface", 'i', 0, G_OPTION_ARG_CALLBACK, add_iface_str, - "add interface with NETWORK source. Possible ARGs: mac, link={up,down}", "NETWORK[,ARG=VAL]"}, - {"resources", 'r', 0, G_OPTION_ARG_STRING, &resources_str, - "Set minimal or recommended values for cpu count and RAM amount", "{minimal|recommended}"}, - {NULL} - }; + GOptionGroup *group; + GVirDesignerOptions *options = NULL; + GVirDesignerDomain *domain = NULL; if (!gvir_designer_init_check(&argc, &argv, NULL)) - return EXIT_FAILURE; + goto cleanup; - context = g_option_context_new ("- test tree model performance"); - g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + options = gvir_designer_options_new(); + group = gvir_designer_options_get_option_group(options); + context = g_option_context_new("- test tree model performance"); + g_option_context_set_main_group(context, group); if (!g_option_context_parse(context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); - return EXIT_FAILURE; - } - - conn = gvir_connection_new(connect_uri); - gvir_connection_open(conn, NULL, &error); - CHECK_ERROR; - - caps = gvir_connection_get_capabilities(conn, &error); - CHECK_ERROR; - - if (os_str) { - os = find_os(os_str); - if (!os) - os = find_os_by_short_id(os_str); - } else { - os = guess_os_from_disk(disk_str_list); - } - - if (!os) { - print_error("Operating system could not be find or guessed"); - goto cleanup; - } - - if (platform_str) { - platform = find_platform(platform_str); - if (!platform) - platform = find_platform_by_short_id(platform_str); - } else { - platform = guess_platform_from_connect(conn); - } - - if (!platform) { - print_error("Platform was not specified or could not be guessed"); goto cleanup; } - domain = gvir_designer_domain_new(db, os, platform, caps); - - gvir_designer_domain_setup_machine(domain, &error); - CHECK_ERROR; - g_object_unref(gvir_designer_domain_add_graphics(domain, - GVIR_DESIGNER_DOMAIN_GRAPHICS_SPICE, - &error)); - CHECK_ERROR; - for (i = 0; i < 4; i++) { - g_object_unref(gvir_designer_domain_add_usb_redir(domain, &error)); - CHECK_ERROR; - } - g_object_unref(gvir_designer_domain_add_smartcard(domain, &error)); - CHECK_ERROR; - g_object_unref(gvir_designer_domain_add_video(domain, &error)); + domain = gvir_designer_domain_new_from_options(options, &error); CHECK_ERROR; - - if (arch_str) { - gvir_designer_domain_setup_container_full(domain, arch_str, &error); - CHECK_ERROR; - } - - if (resources_str) { - if (g_str_equal(resources_str, "minimal") || - g_str_equal(resources_str, "min")) - resources = GVIR_DESIGNER_DOMAIN_RESOURCES_MINIMAL; - else if (g_str_equal(resources_str, "recommended") || - g_str_equal(resources_str, "rec")) - resources = GVIR_DESIGNER_DOMAIN_RESOURCES_RECOMMENDED; - else { - print_error("Unknown value '%s' for resources", resources_str); - goto cleanup; - } - gvir_designer_domain_setup_resources(domain, resources, &error); - CHECK_ERROR; - } else { - gvir_designer_domain_setup_resources(domain, - GVIR_DESIGNER_DOMAIN_RESOURCES_RECOMMENDED, - NULL); + if (domain == NULL) { + print_error ("Failed to create domain from commandline options"); + goto cleanup; } - g_list_foreach(disk_str_list, add_disk, domain); - - g_list_foreach(iface_str_list, add_iface, domain); - config = gvir_designer_domain_get_config(domain); xml = gvir_config_object_to_xml(GVIR_CONFIG_OBJECT(config)); @@ -621,16 +75,10 @@ main(int argc, char *argv[]) cleanup: if (context) g_option_context_free(context); - if (os) - g_object_unref(G_OBJECT(os)); - if (platform) - g_object_unref(G_OBJECT(platform)); - if (caps) - g_object_unref(G_OBJECT(caps)); + if (options) + gvir_designer_options_free(options); if (domain) g_object_unref(G_OBJECT(domain)); - if (conn) - gvir_connection_close(conn); return ret; } |