Re: [pve-devel] [PATCH ifupdown2] patch: fix bond mac address at boot.

2023-11-14 Thread DERUMIER, Alexandre
Hi,

Something definitively wrong with this patch
https://forum.proxmox.com/threads/same-mac-on-all-lacp-bonds-bridges-after-upgrade-proxmox-8.136359/

This forum user is getting mac of his fiber transceiver at
boot...(that's superstange as they shouldn't have a mac address)


I think that it's because it's still doing crap with mac address and
some race can occur.


This patch
https://lists.proxmox.com/pipermail/pve-devel/2023-September/059129.html

should fix it, disabling systemd mac random renaming. (like on
rhel8,fedora et other distro)


and revert the last ifupdown2 patch.


 Message initial 
De: Thomas Lamprecht 
À: Proxmox VE development discussion ,
Alexandre Derumier 
Objet: Re: [pve-devel] [PATCH ifupdown2] patch: fix bond mac address at
boot.
Date: 19/10/2023 10:53:22

Am 01/09/2023 um 11:12 schrieb Alexandre Derumier:
> since systemd v241, like for bridge, the bond mac is setup
> randomly at boot, instead inherit from first slave.
> 
> Then, on next ifreload, ifupdown2 was already fixing it,
> but with an down/up of the bond (with potentials impact on the
> network).
> 


Hmm, we now got a few reports in the forum that get quite a few
"Received packet on bond0 with own address as source" warnings
after upgrading to ifupdown2 to the version that just ships this
patch here:


https://antiphishing.cetsi.fr/proxy/v3?i=cWdzUmRSM0ZiRHpoUDkxTSw3-
90dQgKDkqmWWemZ6js=WXNQOUY5VXRSNUlTdlVTThIwPzZaoDCvq1Kc_09FxyZzKHGZEQ
ETBzOHeMNLlvdN=R0pWUVNEaUFuMTBCTlptbqfFzF4TB2H57gw42PegMEHMfdECltiGwe
4Ql2leQkqk=https%3A//forum.proxmox.com/threads/kernel-vmbr0-received-
packet-on-bond0-with-own-address-as-source-address.133152/%23post-
597121=8YLU
https://antiphishing.cetsi.fr/proxy/v3?i=cWdzUmRSM0ZiRHpoUDkxTSw3-
90dQgKDkqmWWemZ6js=WXNQOUY5VXRSNUlTdlVTThIwPzZaoDCvq1Kc_09FxyZzKHGZEQ
ETBzOHeMNLlvdN=R0pWUVNEaUFuMTBCTlptbqfFzF4TB2H57gw42PegMEHMfdECltiGwe
4Ql2leQkqk=https%3A//forum.proxmox.com/threads/vmbr0-received-packet-
on-bond0-with-own-address-as-source-address.135044/%23post-
597283=8YLU

Seem like the switches send the ECTP loopback packages sometimes
back over the other link due both having the same MAC address,
Wolfgang thinks that shouldn't matter though..

They mention that a downgrade helps so even if this is a switch
(configuration) issue on their side it might not be ideal to regress
here – do you have any idea what's going on?


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] applied: [PATCH v2 container] api: network: get interfaces from containers

2023-11-14 Thread Thomas Lamprecht
Am 15/06/2023 um 11:43 schrieb Leo Nunner:
> Adds an 'interfaces' endpoint in the API
> (/nodes/{node}/lxc/{vmid}/interfaces'), which returns a list of
> interface names, together with a MAC, IPv4 and IPv6 address. This list
> may be expanded in the future. Note that this is only returned for
> *running* containers, stopped containers simply return an empty list.
> 
> Signed-off-by: Leo Nunner 
> ---
>  src/PVE/API2/LXC.pm | 50 +
>  src/PVE/LXC.pm  | 26 +++
>  2 files changed, 76 insertions(+)
> 
>

applied this one for now, thanks! UI might need slight rework/follow-up, as
this was done before we had the extra line for "is-unprivileged" in the status
panel, so there's not enough space left now.


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 qemu-server 21/22] api2: create|restore|clone: add_free_ip

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

Signed-off-by: Stefan Hanreich 
---
 PVE/API2/Qemu.pm  |  6 ++
 PVE/QemuServer.pm | 31 +++
 2 files changed, 37 insertions(+)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 38bdaab..a0f8243 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -991,6 +991,8 @@ __PACKAGE__->register_method({
eval { PVE::QemuServer::template_create($vmid, 
$restored_conf) };
warn $@ if $@;
}
+
+   PVE::QemuServer::create_ifaces_ipams_ips($restored_conf, $vmid) 
if $unique;
};
 
# ensure no old replication state are exists
@@ -1066,6 +1068,8 @@ __PACKAGE__->register_method({
}
 
PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
+
+   PVE::QemuServer::create_ifaces_ipams_ips($conf, $vmid);
};
 
PVE::QemuConfig->lock_config_full($vmid, 1, $realcmd);
@@ -3763,6 +3767,8 @@ __PACKAGE__->register_method({
 
PVE::QemuConfig->write_config($newid, $newconf);
 
+   PVE::QemuServer::create_ifaces_ipams_ips($newconf, $vmid);
+
if ($target) {
# always deactivate volumes - avoid lvm LVs to be active on 
several nodes
PVE::Storage::deactivate_volumes($storecfg, $vollist, 
$snapname) if !$running;
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 1ae1cb0..fecdb9c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -8626,4 +8626,35 @@ sub del_nets_bridge_fdb {
 }
 }
 
+sub create_ifaces_ipams_ips {
+my ($conf, $vmid) = @_;
+
+return if !$have_sdn;
+
+foreach my $opt (keys %$conf) {
+if ($opt =~ m/^net(\d+)$/) {
+my $value = $conf->{$opt};
+my $net = PVE::QemuServer::parse_net($value);
+eval { 
PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{name}, 
$net->{macaddr}, $vmid, undef, 1) };
+warn $@ if $@;
+}
+}
+}
+
+sub delete_ifaces_ipams_ips {
+my ($conf, $vmid) = @_;
+
+return if !$have_sdn;
+
+foreach my $opt (keys %$conf) {
+   if ($opt =~ m/^net(\d+)$/) {
+   my $net = PVE::QemuServer::parse_net($conf->{$opt});
+   eval { PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, 
$net->{macaddr}) };
+   warn $@ if $@;
+   eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, 
$net->{macaddr}, $conf->{name}) };
+   warn $@ if $@;
+   }
+}
+}
+
 1;
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-manager 16/22] sdn: add DHCP option to Zone dialogue

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 www/manager6/sdn/zones/Base.js   |  4 ++--
 www/manager6/sdn/zones/SimpleEdit.js | 10 ++
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/www/manager6/sdn/zones/Base.js b/www/manager6/sdn/zones/Base.js
index 602e4c16b..80ce51bac 100644
--- a/www/manager6/sdn/zones/Base.js
+++ b/www/manager6/sdn/zones/Base.js
@@ -55,7 +55,7 @@ Ext.define('PVE.panel.SDNZoneBase', {
},
);
 
-   me.advancedItems = [
+   me.advancedItems.unshift(
{
xtype: 'pveSDNDnsSelector',
fieldLabel: gettext('DNS Server'),
@@ -77,7 +77,7 @@ Ext.define('PVE.panel.SDNZoneBase', {
fieldLabel: gettext('DNS Zone'),
allowBlank: true,
},
-   ];
+   );
 
me.callParent();
 },
diff --git a/www/manager6/sdn/zones/SimpleEdit.js 
b/www/manager6/sdn/zones/SimpleEdit.js
index cb7c34035..ee4ac8ec8 100644
--- a/www/manager6/sdn/zones/SimpleEdit.js
+++ b/www/manager6/sdn/zones/SimpleEdit.js
@@ -19,6 +19,16 @@ Ext.define('PVE.sdn.zones.SimpleInputPanel', {
var me = this;
 
 me.items = [];
+   me.advancedItems = [
+   {
+   xtype: 'proxmoxcheckbox',
+   name: 'dhcp',
+   inputValue: 'dnsmasq',
+   uncheckedValue: 0,
+   checked: false,
+   fieldLabel: gettext('automatic DHCP'),
+   },
+   ];
 
me.callParent();
 },
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 qemu-server 19/22] vmnic add|remove : add|del ip in ipam

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

Signed-off-by: Stefan Hanreich 
---
 PVE/QemuServer.pm | 38 ++
 1 file changed, 38 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index c465fb6..1ae1cb0 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -64,6 +64,8 @@ use PVE::QemuServer::USB;
 my $have_sdn;
 eval {
 require PVE::Network::SDN::Zones;
+require PVE::Network::SDN::Vnets;
+require PVE::Network::SDN::Dhcp;
 $have_sdn = 1;
 };
 
@@ -4998,6 +5000,11 @@ sub vmconfig_hotplug_pending {
} elsif ($opt =~ m/^net(\d+)$/) {
die "skip\n" if !$hotplug_features->{network};
vm_deviceunplug($vmid, $conf, $opt);
+   if($have_sdn) {
+   my $net = PVE::QemuServer::parse_net($conf->{$opt});
+   PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, 
$net->{macaddr});
+   PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, 
$net->{macaddr}, $conf->{name});
+   }
} elsif (is_valid_drivename($opt)) {
die "skip\n" if !$hotplug_features->{disk} || $opt =~ 
m/(ide|sata)(\d+)/;
vm_deviceunplug($vmid, $conf, $opt);
@@ -5203,6 +5210,12 @@ sub vmconfig_apply_pending {
die "internal error";
} elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, 
$force);
+   } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
+   if($have_sdn) {
+   my $net = PVE::QemuServer::parse_net($conf->{$opt});
+   PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, 
$net->{macaddr});
+   PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, 
$net->{macaddr}, $conf->{name});
+   }
}
};
if (my $err = $@) {
@@ -5222,6 +5235,21 @@ sub vmconfig_apply_pending {
eval {
if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
vmconfig_register_unused_drive($storecfg, $vmid, $conf, 
parse_drive($opt, $conf->{$opt}))
+   } elsif (defined($conf->{pending}->{$opt}) && $opt =~ m/^net\d+$/) {
+   if($have_sdn) {
+my $new_net = 
PVE::QemuServer::parse_net($conf->{pending}->{$opt});
+   if ($conf->{$opt}){
+   my $old_net = PVE::QemuServer::parse_net($conf->{$opt});
+
+   if ($old_net->{bridge} ne $new_net->{bridge} ||
+   $old_net->{macaddr} ne $new_net->{macaddr}) {
+   
PVE::Network::SDN::Dhcp::remove_mapping($old_net->{bridge}, 
$old_net->{macaddr});
+   
PVE::Network::SDN::Vnets::del_ips_from_mac($old_net->{bridge}, 
$old_net->{macaddr}, $conf->{name});
+   }
+  }
+  #fixme: reuse ip if mac change && same bridge
+  
PVE::Network::SDN::Vnets::add_next_free_cidr($new_net->{bridge}, $conf->{name}, 
$new_net->{macaddr}, $vmid, undef, 1);
+   }
}
};
if (my $err = $@) {
@@ -5265,6 +5293,13 @@ sub vmconfig_update_net {
 # for non online change, we try to hot-unplug
die "skip\n" if !$hotplug;
vm_deviceunplug($vmid, $conf, $opt);
+
+   # fixme: force device_unplug on bridge change if mac is present in 
dhcp, to force guest os to retrieve a new ip
+   if($have_sdn) {
+   PVE::Network::SDN::Dhcp::remove_mapping($oldnet->{bridge}, 
$oldnet->{macaddr});
+   PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, 
$oldnet->{macaddr}, $conf->{name});
+   }
+
} else {
 
die "internal error" if $opt !~ m/net(\d+)/;
@@ -5296,6 +5331,9 @@ sub vmconfig_update_net {
 }
 
 if ($hotplug) {
+   if ($have_sdn) {
+   PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, 
$conf->{name}, $newnet->{macaddr}, "vmid:$vmid", undef, 1);
+   }
vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, 
$machine_type);
 } else {
die "skip\n";
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 13/22] dhcp: regenerate config for DHCP servers on reload

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN.pm | 9 -
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
index 057034f..c306527 100644
--- a/src/PVE/Network/SDN.pm
+++ b/src/PVE/Network/SDN.pm
@@ -12,6 +12,7 @@ use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 use PVE::Network::SDN::Controllers;
 use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Dhcp;
 
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
@@ -155,7 +156,7 @@ sub commit_config {
 my $controllers = { ids => $controllers_cfg->{ids} };
 my $subnets = { ids => $subnets_cfg->{ids} };
 
- $cfg = { version => $version, vnets => $vnets, zones => $zones, 
controllers => $controllers, subnets => $subnets };
+$cfg = { version => $version, vnets => $vnets, zones => $zones, 
controllers => $controllers, subnets => $subnets };
 
 cfs_write_file($running_cfg, $cfg);
 }
@@ -231,6 +232,12 @@ sub generate_controller_config {
 PVE::Network::SDN::Controllers::reload_controller() if $reload;
 }
 
+sub generate_dhcp_config {
+my ($reload) = @_;
+
+PVE::Network::SDN::Dhcp::regenerate_config($reload);
+}
+
 sub encode_value {
 my ($type, $key, $value) = @_;
 
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 09/22] sdn: dhcp: add helper for creating DHCP leases

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/Dhcp.pm  | 115 +++
 src/PVE/Network/SDN/Makefile |   2 +-
 2 files changed, 116 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/Network/SDN/Dhcp.pm

diff --git a/src/PVE/Network/SDN/Dhcp.pm b/src/PVE/Network/SDN/Dhcp.pm
new file mode 100644
index 000..b178927
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp.pm
@@ -0,0 +1,115 @@
+package PVE::Network::SDN::Dhcp;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::SubnetPlugin;
+use PVE::Network::SDN::Dhcp qw(config);
+use PVE::Network::SDN::Subnets qw(sdn_subnets_config config get_dhcp_ranges);
+use PVE::Network::SDN::Dhcp::Plugin;
+use PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use PVE::INotify qw(nodename);
+
+PVE::Network::SDN::Dhcp::Plugin->init();
+
+PVE::Network::SDN::Dhcp::Dnsmasq->register();
+PVE::Network::SDN::Dhcp::Dnsmasq->init();
+
+sub add_mapping {
+my ($vnetid, $mac, $ip4, $ip6) = @_;
+
+my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+return if !$vnet;
+
+my $zoneid = $vnet->{zone};
+my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+return if !$zone->{ipam} || !$zone->{dhcp};
+
+my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+$dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip4) if $ip4;
+$dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip6) if $ip6;
+}
+
+sub remove_mapping {
+my ($vnetid, $mac) = @_;
+
+my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+return if !$vnet;
+
+my $zoneid = $vnet->{zone};
+my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+return if !$zone->{ipam} || !$zone->{dhcp};
+
+my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+$dhcp_plugin->del_ip_mapping($zoneid, $mac);
+}
+
+sub regenerate_config {
+my ($reload) = @_;
+
+my $cfg = PVE::Network::SDN::running_config();
+
+my $zone_cfg = $cfg->{zones};
+my $subnet_cfg = $cfg->{subnets};
+return if !$zone_cfg && !$subnet_cfg;
+
+my $nodename = PVE::INotify::nodename();
+
+my $plugins = PVE::Network::SDN::Dhcp::Plugin->lookup_types();
+
+foreach my $plugin_name (@$plugins) {
+   my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+   eval { $plugin->before_regenerate() };
+   die "Could not run before_regenerate for DHCP plugin $plugin_name $@\n" 
if $@;
+}
+
+foreach my $zoneid (sort keys %{$zone_cfg->{ids}}) {
+my $zone = $zone_cfg->{ids}->{$zoneid};
+next if !$zone->{dhcp};
+
+   my $dhcp_plugin_name = $zone->{dhcp};
+   my $dhcp_plugin = 
PVE::Network::SDN::Dhcp::Plugin->lookup($dhcp_plugin_name);
+
+   die "Could not find DHCP plugin: $dhcp_plugin_name" if !$dhcp_plugin; 
+
+   eval { $dhcp_plugin->before_configure($zoneid) };
+   die "Could not run before_configure for DHCP server $zoneid $@\n" if $@;
+
+
+   foreach my $subnet_id (keys %{$subnet_cfg->{ids}}) {
+   my $subnet_config = 
PVE::Network::SDN::Subnets::sdn_subnets_config($subnet_cfg, $subnet_id);
+   my $dhcp_ranges = 
PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet_config);
+
+   my ($zone, $subnet_network, $subnet_mask) = split(/-/, $subnet_id);
+   next if $zone ne $zoneid;
+   next if !$dhcp_ranges;
+
+   eval { $dhcp_plugin->configure_subnet($zoneid, $subnet_config) };
+   warn "Could not configure subnet $subnet_id: $@\n" if $@;
+
+   foreach my $dhcp_range (@$dhcp_ranges) {
+   eval { $dhcp_plugin->configure_range($zoneid, $subnet_config, 
$dhcp_range) };
+   warn "Could not configure DHCP range for $subnet_id: $@\n" if 
$@;
+   }
+   }
+
+   eval { $dhcp_plugin->after_configure($zoneid) };
+   warn "Could not run after_configure for DHCP server $zoneid $@\n" if $@;
+
+}
+
+foreach my $plugin_name (@$plugins) {
+   my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+
+   eval { $plugin->after_regenerate() };
+   warn "Could not run after_regenerate for DHCP plugin $plugin_name $@\n" 
if $@;
+}
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 848f7d4..3e6e5fb 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm 
SubnetPlugin.pm Ipams.pm Dns.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm 
SubnetPlugin.pm Ipams.pm Dns.pm Dhcp.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 11/22] api: subnet: add dhcp ranges

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/API2/Network/SDN/Subnets.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/PVE/API2/Network/SDN/Subnets.pm 
b/src/PVE/API2/Network/SDN/Subnets.pm
index eb6b41b..c263cd5 100644
--- a/src/PVE/API2/Network/SDN/Subnets.pm
+++ b/src/PVE/API2/Network/SDN/Subnets.pm
@@ -29,6 +29,7 @@ my $api_sdn_subnets_config = sub {
 my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, 
$id));
 $scfg->{subnet} = $id;
 $scfg->{digest} = $cfg->{digest};
+$scfg->{'dhcp-range'} = PVE::Network::SDN::Subnets::get_dhcp_ranges($scfg);
 
 return $scfg;
 };
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 12/22] api: zone: add dhcp options

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/API2/Network/SDN/Zones.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/PVE/API2/Network/SDN/Zones.pm 
b/src/PVE/API2/Network/SDN/Zones.pm
index 4c8b7e1..1c3356e 100644
--- a/src/PVE/API2/Network/SDN/Zones.pm
+++ b/src/PVE/API2/Network/SDN/Zones.pm
@@ -99,6 +99,7 @@ __PACKAGE__->register_method ({
reversedns => { type => 'string', optional => 1},
dnszone => { type => 'string', optional => 1},
ipam => { type => 'string', optional => 1},
+   dhcp => { type => 'string', optional => 1},
pending => { optional => 1},
state => { type => 'string', optional => 1},
nodes => { type => 'string', optional => 1},
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN

2023-11-14 Thread Stefan Hanreich
This patch series adds support for automatically deploying dnsmasq as a DHCP
server to a simple SDN Zone.

This is mostly an update for Alexandre, Stefan and Thomas so we have a
consolidated base for further development of this feature. Code and UI is (very)
rough in some places, but all the planned functionality is now included and
usable via the web UI.

I will be doing some cleanup and refactoring the following days. Additionally,
permissions and validations are still missing and are now top priority on my
TODO list.

Alexandre is still working on the new LXC integration, that should follow
shortly.

You need to install dnsmasq (and disable it afterwards):

  apt install dnsmasq && systemctl disable --now dnsmasq


You can use the following example configuration for deploying a DHCP server in
a SDN subnet, you should also be able to recreate this configuration in the
web UI:


/etc/pve/sdn/zones.cfg:

  simple: DHCPNAT
  ipam pve
  dhcp dnsmasq


/etc/pve/sdn/vnets.cfg:

  vnet: dhcpnat
  zone DHCPNAT


/etc/pve/sdn/subnets.cfg:

  subnet: DHCPNAT-10.1.0.0-16
  vnet dhcpnat
  dhcp-dns-server 10.1.0.1
  dhcp-range start-address=10.1.0.100,end-address=10.1.0.200
  gateway 10.1.0.1
  snat 1

Don't forget to apply the new configuration!

For testing it can be helpful to monitor the following files (e.g. with watch)
to find out what is happening
  * /etc/dnsmasq.d//ethers (on each node)
  * /etc/pve/priv/ipam.db
  * /etc/pve/priv/macs.db

Changes from v2 -> v3:
  * Removed dhcp.cfg, DHCP server now get configured at the zone
  * added UI
  * added / updated API
  * DHCP acquires IPs at vNIC creation instead of VM start
  * DHCP releases IPs at vNIC removal instead of VM stop
  * improved dnsmasq configuration generation
  * added priv/macs.db for caching mac/IP mappings
  * refactored IPAM plugins
  * updated tests

Changes from v1 -> v2:
  * added hooks for handling DHCP when starting / stopping / .. VMs and CTs
  * Get an IP from IPAM and register that IP in the DHCP server
(pve only for now)
  * remove lease-time, since it is now infinite and managed by the VM lifecycle
  * add hooks for setting & deleting DHCP mappings to DHCP plugins
  * modified interface of the abstract class to reflect new requirements
  * added helpers in existing SDN classes
  * simplified DHCP configuration settings



pve-cluster:

Alexandre Derumier (1):
  add priv/macs.db

 src/PVE/Cluster.pm  | 1 +
 src/pmxcfs/status.c | 1 +
 2 files changed, 2 insertions(+)


pve-network:

Alexandre Derumier (1):
  sdn: fix tests

Stefan Hanreich (12):
  sdn: preparations for DHCP plugin
  subnet: add dhcp options
  sdn: zone: add dhcp options
  sdn: subnet: vnet: refactor IPAM related methods
  ipam: plugins: preparations for DHCP
  dhcp: add abstract class for DHCP plugins
  sdn: dhcp: add dnsmasq plugin
  sdn: dhcp: add helper for creating DHCP leases
  api: add IPAM endpoints
  api: subnet: add dhcp ranges
  api: zone: add dhcp options
  dhcp: regenerate config for DHCP servers on reload

 debian/control |   1 +
 src/PVE/API2/Network/SDN.pm|   6 +
 src/PVE/API2/Network/SDN/Ipam.pm   | 172 ++
 src/PVE/API2/Network/SDN/Makefile  |   2 +-
 src/PVE/API2/Network/SDN/Subnets.pm|   1 +
 src/PVE/API2/Network/SDN/Zones.pm  |   1 +
 src/PVE/Network/SDN.pm |   9 +-
 src/PVE/Network/SDN/Dhcp.pm| 115 
 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm| 198 +
 src/PVE/Network/SDN/Dhcp/Makefile  |   8 +
 src/PVE/Network/SDN/Dhcp/Plugin.pm |  65 +++
 src/PVE/Network/SDN/Ipams.pm   |  80 -
 src/PVE/Network/SDN/Ipams/NetboxPlugin.pm  |  86 -
 src/PVE/Network/SDN/Ipams/PVEPlugin.pm |  85 -
 src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm |  29 +++
 src/PVE/Network/SDN/Ipams/Plugin.pm|  19 +-
 src/PVE/Network/SDN/Makefile   |   3 +-
 src/PVE/Network/SDN/SubnetPlugin.pm|  32 +++-
 src/PVE/Network/SDN/Subnets.pm |  98 +++---
 src/PVE/Network/SDN/Vnets.pm   | 122 +++--
 src/PVE/Network/SDN/Zones.pm   |  34 +++-
 src/PVE/Network/SDN/Zones/SimplePlugin.pm  |   7 +-
 src/test/run_test_subnets.pl   |   8 +-
 src/test/run_test_vnets.pl |   4 +-
 24 files changed, 1069 insertions(+), 116 deletions(-)
 create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
 create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
 create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm


pve-manager:

Stefan Hanreich (4):
  sdn: regenerate DHCP config on reload
  sdn: add DHCP option to Zone dialogue
  sdn: subnet: add panel for editing DHCP ranges
  sdn: dhcp: add view for DHCP mappings

 PVE/API2/Network.pm 

[pve-devel] [WIP v3 pve-network 02/22] sdn: preparations for DHCP plugin

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/Subnets.pm | 25 +
 src/PVE/Network/SDN/Vnets.pm   | 27 +--
 src/PVE/Network/SDN/Zones.pm   | 34 +-
 3 files changed, 51 insertions(+), 35 deletions(-)

diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index 6bb42e5..f654d3a 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -23,7 +23,9 @@ sub sdn_subnets_config {
 my $scfg = $cfg->{ids}->{$id};
 die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
 
-if($scfg) {
+if ($scfg) {
+   $scfg->{id} = $id;
+
my ($zone, $network, $mask) = split(/-/, $id);
$scfg->{cidr} = "$network/$mask";
$scfg->{zone} = $zone;
@@ -35,7 +37,14 @@ sub sdn_subnets_config {
 }
 
 sub config {
-my $config = cfs_read_file("sdn/subnets.cfg");
+my ($running) = @_;
+
+if ($running) {
+   my $cfg = PVE::Network::SDN::running_config();
+   return $cfg->{subnets};
+}
+
+return cfs_read_file("sdn/subnets.cfg");
 }
 
 sub write_config {
@@ -61,16 +70,8 @@ sub complete_sdn_subnet {
 sub get_subnet {
 my ($subnetid, $running) = @_;
 
-my $cfg = {};
-if($running) {
-   my $cfg = PVE::Network::SDN::running_config();
-   $cfg = $cfg->{subnets};
-} else {
-   $cfg = PVE::Network::SDN::Subnets::config();
-}
-
-my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, 
$subnetid, 1);
-return $subnet;
+my $cfg = PVE::Network::SDN::Subnets::config($running);
+return PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
 }
 
 sub find_ip_subnet {
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 1106c9f..39bdda0 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -26,6 +26,13 @@ sub sdn_vnets_config {
 }
 
 sub config {
+my ($running) = @_;
+
+if ($running) {
+   my $cfg = PVE::Network::SDN::running_config();
+   return $cfg->{vnets};
+}
+
 return cfs_read_file("sdn/vnets.cfg");
 }
 
@@ -54,31 +61,23 @@ sub get_vnet {
 
 return if !$vnetid;
 
-my $scfg = {};
-if($running) {
-   my $cfg = PVE::Network::SDN::running_config();
-   $scfg = $cfg->{vnets};
-} else {
-   $scfg = PVE::Network::SDN::Vnets::config();
-}
-
-my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($scfg, $vnetid, 1);
-
-return $vnet;
+my $cfg = PVE::Network::SDN::Vnets::config($running);
+return PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $vnetid, 1);
 }
 
 sub get_subnets {
-my ($vnetid) = @_;
+my ($vnetid, $running) = @_;
 
 my $subnets = undef;
-my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+my $subnets_cfg = PVE::Network::SDN::Subnets::config($running);
+
 foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
my $subnet = 
PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
next if !$subnet->{vnet} || ($vnetid && $subnet->{vnet} ne $vnetid);
$subnets->{$subnetid} = $subnet;
 }
-return $subnets;
 
+return $subnets;
 }
 
 sub get_subnet_from_vnet_cidr {
diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm
index 4ad4e4d..5bd3536 100644
--- a/src/PVE/Network/SDN/Zones.pm
+++ b/src/PVE/Network/SDN/Zones.pm
@@ -40,8 +40,14 @@ sub sdn_zones_config {
 }
 
 sub config {
-my $config = cfs_read_file("sdn/zones.cfg");
-return $config;
+my ($running) = @_;
+
+if ($running) {
+   my $cfg = PVE::Network::SDN::running_config();
+   return $cfg->{zones};
+}
+
+return cfs_read_file("sdn/zones.cfg");
 }
 
 sub get_plugin_config {
@@ -74,19 +80,29 @@ sub complete_sdn_zone {
 sub get_zone {
 my ($zoneid, $running) = @_;
 
-my $cfg = {};
-if($running) {
-my $cfg = PVE::Network::SDN::running_config();
-$cfg = $cfg->{vnets};
-} else {
-$cfg = PVE::Network::SDN::Zones::config();
-}
+my $cfg = PVE::Network::SDN::Zones::config($running);
 
 my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
 
 return $zone;
 }
 
+sub get_vnets {
+my ($zoneid, $running) = @_;
+
+return if !$zoneid;
+
+my $vnets_config = PVE::Network::SDN::Vnets::config($running);
+my $vnets = undef;
+
+for my $vnetid (keys %{$vnets_config->{ids}}) {
+my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_config, 
$vnetid);
+next if !$vnet->{zone} || $vnet->{zone} ne $zoneid;
+$vnets->{$vnetid} = $vnet;
+}
+
+return $vnets;
+}
 
 sub generate_etc_network_config {
 
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 04/22] sdn: zone: add dhcp options

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/Zones/SimplePlugin.pm | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm 
b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
index 4922903..f30278c 100644
--- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -26,7 +26,11 @@ sub properties {
dnszone => {
type => 'string', format => 'dns-name',
description => "dns domain zone  ex: mydomain.com",
-   }
+   },
+   dhcp => {
+   type => 'pve-configid',
+   description => 'ID of the DHCP server responsible for managing this 
range',
+   },
 };
 }
 
@@ -38,6 +42,7 @@ sub options {
reversedns => { optional => 1 },
dnszone => { optional => 1 },
ipam => { optional => 1 },
+   dhcp => { optional => 1 },
 };
 }
 
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 www/css/ext6-pve.css|  10 +-
 www/manager6/Makefile   |   2 +
 www/manager6/dc/Config.js   |  12 +-
 www/manager6/sdn/MappingEdit.js |  65 ++
 www/manager6/tree/DhcpTree.js   | 215 
 5 files changed, 296 insertions(+), 8 deletions(-)
 create mode 100644 www/manager6/sdn/MappingEdit.js
 create mode 100644 www/manager6/tree/DhcpTree.js

diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e18b173f5..e5e616832 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -510,23 +510,21 @@ div.right-aligned {
 content: ' ';
 }
 
-.fa-sdn:before {
+.x-fa-sdn-treelist:before {
 width: 14px;
 height: 14px;
 position: absolute;
 left: 1px;
 top: 4px;
+}
+
+.fa-sdn:before {
 background-image:url(../images/icon-sdn.svg);
 background-size: 14px 14px;
 content: ' ';
 }
 
 .fa-network-wired:before {
-width: 14px;
-height: 14px;
-position: absolute;
-left: 1px;
-top: 4px;
 background-image:url(../images/icon-fa-network-wired.svg);
 background-size: 14px 14px;
 content: ' ';
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index dccd2ba1c..d226c8faa 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -108,6 +108,7 @@ JSSRC=  
\
tree/ResourceTree.js\
tree/SnapshotTree.js\
tree/ResourceMapTree.js \
+   tree/DhcpTree.js\
window/Backup.js\
window/BackupConfig.js  \
window/BulkAction.js\
@@ -274,6 +275,7 @@ JSSRC=  
\
sdn/ZoneContentView.js  \
sdn/ZoneContentPanel.js \
sdn/ZoneView.js \
+   sdn/MappingEdit.js  \
sdn/OptionsPanel.js \
sdn/controllers/Base.js \
sdn/controllers/EvpnEdit.js \
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5fb..0e3948ef4 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -195,7 +195,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: gettext('Zones'),
hidden: true,
-   iconCls: 'fa fa-th',
+   iconCls: 'fa fa-th x-fa-sdn-treelist',
itemId: 'sdnzone',
},
{
@@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: 'VNets',
hidden: true,
-   iconCls: 'fa fa-network-wired',
+   iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
itemId: 'sdnvnet',
},
{
@@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
hidden: true,
iconCls: 'fa fa-gear',
itemId: 'sdnoptions',
+   },
+   {
+   xtype: 'pveDhcpTree',
+   groups: ['sdn'],
+   title: 'Dhcp Mappings',
+   hidden: true,
+   iconCls: 'fa fa-gear',
+   itemId: 'sdnmappings',
});
}
 
diff --git a/www/manager6/sdn/MappingEdit.js b/www/manager6/sdn/MappingEdit.js
new file mode 100644
index 0..533fc6249
--- /dev/null
+++ b/www/manager6/sdn/MappingEdit.js
@@ -0,0 +1,65 @@
+Ext.define('PVE.sdn.DhcpMappingInputPanel', {
+extend: 'Proxmox.panel.InputPanel',
+mixins: ['Proxmox.Mixin.CBind'],
+
+isCreate: false,
+
+items: [
+   {
+   xtype: 'pmxDisplayEditField',
+   name: 'vmid',
+   fieldLabel: gettext('VMID'),
+   allowBlank: false,
+   editable: false,
+   },
+   {
+   xtype: 'pmxDisplayEditField',
+   name: 'mac',
+   fieldLabel: gettext('MAC'),
+   allowBlank: false,
+   cbind: {
+   editable: '{isCreate}',
+   },
+   },
+   {
+   xtype: 'proxmoxtextfield',
+   name: 'ip',
+   fieldLabel: gettext('IP'),
+   allowBlank: false,
+   },
+],
+});
+
+Ext.define('PVE.sdn.MappingEdit', {
+extend: 'Proxmox.window.Edit',
+
+subject: gettext('DHCP Mapping'),
+width: 350,
+
+isCreate: false,
+mapping: {},
+
+submitUrl: function(url, values) {
+   return `${url}/${values.vnet}/${values.mac}`;
+},
+
+initComponent: function() {
+   var me = this;
+
+   me.method = me.isCreate ? 'POST' : 'PUT';
+
+   let ipanel = 

[pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 www/manager6/sdn/SubnetEdit.js | 161 -
 1 file changed, 160 insertions(+), 1 deletion(-)

diff --git a/www/manager6/sdn/SubnetEdit.js b/www/manager6/sdn/SubnetEdit.js
index b9825d2a3..ab3b9d021 100644
--- a/www/manager6/sdn/SubnetEdit.js
+++ b/www/manager6/sdn/SubnetEdit.js
@@ -56,6 +56,148 @@ Ext.define('PVE.sdn.SubnetInputPanel', {
 ],
 });
 
+Ext.define('PVE.sdn.SubnetDhcpRangePanel', {
+extend: 'Ext.form.FieldContainer',
+mixins: ['Ext.form.field.Field'],
+
+initComponent: function() {
+   let me = this;
+
+   me.callParent();
+   me.initField();
+},
+
+getValue: function() {
+   let me = this;
+   let store = me.lookup('grid').getStore();
+
+   let data = [];
+
+   store.getData().each((item) => console.log(item));
+
+   store.getData()
+   .each((item) =>
+   
data.push(`start-address=${item.data['start-address']},end-address=${item.data['end-address']}`),
+   );
+
+   return data;
+},
+
+getSubmitData: function() {
+   let me = this;
+   let data = {};
+
+   let value = me.getValue();
+   if (value) {
+   data[me.getName()] = value;
+   }
+
+   return data;
+},
+
+setValue: function(dhcpRanges) {
+   let me = this;
+   let store = me.lookup('grid').getStore();
+   store.setData(dhcpRanges);
+},
+
+getErrors: function() {
+   let me = this;
+let errors = [];
+
+   return errors;
+},
+
+controller: {
+   xclass: 'Ext.app.ViewController',
+
+   addRange: function() {
+   let me = this;
+   me.lookup('grid').getStore().add({});
+   },
+
+   removeRange: function(field) {
+   let me = this;
+   let record = field.getWidgetRecord();
+
+   me.lookup('grid').getStore().remove(record);
+   },
+
+   onValueChange: function(field, value) {
+   let me = this;
+   let record = field.getWidgetRecord();
+   let column = field.getWidgetColumn();
+
+   record.set(column.dataIndex, value);
+   record.commit();
+   },
+
+   control: {
+   'grid button': {
+   click: 'removeRange',
+   },
+   'field': {
+   change: 'onValueChange',
+   },
+   },
+},
+
+items: [
+   {
+   xtype: 'grid',
+   reference: 'grid',
+   scrollable: true,
+   store: {
+   fields: ['start-address', 'end-address'],
+   },
+   columns: [
+   {
+   text: gettext('Start Address'),
+   xtype: 'widgetcolumn',
+   dataIndex: 'start-address',
+   flex: 1,
+   widget: {
+   xtype: 'textfield',
+   vtype: 'IP64Address',
+   },
+   },
+   {
+   text: gettext('End Address'),
+   xtype: 'widgetcolumn',
+   dataIndex: 'end-address',
+   flex: 1,
+   widget: {
+   xtype: 'textfield',
+   vtype: 'IP64Address',
+   },
+   },
+   {
+   xtype: 'widgetcolumn',
+   width: 40,
+   widget: {
+   xtype: 'button',
+   iconCls: 'fa fa-trash-o',
+   },
+   },
+   ],
+   },
+   {
+   xtype: 'container',
+   layout: {
+   type: 'hbox',
+   },
+   items: [
+   {
+   xtype: 'button',
+   text: gettext('Add'),
+   iconCls: 'fa fa-plus-circle',
+   handler: 'addRange',
+   },
+   ],
+   },
+],
+});
+
 Ext.define('PVE.sdn.SubnetEdit', {
 extend: 'Proxmox.window.Edit',
 
@@ -67,6 +209,8 @@ Ext.define('PVE.sdn.SubnetEdit', {
 
 base_url: undefined,
 
+bodyPadding: 0,
+
 initComponent: function() {
var me = this;
 
@@ -82,11 +226,22 @@ Ext.define('PVE.sdn.SubnetEdit', {
 
let ipanel = Ext.create('PVE.sdn.SubnetInputPanel', {
isCreate: me.isCreate,
+   title: gettext('General'),
+   });
+
+   let dhcpPanel = Ext.create('PVE.sdn.SubnetDhcpRangePanel', {
+   isCreate: me.isCreate,
+   title: gettext('DHCP Ranges'),
+   name: 'dhcp-range',
});
 
Ext.apply(me, {
items: [
-   ipanel,
+   {
+   xtype: 'tabpanel',
+   bodyPadding: 10,
+   items: [ipanel, dhcpPanel],
+   },
],
});
 
@@ -97,6 +252,10 @@ Ext.define('PVE.sdn.SubnetEdit', {
success: function(response, options) {

[pve-devel] [WIP v3 pve-network 06/22] ipam: plugins: preparations for DHCP

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/Ipams.pm   | 80 +++-
 src/PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 86 --
 src/PVE/Network/SDN/Ipams/PVEPlugin.pm | 85 +++--
 src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 29 
 src/PVE/Network/SDN/Ipams/Plugin.pm| 19 -
 5 files changed, 281 insertions(+), 18 deletions(-)

diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm
index e8a4b0b..926df90 100644
--- a/src/PVE/Network/SDN/Ipams.pm
+++ b/src/PVE/Network/SDN/Ipams.pm
@@ -4,9 +4,10 @@ use strict;
 use warnings;
 
 use JSON;
+use Net::IP;
 
 use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file 
cfs_lock_file);
 use PVE::Network;
 
 use PVE::Network::SDN::Ipams::PVEPlugin;
@@ -19,6 +20,64 @@ PVE::Network::SDN::Ipams::NetboxPlugin->register();
 PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
 PVE::Network::SDN::Ipams::Plugin->init();
 
+my $macdb_filename = 'priv/macs.db';
+
+cfs_register_file($macdb_filename, \_reader, \_writer);
+
+sub json_reader {
+my ($filename, $data) = @_;
+
+return defined($data) && length($data) > 0 ? decode_json($data) : {};
+}
+
+sub json_writer {
+my ($filename, $data) = @_;
+
+return encode_json($data);
+}
+
+sub read_macdb {
+my () = @_;
+
+return cfs_read_file($macdb_filename);
+}
+
+sub write_macdb {
+my ($data) = @_;
+
+cfs_write_file($macdb_filename, $data);
+}
+
+sub add_cache_mac_ip {
+my ($mac, $ip) = @_;
+
+cfs_lock_file($macdb_filename, undef, sub {
+   my $db = read_macdb();
+   if (Net::IP::ip_is_ipv4($ip)) {
+   $db->{macs}->{$mac}->{ip4} = $ip;
+   } else {
+   $db->{macs}->{$mac}->{ip6} = $ip;
+   }
+   write_macdb($db);
+});
+warn "$@" if $@;
+}
+
+sub del_cache_mac_ip {
+my ($mac, $ip) = @_;
+
+cfs_lock_file($macdb_filename, undef, sub {
+   my $db = read_macdb();
+   if (Net::IP::ip_is_ipv4($ip)) {
+   delete $db->{macs}->{$mac}->{ip4};
+   } else {
+   delete $db->{macs}->{$mac}->{ip6};
+   }
+delete $db->{macs}->{$mac} if !defined($db->{macs}->{$mac}->{ip4}) && 
!defined($db->{macs}->{$mac}->{ip6});
+   write_macdb($db);
+});
+warn "$@" if $@;
+}
 
 sub sdn_ipams_config {
 my ($cfg, $id, $noerr) = @_;
@@ -39,8 +98,8 @@ sub config {
 }
 
 sub get_plugin_config {
-my ($vnet) = @_;
-my $ipamid = $vnet->{ipam};
+my ($zone) = @_;
+my $ipamid = $zone->{ipam};
 my $ipam_cfg = PVE::Network::SDN::Ipams::config();
 return $ipam_cfg->{ids}->{$ipamid};
 }
@@ -65,5 +124,20 @@ sub complete_sdn_vnet {
 return  $cmdname eq 'add' ? [] : [ 
PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
 }
 
+sub get_ips_from_mac {
+my ($mac, $zoneid, $zone) = @_;
+
+my $macdb = read_macdb();
+return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) if 
$macdb->{macs}->{$mac};
+
+my $plugin_config = get_plugin_config($zone);
+my $plugin = 
PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) = 
$plugin->get_ips_from_mac($plugin_config, $mac, $zoneid);
+
+write_macdb($macdb);
+
+return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6});
+}
+
 1;
 
diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm 
b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index f0e7168..91010bb 100644
--- a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -77,14 +77,20 @@ sub del_subnet {
 }
 
 sub add_ip {
-my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, 
$description, $is_gateway, $noerr) = @_;
+my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, 
$vmid, $is_gateway, $noerr) = @_;
 
 my $mask = $subnet->{mask};
 my $url = $plugin_config->{url};
 my $token = $plugin_config->{token};
 my $section = $plugin_config->{section};
 my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 
'Authorization' => "token $token"];
-$description .= " mac:$mac" if $mac && $description;
+
+my $description = undef;
+if ($is_gateway) {
+   $description = 'gateway'
+} elsif ($mac) {
+   $description = "mac:$mac";
+}
 
 my $params = { address => "$ip/$mask", dns_name => $hostname, description 
=> $description };
 
@@ -102,14 +108,20 @@ sub add_ip {
 }
 
 sub update_ip {
-my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, 
$description, $is_gateway, $noerr) = @_;
+my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, 
$vmid, $is_gateway, $noerr) = @_;
 
 my $mask = $subnet->{mask};
 my $url = $plugin_config->{url};
 my $token = 

[pve-devel] [WIP v3 pve-network 05/22] sdn: subnet: vnet: refactor IPAM related methods

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/SubnetPlugin.pm |  3 +-
 src/PVE/Network/SDN/Subnets.pm  | 50 ++-
 src/PVE/Network/SDN/Vnets.pm| 95 +
 3 files changed, 92 insertions(+), 56 deletions(-)

diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm 
b/src/PVE/Network/SDN/SubnetPlugin.pm
index a4adae8..88933f5 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -172,8 +172,7 @@ sub on_update_hook {
}
 if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
my $hostname = "$vnetid-gw";
-   my $description = "gateway";
-   PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, 
$gateway, $hostname, $mac, $description, 1);
+   PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, 
$gateway, $hostname, $mac, undef, 1);
}
 
#delete old gateway after update
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index 6e74de1..b05b3d9 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -98,14 +98,12 @@ sub get_subnet {
 }
 
 sub find_ip_subnet {
-my ($ip, $mask, $subnets) = @_;
+my ($ip, $subnets) = @_;
 
 my $subnet = undef;
 my $subnetid = undef;
 
 foreach my $id (sort keys %{$subnets}) {
-
-   next if $mask ne $subnets->{$id}->{mask};
my $cidr = $subnets->{$id}->{cidr};
my $subnet_matcher = subnet_matcher($cidr);
next if !$subnet_matcher->($ip);
@@ -207,12 +205,11 @@ sub del_subnet {
 $plugin->del_subnet($plugin_config, $subnetid, $subnet);
 }
 
-sub next_free_ip {
-my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = 
@_;
+sub add_next_free_ip {
+my ($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, 
$dhcprange) = @_;
 
 my $cidr = undef;
 my $ip = undef;
-$description = '' if !$description;
 
 my $ipamid = $zone->{ipam};
 my $dns = $zone->{dns};
@@ -230,10 +227,28 @@ sub next_free_ip {
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = 
PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
-   $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, 
$subnet, $hostname, $mac, $description);
-   ($ip, undef) = split(/\//, $cidr);
+   if ($dhcprange) {
+   my $data = {
+   mac => $mac,
+   hostname => $hostname,
+   vmid => $vmid,
+   };
+
+   my $dhcp_ranges = 
PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet);
+
+   foreach my $range (@$dhcp_ranges) {
+   $ip = $plugin->add_range_next_freeip($plugin_config, 
$subnet, $range, $data);
+   next if !$ip;
+   }
+   } else {
+   $ip = $plugin->add_next_freeip($plugin_config, $subnetid, 
$subnet, $hostname, $mac, $vmid);
+   }
};
+
die $@ if $@;
+
+   eval { PVE::Network::SDN::Ipams::add_cache_mac_ip($mac, $ip); };
+   warn $@ if $@;
 }
 
 eval {
@@ -250,15 +265,15 @@ sub next_free_ip {
#rollback
my $err = $@;
eval {
-   PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, 
$hostname)
+   PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, 
$hostname, $mac)
};
die $err;
 }
-return $cidr;
+return $ip;
 }
 
 sub add_ip {
-my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, 
$is_gateway, $skipdns) = @_;
+my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, 
$skipdns) = @_;
 
 return if !$subnet || !$ip; 
 
@@ -287,7 +302,7 @@ sub add_ip {
my $plugin = 
PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
 
eval {
-   $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, 
$mac, $description, $is_gateway);
+   $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, 
$mac, $vmid, $is_gateway);
};
die $@ if $@;
 }
@@ -304,14 +319,14 @@ sub add_ip {
#rollback
my $err = $@;
eval {
-   PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, 
$hostname)
+   PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, 
$hostname, $mac)
};
die $err;
 }
 }
 
 sub update_ip {
-my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, 
$description, $skipdns) = @_;
+my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, 
$skipdns) = @_;
 
 return if !$subnet || !$ip; 
 
@@ -338,7 +353,7 @@ sub update_ip {
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = 
PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
-  

[pve-devel] [WIP v3 qemu-server 22/22] vm_destroy: delete ip from ipam && dhcp

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

Co-Authored-By: Stefan Hanreich 
Signed-off-by: Stefan Hanreich 
---
 PVE/QemuServer.pm | 17 +
 1 file changed, 17 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index fecdb9c..c9c061c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -2342,6 +2342,9 @@ sub destroy_vm {
});
 }
 
+eval { delete_ifaces_ipams_ips($conf, $vmid)};
+warn $@ if $@;
+
 if (defined $replacement_conf) {
PVE::QemuConfig->write_config($vmid, $replacement_conf);
 } else {
@@ -6153,6 +6156,18 @@ sub cleanup_pci_devices {
 PVE::QemuServer::PCI::remove_pci_reservation($vmid);
 }
 
+sub cleanup_sdn_dhcp {
+my ($vmid, $conf) = @_;
+
+for my $k (keys %$conf) {
+   next if $k !~ /^net(\d+)/;
+   my $netconf = $conf->{$k};
+   my $net = PVE::QemuServer::parse_net($netconf);
+
+   PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, 
$net->{macaddr});
+}
+}
+
 sub vm_stop_cleanup {
 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
 
@@ -6186,6 +6201,8 @@ sub vm_stop_cleanup {
 
cleanup_pci_devices($vmid, $conf);
 
+   cleanup_sdn_dhcp($vmid, $conf);
+
vmconfig_apply_pending($vmid, $conf, $storecfg) if 
$apply_pending_changes;
 };
 warn $@ if $@; # avoid errors - just warn
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 08/22] sdn: dhcp: add dnsmasq plugin

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 debian/control  |   1 +
 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 198 
 2 files changed, 199 insertions(+)
 create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm

diff --git a/debian/control b/debian/control
index 8b720c3..4424096 100644
--- a/debian/control
+++ b/debian/control
@@ -24,6 +24,7 @@ Depends: libpve-common-perl (>= 5.0-45),
  ${misc:Depends},
  ${perl:Depends},
 Recommends: frr-pythontools (>= 8.5.1~), ifupdown2
+Suggests: dnsmasq
 Description: Proxmox VE's SDN (Software Defined Network) stack
  This package contains the Software Defined Network (tech preview) for
  Proxmox VE.
diff --git a/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm 
b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
new file mode 100644
index 000..21a6ddd
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
@@ -0,0 +1,198 @@
+package PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use strict;
+use warnings;
+
+use base qw(PVE::Network::SDN::Dhcp::Plugin);
+
+use Net::IP qw(:PROC);
+use PVE::Tools qw(file_set_contents run_command lock_file);
+
+use File::Copy;
+
+my $DNSMASQ_CONFIG_ROOT = '/etc/dnsmasq.d';
+my $DNSMASQ_DEFAULT_ROOT = '/etc/default';
+my $DNSMASQ_LEASE_ROOT = '/var/lib/misc';
+
+sub type {
+return 'dnsmasq';
+}
+
+sub del_ip_mapping {
+my ($class, $dhcpid, $mac) = @_;
+
+my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+my $ethers_tmp_file = "$ethers_file.tmp";
+
+my $removeFn = sub {
+   open(my $in, '<', $ethers_file) or die "Could not open file 
'$ethers_file' $!\n";
+   open(my $out, '>', $ethers_tmp_file) or die "Could not open file 
'$ethers_tmp_file' $!\n";
+
+while (my $line = <$in>) {
+   next if $line =~ m/^$mac/;
+   print $out $line;
+   }
+
+   close $in;
+   close $out;
+
+   move $ethers_tmp_file, $ethers_file;
+
+   chmod 0644, $ethers_file;
+};
+
+PVE::Tools::lock_file($ethers_file, 10, $removeFn);
+
+if ($@) {
+   warn "Unable to remove $mac from the dnsmasq configuration: $@\n";
+   return;
+}
+
+my $service_name = "dnsmasq\@$dhcpid";
+PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub add_ip_mapping {
+my ($class, $dhcpid, $mac, $ip) = @_;
+
+my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+my $ethers_tmp_file = "$ethers_file.tmp";
+
+my $appendFn = sub {
+   open(my $in, '<', $ethers_file) or die "Could not open file 
'$ethers_file' $!\n";
+   open(my $out, '>', $ethers_tmp_file) or die "Could not open file 
'$ethers_tmp_file' $!\n";
+
+while (my $line = <$in>) {
+   next if $line =~ m/^$mac/;
+   print $out $line;
+   }
+
+   print $out "$mac,$ip\n";
+   close $in;
+   close $out;
+   move $ethers_tmp_file, $ethers_file;
+   chmod 0644, $ethers_file;
+};
+
+PVE::Tools::lock_file($ethers_file, 10, $appendFn);
+
+if ($@) {
+   warn "Unable to add $mac/$ip to the dnsmasq configuration: $@\n";
+   return;
+}
+
+my $service_name = "dnsmasq\@$dhcpid";
+PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub configure_subnet {
+my ($class, $dhcpid, $subnet_config) = @_;
+
+die "No gateway defined for subnet $subnet_config->{id}"
+   if !$subnet_config->{gateway};
+
+my $tag = $subnet_config->{id};
+
+my @dnsmasq_config = (
+   "listen-address=$subnet_config->{gateway}",
+);
+
+my $option_string;
+if (ip_is_ipv6($subnet_config->{network})) {
+   $option_string = 'option6';
+   push @dnsmasq_config, "enable-ra";
+} else {
+   $option_string = 'option';
+   push @dnsmasq_config, 
"dhcp-option=tag:$tag,$option_string:router,$subnet_config->{gateway}";
+}
+
+push @dnsmasq_config, 
"dhcp-option=tag:$tag,$option_string:dns-server,$subnet_config->{'dhcp-dns-server'}"
+   if $subnet_config->{'dhcp-dns-server'};
+
+PVE::Tools::file_set_contents(
+   "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.conf",
+   join("\n", @dnsmasq_config) . "\n"
+);
+}
+
+sub configure_range {
+my ($class, $dhcpid, $subnet_config, $range_config) = @_;
+
+my $range_file = 
"$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.ranges.conf",
+my $tag = $subnet_config->{id};
+
+open(my $fh, '>>', $range_file) or die "Could not open file '$range_file' 
$!\n";
+print $fh 
"dhcp-range=set:$tag,$range_config->{'start-address'},$range_config->{'end-address'}\n";
+close $fh;
+}
+
+sub before_configure {
+my ($class, $dhcpid) = @_;
+
+my $config_directory = "$DNSMASQ_CONFIG_ROOT/$dhcpid";
+
+mkdir($config_directory, 755) if !-d $config_directory;
+
+my $default_config = 

[pve-devel] [WIP v3 qemu-server 20/22] vm_start : vm-network-scripts: get ip from ipam and add dhcp reservation

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

Signed-off-by: Stefan Hanreich 
---
 vm-network-scripts/pve-bridge | 5 +
 1 file changed, 5 insertions(+)

diff --git a/vm-network-scripts/pve-bridge b/vm-network-scripts/pve-bridge
index d37ce33..24efaad 100755
--- a/vm-network-scripts/pve-bridge
+++ b/vm-network-scripts/pve-bridge
@@ -10,6 +10,8 @@ use PVE::Network;
 my $have_sdn;
 eval {
 require PVE::Network::SDN::Zones;
+require PVE::Network::SDN::Vnets;
+require PVE::Network::SDN::Dhcp;
 $have_sdn = 1;
 };
 
@@ -44,6 +46,9 @@ my $net = PVE::QemuServer::parse_net($netconf);
 die "unable to parse network config '$netid'\n" if !$net;
 
 if ($have_sdn) {
+my ($ip4, $ip6) = 
PVE::Network::SDN::Vnets::get_ips_from_mac($net->{bridge}, $net->{macaddr});
+PVE::Network::SDN::Dhcp::add_mapping($net->{bridge}, $net->{macaddr}, 
$ip4, $ip6);
+
 PVE::Network::SDN::Zones::tap_create($iface, $net->{bridge});
 PVE::Network::SDN::Zones::tap_plug($iface, $net->{bridge}, $net->{tag}, 
$net->{firewall}, $net->{trunks}, $net->{rate});
 } else {
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 14/22] sdn: fix tests

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

Signed-off-by: Stefan Hanreich 
---
 src/test/run_test_subnets.pl | 8 +++-
 src/test/run_test_vnets.pl   | 4 ++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
index f6564e1..c98359a 100755
--- a/src/test/run_test_subnets.pl
+++ b/src/test/run_test_subnets.pl
@@ -109,6 +109,12 @@ foreach my $path (@plugins) {
my $ipam_config = read_sdn_config ("$path/ipam_config");
return $ipam_config;
},
+   add_cache_mac_ip => sub {
+   return;
+   },
+   del_cache_mac_ip => sub {
+   return;
+   }
 );
 
 ## add_subnet
@@ -192,7 +198,7 @@ foreach my $path (@plugins) {
 $expected = 
'{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}';
 
 eval {
-   $ip3 = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, 
$subnet, $hostname, $mac, $description);
+   $ip3 = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, 
$subnet, $hostname, $mac, $description);
 };
 
 if ($@) {
diff --git a/src/test/run_test_vnets.pl b/src/test/run_test_vnets.pl
index 5aeb676..dc9da67 100755
--- a/src/test/run_test_vnets.pl
+++ b/src/test/run_test_vnets.pl
@@ -231,7 +231,7 @@ foreach my $path (@plugins) {
 $expected = $ipam ? $cidr3 : undef;
 
 eval {
-   $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, 
$hostname, $mac, $description, $ipversion);
+   $result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, 
$hostname, $mac, $description);
 };
 
 if ($@) {
@@ -309,7 +309,7 @@ foreach my $path (@plugins) {
 $expected = $ipam ? $cidr1 : undef;
 
 eval {
-   $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, 
$hostname, $mac, $description, $ipversion);
+   $result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, 
$hostname, $mac, $description);
 };
 
 if ($@) {
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 10/22] api: add IPAM endpoints

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/API2/Network/SDN.pm   |   6 ++
 src/PVE/API2/Network/SDN/Ipam.pm  | 172 ++
 src/PVE/API2/Network/SDN/Makefile |   2 +-
 3 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm

diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm
index d216e48..551afcf 100644
--- a/src/PVE/API2/Network/SDN.pm
+++ b/src/PVE/API2/Network/SDN.pm
@@ -15,6 +15,7 @@ use PVE::Network::SDN;
 use PVE::API2::Network::SDN::Controllers;
 use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
+use PVE::API2::Network::SDN::Ipam;
 use PVE::API2::Network::SDN::Ipams;
 use PVE::API2::Network::SDN::Dns;
 
@@ -35,6 +36,11 @@ __PACKAGE__->register_method ({
 path => 'controllers',
 });
 
+__PACKAGE__->register_method ({
+subclass => "PVE::API2::Network::SDN::Ipam",
+path => 'ipam',
+});
+
 __PACKAGE__->register_method ({
 subclass => "PVE::API2::Network::SDN::Ipams",
 path => 'ipams',
diff --git a/src/PVE/API2/Network/SDN/Ipam.pm b/src/PVE/API2/Network/SDN/Ipam.pm
new file mode 100644
index 000..66131e3
--- /dev/null
+++ b/src/PVE/API2/Network/SDN/Ipam.pm
@@ -0,0 +1,172 @@
+package PVE::API2::Network::SDN::Ipam;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dhcp;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams::Plugin;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+name => 'dhcpindex',
+path => '',
+method => 'GET',
+description => 'List DHCP Mappings',
+protected => 1,
+permissions => {
+},
+parameters => {
+},
+returns => {
+   type => 'array',
+},
+code => sub {
+   my ($param) = @_;
+
+   my $ipam_plugin = PVE::Network::SDN::Ipams::Plugin->lookup('pve');
+   my $ipam_db = $ipam_plugin->read_db();
+
+   my $result = [];
+
+   for my $zone_id (keys %{$ipam_db->{zones}}) {
+   my $zone_config = PVE::Network::SDN::Zones::get_zone($zone_id, 1);
+next if !$zone_config || $zone_config->{ipam} ne 'pve' || 
!$zone_config->{dhcp};
+
+   my $zone = $ipam_db->{zones}->{$zone_id};
+
+   my $vnets = PVE::Network::SDN::Zones::get_vnets($zone_id, 1);
+
+   for my $subnet_cidr (keys %{$zone->{subnets}}) {
+   my $subnet = $zone->{subnets}->{$subnet_cidr};
+   my $ip = new Net::IP ($subnet_cidr) or die 'Found invalid CIDR 
in IPAM';
+
+   my $vnet = undef;
+   for my $vnet_id (keys %$vnets) {
+   eval {
+   my ($zone, $subnetid, $subnet_cfg, $ip) = 
PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip(
+   $vnet_id,
+   $ip->ip(),
+   );
+
+   $vnet = $subnet_cfg->{vnet};
+   };
+
+   last if $vnet;
+   }
+
+   for my $ip (keys %{$subnet->{ips}}) {
+   my $entry = $subnet->{ips}->{$ip};
+   $entry->{zone} = $zone_id;
+   $entry->{subnet} = $subnet_cidr;
+   $entry->{ip} = $ip;
+
+   if ($vnet) {
+   $entry->{vnet} = $vnet;
+   }
+
+   push @$result, $entry;
+   }
+   }
+   }
+
+   return $result;
+},
+});
+
+__PACKAGE__->register_method ({
+name => 'dhcpdelete',
+path => '{vnet}/{mac}',
+method => 'DELETE',
+description => 'Delete DHCP Mapping',
+protected => 1,
+permissions => {
+},
+parameters => {
+   additionalProperties => 0,
+   mac => { type => 'string' },
+   vnet => { type => 'string' },
+},
+returns => { type => 'null' },
+code => sub {
+   my ($param) = @_;
+
+   my $vnet = extract_param($param, 'vnet');
+   my $mac = extract_param($param, 'mac');
+
+   PVE::Network::SDN::Dhcp::remove_mapping($vnet, $mac);
+   PVE::Network::SDN::Vnets::del_ips_from_mac($vnet, $mac);
+
+   return undef;
+},
+});
+
+__PACKAGE__->register_method ({
+name => 'dhcpcreate',
+path => '{vnet}/{mac}',
+method => 'POST',
+description => 'Create DHCP Mapping',
+protected => 1,
+permissions => {
+},
+parameters => {
+   additionalProperties => 0,
+   mac => { type => 'string' },
+   vnet => { type => 'string' },
+   ip => { type => 'string' },
+},
+returns => { type => 'null' },
+code => sub {
+   my ($param) = @_;
+
+   my $vnet = extract_param($param, 'vnet');
+   my $mac = extract_param($param, 'mac');
+   my $ip = extract_param($param, 'ip');
+
+   

[pve-devel] [WIP v3 pve-manager 15/22] sdn: regenerate DHCP config on reload

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 PVE/API2/Network.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm
index 00d964a79..f39f04f52 100644
--- a/PVE/API2/Network.pm
+++ b/PVE/API2/Network.pm
@@ -660,6 +660,7 @@ __PACKAGE__->register_method({
 
if ($have_sdn) {
PVE::Network::SDN::generate_zone_config();
+   PVE::Network::SDN::generate_dhcp_config();
}
 
my $err = sub {
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 07/22] dhcp: add abstract class for DHCP plugins

2023-11-14 Thread Stefan Hanreich
Co-Authored-By: Alexandre Derumier 
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/Dhcp/Makefile  |  8 
 src/PVE/Network/SDN/Dhcp/Plugin.pm | 65 ++
 src/PVE/Network/SDN/Makefile   |  1 +
 3 files changed, 74 insertions(+)
 create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
 create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm

diff --git a/src/PVE/Network/SDN/Dhcp/Makefile 
b/src/PVE/Network/SDN/Dhcp/Makefile
new file mode 100644
index 000..6546513
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm Dnsmasq.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+   for i in ${SOURCES}; do install -D -m 0644 $$i 
${PERL5DIR}/PVE/Network/SDN/Dhcp/$$i; done
diff --git a/src/PVE/Network/SDN/Dhcp/Plugin.pm 
b/src/PVE/Network/SDN/Dhcp/Plugin.pm
new file mode 100644
index 000..7b9e9b7
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Plugin.pm
@@ -0,0 +1,65 @@
+package PVE::Network::SDN::Dhcp::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Cluster;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::SectionConfig);
+
+my $defaultData = {
+propertyList => {
+   type => {
+   description => "Plugin type.",
+   format => 'pve-configid',
+   type => 'string',
+   },
+},
+};
+
+sub private {
+return $defaultData;
+}
+
+sub add_ip_mapping {
+my ($class, $dhcp_config, $mac, $ip) = @_;
+die 'implement in sub class';
+}
+
+sub del_ip_mapping {
+my ($class, $dhcp_config, $mac) = @_;
+die 'implement in sub class';
+}
+
+sub configure_range {
+my ($class, $dhcp_config, $subnet_config, $range_config) = @_;
+die 'implement in sub class';
+}
+
+sub configure_subnet {
+my ($class, $dhcp_config, $subnet_config) = @_;
+die 'implement in sub class';
+}
+
+sub before_configure {
+my ($class, $dhcp_config) = @_;
+die 'implement in sub class';
+}
+
+sub after_configure {
+my ($class, $dhcp_config) = @_;
+die 'implement in sub class';
+}
+
+sub before_regenerate {
+my ($class) = @_;
+die 'implement in sub class';
+}
+
+sub after_regenerate {
+my ($class, $dhcp_config) = @_;
+die 'implement in sub class';
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 92cfcd0..848f7d4 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -10,4 +10,5 @@ install:
make -C Zones install
make -C Ipams install
make -C Dns install
+   make -C Dhcp install
 
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-network 03/22] subnet: add dhcp options

2023-11-14 Thread Stefan Hanreich
Signed-off-by: Stefan Hanreich 
---
 src/PVE/Network/SDN/SubnetPlugin.pm | 29 +
 src/PVE/Network/SDN/Subnets.pm  | 23 +++
 2 files changed, 52 insertions(+)

diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm 
b/src/PVE/Network/SDN/SubnetPlugin.pm
index 15b370f..a4adae8 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -61,6 +61,19 @@ sub private {
 return $defaultData;
 }
 
+my $dhcp_range_fmt = {
+'start-address' => {
+   type => 'ip',
+   description => 'Start address for the DHCP IP range',
+},
+'end-address' => {
+   type => 'ip',
+   description => 'End address for the DHCP IP range',
+},
+};
+
+PVE::JSONSchema::register_format('pve-sdn-dhcp-range', $dhcp_range_fmt);
+
 sub properties {
 return {
 vnet => {
@@ -84,6 +97,20 @@ sub properties {
 type => 'string', format => 'dns-name',
 description => "dns domain zone prefix  ex: 'adm' -> 
.adm.mydomain.com",
 },
+   'dhcp-range' => {
+   type => 'array',
+   description => 'A list of DHCP ranges for this subnet',
+   optional => 1,
+   items => {
+   type => 'string',
+   format => 'pve-sdn-dhcp-range',
+   }
+   },
+   'dhcp-dns-server' => {
+   type => 'ip',
+   description => 'IP address for the DNS server',
+   optional => 1,
+   },
 };
 }
 
@@ -94,6 +121,8 @@ sub options {
 #  routes => { optional => 1 },
snat => { optional => 1 },
dnszoneprefix => { optional => 1 },
+   'dhcp-range' => { optional => 1 },
+   'dhcp-dns-server' => { optional => 1 },
 };
 }
 
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index f654d3a..6e74de1 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -8,6 +8,7 @@ use Net::IP;
 use NetAddr::IP qw(:lower);
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::JSONSchema qw(parse_property_string);
 use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::Ipams;
 
@@ -36,6 +37,28 @@ sub sdn_subnets_config {
 return $scfg;
 }
 
+sub get_dhcp_ranges {
+my ($subnet_config) = @_;
+
+my @dhcp_ranges = ();
+
+if ($subnet_config->{'dhcp-range'}) {
+   foreach my $element (@{$subnet_config->{'dhcp-range'}}) {
+   my $dhcp_range = eval { parse_property_string('pve-sdn-dhcp-range', 
$element) };
+
+   if ($@ || !$dhcp_range) {
+   warn "Unable to parse dhcp-range string: $element\n";
+   warn "$@\n" if $@;
+   next;
+   }
+
+   push @dhcp_ranges, $dhcp_range;
+   }
+}
+
+return \@dhcp_ranges;
+}
+
 sub config {
 my ($running) = @_;
 
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [WIP v3 pve-cluster 01/22] add priv/macs.db

2023-11-14 Thread Stefan Hanreich
From: Alexandre Derumier 

use to cache mac-ip list association.

can be use by external ipam, firewall,etc for fast lookup

Signed-off-by: Alexandre Derumier 
---
 src/PVE/Cluster.pm  | 1 +
 src/pmxcfs/status.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/PVE/Cluster.pm b/src/PVE/Cluster.pm
index cfa2583..80c4bc0 100644
--- a/src/PVE/Cluster.pm
+++ b/src/PVE/Cluster.pm
@@ -62,6 +62,7 @@ my $observed = {
 'priv/token.cfg' => 1,
 'priv/acme/plugins.cfg' => 1,
 'priv/ipam.db' => 1,
+'priv/macs.db' => 1,
 '/qemu-server/' => 1,
 '/openvz/' => 1,
 '/lxc/' => 1,
diff --git a/src/pmxcfs/status.c b/src/pmxcfs/status.c
index c8094ac..078602e 100644
--- a/src/pmxcfs/status.c
+++ b/src/pmxcfs/status.c
@@ -89,6 +89,7 @@ static memdb_change_t memdb_change_array[] = {
{ .path = "priv/tfa.cfg" },
{ .path = "priv/token.cfg" },
{ .path = "priv/ipam.db" },
+   { .path = "priv/macs.db" },
{ .path = "datacenter.cfg" },
{ .path = "vzdump.cron" },
{ .path = "vzdump.conf" },
-- 
2.39.2


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] applied-series: [PATCH installer 0/2] minimal changes for a working serial installation

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 18:31 schrieb Stoiko Ivanov:
> patch 1/2 is a tiny issue I ran into while building  the installer
> 
> patch 2/2 adds the correct console parameter for serial installations
> patch 2/2 needs some further work (the other kernel cmdline parameter
> matchings are probably wrong as well, additionally #4747 would be a better
> and more general solution for what to add to the cmdline from the install
> environment) - I'll try to send a follow-up tomorrow
> 
> Stoiko Ivanov (2):
>   d/control: add libgtk3-perl to B-D
>   serial install: fix console parameter parsing
> 
>  Proxmox/Install.pm| 2 +-
>  Proxmox/Install/Config.pm | 2 +-
>  debian/control| 1 +
>  3 files changed, 3 insertions(+), 2 deletions(-)
> 


applied series, thanks!


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [PATCH v2 container] api: network: get interfaces from containers

2023-11-14 Thread DERUMIER, Alexandre
 Message initial 
De: Thomas Lamprecht 
À: Proxmox VE development discussion ,
"DERUMIER, Alexandre" 
Objet: Re: [pve-devel] [PATCH v2 container] api: network: get
interfaces from containers
Date: 14/11/2023 18:00:30

Am 14/11/2023 um 17:43 schrieb DERUMIER, Alexandre:
> Any chance to have this commit applied for pve 8.1 ? 

>>Would be fine for me. Did you test it?

Not yet. I will test it tomorrow. (Seem to be quite small).




___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [PATCH installer 2/2] serial install: fix console parameter parsing

2023-11-14 Thread Stoiko Ivanov
The regex matching in Proxmox::Install::Config was blindly copied from
above - so the other parameters are also likely to not get recognized
if they are the last on the cmdline

Signed-off-by: Stoiko Ivanov 
---
 Proxmox/Install.pm| 2 +-
 Proxmox/Install/Config.pm | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Proxmox/Install.pm b/Proxmox/Install.pm
index a289037..66adb2d 100644
--- a/Proxmox/Install.pm
+++ b/Proxmox/Install.pm
@@ -1154,7 +1154,7 @@ _EOD
update_progress(0.8, 0.95, 1, "make system bootable");
my $console_param='';
if (my $console = Proxmox::Install::Config::get_console()) {
-   my $console_param="console=$console";;
+   $console_param="console=$console";
my $console_snippet = "GRUB_CMDLINE_LINUX=\"\$GRUB_CMDLINE_LINUX 
$console_param\"";
file_write_all("$targetdir/etc/default/grub.d/console.cfg", 
$console_snippet);
}
diff --git a/Proxmox/Install/Config.pm b/Proxmox/Install/Config.pm
index 55e53c7..5e80255 100644
--- a/Proxmox/Install/Config.pm
+++ b/Proxmox/Install/Config.pm
@@ -43,7 +43,7 @@ my sub parse_kernel_cmdline {
}
 }
 
-if ($cmdline =~ m/console=(\S+)[\s\n]/i) {
+if ($cmdline =~ m/console=(\S+)[\s\n]?/i) {
$cfg->{console} = $1;
 }
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH installer 0/2] minimal changes for a working serial installation

2023-11-14 Thread Stoiko Ivanov
patch 1/2 is a tiny issue I ran into while building  the installer

patch 2/2 adds the correct console parameter for serial installations
patch 2/2 needs some further work (the other kernel cmdline parameter
matchings are probably wrong as well, additionally #4747 would be a better
and more general solution for what to add to the cmdline from the install
environment) - I'll try to send a follow-up tomorrow

Stoiko Ivanov (2):
  d/control: add libgtk3-perl to B-D
  serial install: fix console parameter parsing

 Proxmox/Install.pm| 2 +-
 Proxmox/Install/Config.pm | 2 +-
 debian/control| 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH installer 1/2] d/control: add libgtk3-perl to B-D

2023-11-14 Thread Stoiko Ivanov
With the first tests entering the installer in
9a0d66cb36d395a1186904132aed1d5dc33a0937

we now need libgtk3-perl during package-building with `make deb`

Signed-off-by: Stoiko Ivanov 
---
 debian/control | 1 +
 1 file changed, 1 insertion(+)

diff --git a/debian/control b/debian/control
index d77b12a..9057f59 100644
--- a/debian/control
+++ b/debian/control
@@ -5,6 +5,7 @@ Maintainer: Proxmox Support Team 
 Build-Depends: cargo:native,
debhelper-compat (= 12),
iso-codes,
+   libgtk3-perl,
libpve-common-perl,
librsvg2-bin,
librust-cursive+termion-backend-dev (>= 0.20.0),
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [PATCH v2 container] api: network: get interfaces from containers

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 17:43 schrieb DERUMIER, Alexandre:
> Any chance to have this commit applied for pve 8.1 ? 

Would be fine for me. Did you test it?




___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [PATCH v2 container] api: network: get interfaces from containers

2023-11-14 Thread DERUMIER, Alexandre
Hi,

Any chance to have this commit applied for pve 8.1 ? 

I have a customer who's need this feature.

 Message initial 
De: Leo Nunner 
Répondre à: Proxmox VE development discussion 
À: pve-devel@lists.proxmox.com
Objet: [pve-devel] [PATCH v2 container] api: network: get interfaces
from containers
Date: 15/06/2023 11:43:31

Adds an 'interfaces' endpoint in the API
(/nodes/{node}/lxc/{vmid}/interfaces'), which returns a list of
interface names, together with a MAC, IPv4 and IPv6 address. This list
may be expanded in the future. Note that this is only returned for
*running* containers, stopped containers simply return an empty list.

Signed-off-by: Leo Nunner 
---
 src/PVE/API2/LXC.pm | 50 +
 src/PVE/LXC.pm  | 26 +++
 2 files changed, 76 insertions(+)

diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm
index 28d14de..8839105 100644
--- a/src/PVE/API2/LXC.pm
+++ b/src/PVE/API2/LXC.pm
@@ -2510,6 +2510,56 @@ __PACKAGE__->register_method({
    return PVE::GuestHelpers::config_with_pending_array($conf,
$pending_delete_hash);
 }});
 
+__PACKAGE__->register_method({
+    name => 'ip',
+    path => '{vmid}/interfaces',
+    method => 'GET',
+    protected => 1,
+    permissions => {
+   check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
+    },
+    description => 'Get IP addresses of the specified container
interface.',
+    parameters => {
+   additionalProperties => 0,
+   properties => {
+       node => get_standard_option('pve-node'),
+       vmid => get_standard_option('pve-vmid', { completion =>
\::LXC::complete_ctid }),
+   },
+    },
+    returns => {
+   type => "array",
+   items => {
+       type => 'object',
+       properties => {
+   name => {
+       type => 'string',
+       description => 'The name of the interface',
+       optional => 0,
+   },
+   hwaddr => {
+       type => 'string',
+       description => 'The MAC address of the interface',
+       optional => 0,
+   },
+   inet => {
+       type => 'string',
+       description => 'The IPv4 address of the
interface',
+       optional => 1,
+   },
+   inet6 => {
+       type => 'string',
+       description => 'The IPv6 address of the
interface',
+       optional => 1,
+   },
+       }
+   },
+    },
+    code => sub {
+   my ($param) = @_;
+
+   return PVE::LXC::get_interfaces($param->{vmid});
+    }});
+
 __PACKAGE__->register_method({
 name => 'mtunnel',
 path => '{vmid}/mtunnel',
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index a531ea5..611f5c2 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1036,6 +1036,32 @@ sub hotplug_net {
 PVE::LXC::Config->write_config($vmid, $conf);
 }
 
+sub get_interfaces {
+    my ($vmid) = @_;
+
+    my $pid = eval { find_lxc_pid($vmid); };
+    return if $@;
+
+    my $output;
+    # enters the network namespace of the container and executes 'ip
a'
+    run_command(['nsenter', '-t', $pid, '--net', '--', 'ip', '--json',
'a'],
+   outfunc => sub { $output .= shift; });
+
+    my $config = JSON::decode_json($output);
+
+    my $res;
+    for my $interface ($config->@*) {
+   my $obj = { name => $interface->{ifname} };
+   for my $ip ($interface->{addr_info}->@*) {
+       $obj->{$ip->{family}} = $ip->{local} . "/" . $ip-
>{prefixlen};
+   }
+   $obj->{hwaddr} = $interface->{address};
+   push @$res, $obj
+    }
+
+    return $res;
+}
+
 sub update_ipconfig {
 my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
 

___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] applied: [PATCH v2 debcargo-conf 02/52] update lettre to 0.11.1

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 13:59 schrieb Lukas Wagner:
> Signed-off-by: Lukas Wagner 
> ---
>  src/lettre/debian/changelog   | 10 +++
>  .../debian/patches/downgrade_fastrand.patch   | 13 
>  .../debian/patches/downgrade_idna.patch   | 13 
>  src/lettre/debian/patches/downgrade_url.patch | 13 
>  .../patches/remove_unused_features.patch  | 69 ++-
>  src/lettre/debian/patches/series  |  4 +-
>  .../patches/upgrade_quoted_printable.patch| 13 
>  7 files changed, 88 insertions(+), 47 deletions(-)
>  create mode 100644 src/lettre/debian/patches/downgrade_fastrand.patch
>  create mode 100644 src/lettre/debian/patches/downgrade_idna.patch
>  create mode 100644 src/lettre/debian/patches/downgrade_url.patch
>  delete mode 100644 src/lettre/debian/patches/upgrade_quoted_printable.patch
> 
>

applied, thanks!


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] applied: [PATCH v2 debcargo-conf 01/52] cherry-pick chumsky 0.9.2 from debian unstable

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 13:59 schrieb Lukas Wagner:
> Signed-off-by: Lukas Wagner 
> ---
>  src/chumsky/debian/changelog   |  5 +++
>  src/chumsky/debian/copyright   | 39 +
>  src/chumsky/debian/copyright.debcargo.hint | 51 ++
>  src/chumsky/debian/debcargo.toml   |  2 +
>  4 files changed, 97 insertions(+)
>  create mode 100644 src/chumsky/debian/changelog
>  create mode 100644 src/chumsky/debian/copyright
>  create mode 100644 src/chumsky/debian/copyright.debcargo.hint
>  create mode 100644 src/chumsky/debian/debcargo.toml
> 
>

applied, thanks!


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] applied-series: [PATCH widget-toolkit/manager] improve combogrid default value handling

2023-11-14 Thread Thomas Lamprecht
Am 19/07/2023 um 14:11 schrieb Dominik Csapak:
> pve-manager:
> 
> Dominik Csapak (3):
>   ui: ipset: make ip/cidr required
>   ui: don't set the default value of combogrids to ''
>   ui: don't set the default value of combogrids to []
> 

applied those now too, thanks!



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] applied: [PATCH v2 proxmox-widget-toolkit manager pmg-gui 0/4] #4442: impl firewall log filtering

2023-11-14 Thread Thomas Lamprecht
Am 09/08/2023 um 12:55 schrieb Christian Ebner:
> This series is send to pmg-devel and pve-devel list, as it changes the
> DateTimeField used by Proxmox Virtual Environment and Proxmox Mail
> Gateway.
> 
> This series of patches implements functionality to extend the firewall
> log panel to filter the logs by date and time.
> 
> The series consists of patches for the proxmox-widget-toolkit and
> patches for pve-manager and pmg-gui which depend on the changes to
> proxmox-widget-toolkit.
> 
> proxmox-widget-toolkit:
> The DateTimeField component is reworked to be more declarative and
> formula and data bindings are utilized to update state changes between
> parent and child components.
> The LogView component is extended to control if the date/time based
> filtering should be shown (as for example the Ceph logs use the same
> panel, but filters should not be shown), and to configure the submit
> values for api calls.
> 
> pve-manager:
> The neccessary configs are set to show the filters in the panel and the
> correct submit format for api call.
> 
> pmg-gui:
> Revert a commit, introduced to fix a side effect of one of the patches
> from the previous version of the patches, which got applied.
> 
> proxmox-widget-toolkit:
> Christian Ebner (2):
>   fix #4442: adapt DateTimeField to be more declarative
>   fix #4442: Extend LogView for firewall datetime filtering
> 
>  src/form/DateTimeField.js | 281 --
>  src/panel/LogView.js  |  89 ++--
>  2 files changed, 201 insertions(+), 169 deletions(-)
> 
> pve-manager:
> Christian Ebner (1):
>   fix #4442: Add date-time filtering for firewall logs
> 
>  www/manager6/node/Config.js | 2 ++
>  www/manager6/qemu/Config.js | 2 ++
>  2 files changed, 4 insertions(+)
> 
> pmg-gui:
> Christian Ebner (1):
>   Revert "fix tracking center with newer proxmox-widget-toolkit"
> 
>  js/MailTracker.js | 2 --
>  1 file changed, 2 deletions(-)
> 


applied, thanks!


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] applied: [PATCH kernel] backport UBSAN fixes for amdgpu

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 13:14 schrieb Fiona Ebner:
> to silence array-index-out-of-bounds warnings for dynamically-sized
> arrays. All commits applied cleanly and just replace array[1] with
> array[].
> 
> Signed-off-by: Fiona Ebner 
> ---
>  ...N-array-index-out-of-bounds-for-SMU7.patch |  63 
>  ...N-array-index-out-of-bounds-for-Pola.patch |  76 +
>  ...N-array-index-out-of-bounds-for-Powe.patch | 146 ++
>  3 files changed, 285 insertions(+)
>  create mode 100644 
> patches/kernel/0014-drm-amd-Fix-UBSAN-array-index-out-of-bounds-for-SMU7.patch
>  create mode 100644 
> patches/kernel/0015-drm-amd-Fix-UBSAN-array-index-out-of-bounds-for-Pola.patch
>  create mode 100644 
> patches/kernel/0016-drm-amd-Fix-UBSAN-array-index-out-of-bounds-for-Powe.patch
> 
>

applied, thanks!


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH storage] fix #5008: prevent adding pbs storage with invalid namespace

2023-11-14 Thread Philipp Hufnagl
Currently, when adding a PBS storage with a namespace that does not
exist, the storage gets added normally, but browsing/using it only
returns a cryptic error message.

This change checks if the namespace entered when adding is valid and
prompts an error if it is not. If no namespace is provided, the storage
will be added without error.

Signed-off-by: Philipp Hufnagl 
---
 src/PVE/Storage/PBSPlugin.pm | 21 -
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Storage/PBSPlugin.pm b/src/PVE/Storage/PBSPlugin.pm
index 4320974..aceb2c4 100644
--- a/src/PVE/Storage/PBSPlugin.pm
+++ b/src/PVE/Storage/PBSPlugin.pm
@@ -817,6 +817,17 @@ sub scan_datastores {
 return $response;
 }
 
+sub scan_namespaces {
+my ($scfg, $datastore, $password) = @_;
+
+my $conn = pbs_api_connect($scfg, $password);
+
+my $namespaces = eval { 
$conn->get("/api2/json/admin/datastore/$datastore/namespace", {}); };
+die "error fetching namespaces - $@" if $@;
+
+return $namespaces;
+}
+
 sub activate_storage {
 my ($class, $storeid, $scfg, $cache) = @_;
 
@@ -826,10 +837,18 @@ sub activate_storage {
 die "$storeid: $@" if $@;
 
 my $datastore = $scfg->{datastore};
+my $namespace = $scfg->{namespace};
 
 for my $ds (@$datastores) {
if ($ds->{store} eq $datastore) {
-   return 1;
+   return 1 if !defined($namespace);
+   my $namespaces = eval { scan_namespaces($scfg, $datastore, 
$password) };
+   for my $ns (@$namespaces) {
+   if ($ns->{ns} eq $namespace) {
+   return 1;
+   }
+   }
+   die "$storeid: Cannot find namespace '$namespace', check 
permissions and existence!\n";
}
 }
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [RFC common 2/2] fix #4501: next unused port: work around issue with too short expiretime

2023-11-14 Thread Fiona Ebner
Am 14.11.23 um 15:02 schrieb Fiona Ebner:
> For QEMU migration via TCP, there's a bit of time between port
> reservation and usage, because currently, the port needs to be
> reserved before doing a fork, where the systemd scope needs to be set
> up and swtpm might need to be started before the QEMU binary can be
> invoked and actually use the port.
> 
> To improve the situation, get the latest port recorded in the
> reservation file and start trying from the next port, wrapping around
> when hitting the end. Drastically reduces the chances to run into a
> conflict, because after a given port reservation, all other ports are
> tried first before returning to that port.

Sorry, this is not true. It can be that in the meantime, a port for a
different range is reserved and that will remove the reservation for the
port in the migration range if expired. So we'd need to change the code
to remove only reservations from the current range to not lose track of
the latest previously used migration port.


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [PATCH acme-rs/backup/perl-rs/pmg-api 0/8] add external account binding to pmg and pbs

2023-11-14 Thread Folke Gleumes
Please ignore this, wrong mailing list

On Tue, 2023-11-14 at 15:07 +0100, Folke Gleumes wrote:
> Following the implementation for pve [0], this implements external
> account
> binding for pmg and pbs.
> 
> For pmg, the tos endpoint was replaced with a meta endpoint, for pbs
> this was not necessary, although it might be in the future if the
> functionality is introduced in the gui.
> 
> Similar to the pve implementation, the cli will ask for eab
> credentials
> if the ca requires it, or optionally if the user provided a custom
> directory url.
> 
> The patches were tested against pebble with eab and le-staging +
> pebble
> without eab to ensure no regression have taken place.
> 
> [0]
> https://lists.proxmox.com/pipermail/pve-devel/2023-October/059726.html
> 
> acme-rs:
> Folke Gleumes (2):
>   add external account binding
>   add meta fields returned by the directory
> 
>  src/account.rs   | 28 +++-
>  src/client.rs    |  6 -
>  src/directory.rs | 25 --
>  src/eab.rs   | 66
> 
>  src/error.rs | 10 
>  src/lib.rs   |  1 +
>  6 files changed, 127 insertions(+), 9 deletions(-)
>  create mode 100644 src/eab.rs
> 
> backup:
> Folke Gleumes (2):
>   acme: api: add eab options to api
>   cli: acme: add possibility to set eab via the cli
> 
>  src/acme/client.rs |  9 +++-
>  src/api2/config/acme.rs    | 35 +--
>  src/bin/proxmox_backup_manager/acme.rs | 61 +---
> --
>  3 files changed, 89 insertions(+), 16 deletions(-)
> 
> perl-rs:
> Folke Gleumes (1):
>   acme: add eab fields for pmg
> 
>  pmg-rs/src/acme.rs | 18 +-
>  1 file changed, 13 insertions(+), 5 deletions(-)
> 
> pmg-api:
> Folke Gleumes (3):
>   api: acme: add eab parameters
>   api: acme: deprecate tos endpoint in favor of new meta endpoint
>   cli: acme: expose acme eab options on the cli
> 
>  src/PMG/API2/ACME.pm | 75
> ++--
>  src/PMG/CLI/pmgconfig.pm | 29 ++--
>  2 files changed, 99 insertions(+), 5 deletions(-)



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


[pve-devel] [PATCH backup 4/8] cli: acme: add possibility to set eab via the cli

2023-11-14 Thread Folke Gleumes
If the ca demands external account binding credentials, the user will be
asked for them. If a custom directory is used, the user will be asked if
eab should be used.

Signed-off-by: Folke Gleumes 
---
 src/acme/client.rs |  2 +-
 src/bin/proxmox_backup_manager/acme.rs | 51 +-
 2 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/src/acme/client.rs b/src/acme/client.rs
index 1396eb2c..6130748b 100644
--- a/src/acme/client.rs
+++ b/src/acme/client.rs
@@ -577,7 +577,7 @@ impl AcmeClient {
 Self::execute( self.http_client, request,  self.nonce).await
 }
 
-async fn directory( self) -> Result<, Error> {
+pub async fn directory( self) -> Result<, Error> {
 Ok(Self::get_directory(
  self.http_client,
 _url,
diff --git a/src/bin/proxmox_backup_manager/acme.rs 
b/src/bin/proxmox_backup_manager/acme.rs
index 17ca5958..f3e62115 100644
--- a/src/bin/proxmox_backup_manager/acme.rs
+++ b/src/bin/proxmox_backup_manager/acme.rs
@@ -103,8 +103,8 @@ async fn register_account(
 contact: String,
 directory: Option,
 ) -> Result<(), Error> {
-let directory = match directory {
-Some(directory) => directory,
+let (directory_url, custom_directory) = match directory {
+Some(directory) => (directory, true),
 None => {
 println!("Directory endpoints:");
 for (i, dir) in KNOWN_ACME_DIRECTORIES.iter().enumerate() {
@@ -122,12 +122,12 @@ async fn register_account(
 
 match input.trim().parse::() {
 Ok(n) if n < KNOWN_ACME_DIRECTORIES.len() => {
-break KNOWN_ACME_DIRECTORIES[n].url.to_owned();
+break (KNOWN_ACME_DIRECTORIES[n].url.to_owned(), 
false);
 }
 Ok(n) if n == KNOWN_ACME_DIRECTORIES.len() => {
 input.clear();
 std::io::stdin().read_line( input)?;
-break input.trim().to_owned();
+break (input.trim().to_owned(), true);
 }
 _ => eprintln!("Invalid selection."),
 }
@@ -140,9 +140,13 @@ async fn register_account(
 }
 };
 
-println!("Attempting to fetch Terms of Service from {:?}", directory);
-let mut client = AcmeClient::new(directory.clone());
-let tos_agreed = if let Some(tos_url) = 
client.terms_of_service_url().await? {
+println!(
+"Attempting to fetch Terms of Service from {:?}",
+directory_url
+);
+let mut client = AcmeClient::new(directory_url.clone());
+let directory = client.directory().await?;
+let tos_agreed = if let Some(tos_url) = directory.terms_of_service_url() {
 println!("Terms of Service: {}", tos_url);
 print!("Do you agree to the above terms? [y|N]: ");
 std::io::stdout().flush()?;
@@ -154,7 +158,36 @@ async fn register_account(
 true
 };
 
-println!("Attempting to register account with {:?}...", directory);
+let mut eab_enabled = directory.external_account_binding_required();
+if !eab_enabled && custom_directory {
+print!("Do you want to use external account binding? [y|N]: ");
+std::io::stdout().flush()?;
+let mut input = String::new();
+std::io::stdin().read_line( input)?;
+eab_enabled = input.trim().eq_ignore_ascii_case("y");
+} else if eab_enabled {
+println!("The CA requires external account binding.");
+}
+
+let eab_creds = if eab_enabled {
+println!("You should have received a key id and a key from your CA.");
+
+print!("Enter EAB key id: ");
+std::io::stdout().flush()?;
+let mut eab_kid = String::new();
+std::io::stdin().read_line( eab_kid)?;
+
+print!("Enter EAB key: ");
+std::io::stdout().flush()?;
+let mut eab_hmac_key = String::new();
+std::io::stdin().read_line( eab_hmac_key)?;
+
+Some((eab_kid.trim().to_owned(), eab_hmac_key.trim().to_owned()))
+} else {
+None
+};
+
+println!("Attempting to register account with {:?}...", directory_url);
 
 let account = api2::config::acme::do_register_account(
  client,
@@ -162,7 +195,7 @@ async fn register_account(
 tos_agreed,
 contact,
 None,
-None,
+eab_creds,
 )
 .await?;
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH backup 3/8] acme: api: add eab options to api

2023-11-14 Thread Folke Gleumes
Optionally allow for setting external account binding credentials at the
account registration endpoint.

Signed-off-by: Folke Gleumes 
---
 src/acme/client.rs |  7 +-
 src/api2/config/acme.rs| 35 +++---
 src/bin/proxmox_backup_manager/acme.rs | 12 ++---
 3 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/src/acme/client.rs b/src/acme/client.rs
index 46566210..1396eb2c 100644
--- a/src/acme/client.rs
+++ b/src/acme/client.rs
@@ -116,6 +116,7 @@ impl AcmeClient {
 tos_agreed: bool,
 contact: Vec,
 rsa_bits: Option,
+eab_creds: Option<(String, String)>,
 ) -> Result<&'a Account, anyhow::Error> {
 self.tos = if tos_agreed {
 self.terms_of_service_url().await?.map(str::to_owned)
@@ -123,10 +124,14 @@ impl AcmeClient {
 None
 };
 
-let account = Account::creator()
+let mut account = Account::creator()
 .set_contacts(contact)
 .agree_to_tos(tos_agreed);
 
+if let Some((eab_kid, eab_hmac_key)) = eab_creds {
+account = account.set_eab_credentials(eab_kid, eab_hmac_key)?;
+}
+
 let account = if let Some(bits) = rsa_bits {
 account.generate_rsa_key(bits)?
 } else {
diff --git a/src/api2/config/acme.rs b/src/api2/config/acme.rs
index 1954318b..8f010027 100644
--- a/src/api2/config/acme.rs
+++ b/src/api2/config/acme.rs
@@ -182,6 +182,16 @@ fn account_contact_from_string(s: ) -> Vec {
 description: "The ACME Directory.",
 optional: true,
 },
+eab_kid: {
+type: String,
+description: "Key Identifier for External Account Binding.",
+optional: true,
+},
+eab_hmac_key: {
+type: String,
+description: "HMAC Key for External Account Binding.",
+optional: true,
+}
 },
 },
 access: {
@@ -196,6 +206,8 @@ fn register_account(
 contact: String,
 tos_url: Option,
 directory: Option,
+eab_kid: Option,
+eab_hmac_key: Option,
 rpcenv:  dyn RpcEnvironment,
 ) -> Result {
 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
@@ -204,6 +216,15 @@ fn register_account(
 AcmeAccountName::from_string_unchecked("default".to_string())
 });
 
+// TODO: this should be done via the api definition, but
+// the api macro currently lacks this ability (2023-11-06)
+if eab_kid.is_some() ^ eab_hmac_key.is_some() {
+http_bail!(
+BAD_REQUEST,
+"either both or none of 'eab_kid' and 'eab_hmac_key' have to be 
set."
+);
+}
+
 if Path::new(::config::acme::account_path()).exists() {
 http_bail!(BAD_REQUEST, "account {} already exists", name);
 }
@@ -224,8 +245,15 @@ fn register_account(
 
 task_log!(worker, "Registering ACME account '{}'...", );
 
-let account =
-do_register_account( client, , tos_url.is_some(), 
contact, None).await?;
+let account = do_register_account(
+ client,
+,
+tos_url.is_some(),
+contact,
+None,
+eab_kid.zip(eab_hmac_key),
+)
+.await?;
 
 task_log!(
 worker,
@@ -244,10 +272,11 @@ pub async fn do_register_account<'a>(
 agree_to_tos: bool,
 contact: String,
 rsa_bits: Option,
+eab_creds: Option<(String, String)>,
 ) -> Result<&'a Account, Error> {
 let contact = account_contact_from_string();
 client
-.new_account(name, agree_to_tos, contact, rsa_bits)
+.new_account(name, agree_to_tos, contact, rsa_bits, eab_creds)
 .await
 }
 
diff --git a/src/bin/proxmox_backup_manager/acme.rs 
b/src/bin/proxmox_backup_manager/acme.rs
index de48a420..17ca5958 100644
--- a/src/bin/proxmox_backup_manager/acme.rs
+++ b/src/bin/proxmox_backup_manager/acme.rs
@@ -156,9 +156,15 @@ async fn register_account(
 
 println!("Attempting to register account with {:?}...", directory);
 
-let account =
-api2::config::acme::do_register_account( client, , 
tos_agreed, contact, None)
-.await?;
+let account = api2::config::acme::do_register_account(
+ client,
+,
+tos_agreed,
+contact,
+None,
+None,
+)
+.await?;
 
 println!("Registration successful, account URL: {}", account.location);
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH acme-rs 2/8] add meta fields returned by the directory

2023-11-14 Thread Folke Gleumes
According to the rfc, the meta field contains additional fields that
weren't covered by the Meta struct. Of the additional fields, only
external_account_required will be used in the near future, but others
were added for completeness and the case that they might be used in the
future.

Signed-off-by: Folke Gleumes 
---
 src/directory.rs | 25 +++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/directory.rs b/src/directory.rs
index 755ea8c..a9d31f2 100644
--- a/src/directory.rs
+++ b/src/directory.rs
@@ -47,6 +47,18 @@ pub struct Meta {
 /// The terms of service. This is typically in the form of an URL.
 #[serde(skip_serializing_if = "Option::is_none")]
 pub terms_of_service: Option,
+
+/// Flag indicating if EAB is required, None is equivalent to false
+#[serde(skip_serializing_if = "Option::is_none")]
+pub external_account_required: Option,
+
+/// Website with information about the ACME Server
+#[serde(skip_serializing_if = "Option::is_none")]
+pub website: Option,
+
+/// List of hostnames used by the CA, intended for the use with caa dns 
records
+#[serde(skip_serializing_if = "Option::is_none")]
+pub caa_identities: Option>,
 }
 
 impl Directory {
@@ -64,6 +76,17 @@ impl Directory {
 }
 }
 
+/// Get if external account binding is required
+pub fn external_account_binding_required() -> bool {
+matches!(
+,
+Some(Meta {
+external_account_required: Some(true),
+..
+})
+)
+}
+
 /// Get the "newNonce" URL. Use `HEAD` requests on this to get a new nonce.
 pub fn new_nonce_url() ->  {
 _nonce
@@ -78,8 +101,6 @@ impl Directory {
 }
 
 /// Access to the in the Acme spec defined metadata structure.
-/// Currently only contains the ToS URL already exposed via the 
`terms_of_service_url()`
-/// method.
 pub fn meta() -> Option<> {
 self.data.meta.as_ref()
 }
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH acme-rs/backup/perl-rs/pmg-api 0/8] add external account binding to pmg and pbs

2023-11-14 Thread Folke Gleumes
Following the implementation for pve [0], this implements external account
binding for pmg and pbs.

For pmg, the tos endpoint was replaced with a meta endpoint, for pbs
this was not necessary, although it might be in the future if the
functionality is introduced in the gui.

Similar to the pve implementation, the cli will ask for eab credentials
if the ca requires it, or optionally if the user provided a custom
directory url.

The patches were tested against pebble with eab and le-staging + pebble
without eab to ensure no regression have taken place.

[0] https://lists.proxmox.com/pipermail/pve-devel/2023-October/059726.html

acme-rs:
Folke Gleumes (2):
  add external account binding
  add meta fields returned by the directory

 src/account.rs   | 28 +++-
 src/client.rs|  6 -
 src/directory.rs | 25 --
 src/eab.rs   | 66 
 src/error.rs | 10 
 src/lib.rs   |  1 +
 6 files changed, 127 insertions(+), 9 deletions(-)
 create mode 100644 src/eab.rs

backup:
Folke Gleumes (2):
  acme: api: add eab options to api
  cli: acme: add possibility to set eab via the cli

 src/acme/client.rs |  9 +++-
 src/api2/config/acme.rs| 35 +--
 src/bin/proxmox_backup_manager/acme.rs | 61 +-
 3 files changed, 89 insertions(+), 16 deletions(-)

perl-rs:
Folke Gleumes (1):
  acme: add eab fields for pmg

 pmg-rs/src/acme.rs | 18 +-
 1 file changed, 13 insertions(+), 5 deletions(-)

pmg-api:
Folke Gleumes (3):
  api: acme: add eab parameters
  api: acme: deprecate tos endpoint in favor of new meta endpoint
  cli: acme: expose acme eab options on the cli

 src/PMG/API2/ACME.pm | 75 ++--
 src/PMG/CLI/pmgconfig.pm | 29 ++--
 2 files changed, 99 insertions(+), 5 deletions(-)
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH pmg-api 8/8] cli: acme: expose acme eab options on the cli

2023-11-14 Thread Folke Gleumes
interactively ask for external account binding credentials if either:
* the ca requests it
* a custom ca is used

Signed-off-by: Folke Gleumes 
---
 src/PMG/CLI/pmgconfig.pm | 29 ++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/PMG/CLI/pmgconfig.pm b/src/PMG/CLI/pmgconfig.pm
index cc036fa..2a7a7a1 100644
--- a/src/PMG/CLI/pmgconfig.pm
+++ b/src/PMG/CLI/pmgconfig.pm
@@ -244,6 +244,7 @@ __PACKAGE__->register_method({
 code => sub {
my ($param) = @_;
 
+   my $custom_directory = 1;
if (!$param->{directory}) {
my $directories = PMG::API2::ACME->get_directories({});
print "Directory endpoints:\n";
@@ -264,6 +265,7 @@ __PACKAGE__->register_method({
return;
} elsif ($selection < $i && $selection >= 0) {
$param->{directory} = $directories->[$selection]->{url};
+   $custom_directory = 0;
return;
}
}
@@ -277,11 +279,13 @@ __PACKAGE__->register_method({
$attempts++;
}
}
+
print "\nAttempting to fetch Terms of Service from 
'$param->{directory}'..\n";
-   my $tos = PMG::API2::ACME->get_tos({ directory => $param->{directory} 
});
-   if ($tos) {
+   my $meta = PMG::API2::ACME->get_meta({ directory => $param->{directory} 
});
+   if ($meta->{termsOfService}) {
+   my $tos = $meta->{termsOfService};
print "Terms of Service: $tos\n";
-   my $term = Term::ReadLine->new('pvenode');
+   my $term = Term::ReadLine->new('pmgconfig');
my $agreed = $term->readline('Do you agree to the above terms? 
[y|N]: ');
die "Cannot continue without agreeing to ToS, aborting.\n"
if ($agreed !~ /^y$/i);
@@ -290,6 +294,25 @@ __PACKAGE__->register_method({
} else {
print "No Terms of Service found, proceeding.\n";
}
+
+   my $eab_enabled = $meta->{externalAccountRequired};
+   if (!$eab_enabled && $custom_directory) {
+   my $term = Term::ReadLine->new('pmgconfig');
+   my $agreed = $term->readline('Do you want to use external account 
binding? [y|N]: ');
+   $eab_enabled = ($agreed =~ /^y$/i);
+   } elsif ($eab_enabled) {
+   print "The CA requires external account binding.\n";
+   }
+   if ($eab_enabled) {
+   print "You should have received a key id and a key from your CA.\n";
+   my $term = Term::ReadLine->new('pmgconfig');
+   my $eab_kid = $term->readline('Enter EAB key id: ');
+   my $eab_hmac_key = $term->readline('Enter EAB key: ');
+
+   $param->{'eab-kid'} = $eab_kid;
+   $param->{'eab-hmac-key'} = $eab_hmac_key;
+   }
+
print "\nAttempting to register account with '$param->{directory}'..\n";
 
$upid_exit->(PMG::API2::ACME->register_account($param));
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH pmg-api 6/8] api: acme: add eab parameters

2023-11-14 Thread Folke Gleumes
Signed-off-by: Folke Gleumes 
---
 src/PMG/API2/ACME.pm | 16 +++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/PMG/API2/ACME.pm b/src/PMG/API2/ACME.pm
index 42c9f4e..9e3eb8d 100644
--- a/src/PMG/API2/ACME.pm
+++ b/src/PMG/API2/ACME.pm
@@ -132,6 +132,18 @@ __PACKAGE__->register_method ({
default => $acme_default_directory_url,
optional => 1,
}),
+   'eab-kid' => {
+   type => 'string',
+   description => 'Key Identifier for External Account Binding.',
+   requires => 'eab-hmac-key',
+   optional => 1,
+   },
+   'eab-hmac-key' => {
+   type => 'string',
+   description => 'HMAC key for External Account Binding.',
+   requires => 'eab-kid',
+   optional => 1,
+   },
},
 },
 returns => {
@@ -151,6 +163,8 @@ __PACKAGE__->register_method ({
 
my $directory = extract_param($param, 'directory') // 
$acme_default_directory_url;
my $contact = $account_contact_from_param->($param);
+   my $eab_kid = extract_param($param, 'eab-kid');
+   my $eab_hmac_key = extract_param($param, 'eab-hmac-key');
 
my $realcmd = sub {
PMG::CertHelpers::lock_acme($account_name, 10, sub {
@@ -160,7 +174,7 @@ __PACKAGE__->register_method ({
print "Registering new ACME account..\n";
my $acme = PMG::RS::Acme->new($directory);
eval {
-   $acme->new_account($account_file, 
defined($param->{tos_url}), $contact, undef);
+   $acme->new_account($account_file, 
defined($param->{tos_url}), $contact, undef, $eab_kid, $eab_hmac_key);
};
if (my $err = $@) {
unlink $account_file;
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH pmg-api 7/8] api: acme: deprecate tos endpoint in favor of new meta endpoint

2023-11-14 Thread Folke Gleumes
The ToS endpoint ignored data that is needed to detect if EAB needs to
be used. Instead of adding a new endpoint that does the same request,
the tos endpoint is deprecated and replaced by the meta endpoint,
that returns all information returned by the directory.

Signed-off-by: Folke Gleumes 
---
 src/PMG/API2/ACME.pm | 59 +++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/src/PMG/API2/ACME.pm b/src/PMG/API2/ACME.pm
index 9e3eb8d..8795c33 100644
--- a/src/PMG/API2/ACME.pm
+++ b/src/PMG/API2/ACME.pm
@@ -67,6 +67,7 @@ __PACKAGE__->register_method ({
return [
{ name => 'account' },
{ name => 'tos' },
+   { name => 'meta' },
{ name => 'directories' },
{ name => 'plugins' },
{ name => 'challenge-schema' },
@@ -353,11 +354,12 @@ __PACKAGE__->register_method ({
return $update_account->($param, 'deactivate', $force_deactivate, 
status => 'deactivated');
 }});
 
+# TODO: deprecated, remove with pmg 9
 __PACKAGE__->register_method ({
 name => 'get_tos',
 path => 'tos',
 method => 'GET',
-description => "Retrieve ACME TermsOfService URL from CA.",
+description => "Retrieve ACME TermsOfService URL from CA. Deprecated, 
please use /config/acme/meta.",
 permissions => { user => 'all' },
 parameters => {
additionalProperties => 0,
@@ -384,6 +386,61 @@ __PACKAGE__->register_method ({
return $meta ? $meta->{termsOfService} : undef;
 }});
 
+__PACKAGE__->register_method ({
+name => 'get_meta',
+path => 'meta',
+method => 'GET',
+description => "Retrieve ACME Directory Meta Information",
+permissions => { user => 'all' },
+parameters => {
+   additionalProperties => 0,
+   properties => {
+   directory => get_standard_option('pmg-acme-directory-url', {
+   default => $acme_default_directory_url,
+   optional => 1,
+   }),
+   },
+},
+returns => {
+   type => 'object',
+   additionalProperties => 1,
+   properties => {
+   termsOfService => {
+   description => 'ACME TermsOfService URL.',
+   type => 'string',
+   optional => 1,
+   },
+   externalAccountRequired => {
+   description => 'EAB Required',
+   type => 'boolean',
+   optional => 1,
+   },
+   website => {
+   description => 'URL to more information about the ACME server.',
+   type => 'string',
+   optional => 1,
+   },
+   caaIdentities => {
+   description => 'Hostnames referring to the ACME servers.',
+   type => 'array',
+   items => {
+   type => 'string',
+   },
+   optional => 1,
+   },
+   },
+},
+code => sub {
+   my ($param) = @_;
+
+   my $directory = extract_param($param, 'directory') // 
$acme_default_directory_url;
+
+   my $acme = PVE::ACME->new(undef, $directory);
+   my $meta = $acme->get_meta();
+
+   return $meta;
+}});
+
 __PACKAGE__->register_method ({
 name => 'get_directories',
 path => 'directories',
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH acme-rs 1/8] add external account binding

2023-11-14 Thread Folke Gleumes
Functionality was added as a additional setter function, which hopefully
prevents any breakages. Since a placeholder Option an the AccountData
was already present, but has never been used, replacing the field with
an Option of a fully defined type should also be minimally intrusive.

Signed-off-by: Folke Gleumes 
---
 src/account.rs | 28 -
 src/eab.rs | 66 ++
 src/error.rs   | 10 
 src/lib.rs |  1 +
 4 files changed, 99 insertions(+), 6 deletions(-)
 create mode 100644 src/eab.rs

diff --git a/src/account.rs b/src/account.rs
index 8144d39..9f3af26 100644
--- a/src/account.rs
+++ b/src/account.rs
@@ -11,8 +11,9 @@ use serde_json::Value;
 use crate::authorization::{Authorization, GetAuthorization};
 use crate::b64u;
 use crate::directory::Directory;
+use crate::eab::ExternalAccountBinding;
 use crate::jws::Jws;
-use crate::key::PublicKey;
+use crate::key::{Jwk, PublicKey};
 use crate::order::{NewOrder, Order, OrderData};
 use crate::request::Request;
 use crate::Error;
@@ -336,10 +337,9 @@ pub struct AccountData {
 #[serde(skip_serializing_if = "Option::is_none")]
 pub terms_of_service_agreed: Option,
 
-/// External account information. This is currently not directly supported 
in any way and only
-/// stored to completeness.
+/// External account information.
 #[serde(skip_serializing_if = "Option::is_none")]
-pub external_account_binding: Option,
+pub external_account_binding: Option,
 
 /// This is only used by the client when querying an account.
 #[serde(default = "default_true", skip_serializing_if = "is_false")]
@@ -375,6 +375,7 @@ pub struct AccountCreator {
 contact: Vec,
 terms_of_service_agreed: bool,
 key: Option>,
+eab_credentials: Option<(String, PKey)>,
 }
 
 impl AccountCreator {
@@ -402,6 +403,13 @@ impl AccountCreator {
 self
 }
 
+/// Set the EAB credentials for the account registration
+pub fn set_eab_credentials(mut self, kid: String, hmac_key: String) -> 
Result {
+let hmac_key = PKey::hmac(::decode(hmac_key)?)?;
+self.eab_credentials = Some((kid, hmac_key));
+Ok(self)
+}
+
 /// Generate a new RSA key of the specified key size.
 pub fn generate_rsa_key(self, bits: u32) -> Result {
 let key = openssl::rsa::Rsa::generate(bits)?;
@@ -431,6 +439,15 @@ impl AccountCreator {
 /// [`response`](AccountCreator::response()) will render the account 
unusable!
 pub fn request(, directory: , nonce: ) -> 
Result {
 let key = self.key.as_deref().ok_or(Error::MissingKey)?;
+let url = directory.new_account_url();
+
+let external_account_binding = self
+.eab_credentials
+.as_ref()
+.map(|cred| {
+ExternalAccountBinding::new(, , 
Jwk::try_from(key)?, url.to_string())
+})
+.transpose()?;
 
 let data = AccountData {
 orders: None,
@@ -441,12 +458,11 @@ impl AccountCreator {
 } else {
 None
 },
-external_account_binding: None,
+external_account_binding,
 only_return_existing: false,
 extra: HashMap::new(),
 };
 
-let url = directory.new_account_url();
 let body = serde_json::to_string(::new(
 key,
 None,
diff --git a/src/eab.rs b/src/eab.rs
new file mode 100644
index 000..a4c0642
--- /dev/null
+++ b/src/eab.rs
@@ -0,0 +1,66 @@
+use openssl::hash::MessageDigest;
+use openssl::pkey::{HasPrivate, PKeyRef};
+use openssl::sign::Signer;
+use serde::{Deserialize, Serialize};
+
+use crate::key::Jwk;
+use crate::{b64u, Error};
+
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct Protected {
+alg: &'static str,
+url: String,
+kid: String,
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct ExternalAccountBinding {
+protected: String,
+payload: String,
+signature: String,
+}
+
+impl ExternalAccountBinding {
+pub fn new(
+eab_kid: ,
+eab_hmac_key: ,
+jwk: Jwk,
+url: String,
+) -> Result
+where
+P: HasPrivate,
+{
+let protected = Protected {
+alg: "HS256",
+kid: eab_kid.to_string(),
+url,
+};
+let payload = b64u::encode(serde_json::to_string()?.as_bytes());
+let protected_data = 
b64u::encode(serde_json::to_string()?.as_bytes());
+let signature = {
+let protected = protected_data.as_bytes();
+let payload = payload.as_bytes();
+Self::sign_hmac(eab_hmac_key, protected, payload)?
+};
+
+let signature = b64u::encode();
+Ok(ExternalAccountBinding {
+protected: protected_data,
+payload,
+signature,
+})
+}
+
+fn sign_hmac(key: , 

[pve-devel] [PATCH perl-rs 5/8] acme: add eab fields for pmg

2023-11-14 Thread Folke Gleumes
Signed-off-by: Folke Gleumes 
---
 pmg-rs/src/acme.rs | 18 +-
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/pmg-rs/src/acme.rs b/pmg-rs/src/acme.rs
index b38e1ea..fe1e465 100644
--- a/pmg-rs/src/acme.rs
+++ b/pmg-rs/src/acme.rs
@@ -79,6 +79,7 @@ impl Inner {
 tos_agreed: bool,
 contact: Vec,
 rsa_bits: Option,
+eab_creds: Option<(String, String)>,
 ) -> Result<(), Error> {
 self.tos = if tos_agreed {
 self.client.terms_of_service_url()?.map(str::to_owned)
@@ -86,7 +87,9 @@ impl Inner {
 None
 };
 
-let _account = self.client.new_account(contact, tos_agreed, rsa_bits)?;
+let _account = self
+.client
+.new_account(contact, tos_agreed, rsa_bits, eab_creds)?;
 let file = OpenOptions::new()
 .write(true)
 .create(true)
@@ -238,11 +241,16 @@ pub mod export {
 tos_agreed: bool,
 contact: Vec,
 rsa_bits: Option,
+eab_kid: Option,
+eab_hmac_key: Option,
 ) -> Result<(), Error> {
-this.inner
-.lock()
-.unwrap()
-.new_account(account_path, tos_agreed, contact, rsa_bits)
+this.inner.lock().unwrap().new_account(
+account_path,
+tos_agreed,
+contact,
+rsa_bits,
+eab_kid.zip(eab_hmac_key),
+)
 }
 
 /// Get the directory's meta information.
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH] expand helper function by eab credentials

2023-11-14 Thread Folke Gleumes
Signed-off-by: Folke Gleumes 
---
 src/client.rs | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/client.rs b/src/client.rs
index 78c83a2..53f2688 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -367,10 +367,14 @@ impl Client {
 contact: Vec,
 tos_agreed: bool,
 rsa_bits: Option,
+eab_creds: Option<(String, String)>,
 ) -> Result<, Error> {
-let account = Account::creator()
+let mut account = Account::creator()
 .set_contacts(contact)
 .agree_to_tos(tos_agreed);
+if let Some((eab_kid, eab_hmac_key)) = eab_creds {
+account = account.set_eab_credentials(eab_kid, eab_hmac_key)?;
+}
 let account = if let Some(bits) = rsa_bits {
 account.generate_rsa_key(bits)?
 } else {
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [RFC qemu-server/common] fix #4501: improve port reservation for QEMU TCP migration

2023-11-14 Thread Fiona Ebner
Each patch is a different approach for improving the situation and
each subset could be applied. Personally, I like common 2/2, because
it removes the competition for early ports and IMHO the only one
worth considering a full fix, but it is a bit complex.

Another approach (not in the RFC, also could be considered a full fix)
would be to opt-in for a higher expire time for migration ports, add a
mechanism to remove the reservation and have vm_start_nolock() remove
the reservation after it made sure that QEMU got the port.


qemu-server:

Fiona Ebner (1):
  partially fix #4501: migration: start vm: move port reservation and
usage closer together

 PVE/QemuServer.pm | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)


common:

Fiona Ebner (2):
  partially fix #4501: next unused port: bump port reservation
expiretime
  fix #4501: next unused port: work around issue with too short
expiretime

 src/PVE/Tools.pm | 21 +++--
 1 file changed, 19 insertions(+), 2 deletions(-)

-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [RFC common 1/2] partially fix #4501: next unused port: bump port reservation expiretime

2023-11-14 Thread Fiona Ebner
For QEMU migration via TCP, there's a bit of time between port
reservation and usage, because currently, the port needs to be
reserved before invoking a fork, where the systemd scope needs to be
set up and swtpm might need to be started before the QEMU binary can
be invoked and actually use the port.

Not bumping too much, because mass migration with many small VMs might
need to re-use the ports rather quickly (there's only 50 ports).

The other two usages of the function are for VNC and SPICE, with
100 and 999 ports respectively. And for those, ports are usually used
for longer than 30 seconds anyways, so the higher expire time should
be fine.

Signed-off-by: Fiona Ebner 
---
 src/PVE/Tools.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
index b3af2c6..4d018e9 100644
--- a/src/PVE/Tools.pm
+++ b/src/PVE/Tools.pm
@@ -918,7 +918,7 @@ sub next_unused_port {
 
 my $code = sub {
 
-   my $expiretime = 5;
+   my $expiretime = 30;
my $ctime = time();
 
my $ports = {};
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [RFC qemu-server 1/1] partially fix #4501: migration: start vm: move port reservation and usage closer together

2023-11-14 Thread Fiona Ebner
Currently, volume activation, PCI reservation and resetting systemd
scope happen in between and the 5 second expiretime used for port
reservation might not be enough.

Still not ideal, because entering systemd scope and maybe starting
swtpm still happen after reservation before the QEMU binary can be
invoked and actually use the port, but the reservation needs to happen
outside of the fork, because the result is used there too.

Signed-off-by: Fiona Ebner 
---
 PVE/QemuServer.pm | 20 ++--
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index c465fb6f..aeaea8eb 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -5697,6 +5697,9 @@ sub vm_start_nolock {
return $migration_ip;
 };
 
+# helper to move port reservation and usage closer together to avoid 
expiry (bug #4501)
+my $append_tcp_migration_cmdline;
+
 if ($statefile) {
if ($statefile eq 'tcp') {
my $migrate = $res->{migrate} = { proto => 'tcp' };
@@ -5717,12 +5720,13 @@ sub vm_start_nolock {
$migrate->{addr} = "[$migrate->{addr}]" if 
Net::IP::ip_is_ipv6($migrate->{addr});
}
 
-   my $pfamily = PVE::Tools::get_host_address_family($nodename);
-   $migrate->{port} = PVE::Tools::next_migrate_port($pfamily);
-   $migrate->{uri} = "tcp:$migrate->{addr}:$migrate->{port}";
-   push @$cmd, '-incoming', $migrate->{uri};
-   push @$cmd, '-S';
-
+   $append_tcp_migration_cmdline = sub {
+   my $pfamily = PVE::Tools::get_host_address_family($nodename);
+   $migrate->{port} = PVE::Tools::next_migrate_port($pfamily);
+   $migrate->{uri} = "tcp:$migrate->{addr}:$migrate->{port}";
+   push @$cmd, '-incoming', $migrate->{uri};
+   push @$cmd, '-S';
+   };
} elsif ($statefile eq 'unix') {
# should be default for secure migrations as a ssh TCP forward
# tunnel is not deterministic reliable ready and fails regurarly
@@ -5840,6 +5844,10 @@ sub vm_start_nolock {
 $systemd_properties{timeout} = 10 if $statefile; # setting up the scope 
shoul be quick
 
 my $run_qemu = sub {
+   # sets the port+uri for $res->{migrate} which is printed below and part 
of the result, so
+   # needs to happen outside of the fork.
+   $append_tcp_migration_cmdline->() if $append_tcp_migration_cmdline;
+
PVE::Tools::run_fork sub {
PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", 
%systemd_properties);
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [RFC common 2/2] fix #4501: next unused port: work around issue with too short expiretime

2023-11-14 Thread Fiona Ebner
For QEMU migration via TCP, there's a bit of time between port
reservation and usage, because currently, the port needs to be
reserved before doing a fork, where the systemd scope needs to be set
up and swtpm might need to be started before the QEMU binary can be
invoked and actually use the port.

To improve the situation, get the latest port recorded in the
reservation file and start trying from the next port, wrapping around
when hitting the end. Drastically reduces the chances to run into a
conflict, because after a given port reservation, all other ports are
tried first before returning to that port.

Signed-off-by: Fiona Ebner 
---
 src/PVE/Tools.pm | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/PVE/Tools.pm b/src/PVE/Tools.pm
index 4d018e9..820229d 100644
--- a/src/PVE/Tools.pm
+++ b/src/PVE/Tools.pm
@@ -923,6 +923,11 @@ sub next_unused_port {
 
my $ports = {};
 
+   # Avoid that bulk actions compete for the first few ports by detecting 
the latest
+   # (previously) used port and start checking from there when trying to 
get a reservation.
+   my $latest_timestamp = 0;
+   my $latest_port = $range_end - 1;
+
if (my $fh = IO::File->new ($filename, "r")) {
while (my $line = <$fh>) {
if ($line =~ m/^(\d+)\s(\d+)$/) {
@@ -930,6 +935,14 @@ sub next_unused_port {
if (($timestamp + $expiretime) > $ctime) {
$ports->{$port} = $timestamp; # not expired
}
+   if (
+   $port >= $range_start
+   && $port < $range_end
+   && $timestamp > $latest_timestamp
+   ) {
+   $latest_timestamp = $timestamp;
+   $latest_port = $port;
+   }
}
}
}
@@ -942,7 +955,11 @@ sub next_unused_port {
GetAddrInfoFlags => 0);
$sockargs{LocalAddr} = $address if defined($address);
 
-   for (my $p = $range_start; $p < $range_end; $p++) {
+   my $range = $range_end - $range_start;
+   for (my $offset = 1; $offset <= $range; $offset++) {
+   my $p = $latest_port + $offset;
+   $p -= $range if $p >= $range_end; # wrap around
+
next if $ports->{$p}; # reserved
 
$sockargs{LocalPort} = $p;
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



Re: [pve-devel] [PATCH common 2/4] json schema: implement 'oneOf' schema

2023-11-14 Thread Wolfgang Bumiller
mostly LGTM, just minor things

On Tue, Nov 14, 2023 at 11:33:37AM +0100, Dominik Csapak wrote:
> a schema can now have the 'oneOf' property which is an array of regular
> schemas. In the default case any of that has to match. If the
> 'type-property'/'instance-types' are given, only the schema for the specific
> type will be checked (and handles as 'additionalProperties' if there is
> no matching type)
> 
> the field found in 'type-property' has to be on the same level
> (so for oneOf the nested schemas should not include that).
> 
> Documentation is adapted so that options are grouped per `type-property=value`
> after the regular options (with their individual descriptions/types/etc.)
> 
> oneOfs without 'type-property'/'instance-tyeps' simply show up twice for
> now with an 'or' line in between.
> 
> command line parsing is a bit weird for now since Getopt::Long
> can't have multiple variants for the same property (but works fine with
> pvesh for our current use cases). it gets shown as '--foo  they are not optional.
> 
> Signed-off-by: Dominik Csapak 
> ---
>  src/PVE/CLIHandler.pm  |   2 +-
>  src/PVE/JSONSchema.pm  | 117 ++---
>  src/PVE/RESTHandler.pm |  82 +++--
>  3 files changed, 188 insertions(+), 13 deletions(-)
> 
> diff --git a/src/PVE/CLIHandler.pm b/src/PVE/CLIHandler.pm
> index 5c7863a..bb97a7d 100644
> --- a/src/PVE/CLIHandler.pm
> +++ b/src/PVE/CLIHandler.pm
> @@ -433,7 +433,7 @@ my $print_bash_completion = sub {
>   my $res = $d->{completion}->($cmd, $pname, $cur, $args);
>   &$print_result(@$res);
>   }
> - } elsif ($d->{type} eq 'boolean') {
> + } elsif ($d->{type} && $d->{type} eq 'boolean') {
>   &$print_result('0', '1');
>   } elsif ($d->{enum}) {
>   &$print_result(@{$d->{enum}});
> diff --git a/src/PVE/JSONSchema.pm b/src/PVE/JSONSchema.pm
> index 49e0d7a..61c57b3 100644
> --- a/src/PVE/JSONSchema.pm
> +++ b/src/PVE/JSONSchema.pm
> @@ -1163,6 +1190,58 @@ sub check_prop {
>   return;
>  }
>  
> +# must pass any of the given schemas
> +my $optional_for_type = 0;
> +if ($schema->{oneOf}) {
> + # in case we have an instance_type given, just check for that variant
> + if ($schema->{'type-property'}) {
> + $optional_for_type = 1;
> + for (my $i = 0; $i < scalar($schema->{oneOf}->@*); $i++) {
> + last if !$instance_type; # treat as optional if we don't have a 
> type
> + my $inner_schema = $schema->{oneOf}->[$i];
> +
> + if (!defined($inner_schema->{'instance-types'})) {
> + add_error($errors, $path, "missing 'instance-types' in 
> oneOf alternative");
> + return;
> + }
> +
> + next if !grep { $_ eq $instance_type } 
> $inner_schema->{'instance-types'}->@*;
> + $optional_for_type = $inner_schema->{optional} // 0;
> + check_prop($value, $inner_schema, $path, $errors);
> + }
> + } else {
> + my $is_valid = 0;
> + my $collected_errors = {};
> + for (my $i = 0; $i < scalar($schema->{oneOf}->@*); $i++) {
> + my $inner_schema = $schema->{oneOf}->[$i];
> + my $inner_errors = {};
> + check_prop($value, $inner_schema, "$path.oneOf[$i]", 
> $inner_errors);
> + if (scalar(keys $inner_errors->%*) == 0) {

^ iirc perl can optimize `if (!%$inner_errors)` better

> + $is_valid = 1;
> + last;
> + }
> +
> + for my $inner_path (keys $inner_errors->%*) {
> + add_error($collected_errors, $inner_path, 
> $inner_errors->{$path});
> + }
> + }
> +
> + if (!$is_valid) {
> + for my $inner_path (keys $collected_errors->%*) {
> + add_error($errors, $inner_path, $collected_errors->{$path});
> + }
> + }
> + }
> +} elsif ($instance_type) {
> + if (!defined($schema->{'instance-types'})) {
> + add_error($errors, $path, "missing 'instance-types'");
> + return;
> + }
> + if (grep { $_ eq $instance_type} $schema->{'instance_types'}->@*) {
> + $optional_for_type = 1;
> + }
> +}
> +
>  # if it extends another schema, it must pass that schema as well
>  if($schema->{extends}) {
>   check_prop($value, $schema->{extends}, $path, $errors);
> @@ -1170,7 +1249,7 @@ sub check_prop {
>  
>  if (!defined ($value)) {
>   return if $schema->{type} && $schema->{type} eq 'null';
> - if (!$schema->{optional} && !$schema->{alias} && !$schema->{group}) {
> + if (!$schema->{optional} && !$schema->{alias} && !$schema->{group} && 
> !$optional_for_type) {
>   add_error($errors, $path, "property is missing and it is not 
> optional");
>   }
>   return;
> @@ -1317,6 +1396,29 @@ my $default_schema_noref = {
>   },
>   enum => 

Re: [pve-devel] [PATCH cluster/guest-common/qemu-server/container/manager v2] add backend profile support

2023-11-14 Thread Thomas Lamprecht
Am 14/11/2023 um 11:35 schrieb Dominik Csapak:
> This series aims to provide profile support when creating guests (ct/vm)
> so that users can reuse options without having to specify them every
> time.

Great! Nice to see that my hopes^Wexpectations about this being really
not that much work/code are being met.

As mentioned off-list, some way for configuring the base defaults for
disks/mountpoints and networks without having to already explicitly
specify the single would be nice to have.

As talked, having a net-defaults, scsi-defaults, mp-defaults, ...
property supported by the respective VM/CT config itself could solve
that and even provide an additional feature.
The values decoded there would be merged with the actual instances,
e.g., scsi0, scsi1, ... or net0, net1, ..., on start.
There might be a few edge cases, and me might not want to support
every option (e.g., a default serial for all disks is rather counter
productive), but otherwise this could be a nice way to encode things
like "discard=on,sdd=1" for all (future) scsi disks, be it for a
specific VM or CT, or also in a profile.


___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox 05/52] notify: replace filters and groups with matcher-based system

2023-11-14 Thread Lukas Wagner
This shifts notification routing into the matcher-system. Every
notification has associated metadata (key-value fields, severity -
to be extended) that can be match with match directives in
notification matchers. Right now, there are 2 matching directives,
match-field and match-severity. The first one allows one to do a
regex match/exact match on a metadata field, the other one allows one
to match one or more severites.
Every matcher also allows 'target' directives, these decide which
target(s) will be notified if a matcher matches a notification.

Since routing now happens in matchers, the API for sending is
simplified, since we do not need to specify a target any more.

The API routes for filters and groups have been removed completely.
The parser for the configuration file will still accept filter/group
entries, but will delete them once the config is saved again. This is
needed to allow a smooth transition from the old system to the new
system, since the old system was already available on pvetest.

Signed-off-by: Lukas Wagner 
---

Notes:
Sorry for the large commit, many of these changes interact with each
other and it would have been significantly more effort to keep
everything nice, tidy and compileable after splitting this apart.
I wantend to get these changes out ASAP.

 proxmox-notify/Cargo.toml|   2 +
 proxmox-notify/src/api/common.rs |   6 +-
 proxmox-notify/src/api/filter.rs | 231 -
 proxmox-notify/src/api/gotify.rs |  16 -
 proxmox-notify/src/api/group.rs  | 259 ---
 proxmox-notify/src/api/matcher.rs| 254 +++
 proxmox-notify/src/api/mod.rs| 115 ++-
 proxmox-notify/src/api/sendmail.rs   |  15 -
 proxmox-notify/src/config.rs |  34 +-
 proxmox-notify/src/endpoints/gotify.rs   |  19 +-
 proxmox-notify/src/endpoints/sendmail.rs |  14 +-
 proxmox-notify/src/filter.rs | 195 +--
 proxmox-notify/src/group.rs  |  40 +--
 proxmox-notify/src/lib.rs| 317 +++---
 proxmox-notify/src/matcher.rs| 395 +++
 proxmox-notify/src/schema.rs |  11 +-
 16 files changed, 848 insertions(+), 1075 deletions(-)
 delete mode 100644 proxmox-notify/src/api/filter.rs
 delete mode 100644 proxmox-notify/src/api/group.rs
 create mode 100644 proxmox-notify/src/api/matcher.rs
 create mode 100644 proxmox-notify/src/matcher.rs

diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
index 1541b8b..4812896 100644
--- a/proxmox-notify/Cargo.toml
+++ b/proxmox-notify/Cargo.toml
@@ -8,6 +8,7 @@ repository.workspace = true
 exclude.workspace = true
 
 [dependencies]
+anyhow.workspace = true
 handlebars = { workspace = true }
 lazy_static.workspace = true
 log.workspace = true
@@ -16,6 +17,7 @@ openssl.workspace = true
 proxmox-http = { workspace = true, features = ["client-sync"], optional = true 
}
 proxmox-http-error.workspace = true
 proxmox-human-byte.workspace = true
+proxmox-serde.workspace = true
 proxmox-schema = { workspace = true, features = ["api-macro", "api-types"]}
 proxmox-section-config = { workspace = true }
 proxmox-sys = { workspace = true, optional = true }
diff --git a/proxmox-notify/src/api/common.rs b/proxmox-notify/src/api/common.rs
index d17f4db..fa2356e 100644
--- a/proxmox-notify/src/api/common.rs
+++ b/proxmox-notify/src/api/common.rs
@@ -7,7 +7,7 @@ use crate::{Bus, Config, Notification};
 ///
 /// The caller is responsible for any needed permission checks.
 /// Returns an `anyhow::Error` in case of an error.
-pub fn send(config: , channel: , notification: ) -> 
Result<(), HttpError> {
+pub fn send(config: , notification: ) -> Result<(), 
HttpError> {
 let bus = Bus::from_config(config).map_err(|err| {
 http_err!(
 INTERNAL_SERVER_ERROR,
@@ -15,7 +15,7 @@ pub fn send(config: , channel: , notification: 
) -> Resu
 )
 })?;
 
-bus.send(channel, notification);
+bus.send(notification);
 
 Ok(())
 }
@@ -50,5 +50,5 @@ pub fn test_target(config: , endpoint: ) -> 
Result<(), HttpError> {
 /// If the entity does not exist, the result will only contain the entity.
 pub fn get_referenced_entities(config: , entity: ) -> 
Result, HttpError> {
 let entities = super::get_referenced_entities(config, entity);
-Ok(Vec::from_iter(entities.into_iter()))
+Ok(Vec::from_iter(entities))
 }
diff --git a/proxmox-notify/src/api/filter.rs b/proxmox-notify/src/api/filter.rs
deleted file mode 100644
index b8682f4..000
--- a/proxmox-notify/src/api/filter.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-use proxmox_http_error::HttpError;
-
-use crate::api::http_err;
-use crate::filter::{DeleteableFilterProperty, FilterConfig, 
FilterConfigUpdater, FILTER_TYPENAME};
-use crate::Config;
-
-/// Get a list of all filters
-///
-/// The caller is responsible for any needed permission checks.
-/// Returns a list of all filters or a `HttpError` if 

[pve-devel] [PATCH v2 proxmox 12/52] notify: add 'smtp' endpoint

2023-11-14 Thread Lukas Wagner
This commit adds a new endpoint type, namely 'smtp'. This endpoint
uses the `lettre` crate to directly send emails to SMTP relays.

The `lettre` crate was chosen since it is by far the most popular SMTP
implementation for Rust that looks like it is well maintained.
Also, it includes async support (for when we want to extend
proxmox-notify to be async).

For this new endpoint type, a new section-config type was introduced
(smtp). It has the same fields as the type for `sendmail`, with the
addition of some new options (smtp server, authentication, tls mode,
etc.).

Some of the behavior that is shared between sendmail and smtp
endpoints has been moved to a new `endpoints::common::mail` module.

Signed-off-by: Lukas Wagner 
---
 Cargo.toml  |   1 +
 proxmox-notify/Cargo.toml   |   4 +-
 proxmox-notify/src/config.rs|  23 ++
 proxmox-notify/src/endpoints/common/mail.rs |  24 ++
 proxmox-notify/src/endpoints/common/mod.rs  |   2 +
 proxmox-notify/src/endpoints/mod.rs |   4 +
 proxmox-notify/src/endpoints/sendmail.rs|  22 +-
 proxmox-notify/src/endpoints/smtp.rs| 258 
 proxmox-notify/src/lib.rs   |  16 ++
 9 files changed, 336 insertions(+), 18 deletions(-)
 create mode 100644 proxmox-notify/src/endpoints/common/mail.rs
 create mode 100644 proxmox-notify/src/endpoints/common/mod.rs
 create mode 100644 proxmox-notify/src/endpoints/smtp.rs

diff --git a/Cargo.toml b/Cargo.toml
index 3d81d85..9f247be 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -62,6 +62,7 @@ http = "0.2"
 hyper = "0.14.5"
 lazy_static = "1.4"
 ldap3 = { version = "0.11", default-features = false }
+lettre = "0.11.1"
 libc = "0.2.107"
 log = "0.4.17"
 mail-parser = "0.8.2"
diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
index 7a3d434..64e3ab7 100644
--- a/proxmox-notify/Cargo.toml
+++ b/proxmox-notify/Cargo.toml
@@ -11,6 +11,7 @@ exclude.workspace = true
 anyhow.workspace = true
 handlebars = { workspace = true }
 lazy_static.workspace = true
+lettre = { workspace = true, optional = true }
 log.workspace = true
 mail-parser = { workspace = true, optional = true }
 openssl.workspace = true
@@ -27,9 +28,10 @@ serde = { workspace = true, features = ["derive"]}
 serde_json.workspace = true
 
 [features]
-default = ["sendmail", "gotify"]
+default = ["sendmail", "gotify", "smtp"]
 mail-forwarder = ["dep:mail-parser"]
 sendmail = ["dep:proxmox-sys"]
 gotify = ["dep:proxmox-http"]
 pve-context = ["dep:proxmox-sys"]
 pbs-context = ["dep:proxmox-sys"]
+smtp = ["dep:lettre"]
diff --git a/proxmox-notify/src/config.rs b/proxmox-notify/src/config.rs
index a86995e..fe25ea7 100644
--- a/proxmox-notify/src/config.rs
+++ b/proxmox-notify/src/config.rs
@@ -28,6 +28,17 @@ fn config_init() -> SectionConfig {
 SENDMAIL_SCHEMA,
 ));
 }
+#[cfg(feature = "smtp")]
+{
+use crate::endpoints::smtp::{SmtpConfig, SMTP_TYPENAME};
+
+const SMTP_SCHEMA:  = 
SmtpConfig::API_SCHEMA.unwrap_object_schema();
+config.register_plugin(SectionConfigPlugin::new(
+SMTP_TYPENAME.to_string(),
+Some(String::from("name")),
+SMTP_SCHEMA,
+));
+}
 #[cfg(feature = "gotify")]
 {
 use crate::endpoints::gotify::{GotifyConfig, GOTIFY_TYPENAME};
@@ -80,6 +91,18 @@ fn private_config_init() -> SectionConfig {
 ));
 }
 
+#[cfg(feature = "smtp")]
+{
+use crate::endpoints::smtp::{SmtpPrivateConfig, SMTP_TYPENAME};
+
+const SMTP_SCHEMA:  = 
SmtpPrivateConfig::API_SCHEMA.unwrap_object_schema();
+config.register_plugin(SectionConfigPlugin::new(
+SMTP_TYPENAME.to_string(),
+Some(String::from("name")),
+SMTP_SCHEMA,
+));
+}
+
 config
 }
 
diff --git a/proxmox-notify/src/endpoints/common/mail.rs 
b/proxmox-notify/src/endpoints/common/mail.rs
new file mode 100644
index 000..0929d7c
--- /dev/null
+++ b/proxmox-notify/src/endpoints/common/mail.rs
@@ -0,0 +1,24 @@
+use std::collections::HashSet;
+
+use crate::context;
+
+pub(crate) fn get_recipients(
+email_addrs: Option<&[String]>,
+users: Option<&[String]>,
+) -> HashSet {
+let mut recipients = HashSet::new();
+
+if let Some(mailto_addrs) = email_addrs {
+for addr in mailto_addrs {
+recipients.insert(addr.clone());
+}
+}
+if let Some(users) = users {
+for user in users {
+if let Some(addr) = context::context().lookup_email_for_user(user) 
{
+recipients.insert(addr);
+}
+}
+}
+recipients
+}
diff --git a/proxmox-notify/src/endpoints/common/mod.rs 
b/proxmox-notify/src/endpoints/common/mod.rs
new file mode 100644
index 000..60e0761
--- /dev/null
+++ b/proxmox-notify/src/endpoints/common/mod.rs
@@ -0,0 +1,2 @@
+#[cfg(any(feature = "sendmail", feature = "smtp"))]
+pub(crate) mod mail;
diff --git 

[pve-devel] [PATCH v2 proxmox-widget-toolkit 37/52] notification ui: remove filter setting for targets

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/Makefile|  1 -
 src/form/NotificationFilterSelector.js  | 58 -
 src/panel/GotifyEditPanel.js|  9 
 src/panel/NotificationGroupEditPanel.js |  9 
 src/panel/SendmailEditPanel.js  |  9 
 5 files changed, 86 deletions(-)
 delete mode 100644 src/form/NotificationFilterSelector.js

diff --git a/src/Makefile b/src/Makefile
index 21fbe76..85ecea4 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -44,7 +44,6 @@ JSSRC=\
form/RoleSelector.js\
form/DiskSelector.js\
form/MultiDiskSelector.js   \
-   form/NotificationFilterSelector.js  \
form/TaskTypeSelector.js\
form/ACME.js\
form/UserSelector.js\
diff --git a/src/form/NotificationFilterSelector.js 
b/src/form/NotificationFilterSelector.js
deleted file mode 100644
index d2ab8be..000
--- a/src/form/NotificationFilterSelector.js
+++ /dev/null
@@ -1,58 +0,0 @@
-Ext.define('Proxmox.form.NotificationFilterSelector', {
-extend: 'Proxmox.form.ComboGrid',
-alias: ['widget.pmxNotificationFilterSelector'],
-
-// set default value to empty array, else it inits it with
-// null and after the store load it is an empty array,
-// triggering dirtychange
-value: [],
-valueField: 'name',
-displayField: 'name',
-deleteEmpty: true,
-skipEmptyText: true,
-allowBlank: true,
-editable: false,
-autoSelect: false,
-
-listConfig: {
-   columns: [
-   {
-   header: gettext('Filter'),
-   dataIndex: 'name',
-   sortable: true,
-   hideable: false,
-   flex: 1,
-   },
-   {
-   header: gettext('Comment'),
-   dataIndex: 'comment',
-   sortable: true,
-   hideable: false,
-   flex: 2,
-   },
-   ],
-},
-
-initComponent: function() {
-   let me = this;
-
-   Ext.apply(me, {
-   store: {
-   fields: ['name', 'comment'],
-   proxy: {
-   type: 'proxmox',
-   url: `/api2/json/${me.baseUrl}/filters`,
-   },
-   sorters: [
-   {
-   property: 'name',
-   direction: 'ASC',
-   },
-   ],
-   autoLoad: true,
-   },
-   });
-
-   me.callParent();
-},
-});
diff --git a/src/panel/GotifyEditPanel.js b/src/panel/GotifyEditPanel.js
index 3ddcc4d..5d814e5 100644
--- a/src/panel/GotifyEditPanel.js
+++ b/src/panel/GotifyEditPanel.js
@@ -32,15 +32,6 @@ Ext.define('Proxmox.panel.GotifyEditPanel', {
allowBlank: '{!isCreate}',
},
},
-   {
-   xtype: 'pmxNotificationFilterSelector',
-   name: 'filter',
-   fieldLabel: gettext('Filter'),
-   cbind: {
-   deleteEmpty: '{!isCreate}',
-   baseUrl: '{baseUrl}',
-   },
-   },
{
xtype: 'proxmoxtextfield',
name: 'comment',
diff --git a/src/panel/NotificationGroupEditPanel.js 
b/src/panel/NotificationGroupEditPanel.js
index aa76810..910d15a 100644
--- a/src/panel/NotificationGroupEditPanel.js
+++ b/src/panel/NotificationGroupEditPanel.js
@@ -21,15 +21,6 @@ Ext.define('Proxmox.panel.NotificationGroupEditPanel', {
name: 'endpoint',
allowBlank: false,
},
-   {
-   xtype: 'pmxNotificationFilterSelector',
-   name: 'filter',
-   fieldLabel: gettext('Filter'),
-   cbind: {
-   deleteEmpty: '{!isCreate}',
-   baseUrl: '{baseUrl}',
-   },
-   },
{
xtype: 'proxmoxtextfield',
name: 'comment',
diff --git a/src/panel/SendmailEditPanel.js b/src/panel/SendmailEditPanel.js
index ace6129..16abebc 100644
--- a/src/panel/SendmailEditPanel.js
+++ b/src/panel/SendmailEditPanel.js
@@ -86,15 +86,6 @@ Ext.define('Proxmox.panel.SendmailEditPanel', {
return this.up('pmxSendmailEditPanel').mailValidator();
},
},
-   {
-   xtype: 'pmxNotificationFilterSelector',
-   name: 'filter',
-   fieldLabel: gettext('Filter'),
-   cbind: {
-   deleteEmpty: '{!isCreate}',
-   baseUrl: '{baseUrl}',
-   },
-   },
{
xtype: 'proxmoxtextfield',
name: 'comment',
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-widget-toolkit 44/52] notification ui: add enable checkbox for targets/matchers

2023-11-14 Thread Lukas Wagner
Add a 'enable' checkbox for targets and matchers in their edit
windows. Also show a new 'enable' column in the overview panel.
The parameter in the config is actually called 'disable', so
the UI needs to invert the setting in the appropriate
on{Get,Set}Values hooks.

Signed-off-by: Lukas Wagner 
---
 src/data/model/NotificationConfig.js  |  4 ++--
 src/panel/GotifyEditPanel.js  | 30 +++
 src/panel/NotificationConfigView.js   | 12 +++
 src/panel/SendmailEditPanel.js| 28 -
 src/panel/SmtpEditPanel.js| 23 +++-
 src/window/NotificationMatcherEdit.js | 30 +++
 6 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/src/data/model/NotificationConfig.js 
b/src/data/model/NotificationConfig.js
index f447db4..9171515 100644
--- a/src/data/model/NotificationConfig.js
+++ b/src/data/model/NotificationConfig.js
@@ -1,6 +1,6 @@
 Ext.define('proxmox-notification-endpoints', {
 extend: 'Ext.data.Model',
-fields: ['name', 'type', 'comment'],
+fields: ['name', 'type', 'comment', 'disable'],
 proxy: {
 type: 'proxmox',
 },
@@ -9,7 +9,7 @@ Ext.define('proxmox-notification-endpoints', {
 
 Ext.define('proxmox-notification-matchers', {
 extend: 'Ext.data.Model',
-fields: ['name', 'comment'],
+fields: ['name', 'comment', 'disable'],
 proxy: {
 type: 'proxmox',
 },
diff --git a/src/panel/GotifyEditPanel.js b/src/panel/GotifyEditPanel.js
index 5d814e5..7e6ecd8 100644
--- a/src/panel/GotifyEditPanel.js
+++ b/src/panel/GotifyEditPanel.js
@@ -16,6 +16,13 @@ Ext.define('Proxmox.panel.GotifyEditPanel', {
fieldLabel: gettext('Endpoint Name'),
allowBlank: false,
},
+   {
+   xtype: 'proxmoxcheckbox',
+   name: 'enable',
+   fieldLabel: gettext('Enable'),
+   allowBlank: false,
+   checked: true,
+   },
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Server URL'),
@@ -41,4 +48,27 @@ Ext.define('Proxmox.panel.GotifyEditPanel', {
},
},
 ],
+
+onSetValues: (values) => {
+   values.enable = !values.disable;
+
+   delete values.disable;
+   return values;
+},
+
+onGetValues: function(values) {
+   let me = this;
+
+   if (values.enable) {
+   if (!me.isCreate) {
+   Proxmox.Utils.assemble_field_data(values, { 'delete': 'disable' 
});
+   }
+   } else {
+   values.disable = 1;
+   }
+
+   delete values.enable;
+
+   return values;
+},
 });
diff --git a/src/panel/NotificationConfigView.js 
b/src/panel/NotificationConfigView.js
index 6a9bc20..ba69298 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -119,6 +119,12 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
 emptyText: gettext('No notification targets configured'),
 
 columns: [
+   {
+   dataIndex: 'disable',
+   text: gettext('Enable'),
+   renderer: (disable) => Proxmox.Utils.renderEnabledIcon(!disable),
+   align: 'center',
+   },
{
dataIndex: 'name',
text: gettext('Target Name'),
@@ -250,6 +256,12 @@ Ext.define('Proxmox.panel.NotificationMatcherView', {
 emptyText: gettext('No notification matchers configured'),
 
 columns: [
+   {
+   dataIndex: 'disable',
+   text: gettext('Enable'),
+   renderer: (disable) => Proxmox.Utils.renderEnabledIcon(!disable),
+   align: 'center',
+   },
{
dataIndex: 'name',
text: gettext('Matcher Name'),
diff --git a/src/panel/SendmailEditPanel.js b/src/panel/SendmailEditPanel.js
index 17f2d4f..930c4db 100644
--- a/src/panel/SendmailEditPanel.js
+++ b/src/panel/SendmailEditPanel.js
@@ -27,6 +27,13 @@ Ext.define('Proxmox.panel.SendmailEditPanel', {
fieldLabel: gettext('Endpoint Name'),
allowBlank: false,
},
+   {
+   xtype: 'proxmoxcheckbox',
+   name: 'enable',
+   fieldLabel: gettext('Enable'),
+   allowBlank: false,
+   checked: true,
+   },
{
// provides 'mailto' and 'mailto-user' fields
xtype: 'pmxEmailRecipientPanel',
@@ -67,7 +74,26 @@ Ext.define('Proxmox.panel.SendmailEditPanel', {
},
 ],
 
-onGetValues: (values) => {
+onSetValues: (values) => {
+   values.enable = !values.disable;
+
+   delete values.disable;
+   return values;
+},
+
+onGetValues: function(values) {
+   let me = this;
+
+   if (values.enable) {
+   if (!me.isCreate) {
+   Proxmox.Utils.assemble_field_data(values, { 'delete': 'disable' 
});
+   }
+   } else {
+   values.disable = 1;
+   }
+
+   delete values.enable;
+
if (values.mailto) {

[pve-devel] [PATCH v2 pve-docs 48/52] notifications: document 'comment' option for targets/matchers

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 notifications.adoc | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/notifications.adoc b/notifications.adoc
index acbdfae..e8ed51b 100644
--- a/notifications.adoc
+++ b/notifications.adoc
@@ -67,6 +67,7 @@ accomodate multiple recipients.
 set, the plugin will fall back to the `email_from` setting from
 `datacenter.cfg`. If that is also not set, the plugin will default to
 `root@$hostname`, where `$hostname` is the hostname of the node.
+* `comment`: Comment for this target
 The `From` header in the email will be set to `$author <$from-address>`.
 
 Example configuration (`/etc/pve/notifications.cfg`):
@@ -138,6 +139,7 @@ The configuration for Gotify target plugins has the 
following options:
 * `server`: The base URL of the Gotify server, e.g. `http://:`
 * `token`: The authentication token. Tokens can be generated within the Gotify
 web interface.
+* `comment`: Comment for this target
 
 NOTE: The Gotify target plugin will respect the HTTP proxy settings from the
  xref:datacenter_configuration_file[datacenter configuration]
@@ -192,6 +194,7 @@ a matcher must be true. Defaults to `all`.
 * `match-calendar`: Match the notification's timestamp against a schedule
 * `match-field`: Match the notification's metadata fields
 * `match-severity`: Match the notification's severity
+* `comment`: Comment for this matcher
 
 Calendar Matching Rules
 ~~~
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 pve-manager 33/52] notify: add API routes for smtp endpoints

2023-11-14 Thread Lukas Wagner
The Perl part of the API methods primarily defines the API schema,
checks for any needed privileges and then calls the actual Rust
implementation exposed via perlmod. Any errors returned by the Rust
code are translated into PVE::Exception, so that the API call fails
with the correct HTTP error code.

Signed-off-by: Lukas Wagner 
---
 PVE/API2/Cluster/Notifications.pm | 323 ++
 1 file changed, 323 insertions(+)

diff --git a/PVE/API2/Cluster/Notifications.pm 
b/PVE/API2/Cluster/Notifications.pm
index 8f716f26..42207aaa 100644
--- a/PVE/API2/Cluster/Notifications.pm
+++ b/PVE/API2/Cluster/Notifications.pm
@@ -194,6 +194,14 @@ __PACKAGE__->register_method ({
};
}
 
+   for my $target (@{$config->get_smtp_endpoints()}) {
+   push @$result, {
+   name => $target->{name},
+   comment => $target->{comment},
+   type => 'smtp',
+   };
+   }
+
$result
};
 
@@ -758,6 +766,321 @@ __PACKAGE__->register_method ({
 }
 });
 
+my $smtp_properties= {
+name => {
+   description => 'The name of the endpoint.',
+   type => 'string',
+   format => 'pve-configid',
+},
+server => {
+   description => 'The address of the SMTP server.',
+   type => 'string',
+},
+port => {
+   description => 'The port to be used. Defaults to 465 for TLS based 
connections,'
+   . ' 587 for STARTTLS based connections and port 25 for insecure 
plain-text'
+   . ' connections.',
+   type => 'integer',
+   optional => 1,
+},
+mode => {
+   description => 'Determine which encryption method shall be used for the 
connection.',
+   type => 'string',
+   enum => [ qw(insecure starttls tls) ],
+   default => 'tls',
+   optional => 1,
+},
+username => {
+   description => 'Username for SMTP authentication',
+   type => 'string',
+   optional => 1,
+},
+password => {
+   description => 'Password for SMTP authentication',
+   type => 'string',
+   optional => 1,
+},
+mailto => {
+   type => 'array',
+   items => {
+   type => 'string',
+   format => 'email-or-username',
+   },
+   description => 'List of email recipients',
+   optional => 1,
+},
+'mailto-user' => {
+   type => 'array',
+   items => {
+   type => 'string',
+   format => 'pve-userid',
+   },
+   description => 'List of users',
+   optional => 1,
+},
+'from-address' => {
+   description => '`From` address for the mail',
+   type => 'string',
+},
+author => {
+   description => 'Author of the mail. Defaults to \'Proxmox VE\'.',
+   type => 'string',
+   optional => 1,
+},
+'comment' => {
+   description => 'Comment',
+   type=> 'string',
+   optional=> 1,
+},
+};
+
+__PACKAGE__->register_method ({
+name => 'get_smtp_endpoints',
+path => 'endpoints/smtp',
+method => 'GET',
+description => 'Returns a list of all smtp endpoints',
+permissions => {
+   description => "Only lists entries where you have 'Mapping.Modify', 
'Mapping.Use' or"
+   . " 'Mapping.Audit' permissions on 
'/mapping/notification/targets/'.",
+   user => 'all',
+},
+protected => 1,
+parameters => {
+   additionalProperties => 0,
+   properties => {},
+},
+returns => {
+   type => 'array',
+   items => {
+   type => 'object',
+   properties => $smtp_properties,
+   },
+   links => [ { rel => 'child', href => '{name}' } ],
+},
+code => sub {
+   my $config = PVE::Notify::read_config();
+   my $rpcenv = PVE::RPCEnvironment::get();
+
+   my $entities = eval {
+   $config->get_smtp_endpoints();
+   };
+   raise_api_error($@) if $@;
+
+   return filter_entities_by_privs($rpcenv, "targets", $entities);
+}
+});
+
+__PACKAGE__->register_method ({
+name => 'get_smtp_endpoint',
+path => 'endpoints/smtp/{name}',
+method => 'GET',
+description => 'Return a specific smtp endpoint',
+permissions => {
+   check => ['or',
+   ['perm', '/mapping/notification/targets/{name}', 
['Mapping.Modify']],
+   ['perm', '/mapping/notification/targets/{name}', ['Mapping.Audit']],
+   ],
+},
+protected => 1,
+parameters => {
+   additionalProperties => 0,
+   properties => {
+   name => {
+   type => 'string',
+   format => 'pve-configid',
+   },
+   }
+},
+returns => {
+   type => 'object',
+   properties => {
+   %{ remove_protected_properties($smtp_properties, ['password']) },
+   digest => get_standard_option('pve-config-digest'),
+   }
+
+},
+code => sub {
+   my ($param) = @_;
+   my $name = 

[pve-devel] [PATCH v2 proxmox-widget-toolkit 40/52] notification: matcher: add UI for matcher editing

2023-11-14 Thread Lukas Wagner
This modifies the old filter edit window in the following ways:
  - Split content into multiple panels
- Name and comment in the first tab
- Match rules in a tree-structure in the second tab
- Targets to notify in the third tab

Signed-off-by: Lukas Wagner 
---

Notes:
The code binding the match rule tree structure to the editable fields
could definitely be a bit cleaner. I think this is the first time that
we have used such a pattern, so there there was much experimentation
needed to get this working.
I plan to revisit it and clean up a bit later, I wanted to get
the notification system changes on the list ASAP.

 src/window/NotificationMatcherEdit.js | 867 --
 1 file changed, 820 insertions(+), 47 deletions(-)

diff --git a/src/window/NotificationMatcherEdit.js 
b/src/window/NotificationMatcherEdit.js
index a014f3e..c6f0726 100644
--- a/src/window/NotificationMatcherEdit.js
+++ b/src/window/NotificationMatcherEdit.js
@@ -1,6 +1,6 @@
-Ext.define('Proxmox.panel.NotificationMatcherEditPanel', {
+Ext.define('Proxmox.panel.NotificationMatcherGeneralPanel', {
 extend: 'Proxmox.panel.InputPanel',
-xtype: 'pmxNotificationMatcherEditPanel',
+xtype: 'pmxNotificationMatcherGeneralPanel',
 mixins: ['Proxmox.Mixin.CBind'],
 
 items: [
@@ -15,53 +15,27 @@ Ext.define('Proxmox.panel.NotificationMatcherEditPanel', {
allowBlank: false,
},
{
-   xtype: 'proxmoxKVComboBox',
-   name: 'min-severity',
-   fieldLabel: gettext('Minimum Severity'),
-   value: null,
+   xtype: 'proxmoxtextfield',
+   name: 'comment',
+   fieldLabel: gettext('Comment'),
cbind: {
deleteEmpty: '{!isCreate}',
},
-   comboItems: [
-   ['info', 'info'],
-   ['notice', 'notice'],
-   ['warning', 'warning'],
-   ['error', 'error'],
-   ],
-   triggers: {
-   clear: {
-   cls: 'pmx-clear-trigger',
-   weight: -1,
-   hidden: false,
-   handler: function() {
-   this.setValue('');
-   },
-   },
-   },
-   },
-   {
-   xtype: 'proxmoxcheckbox',
-   fieldLabel: gettext('Invert match'),
-   name: 'invert-match',
-   uncheckedValue: 0,
-   defaultValue: 0,
-   cbind: {
-   deleteDefaultValue: '{!isCreate}',
-   },
},
+],
+});
+
+Ext.define('Proxmox.panel.NotificationMatcherTargetPanel', {
+extend: 'Proxmox.panel.InputPanel',
+xtype: 'pmxNotificationMatcherTargetPanel',
+mixins: ['Proxmox.Mixin.CBind'],
+
+items: [
{
xtype: 'pmxNotificationTargetSelector',
name: 'target',
allowBlank: false,
},
-   {
-   xtype: 'proxmoxtextfield',
-   name: 'comment',
-   fieldLabel: gettext('Comment'),
-   cbind: {
-   deleteEmpty: '{!isCreate}',
-   },
-   },
 ],
 });
 
@@ -74,7 +48,7 @@ Ext.define('Proxmox.window.NotificationMatcherEdit', {
labelWidth: 120,
 },
 
-width: 500,
+width: 700,
 
 initComponent: function() {
let me = this;
@@ -97,12 +71,38 @@ Ext.define('Proxmox.window.NotificationMatcherEdit', {
me.subject = gettext('Notification Matcher');
 
Ext.apply(me, {
-   items: [{
-   name: me.name,
-   xtype: 'pmxNotificationMatcherEditPanel',
-   isCreate: me.isCreate,
-   baseUrl: me.baseUrl,
-   }],
+   bodyPadding: 0,
+   items: [
+   {
+   xtype: 'tabpanel',
+   region: 'center',
+   layout: 'fit',
+   bodyPadding: 10,
+   items: [
+   {
+   name: me.name,
+   title: gettext('General'),
+   xtype: 'pmxNotificationMatcherGeneralPanel',
+   isCreate: me.isCreate,
+   baseUrl: me.baseUrl,
+   },
+   {
+   name: me.name,
+   title: gettext('Match Rules'),
+   xtype: 'pmxNotificationMatchRulesEditPanel',
+   isCreate: me.isCreate,
+   baseUrl: me.baseUrl,
+   },
+   {
+   name: me.name,
+   title: gettext('Targets to notify'),
+   xtype: 'pmxNotificationMatcherTargetPanel',
+   isCreate: me.isCreate,
+   baseUrl: me.baseUrl,
+   },
+   ],
+  

[pve-devel] [PATCH v2 proxmox-widget-toolkit 38/52] notification ui: remove notification groups

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/Makefile|   1 -
 src/Schema.js   |   5 -
 src/panel/NotificationConfigView.js |   4 -
 src/panel/NotificationGroupEditPanel.js | 174 
 4 files changed, 184 deletions(-)
 delete mode 100644 src/panel/NotificationGroupEditPanel.js

diff --git a/src/Makefile b/src/Makefile
index 85ecea4..e07f17c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -61,7 +61,6 @@ JSSRC=\
panel/LogView.js\
panel/NodeInfoRepoStatus.js \
panel/NotificationConfigView.js \
-   panel/NotificationGroupEditPanel.js \
panel/JournalView.js\
panel/PermissionView.js \
panel/PruneKeepPanel.js \
diff --git a/src/Schema.js b/src/Schema.js
index a7ffdf8..37ecd88 100644
--- a/src/Schema.js
+++ b/src/Schema.js
@@ -48,11 +48,6 @@ Ext.define('Proxmox.Schema', { // a singleton
ipanel: 'pmxGotifyEditPanel',
iconCls: 'fa-bell-o',
},
-   group: {
-   name: gettext('Notification Group'),
-   ipanel: 'pmxNotificationGroupEditPanel',
-   iconCls: 'fa-bell-o',
-   },
 },
 
 pxarFileTypes: {
diff --git a/src/panel/NotificationConfigView.js 
b/src/panel/NotificationConfigView.js
index ff9c512..ba98395 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -191,10 +191,6 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
callback: 'reload',
enableFn: rec => rec.data.name !== 'mail-to-root',
getUrl: function(rec) {
-   if (rec.data.type === 'group') {
-   return `${me.baseUrl}/groups/${rec.getId()}`;
-   }
-
return 
`${me.baseUrl}/endpoints/${rec.data.type}/${rec.getId()}`;
},
},
diff --git a/src/panel/NotificationGroupEditPanel.js 
b/src/panel/NotificationGroupEditPanel.js
deleted file mode 100644
index 910d15a..000
--- a/src/panel/NotificationGroupEditPanel.js
+++ /dev/null
@@ -1,174 +0,0 @@
-Ext.define('Proxmox.panel.NotificationGroupEditPanel', {
-extend: 'Proxmox.panel.InputPanel',
-xtype: 'pmxNotificationGroupEditPanel',
-mixins: ['Proxmox.Mixin.CBind'],
-
-type: 'group',
-
-items: [
-   {
-   xtype: 'pmxDisplayEditField',
-   name: 'name',
-   cbind: {
-   value: '{name}',
-   editable: '{isCreate}',
-   },
-   fieldLabel: gettext('Group Name'),
-   allowBlank: false,
-   },
-   {
-   xtype: 'pmxNotificationEndpointSelector',
-   name: 'endpoint',
-   allowBlank: false,
-   },
-   {
-   xtype: 'proxmoxtextfield',
-   name: 'comment',
-   fieldLabel: gettext('Comment'),
-   cbind: {
-   deleteEmpty: '{!isCreate}',
-   },
-   },
-],
-});
-
-Ext.define('Proxmox.form.NotificationEndpointSelector', {
-extend: 'Ext.grid.Panel',
-alias: 'widget.pmxNotificationEndpointSelector',
-
-mixins: {
-   field: 'Ext.form.field.Field',
-},
-
-padding: '0 0 10 0',
-
-allowBlank: true,
-selectAll: false,
-isFormField: true,
-
-store: {
-   autoLoad: true,
-   model: 'proxmox-notification-endpoints',
-   sorters: 'name',
-   filters: item => item.data.type !== 'group',
-},
-
-columns: [
-   {
-   header: gettext('Endpoint Name'),
-   dataIndex: 'name',
-   flex: 1,
-   },
-   {
-   header: gettext('Type'),
-   dataIndex: 'type',
-   flex: 1,
-   },
-   {
-   header: gettext('Comment'),
-   dataIndex: 'comment',
-   flex: 3,
-   },
-],
-
-selModel: {
-   selType: 'checkboxmodel',
-   mode: 'SIMPLE',
-},
-
-checkChangeEvents: [
-   'selectionchange',
-   'change',
-],
-
-listeners: {
-   selectionchange: function() {
-   // to trigger validity and error checks
-   this.checkChange();
-   },
-},
-
-getSubmitData: function() {
-   let me = this;
-   let res = {};
-   res[me.name] = me.getValue();
-   return res;
-},
-
-getValue: function() {
-   let me = this;
-   if (me.savedValue !== undefined) {
-   return me.savedValue;
-   }
-   let sm = me.getSelectionModel();
-   return (sm.getSelection() ?? []).map(item => item.data.name);
-},
-
-setValueSelection: function(value) {
-   let me = this;
-
-   let store = me.getStore();
-
-   let notFound = [];
-   let selection = value.map(item => {
-   let found = store.findRecord('name', item, 0, false, true, true);
-   if (!found) {
-   notFound.push(item);
- 

[pve-devel] [PATCH v2 pve-docs 47/52] notifications: document SMTP endpoints

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 notifications.adoc | 47 ++
 1 file changed, 47 insertions(+)

diff --git a/notifications.adoc b/notifications.adoc
index 764ec72..acbdfae 100644
--- a/notifications.adoc
+++ b/notifications.adoc
@@ -67,6 +67,7 @@ accomodate multiple recipients.
 set, the plugin will fall back to the `email_from` setting from
 `datacenter.cfg`. If that is also not set, the plugin will default to
 `root@$hostname`, where `$hostname` is the hostname of the node.
+The `From` header in the email will be set to `$author <$from-address>`.
 
 Example configuration (`/etc/pve/notifications.cfg`):
 
@@ -78,6 +79,52 @@ sendmail: example
 comment Send to multiple users/addresses
 
 
+SMTP
+
+
+SMTP notification targets can send emails directly to an SMTP mail relay.
+
+The configuration for SMTP target plugins has the following options:
+
+* `mailto`: E-Mail address to which the notification shall be sent to. Can be
+set multiple times to accomodate multiple recipients.
+* `mailto-user`: Users to which emails shall be sent to. The user's email
+address will be looked up in `users.cfg`. Can be set multiple times to
+accomodate multiple recipients.
+* `author`: Sets the author of the E-Mail. Defaults to `Proxmox VE`.
+* `from-address`: Sets the From-addresss of the email. SMTP relays might 
require
+that this address is owned by the user in order to avoid spoofing.
+The `From` header in the email will be set to `$author <$from-address>`.
+* `username`: Username to use during authentication. If no username is set,
+no authentication will be performed. The PLAIN and LOGIN authentication methods
+are supported.
+* `password`: Password to use when authenticating.
+* `mode`: Sets the encryption mode (`insecure`, `starttls` or `tls`). Defaults
+to `tls`.
+* `server`: Address/IP of the SMTP relay
+* `port`: The port to connect to. If not set, the used port
+defaults to 25 (`insecure`), 465 (`tls`) or 587 (`starttls`), depending on the
+value of `mode`.
+* `comment`: Comment for this target
+
+Example configuration (`/etc/pve/notifications.cfg`):
+
+smtp: example
+mailto-user root@pam
+mailto-user admin@pve
+mailto m...@example.com
+from-address p...@example.com
+username pve1
+server mail.example.com
+mode starttls
+
+The matching entry in `/etc/pve/priv/notifications.cfg`, containing the
+secret token:
+
+smtp: example
+password somepassword
+
+
 Gotify
 ~~
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-mail-forward 51/52] feed forwarded mails into proxmox_notify

2023-11-14 Thread Lukas Wagner
This allows us to send notifications for events from daemons that are
not under our control, e.g. zed, smartd, cron. etc...

For mail-based notification targets (sendmail, soon smtp) the mail is
forwarded as is, including all headers.
All other target types will try to parse the email to extra subject
and text body.

On PBS, where proxmox-notify is not yet fully integrated,
we simply add a default target/matcher to an empty config. That way
the behavior should be unchanged - mails will be forwarded to
root@pam.

Signed-off-by: Lukas Wagner 
---

Notes:
Changes v2 -> v3:
  - Update to matcher-based system - no need to read
datacenter.cfg/node.cfg any more

 Cargo.toml  |   6 +-
 src/main.rs | 255 
 2 files changed, 121 insertions(+), 140 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index c68e802..a562f3e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ name = "proxmox-mail-forward"
 version = "0.2.0"
 authors = [
 "Fiona Ebner ",
+"Lukas Wagner ",
 "Proxmox Support Team ",
 ]
 edition = "2021"
@@ -16,10 +17,7 @@ exclude = [ "debian" ]
 anyhow = "1.0"
 log = "0.4.17"
 nix = "0.26"
-serde = { version = "1.0", features = ["derive"] }
-#serde_json = "1.0"
 syslog = "6.0"
 
-proxmox-schema = "1.3"
-proxmox-section-config = "1.0.2"
 proxmox-sys = "0.5"
+proxmox-notify = {version = "0.2", features = ["mail-forwarder", 
"pve-context", "pbs-context"] }
diff --git a/src/main.rs b/src/main.rs
index f3d4193..e56bc1e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,132 +1,124 @@
+//! A helper binary that forwards any mail passed via stdin to
+//! proxmox_notify.
+//!
+//! The binary's path is added to /root/.forward, which means that
+//! postfix will invoke it when the local root user receives an email message.
+//! The message is passed via stdin.
+//! The binary is installed with setuid permissions and will thus run as
+//! root (euid ~ root, ruid ~ nobody)
+//!
+//! The forwarding behavior is the following:
+//!   - PVE installed: Use PVE's notifications.cfg
+//!   - PBS installed: Use PBS's notifications.cfg if present. If not,
+//! use an empty configuration and add a default sendmail target and
+//! a matcher - this is needed because notifications are not yet
+//! integrated in PBS.
+//!   - PVE/PBS co-installed: Use PVE's config *and* PBS's config, but if
+//! PBS's config does not exist, a default sendmail target will *not* be
+//! added. We assume that PVE's config contains the desired notification
+//! behavior for system mails.
+//!
+use std::io::Read;
 use std::path::Path;
-use std::process::Command;
 
-use anyhow::{bail, format_err, Error};
-use serde::Deserialize;
+use anyhow::Error;
 
-use proxmox_schema::{ObjectSchema, Schema, StringSchema};
-use proxmox_section_config::{SectionConfig, SectionConfigPlugin};
+use proxmox_notify::context::pbs::PBS_CONTEXT;
+use proxmox_notify::context::pve::PVE_CONTEXT;
+use proxmox_notify::endpoints::sendmail::SendmailConfig;
+use proxmox_notify::matcher::MatcherConfig;
+use proxmox_notify::Config;
 use proxmox_sys::fs;
 
-const PBS_USER_CFG_FILENAME:  = "/etc/proxmox-backup/user.cfg";
-const PBS_ROOT_USER:  = "root@pam";
-
-// FIXME: Switch to the actual schema when possible in terms of dependency.
-// It's safe to assume that the config was written with the actual schema 
restrictions, so parsing
-// it with the less restrictive schema should be enough for the purpose of 
getting the mail address.
-const DUMMY_ID_SCHEMA: Schema = StringSchema::new("dummy 
ID").min_length(3).schema();
-const DUMMY_EMAIL_SCHEMA: Schema = StringSchema::new("dummy email").schema();
-const DUMMY_USER_SCHEMA: ObjectSchema = ObjectSchema {
-description: "minimal PBS user",
-properties: &[
-("userid", false, _ID_SCHEMA),
-("email", true, _EMAIL_SCHEMA),
-],
-additional_properties: true,
-default_key: None,
-};
-
-#[derive(Deserialize)]
-struct DummyPbsUser {
-pub email: Option,
-}
-
-const PVE_USER_CFG_FILENAME:  = "/etc/pve/user.cfg";
-const PVE_DATACENTER_CFG_FILENAME:  = "/etc/pve/datacenter.cfg";
-const PVE_ROOT_USER:  = "root@pam";
+const PVE_CFG_PATH:  = "/etc/pve";
+const PVE_PUB_NOTIFICATION_CFG_FILENAME:  = "/etc/pve/notifications.cfg";
+const PVE_PRIV_NOTIFICATION_CFG_FILENAME:  = 
"/etc/pve/priv/notifications.cfg";
 
-/// Convenience helper to get the trimmed contents of an optional , 
mapping blank ones to `None`
-/// and creating a String from it for returning.
-fn normalize_for_return(s: Option<>) -> Option {
-match s?.trim() {
-"" => None,
-s => Some(s.to_string()),
-}
-}
+const PBS_CFG_PATH:  = "/etc/proxmox-backup";
+const PBS_PUB_NOTIFICATION_CFG_FILENAME:  = 
"/etc/proxmox-backup/notifications.cfg";
+const PBS_PRIV_NOTIFICATION_CFG_FILENAME:  = 
"/etc/proxmox-backup/notifications-priv.cfg";
 
-/// Extract the root user's email address from the PBS user config.
-fn get_pbs_mail_to(content: ) -> 

[pve-devel] [PATCH v2 pve-docs 46/52] notifications: update docs to for matcher-based notifications

2023-11-14 Thread Lukas Wagner
Target groups and filters have been replaced by notification matchers.
The matcher can match on certain notification properties and route
the notification to a target in case of a match.

This patch updates the docs to reflect these changes.

Signed-off-by: Lukas Wagner 
---
 notifications.adoc | 254 +++--
 1 file changed, 174 insertions(+), 80 deletions(-)

diff --git a/notifications.adoc b/notifications.adoc
index c4d2931..764ec72 100644
--- a/notifications.adoc
+++ b/notifications.adoc
@@ -5,45 +5,40 @@ ifndef::manvolnum[]
 :pve-toplevel:
 endif::manvolnum[]
 
-[[notification_events]]
-Notification Events

-
-{pve} will attempt to notify system administrators in case of certain events,
-such as:
-
-[width="100%",options="header"]
-|===
-| Event name| Description | Severity
-| `package-updates` | System updates are available| `info`
-| `fencing` | The {pve} HA manager has fenced a node  | `error`
-| `replication` | A storage replication job has failed| `error`
-| `vzdump`  | vzdump backup finished  | `info` 
(`error` on failure)
-|===
-
-In the 'Notification' panel of the datacenter view, the system's behavior can 
be
-configured for all events except backup jobs. For backup jobs,
-the settings can be found in the respective backup job configuration.
-For every notification event there is an option to configure the notification
-behavior (*when* to send a notification) and the notification target (*where* 
to
-send the notification).
-
-
-See also:
-
-* xref:datacenter_configuration_file[Datacenter Configuration]
-* xref:datacenter_configuration_file[vzdump]
+Overview
+
+
+{pve} will send notifications if case of noteworthy events in the system.
+
+There are a number of different xref:notification_events[notification events],
+each with their own set of metadata fields that can be used in
+notification matchers.
+
+A xref:notification_matchers[notification matcher] determines
+_which_ notifications shall be sent _where_.
+A matcher has _match rules_, that can be used to
+match on certain notification properties (e.g. timestamp, severity,
+metadata fields).
+If a matcher matches a notification, the notification will be routed
+to a configurable set of notification targets.
+
+A xref:notification_targets[notification target] is an abstraction for a
+destination where a notification should be sent to - for instance,
+a Gotify server instance, or a set of email addresses.
+There are multiple types of notification targets, including
+`sendmail`, which uses the system's sendmail command to send emails,
+or `gotify`, which sends a notification to a Gotify instance.
+
+The notification system can be configured in the GUI under
+Datacenter -> Notifications. The configuration is stored in
+`/etc/pve/notifications.cfg` and `/etc/pve/priv/notifications.cfg` -
+the latter contains sensitive configuration options such as
+passwords or authentication tokens for notification targets.
 
 [[notification_targets]]
 Notification Targets
 
 
-Notification targets can be configured in the 'Notification Targets' panel.
-
-NOTE: The `mail-to-root` target is always available and cannot be modified or
-removed. It sends a mail the `root@pam` user by using the `sendmail` command 
and
-serves as a fallback target if no other target is configured for an event.
-
 Sendmail
 
 The sendmail binary is a program commonly found on Unix-like operating systems
@@ -73,7 +68,15 @@ set, the plugin will fall back to the `email_from` setting 
from
 `datacenter.cfg`. If that is also not set, the plugin will default to
 `root@$hostname`, where `$hostname` is the hostname of the node.
 
-* `filter`: The name of the filter to use for this target.
+Example configuration (`/etc/pve/notifications.cfg`):
+
+sendmail: example
+mailto-user root@pam
+mailto-user admin@pve
+mailto m...@example.com
+from-address p...@example.com
+comment Send to multiple users/addresses
+
 
 Gotify
 ~~
@@ -88,72 +91,163 @@ The configuration for Gotify target plugins has the 
following options:
 * `server`: The base URL of the Gotify server, e.g. `http://:`
 * `token`: The authentication token. Tokens can be generated within the Gotify
 web interface.
-* `filter`: The name of the filter to use for this target.
 
 NOTE: The Gotify target plugin will respect the HTTP proxy settings from the
  xref:datacenter_configuration_file[datacenter configuration]
 
-Group
-~
+Example configuration (`/etc/pve/notifications.cfg`):
+
+gotify: example
+server http://gotify.example.com:
+comment Send to multiple users/addresses
+
 
-One can only select a single target for notification 

[pve-devel] [PATCH v2 pve-manager 35/52] api: notification: simplify ACLs for notification

2023-11-14 Thread Lukas Wagner
Use coarse-grained /mapping/notifications for now. We
can always extend later if we need to.

Signed-off-by: Lukas Wagner 
---
 PVE/API2/Cluster/Notifications.pm | 131 --
 1 file changed, 54 insertions(+), 77 deletions(-)

diff --git a/PVE/API2/Cluster/Notifications.pm 
b/PVE/API2/Cluster/Notifications.pm
index 27e3a66d..7047f0b1 100644
--- a/PVE/API2/Cluster/Notifications.pm
+++ b/PVE/API2/Cluster/Notifications.pm
@@ -56,24 +56,6 @@ sub raise_api_error {
 die $exc;
 }
 
-sub filter_entities_by_privs {
-my ($rpcenv, $entities) = @_;
-my $authuser = $rpcenv->get_user();
-
-my $can_see_mapping_privs = ['Mapping.Modify', 'Mapping.Use', 
'Mapping.Audit'];
-
-my $filtered = [grep {
-   $rpcenv->check_any(
-   $authuser,
-   "/mapping/notification/$_->{name}",
-   $can_see_mapping_privs,
-   1
-   );
-} @$entities];
-
-return $filtered;
-}
-
 __PACKAGE__->register_method ({
 name => 'index',
 path => '',
@@ -137,10 +119,11 @@ __PACKAGE__->register_method ({
 method => 'GET',
 description => 'Returns a list of all entities that can be used as 
notification targets.',
 permissions => {
-   description => "Only lists entries where you have 'Mapping.Modify', 
'Mapping.Use' or"
-   . " 'Mapping.Audit' permissions on '/mapping/notification/'."
-   . " The special 'mail-to-root' target is available to all users.",
-   user => 'all',
+   check => ['or',
+   ['perm', '/mapping/notifications', ['Mapping.Modify']],
+   ['perm', '/mapping/notifications', ['Mapping.Audit']],
+   ['perm', '/mapping/notifications', ['Mapping.Use']],
+   ],
 },
 protected => 1,
 parameters => {
@@ -184,7 +167,6 @@ __PACKAGE__->register_method ({
 },
 code => sub {
my $config = PVE::Notify::read_config();
-   my $rpcenv = PVE::RPCEnvironment::get();
 
my $targets = eval {
my $result = [];
@@ -224,7 +206,7 @@ __PACKAGE__->register_method ({
 
raise_api_error($@) if $@;
 
-   return filter_entities_by_privs($rpcenv, $targets);
+   return $targets;
 }
 });
 
@@ -235,10 +217,11 @@ __PACKAGE__->register_method ({
 method => 'POST',
 description => 'Send a test notification to a provided target.',
 permissions => {
-   description => "The user requires 'Mapping.Modify', 'Mapping.Use' or"
-   . " 'Mapping.Audit' permissions on '/mapping/notification/'."
-   . " The special 'mail-to-root' target can be accessed by all 
users.",
-   user => 'all',
+   check => ['or',
+   ['perm', '/mapping/notifications', ['Mapping.Modify']],
+   ['perm', '/mapping/notifications', ['Mapping.Audit']],
+   ['perm', '/mapping/notifications', ['Mapping.Use']],
+   ],
 },
 parameters => {
additionalProperties => 0,
@@ -254,16 +237,6 @@ __PACKAGE__->register_method ({
 code => sub {
my ($param) = @_;
my $name = extract_param($param, 'name');
-   my $rpcenv = PVE::RPCEnvironment::get();
-   my $authuser = $rpcenv->get_user();
-
-   my $privs = ['Mapping.Modify', 'Mapping.Use', 'Mapping.Audit'];
-
-   $rpcenv->check_any(
-   $authuser,
-   "/mapping/notification/$name",
-   $privs,
-   );
 
eval {
my $config = PVE::Notify::read_config();
@@ -329,9 +302,10 @@ __PACKAGE__->register_method ({
 method => 'GET',
 description => 'Returns a list of all sendmail endpoints',
 permissions => {
-   description => "Only lists entries where you have 'Mapping.Modify', 
'Mapping.Use' or"
-   . " 'Mapping.Audit' permissions on '/mapping/notification/'.",
-   user => 'all',
+   check => ['or',
+   ['perm', '/mapping/notifications', ['Mapping.Modify']],
+   ['perm', '/mapping/notifications', ['Mapping.Audit']],
+   ],
 },
 protected => 1,
 parameters => {
@@ -355,14 +329,13 @@ __PACKAGE__->register_method ({
 },
 code => sub {
my $config = PVE::Notify::read_config();
-   my $rpcenv = PVE::RPCEnvironment::get();
 
my $entities = eval {
$config->get_sendmail_endpoints();
};
raise_api_error($@) if $@;
 
-   return filter_entities_by_privs($rpcenv, $entities);
+   return $entities;
 }
 });
 
@@ -373,8 +346,8 @@ __PACKAGE__->register_method ({
 description => 'Return a specific sendmail endpoint',
 permissions => {
check => ['or',
-   ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
-   ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
+   ['perm', '/mapping/notifications', ['Mapping.Modify']],
+   ['perm', '/mapping/notifications', ['Mapping.Audit']],
],
 },
 protected => 1,
@@ -418,7 +391,7 @@ __PACKAGE__->register_method ({
 method => 'POST',
 description => 'Create a 

[pve-devel] [PATCH v2 pve-manager 25/52] api: notification: add new matcher-based notification API

2023-11-14 Thread Lukas Wagner
This renames filters -> matchers and adds new configuration options
needed by matchers (e.g. match-field, match-calendar, etc.)

Signed-off-by: Lukas Wagner 
---
 PVE/API2/Cluster/Notifications.pm | 195 ++
 1 file changed, 88 insertions(+), 107 deletions(-)

diff --git a/PVE/API2/Cluster/Notifications.pm 
b/PVE/API2/Cluster/Notifications.pm
index b34802c8..8f716f26 100644
--- a/PVE/API2/Cluster/Notifications.pm
+++ b/PVE/API2/Cluster/Notifications.pm
@@ -68,37 +68,12 @@ sub filter_entities_by_privs {
"/mapping/notification/$_->{name}",
$can_see_mapping_privs,
1
-   ) || $_->{name} eq PVE::Notify::default_target();
+   );
 } @$entities];
 
 return $filtered;
 }
 
-sub target_used_by {
-my ($target) = @_;
-
-my $used_by = [];
-
-# Check keys in datacenter.cfg
-my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
-for my $key (qw(target-package-updates target-replication target-fencing)) 
{
-   if ($dc_conf->{notify} && $dc_conf->{notify}->{$key} eq $target) {
-   push @$used_by, $key;
-   }
-}
-
-# Check backup jobs
-my $jobs_conf = PVE::Cluster::cfs_read_file('jobs.cfg');
-for my $key (keys %{$jobs_conf->{ids}}) {
-   my $job = $jobs_conf->{ids}->{$key};
-   if ($job->{'notification-target'} eq $target) {
-   push @$used_by, $key;
-   }
-}
-
-return join(', ', @$used_by);
-}
-
 __PACKAGE__->register_method ({
 name => 'index',
 path => '',
@@ -120,7 +95,7 @@ __PACKAGE__->register_method ({
 code => sub {
my $result = [
{ name => 'endpoints' },
-   { name => 'filters' },
+   { name => 'matchers' },
{ name => 'targets' },
];
 
@@ -259,15 +234,11 @@ __PACKAGE__->register_method ({
 
my $privs = ['Mapping.Modify', 'Mapping.Use', 'Mapping.Audit'];
 
-   if ($name ne PVE::Notify::default_target()) {
-   # Due to backwards compatibility reasons the 'mail-to-root'
-   # target must be accessible for any user
-   $rpcenv->check_any(
-   $authuser,
-   "/mapping/notification/$name",
-   $privs,
-   );
-   }
+   $rpcenv->check_any(
+   $authuser,
+   "/mapping/notification/$name",
+   $privs,
+   );
 
eval {
my $config = PVE::Notify::read_config();
@@ -319,12 +290,6 @@ my $sendmail_properties = {
type=> 'string',
optional=> 1,
 },
-filter => {
-   description => 'Name of the filter that should be applied.',
-   type => 'string',
-   format => 'pve-configid',
-   optional => 1,
-},
 };
 
 __PACKAGE__->register_method ({
@@ -431,7 +396,6 @@ __PACKAGE__->register_method ({
my $from_address = extract_param($param, 'from-address');
my $author = extract_param($param, 'author');
my $comment = extract_param($param, 'comment');
-   my $filter = extract_param($param, 'filter');
 
eval {
PVE::Notify::lock_config(sub {
@@ -444,7 +408,6 @@ __PACKAGE__->register_method ({
$from_address,
$author,
$comment,
-   $filter
);
 
PVE::Notify::write_config($config);
@@ -492,7 +455,6 @@ __PACKAGE__->register_method ({
my $from_address = extract_param($param, 'from-address');
my $author = extract_param($param, 'author');
my $comment = extract_param($param, 'comment');
-   my $filter = extract_param($param, 'filter');
 
my $delete = extract_param($param, 'delete');
my $digest = extract_param($param, 'digest');
@@ -508,7 +470,6 @@ __PACKAGE__->register_method ({
$from_address,
$author,
$comment,
-   $filter,
$delete,
$digest,
);
@@ -545,11 +506,6 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $name = extract_param($param, 'name');
 
-   my $used_by = target_used_by($name);
-   if ($used_by) {
-   raise_param_exc({'name' => "Cannot remove $name, used by: 
$used_by"});
-   }
-
eval {
PVE::Notify::lock_config(sub {
my $config = PVE::Notify::read_config();
@@ -582,12 +538,6 @@ my $gotify_properties = {
type=> 'string',
optional=> 1,
 },
-'filter' => {
-   description => 'Name of the filter that should be applied.',
-   type => 'string',
-   format => 'pve-configid',
-   optional => 1,
-}
 };
 
 __PACKAGE__->register_method ({
@@ -692,7 +642,6 @@ __PACKAGE__->register_method ({
my $server = extract_param($param, 'server');
my $token = extract_param($param, 'token');
my $comment = extract_param($param, 'comment');
-   my $filter = 

[pve-devel] [PATCH v2 proxmox 15/52] notify: add built-in config and 'origin' parameter

2023-11-14 Thread Lukas Wagner
This allows us to define a (modifiable) builtin-config, which is
at the moment hardcoded in PVEContext

The 'origin' parameter indicates whether a config entry was created by
a user, builtin or a modified builtin.

These changes require context to be set for tests, so we set
PVEContext by default if in a test context. There might be a nicer
solution for that, but for now this should work.

Signed-off-by: Lukas Wagner 
---
 proxmox-notify/src/api/gotify.rs |  2 +-
 proxmox-notify/src/api/matcher.rs|  3 +-
 proxmox-notify/src/api/sendmail.rs   |  5 +-
 proxmox-notify/src/api/smtp.rs   | 24 
 proxmox-notify/src/context/mod.rs|  7 +++
 proxmox-notify/src/context/pbs.rs| 16 +
 proxmox-notify/src/context/pve.rs| 16 +
 proxmox-notify/src/endpoints/gotify.rs   |  6 +-
 proxmox-notify/src/endpoints/sendmail.rs |  6 +-
 proxmox-notify/src/endpoints/smtp.rs |  6 +-
 proxmox-notify/src/lib.rs| 77 +++-
 proxmox-notify/src/matcher.rs|  7 ++-
 12 files changed, 150 insertions(+), 25 deletions(-)

diff --git a/proxmox-notify/src/api/gotify.rs b/proxmox-notify/src/api/gotify.rs
index 10f5d7d..98ff255 100644
--- a/proxmox-notify/src/api/gotify.rs
+++ b/proxmox-notify/src/api/gotify.rs
@@ -165,7 +165,7 @@ fn remove_private_config_entry(config:  Config, name: 
) -> Result<(), Ht
 Ok(())
 }
 
-#[cfg(test)]
+#[cfg(all(feature = "pve-context", test))]
 mod tests {
 use super::*;
 use crate::api::test_helpers::empty_config;
diff --git a/proxmox-notify/src/api/matcher.rs 
b/proxmox-notify/src/api/matcher.rs
index a69ca40..ca01bc9 100644
--- a/proxmox-notify/src/api/matcher.rs
+++ b/proxmox-notify/src/api/matcher.rs
@@ -151,7 +151,7 @@ pub fn delete_matcher(config:  Config, name: ) -> 
Result<(), HttpError>
 Ok(())
 }
 
-#[cfg(all(test, feature = "sendmail"))]
+#[cfg(all(test, feature = "sendmail", feature = "pve-context"))]
 mod tests {
 use super::*;
 use crate::matcher::MatchModeOperator;
@@ -259,7 +259,6 @@ matcher: matcher2
 
 delete_matcher( config, "matcher1")?;
 assert!(delete_matcher( config, "matcher1").is_err());
-assert_eq!(get_matchers()?.len(), 1);
 
 Ok(())
 }
diff --git a/proxmox-notify/src/api/sendmail.rs 
b/proxmox-notify/src/api/sendmail.rs
index 1f6e9ae..0f40178 100644
--- a/proxmox-notify/src/api/sendmail.rs
+++ b/proxmox-notify/src/api/sendmail.rs
@@ -151,7 +151,7 @@ pub fn delete_endpoint(config:  Config, name: ) -> 
Result<(), HttpError>
 Ok(())
 }
 
-#[cfg(test)]
+#[cfg(all(feature = "pve-context", test))]
 pub mod tests {
 use super::*;
 use crate::api::test_helpers::*;
@@ -182,12 +182,10 @@ pub mod tests {
 fn test_sendmail_create() -> Result<(), HttpError> {
 let mut config = empty_config();
 
-assert_eq!(get_endpoints()?.len(), 0);
 add_sendmail_endpoint_for_test( config, "sendmail-endpoint")?;
 
 // Endpoints must have a unique name
 assert!(add_sendmail_endpoint_for_test( config, 
"sendmail-endpoint").is_err());
-assert_eq!(get_endpoints()?.len(), 1);
 Ok(())
 }
 
@@ -287,7 +285,6 @@ pub mod tests {
 
 delete_endpoint( config, "sendmail-endpoint")?;
 assert!(delete_endpoint( config, "sendmail-endpoint").is_err());
-assert_eq!(get_endpoints()?.len(), 0);
 
 Ok(())
 }
diff --git a/proxmox-notify/src/api/smtp.rs b/proxmox-notify/src/api/smtp.rs
index aca08e8..14b301c 100644
--- a/proxmox-notify/src/api/smtp.rs
+++ b/proxmox-notify/src/api/smtp.rs
@@ -200,7 +200,7 @@ pub fn delete_endpoint(config:  Config, name: ) -> 
Result<(), HttpError>
 Ok(())
 }
 
-#[cfg(test)]
+#[cfg(all(feature = "pve-context", test))]
 pub mod tests {
 use super::*;
 use crate::api::test_helpers::*;
@@ -348,15 +348,15 @@ pub mod tests {
 Ok(())
 }
 
-#[test]
-fn test_delete() -> Result<(), HttpError> {
-let mut config = empty_config();
-add_smtp_endpoint_for_test( config, "smtp-endpoint")?;
-
-delete_endpoint( config, "smtp-endpoint")?;
-assert!(delete_endpoint( config, "smtp-endpoint").is_err());
-assert_eq!(get_endpoints()?.len(), 0);
-
-Ok(())
-}
+// #[test]
+// fn test_delete() -> Result<(), HttpError> {
+// let mut config = empty_config();
+// add_smtp_endpoint_for_test( config, "smtp-endpoint")?;
+//
+// delete_endpoint( config, "smtp-endpoint")?;
+// assert!(delete_endpoint( config, "smtp-endpoint").is_err());
+// assert_eq!(get_endpoints()?.len(), 0);
+//
+// Ok(())
+// }
 }
diff --git a/proxmox-notify/src/context/mod.rs 
b/proxmox-notify/src/context/mod.rs
index 99d86de..b419641 100644
--- a/proxmox-notify/src/context/mod.rs
+++ b/proxmox-notify/src/context/mod.rs
@@ -18,9 +18,16 @@ pub trait Context: Send + Sync + Debug {
 fn default_sendmail_from() -> String;
 /// Proxy 

[pve-devel] [PATCH v2 pve-manager 26/52] ui: dc: remove unneeded notification events panel

2023-11-14 Thread Lukas Wagner
The notification event settings are replaced by notification matchers,
which will combine the notification routing and filtering into a
single concept.

Signed-off-by: Lukas Wagner 
---
 www/manager6/Makefile |   4 -
 www/manager6/dc/Config.js |  17 +-
 www/manager6/dc/NotificationEvents.js | 276 --
 3 files changed, 2 insertions(+), 295 deletions(-)
 delete mode 100644 www/manager6/dc/NotificationEvents.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 57e1b48f..18baa024 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -159,7 +159,6 @@ JSSRC=  
\
dc/Health.js\
dc/Log.js   \
dc/NodeView.js  \
-   dc/NotificationEvents.js\
dc/OptionView.js\
dc/PermissionView.js\
dc/PoolEdit.js  \
@@ -346,6 +345,3 @@ install: pvemanagerlib.js
 .PHONY: clean
 clean:
rm -rf pvemanagerlib.js OnlineHelpInfo.js .lint-incremental
-
-
-
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5f..0dea1c67 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -319,18 +319,6 @@ Ext.define('PVE.dc.Config', {
 
// this is being reworked, but we need to release newer manager 
versions already..
let notification_enabled = false;
-   if (notification_enabled && caps.dc['Sys.Audit']) {
-   me.items.push(
-   {
-   xtype: 'pveNotificationEvents',
-   title: gettext('Notifications'),
-   onlineHelp: 'notification_events',
-   iconCls: 'fa fa-bell-o',
-   itemId: 'notifications',
-   },
-   );
-   }
-
if (notification_enabled && (
caps.mapping['Mapping.Audit'] ||
caps.mapping['Mapping.Use'] ||
@@ -340,12 +328,11 @@ Ext.define('PVE.dc.Config', {
me.items.push(
{
xtype: 'pmxNotificationConfigView',
-   title: gettext('Notification Targets'),
+   title: gettext('Notifications'),
onlineHelp: 'notification_targets',
itemId: 'notification-targets',
-   iconCls: 'fa fa-dot-circle-o',
+   iconCls: 'fa fa-bell-o',
baseUrl: '/cluster/notifications',
-   groups: ['notifications'],
},
);
}
diff --git a/www/manager6/dc/NotificationEvents.js 
b/www/manager6/dc/NotificationEvents.js
deleted file mode 100644
index 18816290..
--- a/www/manager6/dc/NotificationEvents.js
+++ /dev/null
@@ -1,276 +0,0 @@
-Ext.define('PVE.dc.NotificationEventsPolicySelector', {
-alias: ['widget.pveNotificationEventsPolicySelector'],
-extend: 'Proxmox.form.KVComboBox',
-deleteEmpty: false,
-value: '__default__',
-
-config: {
-   warningRef: null,
-   warnIfValIs: null,
-},
-
-listeners: {
-   change: function(field, newValue) {
-   let me = this;
-   if (!me.warningRef && !me.warnIfValIs) {
-   return;
-   }
-
-   let warningField = field.nextSibling(
-   `displayfield[reference=${me.warningRef}]`,
-   );
-   warningField.setVisible(newValue === me.warnIfValIs);
-   },
-},
-});
-
-Ext.define('PVE.dc.NotificationEventDisabledWarning', {
-alias: ['widget.pveNotificationEventDisabledWarning'],
-extend: 'Ext.form.field.Display',
-userCls: 'pmx-hint',
-hidden: true,
-value: gettext('Disabling notifications is not recommended for production 
systems!'),
-});
-
-Ext.define('PVE.dc.NotificationEventsTargetSelector', {
-alias: ['widget.pveNotificationEventsTargetSelector'],
-extend: 'PVE.form.NotificationTargetSelector',
-fieldLabel: gettext('Notification Target'),
-allowBlank: true,
-editable: true,
-autoSelect: false,
-deleteEmpty: false,
-emptyText: `${Proxmox.Utils.defaultText} (mail-to-root)`,
-});
-
-Ext.define('PVE.dc.NotificationEvents', {
-extend: 'Proxmox.grid.ObjectGrid',
-alias: ['widget.pveNotificationEvents'],
-
-// Taken from OptionView.js, but adapted slightly.
-// The modified version allows us to have multiple rows in the ObjectGrid
-// for the same underlying property (notify).
-// Every setting is eventually stored as a property string in the
-// notify key of datacenter.cfg.
-// When updating 'notify', all properties that were already set
-// also have to be submitted, even if they were not modified.
-// This means that we need to save the old value somewhere.
-addInputPanelRow: function(name, 

[pve-devel] [PATCH v2 pve-docs 49/52] notifications: add documentation for system mail forwarding

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---

Notes:
Changes v2 -> v3:
  - Dropped paragraph about target/policy, since we now do routing in
matchers

 notifications.adoc | 16 
 1 file changed, 16 insertions(+)

diff --git a/notifications.adoc b/notifications.adoc
index e8ed51b..c7bdc5a 100644
--- a/notifications.adoc
+++ b/notifications.adoc
@@ -281,6 +281,7 @@ Notification Events
 | Cluster node fenced  |`fencing`  | `error`  | `hostname`
 | Storage replication failed   |`replication`  | `error`  | -
 | Backup finished  |`vzdump`   | `info` (`error` on 
failure) | `hostname`
+| Mail for root|`system-mail`  | `unknown`| -
 |===
 
 [width="100%",options="header"]
@@ -290,6 +291,21 @@ Notification Events
 | `hostname` | Hostname, including domain (e.g. `pve1.example.com`)
 |===
 
+System Mail Forwarding
+-
+
+Certain local system daemons, such as `smartd`, generate notification emails
+that are initially directed to the local `root` user. {pve} will
+feed these mails into the notification system as a notification of
+type `system-mail` and with severity `unknown`.
+
+When the forwarding process involves an email-based target
+(like `sendmail` or `smtp`), the email is forwarded exactly as received, with 
all
+original mail headers remaining intact. For all other targets,
+the system tries to extract both a subject line and the main text body
+from the email content. In instances where emails solely consist of HTML
+content, they will be transformed into plain text format during this process.
+
 Permissions
 ---
 
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 pve-docs 50/52] notifications: change to simplified ACL structure.

2023-11-14 Thread Lukas Wagner
For now, we use a less deeply nested structure. We can always extend
it if we need to.

Signed-off-by: Lukas Wagner 
---
 notifications.adoc | 14 ++
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/notifications.adoc b/notifications.adoc
index c7bdc5a..74447e5 100644
--- a/notifications.adoc
+++ b/notifications.adoc
@@ -309,11 +309,9 @@ content, they will be transformed into plain text format 
during this process.
 Permissions
 ---
 
-For every target, there exists a corresponding ACL path
-`/mapping/notification/targets/`. Matchers use
-a seperate namespace in the ACL tree: `/mapping/notification/matchers/`.
-
-To test a target, a user must have the `Mapping.Use` permission on the 
corresponding
-node in the ACL tree.
-`Mapping.Modify` and `Mapping.Audit` are needed to read/modify the
-configuration of a target or matcher.
+In order to modify/view the configuration for notification targets,
+the `Mapping.Modify/Mapping.Audit` permissions are required for the
+`/mapping/notifications` ACL node.
+
+Testing a target requires `Mapping.Use`, `Mapping.Audit` or `Mapping.Modify`
+permissions on `/mapping/notifications`
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox 11/52] notify: add PVE/PBS context

2023-11-14 Thread Lukas Wagner
This commit moves PVEContext from `proxmox-perl-rs` into the
`proxmox-notify` crate, since we now also need to access it from
`promxox-mail-forward`. The context is now hidden behind a feature
flag `pve-context`, ensuring that we only compile it when needed.

This commit adds PBSContext, since we now require it for
`proxmox-mail-forward`. Some of the code for PBSContext comes
from `proxmox-mail-forward`.

This commit also changes the global context from being stored in a
`once_cell` to a regular `Mutex`, since we now need to set/reset
the context in `proxmox-mail-forward`.

Signed-off-by: Lukas Wagner 
---

Notes:
Changes v2 -> v3:
  - no changes

 proxmox-notify/Cargo.toml|   3 +-
 proxmox-notify/src/context.rs|  21 -
 proxmox-notify/src/context/common.rs |  27 ++
 proxmox-notify/src/context/mod.rs|  36 
 proxmox-notify/src/context/pbs.rs| 130 +++
 proxmox-notify/src/context/pve.rs|  82 +
 6 files changed, 277 insertions(+), 22 deletions(-)
 delete mode 100644 proxmox-notify/src/context.rs
 create mode 100644 proxmox-notify/src/context/common.rs
 create mode 100644 proxmox-notify/src/context/mod.rs
 create mode 100644 proxmox-notify/src/context/pbs.rs
 create mode 100644 proxmox-notify/src/context/pve.rs

diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
index f2b4db5..7a3d434 100644
--- a/proxmox-notify/Cargo.toml
+++ b/proxmox-notify/Cargo.toml
@@ -13,7 +13,6 @@ handlebars = { workspace = true }
 lazy_static.workspace = true
 log.workspace = true
 mail-parser = { workspace = true, optional = true }
-once_cell.workspace = true
 openssl.workspace = true
 proxmox-http = { workspace = true, features = ["client-sync"], optional = true 
}
 proxmox-http-error.workspace = true
@@ -32,3 +31,5 @@ default = ["sendmail", "gotify"]
 mail-forwarder = ["dep:mail-parser"]
 sendmail = ["dep:proxmox-sys"]
 gotify = ["dep:proxmox-http"]
+pve-context = ["dep:proxmox-sys"]
+pbs-context = ["dep:proxmox-sys"]
diff --git a/proxmox-notify/src/context.rs b/proxmox-notify/src/context.rs
deleted file mode 100644
index 370c7ee..000
--- a/proxmox-notify/src/context.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use std::fmt::Debug;
-
-use once_cell::sync::OnceCell;
-
-pub trait Context: Send + Sync + Debug {
-fn lookup_email_for_user(, user: ) -> Option;
-fn default_sendmail_author() -> String;
-fn default_sendmail_from() -> String;
-fn http_proxy_config() -> Option;
-}
-
-static CONTEXT: OnceCell<&'static dyn Context> = OnceCell::new();
-
-pub fn set_context(context: &'static dyn Context) {
-CONTEXT.set(context).expect("context has already been set");
-}
-
-#[allow(unused)] // context is not used if all endpoint features are disabled
-pub(crate) fn context() -> &'static dyn Context {
-*CONTEXT.get().expect("context has not been yet")
-}
diff --git a/proxmox-notify/src/context/common.rs 
b/proxmox-notify/src/context/common.rs
new file mode 100644
index 000..7580bd1
--- /dev/null
+++ b/proxmox-notify/src/context/common.rs
@@ -0,0 +1,27 @@
+use std::path::Path;
+
+pub(crate) fn attempt_file_read>(path: P) -> Option {
+match proxmox_sys::fs::file_read_optional_string(path) {
+Ok(contents) => contents,
+Err(err) => {
+log::error!("{err}");
+None
+}
+}
+}
+
+pub(crate) fn lookup_datacenter_config_key(content: , key: ) -> 
Option {
+let key_prefix = format!("{key}:");
+normalize_for_return(
+content
+.lines()
+.find_map(|line| line.strip_prefix(_prefix)),
+)
+}
+
+pub(crate) fn normalize_for_return(s: Option<>) -> Option {
+match s?.trim() {
+"" => None,
+s => Some(s.to_string()),
+}
+}
diff --git a/proxmox-notify/src/context/mod.rs 
b/proxmox-notify/src/context/mod.rs
new file mode 100644
index 000..99d86de
--- /dev/null
+++ b/proxmox-notify/src/context/mod.rs
@@ -0,0 +1,36 @@
+use std::fmt::Debug;
+use std::sync::Mutex;
+
+#[cfg(any(feature = "pve-context", feature = "pbs-context"))]
+pub mod common;
+#[cfg(feature = "pbs-context")]
+pub mod pbs;
+#[cfg(feature = "pve-context")]
+pub mod pve;
+
+/// Product-specific context
+pub trait Context: Send + Sync + Debug {
+/// Look up a user's email address from users.cfg
+fn lookup_email_for_user(, user: ) -> Option;
+/// Default mail author for mail-based targets
+fn default_sendmail_author() -> String;
+/// Default from address for sendmail-based targets
+fn default_sendmail_from() -> String;
+/// Proxy configuration for the current node
+fn http_proxy_config() -> Option;
+}
+
+static CONTEXT: Mutex> = Mutex::new(None);
+
+/// Set the product-specific context
+pub fn set_context(context: &'static dyn Context) {
+*CONTEXT.lock().unwrap() = Some(context);
+}
+
+/// Get product-specific context.
+///
+/// Panics if the context has not been set yet.
+#[allow(unused)] // context is not used if all 

[pve-devel] [PATCH v2 proxmox-widget-toolkit 45/52] notification ui: add column for 'origin'

2023-11-14 Thread Lukas Wagner
This column shows whether a matcher/target was provided as a built-in
default config or if it was created by the user. For built-ins, it
also shows whether the built-in settings have been changed.

To reset a built-in entry to its defaults, one can simply delete it.
For best UX, the 'delete' button should change its text to 'reset
defaults' when a built-in target/matcher is selected. This will be
added in another patch.

Signed-off-by: Lukas Wagner 
---
 src/data/model/NotificationConfig.js |  4 ++--
 src/panel/NotificationConfigView.js  | 32 ++--
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/data/model/NotificationConfig.js 
b/src/data/model/NotificationConfig.js
index 9171515..e8ebf28 100644
--- a/src/data/model/NotificationConfig.js
+++ b/src/data/model/NotificationConfig.js
@@ -1,6 +1,6 @@
 Ext.define('proxmox-notification-endpoints', {
 extend: 'Ext.data.Model',
-fields: ['name', 'type', 'comment', 'disable'],
+fields: ['name', 'type', 'comment', 'disable', 'origin'],
 proxy: {
 type: 'proxmox',
 },
@@ -9,7 +9,7 @@ Ext.define('proxmox-notification-endpoints', {
 
 Ext.define('proxmox-notification-matchers', {
 extend: 'Ext.data.Model',
-fields: ['name', 'comment', 'disable'],
+fields: ['name', 'comment', 'disable', 'origin'],
 proxy: {
 type: 'proxmox',
 },
diff --git a/src/panel/NotificationConfigView.js 
b/src/panel/NotificationConfigView.js
index ba69298..4695da5 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -129,7 +129,7 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
dataIndex: 'name',
text: gettext('Target Name'),
renderer: Ext.String.htmlEncode,
-   flex: 1,
+   flex: 2,
},
{
dataIndex: 'type',
@@ -141,7 +141,21 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
dataIndex: 'comment',
text: gettext('Comment'),
renderer: Ext.String.htmlEncode,
-   flex: 1,
+   flex: 3,
+   },
+   {
+   dataIndex: 'origin',
+   text: gettext('Origin'),
+   renderer: (origin) => {
+   switch (origin) {
+   case 'user-created': return gettext('Custom');
+   case 'modified-builtin': return gettext('Built-In 
(modified)');
+   case 'builtin': return gettext('Built-In');
+   }
+
+   // Should not happen...
+   return 'unknown';
+   },
},
 ],
 
@@ -274,6 +288,20 @@ Ext.define('Proxmox.panel.NotificationMatcherView', {
renderer: Ext.String.htmlEncode,
flex: 2,
},
+   {
+   dataIndex: 'origin',
+   text: gettext('Origin'),
+   renderer: (origin) => {
+   switch (origin) {
+   case 'user-created': return gettext('Custom');
+   case 'modified-builtin': return gettext('Built-In 
(modified)');
+   case 'builtin': return gettext('Built-In');
+   }
+
+   // Should not happen...
+   return 'unknown';
+   },
+   },
 ],
 
 store: {
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-mail-forward 52/52] update d/control

2023-11-14 Thread Lukas Wagner
proxmox-schema and proxmox-section config is not required anymore.
add new dependency to proxmox-notify.

Signed-off-by: Lukas Wagner 
---

Notes:
Changes v2 -> v3:
  - new in v3

 debian/control | 6 --
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/debian/control b/debian/control
index 43af70e..eb83a32 100644
--- a/debian/control
+++ b/debian/control
@@ -6,8 +6,10 @@ Build-Depends: cargo:native,
librust-anyhow-1+default-dev,
librust-log-0.4+default-dev (>= 0.4.17~~),
librust-nix-0.26+default-dev,
-   librust-proxmox-schema-1+default-dev (>= 1.3~~),
-   librust-proxmox-section-config-1+default-dev (>= 1.0.2-~~),
+   librust-proxmox-notify-0.2+default-dev,
+   librust-proxmox-notify-0.2+mail-forwarder-dev,
+   librust-proxmox-notify-0.2+pbs-context-dev,
+   librust-proxmox-notify-0.2+pve-context-dev,
librust-proxmox-sys-0.5+default-dev,
librust-serde-1+default-dev,
librust-serde-1+derive-dev,
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-widget-toolkit 42/52] noficiation: matcher edit: make 'field' an editable combobox

2023-11-14 Thread Lukas Wagner
For now with fixed options that are shared between most notification
events - later, once we have a notification registry, this should be
filled dynamically.

Signed-off-by: Lukas Wagner 
---
 src/window/NotificationMatcherEdit.js | 11 ++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/window/NotificationMatcherEdit.js 
b/src/window/NotificationMatcherEdit.js
index c6f0726..fb55e17 100644
--- a/src/window/NotificationMatcherEdit.js
+++ b/src/window/NotificationMatcherEdit.js
@@ -963,14 +963,23 @@ Ext.define('Proxmox.panel.NotificationMatchRuleSettings', 
{
},
{
fieldLabel: gettext('Field'),
-   xtype: 'textfield',
+   xtype: 'proxmoxKVComboBox',
isFormField: false,
submitValue: false,
+   allowBlank: false,
+   editable: true,
+   displayField: 'key',
bind: {
hidden: '{!typeIsMatchField}',
disabled: '{!typeIsMatchField}',
value: '{matchFieldField}',
},
+   // TODO: Once we have a 'notification registry', we should
+   // retrive those via an API call.
+   comboItems: [
+   ['type', ''],
+   ['hostname', ''],
+   ],
},
{
fieldLabel: gettext('Value'),
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-widget-toolkit 41/52] notification ui: unprotected mailto-root target

2023-11-14 Thread Lukas Wagner
A default notification config will now be created in pve-manager's
postinst hook - which is not magic in any way and can be modified
and deleted as desired.

Signed-off-by: Lukas Wagner 
---
 src/panel/NotificationConfigView.js | 6 --
 1 file changed, 6 deletions(-)

diff --git a/src/panel/NotificationConfigView.js 
b/src/panel/NotificationConfigView.js
index ecf764d..6a9bc20 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -41,10 +41,6 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
openEditWindow: function(endpointType, endpoint) {
let me = this;
 
-   if (endpoint === 'mail-to-root') {
-   return;
-   }
-
Ext.create('Proxmox.window.EndpointEditBase', {
baseUrl: me.getView().baseUrl,
type: endpointType,
@@ -183,13 +179,11 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
xtype: 'proxmoxButton',
text: gettext('Modify'),
handler: 'openEditForSelectedItem',
-   enableFn: rec => rec.data.name !== 'mail-to-root',
disabled: true,
},
{
xtype: 'proxmoxStdRemoveButton',
callback: 'reload',
-   enableFn: rec => rec.data.name !== 'mail-to-root',
getUrl: function(rec) {
return 
`${me.baseUrl}/endpoints/${rec.data.type}/${rec.getId()}`;
},
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-perl-rs 16/52] notify: adapt to new matcher-based notification routing

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 common/src/notify.rs | 167 +--
 1 file changed, 50 insertions(+), 117 deletions(-)

diff --git a/common/src/notify.rs b/common/src/notify.rs
index 9f44225..4fbd705 100644
--- a/common/src/notify.rs
+++ b/common/src/notify.rs
@@ -1,10 +1,12 @@
 #[perlmod::package(name = "Proxmox::RS::Notify")]
 mod export {
+use std::collections::HashMap;
+use std::sync::Mutex;
+
 use anyhow::{bail, Error};
-use perlmod::Value;
 use serde_json::Value as JSONValue;
-use std::sync::Mutex;
 
+use perlmod::Value;
 use proxmox_http_error::HttpError;
 use proxmox_notify::endpoints::gotify::{
 DeleteableGotifyProperty, GotifyConfig, GotifyConfigUpdater, 
GotifyPrivateConfig,
@@ -13,10 +15,10 @@ mod export {
 use proxmox_notify::endpoints::sendmail::{
 DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater,
 };
-use proxmox_notify::filter::{
-DeleteableFilterProperty, FilterConfig, FilterConfigUpdater, 
FilterModeOperator,
+use proxmox_notify::matcher::{
+CalendarMatcher, DeleteableMatcherProperty, FieldMatcher, 
MatchModeOperator, MatcherConfig,
+MatcherConfigUpdater, SeverityMatcher,
 };
-use proxmox_notify::group::{DeleteableGroupProperty, GroupConfig, 
GroupConfigUpdater};
 use proxmox_notify::{api, Config, Notification, Severity};
 
 pub struct NotificationConfig {
@@ -87,22 +89,22 @@ mod export {
 #[export(serialize_error)]
 fn send(
 #[try_from_ref] this: ,
-channel: ,
 severity: Severity,
 title: String,
 body: String,
-properties: Option,
+template_data: Option,
+fields: Option>,
 ) -> Result<(), HttpError> {
 let config = this.config.lock().unwrap();
-
-let notification = Notification {
+let notification = Notification::new_templated(
 severity,
 title,
 body,
-properties,
-};
+template_data.unwrap_or_default(),
+fields.unwrap_or_default(),
+);
 
-api::common::send(, channel, )
+api::common::send(, )
 }
 
 #[export(serialize_error)]
@@ -114,78 +116,6 @@ mod export {
 api::common::test_target(, target)
 }
 
-#[export(serialize_error)]
-fn get_groups(
-#[try_from_ref] this: ,
-) -> Result, HttpError> {
-let config = this.config.lock().unwrap();
-api::group::get_groups()
-}
-
-#[export(serialize_error)]
-fn get_group(
-#[try_from_ref] this: ,
-id: ,
-) -> Result {
-let config = this.config.lock().unwrap();
-api::group::get_group(, id)
-}
-
-#[export(serialize_error)]
-fn add_group(
-#[try_from_ref] this: ,
-name: String,
-endpoints: Vec,
-comment: Option,
-filter: Option,
-) -> Result<(), HttpError> {
-let mut config = this.config.lock().unwrap();
-api::group::add_group(
- config,
- {
-name,
-endpoint: endpoints,
-comment,
-filter,
-},
-)
-}
-
-#[export(serialize_error)]
-fn update_group(
-#[try_from_ref] this: ,
-name: ,
-endpoints: Option>,
-comment: Option,
-filter: Option,
-delete: Option>,
-digest: Option<>,
-) -> Result<(), HttpError> {
-let mut config = this.config.lock().unwrap();
-let digest = decode_digest(digest)?;
-
-api::group::update_group(
- config,
-name,
- {
-endpoint: endpoints,
-comment,
-filter,
-},
-delete.as_deref(),
-digest.as_deref(),
-)
-}
-
-#[export(serialize_error)]
-fn delete_group(
-#[try_from_ref] this: ,
-name: ,
-) -> Result<(), HttpError> {
-let mut config = this.config.lock().unwrap();
-api::group::delete_group( config, name)
-}
-
 #[export(serialize_error)]
 fn get_sendmail_endpoints(
 #[try_from_ref] this: ,
@@ -213,7 +143,6 @@ mod export {
 from_address: Option,
 author: Option,
 comment: Option,
-filter: Option,
 ) -> Result<(), HttpError> {
 let mut config = this.config.lock().unwrap();
 
@@ -226,7 +155,7 @@ mod export {
 from_address,
 author,
 comment,
-filter,
+filter: None,
 },
 )
 }
@@ -241,7 +170,6 @@ mod export {
 from_address: Option,
 author: Option,
 comment: Option,
-filter: Option,
 delete: Option>,
 digest: Option<>,
 ) -> Result<(), HttpError> {
@@ -257,7 +185,6 @@ mod export {
 from_address,
 

[pve-devel] [PATCH v2 pve-manager 27/52] vzdump: adapt to new matcher based notification system

2023-11-14 Thread Lukas Wagner
To ease the migration from old-style mailto/mailnotification paramters
for backup jobs, the code will add a ephemeral sendmail endpoint and
a matcher.

Signed-off-by: Lukas Wagner 
---
 PVE/API2/VZDump.pm |  8 +---
 PVE/VZDump.pm  | 40 +++-
 2 files changed, 20 insertions(+), 28 deletions(-)

diff --git a/PVE/API2/VZDump.pm b/PVE/API2/VZDump.pm
index 3886772e..f66fc740 100644
--- a/PVE/API2/VZDump.pm
+++ b/PVE/API2/VZDump.pm
@@ -44,9 +44,7 @@ __PACKAGE__->register_method ({
."'Datastore.AllocateSpace' on the backup storage. The 'tmpdir', 
'dumpdir' and "
."'script' parameters are restricted to the 'root\@pam' user. The 
'maxfiles' and "
."'prune-backups' settings require 'Datastore.Allocate' on the 
backup storage. The "
-   ."'bwlimit', 'performance' and 'ionice' parameters require 
'Sys.Modify' on '/'. "
-   ."If 'notification-target' is set, then the 'Mapping.Use' 
permission is needed on "
-   ."'/mapping/notification/'.",
+   ."'bwlimit', 'performance' and 'ionice' parameters require 
'Sys.Modify' on '/'. ",
user => 'all',
 },
 protected => 1,
@@ -115,10 +113,6 @@ __PACKAGE__->register_method ({
$rpcenv->check($user, "/storage/$storeid", [ 
'Datastore.AllocateSpace' ]);
}
 
-   if (my $target = $param->{'notification-target'}) {
-   PVE::Notify::check_may_use_target($target, $rpcenv);
-   }
-
my $worker = sub {
my $upid = shift;
 
diff --git a/PVE/VZDump.pm b/PVE/VZDump.pm
index 454ab494..b0574d41 100644
--- a/PVE/VZDump.pm
+++ b/PVE/VZDump.pm
@@ -452,20 +452,18 @@ sub send_notification {
 my $opts = $self->{opts};
 my $mailto = $opts->{mailto};
 my $cmdline = $self->{cmdline};
-my $target = $opts->{"notification-target"};
-# Fall back to 'mailnotification' if 'notification-policy' is not set.
-# If both are set, 'notification-policy' takes precedence
-my $policy = $opts->{"notification-policy"} // $opts->{mailnotification} 
// 'always';
+# Old-style notification policy. This parameter will influce
+# if an ad-hoc notification target/matcher will be created.
+my $policy = $opts->{"notification-policy"} //
+   $opts->{mailnotification} //
+   'always';
 
-return if ($policy eq 'never');
 
 sanitize_task_list($tasklist);
 my $error_count = count_failed_tasks($tasklist);
 
 my $failed = ($error_count || $err);
 
-return if (!$failed && ($policy eq 'failure'));
-
 my $status_text = $failed ? 'backup failed' : 'backup successful';
 
 if ($err) {
@@ -489,8 +487,10 @@ sub send_notification {
"See Task History for details!\n";
 };
 
+my $hostname = get_hostname();
+
 my $notification_props = {
-   "hostname"  => get_hostname(),
+   "hostname"  => $hostname,
"error-message" => $err,
"guest-table"   => build_guest_table($tasklist),
"logs"  => $text_log_part,
@@ -498,9 +498,16 @@ sub send_notification {
"total-time"=> $total_time,
 };
 
+my $fields = {
+   type => "vzdump",
+   hostname => $hostname,
+};
+
 my $notification_config = PVE::Notify::read_config();
 
-if ($mailto && scalar(@$mailto)) {
+my $legacy_sendmail = $policy eq "always" || ($policy eq "failure" && 
$failed);
+
+if ($mailto && scalar(@$mailto) && $legacy_sendmail) {
# <, >, @ are not allowed in endpoint names, but that is only
# verified once the config is serialized. That means that
# we can rely on that fact that no other endpoint with this name exists.
@@ -514,29 +521,20 @@ sub send_notification {
 
my $endpoints = [$endpoint_name];
 
-   # Create an anonymous group containing the sendmail endpoint and the
-   # $target endpoint, if specified
-   if ($target) {
-   push @$endpoints, $target;
-   }
-
-   $target = "";
-   $notification_config->add_group(
-   $target,
+   $notification_config->add_matcher(
+   "",
$endpoints,
);
 }
 
-return if (!$target);
-
 my $severity = $failed ? "error" : "info";
 
 PVE::Notify::notify(
-   $target,
$severity,
$subject_template,
$body_template,
$notification_props,
+   $fields,
$notification_config
 );
 };
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 pve-manager 32/52] ui: dc: config: show notification panel again

2023-11-14 Thread Lukas Wagner
Rework should be done now.

Signed-off-by: Lukas Wagner 
---
 www/manager6/dc/Config.js | 11 +++
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 0dea1c67..74a84e91 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -317,14 +317,9 @@ Ext.define('PVE.dc.Config', {
);
}
 
-   // this is being reworked, but we need to release newer manager 
versions already..
-   let notification_enabled = false;
-   if (notification_enabled && (
-   caps.mapping['Mapping.Audit'] ||
-   caps.mapping['Mapping.Use'] ||
-   caps.mapping['Mapping.Modify']
-   )
-   ) {
+   if (caps.mapping['Mapping.Audit'] ||
+   caps.mapping['Mapping.Use'] ||
+   caps.mapping['Mapping.Modify']) {
me.items.push(
{
xtype: 'pmxNotificationConfigView',
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-perl-rs 18/52] pve-rs: notify: remove notify_context for PVE

2023-11-14 Thread Lukas Wagner
The context has now been moved to `proxmox-notify` due to the fact
that we also need it in `proxmox-mail-forward` now.

Signed-off-by: Lukas Wagner 
---

Notes:
Changes v2 -> v3:
  - No changes

 pve-rs/Cargo.toml|   2 +-
 pve-rs/src/lib.rs|   7 ++-
 pve-rs/src/notify_context.rs | 117 ---
 3 files changed, 5 insertions(+), 121 deletions(-)
 delete mode 100644 pve-rs/src/notify_context.rs

diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml
index e222d9d..2300c8d 100644
--- a/pve-rs/Cargo.toml
+++ b/pve-rs/Cargo.toml
@@ -36,7 +36,7 @@ perlmod = { version = "0.13", features = [ "exporter" ] }
 proxmox-apt = "0.10.6"
 proxmox-http = { version = "0.9", features = ["client-sync", "client-trait"] }
 proxmox-http-error = "0.1.0"
-proxmox-notify = "0.2"
+proxmox-notify = { version = "0.2", features = ["pve-context"] }
 proxmox-openid = "0.10"
 proxmox-resource-scheduling = "0.3.0"
 proxmox-subscription = "0.4"
diff --git a/pve-rs/src/lib.rs b/pve-rs/src/lib.rs
index d1915c9..42be39e 100644
--- a/pve-rs/src/lib.rs
+++ b/pve-rs/src/lib.rs
@@ -4,18 +4,19 @@
 pub mod common;
 
 pub mod apt;
-pub mod notify_context;
 pub mod openid;
 pub mod resource_scheduling;
 pub mod tfa;
 
 #[perlmod::package(name = "Proxmox::Lib::PVE", lib = "pve_rs")]
 mod export {
-use crate::{common, notify_context};
+use proxmox_notify::context::pve::PVE_CONTEXT;
+
+use crate::common;
 
 #[export]
 pub fn init() {
 common::logger::init("PVE_LOG", "info");
-notify_context::init();
+proxmox_notify::context::set_context(_CONTEXT)
 }
 }
diff --git a/pve-rs/src/notify_context.rs b/pve-rs/src/notify_context.rs
deleted file mode 100644
index 3cf3e18..000
--- a/pve-rs/src/notify_context.rs
+++ /dev/null
@@ -1,117 +0,0 @@
-use log;
-use std::path::Path;
-
-use proxmox_notify::context::Context;
-
-// Some helpers borrowed and slightly adapted from `proxmox-mail-forward`
-
-fn normalize_for_return(s: Option<>) -> Option {
-match s?.trim() {
-"" => None,
-s => Some(s.to_string()),
-}
-}
-
-fn attempt_file_read>(path: P) -> Option {
-match proxmox_sys::fs::file_read_optional_string(path) {
-Ok(contents) => contents,
-Err(err) => {
-log::error!("{err}");
-None
-}
-}
-}
-
-fn lookup_mail_address(content: , user: ) -> Option {
-normalize_for_return(content.lines().find_map(|line| {
-let fields: Vec<> = line.split(':').collect();
-#[allow(clippy::get_first)] // to keep expression style consistent
-match fields.get(0)?.trim() == "user" && fields.get(1)?.trim() == user 
{
-true => fields.get(6).copied(),
-false => None,
-}
-}))
-}
-
-fn lookup_datacenter_config_key(content: , key: ) -> Option {
-let key_prefix = format!("{key}:");
-normalize_for_return(
-content
-.lines()
-.find_map(|line| line.strip_prefix(_prefix)),
-)
-}
-
-#[derive(Debug)]
-struct PVEContext;
-
-impl Context for PVEContext {
-fn lookup_email_for_user(, user: ) -> Option {
-let content = attempt_file_read("/etc/pve/user.cfg");
-content.and_then(|content| lookup_mail_address(, user))
-}
-
-fn default_sendmail_author() -> String {
-"Proxmox VE".into()
-}
-
-fn default_sendmail_from() -> String {
-let content = attempt_file_read("/etc/pve/datacenter.cfg");
-content
-.and_then(|content| lookup_datacenter_config_key(, 
"email_from"))
-.unwrap_or_else(|| String::from("root"))
-}
-
-fn http_proxy_config() -> Option {
-let content = attempt_file_read("/etc/pve/datacenter.cfg");
-content.and_then(|content| lookup_datacenter_config_key(, 
"http_proxy"))
-}
-}
-
-#[cfg(test)]
-mod tests {
-use super::*;
-
-const USER_CONFIG:  = "
-user:root@pam:1:0:::r...@example.com:::
-user:test@pve:1:0:::t...@example.com:::
-user:no-mail@pve:1:0::
-";
-
-#[test]
-fn test_parse_mail() {
-assert_eq!(
-lookup_mail_address(USER_CONFIG, "root@pam"),
-Some("r...@example.com".to_string())
-);
-assert_eq!(
-lookup_mail_address(USER_CONFIG, "test@pve"),
-Some("t...@example.com".to_string())
-);
-assert_eq!(lookup_mail_address(USER_CONFIG, "no-mail@pve"), None);
-}
-
-const DC_CONFIG:  = "
-email_from: u...@example.com
-http_proxy: http://localhost:1234
-keyboard: en-us
-";
-#[test]
-fn test_parse_dc_config() {
-assert_eq!(
-lookup_datacenter_config_key(DC_CONFIG, "email_from"),
-Some("u...@example.com".to_string())
-);
-assert_eq!(
-lookup_datacenter_config_key(DC_CONFIG, "http_proxy"),
-Some("http://localhost:1234".to_string())
-);
-assert_eq!(lookup_datacenter_config_key(DC_CONFIG, "foo"), None);
-}

[pve-devel] [PATCH v2 pve-cluster 21/52] notify: adapt to matcher based notification system

2023-11-14 Thread Lukas Wagner
This commit removes the target paramters from all notify calls. Also,
the default 'mail-to-root' target is not added automatically any more
- this target will be added by an dpkg hook in the future.

Signed-off-by: Lukas Wagner 
---
 src/PVE/Notify.pm | 101 +-
 1 file changed, 47 insertions(+), 54 deletions(-)

diff --git a/src/PVE/Notify.pm b/src/PVE/Notify.pm
index 419bf6d..872eb25 100644
--- a/src/PVE/Notify.pm
+++ b/src/PVE/Notify.pm
@@ -18,8 +18,6 @@ cfs_register_file(
 \_notification_config,
 );
 
-my $mail_to_root_target = 'mail-to-root';
-
 sub parse_notification_config {
 my ($filename, $raw) = @_;
 
@@ -48,86 +46,81 @@ sub read_config {
 
 my $notification_config = Proxmox::RS::Notify->parse_config($config, 
$priv_config);
 
-eval {
-   # This target should always be available...
-   $notification_config->add_sendmail_endpoint(
-   $mail_to_root_target,
-   undef,
-   ['root@pam'],
-   undef,
-   undef,
-   'Send mail to root@pam\'s email address'
-   );
-};
-
 return $notification_config;
 }
 
 sub write_config {
 my ($notification_config) = @_;
 
-eval {
-   # ... but don't persist it to the config.
-   # Rationale: If it is in the config, the user might think
-   # that it can be changed by editing the configuration there.
-   # However, since we always add it in `read_config`, any changes
-   # will be implicitly overridden by the default.
-
-   # If users want's to change the configuration, they are supposed to
-   # create a new sendmail endpoint.
-   $notification_config->delete_sendmail_endpoint($mail_to_root_target);
-};
-
 my ($config, $priv_config) = $notification_config->write_config();
 cfs_write_file('notifications.cfg', $config, 1);
 cfs_write_file('priv/notifications.cfg', $priv_config, 1);
 }
 
-sub default_target {
-return $mail_to_root_target;
-}
-
 my $send_notification = sub {
-my ($target, $severity, $title, $message, $properties, $config) = @_;
+my ($severity, $title, $message, $template_data, $fields, $config) = @_;
 $config = read_config() if !defined($config);
-my ($module, $file, $line) = caller(1);
-
-# Augment properties with the source code location of the notify call
-my $props_with_source = {
-   %$properties,
-   source => {
-   module => $module,
-   file => $file,
-   line => $line,
-   }
-};
-
-$config->send($target, $severity, $title, $message, $props_with_source);
+$config->send($severity, $title, $message, $template_data, $fields);
 };
 
 sub notify {
-my ($target, $severity, $title, $message, $properties, $config) = @_;
-$send_notification->($target, $severity, $title, $message, $properties, 
$config);
+my ($severity, $title, $message, $template_data, $fields, $config) = @_;
+$send_notification->(
+$severity,
+$title,
+$message,
+$template_data,
+$fields,
+$config
+);
 }
 
 sub info {
-my ($target, $title, $message, $properties, $config) = @_;
-$send_notification->($target, 'info', $title, $message, $properties, 
$config);
+my ($title, $message, $template_data, $fields, $config) = @_;
+$send_notification->(
+'info',
+$title,
+$message,
+$template_data,
+$fields,
+$config
+);
 }
 
 sub notice {
-my ($target, $title, $message, $properties, $config) = @_;
-$send_notification->($target, 'notice', $title, $message, $properties, 
$config);
+my ($title, $message, $template_data, $fields, $config) = @_;
+$send_notification->(
+'notice',
+$title,
+$message,
+$template_data,
+$fields,
+$config
+);
 }
 
 sub warning {
-my ($target, $title, $message, $properties, $config) = @_;
-$send_notification->($target, 'warning', $title, $message, $properties, 
$config);
+my ($title, $message, $template_data, $fields, $config) = @_;
+$send_notification->(
+'warning',
+$title,
+$message,
+$template_data,
+$fields,
+$config
+);
 }
 
 sub error {
-my ($target, $title, $message, $properties, $config) = @_;
-$send_notification->($target, 'error', $title, $message, $properties, 
$config);
+my ($title, $message, $template_data, $fields, $config) = @_;
+$send_notification->(
+'error',
+$title,
+$message,
+$template_data,
+$fields,
+$config
+);
 }
 
 sub check_may_use_target {
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-perl-rs 17/52] notify: add bindings for smtp API calls

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 common/src/notify.rs | 106 +++
 1 file changed, 106 insertions(+)

diff --git a/common/src/notify.rs b/common/src/notify.rs
index 4fbd705..8a6d76e 100644
--- a/common/src/notify.rs
+++ b/common/src/notify.rs
@@ -15,6 +15,10 @@ mod export {
 use proxmox_notify::endpoints::sendmail::{
 DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater,
 };
+use proxmox_notify::endpoints::smtp::{
+DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpMode, 
SmtpPrivateConfig,
+SmtpPrivateConfigUpdater,
+};
 use proxmox_notify::matcher::{
 CalendarMatcher, DeleteableMatcherProperty, FieldMatcher, 
MatchModeOperator, MatcherConfig,
 MatcherConfigUpdater, SeverityMatcher,
@@ -271,6 +275,108 @@ mod export {
 api::gotify::delete_gotify_endpoint( config, name)
 }
 
+#[export(serialize_error)]
+fn get_smtp_endpoints(
+#[try_from_ref] this: ,
+) -> Result, HttpError> {
+let config = this.config.lock().unwrap();
+api::smtp::get_endpoints()
+}
+
+#[export(serialize_error)]
+fn get_smtp_endpoint(
+#[try_from_ref] this: ,
+id: ,
+) -> Result {
+let config = this.config.lock().unwrap();
+api::smtp::get_endpoint(, id)
+}
+
+#[export(serialize_error)]
+#[allow(clippy::too_many_arguments)]
+fn add_smtp_endpoint(
+#[try_from_ref] this: ,
+name: String,
+server: String,
+port: Option,
+mode: Option,
+username: Option,
+password: Option,
+mailto: Option>,
+mailto_user: Option>,
+from_address: String,
+author: Option,
+comment: Option,
+) -> Result<(), HttpError> {
+let mut config = this.config.lock().unwrap();
+api::smtp::add_endpoint(
+ config,
+ {
+name: name.clone(),
+server,
+port,
+mode,
+username,
+mailto,
+mailto_user,
+from_address,
+author,
+comment,
+},
+ { name, password },
+)
+}
+
+#[export(serialize_error)]
+#[allow(clippy::too_many_arguments)]
+fn update_smtp_endpoint(
+#[try_from_ref] this: ,
+name: ,
+server: Option,
+port: Option,
+mode: Option,
+username: Option,
+password: Option,
+mailto: Option>,
+mailto_user: Option>,
+from_address: Option,
+author: Option,
+comment: Option,
+delete: Option>,
+digest: Option<>,
+) -> Result<(), HttpError> {
+let mut config = this.config.lock().unwrap();
+let digest = decode_digest(digest)?;
+
+api::smtp::update_endpoint(
+ config,
+name,
+ {
+server,
+port,
+mode,
+username,
+mailto,
+mailto_user,
+from_address,
+author,
+comment,
+},
+ { password },
+delete.as_deref(),
+digest.as_deref(),
+)
+}
+
+#[export(serialize_error)]
+fn delete_smtp_endpoint(
+#[try_from_ref] this: ,
+name: ,
+) -> Result<(), HttpError> {
+let mut config = this.config.lock().unwrap();
+api::smtp::delete_endpoint( config, name)
+}
+
 #[export(serialize_error)]
 fn get_matchers(
 #[try_from_ref] this: ,
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-perl-rs 19/52] notify: add 'disable' parameter

2023-11-14 Thread Lukas Wagner
This parameter disables a matcher/a target.

Signed-off-by: Lukas Wagner 
---
 common/src/notify.rs | 22 --
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/common/src/notify.rs b/common/src/notify.rs
index 8a6d76e..a5ab754 100644
--- a/common/src/notify.rs
+++ b/common/src/notify.rs
@@ -147,6 +147,7 @@ mod export {
 from_address: Option,
 author: Option,
 comment: Option,
+disable: Option,
 ) -> Result<(), HttpError> {
 let mut config = this.config.lock().unwrap();
 
@@ -159,7 +160,7 @@ mod export {
 from_address,
 author,
 comment,
-filter: None,
+disable,
 },
 )
 }
@@ -174,6 +175,7 @@ mod export {
 from_address: Option,
 author: Option,
 comment: Option,
+disable: Option,
 delete: Option>,
 digest: Option<>,
 ) -> Result<(), HttpError> {
@@ -189,6 +191,7 @@ mod export {
 from_address,
 author,
 comment,
+disable,
 },
 delete.as_deref(),
 digest.as_deref(),
@@ -228,6 +231,7 @@ mod export {
 server: String,
 token: String,
 comment: Option,
+disable: Option,
 ) -> Result<(), HttpError> {
 let mut config = this.config.lock().unwrap();
 api::gotify::add_endpoint(
@@ -236,6 +240,7 @@ mod export {
 name: name.clone(),
 server,
 comment,
+disable,
 filter: None,
 },
  { name, token },
@@ -250,6 +255,7 @@ mod export {
 server: Option,
 token: Option,
 comment: Option,
+disable: Option,
 delete: Option>,
 digest: Option<>,
 ) -> Result<(), HttpError> {
@@ -259,7 +265,11 @@ mod export {
 api::gotify::update_endpoint(
  config,
 name,
- { server, comment },
+ {
+server,
+comment,
+disable,
+},
  { token },
 delete.as_deref(),
 digest.as_deref(),
@@ -307,6 +317,7 @@ mod export {
 from_address: String,
 author: Option,
 comment: Option,
+disable: Option,
 ) -> Result<(), HttpError> {
 let mut config = this.config.lock().unwrap();
 api::smtp::add_endpoint(
@@ -322,6 +333,7 @@ mod export {
 from_address,
 author,
 comment,
+disable,
 },
  { name, password },
 )
@@ -342,6 +354,7 @@ mod export {
 from_address: Option,
 author: Option,
 comment: Option,
+disable: Option,
 delete: Option>,
 digest: Option<>,
 ) -> Result<(), HttpError> {
@@ -361,6 +374,7 @@ mod export {
 from_address,
 author,
 comment,
+disable,
 },
  { password },
 delete.as_deref(),
@@ -406,6 +420,7 @@ mod export {
 mode: Option,
 invert_match: Option,
 comment: Option,
+disable: Option,
 ) -> Result<(), HttpError> {
 let mut config = this.config.lock().unwrap();
 api::matcher::add_matcher(
@@ -419,6 +434,7 @@ mod export {
 mode,
 invert_match,
 comment,
+disable,
 },
 )
 }
@@ -435,6 +451,7 @@ mod export {
 mode: Option,
 invert_match: Option,
 comment: Option,
+disable: Option,
 delete: Option>,
 digest: Option<>,
 ) -> Result<(), HttpError> {
@@ -452,6 +469,7 @@ mod export {
 mode,
 invert_match,
 comment,
+disable,
 },
 delete.as_deref(),
 digest.as_deref(),
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 pve-guest-common 22/52] vzdump: deprecate mailto/mailnotification/notification-{target, policy}

2023-11-14 Thread Lukas Wagner
The first two will be migrated to the notification system, the second
were part for the first attempt for the new notification system.
The first attempt only ever hit pvetest, so we simply tell the user
to not use the two params.

Signed-off-by: Lukas Wagner 
---
 src/PVE/VZDump/Common.pm | 16 +++-
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/src/PVE/VZDump/Common.pm b/src/PVE/VZDump/Common.pm
index be605af..b93ad86 100644
--- a/src/PVE/VZDump/Common.pm
+++ b/src/PVE/VZDump/Common.pm
@@ -175,21 +175,22 @@ my $confdesc = {
 mailto => {
type => 'string',
format => 'email-or-username-list',
-   description => "Comma-separated list of email addresses or users that 
should" .
-   " receive email notifications. Has no effect if the 
'notification-target' option " .
-   " is set at the same time.",
+   description => "Deprecated: Use notification targets/matchers instead." 
.
+   " Comma-separated list of email addresses or users that should" .
+   " receive email notifications.",
optional => 1,
 },
 mailnotification => {
type => 'string',
-   description => "Deprecated: use 'notification-policy' instead.",
+   description => "Deprecated: use notification targets/matchers instead." 
.
+   " Specify when to send a notification mail",
optional => 1,
enum => [ 'always', 'failure' ],
default => 'always',
 },
 'notification-policy' => {
type => 'string',
-   description => "Specify when to send a notification",
+   description => "Deprecated: Do not use",
optional => 1,
enum => [ 'always', 'failure', 'never'],
default => 'always',
@@ -197,10 +198,7 @@ my $confdesc = {
 'notification-target' => {
type => 'string',
format => 'pve-configid',
-   description => "Determine the target to which notifications should be 
sent." .
-   " Can either be a notification endpoint or a notification group." .
-   " This option takes precedence over 'mailto', meaning that if both 
are " .
-   " set, the 'mailto' option will be ignored.",
+   description => "Deprecated: Do not use",
optional => 1,
 },
 tmpdir => {
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox-widget-toolkit 43/52] panel: notification: add gui for SMTP endpoints

2023-11-14 Thread Lukas Wagner
This new endpoint configuration panel is embedded in the existing
EndpointEditBase dialog window. This commit also factors out some of
the non-trivial common form elements that are shared between the new
panel and the already existing SendmailEditPanel into a separate panel
EmailRecipientPanel.

Signed-off-by: Lukas Wagner 
---
 src/Makefile |   2 +
 src/Schema.js|   5 +
 src/panel/EmailRecipientPanel.js |  88 +++
 src/panel/SendmailEditPanel.js   |  58 +-
 src/panel/SmtpEditPanel.js   | 183 +++
 5 files changed, 281 insertions(+), 55 deletions(-)
 create mode 100644 src/panel/EmailRecipientPanel.js
 create mode 100644 src/panel/SmtpEditPanel.js

diff --git a/src/Makefile b/src/Makefile
index c6d31c3..01145b1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -71,7 +71,9 @@ JSSRC=\
panel/ACMEAccount.js\
panel/ACMEPlugin.js \
panel/ACMEDomains.js\
+   panel/EmailRecipientPanel.js\
panel/SendmailEditPanel.js  \
+   panel/SmtpEditPanel.js  \
panel/StatusView.js \
panel/TfaView.js\
panel/NotesView.js  \
diff --git a/src/Schema.js b/src/Schema.js
index 37ecd88..28e1037 100644
--- a/src/Schema.js
+++ b/src/Schema.js
@@ -43,6 +43,11 @@ Ext.define('Proxmox.Schema', { // a singleton
ipanel: 'pmxSendmailEditPanel',
iconCls: 'fa-envelope-o',
},
+   smtp: {
+   name: gettext('SMTP'),
+   ipanel: 'pmxSmtpEditPanel',
+   iconCls: 'fa-envelope-o',
+   },
gotify: {
name: gettext('Gotify'),
ipanel: 'pmxGotifyEditPanel',
diff --git a/src/panel/EmailRecipientPanel.js b/src/panel/EmailRecipientPanel.js
new file mode 100644
index 000..b2bc03c
--- /dev/null
+++ b/src/panel/EmailRecipientPanel.js
@@ -0,0 +1,88 @@
+Ext.define('Proxmox.panel.EmailRecipientPanel', {
+extend: 'Ext.panel.Panel',
+xtype: 'pmxEmailRecipientPanel',
+mixins: ['Proxmox.Mixin.CBind'],
+border: false,
+
+mailValidator: function() {
+   let mailto_user = this.down(`[name=mailto-user]`);
+   let mailto = this.down(`[name=mailto]`);
+
+   if (!mailto_user.getValue()?.length && !mailto.getValue()) {
+   return gettext('Either mailto or mailto-user must be set');
+   }
+
+   return true;
+},
+
+items: [
+   {
+   layout: 'anchor',
+   border: false,
+   items: [
+   {
+   xtype: 'pmxUserSelector',
+   name: 'mailto-user',
+   multiSelect: true,
+   allowBlank: true,
+   editable: false,
+   skipEmptyText: true,
+   fieldLabel: gettext('Recipient(s)'),
+   cbind: {
+   deleteEmpty: '{!isCreate}',
+   },
+   validator: function() {
+   return 
this.up('pmxEmailRecipientPanel').mailValidator();
+   },
+   autoEl: {
+   tag: 'div',
+   'data-qtip': gettext('The notification will be sent to 
the user\'s configured mail address'),
+   },
+   listConfig: {
+   width: 600,
+   columns: [
+   {
+   header: gettext('User'),
+   sortable: true,
+   dataIndex: 'userid',
+   renderer: Ext.String.htmlEncode,
+   flex: 1,
+   },
+   {
+   header: gettext('E-Mail'),
+   sortable: true,
+   dataIndex: 'email',
+   renderer: Ext.String.htmlEncode,
+   flex: 1,
+   },
+   {
+   header: gettext('Comment'),
+   sortable: false,
+   dataIndex: 'comment',
+   renderer: Ext.String.htmlEncode,
+   flex: 1,
+   },
+   ],
+   },
+   },
+   {
+   xtype: 'proxmoxtextfield',
+   fieldLabel: gettext('Additional Recipient(s)'),
+   name: 'mailto',
+   allowBlank: true,
+   emptyText: 'u...@example.com, ...',
+   cbind: {
+   deleteEmpty: '{!isCreate}',
+   },
+   autoEl: {
+   tag: 'div',
+   

[pve-devel] [PATCH v2 proxmox 07/52] notify: matcher: introduce common trait for match directives

2023-11-14 Thread Lukas Wagner
This allows us to make the match-checking code a bit shorter.

Signed-off-by: Lukas Wagner 
---
 proxmox-notify/src/matcher.rs | 92 +--
 1 file changed, 45 insertions(+), 47 deletions(-)

diff --git a/proxmox-notify/src/matcher.rs b/proxmox-notify/src/matcher.rs
index b03d11d..e299fd0 100644
--- a/proxmox-notify/src/matcher.rs
+++ b/proxmox-notify/src/matcher.rs
@@ -142,6 +142,11 @@ pub struct MatcherConfig {
 pub comment: Option,
 }
 
+trait MatchDirective {
+fn matches(, notification: ) -> Result;
+}
+
+/// Check if the notification metadata fields match
 #[derive(Clone, Debug)]
 pub enum FieldMatcher {
 Exact {
@@ -157,9 +162,9 @@ pub enum FieldMatcher {
 proxmox_serde::forward_deserialize_to_from_str!(FieldMatcher);
 proxmox_serde::forward_serialize_to_display!(FieldMatcher);
 
-impl FieldMatcher {
-fn matches(, notification: ) -> bool {
-match self {
+impl MatchDirective for FieldMatcher {
+fn matches(, notification: ) -> Result {
+Ok(match self {
 FieldMatcher::Exact {
 field,
 matched_value,
@@ -186,7 +191,7 @@ impl FieldMatcher {
 false
 }
 }
-}
+})
 }
 }
 
@@ -260,9 +265,22 @@ impl MatcherConfig {
 let mode = self.mode.unwrap_or_default();
 
 let mut is_match = mode.neutral_element();
-is_match = mode.apply(is_match, 
self.check_severity_match(notification));
-is_match = mode.apply(is_match, self.check_field_match(notification)?);
-is_match = mode.apply(is_match, 
self.check_calendar_match(notification)?);
+
+if let Some(severity_matchers) = self.match_severity.as_deref() {
+is_match = mode.apply(
+is_match,
+self.check_matches(notification, severity_matchers)?,
+);
+}
+if let Some(field_matchers) = self.match_field.as_deref() {
+is_match = mode.apply(is_match, self.check_matches(notification, 
field_matchers)?);
+}
+if let Some(calendar_matchers) = self.match_calendar.as_deref() {
+is_match = mode.apply(
+is_match,
+self.check_matches(notification, calendar_matchers)?,
+);
+}
 
 let invert_match = self.invert_match.unwrap_or_default();
 
@@ -273,46 +291,24 @@ impl MatcherConfig {
 })
 }
 
-fn check_field_match(, notification: ) -> Result {
-let mode = self.mode.unwrap_or_default();
-let mut is_match = mode.neutral_element();
-
-if let Some(match_field) = self.match_field.as_deref() {
-for field_matcher in match_field {
-// let field_matcher: FieldMatcher = match_stmt.parse()?;
-is_match = mode.apply(is_match, 
field_matcher.matches(notification));
-}
-}
-
-Ok(is_match)
-}
-
-fn check_severity_match(, notification: ) -> bool {
+/// Check if given `MatchDirectives` match a notification.
+fn check_matches(
+,
+notification: ,
+matchers: &[impl MatchDirective],
+) -> Result {
 let mode = self.mode.unwrap_or_default();
 let mut is_match = mode.neutral_element();
 
-if let Some(matchers) = self.match_severity.as_ref() {
-for severity_matcher in matchers {
-is_match = mode.apply(is_match, 
severity_matcher.matches(notification));
-}
-}
-
-is_match
-}
-
-fn check_calendar_match(, notification: ) -> 
Result {
-let mode = self.mode.unwrap_or_default();
-let mut is_match = mode.neutral_element();
-
-if let Some(matchers) = self.match_calendar.as_ref() {
-for matcher in matchers {
-is_match = mode.apply(is_match, 
matcher.matches(notification)?);
-}
+for field_matcher in matchers {
+is_match = mode.apply(is_match, 
field_matcher.matches(notification)?);
 }
 
 Ok(is_match)
 }
 }
+
+/// Match severity of the notification.
 #[derive(Clone, Debug)]
 pub struct SeverityMatcher {
 severities: Vec,
@@ -321,9 +317,11 @@ pub struct SeverityMatcher {
 proxmox_serde::forward_deserialize_to_from_str!(SeverityMatcher);
 proxmox_serde::forward_serialize_to_display!(SeverityMatcher);
 
-impl SeverityMatcher {
-fn matches(, notification: ) -> bool {
-self.severities.contains()
+/// Common trait implemented by all matching directives
+impl MatchDirective for SeverityMatcher {
+/// Check if this directive matches a given notification
+fn matches(, notification: ) -> Result {
+Ok(self.severities.contains())
 }
 }
 
@@ -360,7 +358,7 @@ pub struct CalendarMatcher {
 proxmox_serde::forward_deserialize_to_from_str!(CalendarMatcher);
 proxmox_serde::forward_serialize_to_display!(CalendarMatcher);
 
-impl CalendarMatcher {
+impl MatchDirective for CalendarMatcher 

[pve-devel] [PATCH v2 proxmox-perl-rs 20/52] notify: support 'origin' paramter

2023-11-14 Thread Lukas Wagner
This parameter shows the origin of a config entry (builtin,
user-created, modified-builtin)

Signed-off-by: Lukas Wagner 
---
 common/src/notify.rs | 5 +
 1 file changed, 5 insertions(+)

diff --git a/common/src/notify.rs b/common/src/notify.rs
index a5ab754..8f9f38f 100644
--- a/common/src/notify.rs
+++ b/common/src/notify.rs
@@ -161,6 +161,8 @@ mod export {
 author,
 comment,
 disable,
+filter: None,
+origin: None,
 },
 )
 }
@@ -242,6 +244,7 @@ mod export {
 comment,
 disable,
 filter: None,
+origin: None,
 },
  { name, token },
 )
@@ -334,6 +337,7 @@ mod export {
 author,
 comment,
 disable,
+origin: None,
 },
  { name, password },
 )
@@ -435,6 +439,7 @@ mod export {
 invert_match,
 comment,
 disable,
+origin: None,
 },
 )
 }
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 proxmox 09/52] sys: email: add `forward`

2023-11-14 Thread Lukas Wagner
This new function forwards an email to new recipients.

Signed-off-by: Lukas Wagner 
---
 proxmox-sys/src/email.rs | 52 +++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/proxmox-sys/src/email.rs b/proxmox-sys/src/email.rs
index 8b3a1b6..c94f634 100644
--- a/proxmox-sys/src/email.rs
+++ b/proxmox-sys/src/email.rs
@@ -3,7 +3,7 @@
 use std::io::Write;
 use std::process::{Command, Stdio};
 
-use anyhow::{bail, Error};
+use anyhow::{bail, format_err, Error};
 
 /// Sends multi-part mail with text and/or html to a list of recipients
 ///
@@ -110,6 +110,56 @@ pub fn sendmail(
 Ok(())
 }
 
+/// Forwards an email message to a given list of recipients.
+///
+/// ``sendmail`` is used for sending the mail, thus `message` must be
+/// compatible with that (the message is piped into stdin unmodified).
+pub fn forward(
+mailto: &[],
+mailfrom: ,
+message: &[u8],
+uid: Option,
+) -> Result<(), Error> {
+use std::os::unix::process::CommandExt;
+
+if mailto.is_empty() {
+bail!("At least one recipient has to be specified!")
+}
+
+let mut builder = Command::new("/usr/sbin/sendmail");
+
+builder
+.args([
+"-N", "never", // never send DSN (avoid mail loops)
+"-f", mailfrom, "--",
+])
+.args(mailto)
+.stdin(Stdio::piped())
+.stdout(Stdio::null())
+.stderr(Stdio::null());
+
+if let Some(uid) = uid {
+builder.uid(uid);
+}
+
+let mut process = builder
+.spawn()
+.map_err(|err| format_err!("could not spawn sendmail process: 
{err}"))?;
+
+process
+.stdin
+.take()
+.unwrap()
+.write_all(message)
+.map_err(|err| format_err!("couldn't write to sendmail stdin: 
{err}"))?;
+
+process
+.wait()
+.map_err(|err| format_err!("sendmail did not exit successfully: 
{err}"))?;
+
+Ok(())
+}
+
 #[cfg(test)]
 mod test {
 use crate::email::sendmail;
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 debcargo-conf 02/52] update lettre to 0.11.1

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/lettre/debian/changelog   | 10 +++
 .../debian/patches/downgrade_fastrand.patch   | 13 
 .../debian/patches/downgrade_idna.patch   | 13 
 src/lettre/debian/patches/downgrade_url.patch | 13 
 .../patches/remove_unused_features.patch  | 69 ++-
 src/lettre/debian/patches/series  |  4 +-
 .../patches/upgrade_quoted_printable.patch| 13 
 7 files changed, 88 insertions(+), 47 deletions(-)
 create mode 100644 src/lettre/debian/patches/downgrade_fastrand.patch
 create mode 100644 src/lettre/debian/patches/downgrade_idna.patch
 create mode 100644 src/lettre/debian/patches/downgrade_url.patch
 delete mode 100644 src/lettre/debian/patches/upgrade_quoted_printable.patch

diff --git a/src/lettre/debian/changelog b/src/lettre/debian/changelog
index d49cbb042..e92c5c070 100644
--- a/src/lettre/debian/changelog
+++ b/src/lettre/debian/changelog
@@ -1,3 +1,13 @@
+rust-lettre (0.11.1-1) UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO; urgency=medium
+
+  * Package lettre 0.11.1 from crates.io using debcargo 2.6.0
+  * Downgrade fastrand from 2.0 to 1.8
+  * Downgrade idna from 0.4 to 0.3
+  * Downgrade url from 2.4 to 2.3
+  * Drop patch that upgrades quoted_printable
+
+ -- Lukas Wagner   Wed, 08 Nov 2023 13:32:49 +0100
+
 rust-lettre (0.10.4-1~bpo12+pve1) proxmox-rust; urgency=medium
 
   * Rebuild for Debian Bookworm / Proxmox
diff --git a/src/lettre/debian/patches/downgrade_fastrand.patch 
b/src/lettre/debian/patches/downgrade_fastrand.patch
new file mode 100644
index 0..975efeb1c
--- /dev/null
+++ b/src/lettre/debian/patches/downgrade_fastrand.patch
@@ -0,0 +1,13 @@
+diff --git a/Cargo.toml b/Cargo.toml
+index 072ea3a..5decb37 100644
+--- a/Cargo.toml
 b/Cargo.toml
+@@ -150,7 +150,7 @@ version = "0.2.1"
+ default-features = false
+ 
+ [dependencies.fastrand]
+-version = "2.0"
++version = "1.8"
+ optional = true
+ 
+ [dependencies.futures-io]
diff --git a/src/lettre/debian/patches/downgrade_idna.patch 
b/src/lettre/debian/patches/downgrade_idna.patch
new file mode 100644
index 0..1cfaaa26c
--- /dev/null
+++ b/src/lettre/debian/patches/downgrade_idna.patch
@@ -0,0 +1,13 @@
+diff --git a/Cargo.toml b/Cargo.toml
+index 5decb37..09d2b9b 100644
+--- a/Cargo.toml
 b/Cargo.toml
+@@ -176,7 +176,7 @@ version = "1"
+ optional = true
+ 
+ [dependencies.idna]
+-version = "0.4"
++version = "0.3"
+ 
+ [dependencies.mime]
+ version = "0.3.4"
diff --git a/src/lettre/debian/patches/downgrade_url.patch 
b/src/lettre/debian/patches/downgrade_url.patch
new file mode 100644
index 0..4da907540
--- /dev/null
+++ b/src/lettre/debian/patches/downgrade_url.patch
@@ -0,0 +1,13 @@
+diff --git a/Cargo.toml b/Cargo.toml
+index 09d2b9b..5004a3b 100644
+--- a/Cargo.toml
 b/Cargo.toml
+@@ -237,7 +237,7 @@ optional = true
+ default-features = false
+ 
+ [dependencies.url]
+-version = "2.4"
++version = "2.3"
+ optional = true
+ 
+ [dependencies.uuid]
diff --git a/src/lettre/debian/patches/remove_unused_features.patch 
b/src/lettre/debian/patches/remove_unused_features.patch
index 0229e41aa..7ce45be0f 100644
--- a/src/lettre/debian/patches/remove_unused_features.patch
+++ b/src/lettre/debian/patches/remove_unused_features.patch
@@ -1,8 +1,8 @@
 diff --git a/Cargo.toml b/Cargo.toml
-index 13c34b6..b4413b6 100644
+index 13e3b77..072ea3a 100644
 --- a/Cargo.toml
 +++ b/Cargo.toml
-@@ -114,32 +114,10 @@ required-features = [
+@@ -114,24 +114,6 @@ required-features = [
  "builder",
  ]
  
@@ -27,6 +27,9 @@ index 13c34b6..b4413b6 100644
  [[bench]]
  name = "transport_smtp"
  harness = false
+@@ -140,10 +122,6 @@ harness = false
+ name = "mailbox_parsing"
+ harness = false
  
 -[dependencies.async-std]
 -version = "1.8"
@@ -35,8 +38,8 @@ index 13c34b6..b4413b6 100644
  [dependencies.async-trait]
  version = "0.1"
  optional = true
-@@ -217,19 +195,6 @@ optional = true
- version = "0.8"
+@@ -224,19 +202,6 @@ optional = true
+ version = "0.9"
  optional = true
  
 -[dependencies.rustls]
@@ -55,19 +58,19 @@ index 13c34b6..b4413b6 100644
  [dependencies.serde]
  version = "1"
  features = ["derive"]
-@@ -248,11 +213,6 @@ optional = true
- version = "0.4.4"
+@@ -255,11 +220,6 @@ optional = true
+ version = "0.5.1"
  optional = true
  
 -[dependencies.tokio1_boring]
--version = "2.1.4"
+-version = "3"
 -optional = true
 -package = "tokio-boring"
 -
  [dependencies.tokio1_crate]
  version = "1"
  optional = true
-@@ -263,11 +223,6 @@ version = "0.3"
+@@ -270,11 +230,6 @@ version = "0.3"
  optional = true
  package = "tokio-native-tls"
  
@@ -79,8 +82,8 @@ index 13c34b6..b4413b6 100644
  [dependencies.tracing]
  version = "0.1.16"
  features = ["std"]
-@@ -283,10 +238,6 @@ optional = true
- version = "0.23"
+@@ -294,10 +249,6 @@ optional = true
+ version = "0.25"
  optional = true
  
 -[dev-dependencies.async-std]
@@ -88,35 +91,35 @@ index 13c34b6..b4413b6 100644
 -features = ["attributes"]
 -
  [dev-dependencies.criterion]
- 

[pve-devel] [PATCH v2 proxmox 13/52] notify: add api for smtp endpoints

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 proxmox-notify/src/api/mod.rs|  33 +++
 proxmox-notify/src/api/smtp.rs   | 356 +++
 proxmox-notify/src/endpoints/smtp.rs |   8 -
 3 files changed, 389 insertions(+), 8 deletions(-)
 create mode 100644 proxmox-notify/src/api/smtp.rs

diff --git a/proxmox-notify/src/api/mod.rs b/proxmox-notify/src/api/mod.rs
index 8042157..762d448 100644
--- a/proxmox-notify/src/api/mod.rs
+++ b/proxmox-notify/src/api/mod.rs
@@ -1,3 +1,4 @@
+use serde::Serialize;
 use std::collections::HashSet;
 
 use proxmox_http_error::HttpError;
@@ -10,6 +11,8 @@ pub mod gotify;
 pub mod matcher;
 #[cfg(feature = "sendmail")]
 pub mod sendmail;
+#[cfg(feature = "smtp")]
+pub mod smtp;
 
 // We have our own, local versions of http_err and http_bail, because
 // we don't want to wrap the error in anyhow::Error. If we were to do that,
@@ -60,6 +63,10 @@ fn ensure_endpoint_exists(#[allow(unused)] config: , 
name: ) -> Resul
 {
 exists = exists || gotify::get_endpoint(config, name).is_ok();
 }
+#[cfg(feature = "smtp")]
+{
+exists = exists || smtp::get_endpoint(config, name).is_ok();
+}
 
 if !exists {
 http_bail!(NOT_FOUND, "endpoint '{name}' does not exist")
@@ -100,6 +107,7 @@ fn get_referrers(config: , entity: ) -> 
Result, HttpE
 }
 }
 }
+
 Ok(referrers)
 }
 
@@ -148,6 +156,31 @@ fn get_referenced_entities(config: , entity: ) 
-> HashSet {
 expanded
 }
 
+#[allow(unused)]
+fn set_private_config_entry(
+config:  Config,
+private_config: ,
+typename: ,
+name: ,
+) -> Result<(), HttpError> {
+config
+.private_config
+.set_data(name, typename, private_config)
+.map_err(|e| {
+http_err!(
+INTERNAL_SERVER_ERROR,
+"could not save private config for endpoint '{}': {e}",
+name
+)
+})
+}
+
+#[allow(unused)]
+fn remove_private_config_entry(config:  Config, name: ) -> Result<(), 
HttpError> {
+config.private_config.sections.remove(name);
+Ok(())
+}
+
 #[cfg(test)]
 mod test_helpers {
 use crate::Config;
diff --git a/proxmox-notify/src/api/smtp.rs b/proxmox-notify/src/api/smtp.rs
new file mode 100644
index 000..bd9d7bb
--- /dev/null
+++ b/proxmox-notify/src/api/smtp.rs
@@ -0,0 +1,356 @@
+use proxmox_http_error::HttpError;
+
+use crate::api::{http_bail, http_err};
+use crate::endpoints::smtp::{
+DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpPrivateConfig,
+SmtpPrivateConfigUpdater, SMTP_TYPENAME,
+};
+use crate::Config;
+
+/// Get a list of all smtp endpoints.
+///
+/// The caller is responsible for any needed permission checks.
+/// Returns a list of all smtp endpoints or a `HttpError` if the config is
+/// erroneous (`500 Internal server error`).
+pub fn get_endpoints(config: ) -> Result, HttpError> {
+config
+.config
+.convert_to_typed_array(SMTP_TYPENAME)
+.map_err(|e| http_err!(NOT_FOUND, "Could not fetch endpoints: {e}"))
+}
+
+/// Get smtp endpoint with given `name`.
+///
+/// The caller is responsible for any needed permission checks.
+/// Returns the endpoint or a `HttpError` if the endpoint was not found (`404 
Not found`).
+pub fn get_endpoint(config: , name: ) -> Result {
+config
+.config
+.lookup(SMTP_TYPENAME, name)
+.map_err(|_| http_err!(NOT_FOUND, "endpoint '{name}' not found"))
+}
+
+/// Add a new smtp endpoint.
+///
+/// The caller is responsible for any needed permission checks.
+/// The caller also responsible for locking the configuration files.
+/// Returns a `HttpError` if:
+///   - an entity with the same name already exists (`400 Bad request`)
+///   - the configuration could not be saved (`500 Internal server error`)
+///   - mailto *and* mailto_user are both set to `None`
+pub fn add_endpoint(
+config:  Config,
+endpoint_config: ,
+private_endpoint_config: ,
+) -> Result<(), HttpError> {
+if endpoint_config.name != private_endpoint_config.name {
+// Programming error by the user of the crate, thus we panic
+panic!("name for endpoint config and private config must be 
identical");
+}
+
+super::ensure_unique(config, _config.name)?;
+
+if endpoint_config.mailto.is_none() && 
endpoint_config.mailto_user.is_none() {
+http_bail!(
+BAD_REQUEST,
+"must at least provide one recipient, either in mailto or in 
mailto-user"
+);
+}
+
+super::set_private_config_entry(
+config,
+private_endpoint_config,
+SMTP_TYPENAME,
+_config.name,
+)?;
+
+config
+.config
+.set_data(_config.name, SMTP_TYPENAME, endpoint_config)
+.map_err(|e| {
+http_err!(
+INTERNAL_SERVER_ERROR,
+"could not save endpoint '{}': {e}",
+endpoint_config.name
+)
+})
+}
+
+/// Update 

[pve-devel] [PATCH v2 pve-manager 34/52] api: notification: add disable and origin params

2023-11-14 Thread Lukas Wagner
'disable' can be set to disable a matcher/target.
'origin' signals whether the configuration entry
was created by the user or whether it was built-in/
built-in-and-modified.

Signed-off-by: Lukas Wagner 
---
 PVE/API2/Cluster/Notifications.pm | 113 ++
 1 file changed, 99 insertions(+), 14 deletions(-)

diff --git a/PVE/API2/Cluster/Notifications.pm 
b/PVE/API2/Cluster/Notifications.pm
index 42207aaa..27e3a66d 100644
--- a/PVE/API2/Cluster/Notifications.pm
+++ b/PVE/API2/Cluster/Notifications.pm
@@ -164,8 +164,19 @@ __PACKAGE__->register_method ({
},
'comment' => {
description => 'Comment',
-   type=> 'string',
-   optional=> 1,
+   type => 'string',
+   optional => 1,
+   },
+   'disable' => {
+   description => 'Show if this target is disabled',
+   type => 'boolean',
+   optional => 1,
+   default => 0,
+   },
+   'origin' => {
+   description => 'Show if this entry was created by a user or 
was built-in',
+   type  => 'string',
+   enum => [qw(user-created builtin modified-builtin)],
},
},
},
@@ -183,6 +194,8 @@ __PACKAGE__->register_method ({
name => $target->{name},
comment => $target->{comment},
type => 'sendmail',
+   disable => $target->{disable},
+   origin => $target->{origin},
};
}
 
@@ -191,6 +204,8 @@ __PACKAGE__->register_method ({
name => $target->{name},
comment => $target->{comment},
type => 'gotify',
+   disable => $target->{disable},
+   origin => $target->{origin},
};
}
 
@@ -199,6 +214,8 @@ __PACKAGE__->register_method ({
name => $target->{name},
comment => $target->{comment},
type => 'smtp',
+   disable => $target->{disable},
+   origin => $target->{origin},
};
}
 
@@ -295,8 +312,14 @@ my $sendmail_properties = {
 },
 'comment' => {
description => 'Comment',
-   type=> 'string',
-   optional=> 1,
+   type => 'string',
+   optional => 1,
+},
+'disable' => {
+   description => 'Disable this target',
+   type => 'boolean',
+   optional => 1,
+   default => 0,
 },
 };
 
@@ -319,7 +342,14 @@ __PACKAGE__->register_method ({
type => 'array',
items => {
type => 'object',
-   properties => $sendmail_properties,
+   properties => {
+   %$sendmail_properties,
+   'origin' => {
+   description => 'Show if this entry was created by a user or 
was built-in',
+   type  => 'string',
+   enum => [qw(user-created builtin modified-builtin)],
+   },
+   },
},
links => [ { rel => 'child', href => '{name}' } ],
 },
@@ -404,6 +434,7 @@ __PACKAGE__->register_method ({
my $from_address = extract_param($param, 'from-address');
my $author = extract_param($param, 'author');
my $comment = extract_param($param, 'comment');
+   my $disable = extract_param($param, 'disable');
 
eval {
PVE::Notify::lock_config(sub {
@@ -416,6 +447,7 @@ __PACKAGE__->register_method ({
$from_address,
$author,
$comment,
+   $disable,
);
 
PVE::Notify::write_config($config);
@@ -463,6 +495,7 @@ __PACKAGE__->register_method ({
my $from_address = extract_param($param, 'from-address');
my $author = extract_param($param, 'author');
my $comment = extract_param($param, 'comment');
+   my $disable = extract_param($param, 'disable');
 
my $delete = extract_param($param, 'delete');
my $digest = extract_param($param, 'digest');
@@ -478,6 +511,7 @@ __PACKAGE__->register_method ({
$from_address,
$author,
$comment,
+   $disable,
$delete,
$digest,
);
@@ -543,8 +577,14 @@ my $gotify_properties = {
 },
 'comment' => {
description => 'Comment',
-   type=> 'string',
-   optional=> 1,
+   type => 'string',
+   optional => 1,
+},
+'disable' => {
+   description => 'Disable this target',
+   type => 'boolean',
+   optional => 1,
+   default => 0,
 },
 };
 
@@ -567,7 +607,14 @@ __PACKAGE__->register_method ({
type => 'array',
   

[pve-devel] [PATCH v2 pve-manager 31/52] ui: vzdump: remove left-overs from target/policy based notifications

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 www/manager6/dc/Backup.js | 81 ---
 .../form/NotificationPolicySelector.js|  1 -
 www/manager6/window/Backup.js | 35 +---
 3 files changed, 15 insertions(+), 102 deletions(-)

diff --git a/www/manager6/dc/Backup.js b/www/manager6/dc/Backup.js
index 0c8d2d4f..e1c76a1d 100644
--- a/www/manager6/dc/Backup.js
+++ b/www/manager6/dc/Backup.js
@@ -36,29 +36,11 @@ Ext.define('PVE.dc.BackupEdit', {
delete values.node;
}
 
-   if (!isCreate) {
-   // 'mailnotification' is deprecated in favor of 
'notification-policy'
-   // -> Migration to the new parameter happens in init, so we are
-   //safe to remove the old parameter here.
-   Proxmox.Utils.assemble_field_data(values, { 'delete': 
'mailnotification' });
-
-   // If sending notifications via mail, remove the current value 
of
-   // 'notification-target'
-   if (values['notification-mode'] === "mailto") {
-   Proxmox.Utils.assemble_field_data(
-   values,
-   { 'delete': 'notification-target' },
-   );
-   } else {
-   // and vice versa...
-   Proxmox.Utils.assemble_field_data(
-   values,
-   { 'delete': 'mailto' },
-   );
-   }
-   }
-
-   delete values['notification-mode'];
+   // Get rid of new-old parameters for notification settings.
+   // These should only be set for those selected few who ran
+   // pve-manager from pvetest.
+   Proxmox.Utils.assemble_field_data(values, { 'delete': 
'notification-policy' });
+   Proxmox.Utils.assemble_field_data(values, { 'delete': 
'notification-target' });
 
if (!values.id && isCreate) {
values.id = 'backup-' + 
Ext.data.identifier.Uuid.Global.generate().slice(0, 13);
@@ -170,20 +152,14 @@ Ext.define('PVE.dc.BackupEdit', {
success: function(response, _options) {
let data = response.result.data;
 
-   // 'mailnotification' is deprecated. Let's automatically
-   // migrate to the compatible 'notification-policy' 
parameter
-   if (data.mailnotification) {
-   if (!data["notification-policy"]) {
-   data["notification-policy"] = 
data.mailnotification;
-   }
-
-   delete data.mailnotification;
-   }
-
-   if (data['notification-target']) {
-   data['notification-mode'] = 'notification-target';
-   } else if (data.mailto) {
-   data['notification-mode'] = 'mailto';
+   // Migrate 'new'-old notification-policy back to
+   // old-old mailnotification. Only should affect
+   // users who used pve-manager from pvetest.
+   // This was a remnant of notifications before the
+   // overhaul.
+   let policy = data['notification-policy'];
+   if (policy === 'always' || policy === 'failure') {
+   data.mailnotification = policy;
}
 
if (data.exclude) {
@@ -228,7 +204,6 @@ Ext.define('PVE.dc.BackupEdit', {
 viewModel: {
data: {
selMode: 'include',
-   notificationMode: 'notification-target',
},
 
formulas: {
@@ -327,44 +302,16 @@ Ext.define('PVE.dc.BackupEdit', {
{
xtype: 'pveEmailNotificationSelector',
fieldLabel: gettext('Notify'),
-   name: 'notification-policy',
+   name: 'mailnotification',
cbind: {
value: (get) => get('isCreate') ? 
'always' : '',
deleteEmpty: '{!isCreate}',
},
},
-   {
-   xtype: 'pveNotificationModeSelector',
-   fieldLabel: gettext('Notify via'),
-   name: 'notification-mode',
-   bind: {
-   value: '{notificationMode}',
-   },
-   },
-   {
-   xtype: 'pveNotificationTargetSelector',
-  

[pve-devel] [PATCH v2 proxmox-widget-toolkit 39/52] notification ui: rename filter to matcher

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/Makefile  |  2 +-
 src/data/model/NotificationConfig.js  |  2 +-
 src/panel/NotificationConfigView.js   | 26 +--
 ...lterEdit.js => NotificationMatcherEdit.js} | 14 +-
 4 files changed, 22 insertions(+), 22 deletions(-)
 rename src/window/{NotificationFilterEdit.js => NotificationMatcherEdit.js} 
(92%)

diff --git a/src/Makefile b/src/Makefile
index e07f17c..c6d31c3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -88,7 +88,7 @@ JSSRC=\
window/ACMEPluginEdit.js\
window/ACMEDomains.js   \
window/EndpointEditBase.js  \
-   window/NotificationFilterEdit.js \
+   window/NotificationMatcherEdit.js \
window/FileBrowser.js   \
window/AuthEditBase.js  \
window/AuthEditOpenId.js\
diff --git a/src/data/model/NotificationConfig.js 
b/src/data/model/NotificationConfig.js
index bb4ef85..f447db4 100644
--- a/src/data/model/NotificationConfig.js
+++ b/src/data/model/NotificationConfig.js
@@ -7,7 +7,7 @@ Ext.define('proxmox-notification-endpoints', {
 idProperty: 'name',
 });
 
-Ext.define('proxmox-notification-filters', {
+Ext.define('proxmox-notification-matchers', {
 extend: 'Ext.data.Model',
 fields: ['name', 'comment'],
 proxy: {
diff --git a/src/panel/NotificationConfigView.js 
b/src/panel/NotificationConfigView.js
index ba98395..ecf764d 100644
--- a/src/panel/NotificationConfigView.js
+++ b/src/panel/NotificationConfigView.js
@@ -21,7 +21,7 @@ Ext.define('Proxmox.panel.NotificationConfigView', {
border: false,
collapsible: true,
animCollapse: false,
-   xtype: 'pmxNotificationFilterView',
+   xtype: 'pmxNotificationMatcherView',
cbind: {
baseUrl: '{baseUrl}',
},
@@ -209,21 +209,21 @@ Ext.define('Proxmox.panel.NotificationEndpointView', {
 },
 });
 
-Ext.define('Proxmox.panel.NotificationFilterView', {
+Ext.define('Proxmox.panel.NotificationMatcherView', {
 extend: 'Ext.grid.Panel',
-alias: 'widget.pmxNotificationFilterView',
+alias: 'widget.pmxNotificationMatcherView',
 
-title: gettext('Notification Filters'),
+title: gettext('Notification Matchers'),
 
 controller: {
xclass: 'Ext.app.ViewController',
 
-   openEditWindow: function(filter) {
+   openEditWindow: function(matcher) {
let me = this;
 
-   Ext.create('Proxmox.window.NotificationFilterEdit', {
+   Ext.create('Proxmox.window.NotificationMatcherEdit', {
baseUrl: me.getView().baseUrl,
-   name: filter,
+   name: matcher,
autoShow: true,
listeners: {
destroy: () => me.reload(),
@@ -253,12 +253,12 @@ Ext.define('Proxmox.panel.NotificationFilterView', {
activate: 'reload',
 },
 
-emptyText: gettext('No notification filters configured'),
+emptyText: gettext('No notification matchers configured'),
 
 columns: [
{
dataIndex: 'name',
-   text: gettext('Filter Name'),
+   text: gettext('Matcher Name'),
renderer: Ext.String.htmlEncode,
flex: 1,
},
@@ -276,8 +276,8 @@ Ext.define('Proxmox.panel.NotificationFilterView', {
autoDestroyRstore: true,
rstore: {
type: 'update',
-   storeid: 'proxmox-notification-filters',
-   model: 'proxmox-notification-filters',
+   storeid: 'proxmox-notification-matchers',
+   model: 'proxmox-notification-matchers',
autoStart: true,
},
sorters: 'name',
@@ -307,12 +307,12 @@ Ext.define('Proxmox.panel.NotificationFilterView', {
{
xtype: 'proxmoxStdRemoveButton',
callback: 'reload',
-   baseurl: `${me.baseUrl}/filters`,
+   baseurl: `${me.baseUrl}/matchers`,
},
],
});
 
me.callParent();
-   me.store.rstore.proxy.setUrl(`/api2/json/${me.baseUrl}/filters`);
+   me.store.rstore.proxy.setUrl(`/api2/json/${me.baseUrl}/matchers`);
 },
 });
diff --git a/src/window/NotificationFilterEdit.js 
b/src/window/NotificationMatcherEdit.js
similarity index 92%
rename from src/window/NotificationFilterEdit.js
rename to src/window/NotificationMatcherEdit.js
index bcde4fa..a014f3e 100644
--- a/src/window/NotificationFilterEdit.js
+++ b/src/window/NotificationMatcherEdit.js
@@ -1,6 +1,6 @@
-Ext.define('Proxmox.panel.NotificationFilterEditPanel', {
+Ext.define('Proxmox.panel.NotificationMatcherEditPanel', {
 extend: 'Proxmox.panel.InputPanel',
-xtype: 'pmxNotificationFilterEditPanel',
+xtype: 'pmxNotificationMatcherEditPanel',
 mixins: ['Proxmox.Mixin.CBind'],
 
 items: [
@@ -11,7 +11,7 @@ 

[pve-devel] [PATCH v2 proxmox 14/52] notify: add 'disable' parameter for matchers and targets.

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 proxmox-notify/src/api/gotify.rs |  8 +++-
 proxmox-notify/src/api/matcher.rs|  6 ++
 proxmox-notify/src/api/sendmail.rs   |  8 
 proxmox-notify/src/api/smtp.rs   |  6 ++
 proxmox-notify/src/endpoints/gotify.rs   |  9 +
 proxmox-notify/src/endpoints/sendmail.rs | 11 ++-
 proxmox-notify/src/endpoints/smtp.rs |  9 +
 proxmox-notify/src/lib.rs| 13 +
 proxmox-notify/src/matcher.rs| 21 -
 9 files changed, 84 insertions(+), 7 deletions(-)

diff --git a/proxmox-notify/src/api/gotify.rs b/proxmox-notify/src/api/gotify.rs
index 22d3d2e..10f5d7d 100644
--- a/proxmox-notify/src/api/gotify.rs
+++ b/proxmox-notify/src/api/gotify.rs
@@ -88,6 +88,7 @@ pub fn update_endpoint(
 for deleteable_property in delete {
 match deleteable_property {
 DeleteableGotifyProperty::Comment => endpoint.comment = None,
+DeleteableGotifyProperty::Disable => endpoint.disable = None,
 }
 }
 }
@@ -110,6 +111,10 @@ pub fn update_endpoint(
 endpoint.comment = Some(comment.into());
 }
 
+if let Some(disable) = _config_updater.disable {
+endpoint.disable = Some(*disable);
+}
+
 config
 .config
 .set_data(name, GOTIFY_TYPENAME, )
@@ -172,7 +177,7 @@ mod tests {
 name: "gotify-endpoint".into(),
 server: "localhost".into(),
 comment: Some("comment".into()),
-filter: None,
+..Default::default()
 },
  {
 name: "gotify-endpoint".into(),
@@ -232,6 +237,7 @@ mod tests {
  {
 server: Some("newhost".into()),
 comment: Some("newcomment".into()),
+..Default::default()
 },
  {
 token: Some("changedtoken".into()),
diff --git a/proxmox-notify/src/api/matcher.rs 
b/proxmox-notify/src/api/matcher.rs
index 0592b14..a69ca40 100644
--- a/proxmox-notify/src/api/matcher.rs
+++ b/proxmox-notify/src/api/matcher.rs
@@ -85,6 +85,7 @@ pub fn update_matcher(
 DeleteableMatcherProperty::Mode => matcher.mode = None,
 DeleteableMatcherProperty::InvertMatch => matcher.invert_match 
= None,
 DeleteableMatcherProperty::Comment => matcher.comment = None,
+DeleteableMatcherProperty::Disable => matcher.disable = None,
 }
 }
 }
@@ -113,6 +114,10 @@ pub fn update_matcher(
 matcher.comment = Some(comment.into());
 }
 
+if let Some(disable) = _updater.disable {
+matcher.disable = Some(*disable);
+}
+
 if let Some(target) = _updater.target {
 super::ensure_endpoints_exist(config, target.as_slice())?;
 matcher.target = Some(target.clone());
@@ -209,6 +214,7 @@ matcher: matcher2
 invert_match: Some(true),
 target: Some(vec!["foo".into()]),
 comment: Some("new comment".into()),
+..Default::default()
 },
 None,
 Some(),
diff --git a/proxmox-notify/src/api/sendmail.rs 
b/proxmox-notify/src/api/sendmail.rs
index dbd9559..1f6e9ae 100644
--- a/proxmox-notify/src/api/sendmail.rs
+++ b/proxmox-notify/src/api/sendmail.rs
@@ -85,6 +85,7 @@ pub fn update_endpoint(
 DeleteableSendmailProperty::Comment => endpoint.comment = None,
 DeleteableSendmailProperty::Mailto => endpoint.mailto = None,
 DeleteableSendmailProperty::MailtoUser => endpoint.mailto_user 
= None,
+DeleteableSendmailProperty::Disable => endpoint.disable = None,
 }
 }
 }
@@ -109,6 +110,10 @@ pub fn update_endpoint(
 endpoint.comment = Some(comment.into());
 }
 
+if let Some(disable) =  {
+endpoint.disable = Some(*disable);
+}
+
 if endpoint.mailto.is_none() && endpoint.mailto_user.is_none() {
 http_bail!(
 BAD_REQUEST,
@@ -165,6 +170,7 @@ pub mod tests {
 author: Some("root".into()),
 comment: Some("Comment".into()),
 filter: None,
+..Default::default()
 },
 )?;
 
@@ -208,6 +214,7 @@ pub mod tests {
 from_address: Some("r...@example.com".into()),
 author: Some("newauthor".into()),
 comment: Some("new comment".into()),
+..Default::default()
 },
 None,
 Some(&[0; 32]),
@@ -233,6 +240,7 @@ pub mod tests {
 from_address: Some("r...@example.com".into()),
 author: Some("newauthor".into()),
 comment: Some("new comment".into()),
+..Default::default()
 },
 None,
 Some(),
diff --git 

[pve-devel] [PATCH v2 pve-manager 24/52] api: notification: remove notification groups

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 PVE/API2/Cluster/Notifications.pm | 267 +-
 1 file changed, 4 insertions(+), 263 deletions(-)

diff --git a/PVE/API2/Cluster/Notifications.pm 
b/PVE/API2/Cluster/Notifications.pm
index ec666903..b34802c8 100644
--- a/PVE/API2/Cluster/Notifications.pm
+++ b/PVE/API2/Cluster/Notifications.pm
@@ -121,7 +121,6 @@ __PACKAGE__->register_method ({
my $result = [
{ name => 'endpoints' },
{ name => 'filters' },
-   { name => 'groups' },
{ name => 'targets' },
];
 
@@ -161,8 +160,7 @@ __PACKAGE__->register_method ({
 name => 'get_all_targets',
 path => 'targets',
 method => 'GET',
-description => 'Returns a list of all entities that can be used as 
notification targets' .
-   ' (endpoints and groups).',
+description => 'Returns a list of all entities that can be used as 
notification targets.',
 permissions => {
description => "Only lists entries where you have 'Mapping.Modify', 
'Mapping.Use' or"
. " 'Mapping.Audit' permissions on '/mapping/notification/'."
@@ -180,14 +178,14 @@ __PACKAGE__->register_method ({
type => 'object',
properties => {
name => {
-   description => 'Name of the endpoint/group.',
+   description => 'Name of the target.',
type => 'string',
format => 'pve-configid',
},
'type' => {
-   description => 'Type of the endpoint or group.',
+   description => 'Type of the target.',
type  => 'string',
-   enum => [qw(sendmail gotify group)],
+   enum => [qw(sendmail gotify)],
},
'comment' => {
description => 'Comment',
@@ -221,14 +219,6 @@ __PACKAGE__->register_method ({
};
}
 
-   for my $target (@{$config->get_groups()}) {
-   push @$result, {
-   name => $target->{name},
-   comment => $target->{comment},
-   type => 'group',
-   };
-   }
-
$result
};
 
@@ -290,255 +280,6 @@ __PACKAGE__->register_method ({
 }
 });
 
-my $group_properties = {
-name => {
-   description => 'Name of the group.',
-   type => 'string',
-   format => 'pve-configid',
-},
-'endpoint' => {
-   type => 'array',
-   items => {
-   type => 'string',
-   format => 'pve-configid',
-   },
-   description => 'List of included endpoints',
-},
-'comment' => {
-   description => 'Comment',
-   type => 'string',
-   optional => 1,
-},
-filter => {
-   description => 'Name of the filter that should be applied.',
-   type => 'string',
-   format => 'pve-configid',
-   optional => 1,
-},
-};
-
-__PACKAGE__->register_method ({
-name => 'get_groups',
-path => 'groups',
-method => 'GET',
-description => 'Returns a list of all groups',
-protected => 1,
-permissions => {
-   description => "Only lists entries where you have 'Mapping.Modify', 
'Mapping.Use' or"
-   . " 'Mapping.Audit' permissions on '/mapping/notification/'.",
-   user => 'all',
-},
-parameters => {
-   additionalProperties => 0,
-   properties => {},
-},
-returns => {
-   type => 'array',
-   items => {
-   type => 'object',
-   properties => $group_properties,
-   },
-   links => [ { rel => 'child', href => '{name}' } ],
-},
-code => sub {
-   my $config = PVE::Notify::read_config();
-   my $rpcenv = PVE::RPCEnvironment::get();
-
-   my $entities = eval {
-   $config->get_groups();
-   };
-   raise_api_error($@) if $@;
-
-   return filter_entities_by_privs($rpcenv, $entities);
-}
-});
-
-__PACKAGE__->register_method ({
-name => 'get_group',
-path => 'groups/{name}',
-method => 'GET',
-description => 'Return a specific group',
-protected => 1,
-permissions => {
-   check => ['or',
-   ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
-   ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
-   ],
-},
-parameters => {
-   additionalProperties => 0,
-   properties => {
-   name => {
-   type => 'string',
-   format => 'pve-configid',
-   },
-   }
-},
-returns => {
-   type => 'object',
-   properties => {
-   %$group_properties,
-   digest => get_standard_option('pve-config-digest'),
-   },
-},
-code => sub {
-   my ($param) = @_;
-   my $name = extract_param($param, 'name');
-
-   my $config = PVE::Notify::read_config();
-
-   my $group = eval {
-   

[pve-devel] [PATCH v2 proxmox-widget-toolkit 36/52] notification ui: add target selector for matcher

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/window/NotificationFilterEdit.js | 145 +++
 1 file changed, 145 insertions(+)

diff --git a/src/window/NotificationFilterEdit.js 
b/src/window/NotificationFilterEdit.js
index 703a9e2..bcde4fa 100644
--- a/src/window/NotificationFilterEdit.js
+++ b/src/window/NotificationFilterEdit.js
@@ -49,6 +49,11 @@ Ext.define('Proxmox.panel.NotificationFilterEditPanel', {
deleteDefaultValue: '{!isCreate}',
},
},
+   {
+   xtype: 'pmxNotificationTargetSelector',
+   name: 'target',
+   allowBlank: false,
+   },
{
xtype: 'proxmoxtextfield',
name: 'comment',
@@ -107,3 +112,143 @@ Ext.define('Proxmox.window.NotificationFilterEdit', {
}
 },
 });
+
+Ext.define('Proxmox.form.NotificationTargetSelector', {
+extend: 'Ext.grid.Panel',
+alias: 'widget.pmxNotificationTargetSelector',
+
+mixins: {
+   field: 'Ext.form.field.Field',
+},
+
+padding: '0 0 10 0',
+
+allowBlank: true,
+selectAll: false,
+isFormField: true,
+
+store: {
+   autoLoad: true,
+   model: 'proxmox-notification-endpoints',
+   sorters: 'name',
+},
+
+columns: [
+   {
+   header: gettext('Target Name'),
+   dataIndex: 'name',
+   flex: 1,
+   },
+   {
+   header: gettext('Type'),
+   dataIndex: 'type',
+   flex: 1,
+   },
+   {
+   header: gettext('Comment'),
+   dataIndex: 'comment',
+   flex: 3,
+   },
+],
+
+selModel: {
+   selType: 'checkboxmodel',
+   mode: 'SIMPLE',
+},
+
+checkChangeEvents: [
+   'selectionchange',
+   'change',
+],
+
+listeners: {
+   selectionchange: function() {
+   // to trigger validity and error checks
+   this.checkChange();
+   },
+},
+
+getSubmitData: function() {
+   let me = this;
+   let res = {};
+   res[me.name] = me.getValue();
+   return res;
+},
+
+getValue: function() {
+   let me = this;
+   if (me.savedValue !== undefined) {
+   return me.savedValue;
+   }
+   let sm = me.getSelectionModel();
+   return (sm.getSelection() ?? []).map(item => item.data.name);
+},
+
+setValueSelection: function(value) {
+   let me = this;
+
+   let store = me.getStore();
+
+   let notFound = [];
+   let selection = value.map(item => {
+   let found = store.findRecord('name', item, 0, false, true, true);
+   if (!found) {
+   notFound.push(item);
+   }
+   return found;
+   }).filter(r => r);
+
+   for (const name of notFound) {
+   let rec = store.add({
+   name,
+   type: '-',
+   comment: gettext('Included target does not exist!'),
+   });
+   selection.push(rec[0]);
+   }
+
+   let sm = me.getSelectionModel();
+   if (selection.length) {
+   sm.select(selection);
+   } else {
+   sm.deselectAll();
+   }
+   // to correctly trigger invalid class
+   me.getErrors();
+},
+
+setValue: function(value) {
+   let me = this;
+
+   let store = me.getStore();
+   if (!store.isLoaded()) {
+   me.savedValue = value;
+   store.on('load', function() {
+   me.setValueSelection(value);
+   delete me.savedValue;
+   }, { single: true });
+   } else {
+   me.setValueSelection(value);
+   }
+   return me.mixins.field.setValue.call(me, value);
+},
+
+getErrors: function(value) {
+   let me = this;
+   if (!me.isDisabled() && me.allowBlank === false &&
+   me.getSelectionModel().getCount() === 0) {
+   me.addBodyCls(['x-form-trigger-wrap-default', 
'x-form-trigger-wrap-invalid']);
+   return [gettext('No target selected')];
+   }
+
+   me.removeBodyCls(['x-form-trigger-wrap-default', 
'x-form-trigger-wrap-invalid']);
+   return [];
+},
+
+initComponent: function() {
+   let me = this;
+   me.callParent();
+   me.initField();
+},
+
+});
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



[pve-devel] [PATCH v2 debcargo-conf 01/52] cherry-pick chumsky 0.9.2 from debian unstable

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 src/chumsky/debian/changelog   |  5 +++
 src/chumsky/debian/copyright   | 39 +
 src/chumsky/debian/copyright.debcargo.hint | 51 ++
 src/chumsky/debian/debcargo.toml   |  2 +
 4 files changed, 97 insertions(+)
 create mode 100644 src/chumsky/debian/changelog
 create mode 100644 src/chumsky/debian/copyright
 create mode 100644 src/chumsky/debian/copyright.debcargo.hint
 create mode 100644 src/chumsky/debian/debcargo.toml

diff --git a/src/chumsky/debian/changelog b/src/chumsky/debian/changelog
new file mode 100644
index 0..ae6b5ff8f
--- /dev/null
+++ b/src/chumsky/debian/changelog
@@ -0,0 +1,5 @@
+rust-chumsky (0.9.2-1) unstable; urgency=medium
+
+  * Package chumsky 0.9.2 from crates.io using debcargo 2.6.0
+
+ -- Jelmer Vernooij   Wed, 14 Jun 2023 23:38:48 +0100
diff --git a/src/chumsky/debian/copyright b/src/chumsky/debian/copyright
new file mode 100644
index 0..eaa3e6768
--- /dev/null
+++ b/src/chumsky/debian/copyright
@@ -0,0 +1,39 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: chumsky
+Upstream-Contact:
+ Joshua Barretto 
+ Elijah Hartvigsen 
+Source: https://github.com/zesterer/chumsky
+
+Files: *
+Copyright:
+ 2021-2023 Joshua Barretto 
+ 2021-2023 Elijah Hartvigsen 
+License: MIT
+
+Files: debian/*
+Copyright:
+ 2023 Debian Rust Maintainers 
+ 2023 Jelmer Vernooij 
+License: MIT
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/src/chumsky/debian/copyright.debcargo.hint 
b/src/chumsky/debian/copyright.debcargo.hint
new file mode 100644
index 0..e02a9ab0f
--- /dev/null
+++ b/src/chumsky/debian/copyright.debcargo.hint
@@ -0,0 +1,51 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: chumsky
+Upstream-Contact:
+ Joshua Barretto 
+ Elijah Hartvigsen 
+Source: https://github.com/zesterer/chumsky
+
+Files: *
+Copyright:
+ FIXME (overlay) UNKNOWN-YEARS Joshua Barretto 
+ FIXME (overlay) UNKNOWN-YEARS Elijah Hartvigsen 
+License: MIT
+Comment:
+ FIXME (overlay): Since upstream copyright years are not available in
+ Cargo.toml, they were extracted from the upstream Git repository. This may not
+ be correct information so you should review and fix this before uploading to
+ the archive.
+
+Files: LICENSE
+Copyright: 2021 Joshua Barretto
+License: UNKNOWN-LICENSE; FIXME (overlay)
+Comment:
+ FIXME (overlay): These notices are extracted from files. Please review them
+ before uploading to the archive.
+
+Files: debian/*
+Copyright:
+ 2023 Debian Rust Maintainers 
+ 2023 Jelmer Vernooij 
+License: MIT
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/src/chumsky/debian/debcargo.toml b/src/chumsky/debian/debcargo.toml
new file mode 100644
index 0..77e8151ed
--- /dev/null
+++ b/src/chumsky/debian/debcargo.toml
@@ -0,0 +1,2 @@
+overlay = "."
+uploaders = 

[pve-devel] [PATCH v2 pve-manager 28/52] api: apt: adapt to matcher-based notifications

2023-11-14 Thread Lukas Wagner
Signed-off-by: Lukas Wagner 
---
 PVE/API2/APT.pm | 27 +++
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/PVE/API2/APT.pm b/PVE/API2/APT.pm
index a213fc59..da75a4dc 100644
--- a/PVE/API2/APT.pm
+++ b/PVE/API2/APT.pm
@@ -286,8 +286,6 @@ __PACKAGE__->register_method({
 description => "This is used to resynchronize the package index files from 
their sources (apt-get update).",
 permissions => {
check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
-   description => "If 'notify: target-package-updates' is set, then the 
user must have the "
-   . "'Mapping.Use' permission on '/mapping/notification/'",
 },
 protected => 1,
 proxyto => 'node',
@@ -297,7 +295,7 @@ __PACKAGE__->register_method({
node => get_standard_option('pve-node'),
notify => {
type => 'boolean',
-   description => "Send notification mail about new packages (to 
email address specified for user 'root\@pam').",
+   description => "Send notification about new packages.",
optional => 1,
default => 0,
},
@@ -317,16 +315,6 @@ __PACKAGE__->register_method({
 
my $rpcenv = PVE::RPCEnvironment::get();
my $dcconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
-   my $target = $dcconf->{notify}->{'target-package-updates'} //
-   PVE::Notify::default_target();
-
-   if ($param->{notify} && $target ne PVE::Notify::default_target()) {
-   # If we notify via anything other than the default target (mail to 
root),
-   # then the user must have the proper permissions for the target.
-   # The mail-to-root target does not require these, as otherwise
-   # we would break compatibility.
-   PVE::Notify::check_may_use_target($target, $rpcenv);
-   }
 
my $authuser = $rpcenv->get_user();
 
@@ -392,16 +380,23 @@ __PACKAGE__->register_method({
 
return if !$count;
 
-   my $properties = {
+   my $template_data = {
updates  => $updates_table,
hostname => $hostname,
};
 
+   # Additional metadata fields that can be used in notification
+   # matchers.
+   my $metadata_fields = {
+   type => 'package-updates',
+   hostname => $hostname,
+   };
+
PVE::Notify::info(
-   $target,
$updates_available_subject_template,
$updates_available_body_template,
-   $properties,
+   $template_data,
+   $metadata_fields,
);
 
foreach my $pi (@$pkglist) {
-- 
2.39.2



___
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel



  1   2   >