--- trunk/vz-clone.pl 2007/10/04 13:19:26 22 +++ trunk/vz-clone.pl 2007/10/04 23:42:24 27 @@ -1,17 +1,57 @@ #!/usr/bin/perl -w use strict; -use Shell qw/rsync vzlist lvcreate mount umount lvremove/; +use Shell qw/rsync vzlist lvcreate mount umount lvremove which lvdisplay mke2fs vzctl/; my $vz = '/vz'; my $conf = '/etc/vz/conf'; my $snap_size = '100M'; -my $VEID = shift @ARGV || die "Usage: $0 VEID\n\n",vzlist; +my ( $action, $VEID ) = @ARGV; + +my $use_snapshot = 0; + +my $valid_actions = 'create|umount|destroy'; + +die "Usage: $0 ($valid_actions) VEID\n\n",vzlist unless $VEID && $action && $action =~ m/($valid_actions)/; my $CVEID = "10$VEID"; +sub vzctl_stop { + my $veid = shift; + my $vzlist = vzlist(); + if ( $vzlist =~ m/$veid/ ) { + print "stop $veid\n"; + vzctl('stop', $veid); + } +} + + +if ( $action eq 'create' ) { + vzctl_stop( $CVEID ); +} elsif ( $action eq 'destroy' ) { + vzctl_stop( $VEID ); +} + + +if ( 1 || $action =~ m/(umount|destroy)/ ) { + my $clone; + open(my $m, '-|', 'mount') || die "can't open mount: $!"; + while(<$m>) { + my @v = split(/\s+/,$_); + if ( $v[2] =~ m!/(private|root)/$VEID$! && $v[5] =~ m/bind/ ) { + umount( $v[2] ); + } elsif ( $v[2] =~ m!vz-clone-$VEID$! ) { + $clone = $v[2]; + } + } + umount( $clone ) if $clone; +} + +#die "unknown action: $action" unless $action eq 'create'; +exit unless $action eq 'create'; + my $orig_conf = "/etc/vz/conf/$VEID.conf"; die "$VEID config $orig_conf doesn't exist\n" unless -e $orig_conf; @@ -26,15 +66,26 @@ my @v = split(/\s+/,$_); if ( $v[1] =~ m/\Q$vz\E/ ) { $vz_lv = $v[0]; - warn "found lv $vz_lv for $vz\n"; + warn "found LV $vz_lv for $vz\n"; last; } } +sub fs_quota { + my $id = shift; + open(my $q, '-|', "vzquota -b show $id") || die "can't exec vzquota show $id: $!"; + my $l = <$q>; + $l =~ s/^\s+//; + my ( $usage, $soft, $hard ) = split(/\s+/,$l); + warn "quota for $id | $soft < $hard | usage: $usage\n"; + return ( $usage, $soft, $hard ) if wantarray; + return $soft; +} + sub copy_files { my ( $from, $to ) = @_; warn "rsync $from -> $to\n"; - rsync('-ra', "$from/", "$to/" ); + rsync('-raSHAD', "$from/", "$to/" ); } if ( $vz_lv ) { @@ -42,25 +93,72 @@ my ( $vz_lv_path, $vz_lv_name ) = ( $1, $2 ) if ( $vz_lv =~ m!^(.+)/([^/]+)$! ); my $snap = $vz_lv_name . '-snap'; + my $clone = "vz-clone-$CVEID"; + + sub do_mount { + my ( $from, $to ) = @_; + mkdir $to || die "can't create $to: $!"; + print "Mounting $from to $to\n"; + mount( $from, $to, '-o', 'noatime' ); + } + + sub test_mkdir { + my $dir = shift; + if ( ! -d $dir ) { + mkdir $dir || die "can't mkdir $dir: $!"; + } + } + + sub mount_bind { + my ( $from, $to ) = @_; + die "$from doesn't exist!" unless -d $from; + test_mkdir( $to ); + mount( '--bind', $from, $to ); + } + + if ( $use_snapshot ) { + + print "Creating $snap_size snapshot $snap from $vz_lv\n"; + lvcreate( '--size', $snap_size, '--snapshot', '--name', $snap, $vz_lv ); + + do_mount( "$vz_lv_path/$snap", "/tmp/$snap" ); - print "Creating $snap_size snapshot $snap from $vz_lv\n"; + } + + my $clone_size = fs_quota( $VEID ) . 'k'; + my $vg_name = $1 if ( $vz_lv_path =~ m!/([^/]+)/*$! ); + + if ( lvdisplay( "$vz_lv_path/$clone" ) ) { + warn "using existing $vz_lv_path/$clone\n"; + } else { + print "Creating LV $clone ($clone_size bytes) in VG $vg_name for $VEID clone filesystem\n"; + lvcreate( '--size', $clone_size, '--name', $clone, $vg_name ); + mke2fs( '-m', 0, '-j', "$vz_lv_path/$clone" ); + } + + do_mount( "$vz_lv_path/$clone", "/tmp/$clone" ); - lvcreate( '--size', $snap_size, '--snapshot', '--name', $snap, $vz_lv ); + test_mkdir( "/tmp/$clone/private" ); + test_mkdir( "/tmp/$clone/root" ); - my $snap_path = "/tmp/$snap"; - mkdir $snap_path || die "can't create $snap_path: $!"; + mount_bind( "/tmp/$clone/private", "$vz/private/$CVEID" ); + mount_bind( "/tmp/$clone/root", "$vz/root/$CVEID" ); - print "Mounting $vz_lv_path/$snap to $snap_path\n"; + if ( $use_snapshot ) { - mount( "$vz_lv_path/$snap", $snap_path, '-o', 'noatime' ); + copy_files( "/tmp/$snap/private/$VEID", "$vz/private/$CVEID" ); - copy_files( "$snap_path/private/$VEID", "$vz/private/$CVEID" ); + print "Cleanup\n"; - print "Umount $snap_path and remove $vz_lv_path/$snap\n"; + #umount( "$vz_lv_path/$clone" ); + umount( "$vz_lv_path/$snap" ); - umount( $snap_path ); + lvremove( '-f', "$vz_lv_path/$snap" ); - lvremove( '-f', "$vz_lv_path/$snap" ); + } else { + copy_files( "$vz/private/$VEID", "$vz/private/$CVEID" ); + + } } else {