+ return $cluster_conf;
+}
+
+my $rules_to_conf = sub {
+ my ($rules, $need_iface) = @_;
+
+ my $raw = '';
+
+ foreach my $rule (@$rules) {
+ if ($rule->{type} eq 'in' || $rule->{type} eq 'out') {
+ $raw .= '|' if defined($rule->{enable}) && !$rule->{enable};
+ $raw .= uc($rule->{type});
+ $raw .= " " . $rule->{action};
+ $raw .= " " . ($rule->{iface} || '-') if $need_iface;
+ $raw .= " " . ($rule->{source} || '-');
+ $raw .= " " . ($rule->{dest} || '-');
+ $raw .= " " . ($rule->{proto} || '-');
+ $raw .= " " . ($rule->{dport} || '-');
+ $raw .= " " . ($rule->{sport} || '-');
+ $raw .= " # " . encode('utf8', $rule->{comment})
+ if $rule->{comment} && $rule->{comment} !~ m/^\s*$/;
+ $raw .= "\n";
+ } else {
+ die "implement me '$rule->{type}'";
+ }
+ }
+
+ return $raw;
+};
+
+sub save_clusterfw_conf {
+ my ($cluster_conf) = @_;
+
+ my $raw = '';
+
+ my $options = $cluster_conf->{options};
+ if (scalar(keys %$options)) {
+ $raw .= "[OPTIONS]\n\n";
+ foreach my $opt (keys %$options) {
+ $raw .= "$opt: $options->{$opt}\n";
+ }
+ $raw .= "\n";
+ }
+
+ # fixme: save ipset
+
+ my $rules = $cluster_conf->{rules};
+ if (scalar(@$rules)) {
+ $raw .= "[RULES]\n\n";
+ $raw .= &$rules_to_conf($rules, 1);
+ $raw .= "\n";
+ }
+
+ foreach my $group (sort keys %{$cluster_conf->{groups}}) {
+ my $rules = $cluster_conf->{groups}->{$group};
+ $raw .= "[group $group]\n\n";
+ $raw .= &$rules_to_conf($rules, 0);
+ $raw .= "\n";
+ }
+
+ PVE::Tools::file_set_contents($clusterfw_conf_filename, $raw);