this patch recovers the config form the tarball. It will nt recover the nework setting if you recover from OVZ --- src/PVE/API2/LXC.pm | 30 +++- src/PVE/LXCCreate.pm | 105 +++++++++++--- src/PVE/VZDump/ConvertOVZ.pm | 319 +++++++++++++++++++++++++++++++++++++++++++ src/PVE/VZDump/Makefile | 1 + 4 files changed, 432 insertions(+), 23 deletions(-) create mode 100644 src/PVE/VZDump/ConvertOVZ.pm
diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm index 9e82bc4..d4e8936 100644 --- a/src/PVE/API2/LXC.pm +++ b/src/PVE/API2/LXC.pm @@ -185,7 +185,7 @@ __PACKAGE__->register_method({ # fixme: limit allowed parameters } - + my $force = extract_param($param, 'force'); if (!($same_container_exists && $restore && $force)) { @@ -241,10 +241,23 @@ __PACKAGE__->register_method({ } my $conf = {}; + if ($restore) { + $conf = PVE::LXCCreate::recover_config($archive, $conf); - $param->{hostname} ||= "CT$vmid"; - $param->{memory} ||= 512; - $param->{swap} = 512 if !defined($param->{swap}); + foreach my $item ( %{$conf}) { + + if ($item =~ m/(net\d+)/) { + my $net = $1; + my $pair = $conf->{$net}->{'veth.pair'}; + $pair =~ s/\d+/$vmid/; + $conf->{$net}->{'veth.pair'} = $pair; + }; + } + } + + $param->{hostname} ||= "CT$vmid" if !defined($conf->{'lxc.utsname'}); + $param->{memory} ||= 512 if !defined($conf->{'lxc.cgroup.memory.limit_in_bytes'}); + $param->{swap} = 512 if (!defined($param->{swap}) && !defined($conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'})); PVE::LXC::update_lxc_config($vmid, $conf, 0, $param); @@ -268,9 +281,14 @@ __PACKAGE__->register_method({ PVE::Cluster::check_vmid_unused($vmid); } }; - + my $code = sub { - + if ($restore && ($ostemplate =~ m/openvz/) ) { + print "###########################################################\n"; + print "Restore from OpenVZ please check the config and add network\n"; + print "###########################################################\n"; + } + &$check_vmid_usage(); # final check after locking PVE::Cluster::check_cfs_quorum(); diff --git a/src/PVE/LXCCreate.pm b/src/PVE/LXCCreate.pm index 91851ee..99521af 100644 --- a/src/PVE/LXCCreate.pm +++ b/src/PVE/LXCCreate.pm @@ -9,6 +9,7 @@ use Data::Dumper; use PVE::Storage; use PVE::LXC; use PVE::LXCSetup; +use PVE::VZDump::ConvertOVZ; sub next_free_nbd_dev { @@ -51,7 +52,7 @@ sub restore_archive { } else { print "extracting archive '$archive'\n"; PVE::Tools::run_command($cmd); - } + } # is this really required? what for? #$cmd = [@$userns_cmd, 'mkdir', '-p', "$rootdir/dev/pts"]; @@ -74,23 +75,93 @@ sub restore_archive { }); } +my $openvz_to_lxc = sub { + my ($openvz_conf) = @_; + + my $conf = {}; + + return $conf; +}; + +sub tar_archive_search_conf { + my ($archive) = @_; + + die "ERROR: file '$archive' does not exist\n" if ! -f $archive; + + my $pid = open(my $fh, '-|', 'tar', 'tf', $archive) || + die "unable to open file '$archive'\n"; + + my $file; + while ($file = <$fh>) { + last if ($file =~ m/.*(vps.conf|lxc.conf)/); + } + + kill 15, $pid; + waitpid $pid, 0; + close $fh; + + die "ERROR: archive contaions no config\n" if !$file; + chomp $file; + + return $file; +} + +sub recover_config { + my ($archive, $conf) = @_; + + my $conf_file = tar_archive_search_conf($archive); + + #untainting var + $conf_file =~ m/(etc\/vzdump\/(lxc\.conf|vps\.conf))/; + $conf_file = "./$1"; + + my ($old_vmid) = $archive =~ /-(\d+)-/; + + my $raw = ''; + my $out = sub { + my $output = shift; + $raw .= "$output\n"; + }; + + PVE::Tools::run_command(['tar', '-xpOf', $archive, $conf_file, '--occurrence'], outfunc => $out); + + $conf = undef; + + if ($conf_file =~ m/lxc.conf/) { + + $conf = PVE::LXC::parse_lxc_config("/lxc/$old_vmid/config" , $raw); + + delete $conf->{'pve.volid'}; + delete $conf->{'lxc.rootfs'}; + delete $conf->{snapshots}; + + } elsif ($conf_file =~ m/vps.conf/) { + + $conf = PVE::VZDump::ConvertOVZ::convert_ovz($raw); + + } + + return $conf; +} + sub restore_and_configure { - my ($vmid, $archive, $rootdir, $conf, $password) = @_; + my ($vmid, $archive, $rootdir, $conf, $password, $restore) = @_; restore_archive($archive, $rootdir, $conf); PVE::LXC::write_config($vmid, $conf); - my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir); # detect OS + if (!$restore) { + my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir); # detect OS - PVE::LXC::write_config($vmid, $conf); # safe config (after OS detection) - - $lxc_setup->post_create_hook($password); + PVE::LXC::write_config($vmid, $conf); # safe config (after OS detection) + $lxc_setup->post_create_hook($password); + } } # directly use a storage directory sub create_rootfs_dir { - my ($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password) = @_; + my ($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password, $restore) = @_; # note: there is no size limit $conf->{'pve.disksize'} = 0; @@ -101,12 +172,12 @@ sub create_rootfs_dir { push @{$cleanup->{files}}, $private; $conf->{'lxc.rootfs'} = $private; - restore_and_configure($vmid, $archive, $private, $conf, $password); + restore_and_configure($vmid, $archive, $private, $conf, $password, $restore); } # use new subvolume API sub create_rootfs_subvol { - my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; + my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password, $restore) = @_; my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'subvol', "subvol-$vmid-rootfs", $size); @@ -118,12 +189,12 @@ sub create_rootfs_subvol { $conf->{'lxc.rootfs'} = $private; $conf->{'pve.volid'} = $volid; - restore_and_configure($vmid, $archive, $private, $conf, $password); + restore_and_configure($vmid, $archive, $private, $conf, $password, $restore); } # create a raw file, then loop mount sub create_rootfs_dir_loop { - my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; + my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password, $restore) = @_; my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw', "vm-$vmid-rootfs.raw", $size); $conf->{'pve.disksize'} = $size/(1024*1024); @@ -154,7 +225,7 @@ sub create_rootfs_dir_loop { $mountpoint = $tmp; $conf->{'pve.volid'} = $volid; - restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); + restore_and_configure($vmid, $archive, $mountpoint, $conf, $password, $restore); }; if (my $err = $@) { if ($mountpoint) { @@ -172,7 +243,7 @@ sub create_rootfs_dir_loop { # create a file, then mount with qemu-nbd sub create_rootfs_dir_qemu { - my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; + my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password, $restore) = @_; my $format = 'qcow2'; @@ -205,7 +276,7 @@ sub create_rootfs_dir_qemu { $mountpoint = $tmp; $conf->{'pve.volid'} = $volid; - restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); + restore_and_configure($vmid, $archive, $mountpoint, $conf, $password, $restore); }; if (my $err = $@) { if ($mountpoint) { @@ -263,14 +334,14 @@ sub create_rootfs { my $scfg = PVE::Storage::storage_config($storage_conf, $storage); if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') { if ($size > 0) { - create_rootfs_dir_loop($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password); + create_rootfs_dir_loop($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password, $restore); } else { - create_rootfs_dir($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password); + create_rootfs_dir($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password, $restore); } } elsif ($scfg->{type} eq 'zfspool') { create_rootfs_subvol($cleanup, $storage_conf, $storage, $size, - $vmid, $conf, $archive, $password); + $vmid, $conf, $archive, $password, $restore); } else { diff --git a/src/PVE/VZDump/ConvertOVZ.pm b/src/PVE/VZDump/ConvertOVZ.pm new file mode 100644 index 0000000..85f747a --- /dev/null +++ b/src/PVE/VZDump/ConvertOVZ.pm @@ -0,0 +1,319 @@ +package PVE::VZDump::ConvertOVZ; + +use strict; +use warnings; +use POSIX qw (LONG_MAX); + +my $res_unlimited = LONG_MAX; + +sub ovz_config_extract_mem_swap { + my ($veconf, $unit) = @_; + + $unit = 1 if !$unit; + + my ($mem, $swap) = (int((512*1024*1024 + $unit - 1)/$unit), 0); + + my $maxpages = ($res_unlimited / 4096); + + if ($veconf->{swappages}) { + if ($veconf->{physpages} && $veconf->{physpages}->{lim} && + ($veconf->{physpages}->{lim} < $maxpages)) { + $mem = int(($veconf->{physpages}->{lim} * 4096 + $unit - 1) / $unit); + } + if ($veconf->{swappages}->{lim} && ($veconf->{swappages}->{lim} < $maxpages)) { + $swap = int (($veconf->{swappages}->{lim} * 4096 + $unit - 1) / $unit); + } + } else { + if ($veconf->{vmguarpages} && $veconf->{vmguarpages}->{bar} && + ($veconf->{vmguarpages}->{bar} < $maxpages)) { + $mem = int(($veconf->{vmguarpages}->{bar} * 4096 + $unit - 1) / $unit); + } + } + + return ($mem, $swap); +} + +sub parse_res_num_ignore { + my ($key, $text) = @_; + + if ($text =~ m/^(\d+|unlimited)(:.*)?$/) { + return { bar => $1 eq 'unlimited' ? $res_unlimited : $1 }; + } + + return undef; +} + +sub parse_res_num_num { + my ($key, $text) = @_; + + if ($text =~ m/^(\d+|unlimited)(:(\d+|unlimited))?$/) { + my $res = { bar => $1 eq 'unlimited' ? $res_unlimited : $1 }; + if (defined($3)) { + $res->{lim} = $3 eq 'unlimited' ? $res_unlimited : $3; + } else { + $res->{lim} = $res->{bar}; + } + return $res; + } + + return undef; +} + +sub parse_res_bar_limit { + my ($text, $base) = @_; + + return $res_unlimited if $text eq 'unlimited'; + + if ($text =~ m/^(\d+)([TGMKP])?$/i) { + my $val = $1; + my $mult = $2 ? lc($2) : ''; + if ($mult eq 'k') { + $val = $val * 1024; + } elsif ($mult eq 'm') { + $val = $val * 1024 * 1024; + } elsif ($mult eq 'g') { + $val = $val * 1024 * 1024 * 1024; + } elsif ($mult eq 't') { + $val = $val * 1024 * 1024 * 1024 * 1024; + } elsif ($mult eq 'p') { + $val = $val * 4096; + } else { + return $val; + } + return int($val/$base); + } + + return undef; +} + +sub parse_res_bytes_bytes { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + $a[1] = $a[0] if !defined($a[1]); + + my $bar = parse_res_bar_limit($a[0], 1); + my $lim = parse_res_bar_limit($a[1], 1); + + if (defined($bar) && defined($lim)) { + return { bar => $bar, lim => $lim }; + } + + return undef; +} + +sub parse_res_block_block { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + $a[1] = $a[0] if !defined($a[1]); + + my $bar = parse_res_bar_limit($a[0], 1024); + my $lim = parse_res_bar_limit($a[1], 1024); + + if (defined($bar) && defined($lim)) { + return { bar => $bar, lim => $lim }; + } + + return undef; +} + +sub parse_res_pages_pages { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + $a[1] = $a[0] if !defined($a[1]); + + my $bar = parse_res_bar_limit($a[0], 4096); + my $lim = parse_res_bar_limit($a[1], 4096); + + if (defined($bar) && defined($lim)) { + return { bar => $bar, lim => $lim }; + } + + return undef; +} + +sub parse_res_pages_unlimited { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + + my $bar = parse_res_bar_limit($a[0], 4096); + + if (defined($bar)) { + return { bar => $bar, lim => $res_unlimited }; + } + + return undef; +} + +sub parse_res_pages_ignore { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + + my $bar = parse_res_bar_limit($a[0], 4096); + + if (defined($bar)) { + return { bar => $bar }; + } + + return undef; +} + +sub parse_res_ignore_pages { + my ($key, $text) = @_; + + my @a = split(/:/, $text); + $a[1] = $a[0] if !defined($a[1]); + + my $lim = parse_res_bar_limit($a[1] , 4096); + + if (defined($lim)) { + return { bar => 0, lim => $lim }; + } + + return undef; +} + +sub parse_boolean { + my ($key, $text) = @_; + + return { value => 1 } if $text =~ m/^(yes|true|on|1)$/i; + return { value => 0 } if $text =~ m/^(no|false|off|0)$/i; + + return undef; +}; + +sub parse_integer { + my ($key, $text) = @_; + + if ($text =~ m/^(\d+)$/) { + return { value => int($1) }; + } + + return undef; +}; + +my $ovz_ressources = { + numproc => \&parse_res_num_ignore, + numtcpsock => \&parse_res_num_ignore, + numothersock => \&parse_res_num_ignore, + numfile => \&parse_res_num_ignore, + numflock => \&parse_res_num_num, + numpty => \&parse_res_num_ignore, + numsiginfo => \&parse_res_num_ignore, + numiptent => \&parse_res_num_ignore, + + vmguarpages => \&parse_res_pages_unlimited, + oomguarpages => \&parse_res_pages_unlimited, + lockedpages => \&parse_res_pages_ignore, + privvmpages => \&parse_res_pages_pages, + shmpages => \&parse_res_pages_ignore, + physpages => \&parse_res_pages_pages, + swappages => \&parse_res_ignore_pages, + + kmemsize => \&parse_res_bytes_bytes, + tcpsndbuf => \&parse_res_bytes_bytes, + tcprcvbuf => \&parse_res_bytes_bytes, + othersockbuf => \&parse_res_bytes_bytes, + dgramrcvbuf => \&parse_res_bytes_bytes, + dcachesize => \&parse_res_bytes_bytes, + + disk_quota => \&parse_boolean, + diskspace => \&parse_res_block_block, + diskinodes => \&parse_res_num_num, + quotatime => \&parse_integer, + quotaugidlimit => \&parse_integer, + + cpuunits => \&parse_integer, + cpulimit => \&parse_integer, + cpus => \&parse_integer, + cpumask => 'string', + meminfo => 'string', + iptables => 'string', + + ip_address => 'string', + netif => 'string', + hostname => 'string', + nameserver => 'string', + searchdomain => 'string', + + name => 'string', + description => 'string', + onboot => \&parse_boolean, + initlog => \&parse_boolean, + bootorder => \&parse_integer, + ostemplate => 'string', + ve_root => 'string', + ve_private => 'string', + disabled => \&parse_boolean, + origin_sample => 'string', + noatime => \&parse_boolean, + capability => 'string', + devnodes => 'string', + devices => 'string', + pci => 'string', + features => 'string', + ioprio => \&parse_integer, + +}; + +my $parse_ovz_config = sub { + my ($raw) = @_; + + my $data = {}; + + return undef if !defined($raw); + + while ($raw && $raw =~ /^(.*?)(\n|$)/mg) { + my $line = $1; + + next if $line =~ m/^\#/; + next if $line =~ m/^\s*$/; + + if ($line =~ m/^\s*([A-Z][A-Z0-9_]*)\s*=\s*\"(.*)\"\s*$/i) { + my $name = lc($1); + my $text = $2; + my $parser = $ovz_ressources->{$name}; + if (!$parser || !ref($parser)) { + $data->{$name}->{value} = $text; + next; + } else { + if (my $res = &$parser($name, $text)) { + $data->{$name} = $res; + next; + } + } + } + die "unable to parse config line: $line\n"; + } + + return $data; +}; + +sub convert_ovz { + my ($raw) = @_; + + my $conf = {}; + + my $ovz_conf = &$parse_ovz_config($raw); + + $conf->{'pve.disksize'} = $ovz_conf->{'diskspace'}->{'bar'} / 1024 / 1024; + + my ($mem, $swap) = ovz_config_extract_mem_swap($ovz_conf, 0); + + $conf->{'lxc.cgroup.memory.limit_in_bytes'} = $mem; + + $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'} = ($swap + $mem); + + $conf->{'lxc.cgroup.cpu.shares'} = 1024; + + $conf->{'lxc.cgroup.cpu.cfs_quota_us'} = $ovz_conf->{cpus}->{value} * 100000; + $conf->{'lxc.cgroup.cpu.cfs_period_us'} = 100000; + + $conf->{'lxc.utsname'} = $ovz_conf->{hostname}->{value}; + + return $conf; +} diff --git a/src/PVE/VZDump/Makefile b/src/PVE/VZDump/Makefile index 8f66c90..4e35dcd 100644 --- a/src/PVE/VZDump/Makefile +++ b/src/PVE/VZDump/Makefile @@ -2,3 +2,4 @@ .PHONY: install install: install -D -m 0644 LXC.pm ${PERLDIR}/PVE/VZDump/LXC.pm + install -D -m 0644 ConvertOVZ.pm ${PERLDIR}/PVE/VZDump/ConvertOVZ.pm -- 2.1.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel