[pve-devel] [PATCH v10 qemu-server/cloudinit 1/8] implement cloudinit v2

Wolfgang Bumiller w.bumiller at proxmox.com
Tue Aug 11 15:51:48 CEST 2015


From: Alexandre Derumier <aderumier at odiso.com>

Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 PVE/QemuServer.pm | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 control.in        |   2 +-
 2 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 84fc712..1bf480f 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;
 	}
@@ -4212,12 +4243,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
@@ -6316,4 +6349,110 @@ 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);
+
+
+}
+
+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 $PVE::Network::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