[pve-devel] [PATCH 1/2] implement cloudinit v2
Wolfgang Bumiller
w.bumiller at proxmox.com
Wed Jun 17 13:02:23 CEST 2015
From: Alexandre Derumier <aderumier at odiso.com>
Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
PVE/QemuServer.pm | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
control.in | 2 +-
2 files changed, 179 insertions(+), 4 deletions(-)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index ab9ac74..7956c50 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -18,17 +18,19 @@ 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);
use PVE::Storage;
-use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach);
+use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach $IPV6RE $IPV4RE);
use PVE::JSONSchema qw(get_standard_option);
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::INotify;
use PVE::ProcFSTools;
use PVE::QMPClient;
use PVE::RPCEnvironment;
+
use Time::HiRes qw(gettimeofday);
my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
@@ -384,6 +386,28 @@ EODESCR
maxLength => 256,
optional => 1,
},
+ searchdomain => {
+ optional => 1,
+ type => 'string',
+ description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
+ },
+ nameserver => {
+ optional => 1,
+ type => 'string',
+ description => "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver.",
+ },
+ sshkey => {
+ optional => 1,
+ type => 'string',
+ description => "Ssh keys for root",
+ },
+ cloudinit => {
+ optional => 1,
+ type => 'boolean',
+ description => "Enable cloudinit config generation.",
+ default => 0,
+ },
+
};
# what about other qemu settings ?
@@ -712,6 +736,8 @@ sub get_iso_path {
return get_cdrom_path();
} elsif ($cdrom eq 'none') {
return '';
+ } elsif ($cdrom eq 'cloudinit') {
+ return "/tmp/cloudinit/$vmid/configdrive.iso";
} elsif ($cdrom =~ m|^/|) {
return $cdrom;
} else {
@@ -723,7 +749,7 @@ sub get_iso_path {
sub filename_to_volume_id {
my ($vmid, $file, $media) = @_;
- if (!($file eq 'none' || $file eq 'cdrom' ||
+ if (!($file eq 'none' || $file eq 'cdrom' || $file eq 'cloudinit' ||
$file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
return undef if $file =~ m|/|;
@@ -1356,6 +1382,11 @@ sub parse_net {
$res->{firewall} = $1;
} elsif ($kvp =~ m/^link_down=([01])$/) {
$res->{link_down} = $1;
+ } elsif ($kvp =~ m/^cidr=($IPV6RE|$IPV4RE)\/(\d+)$/) {
+ $res->{address} = $1;
+ $res->{netmask} = $2;
+ } elsif ($kvp =~ m/^gateway=($IPV6RE|$IPV4RE)$/) {
+ $res->{gateway} = $1;
} else {
return undef;
}
@@ -4143,12 +4174,14 @@ sub vm_start {
check_lock($conf) if !$skiplock;
die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
-
+
if (!$statefile && scalar(keys %{$conf->{pending}})) {
vmconfig_apply_pending($vmid, $conf, $storecfg);
$conf = load_config($vmid); # update/reload
}
+ generate_cloudinitconfig($conf, $vmid);
+
my $defaults = load_defaults();
# set environment variable useful inside network script
@@ -6251,4 +6284,146 @@ sub scsihw_infos {
return ($maxdev, $controller, $controller_prefix);
}
+sub generate_cloudinitconfig {
+ my ($conf, $vmid) = @_;
+
+ return if !$conf->{cloudinit};
+
+ my $path = "/tmp/cloudinit/$vmid";
+
+ mkdir "/tmp/cloudinit";
+ mkdir $path;
+ mkdir "$path/drive";
+ 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 $cmd = [];
+ push @$cmd, 'genisoimage';
+ push @$cmd, '-R';
+ push @$cmd, '-V', 'config-2';
+ push @$cmd, '-o', "$path/configdrive.iso";
+ push @$cmd, "$path/drive";
+
+ 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_userdata {
+ my ($conf, $path) = @_;
+
+ my $content = "#cloud-config\n";
+ my $hostname = $conf->{searchdomain} ? $conf->{name}.".".$conf->{searchdomain} : $conf->{name};
+ $content .= "fqdn: $hostname\n";
+ $content .= "manage_etc_hosts: true\n";
+
+ if ($conf->{sshkey}) {
+ $content .= "users:\n";
+ $content .= " - default\n";
+ $content .= " - name: root\n";
+ $content .= " ssh-authorized-keys:\n";
+ $content .= " - $conf->{sshkey}\n";
+ }
+
+ $content .= "package_upgrade: true\n";
+
+ my $fn = "$path/drive/openstack/latest/user_data";
+ file_write($fn, $content);
+
+}
+
+sub generate_cloudinit_metadata {
+ my ($conf, $path) = @_;
+
+ my ($uuid, $uuid_str);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuid_str);
+
+ my $content = "{\n";
+ $content .= " \"uuid\": \"$uuid_str\",\n";
+ $content .= " \"network_config\" :{ \"content_path\": \"/content/0000\"}\n";
+ $content .= "}\n";
+
+ my $fn = "$path/drive/openstack/latest/meta_data.json";
+
+ return file_write($fn, $content);
+
+
+}
+
+my $ipv4_reverse_mask = [
+ '0.0.0.0',
+ '128.0.0.0',
+ '192.0.0.0',
+ '224.0.0.0',
+ '240.0.0.0',
+ '248.0.0.0',
+ '252.0.0.0',
+ '254.0.0.0',
+ '255.0.0.0',
+ '255.128.0.0',
+ '255.192.0.0',
+ '255.224.0.0',
+ '255.240.0.0',
+ '255.248.0.0',
+ '255.252.0.0',
+ '255.254.0.0',
+ '255.255.0.0',
+ '255.255.128.0',
+ '255.255.192.0',
+ '255.255.224.0',
+ '255.255.240.0',
+ '255.255.248.0',
+ '255.255.252.0',
+ '255.255.254.0',
+ '255.255.255.0',
+ '255.255.255.128',
+ '255.255.255.192',
+ '255.255.255.224',
+ '255.255.255.240',
+ '255.255.255.248',
+ '255.255.255.252',
+ '255.255.255.254',
+ '255.255.255.255',
+];
+
+sub generate_cloudinit_network {
+ my ($conf, $path) = @_;
+
+ 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/;
+
+ $content .="auto $opt\n";
+ if ($net->{address}) {
+ $content .="iface $opt inet static\n";
+ $content .=" address $net->{address}\n";
+ $content .=" netmask $ipv4_reverse_mask->[$net->{netmask}]\n";
+ $content .=" gateway $net->{gateway}\n" if $net->{gateway};
+ } else {
+ $content .="iface $opt inet dhcp\n";
+ }
+ }
+
+ $content .=" dns-nameservers $conf->{nameserver}\n" if $conf->{nameserver};
+ $content .=" dns-search $conf->{searchdomain}\n" if $conf->{searchdomain};
+
+ my $fn = "$path/drive/openstack/content/0000";
+ file_write($fn, $content);
+
+}
+
+
+
1;
diff --git a/control.in b/control.in
index 84841ce..a6f6be1 100644
--- a/control.in
+++ b/control.in
@@ -3,7 +3,7 @@ Version: @@VERSION@@-@@PKGRELEASE@@
Section: admin
Priority: optional
Architecture: @@ARCH@@
-Depends: libc6 (>= 2.7-18), perl (>= 5.10.0-19), libterm-readline-gnu-perl, pve-qemu-kvm (>= 2.2-1), netcat6, libpve-access-control, libpve-storage-perl, pve-cluster, libjson-perl, libjson-xs-perl, libio-multiplex-perl, libnet-ssleay-perl, socat, pve-firewall, libuuid-perl, pve-ha-manager
+Depends: libc6 (>= 2.7-18), perl (>= 5.10.0-19), libterm-readline-gnu-perl, pve-qemu-kvm (>= 2.2-1), netcat6, libpve-access-control, libpve-storage-perl, pve-cluster, libjson-perl, libjson-xs-perl, libio-multiplex-perl, libnet-ssleay-perl, socat, pve-firewall, libuuid-perl, pve-ha-manager, genisoimage
Maintainer: Proxmox Support Team <support at proxmox.com>
Description: Qemu Server Tools
This package contains the Qemu Server tools used by Proxmox VE
--
2.1.4
More information about the pve-devel
mailing list