public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC
@ 2023-05-11 11:12 Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 1/3] cloudinit: introduce config parameters Leo Nunner
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

This series introduces basic cloudinit support for containers. All in
all, it works quite similar to VMs, with the caveat that we only allow
network configuration through the alrady existing systems, and not via
cloud-init.

These patches should still be seen as WIP, but they are in a workable
state and I'd like some feedback on how I currently handle things. Are
there any other parameters/features that are needed here? Is the current
mechanism for providing the configuration to the container optimal, or
is there a better way?

container:

Leo Nunner (3):
  cloudinit: introduce config parameters
  cloudinit: basic implementation
  cloudinit: add dump command to pct

 src/PVE/API2/LXC.pm        |  36 +++++++++++
 src/PVE/API2/LXC/Config.pm |   7 ++-
 src/PVE/CLI/pct.pm         |   4 ++
 src/PVE/LXC.pm             |   1 +
 src/PVE/LXC/Cloudinit.pm   | 125 +++++++++++++++++++++++++++++++++++++
 src/PVE/LXC/Config.pm      |  61 ++++++++++++++++++
 src/PVE/LXC/Makefile       |   1 +
 src/lxc-pve-prestart-hook  |   5 ++
 8 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/LXC/Cloudinit.pm

manager:

Leo Nunner (2):
  cloudinit: rename qemu cloudinit panel
  cloudinit: introduce panel for LXCs

 www/manager6/Makefile          |   1 +
 www/manager6/lxc/CloudInit.js  | 219 +++++++++++++++++++++++++++++++++
 www/manager6/lxc/Config.js     |   6 +
 www/manager6/qemu/CloudInit.js |   4 +-
 www/manager6/qemu/Config.js    |   2 +-
 5 files changed, 229 insertions(+), 3 deletions(-)
 create mode 100644 www/manager6/lxc/CloudInit.js

-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH RFC container 1/3] cloudinit: introduce config parameters
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
@ 2023-05-11 11:12 ` Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 2/3] cloudinit: basic implementation Leo Nunner
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

Introduce configuration parameters for cloud-init. Like with VMs, it's
possible to specify:
    - user
    - password
    - ssh keys
    - enable/disable updates on first boot

It's also possible to pass through custom config files for the user and
vendor settings. We don't allow configuring the network through
cloud-init, since it will clash with whatever configuration we already
did for the container.

Signed-off-by: Leo Nunner <l.nunner@proxmox.com>
---
 src/PVE/API2/LXC.pm        |  3 ++
 src/PVE/API2/LXC/Config.pm |  7 ++++-
 src/PVE/LXC/Config.pm      | 61 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index 50c9eaf..e585509 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -2492,6 +2492,9 @@ __PACKAGE__->register_method({
 
 	my $pending_delete_hash = PVE::LXC::Config->parse_pending_delete($conf->{pending}->{delete});
 
+	$conf->{cipassword} = '**********' if defined($conf->{cipassword});
+	$conf->{pending}->{cipassword} = '********** ' if defined($conf->{pending}->{cipassword});
+
 	return PVE::GuestHelpers::config_with_pending_array($conf, $pending_delete_hash);
     }});
 
diff --git a/src/PVE/API2/LXC/Config.pm b/src/PVE/API2/LXC/Config.pm
index e6c0980..0ff4115 100644
--- a/src/PVE/API2/LXC/Config.pm
+++ b/src/PVE/API2/LXC/Config.pm
@@ -79,7 +79,7 @@ __PACKAGE__->register_method({
 	} else {
 	    $conf = PVE::LXC::Config->load_current_config($param->{vmid}, $param->{current});
 	}
-
+	$conf->{cipassword} = '**********' if $conf->{cipassword};
 	return $conf;
     }});
 
@@ -148,6 +148,11 @@ __PACKAGE__->register_method({
 	$param->{cpuunits} = PVE::CGroup::clamp_cpu_shares($param->{cpuunits})
 	    if defined($param->{cpuunits}); # clamp value depending on cgroup version
 
+	if (defined(my $cipassword = $param->{cipassword})) {
+	    $param->{cipassword} = PVE::Tools::encrypt_pw($cipassword)
+		if $cipassword !~ /^\$(?:[156]|2[ay])(\$.+){2}/;
+	}
+
 	my $code = sub {
 
 	    my $conf = PVE::LXC::Config->load_config($vmid);
diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index ac9db94..8aeb03b 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -442,6 +442,63 @@ my $features_desc = {
     },
 };
 
+my $cicustom_fmt = {
+    user => {
+	type => 'string',
+	optional => 1,
+	description => 'To pass a custom file containing all user data to the container via cloud-init.',
+	format => 'pve-volume-id',
+	format_description => 'volume',
+    },
+    vendor => {
+	type => 'string',
+	optional => 1,
+	description => 'To pass a custom file containing all vendor data to the container via cloud-init.',
+	format => 'pve-volume-id',
+	format_description => 'volume',
+    },
+};
+PVE::JSONSchema::register_format('pve-pct-cicustom', $cicustom_fmt);
+
+my $confdesc_cloudinit = {
+    cienable => {
+	optional => 1,
+	type => 'boolean',
+	description => "cloud-init: provide cloud-init configuration to container.",
+    },
+    ciuser => {
+	optional => 1,
+	type => 'string',
+	description => "cloud-init: User name to change ssh keys and password for instead of the"
+	    ." image's configured default user.",
+    },
+    cipassword => {
+	optional => 1,
+	type => 'string',
+	description => 'cloud-init: Password to assign the user. Using this is generally not'
+	    .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
+	    .' support hashed passwords.',
+    },
+    ciupdate => {
+	optional => 1,
+	type => 'boolean',
+	description => 'cloud-init: do an automatic package update on boot.'
+    },
+    cicustom => {
+	optional => 1,
+	type => 'string',
+	description => 'cloud-init: Specify custom files to replace the automatically generated'
+	    .' ones at start.',
+	format => 'pve-pct-cicustom',
+    },
+    sshkeys => {
+	optional => 1,
+	type => 'string',
+	format => 'urlencoded',
+	description => "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
+    },
+};
+
 my $confdesc = {
     lock => {
 	optional => 1,
@@ -614,6 +671,10 @@ my $confdesc = {
     },
 };
 
+foreach my $key (keys %$confdesc_cloudinit) {
+    $confdesc->{$key} = $confdesc_cloudinit->{$key};
+}
+
 my $valid_lxc_conf_keys = {
     'lxc.apparmor.profile' => 1,
     'lxc.apparmor.allow_incomplete' => 1,
-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH RFC container 2/3] cloudinit: basic implementation
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 1/3] cloudinit: introduce config parameters Leo Nunner
@ 2023-05-11 11:12 ` Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 3/3] cloudinit: add dump command to pct Leo Nunner
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

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.nunner@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 d138161..ea01fbb 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -39,6 +39,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;
diff --git a/src/PVE/LXC/Cloudinit.pm b/src/PVE/LXC/Cloudinit.pm
new file mode 100644
index 0000000..e4bc67d
--- /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;
+
+use PVE::LXC;
+
+sub gen_cloudinit_metadata {
+    my ($user) = @_;
+
+    my $uuid_str = Digest::SHA::sha1_hex($user);
+    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->{ciupdate};
+
+    return $content;
+}
+
+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);
+}
+
+sub read_custom_cloudinit_files {
+    my ($conf) = @_;
+
+    my $cloudinit_conf = $conf->{cicustom};
+    my $files = $cloudinit_conf ? PVE::JSONSchema::parse_property_string('pve-pct-cicustom', $cloudinit_conf) : {};
+
+    my $user_volid = $files->{user};
+    my $vendor_volid = $files->{vendor};
+
+    my $storage_conf = PVE::Storage::config();
+
+    my $user_data;
+    if ($user_volid) {
+	$user_data = read_cloudinit_snippets_file($storage_conf, $user_volid);
+    }
+
+    my $vendor_data;
+    if ($vendor_volid) {
+	$user_data = read_cloudinit_snippets_file($storage_conf, $vendor_volid);
+    }
+
+    return ($user_data, $vendor_data);
+}
+
+sub create_cloudinit_files {
+    my ($conf, $setup) = @_;
+
+    my $cloudinit_dir = "/var/lib/cloud/seed/nocloud-net";
+
+    my ($user_data, $vendor_data) = read_custom_cloudinit_files($conf);
+    $user_data = cloudinit_userdata($conf) if !defined($user_data);
+    $vendor_data = '' if !defined($vendor_data);
+
+    my $meta_data = gen_cloudinit_metadata($user_data);
+
+    $setup->protected_call(sub {
+	my $plugin = $setup->{plugin};
+
+	$plugin->ct_make_path($cloudinit_dir);
+
+	$plugin->ct_file_set_contents("$cloudinit_dir/user-data", $user_data);
+	$plugin->ct_file_set_contents("$cloudinit_dir/vendor-data", $vendor_data);
+	$plugin->ct_file_set_contents("$cloudinit_dir/meta-data", $meta_data);
+    });
+}
+
+1;
diff --git a/src/PVE/LXC/Makefile b/src/PVE/LXC/Makefile
index a190260..5d595ba 100644
--- a/src/PVE/LXC/Makefile
+++ b/src/PVE/LXC/Makefile
@@ -1,5 +1,6 @@
 SOURCES= \
 	CGroup.pm \
+	Cloudinit.pm \
 	Command.pm \
 	Config.pm \
 	Create.pm \
diff --git a/src/lxc-pve-prestart-hook b/src/lxc-pve-prestart-hook
index 3bdf7e4..e5932d2 100755
--- a/src/lxc-pve-prestart-hook
+++ b/src/lxc-pve-prestart-hook
@@ -12,6 +12,7 @@ use POSIX;
 use PVE::CGroup;
 use PVE::Cluster;
 use PVE::LXC::Config;
+use PVE::LXC::Cloudinit;
 use PVE::LXC::Setup;
 use PVE::LXC::Tools;
 use PVE::LXC;
@@ -140,6 +141,10 @@ PVE::LXC::Tools::lxc_hook('pre-start', 'lxc', sub {
     my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
     $lxc_setup->pre_start_hook();
 
+    if ($conf->{cienable}) {
+	PVE::LXC::Cloudinit::create_cloudinit_files($conf, $lxc_setup)
+    }
+
     if (PVE::CGroup::cgroup_mode() == 2) {
 	if (!$lxc_setup->unified_cgroupv2_support()) {
 	    log_warn($vmid, "old systemd (< v232) detected, container won't run in a pure cgroupv2"
-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH RFC container 3/3] cloudinit: add dump command to pct
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 1/3] cloudinit: introduce config parameters Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 2/3] cloudinit: basic implementation Leo Nunner
@ 2023-05-11 11:12 ` Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 1/2] cloudinit: rename qemu cloudinit panel Leo Nunner
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

Introduce a 'pct cloudinit dump <vmid> <section>' command to dump the
generated cloudinit configuration for a section.

Signed-off-by: Leo Nunner <l.nunner@proxmox.com>
---
 src/PVE/API2/LXC.pm      | 33 +++++++++++++++++++++++++++++++++
 src/PVE/CLI/pct.pm       |  4 ++++
 src/PVE/LXC/Cloudinit.pm | 11 +++++++++++
 3 files changed, 48 insertions(+)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index e585509..2cae727 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -2963,4 +2963,37 @@ __PACKAGE__->register_method({
 
 	return { socket => $socket };
     }});
+
+__PACKAGE__->register_method({
+    name => 'cloudinit_generated_config_dump',
+    path => '{vmid}/cloudinit/dump',
+    method => 'GET',
+    proxyto => 'node',
+    description => "Get automatically generated cloudinit config.",
+    permissions => {
+	check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid }),
+	    type => {
+		description => 'Config type.',
+		type => 'string',
+		enum => ['user', 'meta'],
+	    },
+	},
+    },
+    returns => {
+	type => 'string',
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $conf = PVE::LXC::Config->load_config($param->{vmid});
+
+	return PVE::LXC::Cloudinit::dump_cloudinit_config($conf, $param->{type});
+    }});
+
 1;
diff --git a/src/PVE/CLI/pct.pm b/src/PVE/CLI/pct.pm
index ff75d33..69f3560 100755
--- a/src/PVE/CLI/pct.pm
+++ b/src/PVE/CLI/pct.pm
@@ -1000,6 +1000,10 @@ our $cmddef = {
     rescan  => [ __PACKAGE__, 'rescan', []],
     cpusets => [ __PACKAGE__, 'cpusets', []],
     fstrim => [ __PACKAGE__, 'fstrim', ['vmid']],
+
+    cloudinit => {
+	dump => [ "PVE::API2::LXC", 'cloudinit_generated_config_dump', ['vmid', 'type'], { node => $nodename }, sub { print "$_[0]\n"; }],
+    },
 };
 
 1;
diff --git a/src/PVE/LXC/Cloudinit.pm b/src/PVE/LXC/Cloudinit.pm
index e4bc67d..c977a08 100644
--- a/src/PVE/LXC/Cloudinit.pm
+++ b/src/PVE/LXC/Cloudinit.pm
@@ -111,4 +111,15 @@ sub create_cloudinit_files {
     });
 }
 
+sub dump_cloudinit_config {
+    my ($conf, $type) = @_;
+
+    if ($type eq 'user') {
+	return cloudinit_userdata($conf);
+    } else { # metadata config
+	my $user = cloudinit_userdata($conf);
+	return gen_cloudinit_metadata($user);
+    }
+}
+
 1;
-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH RFC manager 1/2] cloudinit: rename qemu cloudinit panel
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
                   ` (2 preceding siblings ...)
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 3/3] cloudinit: add dump command to pct Leo Nunner
@ 2023-05-11 11:12 ` Leo Nunner
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 2/2] cloudinit: introduce panel for LXCs Leo Nunner
  2023-05-12 11:39 ` [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Wolfgang Bumiller
  5 siblings, 0 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Leo Nunner <l.nunner@proxmox.com>
---
 www/manager6/qemu/CloudInit.js | 4 ++--
 www/manager6/qemu/Config.js    | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/www/manager6/qemu/CloudInit.js b/www/manager6/qemu/CloudInit.js
index 77ff93d4..14117ff6 100644
--- a/www/manager6/qemu/CloudInit.js
+++ b/www/manager6/qemu/CloudInit.js
@@ -1,6 +1,6 @@
 Ext.define('PVE.qemu.CloudInit', {
     extend: 'Proxmox.grid.PendingObjectGrid',
-    xtype: 'pveCiPanel',
+    xtype: 'pveQemuCiPanel',
 
     onlineHelp: 'qm_cloud_init',
 
@@ -66,7 +66,7 @@ Ext.define('PVE.qemu.CloudInit', {
 	    xtype: 'proxmoxButton',
 	    disabled: true,
 	    enableFn: function(rec) {
-		let view = this.up('pveCiPanel');
+		let view = this.up('pveQemuCiPanel');
 		return !!view.rows[rec.data.key].editor;
 	    },
 	    handler: function() {
diff --git a/www/manager6/qemu/Config.js b/www/manager6/qemu/Config.js
index 94c540c5..03e1e6d8 100644
--- a/www/manager6/qemu/Config.js
+++ b/www/manager6/qemu/Config.js
@@ -284,7 +284,7 @@ Ext.define('PVE.qemu.Config', {
 		title: 'Cloud-Init',
 		itemId: 'cloudinit',
 		iconCls: 'fa fa-cloud',
-		xtype: 'pveCiPanel',
+		xtype: 'pveQemuCiPanel',
 	    },
 	    {
 		title: gettext('Options'),
-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH RFC manager 2/2] cloudinit: introduce panel for LXCs
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
                   ` (3 preceding siblings ...)
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 1/2] cloudinit: rename qemu cloudinit panel Leo Nunner
@ 2023-05-11 11:12 ` Leo Nunner
  2023-05-12 11:39 ` [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Wolfgang Bumiller
  5 siblings, 0 replies; 7+ messages in thread
From: Leo Nunner @ 2023-05-11 11:12 UTC (permalink / raw)
  To: pve-devel

based on the already existing panel for VMs. Some things have been
changed, there is no network configuration, and a separate "enable"
options toggles cloud-init (simillar to adding/removing a cloud-init
drive for VMs).

Signed-off-by: Leo Nunner <l.nunner@proxmox.com>
---
 www/manager6/Makefile         |   1 +
 www/manager6/lxc/CloudInit.js | 219 ++++++++++++++++++++++++++++++++++
 www/manager6/lxc/Config.js    |   6 +
 3 files changed, 226 insertions(+)
 create mode 100644 www/manager6/lxc/CloudInit.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 2b577c8e..27ac9068 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -168,6 +168,7 @@ JSSRC= 							\
 	dc/UserTagAccessEdit.js				\
 	dc/RegisteredTagsEdit.js			\
 	lxc/CmdMenu.js					\
+	lxc/CloudInit.js				\
 	lxc/Config.js					\
 	lxc/CreateWizard.js				\
 	lxc/DNS.js					\
diff --git a/www/manager6/lxc/CloudInit.js b/www/manager6/lxc/CloudInit.js
new file mode 100644
index 00000000..2e4e26ba
--- /dev/null
+++ b/www/manager6/lxc/CloudInit.js
@@ -0,0 +1,219 @@
+Ext.define('PVE.lxc.CloudInit', {
+    extend: 'Proxmox.grid.PendingObjectGrid',
+    xtype: 'pveLxcCiPanel',
+
+    tbar: [
+	{
+	    xtype: 'proxmoxButton',
+	    disabled: true,
+	    dangerous: true,
+	    confirmMsg: function(rec) {
+		let view = this.up('grid');
+		var warn = gettext('Are you sure you want to remove entry {0}');
+
+		var entry = rec.data.key;
+		var msg = Ext.String.format(warn, "'"
+		    + view.renderKey(entry, {}, rec) + "'");
+
+		return msg;
+	    },
+	    enableFn: function(record) {
+		let view = this.up('grid');
+		var caps = Ext.state.Manager.get('GuiCap');
+		if (view.rows[record.data.key].never_delete ||
+		    !caps.vms['VM.Config.Network']) {
+		    return false;
+		}
+
+		if (record.data.key === 'cipassword' && !record.data.value) {
+		    return false;
+		}
+		return true;
+	    },
+	    handler: function() {
+		let view = this.up('grid');
+		let records = view.getSelection();
+		if (!records || !records.length) {
+		    return;
+		}
+
+		var id = records[0].data.key;
+
+		var params = {};
+		params.delete = id;
+		Proxmox.Utils.API2Request({
+		    url: view.baseurl + '/config',
+		    waitMsgTarget: view,
+		    method: 'PUT',
+		    params: params,
+		    failure: function(response, opts) {
+			Ext.Msg.alert('Error', response.htmlStatus);
+		    },
+		    callback: function() {
+			view.reload();
+		    },
+		});
+	    },
+	    text: gettext('Remove'),
+	},
+	{
+	    xtype: 'proxmoxButton',
+	    disabled: true,
+	    enableFn: function(rec) {
+		let view = this.up('pveLxcCiPanel');
+		return !!view.rows[rec.data.key].editor;
+	    },
+	    handler: function() {
+		let view = this.up('grid');
+		view.run_editor();
+	    },
+	    text: gettext('Edit'),
+	},
+    ],
+
+    border: false,
+
+    renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
+	var me = this;
+	var rows = me.rows;
+	var rowdef = rows[key] || {};
+
+	var icon = "";
+	if (rowdef.iconCls) {
+	    icon = '<i class="' + rowdef.iconCls + '"></i> ';
+	}
+	return icon + (rowdef.header || key);
+    },
+
+    listeners: {
+	activate: function() {
+	    var me = this;
+	    me.rstore.startUpdate();
+	},
+	itemdblclick: function() {
+	    var me = this;
+	    me.run_editor();
+	},
+    },
+
+    initComponent: function() {
+	var me = this;
+
+	var nodename = me.pveSelNode.data.node;
+	if (!nodename) {
+	    throw "no node name specified";
+	}
+
+	var vmid = me.pveSelNode.data.vmid;
+	if (!vmid) {
+	    throw "no VM ID specified";
+	}
+	var caps = Ext.state.Manager.get('GuiCap');
+	me.baseurl = '/api2/extjs/nodes/' + nodename + '/lxc/' + vmid;
+	me.url = me.baseurl + '/pending';
+	me.editorConfig.url = me.baseurl + '/config';
+	me.editorConfig.pveSelNode = me.pveSelNode;
+
+	let caps_ci = caps.vms['VM.Config.Cloudinit'] || caps.vms['VM.Config.Network'];
+	/* editor is string and object */
+	me.rows = {
+	    cienable: {
+		header: gettext('Enable'),
+		iconCls: 'fa fa-cloud',
+		never_delete: true,
+		defaultValue: false,
+		editor: caps_ci ? {
+		    xtype: 'proxmoxWindowEdit',
+		    subject: gettext('Enable Cloud-Init'),
+		    items: [
+			{
+			    xtype: 'proxmoxcheckbox',
+			    deleteEmpty: true,
+			    fieldLabel: gettext('Enable'),
+			    name: 'cienable',
+			},
+		    ],
+		} : undefined,
+		renderer: Proxmox.Utils.format_boolean,
+	    },
+	    ciuser: {
+		header: gettext('User'),
+		iconCls: 'fa fa-user',
+		never_delete: true,
+		defaultValue: '',
+		editor: caps_ci ? {
+		    xtype: 'proxmoxWindowEdit',
+		    subject: gettext('User'),
+		    items: [
+			{
+			    xtype: 'proxmoxtextfield',
+			    deleteEmpty: true,
+			    emptyText: Proxmox.Utils.defaultText,
+			    fieldLabel: gettext('User'),
+			    name: 'ciuser',
+			},
+		    ],
+		} : undefined,
+		renderer: function(value) {
+		    return value || Proxmox.Utils.defaultText;
+		},
+	    },
+	    cipassword: {
+		header: gettext('Password'),
+		iconCls: 'fa fa-unlock',
+		defaultValue: '',
+		editor: caps_ci ? {
+		    xtype: 'proxmoxWindowEdit',
+		    subject: gettext('Password'),
+		    items: [
+			{
+			    xtype: 'proxmoxtextfield',
+			    inputType: 'password',
+			    deleteEmpty: true,
+			    emptyText: Proxmox.Utils.noneText,
+			    fieldLabel: gettext('Password'),
+			    name: 'cipassword',
+			},
+		    ],
+		} : undefined,
+		renderer: function(value) {
+		    return value || Proxmox.Utils.noneText;
+		},
+	    },
+	    sshkeys: {
+		header: gettext('SSH public key'),
+		iconCls: 'fa fa-key',
+		editor: caps_ci ? 'PVE.qemu.SSHKeyEdit' : undefined,
+		never_delete: true,
+		renderer: function(value) {
+		    value = decodeURIComponent(value);
+		    var keys = value.split('\n');
+		    var text = [];
+		    keys.forEach(function(key) {
+			if (key.length) {
+			    let res = PVE.Parser.parseSSHKey(key);
+			    if (res) {
+				key = Ext.String.htmlEncode(res.comment);
+				if (res.options) {
+				    key += ' <span style="color:gray">(' + gettext('with options') + ')</span>';
+				}
+				text.push(key);
+				return;
+			    }
+			    // Most likely invalid at this point, so just stick to
+			    // the old value.
+			    text.push(Ext.String.htmlEncode(key));
+			}
+		    });
+		    if (text.length) {
+			return text.join('<br>');
+		    } else {
+			return Proxmox.Utils.noneText;
+		    }
+		},
+		defaultValue: '',
+	    },
+	};
+	me.callParent();
+    },
+});
diff --git a/www/manager6/lxc/Config.js b/www/manager6/lxc/Config.js
index 23c17d2e..bd86b93b 100644
--- a/www/manager6/lxc/Config.js
+++ b/www/manager6/lxc/Config.js
@@ -260,6 +260,12 @@ Ext.define('PVE.lxc.Config', {
 		itemId: 'dns',
 		xtype: 'pveLxcDNS',
 	    },
+	    {
+		title: 'Cloud-Init',
+		itemId: 'cloudinit',
+		iconCls: 'fa fa-cloud',
+		xtype: 'pveLxcCiPanel',
+	    },
 	    {
 		title: gettext('Options'),
 		itemId: 'options',
-- 
2.30.2





^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC
  2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
                   ` (4 preceding siblings ...)
  2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 2/2] cloudinit: introduce panel for LXCs Leo Nunner
@ 2023-05-12 11:39 ` Wolfgang Bumiller
  5 siblings, 0 replies; 7+ messages in thread
From: Wolfgang Bumiller @ 2023-05-12 11:39 UTC (permalink / raw)
  To: Leo Nunner; +Cc: pve-devel

On Thu, May 11, 2023 at 01:12:44PM +0200, Leo Nunner wrote:
> This series introduces basic cloudinit support for containers. All in
> all, it works quite similar to VMs, with the caveat that we only allow
> network configuration through the alrady existing systems, and not via
> cloud-init.
> 
> These patches should still be seen as WIP, but they are in a workable
> state and I'd like some feedback on how I currently handle things. Are
> there any other parameters/features that are needed here? Is the current
> mechanism for providing the configuration to the container optimal, or
> is there a better way?

Seems fine to me. Regarding other parameters/features... meh, I'd say we
can discuss them when they're requested?




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-05-12 11:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-11 11:12 [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Leo Nunner
2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 1/3] cloudinit: introduce config parameters Leo Nunner
2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 2/3] cloudinit: basic implementation Leo Nunner
2023-05-11 11:12 ` [pve-devel] [PATCH RFC container 3/3] cloudinit: add dump command to pct Leo Nunner
2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 1/2] cloudinit: rename qemu cloudinit panel Leo Nunner
2023-05-11 11:12 ` [pve-devel] [PATCH RFC manager 2/2] cloudinit: introduce panel for LXCs Leo Nunner
2023-05-12 11:39 ` [pve-devel] [PATCH RFC container manager] Introduce cloud-init support for LXC Wolfgang Bumiller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal