From 11ef6d48abcfc90c0a6e795a067547c289e883d6 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
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 /<remote_dir>/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 <unistd.h>
#include <time.h>
#include <errno.h>
+#include <error.h>
#include <locale.h>
#include <libintl.h>
#include <netdb.h>
@@ -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, " </dev/null 2>&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<fp>.
+ */
+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, " </dev/null 2>&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<complete>,
B<unedited> log file in any bug reports or support tickets.
+=item F<virt-v2v-wrapper.sh>
+
+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<C<virt-v2v -i libvirtxml physical.xml ...>>
-command to the conversion server over the control connection. This
-references the F<physical.xml> file (see above), which in turn
-references the NBD listening port(s) of the data connection(s).
+The long S<C<virt-v2v -i libvirtxml physical.xml ...>> 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<physical.xml>
+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