On 2/13/25 12:01, Fiona Ebner wrote: > Am 10.02.25 um 13:07 schrieb Daniel Herzig: >> From: Leo Nunner <l.nun...@proxmox.com> >> >> The code to generate the actual configuration works pretty much the same >> as with the VM system. We generate an instance ID by hashing the user >> configuration, causing cloud-init to run every time said configuration >> changes. >> >> Instead of creating a config drive, we write files directly into the >> volume of the container. We create a folder at >> '/var/lib/cloud/seed/nocloud-net' and write the files 'user-data', >> 'vendor-data' and 'meta-data'. Cloud-init looks at the instance ID >> inside 'meta-data' to decide whether it should run (again) or not. >> >> Custom scripts need to be located inside the snippets directory, and >> overwrite the default generated configuration file. >> >> Signed-off-by: Leo Nunner <l.nun...@proxmox.com> >> --- >> src/PVE/LXC.pm | 1 + >> src/PVE/LXC/Cloudinit.pm | 114 ++++++++++++++++++++++++++++++++++++++ >> src/PVE/LXC/Makefile | 1 + >> src/lxc-pve-prestart-hook | 5 ++ >> 4 files changed, 121 insertions(+) >> create mode 100644 src/PVE/LXC/Cloudinit.pm >> >> diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm >> index 4d20645..35bb6b5 100644 >> --- a/src/PVE/LXC.pm >> +++ b/src/PVE/LXC.pm >> @@ -40,6 +40,7 @@ use PVE::Tools qw( >> use PVE::Syscall qw(:fsmount); >> >> use PVE::LXC::CGroup; >> +use PVE::LXC::Cloudinit; >> use PVE::LXC::Config; >> use PVE::LXC::Monitor; >> use PVE::LXC::Tools; > > Hmm, seems like this import is unused. Can you double check? > >> diff --git a/src/PVE/LXC/Cloudinit.pm b/src/PVE/LXC/Cloudinit.pm >> new file mode 100644 >> index 0000000..3e8617b >> --- /dev/null >> +++ b/src/PVE/LXC/Cloudinit.pm >> @@ -0,0 +1,114 @@ >> +package PVE::LXC::Cloudinit; >> + >> +use strict; >> +use warnings; >> + >> +use Digest::SHA; >> +use File::Path; > > Missing includes: > > use URI::Escape; > > And we also need a dependency on liburi-perl in debian/control ;) > > use PVE::JSONSchema; > >> +use PVE::LXC; > > use PVE::Storage; > use PVE::Tools; > >> + >> +sub gen_cloudinit_metadata { >> + my ($user) = @_; >> + >> + my $uuid_str = Digest::SHA::sha1_hex($user); > > Hmm, shouldn't this also depend on the vendor data? Otherwise, if only > the vendor data changes, then it will still have the same instance ID. > > Seems like for VMs, we only use user and network data here. > > @Mira do you know more by chance? I don't think vendor-data should be part of the instance-id. It's used to create a first configuration that a user can override via the user config. The vendor-data won't be used again once it's already configured. I'm not a 100% sure, but changing the instance-id leads to rerunning lots of modules (e.g. User, Network and others), but the vendor-data parts do not.
Only a complete `cloud-init clean` should trigger the modules using vendor-data to run again. https://cloudinit.readthedocs.io/en/latest/explanation/vendordata.html#vendor-data >> + return cloudinit_metadata($uuid_str); >> +} >> + >> +sub cloudinit_metadata { >> + my ($uuid) = @_; >> + my $raw = ""; >> + >> + $raw .= "instance-id: $uuid\n"; >> + >> + return $raw; >> +} >> + >> +sub cloudinit_userdata { >> + my ($conf) = @_; >> + >> + my $content = "#cloud-config\n"; >> + >> + my $username = $conf->{ciuser}; >> + my $password = $conf->{cipassword}; >> + >> + $content .= "user: $username\n" if defined($username); >> + $content .= "password: $password\n" if defined($password); >> + >> + if (defined(my $keys = $conf->{sshkeys})) { >> + $keys = URI::Escape::uri_unescape($keys); >> + $keys = [map { my $key = $_; chomp $key; $key } split(/\n/, $keys)]; >> + $keys = [grep { /\S/ } @$keys]; >> + $content .= "ssh_authorized_keys:\n"; >> + foreach my $k (@$keys) { >> + $content .= " - $k\n"; >> + } >> + } >> + $content .= "chpasswd:\n"; >> + $content .= " expire: False\n"; >> + >> + if (!defined($username) || $username ne 'root') { >> + $content .= "users:\n"; >> + $content .= " - default\n"; >> + } >> + >> + $content .= "package_upgrade: true\n" if $conf->{ciupgrade}; > > For VMs, we default to true here. I'd like to keep it consistent. > >> + >> + return $content; >> +} >> + >> +sub read_cloudinit_snippets_file { >> + my ($storage_conf, $volid) = @_; >> + >> + my ($full_path, undef, $type) = PVE::Storage::path($storage_conf, >> $volid); > > The qemu-server implementation does things a bit differently here using > parse_volname() and abs_filesystem_path(). The latter makes sure to > activate the storage/volume, which is desirable. I'd either add a call > to activate the volume here too, or align the helpers. > >> + die "$volid is not in the snippets directory\n" if $type ne 'snippets'; >> + return PVE::Tools::file_get_contents($full_path, 1 * 1024 * 1024); >> +} >> + > > ---snip 8<--- > >> diff --git a/src/lxc-pve-prestart-hook b/src/lxc-pve-prestart-hook >> index fdaead2..c9f8ff0 100755 >> --- a/src/lxc-pve-prestart-hook >> +++ b/src/lxc-pve-prestart-hook >> @@ -13,6 +13,7 @@ use POSIX; >> use PVE::CGroup; >> use PVE::Cluster; >> use PVE::LXC::Config; >> +use PVE::LXC::Cloudinit; > > Nit: not ordered alphabetically > >> use PVE::LXC::Setup; >> use PVE::LXC::Tools; >> use PVE::LXC; _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel