32
32
- present
33
33
- update
34
34
- absent
35
+ force:
36
+ description:
37
+ - If state is present and if 1 or more rule already exists at given pos force will update them
38
+ - If state is update and if 1 or more rule doesn't exist force will create
39
+ type: bool
40
+ default: false
35
41
level:
36
42
description:
37
43
- Level at which the firewall rule applies.
157
163
description:
158
164
- Position of the rule in the list.
159
165
type: int
166
+ required: true
160
167
proto:
161
168
description:
162
169
- IP protocol. You can use protocol names ('tcp'/'udp') or simple numbers, as defined in '/etc/protocols'.
276
283
sample:
277
284
test
278
285
286
+ groups:
287
+ description: list of firewall security groups
288
+ returned: on success
289
+ type: list
290
+ elements: str
291
+ sample:
292
+ [ "test" ]
293
+
279
294
firewall_rules:
280
295
description: List of firewall rules.
281
296
returned: on success
391
406
def get_proxmox_args ():
392
407
return dict (
393
408
state = dict (type = "str" , choices = ["present" , "absent" , "update" ], required = False ),
409
+ force = dict (type = "bool" , default = False ),
394
410
level = dict (type = "str" , choices = ["cluster" , "node" , "vm" , "vnet" , "group" ], default = "cluster" , required = False ),
395
411
node = dict (type = "str" , required = False ),
396
412
vmid = dict (type = "int" , required = False ),
@@ -417,7 +433,7 @@ def get_proxmox_args():
417
433
choices = ["emerg" , "alert" , "crit" , "err" , "warning" , "notice" , "info" , "debug" , "nolog" ],
418
434
required = False ),
419
435
macro = dict (type = "str" , required = False ),
420
- pos = dict (type = "int" , required = False ),
436
+ pos = dict (type = "int" , required = True ),
421
437
proto = dict (type = "str" , required = False ),
422
438
source = dict (type = "str" , required = False ),
423
439
sport = dict (type = "str" , required = False )
@@ -464,6 +480,7 @@ def run(self):
464
480
)
465
481
466
482
state = self .params .get ("state" )
483
+ force = self .params .get ("force" )
467
484
level = self .params .get ("level" )
468
485
rules = self .params .get ("rules" )
469
486
@@ -496,24 +513,32 @@ def run(self):
496
513
if self .params .get ('group_conf' ):
497
514
self .create_group (group = self .params .get ('group' ), comment = self .params .get ('comment' ))
498
515
if rules is not None :
499
- self .create_fw_rules (rules_obj = rules_obj , rules = rules )
516
+ self .create_fw_rules (rules_obj = rules_obj , rules = rules , force = force )
500
517
elif state == "update" :
501
518
if self .params .get ('group_conf' ):
502
519
self .create_group (group = self .params .get ('group' ), comment = self .params .get ('comment' ))
503
520
if rules is not None :
504
- self .update_fw_rules (rules_obj = rules_obj , rules = rules )
521
+ self .update_fw_rules (rules_obj = rules_obj , rules = rules , force = force )
505
522
elif state == "absent" :
506
523
if self .params .get ('pos' ):
507
524
self .delete_fw_rule (rules_obj = rules_obj , pos = self .params .get ('pos' ))
508
525
if self .params .get ('group_conf' ):
509
526
self .delete_group (group_name = self .params .get ('group' ))
510
527
else :
511
528
rules = self .get_fw_rules (rules_obj , pos = self .params .get ('pos' ))
529
+ groups = self .get_groups ()
512
530
self .module .exit_json (
513
- changed = False , firewall_rules = rules , msg = 'successfully retrieved firewall rules'
531
+ changed = False ,
532
+ firewall_rules = rules ,
533
+ groups = groups ,
534
+ msg = 'successfully retrieved firewall rules and groups'
514
535
)
515
536
516
537
def create_group (self , group , comment = None ):
538
+ if group in self .get_groups ():
539
+ self .module .exit_json (
540
+ changed = False , group = group , msg = f"security group { group } already exists"
541
+ )
517
542
try :
518
543
self .proxmox_api .cluster ().firewall ().groups .post (group = group , comment = comment )
519
544
self .module .exit_json (
@@ -525,6 +550,10 @@ def create_group(self, group, comment=None):
525
550
)
526
551
527
552
def delete_group (self , group_name ):
553
+ if group_name not in self .get_groups ():
554
+ self .module .exit_json (
555
+ changed = False , group = group_name , msg = f"security group { group_name } already doesn't exists"
556
+ )
528
557
try :
529
558
group = getattr (self .proxmox_api .cluster ().firewall ().groups (), group_name )
530
559
group .delete ()
@@ -546,8 +575,23 @@ def get_fw_rules(self, rules_obj, pos=None):
546
575
msg = f'Failed to retrieve firewall rules: { e } '
547
576
)
548
577
578
+ def get_groups (self ):
579
+ try :
580
+ return [x ['group' ] for x in self .proxmox_api .cluster ().firewall ().groups ().get ()]
581
+ except Exception as e :
582
+ self .module .fail_json (
583
+ msg = f'Failed to retrieve firewall security groups: { e } '
584
+ )
585
+
549
586
def delete_fw_rule (self , rules_obj , pos ):
550
587
try :
588
+ for item in self .get_fw_rules (rules_obj ):
589
+ if item .get ('pos' ) == pos :
590
+ break
591
+ else :
592
+ self .module .exit_json (
593
+ changed = False , msg = "Firewall rule already doesn't exist"
594
+ )
551
595
rule_obj = getattr (rules_obj (), str (pos ))
552
596
digest = rule_obj .get ().get ('digest' )
553
597
rule_obj .delete (pos = pos , digest = digest )
@@ -560,8 +604,21 @@ def delete_fw_rule(self, rules_obj, pos):
560
604
msg = f'Failed to delete firewall rule at pos { pos } : { e } '
561
605
)
562
606
563
- def update_fw_rules (self , rules_obj , rules ):
564
- for rule in rules :
607
+ def update_fw_rules (self , rules_obj , rules , force ):
608
+ existing_rules = self .get_fw_rules (rules_obj )
609
+ rules_to_create = []
610
+ if len (existing_rules ) > 0 :
611
+ existing_pos = [x ['pos' ] for x in existing_rules ]
612
+ for rule in rules :
613
+ if rule .get ('pos' ) not in existing_pos :
614
+ if force :
615
+ rules_to_create .append (rule )
616
+ else :
617
+ self .module .fail_json (
618
+ msg = f"Rule doesn't exists at pos - { rule .get ('pos' )} and force is false."
619
+ )
620
+ rules_to_update = [rule for rule in rules if rule not in rules_to_create ]
621
+ for rule in rules_to_update :
565
622
rule ['icmp-type' ] = rule .get ('icmp_type' )
566
623
rule ['enable' ] = ansible_to_proxmox_bool (rule .get ('enable' ))
567
624
del rule ['icmp_type' ]
@@ -574,12 +631,29 @@ def update_fw_rules(self, rules_obj, rules):
574
631
self .module .fail_json (
575
632
msg = f'Failed to update firewall rule at pos { rule ["pos" ]} : { e } '
576
633
)
634
+
635
+ if len (rules_to_create ) > 0 :
636
+ self .create_fw_rules (rules_obj = rules_obj , rules = rules_to_create , force = False )
577
637
self .module .exit_json (
578
- changed = True , msg = 'successfully created firewall rules'
638
+ changed = True , msg = 'successfully updated firewall rules'
579
639
)
580
640
581
- def create_fw_rules (self , rules_obj , rules ):
582
- for rule in rules :
641
+ def create_fw_rules (self , rules_obj , rules , force ):
642
+ existing_rules = self .get_fw_rules (rules_obj )
643
+ rules_to_update = []
644
+ if len (existing_rules ) > 0 :
645
+ existing_pos = [x ['pos' ] for x in existing_rules ]
646
+ for rule in rules :
647
+ if rule .get ('pos' ) in existing_pos :
648
+ if force :
649
+ rules_to_update .append (rule )
650
+ else :
651
+ self .module .fail_json (
652
+ msg = f"Rule already exists at pos - { rule .get ('pos' )} and force is false."
653
+ )
654
+ rules_to_create = [rule for rule in rules if rule not in rules_to_update ]
655
+
656
+ for rule in rules_to_create :
583
657
rule ['icmp-type' ] = rule .get ('icmp_type' )
584
658
rule ['enable' ] = ansible_to_proxmox_bool (rule .get ('enable' ))
585
659
del rule ['icmp_type' ]
@@ -591,6 +665,8 @@ def create_fw_rules(self, rules_obj, rules):
591
665
self .module .fail_json (
592
666
msg = f'Failed to create firewall rule { rule } : { e } '
593
667
)
668
+ if len (rules_to_update ) > 0 :
669
+ self .update_fw_rules (rules_obj = rules_obj , rules = rules_to_update , force = False )
594
670
self .module .exit_json (
595
671
changed = True , msg = 'successfully created firewall rules'
596
672
)
0 commit comments