From 223be2b8c6a199a1db646aaaadb0d1cf8a808f25 Mon Sep 17 00:00:00 2001 From: Dobrica Pavlinusic Date: Wed, 1 Oct 2014 02:13:00 +0200 Subject: [PATCH] first cut at creating vmdk recovery image --- vmdk-backup.pl | 157 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100755 vmdk-backup.pl diff --git a/vmdk-backup.pl b/vmdk-backup.pl new file mode 100755 index 0000000..36a4157 --- /dev/null +++ b/vmdk-backup.pl @@ -0,0 +1,157 @@ +#!/usr/bin/perl +use warnings; +use strict; +use autodie qw(:all); +use Data::Dump qw(dump); + +my $start_t = time(); + +my $hostname = `hostname -s`; +chomp($hostname); + +# path to create vmdk into (not on same disk as backup) +my $vmdk="/mnt/backup/${hostname}.vmdk"; + +# root disk device name +my $sda="sda"; + +# name of new backup vg +my $vg_backup="backup"; + +# where to mount new filesystem while creating vmdk +my $tmp = "/tmp/backup"; + + +warn "collecting filesystem info\n"; + +my $blks; +open(my $blkid, '-|', 'blkid'); +while(<$blkid>) { + chomp; + my ( $dev, $params ) = split(/:\s+/, $_, 2); + foreach my $single ( split(/\s+/, $params ) ) { + my ( $name, $value ) = split(/=/, $single, 2); + $value =~ s/"//g; + $blks->{$dev}->{$name} = $value; + } +} +warn "blks = ",dump($blks); + +my @pvs; +open(my $pv, '-|', "pvs --noheadings --options pv_name --unbuffered"); +while(<$pv>) { + chomp; + s/ *//g; + if ( s{$sda}{mapper/nbd0p} ) { + push @pvs, $_; + } else { + warn "SKIP pv $_\n"; + } +} +warn "# pvs = ",dump(@pvs); + +my @lv_create; +my @lv_create_snapshot; +my @lv_remove; +open(my $lvs, '-|', "lvs --noheadings --options lv_name,vg_name,lv_size --units k"); +while(<$lvs>) { + chomp; + s/^\s*//; + s/\s*$//; + my ($name,$vg,$size) = split(/\s+/, $_, 3); + if ( $vg eq $vg_backup ) { + warn "SKIP $name in $vg_backup !\n"; + next; + } + push @lv_create, "lvcreate --name $name --size $size $vg_backup"; + push @lv_create, "lvcreate --snapshot /dev/$vg/$name --name ${name}-snap --size 100M"; + push @lv_remove, "/dev/$vg/$name-snap"; +} +warn "# lv_create = ",dump(@lv_create); + +my @mounts; +open(my $mount, '-|', 'mount'); +while(<$mount>) { + chomp; + my ($dev, undef, $path, undef, $fs, undef) = split(/\s+/,$_); + push @mounts, [ $dev, $path, $fs ]; +} +warn "# mounts = ",dump(@mounts); + +sub sh { + warn "# @_\n"; + system @_ unless $ENV{DEBUG}; +} + + +warn "begin vmdk creation...\n"; + +my $size = `blockdev --getsize64 /dev/$sda`; +sh "qemu-img create -f vmdk -o compat6 $vmdk $size"; + +sh "modprobe nbd"; + +my $nbd_pid; +if ( $nbd_pid = fork ) { + # parent + + sh "qemu-nbd --verbose --connect /dev/nbd0 $vmdk"; + + warn "finished in ", time() - $start_t, " seconds\n"; + exit 0; +} + +sleep 1; + +sh "sfdisk -d /dev/$sda | sfdisk --force /dev/nbd0"; + +sh "kpartx -av /dev/nbd0"; + +sh "pvcreate $_" foreach @pvs; + +sh "vgcreate $vg_backup @pvs"; + +sh $_ foreach @lv_create; + +mkdir $tmp unless -d $tmp; + +my @umount; + +foreach my $mount ( @mounts ) { + my ( $dev, $path, $fs ) = @$mount; + if ( exists $blks->{$dev} ) { + warn "working on $dev $path $fs\n"; + my $dev_backup = $dev; + $dev_backup =~ s{/dev/$sda}{/dev/mapper/nbd0p} || + $dev_backup =~ s{/dev/mapper/.+-([^-]+)}{/dev/mapper/$vg_backup-$1} || + die "can't map $dev to new backup device!"; + my $label = $blks->{$dev}->{LABEL}; + $label = $label ? "-L $label" : ''; + sh "mkfs.$fs $label $dev_backup"; + mkdir $tmp . $path unless -e $tmp . $path; + sh "mount $dev_backup $tmp$path"; + unshift @umount, $tmp . $path; + $dev .= '-snap' if -e $dev . '-snap'; + chdir $tmp . $path; + sh "dump -0 -f - $dev | restore -r -f -"; + } else { + warn "SKIP $dev $path $fs"; + } +} + + +warn "FIXME create swap\n"; + + +warn "cleanup...\n"; + +sh "umount $_" foreach @umount; + +sh "lvremove -f $_" foreach @lv_remove; + +sh "kpartx -dv /dev/nbd0"; + +sh "vgchange --available n $vg_backup"; + +sh "qemu-nbd --disconnect /dev/nbd0"; + -- 2.20.1