#!/usr/bin/perl use strict; use warnings; use IO::File; use Digest::SHA; # script to upgrade V0.9.1 to V0.9.2 format my $confvars_0_9_1 = { onboot => 'bool', autostart => 'bool', reboot => 'bool', cpulimit => 'natural', cpuunits => 'natural', hda => 'file', hdb => 'file', sda => 'file', sdb => 'file', cdrom => 'file', memory => 'natural', keyboard => 'lang', name => 'string', ostype => 'ostype', boot => 'boot', smp => 'natural', acpi => 'bool', network => 'network', }; sub load_config_0_9_1 { my ($vmid, $filename) = @_; my $fh = new IO::File ($filename, "r") || return undef; my $res = {}; while (my $line = <$fh>) { next if $line =~ m/^\#/; next if $line =~ m/^\s*$/; if ($line =~ m/^([a-z]+):\s*(\S+)\s*$/) { my $key = $1; my $value = $2; if (my $type = $confvars_0_9_1->{$key}) { $res->{$key} = $value; } else { return undef; # unknown setting } } } return $res; } sub parse_network_0_9_1 { my ($data) = @_; my $res = { type => 'tap', }; foreach my $rec (split (/\s*,\s*/, $data)) { if ($rec =~ m/^(tap|user)$/) { $res->{type} = $rec; } elsif ($rec =~ m/^model\s*=\s*(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)$/) { $res->{model} = $1; } elsif ($rec =~ m/macaddr\s*=\s*([0-9a-f:]+)/i) { $res->{macaddr} = $1; } else { return undef; } } return $res; } sub random_ether_addr { my $rand = Digest::SHA::sha1_hex (rand(), time()); my $mac = ''; for (my $i = 0; $i < 6; $i++) { my $ss = hex (substr ($rand, $i*2, 2)); if (!$i) { $ss &= 0xfe; # clear multicast $ss |= 2; # set local id } $ss = sprintf ("%02X", $ss); if (!$i) { $mac .= "$ss"; } else { $mac .= ":$ss"; } } return $mac; } sub convert_0_9_1_to_0_9_2 { my ($vmid, $cfile, $conf) = @_; print "Upgrading VM $vmid to new format\n"; die "undefined vm id" if !$vmid || $vmid !~ m/^\d+$/; my $dmap = { hda => 'ide0', hdb => 'ide1', sda => 'scsi0', sdb => 'scsi1', }; my $tmpdir = "/var/lib/vz/images/$vmid.upgrade"; my $tmpconf = "$cfile.upgrade"; my $images = []; eval { mkdir $tmpdir || die "unable to create dir '$tmpdir'\n"; my $fh = new IO::File ($cfile, "r") || die "unable to read config for VM $vmid\n"; my $newfh = new IO::File ($tmpconf, "w") || die "unable to create file '$tmpconf'\n"; while (my $line = <$fh>) { next if $line =~ m/^\#/; next if $line =~ m/^\s*$/; if ($line =~ m/^([a-z]+):\s*(\S+)\s*$/) { my $key = $1; my $value = $2; if (my $type = $confvars_0_9_1->{$key}) { if ($key eq 'network') { my $onw = parse_network_0_9_1 ($value); if ($onw && ($onw->{type} eq 'tap')) { if (!$onw->{macaddr}) { $onw->{macaddr} = random_ether_addr (); } print $newfh "vlan0: $onw->{model}=$onw->{macaddr}\n"; } elsif ($onw && ($onw->{type} eq 'user')) { if (!$onw->{macaddr}) { $onw->{macaddr} = random_ether_addr (); } print $newfh "vlanu: $onw->{model}=$onw->{macaddr}\n"; } else { die "unable to convert network specification\n"; } } elsif ($key eq 'cdrom') { $value =~ s|^/.*/||; print $newfh "ide2: $value,media=cdrom\n"; } elsif (defined ($dmap->{$key})) { if ($value =~ m|^/var/lib/vz/images/([^/]+)$|) { $value = $1; } elsif ($value !~ m|/|) { # no nothing } else { die "wrong image path"; } link "/var/lib/vz/images/$value", "$tmpdir/$value"; (-f "$tmpdir/$value") || die "unable to create image link\n"; push @$images, $value; print $newfh "$dmap->{$key}: $value\n"; } else { print $newfh "$key: $value\n"; } } else { die "unknown setting '$key'\n"; } } } if ($conf->{hda}) { print $newfh "bootdisk: ide0\n"; } elsif ($conf->{hdb}) { print $newfh "bootdisk: ide1\n"; } elsif ($conf->{sda}) { print $newfh "bootdisk: scsi0\n"; } elsif ($conf->{sdb}) { print $newfh "bootdisk: scsi1\n"; } }; my $err = $@; if ($err) { system ("rm -rf $tmpdir $tmpconf"); } else { if (!rename $tmpdir, "/var/lib/vz/images/$vmid") { system ("rm -rf $tmpdir $tmpconf"); die "commiting '/var/lib/vz/images/$vmid' failed - $!\n"; } if (!rename $tmpconf, $cfile) { system ("rm -rf /var/lib/vz/images/$vmid $tmpconf"); die "commiting new configuration '$cfile' failed - $!\n"; } foreach my $img (@$images) { unlink "/var/lib/vz/images/$img"; } } die $err if $err; } foreach my $vmconf () { next if $vmconf !~ m|/etc/qemu-server/(\d+)\.conf|; my $vmid = $1; next if -d "/var/lib/vz/images/$vmid"; # already new format eval { my $res = load_config_0_9_1 ($vmid, $vmconf); if ($res && ($res->{network} || $res->{hda} || $res->{hdb} || $res->{sda} || $res->{sda} || $res->{cdrom})) { convert_0_9_1_to_0_9_2 ($vmid, $vmconf, $res); } }; warn $@ if $@; } exit 0;