X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=tools%2Fvirt-resize;h=8a473ca3fcf39047e1d67b94f60ae134d8935042;hp=a70492e846acb9e60f96a11ac5064122a85b0106;hb=6d4815d3a4921219379bc9ec3cceead217668426;hpb=0e28e4104d96bf0bf5b88fb07bb7e5f9f6e6f41f;ds=sidebyside diff --git a/tools/virt-resize b/tools/virt-resize index a70492e..8a473ca 100755 --- a/tools/virt-resize +++ b/tools/virt-resize @@ -33,6 +33,8 @@ $Data::Dumper::Sortkeys = 1; die __"virt-resize: sorry this program does not work on a 32 bit host\n" if ~1 == 4294967294; +$| = 1; + =encoding utf8 =head1 NAME @@ -76,6 +78,11 @@ remaining space to /dev/sda2: virt-resize --resize /dev/sda1=+200M --expand /dev/sda2 olddisk newdisk +As above, but the output format will be uncompressed qcow2: + + qemu-img create -f qcow2 newdisk.qcow2 15G + virt-resize --expand /dev/sda2 olddisk newdisk.qcow2 + =head1 DETAILED USAGE =head2 EXPANDING A VIRTUAL MACHINE DISK @@ -228,6 +235,25 @@ contents of a partition. Deleting a partition removes it completely, but note that it also renumbers any partitions after the one which is deleted, which can leave some guests unbootable. +=head2 QCOW2 AND NON-SPARSE RAW FORMATS + +If the input disk is in qcow2 format, then you may prefer that the +output is in qcow2 format as well. Alternately, virt-resize can +convert the format on the fly. The output format is simply determined +by the format of the empty output container that you provide. Thus to +create qcow2 output, use: + + qemu-img create [-c] -f qcow2 outdisk [size] + +instead of the truncate command (use C<-c> for a compressed disk). + +Similarly, to get non-sparse raw output use: + + fallocate -l size outdisk + +(on older systems that don't have the L command use +C
) + =head1 OPTIONS =over 4 @@ -490,6 +516,36 @@ my $quiet; Don't print the summary. +=cut + +my $format; + +=item B<--format> raw + +Specify the format of the input disk image. If this flag is not +given then it is auto-detected from the image itself. + +If working with untrusted raw-format guest disk images, you should +ensure the format is always specified. + +Note that this option I affect the output format. +See L. + +=cut + +my $output_format; + +=item B<--output-format> raw + +Specify the format of the output disk image. If this flag is not +given then it is auto-detected from the image itself. + +If working with untrusted raw-format guest disk images, you should +ensure the format is always specified. + +Note that you still need to create the output disk with the right +format. See L. + =back =cut @@ -509,6 +565,8 @@ GetOptions ("help|?" => \$help, "d|debug" => \$debug, "n|dryrun|dry-run" => \$dryrun, "q|quiet" => \$quiet, + "format=s" => \$format, + "output-format=s" => \$output_format, ) or pod2usage (2); pod2usage (1) if $help; if ($version) { @@ -528,17 +586,71 @@ die __x("virt-resize: {file}: does not exist or is not readable\n", file => $inf die __x("virt-resize: {file}: does not exist or is not writable\nYou have to create the destination disk before running this program.\nPlease read the virt-resize(1) manpage for more information.\n", file => $outfile) unless -w $outfile; -my @s; -@s = stat $infile; -my $insize = S_ISREG ($s[2]) ? $s[7] : host_blockdevsize ($infile); -@s = stat $outfile; -my $outsize = S_ISREG ($s[2]) ? $s[7] : host_blockdevsize ($outfile); +# Add them to the handle and launch the appliance. +my $g; +launch_guestfs (); + +sub launch_guestfs +{ + $g = Sys::Guestfs->new (); + $g->set_trace (1) if $debug; + my @args = ($infile); + push @args, readonly => 1; + push @args, format => $format if defined $format; + $g->add_drive_opts (@args); + @args = ($outfile); + push @args, format => $output_format if defined $output_format; + $g->add_drive_opts (@args); + $g->set_progress_callback (\&progress_callback) unless $quiet; + $g->launch (); +} + +my $sectsize = $g->blockdev_getss ("/dev/sdb"); + +# Get the size in bytes of each disk. +# +# Originally we computed this by looking at the same of the host file, +# but of course this failed for qcow2 images (RHBZ#633096). The right +# way to do it is with $g->blockdev_getsize64. +my $insize = $g->blockdev_getsize64 ("/dev/sda"); +my $outsize = $g->blockdev_getsize64 ("/dev/sdb"); if ($debug) { print "$infile size $insize bytes\n"; print "$outfile size $outsize bytes\n"; } +# Create a partition table. +# +# We *must* do this before copying the bootloader across, and copying +# the bootloader must be careful not to disturb this partition table +# (RHBZ#633766). There are two reasons for this: +# +# (1) The 'parted' library is stupid and broken. In many ways. In +# this particular instance the stupid and broken bit is that it +# overwrites the whole boot sector when initializating a partition +# table. (Upstream don't consider this obvious problem to be a bug). +# +# (2) GPT has a backup partition table located at the end of the disk. +# It's non-movable, because the primary GPT contains fixed references +# to both the size of the disk and the backup partition table at the +# end. This would be a problem for any resize that didn't either +# carefully move the backup GPT (and rewrite those references) or +# recreate the whole partition table from scratch. + +my $parttype; +create_partition_table (); + +sub create_partition_table +{ + local $_; + + $parttype = $g->part_get_parttype ("/dev/sda"); + print "partition table type: $parttype\n" if $debug; + + $g->part_init ("/dev/sdb", $parttype); +} + # In reality the number of sectors containing boot loader data will be # less than this (although Windows 7 defaults to putting the first # partition on sector 2048, and has quite a large boot loader). @@ -548,14 +660,14 @@ if ($debug) { # offset of the first partition. # # It doesn't matter if we copy too much. -my $boot_sectors = 4096; +my $max_bootloader = 4096 * 512; die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n", file => $infile, sz => $insize) - if $insize < $boot_sectors * 512; + if $insize < $max_bootloader; die __x("virt-resize: {file}: file is too small to be a disk image ({sz} bytes)\n", file => $outfile, sz => $outsize) - if $outsize < $boot_sectors * 512; + if $outsize < $max_bootloader; # Copy the boot loader across. do_copy_boot_loader () if $copy_boot_loader; @@ -563,30 +675,28 @@ do_copy_boot_loader () if $copy_boot_loader; sub do_copy_boot_loader { print "copying boot loader ...\n" if $debug; - open IFILE, $infile or die "$infile: $!"; - my $s; - my $r = sysread (IFILE, $s, $boot_sectors * 512) or die "$infile: $!"; - die "$infile: short read" if $r < $boot_sectors * 512; - open OFILE, "+<$outfile" or die "$outfile: $!"; - sysseek OFILE, 0, SEEK_SET or die "$outfile: seek: $!"; - $r = syswrite (OFILE, $s, $boot_sectors * 512) or die "$outfile: $!"; - die "$outfile: short write" if $r < $boot_sectors * 512; -} -# Add them to the handle and launch the appliance. -my $g; -launch_guestfs (); + # Don't disturb the partition table that we just wrote. + # https://secure.wikimedia.org/wikipedia/en/wiki/Master_Boot_Record + # https://secure.wikimedia.org/wikipedia/en/wiki/GUID_Partition_Table -sub launch_guestfs -{ - $g = Sys::Guestfs->new (); - $g->set_trace (1) if $debug; - $g->add_drive_ro ($infile); - $g->add_drive ($outfile); - $g->launch (); -} + my $bootsect = $g->pread_device ("/dev/sda", 446, 0); + die __"virt-resize: short read" if length ($bootsect) < 446; -my $sectsize = $g->blockdev_getss ("/dev/sdb"); + $g->pwrite_device ("/dev/sdb", $bootsect, 0); + + my $start = 512; + if ($parttype eq "gpt") { + # XXX With 4K sectors does GPT just fit more entries in a + # sector, or does it always use 34 sectors? + $start = 17408; + } + + my $loader = $g->pread_device ("/dev/sda", $max_bootloader, $start); + die __"virt-resize: short read" if length ($loader) < $max_bootloader; + + $g->pwrite_device ("/dev/sdb", $loader, $start); +} my $to_be_expanded = 0; @@ -885,9 +995,9 @@ sub calculate_surplus # EFI partitioning + massive per-partition alignment. my $overhead = $sectsize * ( 2 * 64 + # GPT start and end - (64 * (@partitions + 1)) + # Maximum alignment - ($boot_sectors - 64) # Boot loader - ); + (64 * (@partitions + 1)) # Maximum alignment + ) + + ($max_bootloader - 64 * 512); # boot loader my $required = 0; foreach (@partitions) { @@ -996,36 +1106,12 @@ exit 0 if $dryrun; # Repartition the target disk. my $nextpart = 1; -my $parttype; repartition (); sub repartition { local $_; - if ($copy_boot_loader) { - $parttype = $g->part_get_parttype ("/dev/sdb"); - } else { - $parttype = "efi"; - } - print "partition table type: $parttype\n" if $debug; - - # Delete any existing partitions on the destination disk, - # but leave the bootloader that we copied over intact. - if ($copy_boot_loader) { - # Delete in reverse as an easy way to deal with extended - # partitions. - foreach (sort { $b cmp $a } $g->list_partitions ()) { - if (m{^/dev/.db(\d+)$}) { - $g->part_del ("/dev/sdb", $1); - } - } - } else { - # Didn't copy over the initial boot loader, so we need - # to make a new partition table here. - $g->part_init ("/dev/sdb", $parttype); - } - # Work out where to start the first partition. die __"virt-resize: source disk does not have a first partition\n" unless exists ($partitions{"/dev/sda1"}); @@ -1122,16 +1208,11 @@ sub copy_data } if (!$quiet && !$debug) { - local $| = 1; - print __x("Copying {p} ...", p => $part); + print __x("Copying {p} ...\n", p => $part); } $g->copy_size ($part, $target, $newsize < $oldsize ? $newsize : $oldsize); - - if (!$quiet && !$debug) { - print " ", __"done", "\n"; - } } } } @@ -1160,7 +1241,9 @@ sub restart_appliance $g = Sys::Guestfs->new (); $g->set_trace (1) if $debug; - $g->add_drive ($outfile); + my @args = ($outfile); + push @args, format => $output_format if defined $output_format; + $g->add_drive_opts (@args); $g->launch (); # Target partitions have changed from /dev/sdb to /dev/sda, @@ -1305,19 +1388,6 @@ sub human_size } } -# Return the size in bytes of a HOST block device. -sub host_blockdevsize -{ - local $_; - my $dev = shift; - - open BD, "PATH=/usr/sbin:/sbin:\$PATH blockdev --getsize64 $dev |" - or die "blockdev: $!"; - $_ = ; - chomp $_; - $_; -} - # The reverse of device name translation, see # BLOCK DEVICE NAMING in guestfs(3). sub canonicalize @@ -1330,6 +1400,25 @@ sub canonicalize $_; } +# Not as sophisticated as the guestfish progress bar, because +# I intend to use an external library for this at some point (XXX). +sub progress_callback +{ + my $proc_nr = shift; + my $serial = shift; + my $position = shift; + my $total = shift; + + my $ratio = $position / $total; + if ($ratio < 0) { $ratio = 0 } + elsif ($ratio > 1) { $ratio = 1 } + + my $dots = int ($ratio * 76); + + print "[", "#"x$dots, "-"x(76-$dots), "]\r"; + print "\n" if $ratio == 1; +} + =head1 NOTES =head2 "Partition 1 does not end on cylinder boundary." @@ -1354,6 +1443,23 @@ Windows may initiate a lengthy "chkdsk" on first boot after a resize, if NTFS partitions have been expanded. This is just a safety check and (unless it find errors) is nothing to worry about. +=head2 GUEST BOOT STUCK AT "GRUB" + +If a Linux guest does not boot after resizing, and the boot is stuck +after printing C on the console, try reinstalling grub. This +sometimes happens on older (RHEL 5-era) guests, for reasons we don't +fully understand, although we think is to do with partition alignment. + + guestfish -i -a newdisk + > cat /boot/grub/device.map + # check the contents of this file are sensible or + # edit the file if necessary + > grub-install / /dev/vda + > exit + +For more flexible guest reconfiguration, including if you need to +specify other parameters to grub-install, use L. + =head1 ALTERNATIVE TOOLS There are several proprietary tools for resizing partitions. We @@ -1370,6 +1476,13 @@ hand-calculating sector offsets, which is something that virt-resize was designed to avoid. If you want to see the guestfish-equivalent commands that virt-resize runs, use the C<--debug> flag. +=head1 SHELL QUOTING + +Libvirt guest names can contain arbitrary characters, some of which +have meaning to the shell such as C<#> and space. You may need to +quote or escape these characters on the command line. See the shell +manual page L for details. + =head1 SEE ALSO L, @@ -1384,6 +1497,11 @@ L, L, L, L, +L, +L, +L, +L, +L, L, L.