[libvirt] [libvirt-designer PATCHv4 0/9] Handle more devices in GVirDesignerDomain

Hey, Here is a new version of that series with the issues raised by Michal addressed. Changes since v3: - don't automatically add USB/smartcard/graphics devices, but add options to virtxml to explicitly request those - return an error when gvir_designer_domain_add_graphics() fails to automatically add the spice agent channel - set a GError when gvir_designer_domain_get_fallback_devices() could not find any device. Christophe

Add various devices/configuration to libvirt XML config when creating the VM. This configuration is generic enough that it should be useful on all created VMs, that's why no public API is added to set them up. However, they are split in several helpers that can easily be exported if needed. What this commit adds is: - clock - input device (a tablet which will act as a mouse) - a console (Boxes is adding one, not sure if it's required...) For maximum flexibility, we may want to let applications decide whether they want a console or not. In addition to these devices, gvir_designer_domain_setup_guest() now disables power management, and sets the domain to stop on power off/crash, and to restart on reboots. --- libvirt-designer/libvirt-designer-domain.c | 95 ++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index 2d4204f..e4f72e6 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -313,6 +313,95 @@ end: } +static void gvir_designer_domain_add_clock(GVirDesignerDomain *design) +{ + GVirConfigDomainClock *clock; + GVirConfigDomainTimer *timer; + GVirConfigDomainClockOffset offset; + + clock = gvir_config_domain_clock_new(); + offset = GVIR_CONFIG_DOMAIN_CLOCK_UTC; + if (design->priv->os != NULL) { + const gchar *short_id; + + short_id = osinfo_product_get_short_id(OSINFO_PRODUCT(design->priv->os)); + if (short_id != NULL && g_str_has_suffix(short_id, "win")) { + offset = GVIR_CONFIG_DOMAIN_CLOCK_LOCALTIME; + } + } + gvir_config_domain_clock_set_offset(clock, offset); + + timer = GVIR_CONFIG_DOMAIN_TIMER(gvir_config_domain_timer_rtc_new()); + gvir_config_domain_timer_set_tick_policy(timer, + GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_CATCHUP); + gvir_config_domain_clock_add_timer(clock, timer); + g_object_unref(G_OBJECT(timer)); + + timer = GVIR_CONFIG_DOMAIN_TIMER(gvir_config_domain_timer_pit_new()); + gvir_config_domain_timer_set_tick_policy(timer, + GVIR_CONFIG_DOMAIN_TIMER_TICK_POLICY_DELAY); + gvir_config_domain_clock_add_timer(clock, timer); + g_object_unref(G_OBJECT(timer)); + + gvir_config_domain_set_clock(design->priv->config, clock); + g_object_unref(G_OBJECT(clock)); +} + +static void gvir_designer_domain_add_power_management(GVirDesignerDomain *design) +{ + GVirConfigDomainPowerManagement *pm; + + pm = gvir_config_domain_power_management_new(); + gvir_config_domain_power_management_set_mem_suspend_enabled(pm, FALSE); + gvir_config_domain_power_management_set_disk_suspend_enabled(pm, FALSE); + + gvir_config_domain_set_power_management(design->priv->config, pm); + g_object_unref(G_OBJECT(pm)); +} + +static void gvir_designer_domain_set_lifecycle(GVirDesignerDomain *design) +{ + gvir_config_domain_set_lifecycle(design->priv->config, + GVIR_CONFIG_DOMAIN_LIFECYCLE_ON_POWEROFF, + GVIR_CONFIG_DOMAIN_LIFECYCLE_DESTROY); + gvir_config_domain_set_lifecycle(design->priv->config, + GVIR_CONFIG_DOMAIN_LIFECYCLE_ON_REBOOT, + GVIR_CONFIG_DOMAIN_LIFECYCLE_RESTART); + gvir_config_domain_set_lifecycle(design->priv->config, + GVIR_CONFIG_DOMAIN_LIFECYCLE_ON_CRASH, + GVIR_CONFIG_DOMAIN_LIFECYCLE_DESTROY); +} + +static void gvir_designer_domain_add_console(GVirDesignerDomain *design) +{ + GVirConfigDomainConsole *console; + GVirConfigDomainChardevSourcePty *pty; + + console = gvir_config_domain_console_new(); + pty = gvir_config_domain_chardev_source_pty_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(console), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(pty)); + g_object_unref(G_OBJECT(pty)); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(console)); + g_object_unref(G_OBJECT(console)); +} + +static void gvir_designer_domain_add_input(GVirDesignerDomain *design) +{ + GVirConfigDomainInput *input; + + input = gvir_config_domain_input_new(); + gvir_config_domain_input_set_device_type(input, + GVIR_CONFIG_DOMAIN_INPUT_DEVICE_TABLET); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(input)); + g_object_unref(G_OBJECT(input)); +} + + static void gvir_designer_domain_init(GVirDesignerDomain *design) { GVirDesignerDomainPrivate *priv; @@ -670,6 +759,12 @@ gvir_designer_domain_setup_guest(GVirDesignerDomain *design, gvir_config_capabilities_guest_domain_get_virt_type(domain)); gvir_config_domain_set_os(priv->config, os); + gvir_designer_domain_add_clock(design); + gvir_designer_domain_add_power_management(design); + gvir_designer_domain_set_lifecycle(design); + gvir_designer_domain_add_console(design); + gvir_designer_domain_add_input(design); + ret = TRUE; cleanup: if (domain != NULL) -- 1.8.2.1

--- examples/virtxml.c | 3 + libvirt-designer/libvirt-designer-domain.c | 133 +++++++++++++++++++++++++++++ libvirt-designer/libvirt-designer-domain.h | 2 + libvirt-designer/libvirt-designer.sym | 1 + 4 files changed, 139 insertions(+) diff --git a/examples/virtxml.c b/examples/virtxml.c index 6fb0551..d127406 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -642,6 +642,9 @@ main(int argc, char *argv[]) gvir_designer_domain_setup_machine(domain, &error); CHECK_ERROR; + g_object_unref(gvir_designer_domain_add_sound(domain, &error)); + CHECK_ERROR; + if (arch_str) { gvir_designer_domain_setup_container_full(domain, arch_str, &error); CHECK_ERROR; diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index e4f72e6..f329e89 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -388,6 +388,7 @@ static void gvir_designer_domain_add_console(GVirDesignerDomain *design) g_object_unref(G_OBJECT(console)); } + static void gvir_designer_domain_add_input(GVirDesignerDomain *design) { GVirConfigDomainInput *input; @@ -932,6 +933,138 @@ cleanup: } +static OsinfoDevice * +gvir_designer_domain_get_preferred_soundcard(GVirDesignerDomain *design, + GError **error) +{ + OsinfoDevice *device = NULL; + OsinfoDeviceLink *dev_link; + + dev_link = gvir_designer_domain_get_preferred_device(design, + "audio", + error); + if (dev_link == NULL) + goto cleanup; + + device = osinfo_devicelink_get_target(dev_link); + +cleanup: + if (dev_link != NULL) + g_object_unref(dev_link); + + return device; +} + +static OsinfoDeviceList * +gvir_designer_domain_get_fallback_devices(GVirDesignerDomain *design, + const char *class, + GError **error) +{ + OsinfoDeviceList *devices = NULL; + OsinfoFilter *filter; + + filter = osinfo_filter_new(); + osinfo_filter_add_constraint(filter, OSINFO_DEVICE_PROP_CLASS, class); + devices = gvir_designer_domain_get_supported_devices(design, filter); + g_object_unref(G_OBJECT(filter)); + + if (devices == NULL || + osinfo_list_get_length(OSINFO_LIST(devices)) == 0) { + g_set_error(error, GVIR_DESIGNER_DOMAIN_ERROR, 0, + "No '%s' fallback devices found", class); + goto cleanup; + } + + return devices; + +cleanup: + if (devices != NULL) + g_object_unref(devices); + + return NULL; +} + + +static OsinfoDevice * +gvir_designer_domain_get_fallback_soundcard(GVirDesignerDomain *domain, + GError **error) +{ + OsinfoEntity *dev = NULL; + OsinfoDeviceList *devices = NULL; + + devices = gvir_designer_domain_get_fallback_devices(domain, "audio", error); + if (devices == NULL) + goto cleanup; + + dev = osinfo_list_get_nth(OSINFO_LIST(devices), 0); + g_object_ref(G_OBJECT(dev)); + +cleanup: + if (devices != NULL) + g_object_unref(G_OBJECT(devices)); + + return OSINFO_DEVICE(dev); +} + + +static GVirConfigDomainSoundModel +gvir_designer_sound_model_from_soundcard(OsinfoDevice *soundcard) +{ + const char *name; + + name = osinfo_device_get_name(soundcard); + if (g_strcmp0(name, "ac97") == 0) { + return GVIR_CONFIG_DOMAIN_SOUND_MODEL_AC97; + } else if (g_strcmp0(name, "ich6") == 0) { + return GVIR_CONFIG_DOMAIN_SOUND_MODEL_ICH6; + } else if (g_strcmp0(name, "es1370") == 0) { + return GVIR_CONFIG_DOMAIN_SOUND_MODEL_ES1370; + } else if (g_strcmp0(name, "sb16") == 0) { + return GVIR_CONFIG_DOMAIN_SOUND_MODEL_SB16; + } else { + g_warning("Unknown soundcard %s, falling back to PC speaker", name); + return GVIR_CONFIG_DOMAIN_SOUND_MODEL_PCSPK; + } +} + + +/** + * gvir_designer_domain_add_sound: + * @design: (transfer none): the domain designer instance + * @error: return location for a #GError, or NULL + * + * Add a new soundcard to the domain. + * + * Returns: (transfer full): the pointer to the new soundcard. + * If something fails NULL is returned and @error is set. + */ +GVirConfigDomainSound * +gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error) +{ + GVirConfigDomainSound *sound; + OsinfoDevice *soundcard; + GVirConfigDomainSoundModel model; + + g_return_val_if_fail(GVIR_DESIGNER_IS_DOMAIN(design), NULL); + + soundcard = gvir_designer_domain_get_preferred_soundcard(design, NULL); + if (soundcard == NULL) + soundcard = gvir_designer_domain_get_fallback_soundcard(design, error); + + if (soundcard == NULL) + return NULL; + + sound = gvir_config_domain_sound_new(); + model = gvir_designer_sound_model_from_soundcard(soundcard); + gvir_config_domain_sound_set_model(sound, model); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(sound)); + + return sound; +} + + static gchar * gvir_designer_domain_next_disk_target(GVirDesignerDomain *design, GVirConfigDomainDiskBus bus) diff --git a/libvirt-designer/libvirt-designer-domain.h b/libvirt-designer/libvirt-designer-domain.h index c7b0e5c..c0d06e8 100644 --- a/libvirt-designer/libvirt-designer-domain.h +++ b/libvirt-designer/libvirt-designer-domain.h @@ -125,6 +125,8 @@ GVirConfigDomainInterface *gvir_designer_domain_add_interface_network(GVirDesign const char *network, GError **error); +GVirConfigDomainSound *gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error); + gboolean gvir_designer_domain_setup_resources(GVirDesignerDomain *design, GVirDesignerDomainResources req, GError **error); diff --git a/libvirt-designer/libvirt-designer.sym b/libvirt-designer/libvirt-designer.sym index d1aa916..0a8b49e 100644 --- a/libvirt-designer/libvirt-designer.sym +++ b/libvirt-designer/libvirt-designer.sym @@ -20,6 +20,7 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_add_floppy_file; gvir_designer_domain_add_floppy_device; gvir_designer_domain_add_interface_network; + gvir_designer_domain_add_sound; gvir_designer_domain_setup_resources; gvir_designer_domain_resources_get_type; -- 1.8.2.1

--- libvirt-designer/libvirt-designer-domain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index f329e89..98b30d5 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -1,7 +1,7 @@ /* * libvirt-designer-domain.c: libvirt domain configuration * - * Copyright (C) 2012 Red Hat, Inc. + * Copyright (C) 2012-2013 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 @@ -20,6 +20,7 @@ * Authors: * Daniel P. Berrange <berrange@redhat.com> * Michal Privoznik <mprivozn@redhat.com> + * Christophe Fergeau <cfergeau@redhat.com> */ #include <config.h> -- 1.8.2.1

This allows to choose between SPICE, VNC or a local display, which will go through SDL or 'desktop' depending on the hypervisor. --- configure.ac | 2 +- examples/virtxml.c | 25 +++++++ libvirt-designer/libvirt-designer-domain.c | 111 +++++++++++++++++++++++++++++ libvirt-designer/libvirt-designer-domain.h | 10 +++ libvirt-designer/libvirt-designer.sym | 2 + 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 228a85c..bad199b 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ AC_CANONICAL_HOST AM_SILENT_RULES([yes]) LIBOSINFO_REQUIRED=0.2.7 -LIBVIRT_GCONFIG_REQUIRED=0.0.9 +LIBVIRT_GCONFIG_REQUIRED=0.1.7 LIBVIRT_GOBJECT_REQUIRED=0.1.3 GOBJECT_INTROSPECTION_REQUIRED=0.10.8 diff --git a/examples/virtxml.c b/examples/virtxml.c index d127406..0d4ebff 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -563,6 +563,8 @@ main(int argc, char *argv[]) static char *platform_str = NULL; static char *arch_str = NULL; static char *connect_uri = NULL; + static char *graphics_str = NULL; + GVirDesignerDomainGraphics graphics; static char *resources_str = NULL; GVirDesignerDomainResources resources; GOptionContext *context = NULL; @@ -589,6 +591,8 @@ main(int argc, char *argv[]) "add floppy 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]"}, + {"graphics", 'g', 0, G_OPTION_ARG_STRING, &graphics_str, + "add graphical output to the VM. Possible values are 'spice' or 'vnc'", "GRAPHICS"}, {"resources", 'r', 0, G_OPTION_ARG_STRING, &resources_str, "Set minimal or recommended values for cpu count and RAM amount", "{minimal|recommended}"}, {NULL} @@ -669,6 +673,21 @@ main(int argc, char *argv[]) NULL); } + if (graphics_str) { + if (g_str_equal(graphics_str, "spice")) + graphics = GVIR_DESIGNER_DOMAIN_GRAPHICS_SPICE; + else if (g_str_equal(graphics_str, "vnc")) + graphics = GVIR_DESIGNER_DOMAIN_GRAPHICS_VNC; + else { + print_error("Unknown value '%s' for graphics", graphics_str); + goto cleanup; + } + g_object_unref(gvir_designer_domain_add_graphics(domain, + graphics, + &error)); + CHECK_ERROR; + } + g_list_foreach(cdrom_str_list, add_cdrom, domain); g_list_foreach(disk_str_list, add_disk, domain); @@ -779,6 +798,12 @@ Add an interface of type network with I<NETWORK> source. Moreover, some other configuration knobs can be set (possible I<ARG>s): I<mac>, I<link>={up|down} +=item -g GRAPHICS + +Add a graphics device of type I<GRAPHICS>. Valid values are spice +or vnc. + + =item -r RESOURCE, --resources=RESOURCES Set I<minimal> or I<recommended> resources on the domain XML. By default, diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index 98b30d5..95e855d 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -348,6 +348,117 @@ static void gvir_designer_domain_add_clock(GVirDesignerDomain *design) g_object_unref(G_OBJECT(clock)); } + +static GVirConfigDomainGraphics * +gvir_designer_domain_create_graphics_desktop(GVirDesignerDomain *design, + GError **error) +{ + int virt_type; + + virt_type = gvir_config_domain_get_virt_type(design->priv->config); + + switch (virt_type) { + case GVIR_CONFIG_DOMAIN_VIRT_QEMU: + case GVIR_CONFIG_DOMAIN_VIRT_KQEMU: + case GVIR_CONFIG_DOMAIN_VIRT_KVM: { + GVirConfigDomainGraphicsSdl *sdl; + sdl = gvir_config_domain_graphics_sdl_new(); + return GVIR_CONFIG_DOMAIN_GRAPHICS(sdl); + } + case GVIR_CONFIG_DOMAIN_VIRT_VBOX: { + GVirConfigDomainGraphicsDesktop *desktop; + desktop = gvir_config_domain_graphics_desktop_new(); + return GVIR_CONFIG_DOMAIN_GRAPHICS(desktop); + } + default: + g_set_error(error, GVIR_DESIGNER_DOMAIN_ERROR, 0, + "Virt type %d does not support this graphics output", + virt_type); + return NULL; + } +} + +/** + * gvir_designer_domain_add_graphics: + * @design: (transfer none): the domain designer instance + * @error: return location for a #GError, or NULL + * + * Add a new graphical framebuffer to @design. This allows + * to see what the VM displays. + * Remote display protocols will only be listening on localhost, and the + * port will be automatically allocated when the VM starts (usually + * starting at 5900). You can manipulate further the returned + * #GVirConfigDomainGraphics if you want a different behaviour. + * When setting up a SPICE display, the SPICE agent channel will be + * automatically added to the VM if it's supported and not already + * present. + * + * Returns: (transfer full): the pointer to the new graphical framebuffer + * configuration object. + */ +GVirConfigDomainGraphics * +gvir_designer_domain_add_graphics(GVirDesignerDomain *design, + GVirDesignerDomainGraphics type, + GError **error) +{ + GVirConfigDomainGraphics *graphics; + + g_return_val_if_fail(GVIR_DESIGNER_IS_DOMAIN(design), NULL); + g_return_val_if_fail(!error_is_set(error), NULL); + + switch (type) { + case GVIR_DESIGNER_DOMAIN_GRAPHICS_DESKTOP: { + graphics = gvir_designer_domain_create_graphics_desktop(design, error); + if (graphics == NULL) + return NULL; + } + + case GVIR_DESIGNER_DOMAIN_GRAPHICS_RDP: { + GVirConfigDomainGraphicsRdp *rdp; + + rdp = gvir_config_domain_graphics_rdp_new(); + gvir_config_domain_graphics_rdp_set_autoport(rdp, TRUE); + graphics = GVIR_CONFIG_DOMAIN_GRAPHICS(rdp); + + break; + } + + case GVIR_DESIGNER_DOMAIN_GRAPHICS_SPICE: { + GVirConfigDomainGraphicsSpice *spice; + + spice = gvir_config_domain_graphics_spice_new(); + gvir_config_domain_graphics_spice_set_autoport(spice, TRUE); + /* FIXME: Should only be done for local domains */ + gvir_config_domain_graphics_spice_set_image_compression(spice, + GVIR_CONFIG_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_OFF); + graphics = GVIR_CONFIG_DOMAIN_GRAPHICS(spice); + + break; + } + + case GVIR_DESIGNER_DOMAIN_GRAPHICS_VNC: { + GVirConfigDomainGraphicsVnc *vnc; + + vnc = gvir_config_domain_graphics_vnc_new(); + gvir_config_domain_graphics_vnc_set_autoport(vnc, TRUE); + graphics = GVIR_CONFIG_DOMAIN_GRAPHICS(vnc); + + break; + } + + default: + g_set_error(error, GVIR_DESIGNER_DOMAIN_ERROR, 0, + "Unknown graphics type: %d", type); + g_return_val_if_reached(NULL); + } + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(graphics)); + + return graphics; +} + + static void gvir_designer_domain_add_power_management(GVirDesignerDomain *design) { GVirConfigDomainPowerManagement *pm; diff --git a/libvirt-designer/libvirt-designer-domain.h b/libvirt-designer/libvirt-designer-domain.h index c0d06e8..1399bd4 100644 --- a/libvirt-designer/libvirt-designer-domain.h +++ b/libvirt-designer/libvirt-designer-domain.h @@ -44,6 +44,13 @@ typedef enum { GVIR_DESIGNER_DOMAIN_RESOURCES_RECOMMENDED, } GVirDesignerDomainResources; +typedef enum { + GVIR_DESIGNER_DOMAIN_GRAPHICS_DESKTOP, + GVIR_DESIGNER_DOMAIN_GRAPHICS_RDP, + GVIR_DESIGNER_DOMAIN_GRAPHICS_SPICE, + GVIR_DESIGNER_DOMAIN_GRAPHICS_VNC, +} GVirDesignerDomainGraphics; + typedef struct _GVirDesignerDomain GVirDesignerDomain; typedef struct _GVirDesignerDomainPrivate GVirDesignerDomainPrivate; typedef struct _GVirDesignerDomainClass GVirDesignerDomainClass; @@ -125,6 +132,9 @@ GVirConfigDomainInterface *gvir_designer_domain_add_interface_network(GVirDesign const char *network, GError **error); +GVirConfigDomainGraphics *gvir_designer_domain_add_graphics(GVirDesignerDomain *design, + GVirDesignerDomainGraphics type, + GError **error); GVirConfigDomainSound *gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error); gboolean gvir_designer_domain_setup_resources(GVirDesignerDomain *design, diff --git a/libvirt-designer/libvirt-designer.sym b/libvirt-designer/libvirt-designer.sym index 0a8b49e..9a73993 100644 --- a/libvirt-designer/libvirt-designer.sym +++ b/libvirt-designer/libvirt-designer.sym @@ -5,6 +5,7 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_new; gvir_designer_domain_get_type; + gvir_designer_domain_graphics_get_type; gvir_designer_domain_get_config; gvir_designer_domain_get_os; gvir_designer_domain_get_platform; @@ -19,6 +20,7 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_add_disk_device; gvir_designer_domain_add_floppy_file; gvir_designer_domain_add_floppy_device; + gvir_designer_domain_add_graphics; gvir_designer_domain_add_interface_network; gvir_designer_domain_add_sound; gvir_designer_domain_setup_resources; -- 1.8.2.1

When the user sets up the graphical framebuffer to use SPICE, we now add automatically the SPICE agent channel if it's not present already. --- libvirt-designer/libvirt-designer-domain.c | 85 ++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index 95e855d..c944257 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -69,6 +69,7 @@ static gboolean error_is_set(GError **error) return ((error != NULL) && (*error != NULL)); } +static const char GVIR_DESIGNER_SPICE_CHANNEL_NAME[] = "com.redhat.spice.0"; static const char GVIR_DESIGNER_VIRTIO_BLOCK_DEVICE_ID[] = "http://pciids.sourceforge.net/v2.2/pci.ids/1af4/1001"; enum { @@ -314,6 +315,29 @@ end: } +static GList * +gvir_designer_domain_get_device_by_type(GVirDesignerDomain *design, GType type) +{ + GList *devices; + GList *it; + GList *matched_devices = NULL; + + devices = gvir_config_domain_get_devices(design->priv->config); + for (it = devices; it != NULL; it = it->next) { + GType device_type = G_OBJECT_TYPE(it->data); + + if (g_type_is_a(device_type, type)) { + matched_devices = g_list_prepend(matched_devices, + g_object_ref(G_OBJECT(it->data))); + } + + } + g_list_free_full(devices, g_object_unref); + + return g_list_reverse(matched_devices); +} + + static void gvir_designer_domain_add_clock(GVirDesignerDomain *design) { GVirConfigDomainClock *clock; @@ -349,6 +373,65 @@ static void gvir_designer_domain_add_clock(GVirDesignerDomain *design) } +static gboolean +gvir_designer_domain_has_spice_channel(GVirDesignerDomain *design) +{ + GList *devices; + GList *it; + gboolean has_spice = FALSE; + + devices = gvir_designer_domain_get_device_by_type(design, + GVIR_CONFIG_TYPE_DOMAIN_CHANNEL); + for (it = devices; it != NULL; it = it->next) { + GVirConfigDomainChannel *channel; + const char *target_name; + channel = GVIR_CONFIG_DOMAIN_CHANNEL(it->data); + target_name = gvir_config_domain_channel_get_target_name(channel); + if (g_strcmp0(target_name, GVIR_DESIGNER_SPICE_CHANNEL_NAME) == 0) { + /* FIXME could do more sanity checks (check if the channel + * source has the 'spicevmc' type) + */ + GVirConfigDomainChannelTargetType target_type; + target_type = gvir_config_domain_channel_get_target_type(channel); + if (target_type == GVIR_CONFIG_DOMAIN_CHANNEL_TARGET_VIRTIO) { + has_spice = TRUE; + } else { + g_critical("Inconsistent SPICE channel, target type is wrong (%d)", + target_type); + has_spice = FALSE; + } + + break; + } + } + g_list_free_full(devices, g_object_unref); + + return has_spice; +} + + +static void gvir_designer_domain_add_spice_channel(GVirDesignerDomain *design) +{ + /* FIXME: error out if there is no support for the vioserial device */ + GVirConfigDomainChannel *channel; + GVirConfigDomainChardevSourceSpiceVmc *vmc; + + channel = gvir_config_domain_channel_new(); + gvir_config_domain_channel_set_target_type(channel, + GVIR_CONFIG_DOMAIN_CHANNEL_TARGET_VIRTIO); + gvir_config_domain_channel_set_target_name(channel, + GVIR_DESIGNER_SPICE_CHANNEL_NAME); + vmc = gvir_config_domain_chardev_source_spicevmc_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(channel), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(vmc)); + g_object_unref(G_OBJECT(vmc)); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(channel)); + g_object_unref(G_OBJECT(channel)); +} + + static GVirConfigDomainGraphics * gvir_designer_domain_create_graphics_desktop(GVirDesignerDomain *design, GError **error) @@ -432,6 +515,8 @@ gvir_designer_domain_add_graphics(GVirDesignerDomain *design, gvir_config_domain_graphics_spice_set_image_compression(spice, GVIR_CONFIG_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_OFF); graphics = GVIR_CONFIG_DOMAIN_GRAPHICS(spice); + if (!gvir_designer_domain_has_spice_channel(design)) + gvir_designer_domain_add_spice_channel(design); break; } -- 1.8.2.1

This makes use of the new gvir_designer_domain_get_supported_devices() method. --- libvirt-designer/libvirt-designer-domain.c | 49 ++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index c944257..911fb72 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -70,6 +70,7 @@ static gboolean error_is_set(GError **error) } static const char GVIR_DESIGNER_SPICE_CHANNEL_NAME[] = "com.redhat.spice.0"; +static const char GVIR_DESIGNER_SPICE_CHANNEL_DEVICE_ID[] = "http://pciids.sourceforge.net/v2.2/pci.ids/1af4/1003"; static const char GVIR_DESIGNER_VIRTIO_BLOCK_DEVICE_ID[] = "http://pciids.sourceforge.net/v2.2/pci.ids/1af4/1001"; enum { @@ -410,12 +411,48 @@ gvir_designer_domain_has_spice_channel(GVirDesignerDomain *design) } -static void gvir_designer_domain_add_spice_channel(GVirDesignerDomain *design) +static gboolean +gvir_designer_domain_supports_spice_channel(GVirDesignerDomain *design) +{ + OsinfoDeviceList *devices; + OsinfoFilter *filter; + gboolean vioserial_found = FALSE; + + filter = osinfo_filter_new(); + osinfo_filter_add_constraint(filter, + OSINFO_ENTITY_PROP_ID, + GVIR_DESIGNER_SPICE_CHANNEL_DEVICE_ID); + devices = gvir_designer_domain_get_supported_devices(design, filter); + if (devices) { + /* We only expect 0 or 1 virtio serial devices in that device list, + * so warn if we get more than 1 + */ + g_warn_if_fail(osinfo_list_get_length(OSINFO_LIST(devices)) <= 1); + if (osinfo_list_get_length(OSINFO_LIST(devices)) >= 1) + vioserial_found = TRUE; + g_object_unref(G_OBJECT(devices)); + } + if (filter) + g_object_unref(G_OBJECT(filter)); + + return vioserial_found; +} + + +static gboolean gvir_designer_domain_add_spice_channel(GVirDesignerDomain *design, + GError **error) { - /* FIXME: error out if there is no support for the vioserial device */ GVirConfigDomainChannel *channel; GVirConfigDomainChardevSourceSpiceVmc *vmc; + if (!gvir_designer_domain_supports_spice_channel(design)) { + g_set_error(error, GVIR_DESIGNER_DOMAIN_ERROR, 0, + "OS and/or hypervisor don't support virtio-serial" + " which is required by the SPICE channel"); + g_debug("SPICE channel not supported"); + return FALSE; + } + channel = gvir_config_domain_channel_new(); gvir_config_domain_channel_set_target_type(channel, GVIR_CONFIG_DOMAIN_CHANNEL_TARGET_VIRTIO); @@ -429,6 +466,8 @@ static void gvir_designer_domain_add_spice_channel(GVirDesignerDomain *design) gvir_config_domain_add_device(design->priv->config, GVIR_CONFIG_DOMAIN_DEVICE(channel)); g_object_unref(G_OBJECT(channel)); + + return TRUE; } @@ -516,7 +555,11 @@ gvir_designer_domain_add_graphics(GVirDesignerDomain *design, GVIR_CONFIG_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_OFF); graphics = GVIR_CONFIG_DOMAIN_GRAPHICS(spice); if (!gvir_designer_domain_has_spice_channel(design)) - gvir_designer_domain_add_spice_channel(design); + gvir_designer_domain_add_spice_channel(design, error); + if (error_is_set(error)) { + g_object_unref(graphics); + return NULL; + } break; } -- 1.8.2.1

This will add an USB redirection channel to the VM. This can be called multiple times to redirect several USB devices at once. This will also add the needed controllers if they are not already present in the VM. The current code has 2 shortcomings: - USB redirection is only supported with SPICE, but this is not checked for - the USB controller added to the VM are hardcoded, no check if they are supported by the OS, hypervisor, ... --- examples/virtxml.c | 15 ++++ libvirt-designer/libvirt-designer-domain.c | 117 +++++++++++++++++++++++++++++ libvirt-designer/libvirt-designer-domain.h | 1 + libvirt-designer/libvirt-designer.sym | 2 + 4 files changed, 135 insertions(+) diff --git a/examples/virtxml.c b/examples/virtxml.c index 0d4ebff..6027335 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -565,9 +565,11 @@ main(int argc, char *argv[]) static char *connect_uri = NULL; static char *graphics_str = NULL; GVirDesignerDomainGraphics graphics; + static gboolean enable_usb; static char *resources_str = NULL; GVirDesignerDomainResources resources; GOptionContext *context = NULL; + unsigned int i; static GOptionEntry entries[] = { @@ -593,6 +595,8 @@ main(int argc, char *argv[]) "add interface with NETWORK source. Possible ARGs: mac, link={up,down}", "NETWORK[,ARG=VAL]"}, {"graphics", 'g', 0, G_OPTION_ARG_STRING, &graphics_str, "add graphical output to the VM. Possible values are 'spice' or 'vnc'", "GRAPHICS"}, + {"usb", 'u', 0, G_OPTION_ARG_NONE, &enable_usb, + "add USB redirection to the VM.", NULL}, {"resources", 'r', 0, G_OPTION_ARG_STRING, &resources_str, "Set minimal or recommended values for cpu count and RAM amount", "{minimal|recommended}"}, {NULL} @@ -646,6 +650,14 @@ main(int argc, char *argv[]) gvir_designer_domain_setup_machine(domain, &error); CHECK_ERROR; + if (enable_usb) { + for (i = 0; i < 4; i++) { + /* 4 USB redir channels allow to redirect 4 USB devices at once */ + g_object_unref(gvir_designer_domain_add_usb_redir(domain, &error)); + CHECK_ERROR; + } + } + g_object_unref(gvir_designer_domain_add_sound(domain, &error)); CHECK_ERROR; @@ -803,6 +815,9 @@ I<link>={up|down} Add a graphics device of type I<GRAPHICS>. Valid values are spice or vnc. +=item -u + +Add USB controllers and setup USB redirection in the VM configuration. =item -r RESOURCE, --resources=RESOURCES diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index 911fb72..a435ea0 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -587,6 +587,123 @@ gvir_designer_domain_add_graphics(GVirDesignerDomain *design, } +static gboolean +gvir_designer_domain_supports_usb(GVirDesignerDomain *design) +{ + GList *devices; + devices = gvir_designer_domain_get_device_by_type(design, + GVIR_CONFIG_TYPE_DOMAIN_CONTROLLER_USB); + g_list_free_full(devices, g_object_unref); + + return (devices != NULL); +} + + +static GVirConfigDomainControllerUsb * +gvir_designer_domain_create_usb_controller(GVirDesignerDomain *design, + GVirConfigDomainControllerUsbModel model, + guint indx, + GVirConfigDomainControllerUsb *master, + guint start_port) +{ + GVirConfigDomainControllerUsb *controller; + + controller = gvir_config_domain_controller_usb_new(); + gvir_config_domain_controller_usb_set_model(controller, model); + gvir_config_domain_controller_set_index(GVIR_CONFIG_DOMAIN_CONTROLLER(controller), indx); + if (master) + gvir_config_domain_controller_usb_set_master(controller, master, start_port); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(controller)); + + return controller; +} + + +static void +gvir_designer_domain_add_usb_controllers(GVirDesignerDomain *design) +{ + GVirConfigDomainControllerUsb *master; + GVirConfigDomainControllerUsb *controller; + + g_debug("Adding USB controllers"); + + master = gvir_designer_domain_create_usb_controller(design, + GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_EHCI1, + 0, + NULL, + 0); + controller = gvir_designer_domain_create_usb_controller(design, + GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI1, + 0, + master, + 0); + g_object_unref(G_OBJECT(controller)); + controller = gvir_designer_domain_create_usb_controller(design, + GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI2, + 0, + master, + 2); + g_object_unref(G_OBJECT(controller)); + controller = gvir_designer_domain_create_usb_controller(design, + GVIR_CONFIG_DOMAIN_CONTROLLER_USB_MODEL_ICH9_UHCI3, + 0, + master, + 4); + g_object_unref(G_OBJECT(controller)); + g_object_unref(G_OBJECT(master)); +} + + +/** + * gvir_designer_domain_add_usb_redir: + * @design: (transfer none): the domain designer instance + * @error: return location for a #GError, or NULL + * + * Add a new usb redirection channel into @design. This allows to redirect + * an USB device from the SPICE client to the guest. One USB device + * can be redirected per redirection channel, this function can + * be called multiple times if you need to redirect multiple devices + * simultaneously. An USB2 EHCI controller and USB1 UHCI controllers + * will be automatically added to @design if @design does not have + * USB controllers yet. + * + * Returns: (transfer full): the pointer to the new USB redir channel + */ +GVirConfigDomainRedirdev * +gvir_designer_domain_add_usb_redir(GVirDesignerDomain *design, GError **error) +{ + /* FIXME: check if OS/hypervisor support USB + * check if SPICE is being used + */ + GVirConfigDomainRedirdev *redirdev; + GVirConfigDomainChardevSourceSpiceVmc *vmc; + + g_return_val_if_fail(GVIR_DESIGNER_IS_DOMAIN(design), NULL); + g_return_val_if_fail(!error_is_set(error), NULL); + + redirdev = gvir_config_domain_redirdev_new(); + gvir_config_domain_redirdev_set_bus(redirdev, + GVIR_CONFIG_DOMAIN_REDIRDEV_BUS_USB); + vmc = gvir_config_domain_chardev_source_spicevmc_new(); + gvir_config_domain_chardev_set_source(GVIR_CONFIG_DOMAIN_CHARDEV(redirdev), + GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(vmc)); + g_object_unref(G_OBJECT(vmc)); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(redirdev)); + + if (!gvir_designer_domain_supports_usb(design)) { + gvir_designer_domain_add_usb_controllers(design); + } else { + g_debug("USB controllers are already present"); + } + + return redirdev; +} + + static void gvir_designer_domain_add_power_management(GVirDesignerDomain *design) { GVirConfigDomainPowerManagement *pm; diff --git a/libvirt-designer/libvirt-designer-domain.h b/libvirt-designer/libvirt-designer-domain.h index 1399bd4..981fd2e 100644 --- a/libvirt-designer/libvirt-designer-domain.h +++ b/libvirt-designer/libvirt-designer-domain.h @@ -136,6 +136,7 @@ GVirConfigDomainGraphics *gvir_designer_domain_add_graphics(GVirDesignerDomain * GVirDesignerDomainGraphics type, GError **error); GVirConfigDomainSound *gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error); +GVirConfigDomainRedirdev *gvir_designer_domain_add_usb_redir(GVirDesignerDomain *design, GError **error); gboolean gvir_designer_domain_setup_resources(GVirDesignerDomain *design, GVirDesignerDomainResources req, diff --git a/libvirt-designer/libvirt-designer.sym b/libvirt-designer/libvirt-designer.sym index 9a73993..2894dee 100644 --- a/libvirt-designer/libvirt-designer.sym +++ b/libvirt-designer/libvirt-designer.sym @@ -23,6 +23,8 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_add_graphics; gvir_designer_domain_add_interface_network; gvir_designer_domain_add_sound; + gvir_designer_domain_add_usb_redir; + gvir_designer_domain_setup_resources; gvir_designer_domain_resources_get_type; -- 1.8.2.1

This setups smartcard redirection to the guest. I'm not yet fully sure what users could want to tweak there (there are various ways of setting up the smartcard redirection), so this code may need to be made more flexible. The current code is also not checking whether the hypervisor supports this kind of redirection or not. --- examples/virtxml.c | 12 ++++++++++ libvirt-designer/libvirt-designer-domain.c | 36 ++++++++++++++++++++++++++++++ libvirt-designer/libvirt-designer-domain.h | 1 + libvirt-designer/libvirt-designer.sym | 1 + 4 files changed, 50 insertions(+) diff --git a/examples/virtxml.c b/examples/virtxml.c index 6027335..bd5d14c 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -565,6 +565,7 @@ main(int argc, char *argv[]) static char *connect_uri = NULL; static char *graphics_str = NULL; GVirDesignerDomainGraphics graphics; + static gboolean enable_smartcard; static gboolean enable_usb; static char *resources_str = NULL; GVirDesignerDomainResources resources; @@ -595,6 +596,8 @@ main(int argc, char *argv[]) "add interface with NETWORK source. Possible ARGs: mac, link={up,down}", "NETWORK[,ARG=VAL]"}, {"graphics", 'g', 0, G_OPTION_ARG_STRING, &graphics_str, "add graphical output to the VM. Possible values are 'spice' or 'vnc'", "GRAPHICS"}, + {"smartcard", 's', 0, G_OPTION_ARG_NONE, &enable_smartcard, + "add smartcard reader to the VM.", NULL}, {"usb", 'u', 0, G_OPTION_ARG_NONE, &enable_usb, "add USB redirection to the VM.", NULL}, {"resources", 'r', 0, G_OPTION_ARG_STRING, &resources_str, @@ -658,6 +661,11 @@ main(int argc, char *argv[]) } } + if (enable_smartcard) { + g_object_unref(gvir_designer_domain_add_smartcard(domain, &error)); + CHECK_ERROR; + } + g_object_unref(gvir_designer_domain_add_sound(domain, &error)); CHECK_ERROR; @@ -815,6 +823,10 @@ I<link>={up|down} Add a graphics device of type I<GRAPHICS>. Valid values are spice or vnc. +=item -s + +Add smartcard reader to the VM conifguration + =item -u Add USB controllers and setup USB redirection in the VM configuration. diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index a435ea0..34f5852 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -704,6 +704,42 @@ gvir_designer_domain_add_usb_redir(GVirDesignerDomain *design, GError **error) } +/** + * gvir_designer_domain_add_smartcard: + * @design: (transfer none): the domain designer instance + * @error: return location for a #GError, or NULL + * + * Add a new virtual smartcard reader to @design. This will allow to + * share a smartcard reader between the guest and the host. + * + * Returns: (transfer full): the pointer to the new smartcard device + */ +GVirConfigDomainSmartcard * +gvir_designer_domain_add_smartcard(GVirDesignerDomain *design, GError **error) +{ + /* FIXME: check if OS/hypervisor support smartcard, might need + * libosinfo improvements + */ + GVirConfigDomainSmartcardPassthrough *smartcard; + GVirConfigDomainChardevSourceSpiceVmc *vmc; + GVirConfigDomainChardevSource *source; + + g_return_val_if_fail(GVIR_DESIGNER_IS_DOMAIN(design), NULL); + g_return_val_if_fail(!error_is_set(error), NULL); + + smartcard = gvir_config_domain_smartcard_passthrough_new(); + vmc = gvir_config_domain_chardev_source_spicevmc_new(); + source = GVIR_CONFIG_DOMAIN_CHARDEV_SOURCE(vmc); + gvir_config_domain_smartcard_passthrough_set_source(smartcard, source); + g_object_unref(G_OBJECT(vmc)); + + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(smartcard)); + + return GVIR_CONFIG_DOMAIN_SMARTCARD(smartcard); +} + + static void gvir_designer_domain_add_power_management(GVirDesignerDomain *design) { GVirConfigDomainPowerManagement *pm; diff --git a/libvirt-designer/libvirt-designer-domain.h b/libvirt-designer/libvirt-designer-domain.h index 981fd2e..643e3bf 100644 --- a/libvirt-designer/libvirt-designer-domain.h +++ b/libvirt-designer/libvirt-designer-domain.h @@ -135,6 +135,7 @@ GVirConfigDomainInterface *gvir_designer_domain_add_interface_network(GVirDesign GVirConfigDomainGraphics *gvir_designer_domain_add_graphics(GVirDesignerDomain *design, GVirDesignerDomainGraphics type, GError **error); +GVirConfigDomainSmartcard *gvir_designer_domain_add_smartcard(GVirDesignerDomain *design, GError **error); GVirConfigDomainSound *gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error); GVirConfigDomainRedirdev *gvir_designer_domain_add_usb_redir(GVirDesignerDomain *design, GError **error); diff --git a/libvirt-designer/libvirt-designer.sym b/libvirt-designer/libvirt-designer.sym index 2894dee..da2cad6 100644 --- a/libvirt-designer/libvirt-designer.sym +++ b/libvirt-designer/libvirt-designer.sym @@ -22,6 +22,7 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_add_floppy_device; gvir_designer_domain_add_graphics; gvir_designer_domain_add_interface_network; + gvir_designer_domain_add_smartcard; gvir_designer_domain_add_sound; gvir_designer_domain_add_usb_redir; -- 1.8.2.1

This takes into account the devices specified by the deployment, if this fails, consider the intersection of devices supported by the OS and by the platform, and if this still fails, falls back to a hardcoded hypervisor type -> video model mapping. --- examples/virtxml.c | 3 + libvirt-designer/libvirt-designer-domain.c | 172 +++++++++++++++++++++++++++++ libvirt-designer/libvirt-designer-domain.h | 2 + libvirt-designer/libvirt-designer.sym | 1 + 4 files changed, 178 insertions(+) diff --git a/examples/virtxml.c b/examples/virtxml.c index bd5d14c..30cd885 100644 --- a/examples/virtxml.c +++ b/examples/virtxml.c @@ -666,6 +666,9 @@ main(int argc, char *argv[]) CHECK_ERROR; } + g_object_unref(gvir_designer_domain_add_video(domain, &error)); + CHECK_ERROR; + g_object_unref(gvir_designer_domain_add_sound(domain, &error)); CHECK_ERROR; diff --git a/libvirt-designer/libvirt-designer-domain.c b/libvirt-designer/libvirt-designer-domain.c index 34f5852..848dfea 100644 --- a/libvirt-designer/libvirt-designer-domain.c +++ b/libvirt-designer/libvirt-designer-domain.c @@ -1160,6 +1160,7 @@ gvir_designer_domain_setup_guest(GVirDesignerDomain *design, gvir_designer_domain_add_input(design); ret = TRUE; + cleanup: if (domain != NULL) g_object_unref(domain); @@ -1910,6 +1911,177 @@ gvir_designer_domain_add_interface_network(GVirDesignerDomain *design, return ret; } +static GVirConfigDomainVideoModel +gvir_designer_domain_video_model_str_to_enum(const char *model_str, + GError **error) +{ + GVirConfigDomainVideoModel model; + + if (g_str_equal(model_str, "vga")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VGA; + } else if (g_str_equal(model_str, "cirrus")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_CIRRUS; + } else if (g_str_equal(model_str, "vmvga")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VMVGA; + } else if (g_str_equal(model_str, "xen")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_XEN; + } else if (g_str_equal(model_str, "vbox")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VBOX; + } else if (g_str_equal(model_str, "qxl")) { + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_QXL; + } else { + g_debug("unsupported video model type '%s'", model_str); + g_set_error(error, GVIR_DESIGNER_DOMAIN_ERROR, 0, + "unsupported video model type '%s'", model_str); + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VGA; + } + + return model; +} + +/* FIXME: belongs in platform descriptions, maybe useful as a last resort */ +static GVirConfigDomainVideoModel +gvir_designer_domain_video_model_from_virt_type(GVirDesignerDomain *design) +{ + GVirConfigDomainVirtType virt_type; + GVirConfigDomainVideoModel model; + + virt_type = gvir_config_domain_get_virt_type(design->priv->config); + switch (virt_type) { + case GVIR_CONFIG_DOMAIN_VIRT_QEMU: + case GVIR_CONFIG_DOMAIN_VIRT_KQEMU: + case GVIR_CONFIG_DOMAIN_VIRT_KVM: + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_QXL; + break; + case GVIR_CONFIG_DOMAIN_VIRT_XEN: + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_XEN; + break; + case GVIR_CONFIG_DOMAIN_VIRT_VMWARE: + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VMVGA; + break; + case GVIR_CONFIG_DOMAIN_VIRT_VBOX: + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VBOX; + break; + case GVIR_CONFIG_DOMAIN_VIRT_LXC: + case GVIR_CONFIG_DOMAIN_VIRT_UML: + case GVIR_CONFIG_DOMAIN_VIRT_OPENVZ: + case GVIR_CONFIG_DOMAIN_VIRT_VSERVER: + case GVIR_CONFIG_DOMAIN_VIRT_LDOM: + case GVIR_CONFIG_DOMAIN_VIRT_TEST: + case GVIR_CONFIG_DOMAIN_VIRT_HYPERV: + case GVIR_CONFIG_DOMAIN_VIRT_ONE: + case GVIR_CONFIG_DOMAIN_VIRT_PHYP: + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VGA; + break; + default: + g_warn_if_reached(); + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VGA; + break; + } + + return model; +} + +static const gchar * +gvir_designer_domain_get_preferred_video_model(GVirDesignerDomain *design, + GError **error) +{ + const gchar *ret = NULL; + OsinfoDeviceLink *dev_link = NULL; + OsinfoDevice *dev; + + dev_link = gvir_designer_domain_get_preferred_device(design, "video", error); + if (!dev_link) + goto cleanup; + + dev = osinfo_devicelink_get_target(dev_link); + if (dev) + ret = osinfo_device_get_name(dev); + +cleanup: + if (dev_link) + g_object_unref(dev_link); + return ret; +} + +static GVirConfigDomainVideoModel +gvir_designer_domain_get_fallback_video_model(GVirDesignerDomain *design) +{ + OsinfoDeviceList *supported_devices = NULL; + OsinfoFilter *filter = NULL; + OsinfoDevice *device = NULL; + const char *model_str; + GVirConfigDomainVideoModel model; + GError *error = NULL; + + model = GVIR_CONFIG_DOMAIN_VIDEO_MODEL_VGA; + + filter = osinfo_filter_new(); + osinfo_filter_add_constraint(filter, OSINFO_DEVICE_PROP_CLASS, "video"); + supported_devices = gvir_designer_domain_get_supported_devices(design, filter); + if (supported_devices == NULL || osinfo_list_get_length(OSINFO_LIST(supported_devices)) == 0) + goto fallback; + + device = OSINFO_DEVICE(osinfo_list_get_nth(OSINFO_LIST(supported_devices), 0)); + model_str = osinfo_device_get_name(device); + model = gvir_designer_domain_video_model_str_to_enum(model_str, + &error); + if (error != NULL) { + g_clear_error(&error); + goto fallback; + } + goto end; + +fallback: + model = gvir_designer_domain_video_model_from_virt_type(design); + +end: + if (filter != NULL) + g_object_unref(G_OBJECT(filter)); + if (supported_devices != NULL) + g_object_unref(G_OBJECT(supported_devices)); + + return model; +} + +/** + * gvir_designer_domain_add_video: + * @design: (transfer none): the domain designer instance + * @error: return location for a #GError, or NULL + * + * Add a new video device into @design. + * + * Returns: (transfer full): the pointer to the new video device. + */ +GVirConfigDomainVideo * +gvir_designer_domain_add_video(GVirDesignerDomain *design, GError **error) +{ + GVirConfigDomainVideo *video; + const gchar *model_str = NULL; + GVirConfigDomainVideoModel model; + + g_return_val_if_fail(GVIR_DESIGNER_IS_DOMAIN(design), NULL); + g_return_val_if_fail(!error_is_set(error), NULL); + + model_str = gvir_designer_domain_get_preferred_video_model(design, NULL); + if (model_str != NULL) { + model = gvir_designer_domain_video_model_str_to_enum(model_str, error); + if (error_is_set(error)) { + g_clear_error(error); + model = gvir_designer_domain_get_fallback_video_model(design); + } + } else { + model = gvir_designer_domain_get_fallback_video_model(design); + } + + video = gvir_config_domain_video_new(); + gvir_config_domain_video_set_model(video, model); + gvir_config_domain_add_device(design->priv->config, + GVIR_CONFIG_DOMAIN_DEVICE(video)); + + return video; +} + static void gvir_designer_domain_get_resources(OsinfoResourcesList *res_list, diff --git a/libvirt-designer/libvirt-designer-domain.h b/libvirt-designer/libvirt-designer-domain.h index 643e3bf..85dce97 100644 --- a/libvirt-designer/libvirt-designer-domain.h +++ b/libvirt-designer/libvirt-designer-domain.h @@ -138,6 +138,8 @@ GVirConfigDomainGraphics *gvir_designer_domain_add_graphics(GVirDesignerDomain * GVirConfigDomainSmartcard *gvir_designer_domain_add_smartcard(GVirDesignerDomain *design, GError **error); GVirConfigDomainSound *gvir_designer_domain_add_sound(GVirDesignerDomain *design, GError **error); GVirConfigDomainRedirdev *gvir_designer_domain_add_usb_redir(GVirDesignerDomain *design, GError **error); +GVirConfigDomainVideo *gvir_designer_domain_add_video(GVirDesignerDomain *design, + GError **error); gboolean gvir_designer_domain_setup_resources(GVirDesignerDomain *design, GVirDesignerDomainResources req, diff --git a/libvirt-designer/libvirt-designer.sym b/libvirt-designer/libvirt-designer.sym index da2cad6..96bc656 100644 --- a/libvirt-designer/libvirt-designer.sym +++ b/libvirt-designer/libvirt-designer.sym @@ -25,6 +25,7 @@ LIBVIRT_DESIGNER_0.0.2 { gvir_designer_domain_add_smartcard; gvir_designer_domain_add_sound; gvir_designer_domain_add_usb_redir; + gvir_designer_domain_add_video; gvir_designer_domain_setup_resources; gvir_designer_domain_resources_get_type; -- 1.8.2.1

On 05.06.2013 14:43, Christophe Fergeau wrote:
Hey,
Here is a new version of that series with the issues raised by Michal addressed.
Changes since v3: - don't automatically add USB/smartcard/graphics devices, but add options to virtxml to explicitly request those - return an error when gvir_designer_domain_add_graphics() fails to automatically add the spice agent channel - set a GError when gvir_designer_domain_get_fallback_devices() could not find any device.
Christophe
You've addressed all my concerns. ACK series. Michal
participants (2)
-
Christophe Fergeau
-
Michal Privoznik