#!/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";

