public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-firewall v4 10/27] firewall: api: run reference updates as tasks
Date: Thu, 22 Oct 2026 14:46:51 +0200	[thread overview]
Message-ID: <20261022124708.429069-11-a.bied-charreton@proxmox.com> (raw)
In-Reply-To: <20261022124708.429069-1-a.bied-charreton@proxmox.com>

A 'dangling-references' action other than 'keep' rewrites the rules that
referenced the object, which for a cluster-scoped object means going
through all guest, host and vnet firewall configs in the cluster,
potentially updating each of them.

The update_refs() helper logs each change made to each config. Fork
workers for renames and removals in order for the logs to land in the
task log, which is visible cluster-wide, instead of the syslog, which
is only visible on the node that happened to handle the request. Plain
creates or updates that only change a comment still run synchronously.

Checks that can be performed without holding the lock are made before
forking so a rejected request can still fail right away instead of
requiring the caller to go read the task.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/API2/Firewall/Aliases.pm | 238 +++++++++++++++++-------------
 src/PVE/API2/Firewall/IPSet.pm   | 241 ++++++++++++++++++-------------
 src/PVE/Firewall/References.pm   |  20 +++
 3 files changed, 297 insertions(+), 202 deletions(-)

diff --git a/src/PVE/API2/Firewall/Aliases.pm b/src/PVE/API2/Firewall/Aliases.pm
index cb9b448..36d091e 100644
--- a/src/PVE/API2/Firewall/Aliases.pm
+++ b/src/PVE/API2/Firewall/Aliases.pm
@@ -3,7 +3,7 @@ package PVE::API2::Firewall::AliasesBase;
 use strict;
 use warnings;
 
-use PVE::Firewall::References qw(update_refs);
+use PVE::Firewall::References qw(update_refs run_ref_update);
 use PVE::Exception qw(raise raise_param_exc);
 use PVE::JSONSchema qw(get_standard_option);
 
@@ -84,6 +84,36 @@ my $aliases_to_list = sub {
     return $list;
 };
 
+my $check_digest = sub {
+    my ($aliases, $param) = @_;
+
+    my $list = &$aliases_to_list($aliases);
+    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+
+    PVE::Tools::assert_if_modified($digest, $param->{digest});
+};
+
+my $check_update = sub {
+    my ($aliases, $param, $dangling_refs) = @_;
+
+    $check_digest->($aliases, $param);
+
+    my $name = lc($param->{name});
+    raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
+
+    my $rename = $param->{rename};
+    $rename = lc($rename) if $rename;
+
+    if (
+        $rename
+        && $name ne $rename
+        && defined($aliases->{$rename})
+        && $dangling_refs ne 'force-rename'
+    ) {
+        raise_param_exc({ name => "alias '$param->{rename}' already exists" });
+    }
+};
+
 sub register_get_aliases {
     my ($class) = @_;
 
@@ -245,87 +275,93 @@ sub register_update_alias {
             additionalProperties => 0,
             properties => $properties,
         },
-        returns => { type => "null" },
+        returns => { type => 'string', optional => 1 },
         code => sub {
             my ($param) = @_;
 
-            $class->lock_config(
-                $param,
-                sub {
-                    my ($param) = @_;
-
-                    my $dangling_refs = $param->{'dangling-references'} // 'keep';
-
-                    # 'force-rename' only differs in skipping the check above, the references are
-                    # renamed either way
-                    my $ref_action;
-                    if ($dangling_refs eq 'force-rename') {
-                        $ref_action = 'rename';
-                    } elsif ($dangling_refs ne 'keep') {
-                        $ref_action = $dangling_refs;
-                    }
-
-                    my ($fw_conf, $aliases) = $class->load_config($param);
-
-                    my $list = &$aliases_to_list($aliases);
-
-                    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
-
-                    PVE::Tools::assert_if_modified($digest, $param->{digest});
-
-                    my $name = lc($param->{name});
-
-                    raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
-
-                    my $data = { name => $param->{name}, cidr => $param->{cidr} };
-                    $data->{comment} = $param->{comment} if $param->{comment};
+            my $dangling_refs = $param->{'dangling-references'} // 'keep';
+
+            # 'force-rename' only differs in skipping the check above, the references are renamed
+            # either way
+            my $ref_action;
+            if ($dangling_refs eq 'force-rename') {
+                $ref_action = 'rename';
+            } elsif ($dangling_refs ne 'keep') {
+                $ref_action = $dangling_refs;
+            }
+
+            my $renaming = $param->{rename} && lc($param->{name}) ne lc($param->{rename});
+            my $subject =
+                $renaming ? "alias '$param->{name}' renamed to '$param->{rename}'" : undef;
+
+            my $code = sub {
+                $class->lock_config(
+                    $param,
+                    sub {
+                        my ($param) = @_;
+
+                        my ($fw_conf, $aliases) = $class->load_config($param);
+
+                        $check_update->($aliases, $param, $dangling_refs);
+
+                        my $name = lc($param->{name});
+
+                        my $data = { name => $param->{name}, cidr => $param->{cidr} };
+                        $data->{comment} = $param->{comment} if $param->{comment};
+
+                        $aliases->{$name} = $data;
+
+                        my $rename = $param->{rename};
+                        $rename = lc($rename) if $rename;
+
+                        if ($rename && ($name ne $rename)) {
+                            $aliases->{$rename} =
+                                { $aliases->{$name}->%*, name => $param->{rename} };
+
+                            my $saved = 0;
+                            my $save = sub {
+                                $class->save_aliases($param, $fw_conf, $aliases);
+                                $saved = 1;
+                            };
+
+                            eval {
+                                update_refs({
+                                    conf => $fw_conf,
+                                    kind => 'aliases',
+                                    old => $name,
+                                    new => $param->{rename},
+                                    env => $class->rule_env(),
+                                    action => $ref_action,
+                                    subject => $subject,
+                                    save => $save,
+                                });
+                            };
+                            if (my $err = $@) {
+                                die "rename interrupted, the alias exists under both names; "
+                                    . "retry with 'force-rename' to finish: $err"
+                                    if $saved;
+                                die "could not update references to alias '$name': $err";
+                            }
+
+                            delete $aliases->{$name};
+                        }
 
-                    $aliases->{$name} = $data;
+                        $class->save_aliases($param, $fw_conf, $aliases);
+                    },
+                );
+            };
 
-                    my $rename = $param->{rename};
-                    $rename = lc($rename) if $rename;
-
-                    if ($rename && ($name ne $rename)) {
-                        raise_param_exc({ name => "alias '$param->{rename}' already exists" })
-                            if defined($aliases->{$rename})
-                            && $dangling_refs ne 'force-rename';
-
-                        $aliases->{$rename} =
-                            { $aliases->{$name}->%*, name => $param->{rename} };
-
-                        my $saved = 0;
-                        my $save = sub {
-                            $class->save_aliases($param, $fw_conf, $aliases);
-                            $saved = 1;
-                        };
-
-                        eval {
-                            update_refs({
-                                conf => $fw_conf,
-                                kind => 'aliases',
-                                old => $name,
-                                new => $param->{rename},
-                                env => $class->rule_env(),
-                                action => $ref_action,
-                                subject => "alias '$name' renamed to '$rename'",
-                                save => $save,
-                            });
-                        };
-                        if (my $err = $@) {
-                            die "rename interrupted, the alias exists under both names; "
-                                . "retry with 'force-rename' to finish: $err"
-                                if $saved;
-                            die "could not update references to alias '$name': $err";
-                        }
+            if (!$renaming) {
+                $code->();
+                return undef;
+            }
 
-                        delete $aliases->{$name};
-                    }
+            # fail on the request instead of inside the task.
+            my (undef, $aliases) = $class->load_config($param);
 
-                    $class->save_aliases($param, $fw_conf, $aliases);
-                },
-            );
+            $check_update->($aliases, $param, $dangling_refs);
 
-            return undef;
+            return run_ref_update('fw-alias-rename', $param->{vmid} // $param->{name}, $code);
         },
     });
 }
@@ -359,41 +395,47 @@ sub register_delete_alias {
             additionalProperties => 0,
             properties => $properties,
         },
-        returns => { type => "null" },
+        returns => { type => 'string', optional => 1 },
         code => sub {
             my ($param) = @_;
 
-            $class->lock_config(
-                $param,
-                sub {
-                    my ($param) = @_;
+            my $action = $param->{'dangling-references'} // 'keep';
+            my $subject = "alias '$param->{name}' deleted";
 
-                    my ($fw_conf, $aliases) = $class->load_config($param);
+            my $code = sub {
+                $class->lock_config(
+                    $param,
+                    sub {
+                        my ($param) = @_;
 
-                    my $list = &$aliases_to_list($aliases);
-                    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
-                    PVE::Tools::assert_if_modified($digest, $param->{digest});
+                        my ($fw_conf, $aliases) = $class->load_config($param);
 
-                    my $name = lc($param->{name});
+                        $check_digest->($aliases, $param);
 
-                    my $action = $param->{'dangling-references'} // 'keep';
+                        my $name = lc($param->{name});
 
-                    update_refs({
-                        conf => $fw_conf,
-                        kind => 'aliases',
-                        old => $param->{name},
-                        env => $class->rule_env(),
-                        action => $action eq 'keep' ? undef : $action,
-                        subject => "alias '$param->{name}' deleted",
-                    });
+                        update_refs({
+                            conf => $fw_conf,
+                            kind => 'aliases',
+                            old => $param->{name},
+                            env => $class->rule_env(),
+                            action => $action eq 'keep' ? undef : $action,
+                            subject => $subject,
+                        });
 
-                    delete $aliases->{$name};
+                        delete $aliases->{$name};
 
-                    $class->save_aliases($param, $fw_conf, $aliases);
-                },
-            );
+                        $class->save_aliases($param, $fw_conf, $aliases);
+                    },
+                );
+            };
 
-            return undef;
+            # fail on the request instead of inside the task
+            my (undef, $aliases) = $class->load_config($param);
+
+            $check_digest->($aliases, $param);
+
+            return run_ref_update('fw-alias-delete', $param->{vmid} // $param->{name}, $code);
         },
     });
 }
diff --git a/src/PVE/API2/Firewall/IPSet.pm b/src/PVE/API2/Firewall/IPSet.pm
index fe4f8c9..a4356e4 100644
--- a/src/PVE/API2/Firewall/IPSet.pm
+++ b/src/PVE/API2/Firewall/IPSet.pm
@@ -2,7 +2,7 @@ package PVE::API2::Firewall::IPSetBase;
 
 use strict;
 use warnings;
-use PVE::Firewall::References qw(update_refs);
+use PVE::Firewall::References qw(update_refs run_ref_update);
 use PVE::Exception qw(raise raise_param_exc);
 use PVE::JSONSchema qw(get_standard_option);
 
@@ -162,37 +162,45 @@ sub register_delete_ipset {
             additionalProperties => 0,
             properties => $properties,
         },
-        returns => { type => 'null' },
+        returns => { type => 'string', optional => 1 },
         code => sub {
             my ($param) = @_;
 
-            $class->lock_config(
-                $param,
-                sub {
-                    my ($param) = @_;
+            my $action = $param->{'dangling-references'} // 'keep';
+            my $subject = "ipset '$param->{name}' deleted";
 
-                    my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+            my $code = sub {
+                $class->lock_config(
+                    $param,
+                    sub {
+                        my ($param) = @_;
 
-                    die "IPSet '$param->{name}' is not empty\n"
-                        if scalar(@$ipset) && !$param->{force};
+                        my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
 
-                    my $action = $param->{'dangling-references'} // 'keep';
+                        die "IPSet '$param->{name}' is not empty\n"
+                            if scalar(@$ipset) && !$param->{force};
 
-                    update_refs({
-                        conf => $fw_conf,
-                        kind => 'ipset',
-                        old => $param->{name},
-                        env => $class->rule_env(),
-                        action => $action eq 'keep' ? undef : $action,
-                        subject => "ipset '$param->{name}' deleted",
-                    });
+                        update_refs({
+                            conf => $fw_conf,
+                            kind => 'ipset',
+                            old => $param->{name},
+                            env => $class->rule_env(),
+                            action => $action eq 'keep' ? undef : $action,
+                            subject => $subject,
+                        });
 
-                    $class->save_ipset($param, $fw_conf, undef);
+                        $class->save_ipset($param, $fw_conf, undef);
+                    },
+                );
+            };
 
-                },
-            );
+            # fail on the request instead of inside the task
+            my (undef, undef, $ipset) = $class->load_config($param);
 
-            return undef;
+            die "IPSet '$param->{name}' is not empty\n"
+                if scalar($ipset->@*) && !$param->{force};
+
+            return run_ref_update('fw-ipset-delete', $param->{vmid} // $param->{name}, $code);
         },
     });
 }
@@ -555,7 +563,7 @@ package PVE::API2::Firewall::BaseIPSetList;
 
 use strict;
 use warnings;
-use PVE::Firewall::References qw(update_refs);
+use PVE::Firewall::References qw(update_refs run_ref_update);
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Exception qw(raise_param_exc);
 use PVE::Firewall;
@@ -623,6 +631,25 @@ my $get_ipset_list = sub {
     return wantarray ? ($list, $digest) : $list;
 };
 
+my $check_rename = sub {
+    my ($conf, $param, $dangling_refs) = @_;
+
+    my (undef, $digest) = &$get_ipset_list($conf);
+    PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+    raise_param_exc({ name => "IPSet '$param->{rename}' does not exist" })
+        if !$conf->{ipset}->{ $param->{rename} };
+
+    # prevent overwriting existing ipset
+    if (
+        $param->{name} ne $param->{rename}
+        && $conf->{ipset}->{ $param->{name} }
+        && $dangling_refs ne 'force-rename'
+    ) {
+        raise_param_exc({ name => "IPSet '$param->{name}' already exists" });
+    }
+};
+
 sub register_index {
     my ($class) = @_;
 
@@ -707,100 +734,106 @@ sub register_create {
             additionalProperties => 0,
             properties => $properties,
         },
-        returns => { type => 'null' },
+        returns => { type => 'string', optional => 1 },
         code => sub {
             my ($param) = @_;
 
-            $class->lock_config(
-                $param,
-                sub {
-                    my ($param) = @_;
-
-                    my $dangling_refs = $param->{'dangling-references'} // 'keep';
+            my $dangling_refs = $param->{'dangling-references'} // 'keep';
 
-                    # 'force-rename' only differs in skipping the check below, the references are
-                    # renamed either way
-                    my $ref_action;
-                    if ($dangling_refs eq 'force-rename') {
-                        $ref_action = 'rename';
-                    } elsif ($dangling_refs ne 'keep') {
-                        $ref_action = $dangling_refs;
-                    }
+            # 'force-rename' only differs in skipping the check above, the references are renamed
+            # either way
+            my $ref_action;
+            if ($dangling_refs eq 'force-rename') {
+                $ref_action = 'rename';
+            } elsif ($dangling_refs ne 'keep') {
+                $ref_action = $dangling_refs;
+            }
 
-                    my ($cluster_conf, $fw_conf) = $class->load_config($param);
-
-                    if ($param->{rename}) {
-                        my (undef, $digest) = &$get_ipset_list($fw_conf);
-                        PVE::Tools::assert_if_modified($digest, $param->{digest});
-
-                        raise_param_exc({ name => "IPSet '$param->{rename}' does not exist" })
-                            if !$fw_conf->{ipset}->{ $param->{rename} };
-
-                        if ($param->{name} ne $param->{rename}) {
-                            # prevent overwriting existing ipset
-                            raise_param_exc({
-                                name => "IPSet '$param->{name}' does already exist" })
-                                if $fw_conf->{ipset}->{ $param->{name} }
-                                && $dangling_refs ne 'force-rename';
-
-                            $fw_conf->{ipset}->{ $param->{name} } =
-                                $fw_conf->{ipset}->{ $param->{rename} };
-
-                            my $saved = 0;
-                            my $save = sub {
-                                $class->save_config($param, $fw_conf);
-                                $saved = 1;
-                            };
-
-                            eval {
-                                update_refs({
-                                    conf => $fw_conf,
-                                    kind => 'ipset',
-                                    old => $param->{rename},
-                                    new => $param->{name},
-                                    env => $class->rule_env(),
-                                    action => $ref_action,
-                                    subject =>
-                                        "ipset '$param->{rename}' renamed to '$param->{name}'",
-                                    save => $save,
-                                });
-                            };
-                            if (my $err = $@) {
-                                die "rename interrupted, the IPSet exists under both names; "
-                                    . "retry with 'force-rename' to finish: $err"
-                                    if $saved;
-                                die "could not update references to IPSet "
-                                    . "'$param->{rename}': $err";
+            my $renaming = $param->{rename} && $param->{name} ne $param->{rename};
+            my $subject =
+                $renaming ? "ipset '$param->{rename}' renamed to '$param->{name}'" : undef;
+
+            my $code = sub {
+                $class->lock_config(
+                    $param,
+                    sub {
+                        my ($param) = @_;
+
+                        my ($cluster_conf, $fw_conf) = $class->load_config($param);
+
+                        if ($param->{rename}) {
+                            $check_rename->($fw_conf, $param, $dangling_refs);
+
+                            if ($param->{name} ne $param->{rename}) {
+                                $fw_conf->{ipset}->{ $param->{name} } =
+                                    $fw_conf->{ipset}->{ $param->{rename} };
+
+                                my $saved = 0;
+                                my $save = sub {
+                                    $class->save_config($param, $fw_conf);
+                                    $saved = 1;
+                                };
+
+                                eval {
+                                    update_refs({
+                                        conf => $fw_conf,
+                                        kind => 'ipset',
+                                        old => $param->{rename},
+                                        new => $param->{name},
+                                        env => $class->rule_env(),
+                                        action => $ref_action,
+                                        subject => $subject,
+                                        save => $save,
+                                    });
+                                };
+                                if (my $err = $@) {
+                                    die
+                                        "rename interrupted, the IPSet exists under both names; "
+                                        . "retry with 'force-rename' to finish: $err"
+                                        if $saved;
+                                    die "could not update references to IPSet "
+                                        . "'$param->{rename}': $err";
+                                }
+
+                                delete $fw_conf->{ipset}->{ $param->{rename} };
+
+                                if (
+                                    my $comment =
+                                    delete $fw_conf->{ipset_comments}->{ $param->{rename} }
+                                ) {
+                                    $fw_conf->{ipset_comments}->{ $param->{name} } = $comment;
+                                }
                             }
 
-                            delete $fw_conf->{ipset}->{ $param->{rename} };
-
-                            if (
-                                my $comment =
-                                delete $fw_conf->{ipset_comments}->{ $param->{rename} }
-                            ) {
-                                $fw_conf->{ipset_comments}->{ $param->{name} } = $comment;
+                            $fw_conf->{ipset_comments}->{ $param->{name} } = $param->{comment}
+                                if defined($param->{comment});
+                        } else {
+                            foreach my $name (keys %{ $fw_conf->{ipset} }) {
+                                raise_param_exc({ name => "IPSet '$name' already exists" })
+                                    if $name eq $param->{name};
                             }
-                        }
 
-                        $fw_conf->{ipset_comments}->{ $param->{name} } = $param->{comment}
-                            if defined($param->{comment});
-                    } else {
-                        foreach my $name (keys %{ $fw_conf->{ipset} }) {
-                            raise_param_exc({ name => "IPSet '$name' already exists" })
-                                if $name eq $param->{name};
+                            $fw_conf->{ipset}->{ $param->{name} } = [];
+                            $fw_conf->{ipset_comments}->{ $param->{name} } = $param->{comment}
+                                if defined($param->{comment});
                         }
 
-                        $fw_conf->{ipset}->{ $param->{name} } = [];
-                        $fw_conf->{ipset_comments}->{ $param->{name} } = $param->{comment}
-                            if defined($param->{comment});
-                    }
+                        $class->save_config($param, $fw_conf);
+                    },
+                );
+            };
 
-                    $class->save_config($param, $fw_conf);
-                },
-            );
+            if (!$renaming) {
+                $code->();
+                return undef;
+            }
 
-            return undef;
+            # fail on the request instead of inside the task
+            my (undef, $fw_conf) = $class->load_config($param);
+
+            $check_rename->($fw_conf, $param, $dangling_refs);
+
+            return run_ref_update('fw-ipset-rename', $param->{vmid} // $param->{rename}, $code);
         },
     });
 }
diff --git a/src/PVE/Firewall/References.pm b/src/PVE/Firewall/References.pm
index 37df434..f32c7db 100644
--- a/src/PVE/Firewall/References.pm
+++ b/src/PVE/Firewall/References.pm
@@ -13,6 +13,7 @@ use base 'Exporter';
 our @EXPORT_OK = qw(
     collect_refs
     update_refs
+    run_ref_update
 );
 
 sub collect_refs {
@@ -422,6 +423,25 @@ sub update_refs {
     return $count;
 }
 
+=head3 run_ref_update($type, $id, $code)
+
+Run C<$code> as a worker task of type C<$type> for C<$id>.
+
+C<$id> is the guest a guest-scoped object belongs to so the task is logged with that guest, and the
+name of the object otherwise.
+
+Return the UPID of the task.
+
+=cut
+
+sub run_ref_update {
+    my ($type, $id, $code) = @_;
+
+    my $rpcenv = PVE::RESTEnvironment::get();
+
+    return $rpcenv->fork_worker($type, $id, $rpcenv->get_user(), $code);
+}
+
 =head3 format_ref_change($change)
 
 Format one recorded config change as a single line, e.g.
-- 
2.47.3





  parent reply	other threads:[~2026-10-22 12:51 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22 12:46 [PATCH container/docs/firewall/manager/network/qemu-server v4 00/27] handle dangling references when firewall objects go away Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 01/27] firewall: move object reference helpers to their own module Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 02/27] firewall: references: add helpers to update object references Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 03/27] firewall: references: log the references an object update touched Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 04/27] firewall: references: add wrappers for the SDN-generated IPSets Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 05/27] firewall: parser: do not log errors for disabled rules Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 06/27] firewall: ipset: add option to handle dangling references on rename Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 07/27] firewall: ipset: add option to handle dangling references on delete Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 08/27] firewall: aliases: add option to handle dangling references on rename Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 09/27] firewall: aliases: add option to handle dangling references on delete Arthur Bied-Charreton
2026-10-22 12:46 ` Arthur Bied-Charreton [this message]
2026-10-22 12:46 ` [PATCH pve-firewall v4 11/27] firewall: tests: add tests for object reference updates Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 12/27] firewall: references: handle security groups Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 13/27] firewall: groups: add option to handle dangling references on rename Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-firewall v4 14/27] fix #8176: firewall: groups: handle dangling references on delete Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-network v4 15/27] apply: add option to handle dangling references on VNet deletion Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH qemu-server v4 16/27] api: destroy_vm: add option to handle dangling IPSet references Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-container v4 17/27] " Arthur Bied-Charreton
2026-10-22 12:46 ` [PATCH pve-docs v4 18/27] firewall: remove trailing spaces Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-docs v4 19/27] firewall: document renaming and removing objects Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 20/27] ui: firewall: add a common widget for handling dangling references Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 21/27] ui: firewall: ipset: add controls for " Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 22/27] ui: firewall: aliases: " Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 23/27] ui: sdn: apply: add control for dangling IPSet references Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 24/27] ui: guest destroy: use let for non-constant variable bindings Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 25/27] ui: guest destroy: add control for dangling IPSet references Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 26/27] ui: firewall: show the task for reference updates Arthur Bied-Charreton
2026-10-22 12:47 ` [PATCH pve-manager v4 27/27] ui: firewall: groups: add controls for dangling references Arthur Bied-Charreton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261022124708.429069-11-a.bied-charreton@proxmox.com \
    --to=a.bied-charreton@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal