[pve-devel] [PATCH 1/9] Extend lxc config with options for snapshot

Wolfgang Link w.link at proxmox.com
Fri Jul 17 09:26:43 CEST 2015


now it is possible to add sanpshots to the config.
The snapshot include all config information what are set at the snapshot.
So if you make a rolback all settings will restored

Signed-off-by: Wolfgang Link <w.link at proxmox.com>
---
 src/PVE/LXC.pm | 323 +++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 213 insertions(+), 110 deletions(-)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 44c3fcc..f92b903 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -125,6 +125,14 @@ my $valid_lxc_keys = {
 	PVE::Storage::parse_volume_id($value);
 	return $value;
     },
+
+     #pve snapshot
+    'pve.lock' => 1,
+    'pve.snaptime' => 1,
+    'pve.snapcomment' => 1,
+    'pve.parent' => 1,
+    'pve.snapstate' => 1,
+    'pve.snapname' => 1,
 };
 
 my $valid_lxc_network_keys = {
@@ -163,8 +171,8 @@ sub write_lxc_config {
     my $done_hash = { digest => 1};
 
     my $dump_entry = sub {
-	my ($k) = @_;
-	my $value = $data->{$k};
+	my ($k, $elem, $done_hash, $snap) = @_;
+	my $value = $elem->{$k};
 	return if !defined($value);
 	return if $done_hash->{$k};
 	$done_hash->{$k} = 1;
@@ -172,53 +180,81 @@ sub write_lxc_config {
 	    die "got unexpected reference for '$k'"
 		if !$lxc_array_configs->{$k};
 	    foreach my $v (@$value) {
+		$raw .= "snap\." if $snap;
 		$raw .= "$k = $v\n";
 	    }
 	} else {
+	    $raw .= "snap\." if $snap;
 	    $raw .= "$k = $value\n";
 	}
     };
 
-    # Note: Order is important! Include defaults first, so that we
-    # can overwrite them later.
-    &$dump_entry('lxc.include');
+    my $config_writer = sub {
+	my ($elem, $snapshot) = @_;
 
-    foreach my $k (sort keys %$data) {
-	next if $k !~ m/^lxc\./;
-	&$dump_entry($k);
-    }
+	my $done_hash = { digest => 1};
 
-    foreach my $k (sort keys %$data) {
-	next if $k !~ m/^pve\./;
-	&$dump_entry($k);
-    }
+	if ($elem->{'pve.snapname'}) {
+	     &$dump_entry('pve.snapname', $elem, $done_hash, $snapshot);
+	}
 
-    my $network_count = 0;
-    foreach my $k (sort keys %$data) {
-	next if $k !~ m/^net\d+$/;
-	$done_hash->{$k} = 1;
-	my $net = $data->{$k};
-	$network_count++;
-	$raw .= "lxc.network.type = $net->{type}\n";
-	foreach my $subkey (sort keys %$net) {
-	    next if $subkey eq 'type';
-	    if ($valid_lxc_network_keys->{$subkey}) {
-		$raw .= "lxc.network.$subkey = $net->{$subkey}\n";
-	    } elsif ($valid_pve_network_keys->{$subkey}) {
-		$raw .= "pve.network.$subkey = $net->{$subkey}\n";
-	    } else {
-		die "found invalid network key '$subkey'";
+	# Note: Order is important! Include defaults first, so that we
+	# can overwrite them later.
+	&$dump_entry('lxc.include', $elem, $done_hash, $snapshot);
+
+	foreach my $k (sort keys %$elem) {
+	    next if $k !~ m/^lxc\./;
+	    &$dump_entry($k, $elem, $done_hash, $snapshot);
+	}
+	foreach my $k (sort keys %$elem) {
+	    next if $k !~ m/^pve\./;
+	    &$dump_entry($k, $elem, $done_hash, $snapshot);
+	}
+	my $network_count = 0;
+
+	foreach my $k (sort keys %$elem) {
+	    next if $k !~ m/^net\d+$/;
+	    $done_hash->{$k} = 1;
+
+	    my $net = $elem->{$k};
+	    $network_count++;
+	    $raw .= "snap\." if $snapshot;
+	    $raw .= "lxc.network.type = $net->{type}\n";
+	    foreach my $subkey (sort keys %$net) {
+		next if $subkey eq 'type';
+		if ($valid_lxc_network_keys->{$subkey}) {
+		    $raw .= "snap\." if $snapshot;
+		    $raw .= "lxc.network.$subkey = $net->{$subkey}\n";
+		} elsif ($valid_pve_network_keys->{$subkey}) {
+		    $raw .= "snap\." if $snapshot;
+		    $raw .= "pve.network.$subkey = $net->{$subkey}\n";
+		} else {
+		    die "found invalid network key '$subkey'";
+		}
 	    }
 	}
-    }
+	if (!$network_count) {
+	    $raw .= "snap\." if $snapshot;
+	    $raw .= "lxc.network.type = empty\n";
+	}
+	foreach my $k (sort keys %$elem) {
+	    next if $k eq 'snapshots';
+	    next if $done_hash->{$k};
+	    die "found un-written value in config - implement this!";
+	}
 
-    if (!$network_count) {
-	$raw .= "lxc.network.type = empty\n";
-    }
+    };
 
-    foreach my $k (sort keys %$data) {
-	next if $done_hash->{$k};
-	die "found un-written value in config - implement this!";
+    &$config_writer($data);
+
+    if ($data->{snapshots}){
+	my @tmp = sort  { $data->{snapshots}->{$b}{'pve.snaptime'} <=>
+			      $data->{snapshots}->{$a}{'pve.snaptime'} }
+			keys %{$data->{snapshots}};
+	foreach my $snapname (@tmp) {
+	    $raw .= "\n";
+	    &$config_writer($data->{snapshots}->{$snapname}, 1);
+	}
     }
 
     return $raw;
@@ -258,99 +294,148 @@ sub parse_lxc_config {
 
     my $vmid = $1;
 
-    my $network_counter = 0;
-    my $network_list = [];
-    my $host_ifnames = {};
-
-     my $find_next_hostif_name = sub {
-	for (my $i = 0; $i < 10; $i++) {
-	    my $name = "veth${vmid}.$i";
-	    if (!$host_ifnames->{$name}) {
-		$host_ifnames->{$name} = 1;
-		return $name;
+    my $split_config = sub {
+	my ($raw) = @_;
+	my $sections = [];
+	my $tmp = '';
+	while ($raw && $raw =~ s/^(.*)?(\n|$)//) {
+	    my $line = $1;
+	    if(!$line) {
+		push(@{$sections},$tmp);
+		$tmp = '';
+	    } else {
+		$tmp .= "$line\n";
 	    }
 	}
+	push(@{$sections},$tmp);
 
-	die "unable to find free host_ifname"; # should not happen
+	return $sections;
     };
 
-    my $push_network = sub {
-	my ($netconf) = @_;
-	return if !$netconf;
-	push @{$network_list}, $netconf;
-	$network_counter++;
-	if (my $netname = $netconf->{'veth.pair'}) {
-	    if ($netname =~ m/^veth(\d+).(\d)$/) {
-		die "wrong vmid for network interface pair\n" if $1 != $vmid;
-		my $host_ifnames->{$netname} = 1;
-	    } else {
-		die "wrong network interface pair\n";
-	    }
-	}
-    };
 
-    my $network;
+    my $sec = &$split_config($raw);
 
-    while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
-	my $line = $1;
+    foreach my  $sec_raw (@{$sec}){
+	next if $sec_raw eq '';
+	my $snapname = undef;
 
-	next if $line =~ m/^\#/;
-	next if $line =~ m/^\s*$/;
+	my $network_counter = 0;
+	my $network_list = [];
+	my $host_ifnames = {};
 
-	if ($line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
-	    my ($subkey, $value) = ($1, $2);
-	    if ($subkey eq 'type') {
-		&$push_network($network);
-		$network = { type => $value };
-	    } elsif ($valid_lxc_network_keys->{$subkey}) {
-		$network->{$subkey} = $value;
-	    } else {
-		die "unable to parse config line: $line\n";
-	    }
-	    next;
-	}
-	if ($line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
-	    my ($subkey, $value) = ($1, $2);
-	    if ($valid_pve_network_keys->{$subkey}) {
-		$network->{$subkey} = $value;
-	    } else {
-		die "unable to parse config line: $line\n";
+	my $find_next_hostif_name = sub {
+	    for (my $i = 0; $i < 10; $i++) {
+		my $name = "veth${vmid}.$i";
+		if (!$host_ifnames->{$name}) {
+		    $host_ifnames->{$name} = 1;
+		    return $name;
+		}
 	    }
-	    next;
-	}
-	if ($line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/) {
-	    my ($name, $value) = ($1, $2);
-	    $data->{$name} = $value;
-	    next;
-	}
-	if ($line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/) {
-	    my ($name, $value) = ($1, $2);
 
-	    if ($lxc_array_configs->{$name}) {
-		$data->{$name} = [] if !defined($data->{$name});
-		push @{$data->{$name}},  parse_lxc_option($name, $value);
-	    } else {
-		die "multiple definitions for $name\n" if defined($data->{$name});
-		$data->{$name} = parse_lxc_option($name, $value);
+	    die "unable to find free host_ifname"; # should not happen
+	};
+
+	my $push_network = sub {
+	    my ($netconf) = @_;
+	    return if !$netconf;
+	    push @{$network_list}, $netconf;
+	    $network_counter++;
+	    if (my $netname = $netconf->{'veth.pair'}) {
+		if ($netname =~ m/^veth(\d+).(\d)$/) {
+		    die "wrong vmid for network interface pair\n" if $1 != $vmid;
+		    my $host_ifnames->{$netname} = 1;
+		} else {
+		    die "wrong network interface pair\n";
+		}
 	    }
+	};
 
-	    next;
-	}
+	my $network;
 
-	die "unable to parse config line: $line\n";
-    }
+	while ($sec_raw && $sec_raw =~ s/^(.*?)(\n|$)//) {
+	    my $line = $1;
 
-    &$push_network($network);
+	    next if $line =~ m/^\#/;
+	    next if $line =~ m/^\s*$/;
 
-    foreach my $net (@{$network_list}) {
-	next if $net->{type} eq 'empty'; # skip
-	$net->{'veth.pair'} = &$find_next_hostif_name() if !$net->{'veth.pair'};
-	$net->{hwaddr} =  PVE::Tools::random_ether_addr() if !$net->{hwaddr};
-	die "unsupported network type '$net->{type}'\n" if $net->{type} ne 'veth';
+	    if ($line =~ m/^(snap\.)?lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
+		my ($subkey, $value) = ($2, $3);
+		if ($subkey eq 'type') {
+		    &$push_network($network);
+		    $network = { type => $value };
+		} elsif ($valid_lxc_network_keys->{$subkey}) {
+		    $network->{$subkey} = $value;
+		} else {
+		    die "unable to parse config line: $line\n";
+		}
+		next;
+	    }
+	    if ($line =~ m/^(snap\.)?pve\.network\.(\S+)\s*=\s*(\S+)\s*$/) {
+		my ($subkey, $value) = ($2, $3);
+		if ($valid_pve_network_keys->{$subkey}) {
+		    $network->{$subkey} = $value;
+		} else {
+		    die "unable to parse config line: $line\n";
+		}
+		next;
+	    }
+	    if ($line =~ m/^(snap\.)?(pve.snapcomment)\s*=\s*(\S.*)\s*$/) {
+		my ($name, $value) = ($2, $3);
+		if ($snapname) {
+		    $data->{snapshots}->{$snapname}->{$name} = $value;
+		}
+		next;
+	    }
+	    if ($line =~ m/^(snap\.)?pve\.snapname = (\w*)$/) {
+		if (!$snapname) {
+		    $snapname = $2;
+		    $data->{snapshots}->{$snapname}->{'pve.snapname'} = $snapname;
+		} else {
+		    die "Configuarion broken\n";
+		}
+		next;
+	    }
+	    if ($line =~ m/^(snap\.)?((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/) {
+		my ($name, $value) = ($2, $3);
+
+		if ($lxc_array_configs->{$name}) {
+		    $data->{$name} = [] if !defined($data->{$name});
+		    if ($snapname) {
+			push @{$data->{snapshots}->{$snapname}->{$name}},  parse_lxc_option($name, $value);
+		    } else {
+			push @{$data->{$name}},  parse_lxc_option($name, $value);
+		    }
+		} else {
+		    if ($snapname) {
+			die "multiple definitions for $name\n" if defined($data->{snapshots}->{$snapname}->{$name});
+			$data->{snapshots}->{$snapname}->{$name} = parse_lxc_option($name, $value);
+		    } else {
+			die "multiple definitions for $name\n" if defined($data->{$name});
+			$data->{$name} = parse_lxc_option($name, $value);
+		    }
+		}
 
-	if ($net->{'veth.pair'} =~ m/^veth\d+.(\d+)$/) {
-	    $data->{"net$1"} = $net;
+		next;
+	    }
+	    die "unable to parse config line: $line\n";
+	}
+	&$push_network($network);
+
+	foreach my $net (@{$network_list}) {
+	    next if $net->{type} eq 'empty'; # skip
+	    $net->{'veth.pair'} = &$find_next_hostif_name() if !$net->{'veth.pair'};
+	    $net->{hwaddr} =  PVE::Tools::random_ether_addr() if !$net->{hwaddr};
+	    die "unsupported network type '$net->{type}'\n" if $net->{type} ne 'veth';
+
+	    if ($net->{'veth.pair'} =~ m/^veth\d+.(\d+)$/) {
+		if ($snapname) {
+		    $data->{snapshots}->{$snapname}->{"net$1"} = $net
+		} else {
+		    $data->{"net$1"} = $net;
+	    }
+	    }
 	}
+
     }
 
     return $data;
@@ -918,6 +1003,12 @@ sub parse_ipv4_cidr {
     die "unable to parse ipv4 address/mask\n";
 }
 
+sub check_lock {
+    my ($conf) = @_;
+
+    die "VM is locked ($conf->{'pve.lock'})\n" if $conf->{'pve.lock'};
+}
+
 sub lxc_conf_to_pve {
     my ($vmid, $lxc_conf) = @_;
 
@@ -971,6 +1062,18 @@ sub lxc_conf_to_pve {
 	}
     }
 
+    if (my $parent = $lxc_conf->{'pve.parent'}) {
+	    $conf->{parent} = $lxc_conf->{'pve.parent'};
+    }
+
+    if (my $parent = $lxc_conf->{'pve.snapcomment'}) {
+	$conf->{description} = $lxc_conf->{'pve.snapcomment'};
+    }
+
+    if (my $parent = $lxc_conf->{'pve.snaptime'}) {
+	$conf->{snaptime} = $lxc_conf->{'pve.snaptime'};
+    }
+
     return $conf;
 }
 
-- 
2.1.4





More information about the pve-devel mailing list