use Digest::SHA;
use URI::Escape;
use MIME::Base64 qw(encode_base64);
+use Storable qw(dclone);
use PVE::Tools qw(run_command file_set_contents);
use PVE::Storage;
use PVE::QemuServer;
+use PVE::QemuServer::Helpers;
use constant CLOUDINIT_DISK_SIZE => 4 * 1024 * 1024; # 4MiB in bytes
# the new predicatble network device naming scheme.
if (defined(my $ostype = $conf->{ostype})) {
return 'configdrive2'
- if PVE::QemuServer::windows_version($ostype);
+ if PVE::QemuServer::Helpers::windows_version($ostype);
}
return 'nocloud';
$hostname =~ s/\..*$//;
} elsif (my $search = $conf->{searchdomain}) {
$fqdn = "$hostname.$search";
+ } else {
+ $fqdn = $hostname;
}
return ($hostname, $fqdn);
}
$content .= "hostname: $hostname\n";
$content .= "manage_etc_hosts: true\n";
- $content .= "fqdn: $fqdn\n" if defined($fqdn);
+ $content .= "fqdn: $fqdn\n";
my $username = $conf->{ciuser};
my $password = $conf->{cipassword};
$content .= " - default\n";
}
- $content .= "package_upgrade: true\n";
+ $content .= "package_upgrade: true\n" if !defined($conf->{ciupgrade}) || $conf->{ciupgrade};
return $content;
}
sub generate_configdrive2 {
my ($conf, $vmid, $drive, $volname, $storeid) = @_;
- my ($user_data, $network_data, $meta_data) = get_custom_cloudinit_files($conf);
+ my ($user_data, $network_data, $meta_data, $vendor_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);
+ $vendor_data = '' if !defined($vendor_data);
if (!defined($meta_data)) {
$meta_data = configdrive2_gen_metadata($user_data, $network_data);
}
+
+ # we always allocate a 4MiB disk for cloudinit and with the overhead of the ISO
+ # make sure we always stay below it by keeping the sum of all files below 3 MiB
+ my $sum = length($user_data) + length($network_data) + length($meta_data) + length($vendor_data);
+ die "Cloud-Init sum of snippets too big (> 3 MiB)\n" if $sum > (3 * 1024 * 1024);
+
my $files = {
'/openstack/latest/user_data' => $user_data,
'/openstack/content/0000' => $network_data,
- '/openstack/latest/meta_data.json' => $meta_data
+ '/openstack/latest/meta_data.json' => $meta_data,
+ '/openstack/latest/vendor_data.json' => $vendor_data
};
commit_cloudinit_disk($conf, $vmid, $drive, $volname, $storeid, $files, 'config-2');
}
sub generate_nocloud {
my ($conf, $vmid, $drive, $volname, $storeid) = @_;
- my ($user_data, $network_data, $meta_data) = get_custom_cloudinit_files($conf);
+ my ($user_data, $network_data, $meta_data, $vendor_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);
+ $vendor_data = '' if !defined($vendor_data);
if (!defined($meta_data)) {
$meta_data = nocloud_gen_metadata($user_data, $network_data);
}
+ # we always allocate a 4MiB disk for cloudinit and with the overhead of the ISO
+ # make sure we always stay below it by keeping the sum of all files below 3 MiB
+ my $sum = length($user_data) + length($network_data) + length($meta_data) + length($vendor_data);
+ die "Cloud-Init sum of snippets too big (> 3 MiB)\n" if $sum > (3 * 1024 * 1024);
+
my $files = {
'/user-data' => $user_data,
'/network-config' => $network_data,
- '/meta-data' => $meta_data
+ '/meta-data' => $meta_data,
+ '/vendor-data' => $vendor_data
};
commit_cloudinit_disk($conf, $vmid, $drive, $volname, $storeid, $files, 'cidata');
}
my $network_volid = $files->{network};
my $user_volid = $files->{user};
my $meta_volid = $files->{meta};
+ my $vendor_volid = $files->{vendor};
my $storage_conf = PVE::Storage::config();
$meta_data = read_cloudinit_snippets_file($storage_conf, $meta_volid);
}
- return ($user_data, $network_data, $meta_data);
+ my $vendor_data;
+ if ($vendor_volid) {
+ $vendor_data = read_cloudinit_snippets_file($storage_conf, $vendor_volid);
+ }
+
+ return ($user_data, $network_data, $meta_data, $vendor_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';
+ my ($vtype, undef) = PVE::Storage::parse_volname($storage_conf, $volid);
+
+ die "$volid is not in the snippets directory\n" if $vtype ne 'snippets';
+
+ my $full_path = PVE::Storage::abs_filesystem_path($storage_conf, $volid, 1);
return PVE::Tools::file_get_contents($full_path, 1 * 1024 * 1024);
}
opennebula => \&generate_opennebula,
};
-sub generate_cloudinitconfig {
+sub has_changes {
+ my ($conf) = @_;
+
+ return !!$conf->{cloudinit}->%*;
+}
+
+sub generate_cloudinit_config {
my ($conf, $vmid) = @_;
my $format = get_cloudinit_format($conf);
+ my $has_changes = has_changes($conf);
+
PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
$generator->($conf, $vmid, $drive, $volname, $storeid);
});
+
+ return $has_changes;
+}
+
+sub apply_cloudinit_config {
+ my ($conf, $vmid) = @_;
+
+ my $has_changes = generate_cloudinit_config($conf, $vmid);
+
+ if ($has_changes) {
+ delete $conf->{cloudinit};
+ PVE::QemuConfig->write_config($vmid, $conf);
+ return 1;
+ }
+
+ return $has_changes;
}
sub dump_cloudinit_config {