mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Add support for hotplugging (adding disks) to the libvirt attach-method.
When libvirt is used, we can allow disks to be hotplugged. guestfs_add_drive can be called after launch to hot-add a disk. When a disk is hot-added, we first ask libvirt to add the disk to the appliance, then we make an internal call into the appliance to get it to wait for the disk to appear (ie. udev_settle ()). Hot-added disks are tracked in the g->drives array. This also adds a test.
This commit is contained in:
@@ -52,6 +52,7 @@ SUBDIRS += tests/9p
|
||||
SUBDIRS += tests/rsync
|
||||
SUBDIRS += tests/bigdirs
|
||||
SUBDIRS += tests/disk-labels
|
||||
SUBDIRS += tests/hotplug
|
||||
SUBDIRS += tests/regressions
|
||||
endif
|
||||
|
||||
|
||||
@@ -1379,6 +1379,7 @@ AC_CONFIG_FILES([Makefile
|
||||
tests/disk-labels/Makefile
|
||||
tests/extra/Makefile
|
||||
tests/guests/Makefile
|
||||
tests/hotplug/Makefile
|
||||
tests/luks/Makefile
|
||||
tests/lvm/Makefile
|
||||
tests/md/Makefile
|
||||
|
||||
@@ -125,6 +125,7 @@ guestfsd_SOURCES = \
|
||||
guestfsd.c \
|
||||
headtail.c \
|
||||
hexdump.c \
|
||||
hotplug.c \
|
||||
hivex.c \
|
||||
htonl.c \
|
||||
initrd.c \
|
||||
|
||||
67
daemon/hotplug.c
Normal file
67
daemon/hotplug.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "guestfs_protocol.h"
|
||||
#include "daemon.h"
|
||||
#include "actions.h"
|
||||
|
||||
#define HOT_ADD_TIMEOUT 30 /* seconds */
|
||||
|
||||
/* Wait for /dev/disk/guestfs/<label> to appear. Timeout (and error)
|
||||
* if it doesn't appear after a reasonable length of time.
|
||||
*/
|
||||
int
|
||||
do_internal_hot_add_drive (const char *label)
|
||||
{
|
||||
time_t start_t, now_t;
|
||||
size_t len = strlen (label);
|
||||
char path[len+64];
|
||||
int r;
|
||||
|
||||
snprintf (path, len+64, "/dev/disk/guestfs/%s", label);
|
||||
|
||||
time (&start_t);
|
||||
|
||||
while (time (&now_t) - start_t <= HOT_ADD_TIMEOUT) {
|
||||
udev_settle ();
|
||||
|
||||
r = access (path, F_OK);
|
||||
if (r == -1 && errno != ENOENT) {
|
||||
reply_with_perror ("%s", path);
|
||||
return -1;
|
||||
}
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
sleep (1);
|
||||
}
|
||||
|
||||
reply_with_error ("hot-add drive: '%s' did not appear after %d seconds: "
|
||||
"this could mean that virtio-scsi (in qemu or kernel) "
|
||||
"or udev is not working",
|
||||
path, HOT_ADD_TIMEOUT);
|
||||
return -1;
|
||||
}
|
||||
@@ -72,11 +72,6 @@ alloc_disk (const char *filename, const char *size_str, int add, int sparse)
|
||||
if (parse_size (size_str, &size) == -1)
|
||||
return -1;
|
||||
|
||||
if (!guestfs_is_config (g)) {
|
||||
fprintf (stderr, _("can't allocate or add disks after launching\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC|O_CLOEXEC, 0666);
|
||||
if (fd == -1) {
|
||||
perror (filename);
|
||||
|
||||
@@ -1186,14 +1186,22 @@ not all belong to a single logical operating system
|
||||
name = "add_drive";
|
||||
style = RErr, [String "filename"], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"];
|
||||
once_had_no_optargs = true;
|
||||
fish_alias = ["add"]; config_only = true;
|
||||
fish_alias = ["add"];
|
||||
shortdesc = "add an image to examine or modify";
|
||||
longdesc = "\
|
||||
This function adds a disk image called C<filename> to the handle.
|
||||
C<filename> may be a regular host file or a host device.
|
||||
|
||||
The first time you call this function, the disk appears as
|
||||
C</dev/sda>, the second time as C</dev/sdb>, and so on.
|
||||
When this function is called before C<guestfs_launch> (the
|
||||
usual case) then the first time you call this function,
|
||||
the disk appears in the API as C</dev/sda>, the second time
|
||||
as C</dev/sdb>, and so on.
|
||||
|
||||
In libguestfs E<ge> 1.20 you can also call this function
|
||||
after launch (with some restrictions). This is called
|
||||
\"hotplugging\". When hotplugging, you must specify a
|
||||
C<label> so that the new disk gets a predictable name.
|
||||
For more information see L<guestfs(3)/HOTPLUGGING>.
|
||||
|
||||
You don't necessarily need to be root when using libguestfs. However
|
||||
you obviously do need sufficient permissions to access the filename
|
||||
@@ -9951,6 +9959,16 @@ This returns a hashtable, where keys are the disk labels
|
||||
are the full raw block device and partition names
|
||||
(eg. C</dev/sda> and C</dev/sda1>)." };
|
||||
|
||||
{ defaults with
|
||||
name = "internal_hot_add_drive";
|
||||
style = RErr, [String "label"], [];
|
||||
proc_nr = Some 370;
|
||||
in_fish = false; in_docs = false;
|
||||
tests = [];
|
||||
shortdesc = "internal hotplugging operation";
|
||||
longdesc = "\
|
||||
This function is used internally when hotplugging drives." };
|
||||
|
||||
]
|
||||
|
||||
(* Non-API meta-commands available only in guestfish.
|
||||
|
||||
@@ -42,6 +42,7 @@ daemon/guestfsd.c
|
||||
daemon/headtail.c
|
||||
daemon/hexdump.c
|
||||
daemon/hivex.c
|
||||
daemon/hotplug.c
|
||||
daemon/htonl.c
|
||||
daemon/initrd.c
|
||||
daemon/inotify.c
|
||||
|
||||
@@ -1 +1 @@
|
||||
369
|
||||
370
|
||||
|
||||
@@ -166,6 +166,9 @@ struct attach_ops {
|
||||
|
||||
int (*get_pid) (guestfs_h *g); /* get-pid API. */
|
||||
int (*max_disks) (guestfs_h *g); /* max-disks API. */
|
||||
|
||||
/* Hotplugging drives. */
|
||||
int (*hot_add_drive) (guestfs_h *g, struct drive *drv, size_t drv_index);
|
||||
};
|
||||
extern struct attach_ops attach_ops_appliance;
|
||||
extern struct attach_ops attach_ops_libvirt;
|
||||
|
||||
@@ -143,8 +143,11 @@ one you added), etc.
|
||||
|
||||
Once L</guestfs_launch> has been called you cannot add any more images.
|
||||
You can call L</guestfs_list_devices> to get a list of the device
|
||||
names, in the order that you added them. See also L</BLOCK DEVICE
|
||||
NAMING> below.
|
||||
names, in the order that you added them.
|
||||
See also L</BLOCK DEVICE NAMING> below.
|
||||
|
||||
There are slightly different rules when hotplugging disks (in
|
||||
libguestfs E<ge> 1.20). See L</HOTPLUGGING> below.
|
||||
|
||||
=head2 MOUNTING
|
||||
|
||||
@@ -601,6 +604,35 @@ Libguestfs on top of FUSE performs quite poorly. For best performance
|
||||
do not use it. Use ordinary libguestfs filesystem calls, upload,
|
||||
download etc. instead.
|
||||
|
||||
=head2 HOTPLUGGING
|
||||
|
||||
In libguestfs E<ge> 1.20, you may add drives after calling
|
||||
L</guestfs_launch>. There are some restrictions, see below.
|
||||
This is called I<hotplugging>.
|
||||
|
||||
Only a subset of the attach-method backends support hotplugging
|
||||
(currently only the libvirt attach-method has support). It also
|
||||
requires that you use libvirt E<ge> 0.10.3 and qemu E<ge> 1.2.
|
||||
|
||||
To hot-add a disk, simply call L</guestfs_add_drive_opts> after
|
||||
L</guestfs_launch>. It is mandatory to specify the C<label> parameter
|
||||
so that the newly added disk has a predictable name. For example:
|
||||
|
||||
if (guestfs_launch (g) == -1)
|
||||
error ("launch failed");
|
||||
|
||||
if (guestfs_add_drive_opts (g, filename,
|
||||
GUESTFS_ADD_DRIVE_OPTS_LABEL, "newdisk",
|
||||
-1) == -1)
|
||||
error ("hot-add of disk failed");
|
||||
|
||||
if (guestfs_part_disk ("/dev/disk/guestfs/newdisk", "mbr") == -1)
|
||||
error ("partitioning of hot-added disk failed");
|
||||
|
||||
Backends that support hotplugging do not require that you add
|
||||
E<ge> 1 disk before calling launch. When hotplugging is supported
|
||||
you don't need to add any disks.
|
||||
|
||||
=head2 INSPECTION
|
||||
|
||||
Libguestfs has APIs for inspecting an unknown disk image to find out
|
||||
@@ -2639,7 +2671,7 @@ The guest may be killed by L</guestfs_kill_subprocess>, or may die
|
||||
asynchronously at any time (eg. due to some internal error), and that
|
||||
causes the state to transition back to CONFIG.
|
||||
|
||||
Configuration commands for qemu such as L</guestfs_add_drive> can only
|
||||
Configuration commands for qemu such as L</guestfs_set_path> can only
|
||||
be issued when in the CONFIG state.
|
||||
|
||||
The API offers one call that goes from CONFIG through LAUNCHING to
|
||||
|
||||
@@ -135,14 +135,6 @@ launch_libvirt (guestfs_h *g, const char *libvirt_uri)
|
||||
struct drive *drv;
|
||||
size_t i;
|
||||
|
||||
/* At present you must add drives before starting the appliance. In
|
||||
* future when we enable hotplugging you won't need to do this.
|
||||
*/
|
||||
if (!g->nr_drives) {
|
||||
error (g, _("you must call guestfs_add_drive before guestfs_launch"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: It should be possible to make this work. */
|
||||
if (g->direct) {
|
||||
error (g, _("direct mode flag is not supported yet for libvirt attach method"));
|
||||
@@ -1376,6 +1368,89 @@ max_disks_libvirt (guestfs_h *g)
|
||||
return 255;
|
||||
}
|
||||
|
||||
static xmlChar *construct_libvirt_xml_hot_add_disk (guestfs_h *g, struct drive *drv, size_t drv_index);
|
||||
|
||||
/* Hot-add a drive. Note the appliance is up when this is called. */
|
||||
static int
|
||||
hot_add_drive_libvirt (guestfs_h *g, struct drive *drv, size_t drv_index)
|
||||
{
|
||||
virConnectPtr conn = g->virt.connv;
|
||||
virDomainPtr dom = g->virt.domv;
|
||||
xmlChar *xml = NULL;
|
||||
|
||||
if (!conn || !dom) {
|
||||
/* This is essentially an internal error if it happens. */
|
||||
error (g, "%s: conn == NULL or dom == NULL", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create overlay for read-only drive. This works around lack of
|
||||
* support for <transient/> disks in libvirt.
|
||||
*/
|
||||
if (make_qcow2_overlay_for_drive (g, drv) == -1)
|
||||
return -1;
|
||||
|
||||
/* Create the XML for the new disk. */
|
||||
xml = construct_libvirt_xml_hot_add_disk (g, drv, drv_index);
|
||||
if (xml == NULL)
|
||||
return -1;
|
||||
|
||||
/* Attach it. */
|
||||
if (virDomainAttachDeviceFlags (dom, (char *) xml,
|
||||
VIR_DOMAIN_DEVICE_MODIFY_LIVE) == -1) {
|
||||
libvirt_error (g, _("could not attach disk to libvirt domain"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
free (xml);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free (xml);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static xmlChar *
|
||||
construct_libvirt_xml_hot_add_disk (guestfs_h *g, struct drive *drv,
|
||||
size_t drv_index)
|
||||
{
|
||||
xmlChar *ret = NULL;
|
||||
xmlBufferPtr xb = NULL;
|
||||
xmlOutputBufferPtr ob;
|
||||
xmlTextWriterPtr xo = NULL;
|
||||
|
||||
XMLERROR (NULL, xb = xmlBufferCreate ());
|
||||
XMLERROR (NULL, ob = xmlOutputBufferCreateBuffer (xb, NULL));
|
||||
XMLERROR (NULL, xo = xmlNewTextWriter (ob));
|
||||
|
||||
XMLERROR (-1, xmlTextWriterSetIndent (xo, 1));
|
||||
XMLERROR (-1, xmlTextWriterSetIndentString (xo, BAD_CAST " "));
|
||||
XMLERROR (-1, xmlTextWriterStartDocument (xo, NULL, NULL, NULL));
|
||||
|
||||
if (construct_libvirt_xml_disk (g, xo, drv, drv_index) == -1)
|
||||
goto err;
|
||||
|
||||
XMLERROR (-1, xmlTextWriterEndDocument (xo));
|
||||
XMLERROR (NULL, ret = xmlBufferDetach (xb)); /* caller frees ret */
|
||||
|
||||
debug (g, "hot-add disk XML:\n%s", ret);
|
||||
|
||||
err:
|
||||
if (xo)
|
||||
xmlFreeTextWriter (xo); /* frees 'ob' too */
|
||||
if (xb)
|
||||
xmlBufferFree (xb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct attach_ops attach_ops_libvirt = {
|
||||
.launch = launch_libvirt,
|
||||
.shutdown = shutdown_libvirt,
|
||||
.max_disks = max_disks_libvirt,
|
||||
.hot_add_drive = hot_add_drive_libvirt,
|
||||
};
|
||||
|
||||
#else /* no libvirt or libxml2 at compile time */
|
||||
|
||||
#define NOT_IMPL(r) \
|
||||
@@ -1403,10 +1478,16 @@ max_disks_libvirt (guestfs_h *g)
|
||||
NOT_IMPL (-1);
|
||||
}
|
||||
|
||||
#endif /* no libvirt or libxml2 at compile time */
|
||||
static int
|
||||
hot_add_drive_libvirt (guestfs_h *g, struct drive *drv, size_t drv_index)
|
||||
{
|
||||
NOT_IMPL (-1);
|
||||
}
|
||||
|
||||
struct attach_ops attach_ops_libvirt = {
|
||||
.launch = launch_libvirt,
|
||||
.shutdown = shutdown_libvirt,
|
||||
.max_disks = max_disks_libvirt,
|
||||
};
|
||||
|
||||
#endif /* no libvirt or libxml2 at compile time */
|
||||
|
||||
80
src/launch.c
80
src/launch.c
@@ -29,6 +29,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "c-ctype.h"
|
||||
|
||||
@@ -57,14 +58,29 @@ guestfs___rollback_drives (guestfs_h *g, size_t old_i)
|
||||
g->nr_drives = old_i;
|
||||
}
|
||||
|
||||
/* Add struct drive to the g->drives vector at the given index. */
|
||||
static void
|
||||
add_drive_to_handle_at (guestfs_h *g, struct drive *d, size_t drv_index)
|
||||
{
|
||||
if (drv_index >= g->nr_drives) {
|
||||
g->drives = safe_realloc (g, g->drives,
|
||||
sizeof (struct drive *) * (drv_index + 1));
|
||||
while (g->nr_drives <= drv_index) {
|
||||
g->drives[g->nr_drives] = NULL;
|
||||
g->nr_drives++;
|
||||
}
|
||||
}
|
||||
|
||||
assert (g->drives[drv_index] == NULL);
|
||||
|
||||
g->drives[drv_index] = d;
|
||||
}
|
||||
|
||||
/* Add struct drive to the end of the g->drives vector in the handle. */
|
||||
static void
|
||||
add_drive_to_handle (guestfs_h *g, struct drive *d)
|
||||
{
|
||||
g->nr_drives++;
|
||||
g->drives = safe_realloc (g, g->drives,
|
||||
sizeof (struct drive *) * g->nr_drives);
|
||||
g->drives[g->nr_drives-1] = d;
|
||||
add_drive_to_handle_at (g, d, g->nr_drives);
|
||||
}
|
||||
|
||||
static struct drive *
|
||||
@@ -207,6 +223,48 @@ valid_disk_label (const char *str)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The low-level function that adds a drive. */
|
||||
static int
|
||||
add_drive (guestfs_h *g, struct drive *drv)
|
||||
{
|
||||
size_t i, drv_index;
|
||||
|
||||
if (g->state == CONFIG) {
|
||||
/* Not hotplugging, so just add it to the handle. */
|
||||
add_drive_to_handle (g, drv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ... else, hotplugging case. */
|
||||
if (!g->attach_ops || !g->attach_ops->hot_add_drive) {
|
||||
error (g, _("the current attach-method does not support hotplugging drives"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!drv->disk_label) {
|
||||
error (g, _("'label' is required when hotplugging drives"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the first free index, or add it at the end. */
|
||||
drv_index = g->nr_drives;
|
||||
for (i = 0; i < g->nr_drives; ++i)
|
||||
if (g->drives[i] == NULL)
|
||||
drv_index = i;
|
||||
|
||||
/* Hot-add the drive. */
|
||||
if (g->attach_ops->hot_add_drive (g, drv, drv_index) == -1)
|
||||
return -1;
|
||||
|
||||
add_drive_to_handle_at (g, drv, drv_index);
|
||||
|
||||
/* Call into the appliance to wait for the new drive to appear. */
|
||||
if (guestfs_internal_hot_add_drive (g, drv->disk_label) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Traditionally you have been able to use /dev/null as a filename, as
|
||||
* many times as you like. Ancient KVM (RHEL 5) cannot handle adding
|
||||
* /dev/null readonly. qemu 1.2 + virtio-scsi segfaults when you use
|
||||
@@ -220,7 +278,7 @@ add_null_drive (guestfs_h *g, int readonly, const char *format,
|
||||
const char *iface, const char *name, const char *disk_label)
|
||||
{
|
||||
char *tmpfile = NULL;
|
||||
int fd = -1;
|
||||
int fd = -1, r;
|
||||
struct drive *drv;
|
||||
|
||||
if (format && STRNEQ (format, "raw")) {
|
||||
@@ -252,8 +310,12 @@ add_null_drive (guestfs_h *g, int readonly, const char *format,
|
||||
}
|
||||
|
||||
drv = create_drive_struct (g, tmpfile, readonly, format, iface, name, disk_label, 0);
|
||||
add_drive_to_handle (g, drv);
|
||||
r = add_drive (g, drv);
|
||||
free (tmpfile);
|
||||
if (r == -1) {
|
||||
free_drive_struct (drv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -328,7 +390,11 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
|
||||
|
||||
drv = create_drive_struct (g, filename, readonly, format, iface, name, disk_label,
|
||||
use_cache_none);
|
||||
add_drive_to_handle (g, drv);
|
||||
if (add_drive (g, drv) == -1) {
|
||||
free_drive_struct (drv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
26
tests/hotplug/Makefile.am
Normal file
26
tests/hotplug/Makefile.am
Normal file
@@ -0,0 +1,26 @@
|
||||
# libguestfs
|
||||
# Copyright (C) 2012 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.
|
||||
|
||||
include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
TESTS = \
|
||||
test-hot-add.pl
|
||||
|
||||
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(TESTS)
|
||||
66
tests/hotplug/test-hot-add.pl
Executable file
66
tests/hotplug/test-hot-add.pl
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/perl
|
||||
# Copyright (C) 2012 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.
|
||||
|
||||
# Test hot-adding disks.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Sys::Guestfs;
|
||||
|
||||
my $g = Sys::Guestfs->new ();
|
||||
|
||||
# Skip the test if the default attach-method isn't libvirt, since only
|
||||
# the libvirt backend supports hotplugging.
|
||||
my $attach_method = $g->get_attach_method ();
|
||||
unless ($attach_method eq "libvirt" || $attach_method =~ /^libvirt:/) {
|
||||
print "$0: test skipped because attach-method ($attach_method) is not libvirt\n";
|
||||
exit 77
|
||||
}
|
||||
|
||||
# We don't need to add disks before launch.
|
||||
$g->launch ();
|
||||
|
||||
# Create some temporary disks.
|
||||
open FILE, ">test1.img" or die "test1.img: $!";
|
||||
truncate FILE, 512 * 1024 * 1024 or die "test1.img: truncate: $!";
|
||||
close FILE;
|
||||
|
||||
open FILE, ">test2.img" or die "test2.img: $!";
|
||||
truncate FILE, 512 * 1024 * 1024 or die "test2.img: truncate: $!";
|
||||
close FILE;
|
||||
|
||||
die unless system ("qemu-img create -f qcow2 test3.img 1G") == 0;
|
||||
|
||||
# Hot-add them. Labels are required.
|
||||
$g->add_drive ("test1.img", label => "a"); # autodetect format
|
||||
$g->add_drive ("test2.img", label => "b", format => "raw", readonly => 1);
|
||||
$g->add_drive ("test3.img", label => "c", format => "qcow2");
|
||||
|
||||
# Check we can use the disks immediately.
|
||||
$g->part_disk ("/dev/disk/guestfs/a", "mbr");
|
||||
$g->mkfs ("ext2", "/dev/disk/guestfs/c");
|
||||
$g->mkfs ("ext2", "/dev/disk/guestfs/a1");
|
||||
|
||||
$g->shutdown ();
|
||||
$g->close ();
|
||||
|
||||
unlink "test1.img";
|
||||
unlink "test2.img";
|
||||
unlink "test3.img";
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user