[pve-devel] [PATCH 02/20] cloud-init changes
Alexandre Derumier
aderumier at odiso.com
Thu Jul 27 17:06:35 CEST 2017
From: Wolfgang Bumiller <w.bumiller at proxmox.com>
* Add ipconfigX for all netX configuration options and
using ip=CIDR, gw=IP, ip6=CIDR, gw6=IP as option names
like in LXC.
* Adding explicit ip=dhcp and ip6=dhcp options.
* Removing the config-update code and instead generating
the ide3 commandline in config_to_command.
- Adding a conflict check to write_vm_config similar to
the one for 'cdrom'.
* Replacing UUID generation with a SHA1 hash of the
concatenated userdata and network configuration. For this
generate_cloudinit_userdata/network now returns the
content variable.
* Finishing ipv6 support in generate_cloudinit_network.
Note that ipv4 now only defaults to dhcp if no ipv6
address was specified. (Explicitly requested dhcp is
always used.)
Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
PVE/QemuServer.pm | 175 ++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 131 insertions(+), 44 deletions(-)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 8326031..26ef811 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -18,7 +18,6 @@ use Cwd 'abs_path';
use IPC::Open3;
use JSON;
use Fcntl;
-use UUID;
use PVE::SafeSyslog;
use Storable qw(dclone);
use PVE::Exception qw(raise raise_param_exc);
@@ -675,20 +674,6 @@ my $net_fmt = {
description => 'Whether this interface should be disconnected (like pulling the plug).',
optional => 1,
},
- cidr => {
- type => 'string',
- format => 'CIDR',
- format_description => 'IP/CIDR',
- description => 'IP Address for the interface.',
- optional => 1,
- },
- gateway => {
- type => 'string',
- format => 'ip',
- format_description => 'IP',
- description => 'Default gateway to use with this interface.',
- optional => 1,
- },
};
my $netdesc = {
@@ -699,8 +684,26 @@ my $netdesc = {
PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
+my $ipconfigdesc = {
+ optional => 1,
+ type => 'string', format => 'pve-qm-ipconfig',
+ typetext => "[ip=IPv4_CIDR[,gw=IPv4_GATEWAY]][,ip6=IPv6_CIDR[,gw6=IPv6_GATEWAY]]",
+ description => <<'EODESCR',
+Specify IP addresses and gateways for the corresponding interface.
+
+IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
+
+The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
+For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
+
+If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
+EODESCR
+};
+PVE::JSONSchema::register_standard_option("pve-qm-ipconfig", $netdesc);
+
for (my $i = 0; $i < $MAX_NETS; $i++) {
$confdesc->{"net$i"} = $netdesc;
+ $confdesc->{"ipconfig$i"} = $ipconfigdesc;
}
PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path);
@@ -1862,9 +1865,57 @@ sub parse_net {
my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
$res->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
}
- if (my $cidr = $res->{cidr}) {
- ($res->{address}, $res->{netmask}) = split('/', $cidr);
- delete $res->{cidr};
+ $res->{macaddr} = PVE::Tools::random_ether_addr() if !defined($res->{macaddr});
+ return $res;
+}
+
+# ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
+sub parse_ipconfig {
+ my ($data) = @_;
+
+ my $res = {};
+
+ foreach my $kvp (split(/,/, $data)) {
+ if ($kvp =~ m/^ip=dhcp$/) {
+ $res->{address} = 'dhcp';
+ } elsif ($kvp =~ m/^ip=($IPV4RE)\/(\d+)$/) {
+ $res->{address} = $1;
+ $res->{netmask} = $2;
+ } elsif ($kvp =~ m/^gw=($IPV4RE)$/) {
+ $res->{gateway} = $1;
+ } elsif ($kvp =~ m/^ip6=dhcp6?$/) {
+ $res->{address6} = 'dhcp';
+ } elsif ($kvp =~ m/^ip6=auto$/) {
+ $res->{address6} = 'auto';
+ } elsif ($kvp =~ m/^ip6=($IPV6RE)\/(\d+)$/) {
+ $res->{address6} = $1;
+ $res->{netmask6} = $2;
+ } elsif ($kvp =~ m/^gw6=($IPV6RE)$/) {
+ $res->{gateway6} = $1;
+ } else {
+ return undef;
+ }
+ }
+
+ if ($res->{gateway} && !$res->{address}) {
+ warn 'gateway specified without specifying an IP address';
+ return undef;
+ }
+ if ($res->{gateway6} && !$res->{address6}) {
+ warn 'IPv6 gateway specified without specifying an IPv6 address';
+ return undef;
+ }
+ if ($res->{gateway} && $res->{address} eq 'dhcp') {
+ warn 'gateway specified together with DHCP';
+ return undef;
+ }
+ if ($res->{gateway6} && $res->{address6} eq 'dhcp') {
+ warn 'IPv6 gateway specified together with DHCP6';
+ return undef;
+ }
+
+ if (!$res->{address} && !$res->{address6}) {
+ return { address => 'dhcp' };
}
return $res;
@@ -2056,6 +2107,17 @@ sub verify_bootdisk {
die "invalid boot disk '$value'\n";
}
+PVE::JSONSchema::register_format('pve-qm-ipconfig', \&verify_ipconfig);
+sub verify_ipconfig {
+ my ($value, $noerr) = @_;
+
+ return $value if parse_ipconfig($value);
+
+ return undef if $noerr;
+
+ die "unable to parse ipconfig options\n";
+}
+
sub parse_watchdog {
my ($value) = @_;
@@ -2305,6 +2367,11 @@ sub write_vm_config {
delete $conf->{cdrom};
}
+ if ($conf->{cloudinit}) {
+ die "option cloudinit conflicts with ide3\n" if $conf->{ide3};
+ delete $conf->{cloudinit};
+ }
+
# we do not use 'smp' any longer
if ($conf->{sockets}) {
delete $conf->{smp};
@@ -3345,6 +3412,8 @@ sub config_to_command {
push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
});
+ generate_cloudinit_command($conf, $vmid, $storecfg, $bridges, $devices);
+
for (my $i = 0; $i < $MAX_NETS; $i++) {
next if !$conf->{"net$i"};
my $d = parse_net($conf->{"net$i"});
@@ -6562,9 +6631,9 @@ sub generate_cloudinitconfig {
mkdir "$path/drive/openstack";
mkdir "$path/drive/openstack/latest";
mkdir "$path/drive/openstack/content";
- generate_cloudinit_userdata($conf, $path);
- generate_cloudinit_metadata($conf, $path);
- generate_cloudinit_network($conf, $path);
+ my $digest_data = generate_cloudinit_userdata($conf, $path)
+ . generate_cloudinit_network($conf, $path);
+ generate_cloudinit_metadata($conf, $path, $digest_data);
my $cmd = [];
push @$cmd, 'genisoimage';
@@ -6575,10 +6644,18 @@ sub generate_cloudinitconfig {
run_command($cmd);
rmtree("$path/drive");
- my $drive = PVE::QemuServer::parse_drive('ide3', 'cloudinit,media=cdrom');
- $conf->{'ide3'} = PVE::QemuServer::print_drive($vmid, $drive);
- update_config_nolock($vmid, $conf, 1);
+}
+
+sub generate_cloudinit_command {
+ my ($conf, $vmid, $storecfg, $bridges, $devices) = @_;
+ return if !$conf->{cloudinit};
+
+ my $path = "/tmp/cloudinit/$vmid/configdrive.iso";
+ my $drive = parse_drive('ide3', 'cloudinit,media=cdrom');
+ my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
+ push @$devices, '-drive', $drive_cmd;
+ push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
}
sub generate_cloudinit_userdata {
@@ -6601,15 +6678,13 @@ sub generate_cloudinit_userdata {
my $fn = "$path/drive/openstack/latest/user_data";
file_write($fn, $content);
-
+ return $content;
}
sub generate_cloudinit_metadata {
- my ($conf, $path) = @_;
+ my ($conf, $path, $digest_data) = @_;
- my ($uuid, $uuid_str);
- UUID::generate($uuid);
- UUID::unparse($uuid, $uuid_str);
+ my $uuid_str = Digest::SHA::sha1_hex($digest_data);
my $content = "{\n";
$content .= " \"uuid\": \"$uuid_str\",\n";
@@ -6618,9 +6693,7 @@ sub generate_cloudinit_metadata {
my $fn = "$path/drive/openstack/latest/meta_data.json";
- return file_write($fn, $content);
-
-
+ file_write($fn, $content);
}
sub generate_cloudinit_network {
@@ -6629,19 +6702,33 @@ sub generate_cloudinit_network {
my $content = "auto lo\n";
$content .="iface lo inet loopback\n\n";
- foreach my $opt (keys %$conf) {
- next if $opt !~ m/^net(\d+)$/;
- my $net = parse_net($conf->{$opt});
- $opt =~ s/net/eth/;
+ my @ifaces = grep(/^net(\d+)$/, keys %$conf);
+ foreach my $iface (@ifaces) {
+ (my $id = $iface) =~ s/^net//;
+ next if !$conf->{"ipconfig$id"};
+ my $net = parse_ipconfig($conf->{"ipconfig$id"});
+ $id = "eth$id";
- $content .="auto $opt\n";
+ $content .="auto $id\n";
if ($net->{address}) {
- $content .="iface $opt inet static\n";
- $content .=" address $net->{address}\n";
- $content .=" netmask $PVE::Network::ipv4_reverse_mask->[$net->{netmask}]\n";
- $content .=" gateway $net->{gateway}\n" if $net->{gateway};
- } else {
- $content .="iface $opt inet dhcp\n";
+ if ($net->{address} eq 'dhcp') {
+ $content .= "iface $id inet dhcp\n";
+ } else {
+ $content .= "iface $id inet static\n";
+ $content .= " address $net->{address}\n";
+ $content .= " netmask $PVE::Network::ipv4_reverse_mask->[$net->{netmask}]\n";
+ $content .= " gateway $net->{gateway}\n" if $net->{gateway};
+ }
+ }
+ if ($net->{address6}) {
+ if ($net->{address6} =~ /^(auto|dhcp)$/) {
+ $content .= "iface $id inet6 $1\n";
+ } else {
+ $content .= "iface $id inet6 static\n";
+ $content .= " address $net->{address6}\n";
+ $content .= " netmask $net->{netmask6}\n";
+ $content .= " gateway $net->{gateway6}\n" if $net->{gateway6};
+ }
}
}
@@ -6650,7 +6737,7 @@ sub generate_cloudinit_network {
my $fn = "$path/drive/openstack/content/0000";
file_write($fn, $content);
-
+ return $content;
}
1;
--
2.11.0
More information about the pve-devel
mailing list