kvmclone.pl コードリーディング

#!/usr/bin/perl
#
#   kvmclone.pl for RHEL5.4 KVM
#
#   2009/12/22 ver1.0
#

### モジュール及び、グローバル変数の宣言
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;

### 引数オプションのパース処理とusage
sub chkopts {
    getopts( 'ahs:t:o:n:i:k:g:b:c:m:' );

### -a と -h 以外は指定必須
    $opt_h = 1 unless ( $opt_t && $opt_o && $opt_n && $opt_i && $opt_m &&
                        $opt_g && $opt_s );

# -a : 設定が終了したらvmを起動する
# -o : コピー元のドメイン名を指定する
# -n : コピー先のドメイン名を指定する
# -t : イメージファイルのパス
# -s : 書き換え先のホスト名を指定
# -i : 書き換え先のIPを指定
# -m : 書き換え先のNETMASKを指定
# -g : 書き換え先のGATEWAYを指定
    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" );

### MAC ADDRESSをドメイン定義ファイルから抜き出す
    $_ = `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";

### 使用可能なループバックデバイスにコピーしたイメージを割り当てる
    print "Now trying to find root partition...\n";
    $loopdev = `losetup -f`; chomp $loopdev;
    log_cmd ( "losetup $loopdev $opt_t" );
    $s = `kpartx -av $loopdev`; @dpart = split( /\n/, $s );
    log_cmd ( "mkdir -p $tmpmnt" );

### /etc/sysconfigのあるパーティションを探してマウントする
    foreach $s ( @dpart ) {
        $s = $1 if ( $s =~ m/add map (\w+) :/ );
        log_cmd ( "mount /dev/mapper/$s $tmpmnt" );
        if ( -d "$tmpmnt/etc/sysconfig" ) {
            print "\nFound root partition on $s\n";
            goto OUT;
        } else {
            log_cmd ( "umount $tmpmnt" );
        }
    }

    print "Failed to find root partition.\n";
    log_cmd ( "kpartx -d $loopdev; losetup -d $loopdev; rmdir $tmpmnt" );
    exit 1;

OUT:
### 書き換え開始
    open ( IN, "<$tmpmnt/etc/sysconfig/hwconf" );
    open ( OUT, ">$tmpmnt/etc/sysconfig/hwconf_new" );
    while (<IN>) {
        $_ =~ s/\r\n$/\n/;
        $_ =~ s/^network.hwaddr: $old_mac/network.hwaddr: $mac/;
        print OUT $_;
    }
    close OUT;
    close IN;
    log_cmd ( "mv -f $tmpmnt/etc/sysconfig/hwconf_new $tmpmnt/etc/sysconfig/hwconf" );
    print "\nNew mac address in hwconf file....\n";
    print "---------------------------\n";
    system ( "grep \"network.hwaddr\" $tmpmnt/etc/sysconfig/hwconf" );
    print "---------------------------\n";

    open ( IN, "<$tmpmnt/etc/sysconfig/network" );
    open ( OUT, ">$tmpmnt/etc/sysconfig/network_new" );
    while (<IN>) {
        $_ =~ s/\r\n$/\n/;
        $_ =~ s/HOSTNAME\s*=\s*[\w-]+/HOSTNAME=$opt_s/;
        $_ =~ s/GATEWAY\s*=\s*[\d\.]+/GATEWAY=$opt_g/;
        print OUT $_;
    }
    close OUT;
    close IN;
    log_cmd ( "mv -f $tmpmnt/etc/sysconfig/network_new $tmpmnt/etc/sysconfig/network" );
    print "\nNew network config file....\n";
    print "---------------------------\n";
    system ( "cat $tmpmnt/etc/sysconfig/network" );
    print "---------------------------\n";

    open ( IN, "<$tmpmnt/etc/sysconfig/network-scripts/ifcfg-eth0" );
    open ( OUT, ">$tmpmnt/etc/sysconfig/network-scripts/ifcfg-eth0_new" );
    while (<IN>) {
        $_ =~ s/\r\n$/\n/;
        $_ =~ s/HWADDR\s*=\s*[\w:]+/HWADDR=$mac/;
        $_ =~ s/IPADDR\s*=\s*[\d\.]+/IPADDR=$opt_i/;
        $_ =~ s/NETMASK\s*=\s*[\d\.]+/NETMASK=$opt_m/;
        $_ =~ s/GATEWAY\s*=\s*[\d\.]+/GATEWAY=$opt_g/;
        print OUT $_;
    }
    close OUT;
    close IN;
    log_cmd ( "mv -f $tmpmnt/etc/sysconfig/network-scripts/ifcfg-eth0_new $tmpmnt/etc/sysconfig/network-scripts/ifcfg-eth0" );
    print "\nNew interface eth0 config file....\n";
    print "---------------------------\n";
    system ( "cat $tmpmnt/etc/sysconfig/network-scripts/ifcfg-eth0" );
    print "---------------------------\n";

### 解放処理
    log_cmd ( "umount $tmpmnt" );
    log_cmd ( "kpartx -d $loopdev; losetup -d $loopdev; rmdir $tmpmnt" );

}

MAIN: {
    chkopts();
    chkvm();
    clonfiles();
    if ( $opt_a ) {
        print "\nActivate new vm $opt_n\n";
        log_cmd ( "virsh start $opt_n" );
    }
    print "Done.\n";
}

### viの設定、TAB一つ分は4スペース
# vi:ts=4