@@ -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