my $storecfg = PVE::Storage::config();
my $iso_path = PVE::Storage::path($storecfg, $drive->{file});
my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
- my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
- $plugin->activate_volume($storeid, $scfg, $volname);
my $format = PVE::QemuServer::qemu_img_format($scfg, $volname);
- my $size = PVE::Storage::file_size_info($iso_path);
+ my $size = eval { PVE::Storage::file_size_info($iso_path) };
+ if ($size <= 0) {
+ $volname =~ m/(vm-$vmid-cloudinit(.\Q$format\E)?)/;
+ my $name = $1;
+ $size = 4 * 1024;
+ PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, $name, $size);
+ $size *= 1024; # vdisk alloc takes KB, qemu-img dd's osize takes byte
+ }
+
+ my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+ $plugin->activate_volume($storeid, $scfg, $volname);
eval {
run_command([['genisoimage', '-R', '-V', $label, $path],
} else {
my ($addr, $mask) = split_ip4($net->{ip});
$content .= "iface $id inet static\n";
- $content .= " address '$addr'\n";
- $content .= " netmask '$mask'\n";
- $content .= " gateway '$net->{gw}'\n" if $net->{gw};
+ $content .= " address $addr\n";
+ $content .= " netmask $mask\n";
+ $content .= " gateway $net->{gw}\n" if $net->{gw};
}
}
if ($net->{ip6}) {
} 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 .= " address $addr\n";
+ $content .= " netmask $mask\n";
+ $content .= " gateway $net->{gw6}\n" if $net->{gw6};
}
}
}
sub generate_configdrive2 {
my ($conf, $vmid, $drive, $volname, $storeid) = @_;
- my $user_data = cloudinit_userdata($conf, $vmid);
- my $network_data = configdrive2_network($conf);
-
- my $digest_data = $user_data . $network_data;
- my $uuid_str = Digest::SHA::sha1_hex($digest_data);
+ my ($user_data, $network_data, $meta_data) = get_custom_cloudinit_files($conf);
+ $user_data = cloudinit_userdata($conf, $vmid) if !defined($user_data);
+ $network_data = configdrive2_network($conf) if !defined($network_data);
- my $meta_data = configdrive2_metadata($uuid_str);
+ if (!defined($meta_data)) {
+ my $digest_data = $user_data . $network_data;
+ my $uuid_str = Digest::SHA::sha1_hex($digest_data);
+ $meta_data = configdrive2_metadata($uuid_str);
+ }
my $files = {
'/openstack/latest/user_data' => $user_data,
'/openstack/content/0000' => $network_data,
sub generate_nocloud {
my ($conf, $vmid, $drive, $volname, $storeid) = @_;
- my $user_data = cloudinit_userdata($conf, $vmid);
- my $network_data = nocloud_network($conf);
+ my ($user_data, $network_data, $meta_data) = get_custom_cloudinit_files($conf);
+ $user_data = cloudinit_userdata($conf, $vmid) if !defined($user_data);
+ $network_data = nocloud_network($conf) if !defined($network_data);
- my $digest_data = $user_data . $network_data;
- my $uuid_str = Digest::SHA::sha1_hex($digest_data);
+ if (!defined($meta_data)) {
+ my $digest_data = $user_data . $network_data;
+ my $uuid_str = Digest::SHA::sha1_hex($digest_data);
- my $meta_data = nocloud_metadata($uuid_str);
+ $meta_data = nocloud_metadata($uuid_str);
+ }
my $files = {
'/user-data' => $user_data,
commit_cloudinit_disk($conf, $vmid, $drive, $volname, $storeid, $files, 'cidata');
}
+sub get_custom_cloudinit_files {
+ my ($conf) = @_;
+
+ my $cicustom = $conf->{cicustom};
+ my $files = $cicustom ? PVE::JSONSchema::parse_property_string('pve-qm-cicustom', $cicustom) : {};
+
+ my $network_volid = $files->{network};
+ my $user_volid = $files->{user};
+ my $meta_volid = $files->{meta};
+
+ my $storage_conf = PVE::Storage::config();
+
+ my $network_data;
+ if ($network_volid) {
+ $network_data = read_cloudinit_snippets_file($storage_conf, $network_volid);
+ }
+
+ my $user_data;
+ if ($user_volid) {
+ $user_data = read_cloudinit_snippets_file($storage_conf, $user_volid);
+ }
+
+ my $meta_data;
+ if ($meta_volid) {
+ $meta_data = read_cloudinit_snippets_file($storage_conf, $meta_volid);
+ }
+
+ return ($user_data, $network_data, $meta_data);
+}
+
+sub read_cloudinit_snippets_file {
+ my ($storage_conf, $volid) = @_;
+
+ my ($full_path, undef, $type) = PVE::Storage::path($storage_conf, $volid);
+ die "$volid is not in the snippets directory\n" if $type ne 'snippets';
+ return PVE::Tools::file_get_contents($full_path, 1 * 1024 * 1024);
+}
+
my $cloudinit_methods = {
configdrive2 => \&generate_configdrive2,
nocloud => \&generate_nocloud,