Optimizing PFW management workflows
This commit is contained in:
@@ -32,7 +32,7 @@ requirements:
|
|||||||
- PyJWT module
|
- PyJWT module
|
||||||
- requests module
|
- requests module
|
||||||
- decort_utils utility library (module)
|
- decort_utils utility library (module)
|
||||||
- DECORT cloud platform version 3.4.0 or higher
|
- DECORT cloud platform version 3.4.2 or higher
|
||||||
options:
|
options:
|
||||||
app_id:
|
app_id:
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ from ansible.module_utils.basic import env_fallback
|
|||||||
from ansible.module_utils.decort_utils import *
|
from ansible.module_utils.decort_utils import *
|
||||||
|
|
||||||
|
|
||||||
def decort_pfw_package_facts(pfw_facts, check_mode=False):
|
def decort_pfw_package_facts(comp_id, vins_id, pfw_facts, check_mode=False):
|
||||||
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
"""Package a dictionary of PFW rules facts according to the decort_pfw module specification.
|
||||||
This dictionary will be returned to the upstream Ansible engine at the completion of
|
This dictionary will be returned to the upstream Ansible engine at the completion of
|
||||||
the module run.
|
the module run.
|
||||||
@@ -191,11 +191,10 @@ def decort_pfw_package_facts(pfw_facts, check_mode=False):
|
|||||||
@param (bool) check_mode: boolean that tells if this Ansible module is run in check mode
|
@param (bool) check_mode: boolean that tells if this Ansible module is run in check mode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ret_dict = dict(id=0,
|
ret_dict = dict(state="CHECK_MODE",
|
||||||
name="none",
|
|
||||||
state="CHECK_MODE",
|
|
||||||
compute_id=0,
|
compute_id=0,
|
||||||
vins_id=0,
|
vins_id=0,
|
||||||
|
rules=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
if check_mode:
|
if check_mode:
|
||||||
@@ -207,7 +206,14 @@ def decort_pfw_package_facts(pfw_facts, check_mode=False):
|
|||||||
ret_dict['state'] = "ABSENT"
|
ret_dict['state'] = "ABSENT"
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
ret_dict['compute_id'] = pfw_facts['compute_id']
|
ret_dict['compute_id'] = comp_id
|
||||||
|
ret_dict['vins_id'] = vins_id
|
||||||
|
|
||||||
|
if len(pfw_facts) != 0:
|
||||||
|
ret_dict['state'] = 'PRESENT'
|
||||||
|
ret_dict['rules'] = pfw_facts
|
||||||
|
else:
|
||||||
|
ret_dict['state'] = 'ABSENT'
|
||||||
|
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
@@ -316,7 +322,7 @@ def main():
|
|||||||
amodule.fail_json(**decon.result)
|
amodule.fail_json(**decon.result)
|
||||||
else:
|
else:
|
||||||
# prepare PFW facts to be returned as part of decon.result and then call exit_json(...)
|
# prepare PFW facts to be returned as part of decon.result and then call exit_json(...)
|
||||||
decon.result['facts'] = decort_pfw_package_facts(pfw_facts, amodule.check_mode)
|
decon.result['facts'] = decort_pfw_package_facts(comp_facts['id'], vins_facts['id'], pfw_facts, amodule.check_mode)
|
||||||
amodule.exit_json(**decon.result)
|
amodule.exit_json(**decon.result)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2419,6 +2419,24 @@ class DecortController(object):
|
|||||||
#
|
#
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
|
def _pfw_get(self, comp_id, vins_id):
|
||||||
|
"""Convenience method to get current PFW rules for the specified compute ID.
|
||||||
|
"""
|
||||||
|
api_params = dict(vinsId=vins_id)
|
||||||
|
api_resp = self.decort_api_call(requests.post, "/restmachine/cloudapi/vins/natRuleList", api_params)
|
||||||
|
all_rules = json.loads(api_resp.content.decode('utf8'))
|
||||||
|
|
||||||
|
if comp_id == 0:
|
||||||
|
# no filtering by compute ID, return rules as is
|
||||||
|
return all_rules
|
||||||
|
|
||||||
|
filtered_rules = []
|
||||||
|
for runner in all_rules:
|
||||||
|
if runner['vmId'] == comp_id:
|
||||||
|
filtered_rules.append(runner)
|
||||||
|
|
||||||
|
return filtered_rules
|
||||||
|
|
||||||
def pfw_configure(self, comp_facts, vins_facts, new_rules=None):
|
def pfw_configure(self, comp_facts, vins_facts, new_rules=None):
|
||||||
"""Manage port forwarding rules for Compute in a smart way. The method will try to match existing
|
"""Manage port forwarding rules for Compute in a smart way. The method will try to match existing
|
||||||
rules against the new rules set and calculate the delta settings to apply to the corresponding
|
rules against the new rules set and calculate the delta settings to apply to the corresponding
|
||||||
@@ -2430,6 +2448,9 @@ class DecortController(object):
|
|||||||
to which PFW rules set will be applied.
|
to which PFW rules set will be applied.
|
||||||
@param (list of dicts) new_rules: new PFW rules set. If None is passed, remove all existing
|
@param (list of dicts) new_rules: new PFW rules set. If None is passed, remove all existing
|
||||||
PFW rules for the Compute.
|
PFW rules for the Compute.
|
||||||
|
|
||||||
|
@returns: list of dictionaries with PFW rules as returned by .../vins/natRuleList on success,
|
||||||
|
None on error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# At the entry to this method we assume that initial validations are already passed, namely:
|
# At the entry to this method we assume that initial validations are already passed, namely:
|
||||||
@@ -2450,11 +2471,14 @@ class DecortController(object):
|
|||||||
|
|
||||||
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "pfw_configure")
|
self.result['waypoints'] = "{} -> {}".format(self.result['waypoints'], "pfw_configure")
|
||||||
|
|
||||||
|
ret_rules = []
|
||||||
|
|
||||||
if self.amodule.check_mode:
|
if self.amodule.check_mode:
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['msg'] = ("pfw_configure() in check mode: port forwards configuration requested "
|
self.result['msg'] = ("pfw_configure() in check mode: port forwards configuration requested "
|
||||||
"for Compute ID {} / ViNS ID {}").format(comp_facts['id'], vins_facts['id'])
|
"for Compute ID {} / ViNS ID {}").format(comp_facts['id'], vins_facts['id'])
|
||||||
return None
|
ret_rules = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
||||||
|
return ret_rules
|
||||||
|
|
||||||
iface_ipaddr = "" # keep IP address associated with Compute's connection to this ViNS - need this for natRuleDel API
|
iface_ipaddr = "" # keep IP address associated with Compute's connection to this ViNS - need this for natRuleDel API
|
||||||
for iface in comp_facts['interfaces']:
|
for iface in comp_facts['interfaces']:
|
||||||
@@ -2464,7 +2488,7 @@ class DecortController(object):
|
|||||||
else:
|
else:
|
||||||
decon.result['failed'] = True
|
decon.result['failed'] = True
|
||||||
decon.result['msg'] = "Compute ID {} is not connected to ViNS ID {}.".format(comp_facts['id'], vins_facts['id'])
|
decon.result['msg'] = "Compute ID {} is not connected to ViNS ID {}.".format(comp_facts['id'], vins_facts['id'])
|
||||||
return None
|
return ret_rules
|
||||||
|
|
||||||
existing_rules = []
|
existing_rules = []
|
||||||
for runner in vins_facts['vnfs']['NAT']['config']['rules']:
|
for runner in vins_facts['vnfs']['NAT']['config']['rules']:
|
||||||
@@ -2475,7 +2499,7 @@ class DecortController(object):
|
|||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['warning'] = ("pfw_configure(): both existing and new port forwarding rule lists "
|
self.result['warning'] = ("pfw_configure(): both existing and new port forwarding rule lists "
|
||||||
"for Compute ID {} are empty - nothing to do.").format(comp_facts['id'])
|
"for Compute ID {} are empty - nothing to do.").format(comp_facts['id'])
|
||||||
return None
|
return ret_rules
|
||||||
|
|
||||||
if new_rules == None or len(new_rules) == 0:
|
if new_rules == None or len(new_rules) == 0:
|
||||||
# delete all existing rules for this Compute
|
# delete all existing rules for this Compute
|
||||||
@@ -2483,7 +2507,7 @@ class DecortController(object):
|
|||||||
ruleId=-1)
|
ruleId=-1)
|
||||||
self.decort_api_call(requests.post, "/restmachine/cloudapi/vins/natRuleDel", api_params)
|
self.decort_api_call(requests.post, "/restmachine/cloudapi/vins/natRuleDel", api_params)
|
||||||
self.result['changed'] = True
|
self.result['changed'] = True
|
||||||
return None
|
return ret_rules
|
||||||
|
|
||||||
#
|
#
|
||||||
# delta_list will be a list of dictionaries that describe _changes_ to the port forwarding rules
|
# delta_list will be a list of dictionaries that describe _changes_ to the port forwarding rules
|
||||||
@@ -2501,6 +2525,14 @@ class DecortController(object):
|
|||||||
for rule in new_rules:
|
for rule in new_rules:
|
||||||
rule['action'] = 'add'
|
rule['action'] = 'add'
|
||||||
rule_port_end = rule.get('public_port_end', rule['public_port_start'])
|
rule_port_end = rule.get('public_port_end', rule['public_port_start'])
|
||||||
|
if rule_port_end > rule['public_port_start']:
|
||||||
|
# This is a ranged rule, i.e. when range of public ports maps to an equally
|
||||||
|
# sized range of local ports.
|
||||||
|
# For such case we have to make sure that the local port equals public
|
||||||
|
# port (this check & adjustment will be made by vnf_nat.add method anyway, but
|
||||||
|
# if we adjust here, we can avoid unnecessary rule del / add iteration in the
|
||||||
|
# module run, thus saving execution time)
|
||||||
|
rule['local_port'] = rule['public_port_start']
|
||||||
for runner in existing_rules:
|
for runner in existing_rules:
|
||||||
if (runner['publicPortStart'] == rule['public_port_start'] and
|
if (runner['publicPortStart'] == rule['public_port_start'] and
|
||||||
runner['publicPortEnd'] == rule_port_end and
|
runner['publicPortEnd'] == rule_port_end and
|
||||||
@@ -2536,7 +2568,8 @@ class DecortController(object):
|
|||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
self.result['warning'] = ("pfw_configure() no difference between current and new PFW rules "
|
self.result['warning'] = ("pfw_configure() no difference between current and new PFW rules "
|
||||||
"found. No change applied to Compute ID {}.").format(comp_facts['id'])
|
"found. No change applied to Compute ID {}.").format(comp_facts['id'])
|
||||||
return
|
ret_rules = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
||||||
|
return ret_rules
|
||||||
|
|
||||||
# now delta_list contains a list of enriched rule dictionaries with extra key 'action', which
|
# now delta_list contains a list of enriched rule dictionaries with extra key 'action', which
|
||||||
# tells what kind of action is expected on this rule - 'add' or 'del'
|
# tells what kind of action is expected on this rule - 'add' or 'del'
|
||||||
@@ -2567,4 +2600,5 @@ class DecortController(object):
|
|||||||
|
|
||||||
self.result['failed'] = False
|
self.result['failed'] = False
|
||||||
|
|
||||||
return
|
ret_rules = self._pfw_get(comp_facts['id'], vins_facts['id'])
|
||||||
|
return ret_rules
|
||||||
Reference in New Issue
Block a user