[pve-devel] [PATCH firewall 3/7] api: lock configs

Fabian Grünbichler f.gruenbichler at proxmox.com
Wed Apr 29 10:52:52 CEST 2020


wherever we have a r-m-w cycle.

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---

Notes:
    best viewed with -w

 src/PVE/API2/Firewall/Aliases.pm |  80 +++++++++-------
 src/PVE/API2/Firewall/Cluster.pm |  36 ++++----
 src/PVE/API2/Firewall/Groups.pm  |  52 ++++++-----
 src/PVE/API2/Firewall/Host.pm    |  42 +++++----
 src/PVE/API2/Firewall/IPSet.pm   | 152 ++++++++++++++++++-------------
 src/PVE/API2/Firewall/Rules.pm   |  94 +++++++++++--------
 src/PVE/API2/Firewall/VM.pm      |  43 ++++-----
 7 files changed, 280 insertions(+), 219 deletions(-)

diff --git a/src/PVE/API2/Firewall/Aliases.pm b/src/PVE/API2/Firewall/Aliases.pm
index 9ea6f70..33ac669 100644
--- a/src/PVE/API2/Firewall/Aliases.pm
+++ b/src/PVE/API2/Firewall/Aliases.pm
@@ -143,19 +143,23 @@ sub register_create_alias {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($fw_conf, $aliases) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    my $name = lc($param->{name});
+		my ($fw_conf, $aliases) = $class->load_config($param);
 
-	    raise_param_exc({ name => "alias '$param->{name}' already exists" })
-		if defined($aliases->{$name});
+		my $name = lc($param->{name});
 
-	    my $data = { name => $param->{name}, cidr => $param->{cidr} };
-	    $data->{comment} = $param->{comment} if $param->{comment};
+		raise_param_exc({ name => "alias '$param->{name}' already exists" })
+		    if defined($aliases->{$name});
 
-	    $aliases->{$name} = $data;
+		my $data = { name => $param->{name}, cidr => $param->{cidr} };
+		$data->{comment} = $param->{comment} if $param->{comment};
 
-	    $class->save_aliases($param, $fw_conf, $aliases);
+		$aliases->{$name} = $data;
+
+		$class->save_aliases($param, $fw_conf, $aliases);
+	    });
 
 	    return undef;
 	}});
@@ -219,35 +223,39 @@ sub register_update_alias {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($fw_conf, $aliases) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    my $list = &$aliases_to_list($aliases);
+		my ($fw_conf, $aliases) = $class->load_config($param);
 
-	    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
+		my $list = &$aliases_to_list($aliases);
 
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my (undef, $digest) = PVE::Firewall::copy_list_with_digest($list);
 
-	    my $name = lc($param->{name});
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
+		my $name = lc($param->{name});
 
-	    my $data = { name => $param->{name}, cidr => $param->{cidr} };
-	    $data->{comment} = $param->{comment} if $param->{comment};
+		raise_param_exc({ name => "no such alias" }) if !$aliases->{$name};
 
-	    $aliases->{$name} = $data;
+		my $data = { name => $param->{name}, cidr => $param->{cidr} };
+		$data->{comment} = $param->{comment} if $param->{comment};
 
-	    my $rename = $param->{rename};
-	    $rename = lc($rename) if $rename;
+		$aliases->{$name} = $data;
 
-	    if ($rename && ($name ne $rename)) {
-		raise_param_exc({ name => "alias '$param->{rename}' already exists" })
-		    if defined($aliases->{$rename});
-		$aliases->{$name}->{name} = $param->{rename};
-		$aliases->{$rename} = $aliases->{$name};
-		delete $aliases->{$name};
-	    }
+		my $rename = $param->{rename};
+		$rename = lc($rename) if $rename;
 
-	    $class->save_aliases($param, $fw_conf, $aliases);
+		if ($rename && ($name ne $rename)) {
+		    raise_param_exc({ name => "alias '$param->{rename}' already exists" })
+			if defined($aliases->{$rename});
+		    $aliases->{$name}->{name} = $param->{rename};
+		    $aliases->{$rename} = $aliases->{$name};
+		    delete $aliases->{$name};
+		}
+
+		$class->save_aliases($param, $fw_conf, $aliases);
+	    });
 
 	    return undef;
 	}});
@@ -276,16 +284,20 @@ sub register_delete_alias {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($fw_conf, $aliases) = $class->load_config($param);
+	    $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});
-	    delete $aliases->{$name};
+		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});
+		delete $aliases->{$name};
 
-	    $class->save_aliases($param, $fw_conf, $aliases);
+		$class->save_aliases($param, $fw_conf, $aliases);
+	    });
 
 	    return undef;
 	}});
diff --git a/src/PVE/API2/Firewall/Cluster.pm b/src/PVE/API2/Firewall/Cluster.pm
index 0bf8285..c9c3e67 100644
--- a/src/PVE/API2/Firewall/Cluster.pm
+++ b/src/PVE/API2/Firewall/Cluster.pm
@@ -132,29 +132,31 @@ __PACKAGE__->register_method({
     code => sub {
 	my ($param) = @_;
 
-	my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+	PVE::Firewall::lock_clusterfw_conf(10, sub {
+	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
 
-	my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($cluster_conf->{options});
-	PVE::Tools::assert_if_modified($digest, $param->{digest});
+	    my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($cluster_conf->{options});
+	    PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	if ($param->{delete}) {
-	    foreach my $opt (PVE::Tools::split_list($param->{delete})) {
-		raise_param_exc({ delete => "no such option '$opt'" })
-		    if !$option_properties->{$opt};
-		delete $cluster_conf->{options}->{$opt};
+	    if ($param->{delete}) {
+		foreach my $opt (PVE::Tools::split_list($param->{delete})) {
+		    raise_param_exc({ delete => "no such option '$opt'" })
+			if !$option_properties->{$opt};
+		    delete $cluster_conf->{options}->{$opt};
+		}
 	    }
-	}
 
-	if (defined($param->{enable}) && ($param->{enable} > 1)) {
-	    $param->{enable} = time();
-	}
+	    if (defined($param->{enable}) && ($param->{enable} > 1)) {
+		$param->{enable} = time();
+	    }
 
-	foreach my $k (keys %$option_properties) {
-	    next if !defined($param->{$k});
-	    $cluster_conf->{options}->{$k} = $param->{$k};
-	}
+	    foreach my $k (keys %$option_properties) {
+		next if !defined($param->{$k});
+		$cluster_conf->{options}->{$k} = $param->{$k};
+	    }
 
-	PVE::Firewall::save_clusterfw_conf($cluster_conf);
+	    PVE::Firewall::save_clusterfw_conf($cluster_conf);
+	});
 
 	# instant firewall update when using double (anti-lockout) API call
 	# -> not waiting for a firewall update at the first (timestamp enable) set
diff --git a/src/PVE/API2/Firewall/Groups.pm b/src/PVE/API2/Firewall/Groups.pm
index ec0d016..558ba8e 100644
--- a/src/PVE/API2/Firewall/Groups.pm
+++ b/src/PVE/API2/Firewall/Groups.pm
@@ -91,37 +91,39 @@ __PACKAGE__->register_method({
     code => sub {
 	my ($param) = @_;
 
-	my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+	PVE::Firewall::lock_clusterfw_conf(10, sub {
+	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
 
-	if ($param->{rename}) {
-	    my (undef, $digest) = &$get_security_group_list($cluster_conf);
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+	    if ($param->{rename}) {
+		my (undef, $digest) = &$get_security_group_list($cluster_conf);
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    raise_param_exc({ group => "Security group '$param->{rename}' does not exist" })
-		if !$cluster_conf->{groups}->{$param->{rename}};
+		raise_param_exc({ group => "Security group '$param->{rename}' does not exist" })
+		    if !$cluster_conf->{groups}->{$param->{rename}};
 
-	    # prevent overwriting an existing group
-	    raise_param_exc({ group => "Security group '$param->{group}' does already exist" })
-		if $cluster_conf->{groups}->{$param->{group}} &&
-		$param->{group} ne $param->{rename};
+		# prevent overwriting an existing group
+		raise_param_exc({ group => "Security group '$param->{group}' does already exist" })
+		    if $cluster_conf->{groups}->{$param->{group}} &&
+		    $param->{group} ne $param->{rename};
 
-	    my $data = delete $cluster_conf->{groups}->{$param->{rename}};
-	    $cluster_conf->{groups}->{$param->{group}} = $data;
-	    if (my $comment = delete $cluster_conf->{group_comments}->{$param->{rename}}) {
-		$cluster_conf->{group_comments}->{$param->{group}} = $comment;
-	    }
-	    $cluster_conf->{group_comments}->{$param->{group}} = $param->{comment} if defined($param->{comment});
-	} else {
-	    foreach my $name (keys %{$cluster_conf->{groups}}) {
-		raise_param_exc({ group => "Security group '$name' already exists" })
-		    if $name eq $param->{group};
-	    }
+		my $data = delete $cluster_conf->{groups}->{$param->{rename}};
+		$cluster_conf->{groups}->{$param->{group}} = $data;
+		if (my $comment = delete $cluster_conf->{group_comments}->{$param->{rename}}) {
+		    $cluster_conf->{group_comments}->{$param->{group}} = $comment;
+		}
+		$cluster_conf->{group_comments}->{$param->{group}} = $param->{comment} if defined($param->{comment});
+	    } else {
+		foreach my $name (keys %{$cluster_conf->{groups}}) {
+		    raise_param_exc({ group => "Security group '$name' already exists" })
+			if $name eq $param->{group};
+		}
 
-	    $cluster_conf->{groups}->{$param->{group}} = [];
-	    $cluster_conf->{group_comments}->{$param->{group}} = $param->{comment} if defined($param->{comment});
-	}
+		$cluster_conf->{groups}->{$param->{group}} = [];
+		$cluster_conf->{group_comments}->{$param->{group}} = $param->{comment} if defined($param->{comment});
+	    }
 
-	PVE::Firewall::save_clusterfw_conf($cluster_conf);
+	    PVE::Firewall::save_clusterfw_conf($cluster_conf);
+	});
 
 	return undef;
     }});
diff --git a/src/PVE/API2/Firewall/Host.pm b/src/PVE/API2/Firewall/Host.pm
index 2303494..b66ca55 100644
--- a/src/PVE/API2/Firewall/Host.pm
+++ b/src/PVE/API2/Firewall/Host.pm
@@ -118,30 +118,32 @@ __PACKAGE__->register_method({
     code => sub {
 	my ($param) = @_;
 
-	my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
-	my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
-
-	my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($hostfw_conf->{options});
-	PVE::Tools::assert_if_modified($digest, $param->{digest});
-
-	if ($param->{delete}) {
-	    foreach my $opt (PVE::Tools::split_list($param->{delete})) {
-		raise_param_exc({ delete => "no such option '$opt'" })
-		    if !$option_properties->{$opt};
-		delete $hostfw_conf->{options}->{$opt};
+	PVE::Firewall::lock_hostfw_conf(10, sub {
+	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+	    my $hostfw_conf = PVE::Firewall::load_hostfw_conf($cluster_conf);
+
+	    my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($hostfw_conf->{options});
+	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+	    if ($param->{delete}) {
+		foreach my $opt (PVE::Tools::split_list($param->{delete})) {
+		    raise_param_exc({ delete => "no such option '$opt'" })
+			if !$option_properties->{$opt};
+		    delete $hostfw_conf->{options}->{$opt};
+		}
 	    }
-	}
 
-	if (defined($param->{enable})) {
-	    $param->{enable} = $param->{enable} ? 1 : 0;
-	}
+	    if (defined($param->{enable})) {
+		$param->{enable} = $param->{enable} ? 1 : 0;
+	    }
 
-	foreach my $k (keys %$option_properties) {
-	    next if !defined($param->{$k});
-	    $hostfw_conf->{options}->{$k} = $param->{$k};
-	}
+	    foreach my $k (keys %$option_properties) {
+		next if !defined($param->{$k});
+		$hostfw_conf->{options}->{$k} = $param->{$k};
+	    }
 
-	PVE::Firewall::save_hostfw_conf($hostfw_conf);
+	    PVE::Firewall::save_hostfw_conf($hostfw_conf);
+	});
 
 	return undef;
     }});
diff --git a/src/PVE/API2/Firewall/IPSet.pm b/src/PVE/API2/Firewall/IPSet.pm
index 72e7524..913dd86 100644
--- a/src/PVE/API2/Firewall/IPSet.pm
+++ b/src/PVE/API2/Firewall/IPSet.pm
@@ -148,12 +148,17 @@ sub register_delete_ipset {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
+
+		my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
 
-	    die "IPSet '$param->{name}' is not empty\n"
-		if scalar(@$ipset);
+		die "IPSet '$param->{name}' is not empty\n"
+		    if scalar(@$ipset);
 
-	    $class->save_ipset($param, $fw_conf, undef);
+		$class->save_ipset($param, $fw_conf, undef);
+
+	    });
 
 	    return undef;
 	}});
@@ -184,30 +189,35 @@ sub register_create_ip {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    my $cidr = $param->{cidr};
+		my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
 
-	    foreach my $entry (@$ipset) {
-		raise_param_exc({ cidr => "address '$cidr' already exists" })
-		    if $entry->{cidr} eq $cidr;
-	    }
+		my $cidr = $param->{cidr};
+
+		foreach my $entry (@$ipset) {
+		    raise_param_exc({ cidr => "address '$cidr' already exists" })
+			if $entry->{cidr} eq $cidr;
+		}
+
+		raise_param_exc({ cidr => "a zero prefix is not allowed in ipset entries" })
+		    if $cidr =~ m!/0+$!;
 
-	    raise_param_exc({ cidr => "a zero prefix is not allowed in ipset entries" })
-		if $cidr =~ m!/0+$!;
+		# make sure alias exists (if $cidr is an alias)
+		PVE::Firewall::resolve_alias($cluster_conf, $fw_conf, $cidr)
+		    if $cidr =~ m/^${PVE::Firewall::ip_alias_pattern}$/;
 
-	    # make sure alias exists (if $cidr is an alias)
-	    PVE::Firewall::resolve_alias($cluster_conf, $fw_conf, $cidr)
-		if $cidr =~ m/^${PVE::Firewall::ip_alias_pattern}$/;
+		my $data = { cidr => $cidr };
 
-	    my $data = { cidr => $cidr };
+		$data->{nomatch} = 1 if $param->{nomatch};
+		$data->{comment} = $param->{comment} if $param->{comment};
 
-	    $data->{nomatch} = 1 if $param->{nomatch};
-	    $data->{comment} = $param->{comment} if $param->{comment};
+		unshift @$ipset, $data;
 
-	    unshift @$ipset, $data;
+		$class->save_ipset($param, $fw_conf, $ipset);
 
-	    $class->save_ipset($param, $fw_conf, $ipset);
+	    });
 
 	    return undef;
 	}});
@@ -276,19 +286,27 @@ sub register_update_ip {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+	    my $found = $class->lock_config($param, sub {
+		my ($param) = @_;
+
+		my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
 
-	    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    foreach my $entry (@$ipset) {
-		if($entry->{cidr} eq $param->{cidr}) {
-		    $entry->{nomatch} = $param->{nomatch};
-		    $entry->{comment} = $param->{comment};
-		    $class->save_ipset($param, $fw_conf, $ipset);
-		    return;
+		foreach my $entry (@$ipset) {
+		    if($entry->{cidr} eq $param->{cidr}) {
+			$entry->{nomatch} = $param->{nomatch};
+			$entry->{comment} = $param->{comment};
+			$class->save_ipset($param, $fw_conf, $ipset);
+			return 1;
+		    }
 		}
-	    }
+
+		return 0;
+	    });
+
+	    return if $found;
 
 	    raise_param_exc({ cidr => "no such IP/Network" });
 	}});
@@ -318,18 +336,22 @@ sub register_delete_ip {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my ($cluster_conf, $fw_conf, $ipset) = $class->load_config($param);
 
-	    my $new = [];
+		my (undef, $digest) = PVE::Firewall::copy_list_with_digest($ipset);
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    foreach my $entry (@$ipset) {
-		push @$new, $entry if $entry->{cidr} ne $param->{cidr};
-	    }
+		my $new = [];
 
-	    $class->save_ipset($param, $fw_conf, $new);
+		foreach my $entry (@$ipset) {
+		    push @$new, $entry if $entry->{cidr} ne $param->{cidr};
+		}
+
+		$class->save_ipset($param, $fw_conf, $new);
+	    });
 
 	    return undef;
 	}});
@@ -611,37 +633,41 @@ sub register_create {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    if ($param->{rename}) {
-		my (undef, $digest) = &$get_ipset_list($fw_conf);
-		PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my ($cluster_conf, $fw_conf) = $class->load_config($param);
 
-		raise_param_exc({ name => "IPSet '$param->{rename}' does not exist" })
-		    if !$fw_conf->{ipset}->{$param->{rename}};
+		if ($param->{rename}) {
+		    my (undef, $digest) = &$get_ipset_list($fw_conf);
+		    PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-		# prevent overwriting existing ipset
-		raise_param_exc({ name => "IPSet '$param->{name}' does already exist"})
-		    if $fw_conf->{ipset}->{$param->{name}} &&
-		    $param->{name} ne $param->{rename};
+		    raise_param_exc({ name => "IPSet '$param->{rename}' does not exist" })
+			if !$fw_conf->{ipset}->{$param->{rename}};
 
-		my $data = delete $fw_conf->{ipset}->{$param->{rename}};
-		$fw_conf->{ipset}->{$param->{name}} = $data;
-		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};
-		}
+		    # prevent overwriting existing ipset
+		    raise_param_exc({ name => "IPSet '$param->{name}' does already exist"})
+			if $fw_conf->{ipset}->{$param->{name}} &&
+			$param->{name} ne $param->{rename};
 
-		$fw_conf->{ipset}->{$param->{name}} = [];
-		$fw_conf->{ipset_comments}->{$param->{name}} = $param->{comment} if defined($param->{comment});
-	    }
+		    my $data = delete $fw_conf->{ipset}->{$param->{rename}};
+		    $fw_conf->{ipset}->{$param->{name}} = $data;
+		    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}->{$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);
+	    });
 
 	    return undef;
 	}});
diff --git a/src/PVE/API2/Firewall/Rules.pm b/src/PVE/API2/Firewall/Rules.pm
index 1fde596..1e76e99 100644
--- a/src/PVE/API2/Firewall/Rules.pm
+++ b/src/PVE/API2/Firewall/Rules.pm
@@ -225,18 +225,22 @@ sub register_create_rule {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
+
+		my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
 
-	    my $rule = {};
+		my $rule = {};
 
-	    PVE::Firewall::copy_rule_data($rule, $param);
-	    PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
+		PVE::Firewall::copy_rule_data($rule, $param);
+		PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
 
-	    $rule->{enable} = 0 if !defined($param->{enable});
+		$rule->{enable} = 0 if !defined($param->{enable});
 
-	    unshift @$rules, $rule;
+		unshift @$rules, $rule;
 
-	    $class->save_rules($param, $fw_conf, $rules);
+		$class->save_rules($param, $fw_conf, $rules);
+	    });
 
 	    return undef;
 	}});
@@ -282,36 +286,40 @@ sub register_update_rule {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
+
+		my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
 
-	    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
+		die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
 
-	    my $rule = $rules->[$param->{pos}];
+		my $rule = $rules->[$param->{pos}];
 
-	    my $moveto = $param->{moveto};
-	    if (defined($moveto) && $moveto != $param->{pos}) {
-		my $newrules = [];
-		for (my $i = 0; $i < scalar(@$rules); $i++) {
-		    next if $i == $param->{pos};
-		    if ($i == $moveto) {
-			push @$newrules, $rule;
+		my $moveto = $param->{moveto};
+		if (defined($moveto) && $moveto != $param->{pos}) {
+		    my $newrules = [];
+		    for (my $i = 0; $i < scalar(@$rules); $i++) {
+			next if $i == $param->{pos};
+			if ($i == $moveto) {
+			    push @$newrules, $rule;
+			}
+			push @$newrules, $rules->[$i];
 		    }
-		    push @$newrules, $rules->[$i];
-		}
-		push @$newrules, $rule if $moveto >= scalar(@$rules);
-		$rules = $newrules;
-	    } else {
-		PVE::Firewall::copy_rule_data($rule, $param);
+		    push @$newrules, $rule if $moveto >= scalar(@$rules);
+		    $rules = $newrules;
+		} else {
+		    PVE::Firewall::copy_rule_data($rule, $param);
 
-		PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
+		    PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
 
-		PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
-	    }
+		    PVE::Firewall::verify_rule($rule, $cluster_conf, $fw_conf, $class->rule_env());
+		}
 
-	    $class->save_rules($param, $fw_conf, $rules);
+		$class->save_rules($param, $fw_conf, $rules);
+	    });
 
 	    return undef;
 	}});
@@ -344,16 +352,20 @@ sub register_delete_rule {
 	code => sub {
 	    my ($param) = @_;
 
-	    my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
+	    $class->lock_config($param, sub {
+		my ($param) = @_;
 
-	    my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
+		my ($cluster_conf, $fw_conf, $rules) = $class->load_config($param);
 
-	    die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
+		my (undef, $digest) = PVE::Firewall::copy_list_with_digest($rules);
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
 
-	    splice(@$rules, $param->{pos}, 1);
+		die "no rule at position $param->{pos}\n" if $param->{pos} >= scalar(@$rules);
 
-	    $class->save_rules($param, $fw_conf, $rules);
+		splice(@$rules, $param->{pos}, 1);
+
+		$class->save_rules($param, $fw_conf, $rules);
+	    });
 
 	    return undef;
 	}});
@@ -433,12 +445,16 @@ __PACKAGE__->register_method({
     code => sub {
 	my ($param) = @_;
 
-	my (undef, $cluster_conf, $rules) = __PACKAGE__->load_config($param);
+	__PACKAGE__->lock_config($param, sub {
+	    my ($param) = @_;
+
+	    my (undef, $cluster_conf, $rules) = __PACKAGE__->load_config($param);
 
-	die "Security group '$param->{group}' is not empty\n"
-	    if scalar(@$rules);
+	    die "Security group '$param->{group}' is not empty\n"
+		if scalar(@$rules);
 
-	__PACKAGE__->save_rules($param, $cluster_conf, undef);
+	    __PACKAGE__->save_rules($param, $cluster_conf, undef);
+	});
 
 	return undef;
     }});
diff --git a/src/PVE/API2/Firewall/VM.pm b/src/PVE/API2/Firewall/VM.pm
index 5b9b6dd..48b8c5f 100644
--- a/src/PVE/API2/Firewall/VM.pm
+++ b/src/PVE/API2/Firewall/VM.pm
@@ -121,31 +121,32 @@ sub register_handlers {
 	code => sub {
 	    my ($param) = @_;
 
-
-	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
-	    my $vmfw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid});
-
-	    my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
-	    PVE::Tools::assert_if_modified($digest, $param->{digest});
-
-	    if ($param->{delete}) {
-		foreach my $opt (PVE::Tools::split_list($param->{delete})) {
-		    raise_param_exc({ delete => "no such option '$opt'" })
-			if !$option_properties->{$opt};
-		    delete $vmfw_conf->{options}->{$opt};
+	    PVE::Firewall::lock_vmfw_conf($param->{vmid}, 10, sub {
+		my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+		my $vmfw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid});
+
+		my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
+		PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+		if ($param->{delete}) {
+		    foreach my $opt (PVE::Tools::split_list($param->{delete})) {
+			raise_param_exc({ delete => "no such option '$opt'" })
+			    if !$option_properties->{$opt};
+			delete $vmfw_conf->{options}->{$opt};
+		    }
 		}
-	    }
 
-	    if (defined($param->{enable})) {
-		$param->{enable} = $param->{enable} ? 1 : 0;
-	    }
+		if (defined($param->{enable})) {
+		    $param->{enable} = $param->{enable} ? 1 : 0;
+		}
 
-	    foreach my $k (keys %$option_properties) {
-		next if !defined($param->{$k});
-		$vmfw_conf->{options}->{$k} = $param->{$k};
-	    }
+		foreach my $k (keys %$option_properties) {
+		    next if !defined($param->{$k});
+		    $vmfw_conf->{options}->{$k} = $param->{$k};
+		}
 
-	    PVE::Firewall::save_vmfw_conf($param->{vmid}, $vmfw_conf);
+		PVE::Firewall::save_vmfw_conf($param->{vmid}, $vmfw_conf);
+	    });
 
 	    return undef;
 	}});
-- 
2.20.1





More information about the pve-devel mailing list