[pve-devel] [PATCH_V2] Insert capability for restore LXC- and OpenVZ- dumpfiles

Wolfgang Link w.link at proxmox.com
Tue Jul 28 16:06:51 CEST 2015


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         | 125 ++++++++++++++---
 src/PVE/VZDump/ConvertOVZ.pm | 319 +++++++++++++++++++++++++++++++++++++++++++
 src/PVE/VZDump/Makefile      |   1 +
 4 files changed, 452 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 f8ac02d..5a07281 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..966824d 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,113 @@ sub restore_archive {
     });
 }
 
+my $openvz_to_lxc = sub {
+    my ($openvz_conf) = @_;
+
+    my $conf = {};
+
+    return $conf;
+};
+
+sub tar_archive_read_firstfile {
+    my $archive = shift;
+
+    die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
+
+    # try to detect archive type first
+    my $pid = open (TMP, "tar tf '$archive'|") ||
+	die "unable to open file '$archive'\n";
+    my $firstfile = <TMP>;
+    kill 15, $pid;
+    close TMP;
+
+    die "ERROR: archive contaions no data\n" if !$firstfile;
+    chomp $firstfile;
+
+    return $firstfile;
+}
+
+sub recover_config {
+    my ($archive, $conf) = @_;
+
+    my $conf_file = tar_archive_read_firstfile($archive);
+
+    die "unable to recover LXC form: $conf_file\n"
+	if $conf_file !~  m/.*(vps.conf|lxc.conf)/;
+
+    my $tmpdir = '/tmp/CT/';
+
+    eval {
+	PVE::Tools::run_command("mkdir -p $tmpdir");
+
+	if ( $archive =~ m/.*(tar|tar.gz|tar.lzo)$/ ) {
+
+	    PVE::Tools::run_command(['tar', '-xp', '-C', $tmpdir, '-f', $archive, './etc/vzdump/', '--occurrence']);
+
+	} else {
+	    die "unknown archive format :$archive\n";
+	}
+
+	my $lxc_conf = $tmpdir.'etc/vzdump/lxc.conf';
+	my $openvz_conf = $tmpdir.'etc/vzdump/vps.conf';
+
+	if (-f  $lxc_conf) {
+	    $conf = undef;
+
+	    local $/ = undef;
+
+	    my $fh = IO::File->new("< $lxc_conf");
+	    die "Could not open file $lxc_conf: $!\n" if !$fh;
+
+	    $conf = PVE::LXC::parse_lxc_config('/lxc/100/config', <$fh>);
+
+	    close $fh;
+	} elsif (-e $openvz_conf) {
+
+	    $conf = undef;
+
+	    local $/ = undef;
+
+	    my $fh = IO::File->new("< $openvz_conf");
+	    die "Could not open file $openvz_conf: $!\n" if !$fh;
+
+	    $conf = PVE::VZDump::ConvertOVZ::convert_ovz(<$fh>);
+
+	    close $fh;
+	} else {
+	    die "Unable to recover config from Backup\nConfigfile missing\n";
+	}
+    };
+    my $err = $@;
+
+    PVE::Tools::run_command("rm -R $tmpdir");
+
+    die "$err\n" if ($err);
+
+    delete $conf->{'pve.volid'};
+    delete $conf->{'lxc.rootfs'};
+
+    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 +192,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 +209,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 +245,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 +263,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 +296,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 +354,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





More information about the pve-devel mailing list