Skip to content

Commit 11a3a19

Browse files
committed
proxmox_firewall: Add methods to create/update/delete aliases
1 parent 1adb2ca commit 11a3a19

File tree

1 file changed

+223
-4
lines changed

1 file changed

+223
-4
lines changed

plugins/modules/proxmox_firewall.py

Lines changed: 223 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,27 @@
8484
- Comment for security group.
8585
- Only needed when creating group.
8686
type: str
87+
aliases:
88+
description:
89+
- List of aliases
90+
- Alias can only be created/updated/deleted at cluster or VM level
91+
type: list
92+
elements: dict
93+
suboptions:
94+
name:
95+
description: Alias name
96+
type: str
97+
required: true
98+
cidr:
99+
description:
100+
- CIDR for alias
101+
- only needed when O(state=present) or O(state=update)
102+
type: str
103+
required: false
104+
comment:
105+
description: Comment for Alias
106+
type: str
107+
required: false
87108
rules:
88109
description:
89110
- List of individual rules to be applied.
@@ -273,6 +294,46 @@
273294
group_conf: True
274295
state: absent
275296
group: test
297+
298+
- name: Create FW aliases
299+
community.proxmox.proxmox_firewall:
300+
api_user: "{{ pc.proxmox.api_user }}"
301+
api_token_id: "{{ pc.proxmox.api_token_id }}"
302+
api_token_secret: "{{ vault.proxmox.api_token_secret }}"
303+
api_host: "{{ pc.proxmox.api_host }}"
304+
validate_certs: no
305+
state: present
306+
aliases:
307+
- name: test1
308+
cidr: '10.10.1.0/24'
309+
- name: test2
310+
cidr: '10.10.2.0/24'
311+
312+
- name: Update FW aliases
313+
community.proxmox.proxmox_firewall:
314+
api_user: "{{ pc.proxmox.api_user }}"
315+
api_token_id: "{{ pc.proxmox.api_token_id }}"
316+
api_token_secret: "{{ vault.proxmox.api_token_secret }}"
317+
api_host: "{{ pc.proxmox.api_host }}"
318+
validate_certs: no
319+
state: update
320+
aliases:
321+
- name: test1
322+
cidr: '10.10.1.0/28'
323+
- name: test2
324+
cidr: '10.10.2.0/28'
325+
326+
- name: Delete FW aliases
327+
community.proxmox.proxmox_firewall:
328+
api_user: "{{ pc.proxmox.api_user }}"
329+
api_token_id: "{{ pc.proxmox.api_token_id }}"
330+
api_token_secret: "{{ vault.proxmox.api_token_secret }}"
331+
api_host: "{{ pc.proxmox.api_host }}"
332+
validate_certs: no
333+
state: absent
334+
aliases:
335+
- name: test1
336+
- name: test2
276337
"""
277338

278339
RETURN = r"""
@@ -291,6 +352,35 @@
291352
sample:
292353
[ "test" ]
293354
355+
aliases:
356+
description:
357+
- list of alias present at given level
358+
- aliases are only available for cluster and VM level so if any other level it'll be empty list
359+
returned: on success
360+
type: list
361+
elements: dict
362+
sample:
363+
[
364+
{
365+
"cidr": "10.10.1.0/24",
366+
"digest": "978391f460484e8d4fb3ca785cfe5a9d16fe8b1f",
367+
"ipversion": 4,
368+
"name": "test1"
369+
},
370+
{
371+
"cidr": "10.10.2.0/24",
372+
"digest": "978391f460484e8d4fb3ca785cfe5a9d16fe8b1f",
373+
"ipversion": 4,
374+
"name": "test2"
375+
},
376+
{
377+
"cidr": "10.10.3.0/24",
378+
"digest": "978391f460484e8d4fb3ca785cfe5a9d16fe8b1f",
379+
"ipversion": 4,
380+
"name": "test3"
381+
}
382+
]
383+
294384
firewall_rules:
295385
description: List of firewall rules.
296386
returned: on success
@@ -416,6 +506,16 @@ def get_proxmox_args():
416506
group_conf=dict(type="bool", default=False),
417507
group=dict(type="str", required=False),
418508
comment=dict(type="str", required=False),
509+
aliases=dict(
510+
type="list",
511+
elements="dict",
512+
required=False,
513+
options=dict(
514+
name=dict(type="str", required=True),
515+
cidr=dict(type="str", required=False),
516+
comment=dict(type="str", required=False)
517+
)
518+
),
419519
rules=dict(
420520
type="list",
421521
elements="dict",
@@ -455,6 +555,9 @@ def get_ansible_module():
455555
('level', 'node', ['node']),
456556
('level', 'vnet', ['vnet']),
457557
('level', 'group', ['group']),
558+
],
559+
mutually_exclusive=[
560+
('aliases', 'rules'),
458561
]
459562
)
460563

@@ -466,18 +569,18 @@ def __init__(self, module):
466569

467570
def validate_params(self):
468571
if self.params.get('state') in ['present', 'update']:
469-
if self.params.get('group_conf') != bool(self.params.get('rules')):
572+
if self.params.get('group_conf') != bool(self.params.get('rules') or self.params.get('aliases')):
470573
return True
471574
else:
472575
self.module.fail_json(
473-
msg="When state is present either group_conf should be true or rules must be present but not both"
576+
msg="When state is present either group_conf should be true or rules/aliases must be present but not both"
474577
)
475578
elif self.params.get('state') == 'absent':
476-
if self.params.get('group_conf') != bool(self.params.get('pos')):
579+
if self.params.get('group_conf') != bool(self.params.get('pos') or self.params.get('aliases')):
477580
return True
478581
else:
479582
self.module.fail_json(
480-
msg="When State is absent either group_conf should be true or pos must be present but not both"
583+
msg="When State is absent either group_conf should be true or pos/aliases must be present but not both"
481584
)
482585
else:
483586
return True
@@ -488,6 +591,7 @@ def run(self):
488591
state = self.params.get("state")
489592
force = self.params.get("force")
490593
level = self.params.get("level")
594+
aliases = self.params.get("aliases")
491595
rules = self.params.get("rules")
492596
group = self.params.get("group")
493597
group_conf = self.params.get("group_conf")
@@ -517,6 +621,7 @@ def run(self):
517621
rules_obj = firewall_obj().rules
518622

519623
elif level == "group":
624+
firewall_obj = None
520625
rules_obj = getattr(self.proxmox_api.cluster().firewall().groups(), group)
521626

522627
else:
@@ -528,26 +633,140 @@ def run(self):
528633
self.create_group(group=group, comment=self.params.get('comment'))
529634
if rules is not None:
530635
self.create_fw_rules(rules_obj=rules_obj, rules=rules, force=force)
636+
if aliases is not None:
637+
self.create_aliases(firewall_obj=firewall_obj, level=level, aliases=aliases, force=force)
531638
elif state == "update":
532639
if group_conf:
533640
self.create_group(group=group, comment=self.params.get('comment'))
534641
if rules is not None:
535642
self.update_fw_rules(rules_obj=rules_obj, rules=rules, force=force)
643+
if aliases is not None:
644+
self.update_aliases(firewall_obj=firewall_obj, level=level, aliases=aliases, force=force)
536645
elif state == "absent":
537646
if self.params.get('pos'):
538647
self.delete_fw_rule(rules_obj=rules_obj, pos=self.params.get('pos'))
539648
if group_conf:
540649
self.delete_group(group_name=group)
650+
if aliases is not None:
651+
self.delete_aliases(firewall_obj=firewall_obj, level=level, aliases=aliases)
541652
else:
542653
rules = self.get_fw_rules(rules_obj, pos=self.params.get('pos'))
543654
groups = self.get_groups()
655+
aliases = self.get_aliases(firewall_obj=firewall_obj, level=level)
544656
self.module.exit_json(
545657
changed=False,
546658
firewall_rules=rules,
547659
groups=groups,
660+
aliases=aliases,
548661
msg='successfully retrieved firewall rules and groups'
549662
)
550663

664+
def get_aliases(self, firewall_obj, level):
665+
if firewall_obj is None or level not in ['cluster', 'vm']:
666+
return list()
667+
try:
668+
return firewall_obj().aliases().get()
669+
except Exception as e:
670+
self.module.fail_json(
671+
msg='Failed to retrieve aliases'
672+
)
673+
674+
def create_aliases(self, firewall_obj, level, aliases, force=False):
675+
if firewall_obj is None or level not in ['cluster', 'vm']:
676+
self.module.fail_json(
677+
msg='Aliases can only be created at cluster or VM level'
678+
)
679+
680+
aliases_to_create, aliases_to_update = compare_list_of_dicts(
681+
existing_list=self.get_aliases(firewall_obj=firewall_obj, level=level),
682+
new_list=aliases,
683+
uid='name',
684+
params_to_ignore=['digest', 'ipversion']
685+
)
686+
687+
if len(aliases_to_create) == 0 and len(aliases_to_update) == 0:
688+
self.module.exit_json(
689+
changed=False,
690+
msg='No need to create/update any aliases'
691+
)
692+
elif len(aliases_to_update) > 0 and not force:
693+
self.module.fail_json(
694+
msg=f"Need to update aliases - {[x['name'] for x in aliases_to_update]} but force is false"
695+
)
696+
697+
for alias in aliases_to_create:
698+
try:
699+
firewall_obj().aliases().post(**alias)
700+
except Exception as e:
701+
self.module.fail_json(
702+
msg=f"Failed to create Alias {alias['name']} - {e}"
703+
)
704+
if len(aliases_to_update) > 0 and force:
705+
self.update_aliases(firewall_obj=firewall_obj, level=level, aliases=aliases_to_update, force=False)
706+
else:
707+
self.module.exit_json(
708+
changed=True,
709+
msg="Aliases created"
710+
)
711+
712+
def update_aliases(self, firewall_obj, level, aliases, force=False):
713+
aliases_to_create, aliases_to_update = compare_list_of_dicts(
714+
existing_list=self.get_aliases(firewall_obj=firewall_obj, level=level),
715+
new_list=aliases,
716+
uid='name',
717+
params_to_ignore=['digest', 'ipversion']
718+
)
719+
720+
if len(aliases_to_update) == 0 and len(aliases_to_create) == 0:
721+
self.module.exit_json(
722+
changed=False,
723+
msg='No need to create/update any alias.'
724+
725+
)
726+
elif len(aliases_to_create) > 0 and not force:
727+
self.module.fail_json(
728+
msg=f"Need to create new alias - {[x['name'] for x in aliases_to_create]} But force is false"
729+
)
730+
731+
for alias in aliases_to_update:
732+
try:
733+
alias_obj = getattr(firewall_obj().aliases(), alias['name'])
734+
alias_obj().put(**alias)
735+
except Exception as e:
736+
self.module.fail_json(
737+
msg=f"Failed to update Alias {alias['name']} - {e}"
738+
)
739+
if len(aliases_to_update) > 0 and force:
740+
self.update_aliases(firewall_obj=firewall_obj, level=level, aliases=aliases_to_update, force=False)
741+
else:
742+
self.module.exit_json(
743+
changed=True,
744+
msg="Aliases updated"
745+
)
746+
747+
def delete_aliases(self, firewall_obj, level, aliases):
748+
existing_aliases = set([x.get('name') for x in self.get_aliases(firewall_obj=firewall_obj, level=level)])
749+
aliases = set([x.get('name') for x in aliases])
750+
aliases_to_delete = list(existing_aliases.intersection(aliases))
751+
752+
if len(aliases_to_delete) == 0:
753+
self.module.exit_json(
754+
changed=False,
755+
msg="No need to delete any alias"
756+
)
757+
for alias_name in aliases_to_delete:
758+
try:
759+
alias_obj = getattr(firewall_obj().aliases(), alias_name)
760+
alias_obj().delete()
761+
except Exception as e:
762+
self.module.fail_json(
763+
msg=f"Failed to delete alias {alias_name} - {e}"
764+
)
765+
self.module.exit_json(
766+
changed=True,
767+
msg="Successfully deleted aliases"
768+
)
769+
551770
def create_group(self, group, comment=None):
552771
if group in self.get_groups():
553772
self.module.exit_json(

0 commit comments

Comments
 (0)