Skip to content

Commit b190cb3

Browse files
JanaHochThulium-DrakeIamLunchbox
authored
new_module proxmox_firewall (#183)
* proxmox_firewall: new_module for firewall config - Added method to get FW rules at cluster, node, vm, vnet levels * proxmox_firewall: Add condition for security group * proxmox_firewall: Add method to create rule at cluster level * proxmox_firewall: Refactor to remove similar functions * proxmox_firewall: Add method to update fw rules * proxmox_firewall: Added method to delete rule * proxmox_firewall: create/delete group * proxmox_firewall: Added param validations * proxmox_firewall: Added Doc - Fix Sanity issues * proxmox_firewall: Add force condition - state=present: + check if fw rules already exists and if needed update them instead of creating + check if group exists and if so don't do anything - state=update: + check if fw rules don't existsand if needed create them instead of updating - make rules.pos as required this is to handle above conditions - add method to get security groups and list them with firewall rules when state is not provided - add proxmox_firewall in meta/runtime.yml * proxmox_firewall: Add unit tests * proxmox_firewall: simplify conditions * 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 * proxmox_firewall & proxmox module_utils - Move check_rules() to proxmox module_utils and rename to compare_list_of_dicts() - Generalize the implemnetation as this is usefull in multiple places. - e.g. filtering out which fw rules, aliases, etc needs to be created/updated * proxmox_firewall: Add methods to create/update/delete aliases * proxmox_firewall: remove unnecsary getattr * proxmox_firewall: Split into seprate proxmox_firewall_info - Also add get methods in module_utils. * proxmox_firewall: Merge state present and update * proxmox_firewall & proxmox_firewall_info - Added tests and fixed sanity issues * module_utils/proxmox: updated compare_list_of_dict() * Apply suggestions from code review Added suggestions from @IamLunchbox Co-authored-by: IamLunchbox <[email protected]> * proxmox_firewall: Fix minor bugs and sanity issues - When state is absent and pos is 0 if condition with pos was failing. to fix it explicitly check if pos is not None. --------- Co-authored-by: Jeffrey van Pelt <[email protected]> Co-authored-by: IamLunchbox <[email protected]>
1 parent 0024ab2 commit b190cb3

File tree

7 files changed

+1432
-0
lines changed

7 files changed

+1432
-0
lines changed

meta/runtime.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ action_groups:
1919
- proxmox_cluster_join_info
2020
- proxmox_disk
2121
- proxmox_domain_info
22+
- proxmox_firewall
23+
- proxmox_firewall_info
2224
- proxmox_group
2325
- proxmox_group_info
2426
- proxmox_ipam_info

plugins/module_utils/proxmox.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,53 @@ def ansible_to_proxmox_bool(value):
7373
return 1 if value else 0
7474

7575

76+
def compare_list_of_dicts(existing_list, new_list, uid, params_to_ignore=None):
77+
""" Compare 2 list of dicts
78+
Use case - for firewall rules we will be getting a list of rules from user.
79+
We want to filter out which rules needs to be updated and which rules are completely new and needs to be created
80+
81+
:param existing_list: Existing values example - list of existing rules
82+
:param new_list: New values example - list of rules passed to module
83+
:param uid: unique identifier in dict. It should always be present in both lists - in case of firewall rules it's pos
84+
:param params_to_ignore: list of params we want to ignore which are present in existing_list's dict.
85+
In case of firewall rules we want to ignore ['digest', 'ipversion']
86+
87+
:return: returns 2 list items 1st is the list of items which are completely new and needs to be created
88+
2nd is a list of items which needs to be updated
89+
"""
90+
if params_to_ignore is None:
91+
params_to_ignore = list()
92+
items_to_update = []
93+
new_list = [{k: v for k, v in item.items() if v is not None and k not in params_to_ignore} for item in new_list]
94+
95+
if existing_list is None:
96+
items_to_create = new_list
97+
items_to_update = list()
98+
return items_to_create, items_to_update
99+
100+
existing_list = {x[uid]: x for x in existing_list}
101+
new_list = {x[uid]: x for x in new_list}
102+
103+
common_uids = set(existing_list.keys()).intersection(set(new_list.keys()))
104+
missing_uids = set(new_list.keys()) - set(existing_list.keys())
105+
items_to_create = [new_list[uid] for uid in missing_uids]
106+
107+
for uid in common_uids:
108+
# If new rule has a parameter that is not present in existing rule we need to update
109+
if set(new_list[uid].keys()) - set(existing_list[uid].keys()) != set():
110+
items_to_update.append(new_list[uid])
111+
continue
112+
113+
# If existing rule param value doesn't match new rule param OR
114+
# If existing rule has a param that is not present in new rule except for params in params_to_ignore
115+
for existing_rule_param, existing_parm_value in existing_list[uid].items():
116+
if (existing_rule_param not in params_to_ignore and
117+
new_list[uid].get(existing_rule_param) != existing_parm_value):
118+
items_to_update.append(new_list[uid])
119+
120+
return items_to_create, items_to_update
121+
122+
76123
class ProxmoxAnsible(object):
77124
"""Base class for Proxmox modules"""
78125
TASK_TIMED_OUT = 'timeout expired'

plugins/module_utils/proxmox_sdn.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,47 @@ def get_zones(self, zone_type: str = None) -> List[Dict]:
9999
self.module.fail_json(
100100
msg=f'Failed to retrieve zone information from cluster: {e}'
101101
)
102+
103+
def get_aliases(self, firewall_obj):
104+
"""Get aliases for IP/CIDR at given firewall endpoint level
105+
106+
:param firewall_obj: Firewall endpoint as a ProxmoxResource e.g. self.proxmox_api.cluster().firewall
107+
If it is None it'll return an empty list
108+
:return: List of aliases and corresponding IP/CIDR
109+
"""
110+
if firewall_obj is None:
111+
return list()
112+
try:
113+
return firewall_obj().aliases().get()
114+
except Exception as e:
115+
self.module.fail_json(
116+
msg=f'Failed to retrieve aliases - {e}'
117+
)
118+
119+
def get_fw_rules(self, rules_obj, pos=None):
120+
"""Get firewall rules at given rules endpoint level
121+
122+
:param rules_obj: Firewall Rules endpoint as a ProxmoxResource e.g. self.proxmox_api.cluster().firewall().rules
123+
:param pos: Rule position if it is None it'll return all rules
124+
:return: Firewall rules as a list of dict
125+
"""
126+
if pos is not None:
127+
pos = str(pos)
128+
try:
129+
return rules_obj(pos).get()
130+
except Exception as e:
131+
self.module.fail_json(
132+
msg=f'Failed to retrieve firewall rules: {e}'
133+
)
134+
135+
def get_groups(self):
136+
"""Get firewall security groups
137+
138+
:return: list of groups
139+
"""
140+
try:
141+
return [x['group'] for x in self.proxmox_api.cluster().firewall().groups().get()]
142+
except Exception as e:
143+
self.module.fail_json(
144+
msg=f'Failed to retrieve firewall security groups: {e}'
145+
)

0 commit comments

Comments
 (0)