use strict;
use warnings;
+use POSIX qw(EINTR);
use File::Path;
use Fcntl ':flock';
use PVE::Cluster qw(cfs_register_file cfs_read_file);
+use PVE::Storage;
use PVE::SafeSyslog;
use PVE::INotify;
use PVE::JSONSchema qw(get_standard_option);
use PVE::Tools qw($IPV6RE $IPV4RE);
+use PVE::Network;
use Data::Dumper;
'lxc.utsname' => 1,
'lxc.id_map' => 1,
-
+
'lxc.cgroup.memory.limit_in_bytes' => \&parse_lxc_size,
'lxc.cgroup.memory.memsw.limit_in_bytes' => \&parse_lxc_size,
'lxc.cgroup.cpu.cfs_period_us' => '\d+',
'lxc.cgroup.cpu.cfs_quota_us' => '\d+',
'lxc.cgroup.cpu.shares' => '\d+',
-
+
# mount related
'lxc.mount' => 1,
'lxc.mount.entry' => 1,
'lxc.tty' => '\d+',
'lxc.pts' => 1,
'lxc.haltsignal' => 1,
- 'lxc.rebootsignal' => 1,
+ 'lxc.rebootsignal' => 1,
'lxc.stopsignal' => 1,
'lxc.init_cmd' => 1,
'lxc.console' => 1,
'lxc.loglevel' => 1,
'lxc.logfile' => 1,
'lxc.environment' => 1,
-
+ 'lxc.cgroup.devices.deny' => 1,
# autostart
'lxc.start.auto' => 1,
'lxc.hook.start' => 1,
'lxc.hook.post-stop' => 1,
'lxc.hook.clone' => 1,
-
+
# pve related keys
'pve.nameserver' => sub {
my ($name, $value) = @_;
},
'pve.comment' => 1,
'pve.disksize' => '\d+(\.\d+)?',
+ 'pve.volid' => sub {
+ my ($name, $value) = @_;
+ PVE::Storage::parse_volume_id($value);
+ return $value;
+ },
};
my $valid_lxc_network_keys = {
'lxc.mount' => 1,
'lxc.include' => 1,
'lxc.id_map' => 1,
+ 'lxc.cgroup.devices.deny' => 1,
};
sub write_lxc_config {
my $done_hash = { digest => 1};
- foreach my $k (sort keys %$data) {
- next if $k !~ m/^lxc\./;
+ my $dump_entry = sub {
+ my ($k) = @_;
+ my $value = $data->{$k};
+ return if !defined($value);
+ return if $done_hash->{$k};
$done_hash->{$k} = 1;
- if (ref($data->{$k})) {
- die "got unexpected reference for '$k'" if !$lxc_array_configs->{$k};
- foreach my $v (@{$data->{$k}}) {
+ if (ref($value)) {
+ die "got unexpected reference for '$k'"
+ if !$lxc_array_configs->{$k};
+ foreach my $v (@$value) {
$raw .= "$k = $v\n";
}
} else {
- $raw .= "$k = $data->{$k}\n";
+ $raw .= "$k = $value\n";
}
+ };
+
+ # Note: Order is important! Include defaults first, so that we
+ # can overwrite them later.
+ &$dump_entry('lxc.include');
+
+ foreach my $k (sort keys %$data) {
+ next if $k !~ m/^lxc\./;
+ &$dump_entry($k);
}
foreach my $k (sort keys %$data) {
next if $k !~ m/^pve\./;
- $done_hash->{$k} = 1;
- $raw .= "$k = $data->{$k}\n";
+ &$dump_entry($k);
}
+ 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 (!$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!";
my $parser = $valid_lxc_keys->{$name};
- die "inavlid key '$name'\n" if !defined($parser);
+ die "invalid key '$name'\n" if !defined($parser);
if ($parser eq '1') {
- return $value;
+ return $value;
} elsif (ref($parser)) {
my $res = &$parser($name, $value);
return $res if defined($res);
# assume regex
return $value if $value =~ m/^$parser$/;
}
-
+
die "unable to parse value '$value' for option '$name'\n";
}
die "multiple definitions for $name\n" if defined($data->{$name});
$data->{$name} = parse_lxc_option($name, $value);
}
-
+
next;
}
&$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';
return $filename;
}
+# flock: we use one file handle per process, so lock file
+# can be called multiple times and succeeds for the same process.
+
+my $lock_handles = {};
+my $lockdir = "/run/lock/lxc";
+
+sub lock_filename {
+ my ($vmid) = @_;
+
+ return "$lockdir/pve-config-{$vmid}.lock";
+}
+
+sub lock_aquire {
+ my ($vmid, $timeout) = @_;
+
+ $timeout = 10 if !$timeout;
+ my $mode = LOCK_EX;
+
+ my $filename = lock_filename($vmid);
+
+ mkdir $lockdir if !-d $lockdir;
+
+ my $lock_func = sub {
+ if (!$lock_handles->{$$}->{$filename}) {
+ my $fh = new IO::File(">>$filename") ||
+ die "can't open file - $!\n";
+ $lock_handles->{$$}->{$filename} = { fh => $fh, refcount => 0};
+ }
+
+ if (!flock($lock_handles->{$$}->{$filename}->{fh}, $mode |LOCK_NB)) {
+ print STDERR "trying to aquire lock...";
+ my $success;
+ while(1) {
+ $success = flock($lock_handles->{$$}->{$filename}->{fh}, $mode);
+ # try again on EINTR (see bug #273)
+ if ($success || ($! != EINTR)) {
+ last;
+ }
+ }
+ if (!$success) {
+ print STDERR " failed\n";
+ die "can't aquire lock - $!\n";
+ }
+
+ $lock_handles->{$$}->{$filename}->{refcount}++;
+
+ print STDERR " OK\n";
+ }
+ };
+
+ eval { PVE::Tools::run_with_timeout($timeout, $lock_func); };
+ my $err = $@;
+ if ($err) {
+ die "can't lock file '$filename' - $err";
+ }
+}
+
+sub lock_release {
+ my ($vmid) = @_;
+
+ my $filename = lock_filename($vmid);
+
+ if (my $fh = $lock_handles->{$$}->{$filename}->{fh}) {
+ my $refcount = --$lock_handles->{$$}->{$filename}->{refcount};
+ if ($refcount <= 0) {
+ $lock_handles->{$$}->{$filename} = undef;
+ close ($fh);
+ }
+ }
+}
+
sub lock_container {
my ($vmid, $timeout, $code, @param) = @_;
- my $lockdir = "/run/lock/lxc";
- my $lockfile = "$lockdir/pve-config-{$vmid}.lock";
-
- File::Path::make_path($lockdir);
+ my $res;
- my $res = PVE::Tools::lock_file($lockfile, $timeout, $code, @param);
+ lock_aquire($vmid, $timeout);
+ eval { $res = &$code(@param) };
+ my $err = $@;
+ lock_release($vmid);
- die $@ if $@;
+ die $err if $err;
return $res;
}
default => 0,
},
startup => get_standard_option('pve-startup-order'),
- cpus => {
+ cpulimit => {
optional => 1,
- type => 'integer',
- description => "The number of CPUs for this container (0 is unlimited).",
+ type => 'number',
+ description => "Limit of CPU usage. Note if the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit.",
minimum => 0,
maximum => 128,
default => 0,
$confdesc->{"net$i"} = {
optional => 1,
type => 'string', format => 'pve-lxc-network',
- description => "Specifies network interfaces for the container.",
+ description => "Specifies network interfaces for the container.\n\n".
+ "The string should have the follow format:\n\n".
+ "-net<[0-9]> bridge=<vmbr<Nummber>>[,hwaddr=<MAC>]\n".
+ "[,mtu=<Number>][,name=<String>][,ip=<IPv4Format/CIDR>]\n".
+ ",ip6=<IPv6Format/CIDR>][,gw=<GatwayIPv4>]\n".
+ ",gw6=<GatwayIPv6>][,firewall=<[1|0]>][,tag=<VlanNo>]",
};
}
# container status helpers
sub list_active_containers {
-
+
my $filename = "/proc/net/unix";
# similar test is used by lcxcontainers.c: list_active_containers
my $res = {};
-
+
my $fh = IO::File->new ($filename, "r");
return $res if !$fh;
}
close($fh);
-
+
return $res;
}
my $active_hash = list_active_containers();
return 1 if defined($active_hash->{$vmid});
-
+
return undef;
}
my ($vmid) = @_;
my $cmd = ['lxc-attach', '-n', $vmid, '--', 'df', '-P', '-B', '1', '/'];
-
+
my $res = {
total => 0,
used => 0,
my $list = $opt_vmid ? { $opt_vmid => { type => 'lxc' }} : config_list();
my $active_hash = list_active_containers();
-
+
foreach my $vmid (keys %$list) {
my $d = $list->{$vmid};
my $running = defined($active_hash->{$vmid});
-
+
$d->{status} = $running ? 'running' : 'stopped';
my $cfspath = cfs_config_path($vmid);
my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
-
+
$d->{name} = $conf->{'lxc.utsname'} || "CT$vmid";
$d->{name} =~ s/[\s]//g;
-
+
$d->{cpus} = 0;
my $cfs_period_us = $conf->{'lxc.cgroup.cpu.cfs_period_us'};
if ($cfs_period_us && $cfs_quota_us) {
$d->{cpus} = int($cfs_quota_us/$cfs_period_us);
}
-
+
$d->{disk} = 0;
$d->{maxdisk} = defined($conf->{'pve.disksize'}) ?
int($conf->{'pve.disksize'}*1024*1024)*1024 : 1024*1024*1024*1024*1024;
-
+
if (my $private = $conf->{'lxc.rootfs'}) {
if ($private =~ m!^/!) {
my $res = PVE::Tools::df($private, 2);
$d->{disk} = $res->{used};
$d->{maxdisk} = $res->{total};
} elsif ($running) {
- if ($private =~ m!^loop:(\S+)$!) {
+ if ($private =~ m!^(?:loop|nbd):(?:\S+)$!) {
my $res = get_container_disk_usage($vmid);
$d->{disk} = $res->{used};
$d->{maxdisk} = $res->{total};
- }
+ }
}
}
-
+
$d->{mem} = 0;
$d->{swap} = 0;
$d->{maxmem} = ($conf->{'lxc.cgroup.memory.limit_in_bytes'}||0) +
$d->{diskread} = 0;
$d->{diskwrite} = 0;
}
-
+
foreach my $vmid (keys %$list) {
my $d = $list->{$vmid};
next if $d->{status} ne 'running';
$d->{mem} = read_cgroup_value('memory', $vmid, 'memory.usage_in_bytes');
$d->{swap} = read_cgroup_value('memory', $vmid, 'memory.memsw.usage_in_bytes') - $d->{mem};
+
+ my $blkio_bytes = read_cgroup_value('blkio', $vmid, 'blkio.throttle.io_service_bytes', 1);
+ my @bytes = split(/\n/, $blkio_bytes);
+ foreach my $byte (@bytes) {
+ if (my ($key, $value) = $byte =~ /(Read|Write)\s+(\d+)/) {
+ $d->{diskread} = $2 if $key eq 'Read';
+ $d->{diskwrite} = $2 if $key eq 'Write';
+ }
+ }
}
-
+
return $list;
}
sub print_lxc_network {
my $net = shift;
- die "no network bridge defined\n" if !$net->{bridge};
+ die "no network name defined\n" if !$net->{name};
- my $res = "bridge=$net->{bridge}";
+ my $res = "name=$net->{name}";
- foreach my $k (qw(hwaddr mtu name ip gw ip6 gw6 firewall tag)) {
+ foreach my $k (qw(hwaddr mtu bridge ip gw ip6 gw6 firewall tag)) {
next if !defined($net->{$k});
$res .= ",$k=$net->{$k}";
}
}
$res->{type} = 'veth';
- $res->{hwaddr} = PVE::Tools::random_ether_addr() if !$res->{mac};
-
+ $res->{hwaddr} = PVE::Tools::random_ether_addr() if !$res->{hwaddr};
+
return $res;
}
return PVE::Tools::file_read_firstline($path);
}
+sub write_cgroup_value {
+ my ($group, $vmid, $name, $value) = @_;
+
+ my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
+ PVE::ProcFSTools::write_proc_entry($path, $value) if -e $path;
+
+}
+
sub find_lxc_console_pids {
my $res = {};
my @args = split(/\0/, $cmdline);
# serach for lxc-console -n <vmid>
- return if scalar(@args) != 3;
+ return if scalar(@args) != 3;
return if $args[1] ne '-n';
return if $args[2] !~ m/^\d+$/;
return if $args[0] !~ m|^(/usr/bin/)?lxc-console$|;
-
+
my $vmid = $args[2];
-
+
push @{$res->{$vmid}}, $pid;
});
return $res;
}
+sub find_lxc_pid {
+ my ($vmid) = @_;
+
+ my $pid = undef;
+ my $parser = sub {
+ my $line = shift;
+ $pid = $1 if $line =~ m/^PID:\s+(\d+)$/;
+ };
+ PVE::Tools::run_command(['lxc-info', '-n', $vmid], outfunc => $parser);
+
+ die "unable to get PID for CT $vmid (not running?)\n" if !$pid;
+
+ return $pid;
+}
+
my $ipv4_reverse_mask = [
'0.0.0.0',
'128.0.0.0',
'255.255.255.254',
'255.255.255.255',
];
-
-# Note: we cannot use Net:IP, because that only allows strict
+
+# Note: we cannot use Net:IP, because that only allows strict
# CIDR networks
sub parse_ipv4_cidr {
my ($cidr, $noerr) = @_;
if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 < 32)) {
return { address => $1, netmask => $ipv4_reverse_mask->[$2] };
}
-
+
return undef if $noerr;
-
+
die "unable to parse ipv4 address/mask\n";
}
my $mem = $lxc_conf->{'lxc.cgroup.memory.limit_in_bytes'} || 0;
$conf->{$k} = int(($value -$mem) / (1024*1024));
}
- } elsif ($k eq 'cpus') {
+ } elsif ($k eq 'cpulimit') {
my $cfs_period_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_period_us'};
my $cfs_quota_us = $lxc_conf->{'lxc.cgroup.cpu.cfs_quota_us'};
-
+
if ($cfs_period_us && $cfs_quota_us) {
- $conf->{$k} = int($cfs_quota_us/$cfs_period_us);
+ $conf->{$k} = $cfs_quota_us/$cfs_period_us;
} else {
$conf->{$k} = 0;
}
$conf->{$k} = print_lxc_network($net);
}
}
-
+
return $conf;
}
sub update_lxc_config {
my ($vmid, $conf, $running, $param, $delete) = @_;
- # fixme: hotplug
- die "unable to modify config while container is running\n" if $running;
+ my @nohotplug;
+
+ my $rootdir;
+ if ($running) {
+ my $pid = find_lxc_pid($vmid);
+ $rootdir = "/proc/$pid/root";
+ }
if (defined($delete)) {
foreach my $opt (@$delete) {
die "unable to delete required option '$opt'\n";
} elsif ($opt eq 'swap') {
delete $conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'};
+ write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", -1);
} elsif ($opt eq 'description') {
delete $conf->{'pve.comment'};
} elsif ($opt eq 'onboot') {
delete $conf->{'pve.startup'};
} elsif ($opt eq 'nameserver') {
delete $conf->{'pve.nameserver'};
+ push @nohotplug, $opt;
+ next if $running;
} elsif ($opt eq 'searchdomain') {
delete $conf->{'pve.searchdomain'};
- } elsif ($opt =~ m/^net\d$/) {
+ push @nohotplug, $opt;
+ next if $running;
+ } elsif ($opt =~ m/^net(\d)$/) {
delete $conf->{$opt};
+ next if !$running;
+ my $netid = $1;
+ PVE::Network::veth_delete("veth${vmid}.$netid");
} else {
die "implement me"
}
+ PVE::LXC::write_config($vmid, $conf) if $running;
}
}
} elsif ($opt eq 'nameserver') {
my $list = verify_nameserver_list($value);
$conf->{'pve.nameserver'} = $list;
+ push @nohotplug, $opt;
+ next if $running;
} elsif ($opt eq 'searchdomain') {
my $list = verify_searchdomain_list($value);
$conf->{'pve.searchdomain'} = $list;
+ push @nohotplug, $opt;
+ next if $running;
} elsif ($opt eq 'memory') {
$conf->{'lxc.cgroup.memory.limit_in_bytes'} = $value*1024*1024;
+ write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", $value*1024*1024);
} elsif ($opt eq 'swap') {
my $mem = $conf->{'lxc.cgroup.memory.limit_in_bytes'};
$mem = $param->{memory}*1024*1024 if $param->{memory};
$conf->{'lxc.cgroup.memory.memsw.limit_in_bytes'} = $mem + $value*1024*1024;
- } elsif ($opt eq 'cpus') {
+ write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", $mem + $value*1024*1024);
+
+ } elsif ($opt eq 'cpulimit') {
if ($value > 0) {
my $cfs_period_us = 100000;
$conf->{'lxc.cgroup.cpu.cfs_period_us'} = $cfs_period_us;
$conf->{'lxc.cgroup.cpu.cfs_quota_us'} = $cfs_period_us*$value;
+ write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", $cfs_period_us*$value);
} else {
delete $conf->{'lxc.cgroup.cpu.cfs_period_us'};
delete $conf->{'lxc.cgroup.cpu.cfs_quota_us'};
+ write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", -1);
}
} elsif ($opt eq 'cpuunits') {
- $conf->{'lxc.cgroup.cpu.shares'} = $value;
+ $conf->{'lxc.cgroup.cpu.shares'} = $value;
+ write_cgroup_value("cpu", $vmid, "cpu.shares", $value);
} elsif ($opt eq 'description') {
$conf->{'pve.comment'} = PVE::Tools::encode_text($value);
} elsif ($opt eq 'disk') {
$conf->{'pve.disksize'} = $value;
+ push @nohotplug, $opt;
+ next if $running;
} elsif ($opt =~ m/^net(\d+)$/) {
my $netid = $1;
my $net = PVE::LXC::parse_lxc_network($value);
$net->{'veth.pair'} = "veth${vmid}.$netid";
- $conf->{$opt} = $net;
+ if (!$running) {
+ $conf->{$opt} = $net;
+ } else {
+ update_net($vmid, $conf, $opt, $net, $netid, $rootdir);
+ }
} else {
die "implement me"
}
+ PVE::LXC::write_config($vmid, $conf) if $running;
+ }
+
+ if ($running && scalar(@nohotplug)) {
+ die "unable to modify " . join(',', @nohotplug) . " while container is running\n";
}
}
my ($conf) = @_;
# return data from net0
-
+
my $net = $conf->{net0};
return undef if !$net;
$ipv4 =~ s!/\d+$!! if $ipv4;
my $ipv6 = $net->{ip};
$ipv6 =~ s!/\d+$!! if $ipv6;
-
+
return ($ipv4, $ipv6);
}
-
+
+sub destory_lxc_container {
+ my ($storage_cfg, $vmid, $conf) = @_;
+
+ if (my $volid = $conf->{'pve.volid'}) {
+
+ my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $volid);
+ die "got strange volid (containe is not owner!)\n" if $vmid != $owner;
+
+ PVE::Storage::vdisk_free($storage_cfg, $volid);
+
+ destroy_config($vmid);
+
+ } else {
+ my $cmd = ['lxc-destroy', '-n', $vmid ];
+
+ PVE::Tools::run_command($cmd);
+ }
+}
+
+my $safe_num_ne = sub {
+ my ($a, $b) = @_;
+
+ return 0 if !defined($a) && !defined($b);
+ return 1 if !defined($a);
+ return 1 if !defined($b);
+
+ return $a != $b;
+};
+
+my $safe_string_ne = sub {
+ my ($a, $b) = @_;
+
+ return 0 if !defined($a) && !defined($b);
+ return 1 if !defined($a);
+ return 1 if !defined($b);
+
+ return $a ne $b;
+};
+
+sub update_net {
+ my ($vmid, $conf, $opt, $newnet, $netid, $rootdir) = @_;
+
+ my $veth = $newnet->{'veth.pair'};
+ my $vethpeer = $veth . "p";
+ my $eth = $newnet->{name};
+
+ if ($conf->{$opt}) {
+ if (&$safe_string_ne($conf->{$opt}->{hwaddr}, $newnet->{hwaddr}) ||
+ &$safe_string_ne($conf->{$opt}->{name}, $newnet->{name})) {
+
+ PVE::Network::veth_delete($veth);
+ delete $conf->{$opt};
+ PVE::LXC::write_config($vmid, $conf);
+
+ hotplug_net($vmid, $conf, $opt, $newnet);
+
+ } elsif (&$safe_string_ne($conf->{$opt}->{bridge}, $newnet->{bridge}) ||
+ &$safe_num_ne($conf->{$opt}->{tag}, $newnet->{tag}) ||
+ &$safe_num_ne($conf->{$opt}->{firewall}, $newnet->{firewall})) {
+
+ if ($conf->{$opt}->{bridge}){
+ PVE::Network::tap_unplug($veth);
+ delete $conf->{$opt}->{bridge};
+ delete $conf->{$opt}->{tag};
+ delete $conf->{$opt}->{firewall};
+ PVE::LXC::write_config($vmid, $conf);
+ }
+
+ PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
+ $conf->{$opt}->{bridge} = $newnet->{bridge} if $newnet->{bridge};
+ $conf->{$opt}->{tag} = $newnet->{tag} if $newnet->{tag};
+ $conf->{$opt}->{firewall} = $newnet->{firewall} if $newnet->{firewall};
+ PVE::LXC::write_config($vmid, $conf);
+ }
+ } else {
+ hotplug_net($vmid, $conf, $opt, $newnet);
+ }
+
+ update_ipconfig($vmid, $conf, $opt, $eth, $newnet, $rootdir);
+}
+
+sub hotplug_net {
+ my ($vmid, $conf, $opt, $newnet) = @_;
+
+ my $veth = $newnet->{'veth.pair'};
+ my $vethpeer = $veth . "p";
+ my $eth = $newnet->{name};
+
+ PVE::Network::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
+ PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
+
+ # attach peer in container
+ my $cmd = ['lxc-device', '-n', $vmid, 'add', $vethpeer, "$eth" ];
+ PVE::Tools::run_command($cmd);
+
+ # link up peer in container
+ $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'link', 'set', $eth ,'up' ];
+ PVE::Tools::run_command($cmd);
+
+ $conf->{$opt}->{type} = 'veth';
+ $conf->{$opt}->{bridge} = $newnet->{bridge} if $newnet->{bridge};
+ $conf->{$opt}->{tag} = $newnet->{tag} if $newnet->{tag};
+ $conf->{$opt}->{firewall} = $newnet->{firewall} if $newnet->{firewall};
+ $conf->{$opt}->{hwaddr} = $newnet->{hwaddr} if $newnet->{hwaddr};
+ $conf->{$opt}->{name} = $newnet->{name} if $newnet->{name};
+ $conf->{$opt}->{'veth.pair'} = $newnet->{'veth.pair'} if $newnet->{'veth.pair'};
+
+ delete $conf->{$opt}->{ip} if $conf->{$opt}->{ip};
+ delete $conf->{$opt}->{ip6} if $conf->{$opt}->{ip6};
+ delete $conf->{$opt}->{gw} if $conf->{$opt}->{gw};
+ delete $conf->{$opt}->{gw6} if $conf->{$opt}->{gw6};
+
+ PVE::LXC::write_config($vmid, $conf);
+}
+
+sub update_ipconfig {
+ my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
+
+ my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir);
+
+ my $update_gateway;
+ if (&$safe_string_ne($conf->{$opt}->{gw}, $newnet->{gw})) {
+
+ $update_gateway = 1;
+ if ($conf->{$opt}->{gw}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'del', 'default', 'via', $conf->{$opt}->{gw} ];
+ eval { PVE::Tools::run_command($cmd); };
+ warn $@ if $@; # ignore errors here
+ delete $conf->{$opt}->{gw};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+ }
+
+ if (&$safe_string_ne($conf->{$opt}->{ip}, $newnet->{ip})) {
+
+ if ($conf->{$opt}->{ip}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'del', $conf->{$opt}->{ip}, 'dev', $eth ];
+ eval { PVE::Tools::run_command($cmd); };
+ warn $@ if $@; # ignore errors here
+ delete $conf->{$opt}->{ip};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+
+ if ($newnet->{ip}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'add', $newnet->{ip}, 'dev', $eth ];
+ PVE::Tools::run_command($cmd);
+
+ $conf->{$opt}->{ip} = $newnet->{ip};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+ }
+
+ if ($update_gateway) {
+
+ if ($newnet->{gw}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'add', 'default', 'via', $newnet->{gw} ];
+ PVE::Tools::run_command($cmd);
+
+ $conf->{$opt}->{gw} = $newnet->{gw};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+ }
+
+ my $update_gateway6;
+ if (&$safe_string_ne($conf->{$opt}->{gw6}, $newnet->{gw6})) {
+
+ $update_gateway6 = 1;
+ if ($conf->{$opt}->{gw6}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'del', 'default', 'via', $conf->{$opt}->{gw6} ];
+ eval { PVE::Tools::run_command($cmd); };
+ warn $@ if $@; # ignore errors here
+ delete $conf->{$opt}->{gw6};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+
+
+ if (&$safe_string_ne($conf->{$opt}->{ip6}, $newnet->{ip6})) {
+
+ if ($conf->{$opt}->{ip6}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'del', $conf->{$opt}->{ip6}, 'dev', $eth ];
+ eval { PVE::Tools::run_command($cmd); };
+ warn $@ if $@; # ignore errors here
+ delete $conf->{$opt}->{ip6};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+
+ if ($newnet->{ip6}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'addr', 'add', $newnet->{ip6}, 'dev', $eth ];
+ PVE::Tools::run_command($cmd);
+
+ $conf->{$opt}->{ip6} = $newnet->{ip6};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+ }
+
+ if ($update_gateway6) {
+
+ if ($newnet->{gw6}) {
+ my $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'route', 'add', 'default', 'via', $newnet->{gw6} ];
+ PVE::Tools::run_command($cmd);
+
+ $conf->{$opt}->{gw6} = $newnet->{gw6};
+ PVE::LXC::write_config($vmid, $conf);
+ $lxc_setup->setup_network($conf);
+ }
+ }
+}
+
1;