From 11ef6d48abcfc90c0a6e795a067547c289e883d6 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 18 Jun 2016 14:44:24 +0100 Subject: [PATCH] p2v: Use a wrapper script instead of long virt-v2v command lne. Instead of constructing and directly executing a long virt-v2v command line, build a wrapper script with the same command line and send it to the remote server (stored in //virt-v2v-wrapper.sh). This will make it a bit easier to construct more complex virt-v2v wrappers. Note this commit on its own is a simple refactoring and does not change any functionality. (cherry picked from commit 514893b68b6180b3adc5bb762e9d67054a9a7f63) --- p2v/conversion.c | 203 +++++++++++++++++++++++++++++++++---------------------- p2v/p2v.h | 2 +- p2v/ssh.c | 25 ++++++- p2v/virt-p2v.pod | 17 +++-- 4 files changed, 159 insertions(+), 88 deletions(-) diff --git a/p2v/conversion.c b/p2v/conversion.c index 5265a76..decaf7c 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -78,11 +79,11 @@ struct data_conn { int nbd_remote_port; /* remote NBD port on conversion server */ }; -static int send_quoted (mexp_h *, const char *s); static pid_t start_qemu_nbd (int nbd_local_port, const char *device); static int wait_qemu_nbd (int nbd_local_port, int timeout_seconds); static void cleanup_data_conns (struct data_conn *data_conns, size_t nr); static char *generate_libvirt_xml (struct config *, struct data_conn *); +static char *generate_wrapper_script (struct config *, const char *remote_dir); static const char *map_interface_to_network (struct config *, const char *interface); static char *conversion_error; @@ -168,7 +169,8 @@ start_conversion (struct config *config, size_t i, len; const size_t nr_disks = guestfs_int_count_strings (config->disks); struct data_conn data_conns[nr_disks]; - CLEANUP_FREE char *remote_dir = NULL, *libvirt_xml = NULL; + CLEANUP_FREE char *remote_dir = NULL, *libvirt_xml = NULL, + *wrapper_script = NULL; time_t now; struct tm tm; mexp_h *control_h = NULL; @@ -276,7 +278,18 @@ start_conversion (struct config *config, goto out; #if DEBUG_STDERR - fprintf (stderr, "%s: libvirt XML:\n%s", guestfs_int_program_name, libvirt_xml); + fprintf (stderr, "%s: libvirt XML:\n%s", + guestfs_int_program_name, libvirt_xml); +#endif + + /* Generate the virt-v2v wrapper script. */ + wrapper_script = generate_wrapper_script (config, remote_dir); + if (wrapper_script == NULL) + goto out; + +#if DEBUG_STDERR + fprintf (stderr, "%s: wrapper script:\n%s", + guestfs_int_program_name, wrapper_script); #endif /* Get the output from the 'dmesg' command. We will store this @@ -305,7 +318,9 @@ start_conversion (struct config *config, if (notify_ui) notify_ui (NOTIFY_STATUS, _("Setting up the control connection ...")); - control_h = start_remote_connection (config, remote_dir, libvirt_xml, dmesg); + control_h = start_remote_connection (config, + remote_dir, libvirt_xml, + wrapper_script, dmesg); if (control_h == NULL) { const char *err = get_ssh_error (); @@ -317,66 +332,18 @@ start_conversion (struct config *config, if (notify_ui) notify_ui (NOTIFY_STATUS, _("Doing conversion ...")); - /* Build the virt-v2v command up in pieces to make the quoting - * slightly more sane. - */ - if (mexp_printf (control_h, "( %s virt-v2v%s%s -i libvirtxml", - config->sudo ? "sudo -n " : "", - config->verbose ? " -v -x" : "", - feature_colours_option ? " --colours" : "") == -1) { - printf_fail: - set_conversion_error ("mexp_printf: virt-v2v command: %m"); + if (mexp_printf (control_h, + /* To simplify things in the wrapper script, it + * writes virt-v2v's exit status to + * /remote_dir/status, and here we read that and + * exit the ssh shell with the same status. + */ + "%s/virt-v2v-wrapper.sh; " + "exit $(< %s/status)\n", + remote_dir, remote_dir) == -1) { + set_conversion_error ("mexp_printf: virt-v2v: %m"); goto out; } - if (config->output) { /* -o */ - if (mexp_printf (control_h, " -o ") == -1) - goto printf_fail; - if (send_quoted (control_h, config->output) == -1) - goto printf_fail; - } - switch (config->output_allocation) { /* -oa */ - case OUTPUT_ALLOCATION_NONE: - /* nothing */ - break; - case OUTPUT_ALLOCATION_SPARSE: - if (mexp_printf (control_h, " -oa sparse") == -1) - goto printf_fail; - break; - case OUTPUT_ALLOCATION_PREALLOCATED: - if (mexp_printf (control_h, " -oa preallocated") == -1) - goto printf_fail; - break; - default: - abort (); - } - if (config->output_format) { /* -of */ - if (mexp_printf (control_h, " -of ") == -1) - goto printf_fail; - if (send_quoted (control_h, config->output_format) == -1) - goto printf_fail; - } - if (config->output_storage) { /* -os */ - if (mexp_printf (control_h, " -os ") == -1) - goto printf_fail; - if (send_quoted (control_h, config->output_storage) == -1) - goto printf_fail; - } - if (mexp_printf (control_h, " --root first") == -1) - goto printf_fail; - if (mexp_printf (control_h, " %s/physical.xml", remote_dir) == -1) - goto printf_fail; - /* no stdin, and send stdout and stderr to the same place */ - if (mexp_printf (control_h, " &1") == -1) - goto printf_fail; - if (mexp_printf (control_h, " ; echo $? > %s/status", remote_dir) == -1) - goto printf_fail; - if (mexp_printf (control_h, " ) | tee %s/virt-v2v-conversion-log.txt", - remote_dir) == -1) - goto printf_fail; - if (mexp_printf (control_h, "; exit $(< %s/status)", remote_dir) == -1) - goto printf_fail; - if (mexp_printf (control_h, "\n") == -1) - goto printf_fail; /* Read output from the virt-v2v process and echo it through the * notify function, until virt-v2v closes the connection. @@ -443,26 +410,6 @@ cancel_conversion (void) set_cancel_requested (1); } -/* Send a shell-quoted string to remote. */ -static int -send_quoted (mexp_h *h, const char *s) -{ - if (mexp_printf (h, "\"") == -1) - return -1; - while (*s) { - if (*s == '$' || *s == '`' || *s == '\\' || *s == '"') { - if (mexp_printf (h, "\\") == -1) - return -1; - } - if (mexp_printf (h, "%c", *s) == -1) - return -1; - ++s; - } - if (mexp_printf (h, "\"") == -1) - return -1; - return 0; -} - /* Note: returns process ID (> 0) or 0 if there is an error. */ static pid_t start_qemu_nbd (int port, const char *device) @@ -980,3 +927,95 @@ map_interface_to_network (struct config *config, const char *interface) /* No mapping found. */ return "default"; } + +/** + * Print a shell-quoted string on C. + */ +static void +print_quoted (FILE *fp, const char *s) +{ + fprintf (fp, "\""); + while (*s) { + if (*s == '$' || *s == '`' || *s == '\\' || *s == '"') + fprintf (fp, "\\"); + fprintf (fp, "%c", *s); + ++s; + } + fprintf (fp, "\""); +} + +/** + * Construct the virt-v2v wrapper script. + * + * This will be sent to the remote server, and is easier than trying + * to "type" a long and complex single command line into the ssh + * connection when we start the conversion. + */ +static char * +generate_wrapper_script (struct config *config, const char *remote_dir) +{ + FILE *fp; + char *output = NULL; + size_t output_len = 0; + + fp = open_memstream (&output, &output_len); + if (fp == NULL) + error (EXIT_FAILURE, errno, "open_memstream"); + + fprintf (fp, "#!/bin/sh -\n"); + fprintf (fp, "\n"); + + /* The virt-v2v command. */ + fprintf (fp, "(\n"); + if (config->sudo) + fprintf (fp, "sudo -n "); + fprintf (fp, "virt-v2v"); + if (config->verbose) + fprintf (fp, " -v -x"); + if (feature_colours_option) + fprintf (fp, " --colours"); + fprintf (fp, " -i libvirtxml"); + + if (config->output) { /* -o */ + fprintf (fp, " -o "); + print_quoted (fp, config->output); + } + + switch (config->output_allocation) { /* -oa */ + case OUTPUT_ALLOCATION_NONE: + /* nothing */ + break; + case OUTPUT_ALLOCATION_SPARSE: + fprintf (fp, " -oa sparse"); + break; + case OUTPUT_ALLOCATION_PREALLOCATED: + fprintf (fp, " -oa preallocated"); + break; + default: + abort (); + } + + if (config->output_format) { /* -of */ + fprintf (fp, " -of "); + print_quoted (fp, config->output_format); + } + + if (config->output_storage) { /* -os */ + fprintf (fp, " -os "); + print_quoted (fp, config->output_storage); + } + + fprintf (fp, " --root first"); + fprintf (fp, " %s/physical.xml", remote_dir); + /* no stdin, and send stdout and stderr to the same place */ + fprintf (fp, " &1"); + fprintf (fp, "\n"); + fprintf (fp, "echo $? > %s/status", remote_dir); + fprintf (fp, "\n"); + fprintf (fp, " ) | tee %s/virt-v2v-conversion-log.txt", remote_dir); + fprintf (fp, "\n"); + + fclose (fp); + + return output; /* caller frees */ +} diff --git a/p2v/p2v.h b/p2v/p2v.h index 12dd210..49c97b6 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -127,7 +127,7 @@ extern int conversion_is_running (void); /* ssh.c */ extern int test_connection (struct config *); extern mexp_h *open_data_connection (struct config *, int *local_port, int *remote_port); -extern mexp_h *start_remote_connection (struct config *, const char *remote_dir, const char *libvirt_xml, const char *dmesg); +extern mexp_h *start_remote_connection (struct config *, const char *remote_dir, const char *libvirt_xml, const char *wrapper_script, const char *dmesg); extern const char *get_ssh_error (void); /* utils.c */ diff --git a/p2v/ssh.c b/p2v/ssh.c index 266c236..a8c762d 100644 --- a/p2v/ssh.c +++ b/p2v/ssh.c @@ -904,7 +904,7 @@ wait_for_prompt (mexp_h *h) mexp_h * start_remote_connection (struct config *config, const char *remote_dir, const char *libvirt_xml, - const char *dmesg) + const char *wrapper_script, const char *dmesg) { mexp_h *h; char magic[9]; @@ -960,6 +960,29 @@ start_remote_connection (struct config *config, if (wait_for_prompt (h) == -1) goto error; + /* Upload the wrapper script to the remote directory. */ + if (mexp_printf (h, + "cat > '%s/virt-v2v-wrapper.sh' << '__%s__'\n" + "%s" + "__%s__\n", + remote_dir, magic, + wrapper_script, + magic) == -1) { + set_ssh_error ("mexp_printf: %m"); + goto error; + } + + if (wait_for_prompt (h) == -1) + goto error; + + if (mexp_printf (h, "chmod +x %s/virt-v2v-wrapper.sh\n", remote_dir) == -1) { + set_ssh_error ("mexp_printf: %m"); + goto error; + } + + if (wait_for_prompt (h) == -1) + goto error; + if (dmesg != NULL) { /* Upload the physical host dmesg to the remote directory. */ if (mexp_printf (h, diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod index 5ceafa7..0c233c5 100644 --- a/p2v/virt-p2v.pod +++ b/p2v/virt-p2v.pod @@ -693,6 +693,13 @@ on the conversion server. If conversion fails, you should examine this log file, and you may be asked to supply the B, B log file in any bug reports or support tickets. +=item F + +I<(during/after conversion)> + +This is the wrapper script which is used when running virt-v2v. For +interest only, do not attempt to run this script yourself. + =back Before conversion actually begins, virt-p2v then makes one or more @@ -729,10 +736,12 @@ used. Secondly libguestfs creates an overlay on top of the NBD connection which stores writes in a temporary file on the conversion file. -The final step is to send the S> -command to the conversion server over the control connection. This -references the F file (see above), which in turn -references the NBD listening port(s) of the data connection(s). +The long S> command is +wrapped inside a wrapper script and uploaded to the conversion server. +The final step is to run this wrapper script, in turn running the +virt-v2v command. The virt-v2v command references the F +file (see above), which in turn references the NBD listening port(s) +of the data connection(s). Output from the virt-v2v command (messages, debugging etc) is saved both in the log file on the conversion server, and sent over the -- 1.8.3.1