qemu-server/PVE/QemuServer/Cloudinit.pm
Wolfgang Bumiller 3db6e4ab70 cloud-init: use qemu-img dd instead of nbd
We now have a patch on top of qemu to allow 'qemu-img dd'
to read from stdin when specifying input and output sizes,
as well as a way to tell it that the size of the source is
not known.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2018-03-07 09:11:31 +01:00

159 lines
4.3 KiB
Perl

package PVE::QemuServer::Cloudinit;
use strict;
use warnings;
use File::Path;
use Digest::SHA;
use URI::Escape;
use PVE::Tools qw(run_command file_set_contents);
use PVE::Storage;
use PVE::QemuServer;
sub commit_cloudinit_disk {
my ($file_path, $iso_path, $format) = @_;
my $size = PVE::Storage::file_size_info($iso_path);
run_command([['genisoimage', '-R', '-V', 'config-2', $file_path],
['qemu-img', 'dd', '-f', 'raw', '-O', $format,
'isize=0', "osize=$size", "of=$iso_path"]]);
}
sub generate_cloudinitconfig {
my ($conf, $vmid) = @_;
PVE::QemuServer::foreach_drive($conf, sub {
my ($ds, $drive) = @_;
my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
return if !$volname || $volname !~ m/vm-$vmid-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";
my $digest_data = generate_cloudinit_userdata($conf, $path)
. generate_cloudinit_network($conf, $path);
generate_cloudinit_metadata($conf, $path, $digest_data);
my $storecfg = PVE::Storage::config();
my $iso_path = PVE::Storage::path($storecfg, $drive->{file});
my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
my $format = PVE::QemuServer::qemu_img_format($scfg, $volname);
#fixme : add meta as drive property to compare
commit_cloudinit_disk("$path/drive", $iso_path, $format);
rmtree("$path/drive");
});
}
sub generate_cloudinit_userdata {
my ($conf, $path) = @_;
my $content = "#cloud-config\n";
my $hostname = $conf->{hostname};
if (!defined($hostname)) {
$hostname = $conf->{name};
if (my $search = $conf->{searchdomain}) {
$hostname .= ".$search";
}
}
$content .= "fqdn: $hostname\n";
$content .= "manage_etc_hosts: true\n";
$content .= "bootcmd: \n";
$content .= " - ifdown -a\n";
$content .= " - ifup -a\n";
my $keys = $conf->{sshkeys};
if ($keys) {
$keys = URI::Escape::uri_unescape($keys);
$keys = [map { chomp $_; $_ } split(/\n/, $keys)];
$keys = [grep { /\S/ } @$keys];
$content .= "users:\n";
$content .= " - default\n";
$content .= " - name: root\n";
$content .= " ssh-authorized-keys:\n";
foreach my $k (@$keys) {
$content .= " - $k\n";
}
}
$content .= "package_upgrade: true\n";
my $fn = "$path/drive/openstack/latest/user_data";
file_set_contents($fn, $content);
return $content;
}
sub generate_cloudinit_metadata {
my ($conf, $path, $digest_data) = @_;
my $uuid_str = Digest::SHA::sha1_hex($digest_data);
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";
file_set_contents($fn, $content);
}
sub generate_cloudinit_network {
my ($conf, $path) = @_;
my $content = "auto lo\n";
$content .="iface lo inet loopback\n\n";
my @ifaces = grep(/^net(\d+)$/, keys %$conf);
foreach my $iface (@ifaces) {
(my $id = $iface) =~ s/^net//;
next if !$conf->{"ipconfig$id"};
my $net = PVE::QemuServer::parse_ipconfig($conf->{"ipconfig$id"});
$id = "eth$id";
$content .="auto $id\n";
if ($net->{ip}) {
if ($net->{ip} eq 'dhcp') {
$content .= "iface $id inet dhcp\n";
} else {
my ($addr, $mask) = split('/', $net->{ip});
$content .= "iface $id inet static\n";
$content .= " address $addr\n";
$content .= " netmask $PVE::Network::ipv4_reverse_mask->[$mask]\n";
$content .= " gateway $net->{gw}\n" if $net->{gw};
}
}
if ($net->{ip6}) {
if ($net->{ip6} =~ /^(auto|dhcp)$/) {
$content .= "iface $id inet6 $1\n";
} else {
my ($addr, $mask) = split('/', $net->{ip6});
$content .= "iface $id inet6 static\n";
$content .= " address $addr\n";
$content .= " netmask $mask\n";
$content .= " gateway $net->{gw6}\n" if $net->{gw6};
}
}
}
$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_set_contents($fn, $content);
return $content;
}
1;