[pve-devel] [PATCH_V3] Insert capability for restore LXC- and OpenVZ- dumpfiles
Wolfgang Link
w.link at proxmox.com
Wed Jul 29 09:36:19 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 | 136 +++++++++++++++---
src/PVE/VZDump/ConvertOVZ.pm | 319 +++++++++++++++++++++++++++++++++++++++++++
src/PVE/VZDump/Makefile | 1 +
4 files changed, 463 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..c9306bb 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,124 @@ 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 $file;
+ while ($file = <TMP>) {
+
+ last if ($file =~ m/.*(vps.conf|lxc.conf)/);
+ }
+
+ kill 15, $pid;
+ close TMP;
+
+ die "ERROR: archive contaions no config\n" if !$file;
+ chomp $file;
+
+ return $file;
+}
+
+sub recover_config {
+ my ($archive, $conf) = @_;
+
+ my $conf_file = tar_archive_read_firstfile($archive);
+
+ my ($old_vmid) = $archive =~ /-(\d+)-/;
+
+ my $tmpdir = '/tmp/CT/';
+
+ #this is for untainting
+ $conf_file =~ m/^(etc\/vzdump\/(vps.conf|lxc.conf))$/;
+
+ $conf_file = "./$1";
+
+ 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, $conf_file, '--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/$old_vmid/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'};
+ delete $conf->{snapshots};
+
+ 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
-
- PVE::LXC::write_config($vmid, $conf); # safe config (after OS detection)
+ if (!$restore) {
+ my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir); # detect OS
- $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 +203,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 +220,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 +256,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 +274,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 +307,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 +365,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