Re: [pve-devel] [PATCH ifupdown2] patch: fix bond mac address at boot.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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
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'
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
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
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
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
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
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
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
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
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
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
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}
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
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
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
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`
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
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
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
'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
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
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.
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
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
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
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
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