first cut at creating vmdk recovery image
[vmdk-backup] / vmdk-backup.pl
diff --git a/vmdk-backup.pl b/vmdk-backup.pl
new file mode 100755 (executable)
index 0000000..36a4157
--- /dev/null
@@ -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";
+