#!/usr/bin/perl -w
use strict;
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 ( $action, $VEID ) = @ARGV;
my $use_snapshot = 0;
my $valid_actions = 'create|mount|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 ( $action =~ m/(create|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)/$CVEID$! && $v[5] =~ m/bind/ ) {
umount( $v[2] );
} elsif ( $v[2] =~ m!vz-clone-$CVEID$! ) {
$clone = $v[2];
}
}
umount( $clone ) if $clone;
exit if $action eq 'umount';
}
my $orig_conf = "/etc/vz/conf/$VEID.conf";
die "$VEID config $orig_conf doesn't exist\n" unless -e $orig_conf;
print "Clone VE $VEID -> $CVEID\n";
my $vz_lv;
open(my $m, '<', '/etc/fstab') || die "can't open /etc/fstab: $!";
while(<$m>) {
next if m/^#/;
my @v = split(/\s+/,$_);
if ( $v[1] =~ m/\Q$vz\E/ ) {
$vz_lv = $v[0];
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 ) = @_;
return if $action ne 'create';
warn "rsync $from -> $to\n";
rsync('-raSHAD', '--delete', "$from/", "$to/" );
}
if ( $vz_lv ) {
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" );
}
my $vg_name = $1 if ( $vz_lv_path =~ m!/([^/]+)/*$! );
if ( lvdisplay( "$vz_lv_path/$clone" ) ) {
warn "using existing $vz_lv_path/$clone\n";
} else {
my $clone_size = fs_quota( $VEID ) . 'k';
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" );
test_mkdir( "/tmp/$clone/private" );
test_mkdir( "/tmp/$clone/root" );
mount_bind( "/tmp/$clone/private", "$vz/private/$CVEID" );
mount_bind( "/tmp/$clone/root", "$vz/root/$CVEID" );
if ( $use_snapshot ) {
copy_files( "/tmp/$snap/private/$VEID", "/tmp/$clone/private" );
print "Cleanup\n";
#umount( "$vz_lv_path/$clone" );
umount( "$vz_lv_path/$snap" );
lvremove( '-f', "$vz_lv_path/$snap" );
} else {
copy_files( "$vz/private/$VEID", "/tmp/$clone/private" );
}
} else {
copy_files( "$vz/private/$VEID", "$vz/private/$CVEID" );
}
exit if $action eq 'mount';
my $note = "# modified by $0\n";
my $ve_clone_ip;
sub fix_ip {
my $ip = shift;
$ip =~ s/['"]//g;
# $ip =~ s/^\d+\./10./;
$ip =~ s/^\d+\.\d+\./192.168./;
warn "$CVEID new IP number: $ip\n";
$ve_clone_ip = $ip;
return $note . qq{IP_ADDRESS="$ip"};
}
sub fix_hostname {
my $hostname = shift;
$hostname =~ s/['"]//g;
$hostname = "clone-$hostname";
warn "$CVEID new hostname: $hostname\n";
return $note . qq{HOSTNAME="$hostname"};
}
open(my $o, '<', "$conf/$VEID.conf") || die "can't open $conf/$VEID.conf: $!";
open(my $n, '>', "$conf/$CVEID.conf") || die "can't open $conf/$CVEID.conf: $!";
while(<$o>) {
s!^HOSTNAME=(.*)$!fix_hostname($1)!ie;
s!^IP_ADDRESS=(.*)$!fix_ip($1)!ie;
s!^(ONBOOT=).*$!# modified by $0\n$1"no"!i;
print $n $_;
}
print "\nPlease review config file: $conf/$CVEID.conf\nAdd NAT for new VE with: iptables -t nat -A POSTROUTING -s $ve_clone_ip -o eth0 -j MASQUERADE\nStart clone of $VEID with: vzctl start $CVEID\n"