Skip to content

Commit a7cb2df

Browse files
committed
proxmox_firewall: Improve checks
- Earlier it was only checking if rule at pos already exists - If it did it would update it given force was true. - But it means if we ran same pipeline twice without force it would fail - To fix it Checking the entier rule
1 parent 4b14774 commit a7cb2df

File tree

1 file changed

+65
-32
lines changed

1 file changed

+65
-32
lines changed

plugins/modules/proxmox_firewall.py

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,11 @@ def run(self):
491491
group = self.params.get("group")
492492
group_conf = self.params.get("group_conf")
493493

494+
for rule in rules:
495+
rule['icmp-type'] = rule.get('icmp_type')
496+
rule['enable'] = ansible_to_proxmox_bool(rule.get('enable'))
497+
del rule['icmp_type']
498+
494499
if level == "vm":
495500
vm = self.get_vm(vmid=self.params.get('vmid'))
496501
node = getattr(self.proxmox_api.nodes(), vm['node'])
@@ -611,24 +616,56 @@ def delete_fw_rule(self, rules_obj, pos):
611616
msg=f'Failed to delete firewall rule at pos {pos}: {e}'
612617
)
613618

619+
def check_rules(self, existing_rules, new_rules):
620+
rules_to_update = []
621+
new_rules = [{k: v for k, v in item.items() if v is not None} for item in new_rules]
622+
623+
if existing_rules is None:
624+
rules_to_create = new_rules
625+
rules_to_update = list()
626+
return rules_to_create, rules_to_update
627+
628+
existing_rules = {x['pos']: x for x in existing_rules}
629+
new_rules = {x['pos']: x for x in new_rules}
630+
631+
common_pos = set(existing_rules.keys()).intersection(set(new_rules.keys()))
632+
pos_to_create = set(new_rules.keys()) - set(existing_rules.keys())
633+
rules_to_create = [new_rules[pos] for pos in pos_to_create]
634+
635+
params_to_ignore = ['digest', 'ipversion']
636+
637+
for pos in common_pos:
638+
# If new rule has a parameter that is not present in existing rule we need to update
639+
if set(new_rules[pos].keys()) - set(existing_rules[pos].keys()) != set():
640+
rules_to_update.append(new_rules[pos])
641+
continue
642+
643+
# If existing rule param value doesn't match new rule param OR
644+
# If existing rule has a param that is not present in new rule except for params in params_to_ignore
645+
for existing_rule_param, existing_parm_value in existing_rules[pos].items():
646+
if (existing_rule_param not in params_to_ignore and
647+
new_rules[pos].get(existing_rule_param) != existing_parm_value):
648+
rules_to_update.append(new_rules[pos])
649+
650+
return rules_to_create, rules_to_update
651+
614652
def update_fw_rules(self, rules_obj, rules, force):
615653
existing_rules = self.get_fw_rules(rules_obj)
616-
rules_to_create = []
617-
if len(existing_rules) > 0:
618-
existing_pos = [x['pos'] for x in existing_rules]
619-
for rule in rules:
620-
if rule.get('pos') not in existing_pos:
621-
if force:
622-
rules_to_create.append(rule)
623-
else:
624-
self.module.fail_json(
625-
msg=f"Rule doesn't exists at pos - {rule.get('pos')} and force is false."
626-
)
627-
rules_to_update = [rule for rule in rules if rule not in rules_to_create]
654+
rules_to_create, rules_to_update = self.check_rules(existing_rules=existing_rules, new_rules=rules)
655+
656+
if len(rules_to_update) == 0:
657+
if len(rules_to_create) == 0:
658+
self.module.exit_json(
659+
changed=False,
660+
msg='No need to update any FW rules.'
661+
662+
)
663+
elif len(rules_to_create) > 0 and not force:
664+
self.module.fail_json(
665+
msg=f"Need to create new rules for pos - {[x['pos'] for x in rules_to_create]} But force is false"
666+
)
667+
628668
for rule in rules_to_update:
629-
rule['icmp-type'] = rule.get('icmp_type')
630-
rule['enable'] = ansible_to_proxmox_bool(rule.get('enable'))
631-
del rule['icmp_type']
632669
try:
633670
rule_obj = getattr(rules_obj(), str(rule['pos']))
634671
rule['digest'] = rule_obj.get().get('digest') # Avoids concurrent changes
@@ -646,24 +683,20 @@ def update_fw_rules(self, rules_obj, rules, force):
646683
)
647684

648685
def create_fw_rules(self, rules_obj, rules, force):
649-
existing_rules = self.get_fw_rules(rules_obj)
650-
rules_to_update = []
651-
if len(existing_rules) > 0:
652-
existing_pos = [x['pos'] for x in existing_rules]
653-
for rule in rules:
654-
if rule.get('pos') in existing_pos:
655-
if force:
656-
rules_to_update.append(rule)
657-
else:
658-
self.module.fail_json(
659-
msg=f"Rule already exists at pos - {rule.get('pos')} and force is false."
660-
)
661-
rules_to_create = [rule for rule in rules if rule not in rules_to_update]
686+
existing_rules = self.get_fw_rules(rules_obj=rules_obj)
687+
rules_to_create, rules_to_update = self.check_rules(existing_rules=existing_rules, new_rules=rules)
688+
689+
if len(rules_to_create) == 0 and len(rules_to_update) == 0:
690+
self.module.exit_json(
691+
changed=False,
692+
msg='No need to create/update any rule'
693+
)
694+
elif len(rules_to_update) > 0 and not force:
695+
self.module.fail_json(
696+
msg=f"Need to update rules at pos - {[x['pos'] for x in rules_to_update]} but force is false"
697+
)
662698

663699
for rule in rules_to_create:
664-
rule['icmp-type'] = rule.get('icmp_type')
665-
rule['enable'] = ansible_to_proxmox_bool(rule.get('enable'))
666-
del rule['icmp_type']
667700
try:
668701
rules_obj().post(**rule)
669702
self.move_rule_to_correct_pos(rules_obj, rule)
@@ -672,7 +705,7 @@ def create_fw_rules(self, rules_obj, rules, force):
672705
self.module.fail_json(
673706
msg=f'Failed to create firewall rule {rule}: {e}'
674707
)
675-
if len(rules_to_update) > 0:
708+
if len(rules_to_update) > 0 and force:
676709
self.update_fw_rules(rules_obj=rules_obj, rules=rules_to_update, force=False)
677710
self.module.exit_json(
678711
changed=True, msg='successfully created firewall rules'

0 commit comments

Comments
 (0)