]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC.pm
parse_lxc_config: cleanup parser
[pve-container.git] / src / PVE / LXC.pm
index ab06867eb7d12f681bf764e7987c063e0b1d72dc..ae689169acbb653d068b5d4b3da439b32ffc6035 100644 (file)
@@ -291,138 +291,49 @@ sub parse_lxc_config {
 
     my $vmid = $1;
 
-    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";
+     
+    my $network_counter = 0;
+    my $network_list = [];
+    my $host_ifnames = {};
+    my $snapname;
+    my $network;
+
+    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;
            }
        }
-       push(@{$sections},$tmp);
 
-       return $sections;
+       die "unable to find free host_ifname"; # should not happen
     };
 
-    my $sec = &$split_config($raw);
-
-    foreach my  $sec_raw (@{$sec}){
-       next if $sec_raw eq '';
-       my $snapname = undef;
-
-       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;
-               }
-           }
-
-           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";
-               }
-           }
-       };
-
-       my $network;
-
-       while ($sec_raw && $sec_raw =~ s/^(.*?)(\n|$)//) {
-           my $line = $1;
-
-           next if $line =~ m/^\#/;
-           next if $line =~ m/^\s*$/;
-
-           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);
-                   }
-               }
-
-               next;
+    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";
            }
-           die "unable to parse config line: $line\n";
        }
-       &$push_network($network);
+    };
 
+    my $finalize_section = sub {
+       &$push_network($network); # flush
+       
        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;
@@ -431,8 +342,71 @@ sub parse_lxc_config {
                }
            }
        }
+
+       # reset helper vars
+       $network_counter = 0;
+       $network_list = [];
+       $host_ifnames = {};
+       $network = undef;
+    };
+    
+    while ($raw && $raw =~ s/^(.*)?(\n|$)//) {
+       my $line = $1;
+       next if $line =~ m/^\s*$/; # skip empty lines
+       next if $line =~ m/^#/; # skip comments
+
+       # snap.pve.snapname starts new sections
+       if ($line =~ m/^(snap\.)?pve\.snapname\s*=\s*(\w*)\s*$/) {
+           my $value = $2;
+           
+           &$finalize_section();
+
+           $snapname = $value;
+           $data->{snapshots}->{$snapname}->{'pve.snapname'} = $snapname;
+           
+       } elsif ($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";
+           }
+       } elsif ($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";
+           }
+       } elsif ($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);
+               }
+           }
+       } else {
+           die "unable to parse config line: $line\n";
+       }
     }
 
+    &$finalize_section();
+
     return $data;
 }
 
@@ -1545,9 +1519,120 @@ sub snapshot_create {
        &$snapshot_commit($vmid, $snapname);
     };
     if(my $err = $@) {
-       #ToDo implement delete snapshot
+       snapshot_delete($vmid, $snapname, 1);
        die "$err\n";
     }
 }
 
+sub snapshot_delete {
+    my ($vmid, $snapname, $force) = @_;
+
+    my $snap;
+
+    my $conf;
+
+    my $updatefn =  sub {
+
+       $conf = load_config($vmid);
+
+       $snap = $conf->{snapshots}->{$snapname};
+
+       check_lock($conf);
+
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+       $snap->{'pve.snapstate'} = 'delete';
+
+       PVE::LXC::write_config($vmid, $conf);
+    };
+
+    lock_container($vmid, 10, $updatefn);
+
+    my $storecfg = PVE::Storage::config();
+
+    my $del_snap =  sub {
+
+       check_lock($conf);
+
+       if ($conf->{'pve.parent'} eq $snapname) {
+           if ($conf->{snapshots}->{$snapname}->{'pve.snapname'}) {
+               $conf->{'pve.parent'} = $conf->{snapshots}->{$snapname}->{'pve.parent'};
+           } else {
+               delete $conf->{'pve.parent'};
+           }
+       }
+
+       delete $conf->{snapshots}->{$snapname};
+
+       PVE::LXC::write_config($vmid, $conf);
+    };
+
+    my $volid = $conf->{snapshots}->{$snapname}->{'pve.volid'};
+
+    eval {
+       PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snapname);
+    };
+    my $err = $@;
+
+    if(!$err || ($err && $force)) {
+       lock_container($vmid, 10, $del_snap);
+       if ($err) {
+           die "Can't delete snapshot: $vmid $snapname $err\n";
+       }
+    }
+}
+
+sub snapshot_rollback {
+    my ($vmid, $snapname) = @_;
+
+    my $storecfg = PVE::Storage::config();
+
+    my $conf = load_config($vmid);
+
+    my $snap = $conf->{snapshots}->{$snapname};
+
+    die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+    PVE::Storage::volume_rollback_is_possible($storecfg, $snap->{'pve.volid'},
+                                             $snapname);
+
+    my $updatefn = sub {
+
+       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n" if $snap->{snapstate};
+
+       check_lock($conf);
+
+       system("lxc-stop -n $vmid --kill") if check_running($vmid);
+
+       die "unable to rollback vm $vmid: vm is running\n"
+           if check_running($vmid);
+
+       $conf->{'pve.lock'} = 'rollback';
+
+       my $forcemachine;
+
+       # copy snapshot config to current config
+
+       my $tmp_conf = $conf;
+       &$snapshot_copy_config($tmp_conf->{snapshots}->{$snapname}, $conf);
+       $conf->{snapshots} = $tmp_conf->{snapshots};
+       delete $conf->{'pve.snaptime'};
+       delete $conf->{'pve.snapname'};
+       $conf->{'pve.parent'} = $snapname;
+
+       PVE::LXC::write_config($vmid, $conf);
+    };
+
+    my $unlockfn = sub {
+       delete $conf->{'pve.lock'};
+       PVE::LXC::write_config($vmid, $conf);
+    };
+
+    lock_container($vmid, 10, $updatefn);
+
+    PVE::Storage::volume_snapshot_rollback($storecfg, $conf->{'pve.volid'}, $snapname);
+
+    lock_container($vmid, 5, $unlockfn);
+}
+
 1;