Skip to content

Instantly share code, notes, and snippets.

@manabu
Created April 29, 2011 09:50
Show Gist options
  • Save manabu/948124 to your computer and use it in GitHub Desktop.
Save manabu/948124 to your computer and use it in GitHub Desktop.
kvmclone for ubuntu
#!/usr/bin/perl
#
# kvmclone.pl for ubuntu
#
# 2009/12/22 ver1.0
# 2011/02/02 ver1.1 add LANG=C
# 2011/04/23 ver1.2 support for RHEL6.0
#
# 2011/04/29 support for ubuntu
#
# test only 11.04.
# when you want to clone qcow2 image, you need "qemu-nbd".
#
use strict;
use vars qw( $opt_t $opt_o $opt_n $opt_i $opt_m $opt_g $opt_s $opt_h $opt_a );
use Getopt::Std;
$ENV{'LANG'} = "C";
sub chkopts {
getopts( 'ahs:t:o:n:i:k:g:b:c:m:' );
$opt_h = 1 unless ( $opt_t && $opt_o && $opt_n && $opt_i && $opt_m &&
$opt_g && $opt_s );
if ( $opt_h ) {
print <<EOF;
usage: kvmclone.pl [-aontsimg]
-a: Activate vm after cloning
-o: Original domain name
-n: New domain name
-t: Target disk image file
-s: Server hostname
-i: IP Address
-m: Net mask
-g: Default gateway
EOF
exit 0;
}
$opt_t = "/var/lib/libvirt/images/" . $opt_t unless ( $opt_t =~ m/^\// );
}
sub log_cmd {
print "===> Exec: $_[ 0 ]\n";
system ( $_[ 0 ] );
}
sub chkvm {
if ( system( "virsh dominfo $opt_o | grep -E \"^State: +shut off\$\" >/dev/null 2>&1" ) ) {
print "Domain $opt_o is running, shutdown first.\n\n";
exit 1;
}
}
sub clonfiles {
my ( $old_mac, $mac, $loopdev, $s );
my @dpart;
my $tmpmnt = "/tmp/tmpmnt$$";
print( "\nCloning disk image file of $opt_o to $opt_t...\n" );
log_cmd( "virt-clone --prompt --original $opt_o --name $opt_n --file $opt_t --nonsparse" );
$_ = `grep \"mac address\" /etc/libvirt/qemu/${opt_o}.xml`;
$_ =~ m/address=\'(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)\'/; $old_mac = $1;
$_ = `grep \"mac address\" /etc/libvirt/qemu/${opt_n}.xml`;
$_ =~ m/address=\'(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)\'/; $mac = $1;
print "\nModifying network information in the new disk image...\n\n";
my $qcow2;
my $filetype = `file $opt_t`;
if( $filetype =~ /QEMU QCOW Image \(v2\)/ ){
$qcow2 = 1;
print "$opt_t is qcow2\n";
}else{
$qcow2 = 0;
print "$opt_t is raw\n";
}
print "Now trying to find root partition...\n";
if($qcow2){
print "qcow2 mount takes 5sec sleep for safety\n";
sleep(5);
$loopdev = "/dev/nbd0";
log_cmd ( "qemu-nbd -c $loopdev $opt_t" );
print "qcow2 mount takes more 5sec sleep for safety.(this is last sleep).\n";
sleep(5);
print "qcow2 sleep end\n";
}else{
$loopdev = `losetup -f`; chomp $loopdev;
log_cmd ( "losetup $loopdev $opt_t" );
}
$s = `kpartx -av $loopdev`; @dpart = split( /\n/, $s );
log_cmd ( "mkdir -p $tmpmnt" );
foreach $s ( @dpart ) {
$s = $1 if ( $s =~ m/add map (\w+) / );
log_cmd ( "mount /dev/mapper/$s $tmpmnt" );
if ( -d "$tmpmnt/etc/network" ) {
print "\nFound root partition on $s\n";
goto OUT;
} else {
log_cmd ( "umount $tmpmnt" );
}
}
print "Failed to find root partition.\n";
if($qcow2){
log_cmd ( "kpartx -d $loopdev; qemu-nbd -d $loopdev; rmdir $tmpmnt" );
}else{
log_cmd ( "kpartx -d $loopdev; losetup -d $loopdev; rmdir $tmpmnt" );
}
exit 1;
OUT:
if ( -f "$tmpmnt/etc/udev/rules.d/70-persistent-net.rules" ) {
open ( IN, "<$tmpmnt/etc/udev/rules.d/70-persistent-net.rules" );
open ( OUT, ">$tmpmnt/etc/udev/rules.d/70-persistent-net.rules_new" );
while (<IN>) {
$_ =~ s/\r\n$/\n/;
$_ =~ s/==\"$old_mac\"/==\"$mac\"/;
print OUT $_;
}
close OUT;
close IN;
log_cmd ( "mv -f $tmpmnt/etc/udev/rules.d/70-persistent-net.rules_new $tmpmnt/etc/udev/rules.d/70-persistent-net.rules" );
print "\nNew mac address in udev file....\n";
print "---------------------------\n";
system ( "grep \"address\" $tmpmnt/etc/udev/rules.d/70-persistent-net.rules" );
print "---------------------------\n";
}
open ( OUT, ">$tmpmnt/etc/hostname" );
print OUT $opt_s;
close OUT;
print "\nNew hostname config file....\n";
print "---------------------------\n";
system ( "cat $tmpmnt/etc/hostname" );
print "---------------------------\n";
open ( IN, "<$tmpmnt/etc/network/interfaces" );
open ( OUT, ">$tmpmnt/etc/network/interfaces_new" );
while (<IN>) {
$_ =~ s/\r\n$/\n/;
$_ =~ s/$old_mac/$mac/;
$_ =~ s/\s*address\s*[\"\d\.]+/address $opt_i/i;
$_ =~ s/\s*netmask\s*[\"\d\.]+/netmask $opt_m/i;
$_ =~ s/\s*gateway\s*[\"\d\.]+/gateway $opt_g/i;
print OUT $_;
}
close OUT;
close IN;
log_cmd ( "mv -f $tmpmnt/etc/network/interfaces_new $tmpmnt/etc/network/interfaces" );
print "\nNew interface eth0 config file....\n";
print "---------------------------\n";
system ( "cat $tmpmnt/etc/network/interfaces" );
print "---------------------------\n";
log_cmd ( "umount $tmpmnt" );
if($qcow2){
log_cmd ( "kpartx -d $loopdev; qemu-nbd -d $loopdev; rmdir $tmpmnt" );
}else{
log_cmd ( "kpartx -d $loopdev; losetup -d $loopdev; rmdir $tmpmnt" );
}
exit 1;
}
MAIN: {
chkopts();
chkvm();
clonfiles();
if ( $opt_a ) {
print "\nActivate new vm $opt_n\n";
log_cmd ( "virsh start $opt_n" );
}
print "Done.\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment