Richard W.M. Jones
2017-Mar-17 11:33 UTC
[Libguestfs] [PATCH v2 0/6] v2v: Pass CPU vendor, model and topology from source to target.
v1 -> v2: - Support for passing topology through -o glance. - Support for passing topology through -o rhv. - Use bool for acpi/apic/pae struct fields in virt-p2v. - Write the xpath expression in error messages instead of file/line. - Fix more memory leaks in virt-p2v cpuid.c. - Passes make check & check-valgrind. There may be some other minor changes. I believe that everything mentioned in reviews has been fixed.
Richard W.M. Jones
2017-Mar-17 11:33 UTC
[Libguestfs] [PATCH v2 1/6] p2v: Pass host CPU details to virt-v2v.
In the fake <domain type='physical'> libvirt XML that we create to describe the physical host, we did not accurately pass any information about the host CPU except the number of cores (<vcpu/>). This commit extracts detailed information about the vendor, model and topology of the host CPU and adds that to the libvirt XML for virt-v2v. Conveniently we can use libvirt capabilities to get this information without needing to parse /proc/cpuinfo or similar techniques. The libvirt XML looks like this: <domain type="physical"> ... <cpu match="minimum"> <vendor>Intel</vendor> <model fallback="allow">Broadwell</model> <topology sockets="1" cores="2" threads="2"/> </cpu> ... <features> <acpi/> <apic/> <pae/> </features> --- p2v/Makefile.am | 12 ++- p2v/config.c | 18 +++- p2v/conversion.c | 35 +++++- p2v/cpuid.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++ p2v/dependencies.m4 | 5 + p2v/main.c | 56 +--------- p2v/p2v.h | 21 +++- 7 files changed, 379 insertions(+), 67 deletions(-) create mode 100644 p2v/cpuid.c diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 9401220..0b8c1c6 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -74,6 +74,7 @@ virt_p2v_SOURCES = \ about-license.c \ config.c \ conversion.c \ + cpuid.c \ gui.c \ gui-gtk2-compat.h \ gui-gtk3-compat.h \ @@ -97,6 +98,7 @@ virt_p2v_CPPFLAGS = \ virt_p2v_CFLAGS = \ -pthread \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBVIRT_CFLAGS) \ $(PCRE_CFLAGS) \ $(LIBXML2_CFLAGS) \ $(GTK_CFLAGS) \ @@ -105,6 +107,7 @@ virt_p2v_CFLAGS = \ virt_p2v_LDADD = \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/miniexpect/libminiexpect.la \ + $(LIBVIRT_LIBS) \ $(PCRE_LIBS) \ $(LIBXML2_LIBS) \ $(GTK_LIBS) \ @@ -120,9 +123,16 @@ dependencies_files = \ dependencies.redhat \ dependencies.suse +if HAVE_LIBVIRT +dependencies_have_libvirt = -DHAVE_LIBVIRT=1 +endif + $(dependencies_files): dependencies.m4 define=`echo $@ | $(SED) 's/dependencies.//;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`; \ - m4 -D$$define=1 -DGTK_VERSION=$(GTK_VERSION) $< > $@-t + m4 -D$$define=1 \ + -DGTK_VERSION=$(GTK_VERSION) \ + $(dependencies_have_libvirt) \ + $< > $@-t mv $@-t $@ # Support files needed by the virt-p2v-make-* scripts. diff --git a/p2v/config.c b/p2v/config.c index 054b07c..f9f610c 100644 --- a/p2v/config.c +++ b/p2v/config.c @@ -95,6 +95,8 @@ free_config (struct config *c) free (c->identity_url); free (c->identity_file); free (c->guestname); + free (c->cpu.vendor); + free (c->cpu.model); guestfs_int_free_string_list (c->disks); guestfs_int_free_string_list (c->removable); guestfs_int_free_string_list (c->interfaces); @@ -132,10 +134,20 @@ print_config (struct config *config, FILE *fp) config->guestname ? config->guestname : "none"); fprintf (fp, "vcpus . . . . . %d\n", config->vcpus); fprintf (fp, "memory . . . . . %" PRIu64 "\n", config->memory); + if (config->cpu.vendor) + fprintf (fp, "cpu vendor . . . %s\n", config->cpu.vendor); + if (config->cpu.model) + fprintf (fp, "cpu model . . . %s\n", config->cpu.model); + if (config->cpu.sockets > 0) + fprintf (fp, "cpu sockets . . %u\n", config->cpu.sockets); + if (config->cpu.cores > 0) + fprintf (fp, "cpu cores . . . %u\n", config->cpu.cores); + if (config->cpu.threads > 0) + fprintf (fp, "cpu threads . . %u\n", config->cpu.threads); fprintf (fp, "flags . . . . . %s%s%s\n", - config->flags & FLAG_ACPI ? " acpi" : "", - config->flags & FLAG_APIC ? " apic" : "", - config->flags & FLAG_PAE ? " pae" : ""); + config->cpu.acpi ? " acpi" : "", + config->cpu.apic ? " apic" : "", + config->cpu.pae ? " pae" : ""); fprintf (fp, "disks . . . . . "); if (config->disks != NULL) { for (i = 0; config->disks[i] != NULL; ++i) diff --git a/p2v/conversion.c b/p2v/conversion.c index 0c17ef2..55fbfb1 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -612,6 +612,35 @@ generate_libvirt_xml (struct config *config, struct data_conn *data_conns, string_format ("%d", config->vcpus); } end_element (); + if (config->cpu.vendor || config->cpu.model || + config->cpu.sockets || config->cpu.cores || config->cpu.threads) { + /* https://libvirt.org/formatdomain.html#elementsCPU */ + start_element ("cpu") { + attribute ("match", "minimum"); + if (config->cpu.vendor) { + start_element ("vendor") { + string (config->cpu.vendor); + } end_element (); + } + if (config->cpu.model) { + start_element ("model") { + attribute ("fallback", "allow"); + string (config->cpu.model); + } end_element (); + } + if (config->cpu.sockets || config->cpu.cores || config->cpu.threads) { + start_element ("topology") { + if (config->cpu.sockets) + attribute_format ("sockets", "%u", config->cpu.sockets); + if (config->cpu.cores) + attribute_format ("cores", "%u", config->cpu.cores); + if (config->cpu.threads) + attribute_format ("threads", "%u", config->cpu.threads); + } end_element (); + } + } end_element (); + } + start_element ("os") { start_element ("type") { attribute ("arch", host_cpu); @@ -620,9 +649,9 @@ generate_libvirt_xml (struct config *config, struct data_conn *data_conns, } end_element (); start_element ("features") { - if (config->flags & FLAG_ACPI) empty_element ("acpi"); - if (config->flags & FLAG_APIC) empty_element ("apic"); - if (config->flags & FLAG_PAE) empty_element ("pae"); + if (config->cpu.acpi) empty_element ("acpi"); + if (config->cpu.apic) empty_element ("apic"); + if (config->cpu.pae) empty_element ("pae"); } end_element (); start_element ("devices") { diff --git a/p2v/cpuid.c b/p2v/cpuid.c new file mode 100644 index 0000000..13b61b0 --- /dev/null +++ b/p2v/cpuid.c @@ -0,0 +1,299 @@ +/* virt-p2v + * Copyright (C) 2009-2017 Red Hat Inc. + * + * 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. + */ + +/** + * Process CPU capabilities into libvirt-compatible C<E<lt>cpuE<gt>> data. + * + * If libvirt is available at compile time then this is quite + * simple - libvirt API C<virConnectGetCapabilities> provides + * a C<E<lt>hostE<ge>> element which has mostly what we need. + * + * Flags C<acpi>, C<apic>, C<pae> still have to be parsed out of + * F</proc/cpuinfo> because these will not necessarily be present in + * the libvirt capabilities directly (they are implied by the + * processor model, requiring a complex lookup in the CPU map). + * + * Note that #vCPUs and amount of RAM is handled by F<main.c>. + * + * See: L<https://libvirt.org/formatdomain.html#elementsCPU> + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <libintl.h> + +#ifdef HAVE_LIBVIRT +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> +#endif + +#include <libxml/xpath.h> + +#include "getprogname.h" +#include "ignore-value.h" + +#include "p2v.h" + +static void +free_cpu_config (struct cpu_config *cpu) +{ + if (cpu->vendor) + free (cpu->vendor); + if (cpu->model) + free (cpu->model); + memset (cpu, 0, sizeof *cpu); +} + +/** + * Read flags from F</proc/cpuinfo>. + */ +static void +cpuinfo_flags (struct cpu_config *cpu) +{ + const char *cmd; + CLEANUP_PCLOSE FILE *fp = NULL; + CLEANUP_FREE char *flag = NULL; + ssize_t len; + size_t buflen = 0; + + /* Get the flags, one per line. */ + cmd = "< /proc/cpuinfo " +#if defined(__arm__) + "grep ^Features" +#else + "grep ^flags" +#endif + " | awk '{ for (i = 3; i <= NF; ++i) { print $i }; exit }'"; + + fp = popen (cmd, "re"); + if (fp == NULL) { + perror ("/proc/cpuinfo"); + return; + } + + while (errno = 0, (len = getline (&flag, &buflen, fp)) != -1) { + if (len > 0 && flag[len-1] == '\n') + flag[len-1] = '\0'; + + if (STREQ (flag, "acpi")) + cpu->acpi = 1; + else if (STREQ (flag, "apic")) + cpu->apic = 1; + else if (STREQ (flag, "pae")) + cpu->pae = 1; + } + + if (errno) { + perror ("getline"); + return; + } +} + +#ifdef HAVE_LIBVIRT + +static void +ignore_errors (void *ignore, virErrorPtr ignore2) +{ + /* empty */ +} + +static void libvirt_error (const char *fs, ...) __attribute__((format (printf,1,2))); + +static void +libvirt_error (const char *fs, ...) +{ + va_list args; + CLEANUP_FREE char *msg = NULL; + int len; + virErrorPtr err; + + va_start (args, fs); + len = vasprintf (&msg, fs, args); + va_end (args); + + if (len < 0) goto fallback; + + /* In all recent libvirt, this retrieves the thread-local error. */ + err = virGetLastError (); + if (err) + fprintf (stderr, + "%s: %s: %s [code=%d int1=%d]\n", + getprogname (), msg, err->message, err->code, err->int1); + else + fallback: + fprintf (stderr, "%s: %s\n", getprogname (), msg); +} + +/** + * Read the capabilities from libvirt and parse out the fields + * we care about. + */ +static void +libvirt_capabilities (struct cpu_config *cpu) +{ + virConnectPtr conn; + CLEANUP_FREE char *capabilities_xml = NULL; + CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL; + CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL; + CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpathObj = NULL; + const char *xpathexpr; + xmlNodeSetPtr nodes; + size_t nr_nodes, i; + xmlNodePtr node; + + /* Connect to libvirt and get the capabilities XML. */ + conn = virConnectOpenReadOnly (NULL); + if (!conn) { + libvirt_error (_("could not connect to libvirt")); + return; + } + + /* Suppress default behaviour of printing errors to stderr. Note + * you can't set this to NULL to ignore errors; setting it to NULL + * restores the default error handler ... + */ + virConnSetErrorFunc (conn, NULL, ignore_errors); + + capabilities_xml = virConnectGetCapabilities (conn); + if (!capabilities_xml) { + libvirt_error (_("could not get libvirt capabilities")); + virConnectClose (conn); + return; + } + + /* Parse the capabilities XML with libxml2. */ + doc = xmlReadMemory (capabilities_xml, strlen (capabilities_xml), + NULL, NULL, XML_PARSE_NONET); + if (doc == NULL) { + fprintf (stderr, + _("%s: unable to parse capabilities XML returned by libvirt\n"), + getprogname ()); + virConnectClose (conn); + return; + } + + xpathCtx = xmlXPathNewContext (doc); + if (xpathCtx == NULL) { + fprintf (stderr, _("%s: unable to create new XPath context\n"), + getprogname ()); + virConnectClose (conn); + return; + } + + /* Get the CPU vendor. */ + xpathexpr = "/capabilities/host/cpu/vendor/text()"; + xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"), + getprogname (), xpathexpr); + virConnectClose (conn); + return; + } + nodes = xpathObj->nodesetval; + nr_nodes = nodes->nodeNr; + if (nr_nodes > 0) { + node = nodes->nodeTab[0]; + cpu->vendor = (char *) xmlNodeGetContent (node); + } + + /* Get the CPU model. */ + xmlXPathFreeObject (xpathObj); + xpathexpr = "/capabilities/host/cpu/model/text()"; + xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"), + getprogname (), xpathexpr); + virConnectClose (conn); + return; + } + nodes = xpathObj->nodesetval; + nr_nodes = nodes->nodeNr; + if (nr_nodes > 0) { + node = nodes->nodeTab[0]; + cpu->model = (char *) xmlNodeGetContent (node); + } + + /* Get the topology. Note the XPath expression returns all + * attributes of the <topology> node. + */ + xmlXPathFreeObject (xpathObj); + xpathexpr = "/capabilities/host/cpu/topology/@*"; + xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx); + if (xpathObj == NULL) { + fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"), + getprogname (), xpathexpr); + virConnectClose (conn); + return; + } + nodes = xpathObj->nodesetval; + nr_nodes = nodes->nodeNr; + /* Iterate over the attributes of the <topology> node. */ + for (i = 0; i < nr_nodes; ++i) { + node = nodes->nodeTab[i]; + + if (node->type == XML_ATTRIBUTE_NODE) { + xmlAttrPtr attr = (xmlAttrPtr) node; + CLEANUP_FREE char *content = NULL; + unsigned *up; + + if (STREQ ((const char *) attr->name, "sockets")) { + up = &cpu->sockets; + parse_attr: + *up = 0; + content = (char *) xmlNodeListGetString (doc, attr->children, 1); + if (content) + ignore_value (sscanf (content, "%u", up)); + } + else if (STREQ ((const char *) attr->name, "cores")) { + up = &cpu->cores; + goto parse_attr; + } + else if (STREQ ((const char *) attr->name, "threads")) { + up = &cpu->threads; + goto parse_attr; + } + } + } + + virConnectClose (conn); +} + +#else /* !HAVE_LIBVIRT */ + +static void +libvirt_capabilities (struct cpu_config *cpu) +{ + fprintf (stderr, + _("%s: program was compiled without libvirt support\n"), + getprogname ()); +} + +#endif /* !HAVE_LIBVIRT */ + +void +get_cpu_config (struct cpu_config *cpu) +{ + free_cpu_config (cpu); + libvirt_capabilities (cpu); + cpuinfo_flags (cpu); +} diff --git a/p2v/dependencies.m4 b/p2v/dependencies.m4 index 21541b4..adbac26 100644 --- a/p2v/dependencies.m4 +++ b/p2v/dependencies.m4 @@ -25,6 +25,8 @@ ifelse(REDHAT,1, libxml2 gtk`'GTK_VERSION dbus-libs + dnl libvirt is optional, used just to parse the host CPU capabilities. + ifdef(`HAVE_LIBVIRT', `libvirt-libs') dnl Run as external programs by the p2v binary. /usr/bin/ssh @@ -79,6 +81,7 @@ ifelse(DEBIAN,1, libxml2 libgtk`'GTK_VERSION`'.0-0 libdbus-1-3 + ifdef(`HAVE_LIBVIRT', `libvirt0') openssh-client qemu-utils gawk @@ -112,6 +115,7 @@ ifelse(ARCHLINUX,1, libxml2 gtk`'GTK_VERSION dbus + ifdef(`HAVE_LIBVIRT', `libvirt') openssh qemu gawk @@ -146,6 +150,7 @@ ifelse(SUSE,1, libxml2 gtk`'GTK_VERSION libdbus-1-3 + ifdef(`HAVE_LIBVIRT', `libvirt-libs') qemu-tools openssh gawk diff --git a/p2v/main.c b/p2v/main.c index c02d309..7f1e1c0 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -65,7 +65,6 @@ static void udevadm_settle (void); static void set_config_defaults (struct config *config); static void find_all_disks (void); static void find_all_interfaces (void); -static int cpuinfo_flags (void); enum { HELP_OPTION = CHAR_MAX + 1 }; static const char options[] = "Vv"; @@ -277,7 +276,6 @@ set_config_defaults (struct config *config) { long i; char hostname[257]; - int flags; /* Default guest name is derived from the source hostname. If we * assume that the p2v ISO gets its IP address and hostname from @@ -341,11 +339,7 @@ set_config_defaults (struct config *config) config->memory |= config->memory >> 32; config->memory++; - flags = cpuinfo_flags (); - if (flags >= 0) - config->flags = flags; - else - config->flags = 0; + get_cpu_config (&config->cpu); /* Find all block devices in the system. */ if (!test_disk) @@ -586,51 +580,3 @@ find_all_interfaces (void) if (all_interfaces) qsort (all_interfaces, nr_interfaces, sizeof (char *), compare); } - -/** - * Read the list of flags from F</proc/cpuinfo>. - */ -static int -cpuinfo_flags (void) -{ - const char *cmd; - CLEANUP_PCLOSE FILE *fp = NULL; - CLEANUP_FREE char *flag = NULL; - ssize_t len; - size_t buflen = 0; - int ret = 0; - - /* Get the flags, one per line. */ - cmd = "< /proc/cpuinfo " -#if defined(__arm__) - "grep ^Features" -#else - "grep ^flags" -#endif - " | awk '{ for (i = 3; i <= NF; ++i) { print $i }; exit }'"; - - fp = popen (cmd, "re"); - if (fp == NULL) { - perror ("/proc/cpuinfo"); - return -1; - } - - while (errno = 0, (len = getline (&flag, &buflen, fp)) != -1) { - if (len > 0 && flag[len-1] == '\n') - flag[len-1] = '\0'; - - if (STREQ (flag, "acpi")) - ret |= FLAG_ACPI; - else if (STREQ (flag, "apic")) - ret |= FLAG_APIC; - else if (STREQ (flag, "pae")) - ret |= FLAG_PAE; - } - - if (errno) { - perror ("getline"); - return -1; - } - - return ret; -} diff --git a/p2v/p2v.h b/p2v/p2v.h index 5223aa2..6c794f0 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -20,6 +20,7 @@ #define P2V_H #include <stdio.h> +#include <stdbool.h> /* Send various debug information to stderr. Harmless and useful, so * can be left enabled in production builds. @@ -59,6 +60,17 @@ extern int feature_colours_option; extern int force_colour; /* config.c */ +struct cpu_config { + char *vendor; /* eg. "Intel" */ + char *model; /* eg. "Broadwell" */ + unsigned sockets; /* number of sockets */ + unsigned cores; /* number of cores per socket */ + unsigned threads; /* number of hyperthreads per core */ + bool acpi; + bool apic; + bool pae; +}; + struct config { char *server; int port; @@ -71,7 +83,7 @@ struct config { char *guestname; int vcpus; uint64_t memory; - int flags; + struct cpu_config cpu; char **disks; char **removable; char **interfaces; @@ -83,10 +95,6 @@ struct config { char *output_storage; }; -#define FLAG_ACPI 1 -#define FLAG_APIC 2 -#define FLAG_PAE 4 - #define OUTPUT_ALLOCATION_NONE 0 #define OUTPUT_ALLOCATION_SPARSE 1 #define OUTPUT_ALLOCATION_PREALLOCATED 2 @@ -96,6 +104,9 @@ extern struct config *copy_config (struct config *); extern void free_config (struct config *); extern void print_config (struct config *, FILE *); +/* cpuid.c */ +extern void get_cpu_config (struct cpu_config *); + /* kernel-cmdline.c */ extern char **parse_cmdline_string (const char *cmdline); extern char **parse_proc_cmdline (void); -- 2.10.2
Richard W.M. Jones
2017-Mar-17 11:33 UTC
[Libguestfs] [PATCH v2 2/6] tests: v2v: Rearrange test-v2v-print-source.
This rearranges the input files for the test of virt-v2v --print-source, but does not change its semantics. --- v2v/test-v2v-print-source.expected | 14 ++++++++++++++ v2v/test-v2v-print-source.sh | 26 +++----------------------- v2v/test-v2v-print-source.xml | 23 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 v2v/test-v2v-print-source.expected create mode 100644 v2v/test-v2v-print-source.xml diff --git a/v2v/test-v2v-print-source.expected b/v2v/test-v2v-print-source.expected new file mode 100644 index 0000000..b947927 --- /dev/null +++ b/v2v/test-v2v-print-source.expected @@ -0,0 +1,14 @@ + source name: windows +hypervisor type: kvm + memory: 1073741824 (bytes) + nr vCPUs: 1 + CPU features: + firmware: unknown + display: + video: qxl + sound: +disks: + windows.img (raw) [virtio-blk] +removable media: +NICs: + Network "default" mac: 00:11:22:33:44:55 [virtio] diff --git a/v2v/test-v2v-print-source.sh b/v2v/test-v2v-print-source.sh index 62899e6..2b3361e 100755 --- a/v2v/test-v2v-print-source.sh +++ b/v2v/test-v2v-print-source.sh @@ -24,15 +24,12 @@ $TEST_FUNCTIONS skip_if_skipped skip_unless_phony_guest windows.img -libvirt_uri="test://$abs_top_builddir/test-data/phony-guests/guests.xml" -f=$top_builddir/test-data/phony-guests/windows.img - d=test-v2v-print-source.d rm -rf $d mkdir $d $VG virt-v2v --debug-gc \ - -i libvirt -ic "$libvirt_uri" windows \ + -i libvirtxml test-v2v-print-source.xml \ -o local -os $d \ --print-source > $d/output @@ -40,27 +37,10 @@ mv $d/output $d/output.orig < $d/output.orig \ grep -v 'Opening the source' | grep -v 'Source guest information' | -sed -e 's,/.*/,/,' | +sed -e 's,/.*/windows.img,windows.img,' | grep -v '^$' \ > $d/output -if [ "$(cat $d/output)" != " source name: windows -hypervisor type: test - memory: 1073741824 (bytes) - nr vCPUs: 1 - CPU features: - firmware: unknown - display: - video: qxl - sound: -disks: - /windows.img (raw) [virtio-blk] -removable media: -NICs: - Network \"default\" mac: 00:11:22:33:44:55 [virtio]" ]; then - echo "$0: unexpected output from test:" - cat $d/output.orig - exit 1 -fi +diff -u test-v2v-print-source.expected $d/output rm -r $d diff --git a/v2v/test-v2v-print-source.xml b/v2v/test-v2v-print-source.xml new file mode 100644 index 0000000..0667f2e --- /dev/null +++ b/v2v/test-v2v-print-source.xml @@ -0,0 +1,23 @@ +<domain type='kvm'> + <name>windows</name> + <memory>1048576</memory> + <os> + <type>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <disk type='file' device='disk'> + <driver name='qemu' type='raw'/> + <source file='../test-data/phony-guests/windows.img'/> + <target dev='vda' bus='virtio'/> + </disk> + <interface type='network'> + <mac address='00:11:22:33:44:55'/> + <source network='default'/> + <model type='virtio'/> + </interface> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1'/> + </video> + </devices> +</domain> -- 2.10.2
Richard W.M. Jones
2017-Mar-17 11:33 UTC
[Libguestfs] [PATCH v2 3/6] v2v: -o libvirt: Refactor creation of XML body.
Just refactoring, no change in semantics. --- v2v/create_libvirt_xml.ml | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index de3ff39..7830bc3 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -32,6 +32,21 @@ let string_set_of_list let create_libvirt_xml ?pool source target_buses guestcaps target_features target_firmware + (* The main body of the libvirt XML document. *) + let body = ref [] in + + append body [ + Comment generated_by; + e "name" [] [PCData source.s_name]; + ]; + + let memory_k = source.s_memory /^ 1024L in + append body [ + e "memory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)]; + e "currentMemory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)]; + e "vcpu" [] [PCData (string_of_int source.s_vcpu)] + ]; + let uefi_firmware match target_firmware with | TargetBIOS -> None @@ -47,8 +62,6 @@ let create_libvirt_xml ?pool source target_buses guestcaps let machine_q35 = secure_boot_required in let smm = secure_boot_required in - let memory_k = source.s_memory /^ 1024L in - (* We have the machine features of the guest when it was on the * source hypervisor (source.s_features). We have the acpi flag * which tells us whether acpi is required by this guest @@ -91,6 +104,10 @@ let create_libvirt_xml ?pool source target_buses guestcaps let features = List.sort compare (StringSet.elements features) in + append body [ + e "features" [] (List.map (fun s -> e s [] []) features); + ]; + (* The <os> section subelements. *) let os_section let machine = if machine_q35 then [ "machine", "q35" ] else [] in @@ -108,6 +125,14 @@ let create_libvirt_xml ?pool source target_buses guestcaps (e "type" (["arch", guestcaps.gcaps_arch] @ machine) [PCData "hvm"]) :: loader in + append body [ + e "os" [] os_section; + + e "on_poweroff" [] [PCData "destroy"]; + e "on_reboot" [] [PCData "restart"]; + e "on_crash" [] [PCData "restart"]; + ]; + (* The devices. *) let devices = ref [] in @@ -284,23 +309,13 @@ let create_libvirt_xml ?pool source target_buses guestcaps e "console" ["type", "pty"] []; ]; + append body [ + e "devices" [] !devices; + ]; + let doc : doc doc "domain" [ "type", "kvm"; (* Always assume target is kvm? *) - ] [ - Comment generated_by; - e "name" [] [PCData source.s_name]; - e "memory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)]; - e "currentMemory" ["unit", "KiB"] [PCData (Int64.to_string memory_k)]; - e "vcpu" [] [PCData (string_of_int source.s_vcpu)]; - e "os" [] os_section; - e "features" [] (List.map (fun s -> e s [] []) features); - - e "on_poweroff" [] [PCData "destroy"]; - e "on_reboot" [] [PCData "restart"]; - e "on_crash" [] [PCData "restart"]; - - e "devices" [] !devices; - ] in + ] !body in doc -- 2.10.2
Richard W.M. Jones
2017-Mar-17 11:34 UTC
[Libguestfs] [PATCH v2 4/6] v2v: -o glance: Build properties list imperatively.
No change, just refactoring. --- v2v/output_glance.ml | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml index c372b0a..da44700 100644 --- a/v2v/output_glance.ml +++ b/v2v/output_glance.ml @@ -82,7 +82,7 @@ object (* Set the properties (ie. metadata). *) let min_ram = source.s_memory /^ 1024L /^ 1024L in - let properties = [ + let properties = ref [ "hw_disk_bus", (match guestcaps.gcaps_block_bus with | Virtio_blk -> "virtio" @@ -109,26 +109,28 @@ object | x -> x (* everything else is the same in libguestfs and OpenStack*) ) ] in - let properties - match guestcaps.gcaps_block_bus with - | Virtio_SCSI -> ("hw_scsi_model", "virtio-scsi") :: properties - | Virtio_blk | IDE -> properties in - let properties - match inspect.i_major_version, inspect.i_minor_version with - | 0, 0 -> properties - | x, 0 -> ("os_version", string_of_int x) :: properties - | x, y -> ("os_version", sprintf "%d.%d" x y) :: properties in + (match guestcaps.gcaps_block_bus with + | Virtio_SCSI -> + push_back properties ("hw_scsi_model", "virtio-scsi") + | Virtio_blk | IDE -> () + ); + (match inspect.i_major_version, inspect.i_minor_version with + | 0, 0 -> () + | x, 0 -> push_back properties ("os_version", string_of_int x) + | x, y -> push_back properties ("os_version", sprintf "%d.%d" x y) + ); + let properties + List.flatten ( + List.map ( + fun (k, v) -> [ "--property"; sprintf "%s=%s" k v ] + ) !properties + ) in let cmd = [ "glance"; "image-create"; "--name"; name; "--disk-format=" ^ target_format; "--container-format=bare"; "--file"; target_file; "--min-ram"; Int64.to_string min_ram ] @ - (List.flatten - (List.map ( - fun (k, v) -> - [ "--property"; sprintf "%s=%s" k v ] - ) properties - )) in + properties in if run_command cmd <> 0 then error (f_"glance: image upload to glance failed, see earlier errors"); ) targets -- 2.10.2
Richard W.M. Jones
2017-Mar-17 11:34 UTC
[Libguestfs] [PATCH v2 5/6] v2v: Pass CPU vendor, model and topology from source to target.
Where supported, pass the source CPU vendor, model and topology to the target hypervisor. -i ova: We can get just cores per socket via a proprietary VMware extension to OVF. -i libvirt, virt-p2v: We can get all of these fields from the libvirt XML. -o libvirt, -o local: We can preserve all of the information in the target XML. -o rhv/vdsm: We can only pass through the topology, since neither oVirt Engine nor the OVF format supports CPU vendor or model. Topology uses an extension of OVF which is not part of the standard. -o qemu: We preserve the topology only because versions of qemu vary widely in their support for CPU models. -o glance: Only the topology is preserved since that is all that Glance currently supports. Thanks: Pino Toscano. --- v2v/OVF.ml | 111 ++++++++++++++++++++++----------- v2v/create_libvirt_xml.ml | 36 +++++++++++ v2v/input_disk.ml | 5 ++ v2v/input_ova.ml | 8 ++- v2v/output_glance.ml | 19 ++++++ v2v/output_qemu.ml | 24 ++++++- v2v/parse_libvirt_xml.ml | 11 ++++ v2v/parse_ovf_from_ova.ml | 22 ++++++- v2v/parse_ovf_from_ova.mli | 5 +- v2v/test-v2v-i-ova-formats.expected | 3 + v2v/test-v2v-i-ova-gz.expected | 3 + v2v/test-v2v-i-ova-subfolders.expected | 3 + v2v/test-v2v-i-ova-tar.expected | 3 + v2v/test-v2v-i-ova-two-disks.expected | 3 + v2v/test-v2v-print-source.expected | 5 +- v2v/test-v2v-print-source.xml | 10 +++ v2v/types.ml | 13 ++++ v2v/types.mli | 5 ++ v2v/v2v.ml | 27 ++++++++ 19 files changed, 271 insertions(+), 45 deletions(-) diff --git a/v2v/OVF.ml b/v2v/OVF.ml index 1f838f5..5c2ebc9 100644 --- a/v2v/OVF.ml +++ b/v2v/OVF.ml @@ -318,52 +318,87 @@ let rec create_ovf source targets guestcaps inspect (e "Origin" [] [PCData (string_of_int origin)]) ); - append content_subnodes [ + push_back content_subnodes ( e "Section" ["ovf:id", vm_uuid; "ovf:required", "false"; "xsi:type", "ovf:OperatingSystemSection_Type"] [ e "Info" [] [PCData inspect.i_product_name]; e "Description" [] [PCData ostype]; - ]; - - e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] [ - e "Info" [] [PCData (sprintf "%d CPU, %Ld Memory" source.s_vcpu memsize_mb)]; - e "Item" [] [ - e "rasd:Caption" [] [PCData (sprintf "%d virtual cpu" source.s_vcpu)]; - e "rasd:Description" [] [PCData "Number of virtual CPU"]; - e "rasd:InstanceId" [] [PCData "1"]; - e "rasd:ResourceType" [] [PCData "3"]; - e "rasd:num_of_sockets" [] [PCData (string_of_int source.s_vcpu)]; - e "rasd:cpu_per_socket"[] [PCData "1"]; - ]; - e "Item" [] [ - e "rasd:Caption" [] [PCData (sprintf "%Ld MB of memory" memsize_mb)]; - e "rasd:Description" [] [PCData "Memory Size"]; - e "rasd:InstanceId" [] [PCData "2"]; - e "rasd:ResourceType" [] [PCData "4"]; - e "rasd:AllocationUnits" [] [PCData "MegaBytes"]; - e "rasd:VirtualQuantity" [] [PCData (Int64.to_string memsize_mb)]; - ]; - e "Item" [] [ - e "rasd:Caption" [] [PCData "USB Controller"]; - e "rasd:InstanceId" [] [PCData "3"]; - e "rasd:ResourceType" [] [PCData "23"]; - e "rasd:UsbPolicy" [] [PCData "Disabled"]; - ]; - (* We always add a qxl device when outputting to RHV. - * See RHBZ#1213701 and RHBZ#1211231 for the reasoning - * behind that. - *) - e "Item" [] [ - e "rasd:Caption" [] [PCData "Graphical Controller"]; - e "rasd:InstanceId" [] [PCData (uuidgen ())]; - e "rasd:ResourceType" [] [PCData "20"]; - e "Type" [] [PCData "video"]; - e "rasd:VirtualQuantity" [] [PCData "1"]; - e "rasd:Device" [] [PCData "qxl"]; ] + ); + + let virtual_hardware_section_items = ref [ + e "Info" [] [PCData (sprintf "%d CPU, %Ld Memory" + source.s_vcpu memsize_mb)] + ] in + + push_back virtual_hardware_section_items ( + e "Item" [] ([ + e "rasd:Caption" [] [PCData (sprintf "%d virtual cpu" source.s_vcpu)]; + e "rasd:Description" [] [PCData "Number of virtual CPU"]; + e "rasd:InstanceId" [] [PCData "1"]; + e "rasd:ResourceType" [] [PCData "3"] + ] @ + if source.s_cpu_sockets <> None || source.s_cpu_cores <> None || + source.s_cpu_threads <> None then ( + let sockets + match source.s_cpu_sockets with + | None -> "1" + | Some v -> string_of_int v in + let cores + match source.s_cpu_cores with + | None -> "1" + | Some v -> string_of_int v in + let threads + match source.s_cpu_threads with + | None -> "1" + | Some v -> string_of_int v in + [ e "rasd:num_of_sockets" [] [PCData sockets]; + e "rasd:cpu_per_socket"[] [PCData cores]; + e "rasd:threads_per_cpu"[] [PCData threads] ] + ) + else ( + [ e "rasd:num_of_sockets" [] [PCData "1"]; + e "rasd:cpu_per_socket"[] [PCData (string_of_int source.s_vcpu)] ] + ) + ) + ); + + append virtual_hardware_section_items [ + e "Item" [] [ + e "rasd:Caption" [] [PCData (sprintf "%Ld MB of memory" memsize_mb)]; + e "rasd:Description" [] [PCData "Memory Size"]; + e "rasd:InstanceId" [] [PCData "2"]; + e "rasd:ResourceType" [] [PCData "4"]; + e "rasd:AllocationUnits" [] [PCData "MegaBytes"]; + e "rasd:VirtualQuantity" [] [PCData (Int64.to_string memsize_mb)]; + ]; + + e "Item" [] [ + e "rasd:Caption" [] [PCData "USB Controller"]; + e "rasd:InstanceId" [] [PCData "3"]; + e "rasd:ResourceType" [] [PCData "23"]; + e "rasd:UsbPolicy" [] [PCData "Disabled"]; + ]; + + (* We always add a qxl device when outputting to RHV. + * See RHBZ#1213701 and RHBZ#1211231 for the reasoning + * behind that. + *) + e "Item" [] [ + e "rasd:Caption" [] [PCData "Graphical Controller"]; + e "rasd:InstanceId" [] [PCData (uuidgen ())]; + e "rasd:ResourceType" [] [PCData "20"]; + e "Type" [] [PCData "video"]; + e "rasd:VirtualQuantity" [] [PCData "1"]; + e "rasd:Device" [] [PCData "qxl"]; ] ]; + push_back content_subnodes ( + e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] + !virtual_hardware_section_items + ); + e "Content" ["ovf:id", "out"; "xsi:type", "ovf:VirtualSystem_Type"] !content_subnodes ] in diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index 7830bc3..fc71965 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -47,6 +47,42 @@ let create_libvirt_xml ?pool source target_buses guestcaps e "vcpu" [] [PCData (string_of_int source.s_vcpu)] ]; + if source.s_cpu_vendor <> None || source.s_cpu_model <> None || + source.s_cpu_sockets <> None || source.s_cpu_cores <> None || + source.s_cpu_threads <> None then ( + let cpu = ref [] in + + (match source.s_cpu_vendor with + | None -> () + | Some vendor -> + push_back cpu (e "vendor" [] [PCData vendor]) + ); + (match source.s_cpu_model with + | None -> () + | Some model -> + push_back cpu (e "model" ["fallback", "allow"] [PCData model]) + ); + if source.s_cpu_sockets <> None || source.s_cpu_cores <> None || + source.s_cpu_threads <> None then ( + let topology_attrs = ref [] in + (match source.s_cpu_sockets with + | None -> () + | Some v -> push_back topology_attrs ("sockets", string_of_int v) + ); + (match source.s_cpu_cores with + | None -> () + | Some v -> push_back topology_attrs ("cores", string_of_int v) + ); + (match source.s_cpu_threads with + | None -> () + | Some v -> push_back topology_attrs ("threads", string_of_int v) + ); + push_back cpu (e "topology" !topology_attrs []) + ); + + append body [ e "cpu" [ "match", "minimum" ] !cpu ] + ); + let uefi_firmware match target_firmware with | TargetBIOS -> None diff --git a/v2v/input_disk.ml b/v2v/input_disk.ml index 27f8553..d28f45e 100644 --- a/v2v/input_disk.ml +++ b/v2v/input_disk.ml @@ -80,6 +80,11 @@ class input_disk input_format disk = object s_name = name; s_orig_name = name; s_memory = 2048L *^ 1024L *^ 1024L; (* 2048 MB *) s_vcpu = 1; (* 1 vCPU is a safe default *) + s_cpu_vendor = None; + s_cpu_model = None; + s_cpu_sockets = None; + s_cpu_cores = None; + s_cpu_threads = None; s_features = [ "acpi"; "apic"; "pae" ]; s_firmware = UnknownFirmware; (* causes virt-v2v to autodetect *) s_display diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml index e80ec82..b82862f 100644 --- a/v2v/input_ova.ml +++ b/v2v/input_ova.ml @@ -222,7 +222,8 @@ object let ovf_folder = Filename.dirname ovf in (* Parse the ovf file. *) - let name, memory, vcpu, firmware, disks, removables, nics + let name, memory, vcpu, cpu_sockets, cpu_cores, firmware, + disks, removables, nics parse_ovf_from_ova ovf in let name @@ -314,6 +315,11 @@ object s_orig_name = name; s_memory = memory; s_vcpu = vcpu; + s_cpu_vendor = None; + s_cpu_model = None; + s_cpu_sockets = cpu_sockets; + s_cpu_cores = cpu_cores; + s_cpu_threads = None; (* XXX *) s_features = []; (* XXX *) s_firmware = firmware; s_display = None; (* XXX *) diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml index da44700..04a2843 100644 --- a/v2v/output_glance.ml +++ b/v2v/output_glance.ml @@ -109,6 +109,25 @@ object | x -> x (* everything else is the same in libguestfs and OpenStack*) ) ] in + if source.s_cpu_sockets <> None || source.s_cpu_cores <> None || + source.s_cpu_threads <> None then ( + push_back properties ("hw_cpu_sockets", + match source.s_cpu_sockets with + | None -> "1" + | Some v -> string_of_int v); + push_back properties ("hw_cpu_cores", + match source.s_cpu_cores with + | None -> "1" + | Some v -> string_of_int v); + push_back properties ("hw_cpu_threads", + match source.s_cpu_threads with + | None -> "1" + | Some v -> string_of_int v); + ) + else ( + push_back properties ("hw_cpu_sockets", "1"); + push_back properties ("hw_cpu_cores", string_of_int source.s_vcpu); + ); (match guestcaps.gcaps_block_bus with | Virtio_SCSI -> push_back properties ("hw_scsi_model", "virtio-scsi") diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml index 84efd45..a6feeaa 100644 --- a/v2v/output_qemu.ml +++ b/v2v/output_qemu.ml @@ -96,8 +96,28 @@ object ); arg "-m" (Int64.to_string (source.s_memory /^ 1024L /^ 1024L)); - if source.s_vcpu > 1 then - arg "-smp" (string_of_int source.s_vcpu); + if source.s_vcpu > 1 then ( + if source.s_cpu_sockets <> None || source.s_cpu_cores <> None || + source.s_cpu_threads <> None then ( + let a = ref [] in + push_back a (sprintf "cpus=%d" source.s_vcpu); + push_back a (sprintf "sockets=%d" + (match source.s_cpu_sockets with + | None -> 1 + | Some v -> v)); + push_back a (sprintf "cores=%d" + (match source.s_cpu_cores with + | None -> 1 + | Some v -> v)); + push_back a (sprintf "threads=%d" + (match source.s_cpu_threads with + | None -> 1 + | Some v -> v)); + commas "-smp" !a + ) + else + arg "-smp" (string_of_int source.s_vcpu); + ); let make_disk if_name i = function | BusSlotEmpty -> () diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index edffd20..6032c31 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -67,6 +67,12 @@ let parse_libvirt_xml ?conn xml let memory = memory *^ 1024L in let vcpu = xpath_int_default "/domain/vcpu/text()" 1 in + let cpu_vendor = xpath_string "/domain/cpu/vendor/text()" in + let cpu_model = xpath_string "/domain/cpu/model/text()" in + let cpu_sockets = xpath_int "/domain/cpu/topology/@sockets" in + let cpu_cores = xpath_int "/domain/cpu/topology/@cores" in + let cpu_threads = xpath_int "/domain/cpu/topology/@threads" in + let features let features = ref [] in let obj = Xml.xpath_eval_expression xpathctx "/domain/features/*" in @@ -410,6 +416,11 @@ let parse_libvirt_xml ?conn xml s_name = name; s_orig_name = name; s_memory = memory; s_vcpu = vcpu; + s_cpu_vendor = cpu_vendor; + s_cpu_model = cpu_model; + s_cpu_sockets = cpu_sockets; + s_cpu_cores = cpu_cores; + s_cpu_threads = cpu_threads; s_features = features; s_firmware = UnknownFirmware; (* XXX until RHBZ#1217444 is fixed *) s_display = display; diff --git a/v2v/parse_ovf_from_ova.ml b/v2v/parse_ovf_from_ova.ml index 989483e..2a37527 100644 --- a/v2v/parse_ovf_from_ova.ml +++ b/v2v/parse_ovf_from_ova.ml @@ -69,6 +69,26 @@ let parse_ovf_from_ova ovf_filename (* Search for number of vCPUs. *) let vcpu = xpath_int_default "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/rasd:VirtualQuantity/text()" 1 in + (* CPU topology. coresPerSocket is a VMware proprietary extension. + * I couldn't find out how hyperthreads is specified in the OVF. + *) + let cores_per_socket = xpath_int "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/vmw:CoresPerSocket/text()" in + let cpu_sockets, cpu_cores + match cores_per_socket with + | None -> None, None + | Some cores_per_socket when cores_per_socket <= 0 -> + warning (f_"invalid vmw:CoresPerSocket (%d) ignored") + cores_per_socket; + None, None + | Some cores_per_socket -> + let sockets = vcpu / cores_per_socket in + if sockets <= 0 then ( + warning (f_"invalid vmw:CoresPerSocket < number of cores"); + None, None + ) + else + Some sockets, Some cores_per_socket in + (* BIOS or EFI firmware? *) let firmware = xpath_string_default "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/vmw:Config[@vmw:key=\"firmware\"]/@vmw:value" "bios" in let firmware @@ -78,7 +98,7 @@ let parse_ovf_from_ova ovf_filename | s -> error (f_"unknown Config:firmware value %s (expected \"bios\" or \"efi\")") s in - name, memory, vcpu, firmware, + name, memory, vcpu, cpu_sockets, cpu_cores, firmware, parse_disks (), parse_removables (), parse_nics () (* Helper function to return the parent controller of a disk. *) diff --git a/v2v/parse_ovf_from_ova.mli b/v2v/parse_ovf_from_ova.mli index 3f60abc..54cdcf2 100644 --- a/v2v/parse_ovf_from_ova.mli +++ b/v2v/parse_ovf_from_ova.mli @@ -29,8 +29,9 @@ type ovf_disk = { } (** A VMDK disk from a parsed OVF. *) -val parse_ovf_from_ova : string -> string option * int64 * int * Types.source_firmware * ovf_disk list * Types.source_removable list * Types.source_nic list +val parse_ovf_from_ova : string -> string option * int64 * int * int option * int option * Types.source_firmware * ovf_disk list * Types.source_removable list * Types.source_nic list (** Parse an OVF file. The returned tuple is - [name, memory, vcpu, firmware, disks, removables, nics] *) + [name, memory, vcpu, cpu_sockets, cpu_cores, firmware, + disks, removables, nics] *) diff --git a/v2v/test-v2v-i-ova-formats.expected b/v2v/test-v2v-i-ova-formats.expected index 7049aee..11b24e0 100644 --- a/v2v/test-v2v-i-ova-formats.expected +++ b/v2v/test-v2v-i-ova-formats.expected @@ -4,6 +4,9 @@ Source guest information (--print-source option): hypervisor type: vmware memory: 1073741824 (bytes) nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: sockets: - cores/socket: - threads/core: - CPU features: firmware: uefi display: diff --git a/v2v/test-v2v-i-ova-gz.expected b/v2v/test-v2v-i-ova-gz.expected index 50ba746..11db2a3 100644 --- a/v2v/test-v2v-i-ova-gz.expected +++ b/v2v/test-v2v-i-ova-gz.expected @@ -4,6 +4,9 @@ Source guest information (--print-source option): hypervisor type: vmware memory: 1073741824 (bytes) nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: sockets: - cores/socket: - threads/core: - CPU features: firmware: bios display: diff --git a/v2v/test-v2v-i-ova-subfolders.expected b/v2v/test-v2v-i-ova-subfolders.expected index b6fdb07..4ef8b4b 100644 --- a/v2v/test-v2v-i-ova-subfolders.expected +++ b/v2v/test-v2v-i-ova-subfolders.expected @@ -4,6 +4,9 @@ Source guest information (--print-source option): hypervisor type: vmware memory: 1073741824 (bytes) nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: sockets: - cores/socket: - threads/core: - CPU features: firmware: uefi display: diff --git a/v2v/test-v2v-i-ova-tar.expected b/v2v/test-v2v-i-ova-tar.expected index 7049aee..11b24e0 100644 --- a/v2v/test-v2v-i-ova-tar.expected +++ b/v2v/test-v2v-i-ova-tar.expected @@ -4,6 +4,9 @@ Source guest information (--print-source option): hypervisor type: vmware memory: 1073741824 (bytes) nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: sockets: - cores/socket: - threads/core: - CPU features: firmware: uefi display: diff --git a/v2v/test-v2v-i-ova-two-disks.expected b/v2v/test-v2v-i-ova-two-disks.expected index cc850a7..b0bb3ef 100644 --- a/v2v/test-v2v-i-ova-two-disks.expected +++ b/v2v/test-v2v-i-ova-two-disks.expected @@ -4,6 +4,9 @@ Source guest information (--print-source option): hypervisor type: vmware memory: 1073741824 (bytes) nr vCPUs: 1 + CPU vendor: + CPU model: + CPU topology: sockets: - cores/socket: - threads/core: - CPU features: firmware: bios display: diff --git a/v2v/test-v2v-print-source.expected b/v2v/test-v2v-print-source.expected index b947927..6e78aad 100644 --- a/v2v/test-v2v-print-source.expected +++ b/v2v/test-v2v-print-source.expected @@ -2,7 +2,10 @@ hypervisor type: kvm memory: 1073741824 (bytes) nr vCPUs: 1 - CPU features: + CPU vendor: Intel + CPU model: Broadwell + CPU topology: sockets: 4 cores/socket: 8 threads/core: 2 + CPU features: pae,apic,acpi firmware: unknown display: video: qxl diff --git a/v2v/test-v2v-print-source.xml b/v2v/test-v2v-print-source.xml index 0667f2e..3768caf 100644 --- a/v2v/test-v2v-print-source.xml +++ b/v2v/test-v2v-print-source.xml @@ -1,6 +1,16 @@ <domain type='kvm'> <name>windows</name> <memory>1048576</memory> + <cpu match="minimum"> + <vendor>Intel</vendor> + <model fallback="allow">Broadwell</model> + <topology sockets="4" cores="8" threads="2"/> + </cpu> + <features> + <acpi/> + <apic/> + <pae/> + </features> <os> <type>hvm</type> <boot dev='hd'/> diff --git a/v2v/types.ml b/v2v/types.ml index d802e19..31cbbd2 100644 --- a/v2v/types.ml +++ b/v2v/types.ml @@ -29,6 +29,11 @@ type source = { s_orig_name : string; s_memory : int64; s_vcpu : int; + s_cpu_vendor : string option; + s_cpu_model : string option; + s_cpu_sockets : int option; + s_cpu_cores : int option; + s_cpu_threads : int option; s_features : string list; s_firmware : source_firmware; s_display : source_display option; @@ -102,6 +107,9 @@ let rec string_of_source s hypervisor type: %s memory: %Ld (bytes) nr vCPUs: %d + CPU vendor: %s + CPU model: %s + CPU topology: sockets: %s cores/socket: %s threads/core: %s CPU features: %s firmware: %s display: %s @@ -118,6 +126,11 @@ NICs: (string_of_source_hypervisor s.s_hypervisor) s.s_memory s.s_vcpu + (match s.s_cpu_vendor with None -> "" | Some v -> v) + (match s.s_cpu_model with None -> "" | Some v -> v) + (match s.s_cpu_sockets with None -> "-" | Some v -> string_of_int v) + (match s.s_cpu_cores with None -> "-" | Some v -> string_of_int v) + (match s.s_cpu_threads with None -> "-" | Some v -> string_of_int v) (String.concat "," s.s_features) (string_of_source_firmware s.s_firmware) (match s.s_display with diff --git a/v2v/types.mli b/v2v/types.mli index 31a974a..c902b7a 100644 --- a/v2v/types.mli +++ b/v2v/types.mli @@ -64,6 +64,11 @@ type source = { still saved here). *) s_memory : int64; (** Memory size (bytes). *) s_vcpu : int; (** Number of CPUs. *) + s_cpu_vendor : string option; (** Source CPU vendor. *) + s_cpu_model : string option; (** Source CPU model. *) + s_cpu_sockets : int option; (** Number of sockets. *) + s_cpu_cores : int option; (** Number of cores per socket. *) + s_cpu_threads : int option; (** Number of threads per core. *) s_features : string list; (** Machine features. *) s_firmware : source_firmware; (** Firmware (BIOS or EFI). *) s_display : source_display option; (** Guest display. *) diff --git a/v2v/v2v.ml b/v2v/v2v.ml index 551524d..bd3a413 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -181,7 +181,34 @@ and open_source cmdline input assert (source.s_name <> ""); assert (source.s_memory > 0L); + assert (source.s_vcpu >= 1); + assert (source.s_cpu_vendor <> Some ""); + assert (source.s_cpu_model <> Some ""); + (match source.s_cpu_sockets with + | None -> () + | Some i when i > 0 -> () + | _ -> assert false); + (match source.s_cpu_cores with + | None -> () + | Some i when i > 0 -> () + | _ -> assert false); + (match source.s_cpu_threads with + | None -> () + | Some i when i > 0 -> () + | _ -> assert false); + (match source.s_cpu_sockets, source.s_cpu_cores, source.s_cpu_threads with + | None, None, None -> () (* no topology specified *) + | sockets, cores, threads -> + let sockets = match sockets with None -> 1 | Some v -> v in + let cores = match cores with None -> 1 | Some v -> v in + let threads = match threads with None -> 1 | Some v -> v in + let expected_vcpu = sockets * cores * threads in + if expected_vcpu <> source.s_vcpu then + warning (f_"source sockets * cores * threads <> number of vCPUs.\nSockets %d * cores per socket %d * threads %d = %d, but number of vCPUs = %d.\n\nThis is a problem with either the source metadata or the virt-v2v input module. In some circumstances this could stop the guest from booting on the target.") + sockets cores threads expected_vcpu source.s_vcpu + ); + if source.s_disks = [] then error (f_"source has no hard disks!"); List.iter ( -- 2.10.2
Richard W.M. Jones
2017-Mar-17 11:34 UTC
[Libguestfs] [PATCH v2 6/6] v2v: -i libvirt: If <vcpu> is missing, calculate it from CPU topology.
--- v2v/parse_libvirt_xml.ml | 18 ++++++++++++++++-- v2v/test-v2v-print-source.expected | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index 6032c31..2dee274 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -50,7 +50,7 @@ let parse_libvirt_xml ?conn xml let xpathctx = Xml.xpath_new_context doc in let xpath_string = xpath_string xpathctx and xpath_int = xpath_int xpathctx - and xpath_int_default = xpath_int_default xpathctx + (*and xpath_int_default = xpath_int_default xpathctx*) and xpath_int64_default = xpath_int64_default xpathctx in let hypervisor @@ -65,7 +65,6 @@ let parse_libvirt_xml ?conn xml | Some s -> s in let memory = xpath_int64_default "/domain/memory/text()" (1024L *^ 1024L) in let memory = memory *^ 1024L in - let vcpu = xpath_int_default "/domain/vcpu/text()" 1 in let cpu_vendor = xpath_string "/domain/cpu/vendor/text()" in let cpu_model = xpath_string "/domain/cpu/model/text()" in @@ -73,6 +72,21 @@ let parse_libvirt_xml ?conn xml let cpu_cores = xpath_int "/domain/cpu/topology/@cores" in let cpu_threads = xpath_int "/domain/cpu/topology/@threads" in + (* Get the <vcpu> field from the input XML. If not set then + * try calculating it from the <cpu> <topology> node. If that's + * not set either, then assume 1 vCPU. + *) + let vcpu = xpath_int "/domain/vcpu/text()" in + let vcpu + match vcpu, cpu_sockets, cpu_cores, cpu_threads with + | Some vcpu, _, _, _ -> vcpu + | None, None, None, None -> 1 + | None, _, _, _ -> + let sockets = match cpu_sockets with None -> 1 | Some v -> v in + let cores = match cpu_cores with None -> 1 | Some v -> v in + let threads = match cpu_threads with None -> 1 | Some v -> v in + sockets * cores * threads in + let features let features = ref [] in let obj = Xml.xpath_eval_expression xpathctx "/domain/features/*" in diff --git a/v2v/test-v2v-print-source.expected b/v2v/test-v2v-print-source.expected index 6e78aad..df40ec9 100644 --- a/v2v/test-v2v-print-source.expected +++ b/v2v/test-v2v-print-source.expected @@ -1,7 +1,7 @@ source name: windows hypervisor type: kvm memory: 1073741824 (bytes) - nr vCPUs: 1 + nr vCPUs: 64 CPU vendor: Intel CPU model: Broadwell CPU topology: sockets: 4 cores/socket: 8 threads/core: 2 -- 2.10.2
Pino Toscano
2017-Mar-17 13:35 UTC
Re: [Libguestfs] [PATCH v2 0/6] v2v: Pass CPU vendor, model and topology from source to target.
On Friday, 17 March 2017 12:33:56 CET Richard W.M. Jones wrote:> v1 -> v2: > > - Support for passing topology through -o glance. > > - Support for passing topology through -o rhv. > > - Use bool for acpi/apic/pae struct fields in virt-p2v. > > - Write the xpath expression in error messages instead of file/line. > > - Fix more memory leaks in virt-p2v cpuid.c. > > - Passes make check & check-valgrind. > > There may be some other minor changes. I believe that everything > mentioned in reviews has been fixed.Yes, it does, thank you. LGTM. Thanks, -- Pino Toscano
Seemingly Similar Threads
- [PATCH 0/4] Pass CPU vendor, model and topology from source to target.
- [PATCH 0/3] common/mlstdutils: Add Std_utils List and Option modules.
- [[PATCH v2 0/4] common/mlstdutils: Add Std_utils List and Option modules.
- [PATCH v2v v2 0/3] Use host-model
- [PATCH v3 0/2] common/mlstdutils: Extend the List module.