mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
p2v: Use 'scp' to copy the files to remote debugging directory.
Previously we copied / creates files in the remote dir by running
complex shell commands like:
cat > file <<'EOF'
## the file was copied in here
EOF
This was a little hairy, but in particular it doesn't allow us to set
the ssh session to cooked mode (so we can send ^C to cancel a
conversion).
A cleaner way to do it is to use 'scp' to copy the files over.
This commit is contained in:
@@ -27,6 +27,7 @@ EXTRA_DIST = \
|
||||
p2v.ks.in \
|
||||
p2v.service \
|
||||
test-virt-p2v-pxe.sshd_config.in \
|
||||
test-virt-p2v-scp.sh \
|
||||
test-virt-p2v-ssh.sh \
|
||||
virt-p2v.pod \
|
||||
virt-p2v-make-disk.in \
|
||||
|
||||
279
p2v/conversion.c
279
p2v/conversion.c
@@ -44,6 +44,7 @@
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
@@ -95,9 +96,12 @@ struct data_conn {
|
||||
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 void generate_name (struct config *, const char *filename);
|
||||
static void generate_libvirt_xml (struct config *, struct data_conn *, const char *filename);
|
||||
static void generate_wrapper_script (struct config *, const char *remote_dir, const char *filename);
|
||||
static void generate_dmesg_file (const char *filename);
|
||||
static const char *map_interface_to_network (struct config *, const char *interface);
|
||||
static void print_quoted (FILE *fp, const char *s);
|
||||
|
||||
static char *conversion_error;
|
||||
|
||||
@@ -179,15 +183,16 @@ start_conversion (struct config *config,
|
||||
int status;
|
||||
size_t i, len;
|
||||
size_t nr_disks = guestfs_int_count_strings (config->disks);
|
||||
CLEANUP_FREE struct data_conn *data_conns = NULL;
|
||||
CLEANUP_FREE char *remote_dir = NULL, *libvirt_xml = NULL,
|
||||
*wrapper_script = NULL;
|
||||
time_t now;
|
||||
struct tm tm;
|
||||
mexp_h *control_h = NULL;
|
||||
char dmesg_cmd[] = "dmesg > /tmp/dmesg.XXXXXX", *dmesg_file = &dmesg_cmd[8];
|
||||
CLEANUP_FREE char *dmesg = NULL;
|
||||
int fd, r;
|
||||
CLEANUP_FREE struct data_conn *data_conns = NULL;
|
||||
CLEANUP_FREE char *remote_dir = NULL;
|
||||
char tmpdir[] = "/tmp/p2v.XXXXXX";
|
||||
char name_file[] = "/tmp/p2v.XXXXXX/name";
|
||||
char libvirt_xml_file[] = "/tmp/p2v.XXXXXX/physical.xml";
|
||||
char wrapper_script[] = "/tmp/p2v.XXXXXX/virt-v2v-wrapper.sh";
|
||||
char dmesg_file[] = "/tmp/p2v.XXXXXX/dmesg";
|
||||
|
||||
#if DEBUG_STDERR
|
||||
print_config (config, stderr);
|
||||
@@ -285,59 +290,53 @@ start_conversion (struct config *config,
|
||||
if (notify_ui)
|
||||
notify_ui (NOTIFY_LOG_DIR, remote_dir);
|
||||
|
||||
/* Generate the libvirt XML. */
|
||||
libvirt_xml = generate_libvirt_xml (config, data_conns);
|
||||
if (libvirt_xml == NULL)
|
||||
goto out;
|
||||
|
||||
#if DEBUG_STDERR && 0
|
||||
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 && 0
|
||||
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
|
||||
* on the remote server.
|
||||
*/
|
||||
fd = mkstemp (dmesg_file);
|
||||
if (fd == -1) {
|
||||
perror ("mkstemp");
|
||||
goto skip_dmesg;
|
||||
}
|
||||
close (fd);
|
||||
r = system (dmesg_cmd);
|
||||
if (r == -1) {
|
||||
perror ("system");
|
||||
goto skip_dmesg;
|
||||
}
|
||||
if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
|
||||
fprintf (stderr, "'dmesg' failed (ignored)\n");
|
||||
goto skip_dmesg;
|
||||
/* Generate the local temporary directory. */
|
||||
if (mkdtemp (tmpdir) == NULL) {
|
||||
perror ("mkdtemp");
|
||||
cleanup_data_conns (data_conns, nr_disks);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
memcpy (name_file, tmpdir, strlen (tmpdir));
|
||||
memcpy (libvirt_xml_file, tmpdir, strlen (tmpdir));
|
||||
memcpy (wrapper_script, tmpdir, strlen (tmpdir));
|
||||
memcpy (dmesg_file, tmpdir, strlen (tmpdir));
|
||||
|
||||
ignore_value (read_whole_file (dmesg_file, &dmesg, NULL));
|
||||
skip_dmesg:
|
||||
/* Generate the static files. */
|
||||
generate_name (config, name_file);
|
||||
generate_libvirt_xml (config, data_conns, libvirt_xml_file);
|
||||
generate_wrapper_script (config, remote_dir, wrapper_script);
|
||||
generate_dmesg_file (dmesg_file);
|
||||
|
||||
/* Open the control connection and start conversion */
|
||||
/* Open the control connection. This also creates remote_dir. */
|
||||
if (notify_ui)
|
||||
notify_ui (NOTIFY_STATUS, _("Setting up the control connection ..."));
|
||||
|
||||
control_h = start_remote_connection (config,
|
||||
remote_dir, libvirt_xml,
|
||||
wrapper_script, dmesg);
|
||||
control_h = start_remote_connection (config, remote_dir);
|
||||
if (control_h == NULL) {
|
||||
const char *err = get_ssh_error ();
|
||||
set_conversion_error ("could not open control connection over SSH to the conversion server: %s",
|
||||
get_ssh_error ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_conversion_error ("could not open control connection over SSH to the conversion server: %s", err);
|
||||
/* Copy the static files to the remote dir. */
|
||||
if (scp_file (config, name_file, remote_dir) == -1) {
|
||||
set_conversion_error ("scp: %s to %s: %s",
|
||||
name_file, remote_dir, get_ssh_error ());
|
||||
goto out;
|
||||
}
|
||||
if (scp_file (config, libvirt_xml_file, remote_dir) == -1) {
|
||||
set_conversion_error ("scp: %s to %s: %s",
|
||||
libvirt_xml_file, remote_dir, get_ssh_error ());
|
||||
goto out;
|
||||
}
|
||||
if (scp_file (config, wrapper_script, remote_dir) == -1) {
|
||||
set_conversion_error ("scp: %s to %s: %s",
|
||||
wrapper_script, remote_dir, get_ssh_error ());
|
||||
goto out;
|
||||
}
|
||||
if (scp_file (config, dmesg_file, remote_dir) == -1) {
|
||||
set_conversion_error ("scp: %s to %s: %s",
|
||||
dmesg_file, remote_dir, get_ssh_error ());
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -689,20 +688,16 @@ cleanup_data_conns (struct data_conn *data_conns, size_t nr)
|
||||
/* Macros "inspired" by src/launch-libvirt.c */
|
||||
/* <element */
|
||||
#define start_element(element) \
|
||||
if (xmlTextWriterStartElement (xo, BAD_CAST (element)) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterStartElement: %m"); \
|
||||
return NULL; \
|
||||
} \
|
||||
if (xmlTextWriterStartElement (xo, BAD_CAST (element)) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterStartElement"); \
|
||||
do
|
||||
|
||||
/* finish current </element> */
|
||||
#define end_element() \
|
||||
while (0); \
|
||||
do { \
|
||||
if (xmlTextWriterEndElement (xo) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterEndElement: %m"); \
|
||||
return NULL; \
|
||||
} \
|
||||
if (xmlTextWriterEndElement (xo) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterEndElement"); \
|
||||
} while (0)
|
||||
|
||||
/* <element/> */
|
||||
@@ -711,39 +706,39 @@ cleanup_data_conns (struct data_conn *data_conns, size_t nr)
|
||||
|
||||
/* key=value attribute of the current element. */
|
||||
#define attribute(key,value) \
|
||||
if (xmlTextWriterWriteAttribute (xo, BAD_CAST (key), BAD_CAST (value)) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterWriteAttribute: %m"); \
|
||||
return NULL; \
|
||||
}
|
||||
do { \
|
||||
if (xmlTextWriterWriteAttribute (xo, BAD_CAST (key), BAD_CAST (value)) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterWriteAttribute"); \
|
||||
} while (0)
|
||||
|
||||
/* key=value, but value is a printf-style format string. */
|
||||
#define attribute_format(key,fs,...) \
|
||||
if (xmlTextWriterWriteFormatAttribute (xo, BAD_CAST (key), \
|
||||
fs, ##__VA_ARGS__) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterWriteFormatAttribute: %m"); \
|
||||
return NULL; \
|
||||
}
|
||||
do { \
|
||||
if (xmlTextWriterWriteFormatAttribute (xo, BAD_CAST (key), \
|
||||
fs, ##__VA_ARGS__) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterWriteFormatAttribute"); \
|
||||
} while (0)
|
||||
|
||||
/* A string, eg. within an element. */
|
||||
#define string(str) \
|
||||
if (xmlTextWriterWriteString (xo, BAD_CAST (str)) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterWriteString: %m"); \
|
||||
return NULL; \
|
||||
}
|
||||
#define string(str) \
|
||||
do { \
|
||||
if (xmlTextWriterWriteString (xo, BAD_CAST (str)) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterWriteString"); \
|
||||
} while (0)
|
||||
|
||||
/* A string, using printf-style formatting. */
|
||||
#define string_format(fs,...) \
|
||||
if (xmlTextWriterWriteFormatString (xo, fs, ##__VA_ARGS__) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterWriteFormatString: %m"); \
|
||||
return NULL; \
|
||||
}
|
||||
do { \
|
||||
if (xmlTextWriterWriteFormatString (xo, fs, ##__VA_ARGS__) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterWriteFormatString"); \
|
||||
} while (0)
|
||||
|
||||
/* An XML comment. */
|
||||
#define comment(str) \
|
||||
if (xmlTextWriterWriteComment (xo, BAD_CAST (str)) == -1) { \
|
||||
set_conversion_error ("xmlTextWriterWriteComment: %m"); \
|
||||
return NULL; \
|
||||
}
|
||||
do { \
|
||||
if (xmlTextWriterWriteComment (xo, BAD_CAST (str)) == -1) \
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterWriteComment"); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Write the libvirt XML for this physical machine.
|
||||
@@ -752,41 +747,23 @@ cleanup_data_conns (struct data_conn *data_conns, size_t nr)
|
||||
* virt-v2v on the conversion server. Virt-v2v will (if necessary)
|
||||
* generate the final libvirt XML.
|
||||
*/
|
||||
static char *
|
||||
generate_libvirt_xml (struct config *config, struct data_conn *data_conns)
|
||||
static void
|
||||
generate_libvirt_xml (struct config *config, struct data_conn *data_conns,
|
||||
const char *filename)
|
||||
{
|
||||
uint64_t memkb;
|
||||
char *ret;
|
||||
CLEANUP_XMLBUFFERFREE xmlBufferPtr xb = NULL;
|
||||
xmlOutputBufferPtr ob;
|
||||
CLEANUP_XMLFREETEXTWRITER xmlTextWriterPtr xo = NULL;
|
||||
size_t i;
|
||||
|
||||
xb = xmlBufferCreate ();
|
||||
if (xb == NULL) {
|
||||
set_conversion_error ("xmlBufferCreate: %m");
|
||||
return NULL;
|
||||
}
|
||||
ob = xmlOutputBufferCreateBuffer (xb, NULL);
|
||||
if (ob == NULL) {
|
||||
set_conversion_error ("xmlOutputBufferCreateBuffer: %m");
|
||||
return NULL;
|
||||
}
|
||||
xo = xmlNewTextWriter (ob);
|
||||
if (xo == NULL) {
|
||||
set_conversion_error ("xmlNewTextWriter: %m");
|
||||
return NULL;
|
||||
}
|
||||
xo = xmlNewTextWriterFilename (filename, 0);
|
||||
if (xo == NULL)
|
||||
error (EXIT_FAILURE, errno, "xmlNewTextWriterFilename");
|
||||
|
||||
if (xmlTextWriterSetIndent (xo, 1) == -1 ||
|
||||
xmlTextWriterSetIndentString (xo, BAD_CAST " ") == -1) {
|
||||
set_conversion_error ("could not set XML indent: %m");
|
||||
return NULL;
|
||||
}
|
||||
if (xmlTextWriterStartDocument (xo, NULL, NULL, NULL) == -1) {
|
||||
set_conversion_error ("xmlTextWriterStartDocument: %m");
|
||||
return NULL;
|
||||
}
|
||||
xmlTextWriterSetIndentString (xo, BAD_CAST " ") == -1)
|
||||
error (EXIT_FAILURE, errno, "could not set XML indent");
|
||||
if (xmlTextWriterStartDocument (xo, NULL, NULL, NULL) == -1)
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterStartDocument");
|
||||
|
||||
memkb = config->memory / 1024;
|
||||
|
||||
@@ -929,17 +906,8 @@ generate_libvirt_xml (struct config *config, struct data_conn *data_conns)
|
||||
|
||||
} end_element (); /* </domain> */
|
||||
|
||||
if (xmlTextWriterEndDocument (xo) == -1) {
|
||||
set_conversion_error ("xmlTextWriterEndDocument: %m");
|
||||
return NULL;
|
||||
}
|
||||
ret = (char *) xmlBufferDetach (xb); /* caller frees */
|
||||
if (ret == NULL) {
|
||||
set_conversion_error ("xmlBufferDetach: %m");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
if (xmlTextWriterEndDocument (xo) == -1)
|
||||
error (EXIT_FAILURE, errno, "xmlTextWriterEndDocument");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -976,19 +944,18 @@ map_interface_to_network (struct config *config, const char *interface)
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a shell-quoted string on C<fp>.
|
||||
* Write the guest name into C<filename>.
|
||||
*/
|
||||
static void
|
||||
print_quoted (FILE *fp, const char *s)
|
||||
generate_name (struct config *config, const char *filename)
|
||||
{
|
||||
fprintf (fp, "\"");
|
||||
while (*s) {
|
||||
if (*s == '$' || *s == '`' || *s == '\\' || *s == '"')
|
||||
fprintf (fp, "\\");
|
||||
fprintf (fp, "%c", *s);
|
||||
++s;
|
||||
}
|
||||
fprintf (fp, "\"");
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen (filename, "w");
|
||||
if (fp == NULL)
|
||||
error (EXIT_FAILURE, errno, "fopen: %s", filename);
|
||||
fprintf (fp, "%s\n", config->guestname);
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -998,16 +965,15 @@ print_quoted (FILE *fp, const char *s)
|
||||
* 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)
|
||||
static void
|
||||
generate_wrapper_script (struct config *config, const char *remote_dir,
|
||||
const char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
char *output = NULL;
|
||||
size_t output_len = 0;
|
||||
|
||||
fp = open_memstream (&output, &output_len);
|
||||
fp = fopen (filename, "w");
|
||||
if (fp == NULL)
|
||||
error (EXIT_FAILURE, errno, "open_memstream");
|
||||
error (EXIT_FAILURE, errno, "fopen: %s", filename);
|
||||
|
||||
fprintf (fp, "#!/bin/bash -\n");
|
||||
fprintf (fp, "\n");
|
||||
@@ -1106,7 +1072,42 @@ generate_wrapper_script (struct config *config, const char *remote_dir)
|
||||
"fi\n",
|
||||
remote_dir);
|
||||
|
||||
fprintf (fp, "\n");
|
||||
fprintf (fp, "# EOF\n");
|
||||
fclose (fp);
|
||||
|
||||
return output; /* caller frees */
|
||||
if (chmod (filename, 0755) == -1)
|
||||
error (EXIT_FAILURE, errno, "chmod: %s", filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the output of the C<dmesg> command into C<filename>.
|
||||
*
|
||||
* If the command fails, this is non-fatal.
|
||||
*/
|
||||
static void
|
||||
generate_dmesg_file (const char *filename)
|
||||
{
|
||||
CLEANUP_FREE char *cmd = NULL;
|
||||
|
||||
if (asprintf (&cmd, "dmesg >%s 2>&1", filename) == -1)
|
||||
error (EXIT_FAILURE, errno, "asprintf");
|
||||
|
||||
ignore_value (system (cmd));
|
||||
}
|
||||
|
||||
@@ -130,8 +130,9 @@ 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 *wrapper_script, const char *dmesg);
|
||||
extern mexp_h *start_remote_connection (struct config *, const char *remote_dir);
|
||||
extern const char *get_ssh_error (void);
|
||||
extern int scp_file (struct config *config, const char *localfile, const char *remotefile);
|
||||
|
||||
/* utils.c */
|
||||
extern uint64_t get_blockdev_size (const char *dev);
|
||||
|
||||
255
p2v/ssh.c
255
p2v/ssh.c
@@ -553,6 +553,182 @@ start_ssh (struct config *config, char **extra_args, int wait_prompt)
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file to remote using L<scp(1)>.
|
||||
*
|
||||
* This is a simplified version of L</start_ssh> above.
|
||||
*/
|
||||
int
|
||||
scp_file (struct config *config, const char *localfile, const char *remotefile)
|
||||
{
|
||||
size_t j, nr_args;
|
||||
char port_str[64];
|
||||
char connect_timeout_str[128];
|
||||
CLEANUP_FREE char *remote = NULL;
|
||||
CLEANUP_FREE /* [sic] */ const char **args = NULL;
|
||||
mexp_h *h;
|
||||
const int ovecsize = 12;
|
||||
int ovector[ovecsize];
|
||||
int using_password_auth;
|
||||
|
||||
if (cache_ssh_identity (config) == -1)
|
||||
return -1;
|
||||
|
||||
/* Are we using password or identity authentication? */
|
||||
using_password_auth = config->identity_file == NULL;
|
||||
|
||||
/* Create the scp argument array. */
|
||||
if (using_password_auth)
|
||||
nr_args = 12;
|
||||
else
|
||||
nr_args = 14;
|
||||
args = malloc (sizeof (char *) * nr_args);
|
||||
if (args == NULL)
|
||||
error (EXIT_FAILURE, errno, "malloc");
|
||||
|
||||
j = 0;
|
||||
args[j++] = "scp";
|
||||
args[j++] = "-P"; /* Port. */
|
||||
snprintf (port_str, sizeof port_str, "%d", config->port);
|
||||
args[j++] = port_str;
|
||||
args[j++] = "-o"; /* Host key will always be novel. */
|
||||
args[j++] = "StrictHostKeyChecking=no";
|
||||
args[j++] = "-o"; /* ConnectTimeout */
|
||||
snprintf (connect_timeout_str, sizeof connect_timeout_str,
|
||||
"ConnectTimeout=%d", SSH_TIMEOUT);
|
||||
args[j++] = connect_timeout_str;
|
||||
if (using_password_auth) {
|
||||
/* Only use password authentication. */
|
||||
args[j++] = "-o";
|
||||
args[j++] = "PreferredAuthentications=keyboard-interactive,password";
|
||||
}
|
||||
else {
|
||||
/* Use identity file (private key). */
|
||||
args[j++] = "-o";
|
||||
args[j++] = "PreferredAuthentications=publickey";
|
||||
args[j++] = "-i";
|
||||
args[j++] = config->identity_file;
|
||||
}
|
||||
args[j++] = localfile;
|
||||
if (asprintf (&remote, "%s@%s:%s",
|
||||
config->username ? config->username : "root",
|
||||
config->server, remotefile) == -1)
|
||||
error (EXIT_FAILURE, errno, "asprintf");
|
||||
args[j++] = remote;
|
||||
args[j++] = NULL;
|
||||
assert (j == nr_args);
|
||||
|
||||
#if DEBUG_STDERR && 0
|
||||
size_t i;
|
||||
|
||||
fputs ("scp command: ", stderr);
|
||||
for (i = 0; i < nr_args - 1; ++i) {
|
||||
if (i > 0) fputc (' ', stderr);
|
||||
fputs (args[i], stderr);
|
||||
}
|
||||
fputc ('\n', stderr);
|
||||
#endif
|
||||
|
||||
/* Create the miniexpect handle. */
|
||||
h = mexp_spawnv ("scp", (char **) args);
|
||||
if (h == NULL) {
|
||||
set_ssh_internal_error ("scp: mexp_spawnv: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We want the ssh ConnectTimeout to be less than the miniexpect
|
||||
* timeout, so that if the server is completely unresponsive we
|
||||
* still see the error from ssh, not a timeout from miniexpect. The
|
||||
* obvious solution to this is to set ConnectTimeout (above) and to
|
||||
* set the miniexpect timeout to be a little bit larger.
|
||||
*/
|
||||
mexp_set_timeout (h, SSH_TIMEOUT + 20);
|
||||
|
||||
if (using_password_auth &&
|
||||
config->password && strlen (config->password) > 0) {
|
||||
CLEANUP_FREE char *ssh_message = NULL;
|
||||
|
||||
/* Wait for the password prompt. */
|
||||
wait_password_again:
|
||||
switch (mexp_expect (h,
|
||||
(mexp_regexp[]) {
|
||||
{ 100, .re = password_re },
|
||||
{ 101, .re = ssh_message_re },
|
||||
{ 0 }
|
||||
}, ovector, ovecsize)) {
|
||||
case 100: /* Got password prompt. */
|
||||
if (mexp_printf (h, "%s\n", config->password) == -1) {
|
||||
set_ssh_mexp_error ("mexp_printf");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 101:
|
||||
free (ssh_message);
|
||||
ssh_message = strndup (&h->buffer[ovector[2]], ovector[3]-ovector[2]);
|
||||
goto wait_password_again;
|
||||
|
||||
case MEXP_EOF:
|
||||
/* This is where we get to if the user enters an incorrect or
|
||||
* impossible hostname or port number. Hopefully scp printed an
|
||||
* error message, and we picked it up and put it in
|
||||
* 'ssh_message' in case 101 above. If not we have to print a
|
||||
* generic error instead.
|
||||
*/
|
||||
if (ssh_message)
|
||||
set_ssh_error ("%s", ssh_message);
|
||||
else
|
||||
set_ssh_error ("scp closed the connection without printing an error.");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
|
||||
case MEXP_TIMEOUT:
|
||||
set_ssh_unexpected_timeout ("password prompt");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
|
||||
case MEXP_ERROR:
|
||||
set_ssh_mexp_error ("mexp_expect");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
|
||||
case MEXP_PCRE_ERROR:
|
||||
set_ssh_pcre_error ();
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the scp subprocess to finish. */
|
||||
switch (mexp_expect (h, NULL, NULL, 0)) {
|
||||
case MEXP_EOF:
|
||||
break;
|
||||
|
||||
case MEXP_TIMEOUT:
|
||||
set_ssh_unexpected_timeout ("copying (scp) file");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
|
||||
case MEXP_ERROR:
|
||||
set_ssh_mexp_error ("mexp_expect");
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
|
||||
case MEXP_PCRE_ERROR:
|
||||
set_ssh_pcre_error ();
|
||||
mexp_close (h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mexp_close (h) == -1) {
|
||||
set_ssh_internal_error ("scp: mexp_close: %m");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_input_driver (const char *name, size_t len);
|
||||
static void add_output_driver (const char *name, size_t len);
|
||||
static int compatible_version (const char *v2v_version);
|
||||
@@ -955,17 +1131,9 @@ wait_for_prompt (mexp_h *h)
|
||||
}
|
||||
|
||||
mexp_h *
|
||||
start_remote_connection (struct config *config,
|
||||
const char *remote_dir, const char *libvirt_xml,
|
||||
const char *wrapper_script, const char *dmesg)
|
||||
start_remote_connection (struct config *config, const char *remote_dir)
|
||||
{
|
||||
mexp_h *h;
|
||||
char magic[9];
|
||||
|
||||
if (guestfs_int_random_string (magic, 8) == -1) {
|
||||
perror ("random_string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h = start_ssh (config, NULL, 1);
|
||||
if (h == NULL)
|
||||
@@ -980,16 +1148,9 @@ start_remote_connection (struct config *config,
|
||||
if (wait_for_prompt (h) == -1)
|
||||
goto error;
|
||||
|
||||
/* Write some useful config information to files in the remote directory. */
|
||||
if (mexp_printf (h, "echo '%s' > %s/name\n",
|
||||
config->guestname, remote_dir) == -1) {
|
||||
set_ssh_mexp_error ("mexp_printf");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wait_for_prompt (h) == -1)
|
||||
goto error;
|
||||
|
||||
/* It's simplest to create the remote 'time' file by running the date
|
||||
* command, as that won't send any special control characters.
|
||||
*/
|
||||
if (mexp_printf (h, "date > %s/time\n", remote_dir) == -1) {
|
||||
set_ssh_mexp_error ("mexp_printf");
|
||||
goto error;
|
||||
@@ -998,62 +1159,6 @@ start_remote_connection (struct config *config,
|
||||
if (wait_for_prompt (h) == -1)
|
||||
goto error;
|
||||
|
||||
/* Upload the guest libvirt XML to the remote directory. */
|
||||
if (mexp_printf (h,
|
||||
"cat > '%s/physical.xml' << '__%s__'\n"
|
||||
"%s"
|
||||
"__%s__\n",
|
||||
remote_dir, magic,
|
||||
libvirt_xml,
|
||||
magic) == -1) {
|
||||
set_ssh_mexp_error ("mexp_printf");
|
||||
goto error;
|
||||
}
|
||||
|
||||
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_mexp_error ("mexp_printf");
|
||||
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_mexp_error ("mexp_printf");
|
||||
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,
|
||||
"cat > '%s/dmesg' << '__%s__'\n"
|
||||
"%s"
|
||||
"\n"
|
||||
"__%s__\n",
|
||||
remote_dir, magic,
|
||||
dmesg,
|
||||
magic) == -1) {
|
||||
set_ssh_mexp_error ("mexp_printf");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wait_for_prompt (h) == -1)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return h;
|
||||
|
||||
error:
|
||||
|
||||
58
p2v/test-virt-p2v-scp.sh
Executable file
58
p2v/test-virt-p2v-scp.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash -
|
||||
# Copyright (C) 2014-2016 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.
|
||||
|
||||
# This is an scp substitute used by test-virt-p2v.sh.
|
||||
|
||||
TEMP=`getopt \
|
||||
-o 'o:P:' \
|
||||
-- "$@"`
|
||||
if [ $? != 0 ]; then
|
||||
echo "$0: problem parsing the command line arguments"
|
||||
exit 1
|
||||
fi
|
||||
eval set -- "$TEMP"
|
||||
|
||||
while true ; do
|
||||
case "$1" in
|
||||
# Regular arguments that we can just ignore.
|
||||
-o|-P)
|
||||
shift 2
|
||||
;;
|
||||
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "$0: internal error ($1)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Hopefully there are two arguments left, the source (local) file
|
||||
# and a remote file of the form user@server:remote.
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "$0: incorrect number of arguments found:" "$@"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local="$1"
|
||||
remote="$(echo $2 | awk -F: '{print $2}')"
|
||||
|
||||
# Use the copy command.
|
||||
exec cp "$local" "$remote"
|
||||
@@ -50,10 +50,11 @@ d=test-virt-p2v.d
|
||||
rm -rf $d
|
||||
mkdir $d
|
||||
|
||||
# We don't want the program under test to run real 'ssh'. It's
|
||||
# unlikely to work. Therefore create a dummy 'ssh' binary.
|
||||
# We don't want the program under test to run real 'ssh' or 'scp'.
|
||||
# They won't work. Therefore create dummy 'ssh' and 'scp' binaries.
|
||||
pushd $d
|
||||
ln -sf ../test-virt-p2v-ssh.sh ssh
|
||||
ln -sf ../test-virt-p2v-scp.sh scp
|
||||
popd
|
||||
export PATH=$d:$PATH
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ by virt-p2v, and it will not work if this is disabled on the
|
||||
conversion server. (C<AllowTcpForwarding> must be C<yes> in the
|
||||
L<sshd_config(5)> file on the conversion server).
|
||||
|
||||
The scp (secure copy) feature of ssh is required by virt-p2v so it can
|
||||
send over small files (this is I<not> the method by which disks are
|
||||
copied).
|
||||
|
||||
The conversion server does not need to be a physical machine. It
|
||||
could be a virtual machine, as long as it has sufficient memory and
|
||||
disk space to do the conversion, and as long as the physical machine
|
||||
|
||||
Reference in New Issue
Block a user