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:
Richard W.M. Jones
2016-06-30 13:42:53 +01:00
parent c2fab43dd6
commit 6d9bac80b2
7 changed files with 388 additions and 217 deletions

View File

@@ -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 \

View File

@@ -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));
}

View File

@@ -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
View File

@@ -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
View 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"

View File

@@ -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

View File

@@ -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