Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FortiManager Plugin Module Conversion: fmgr_secprof_waf #52789

Merged
merged 3 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
242 changes: 47 additions & 195 deletions lib/ansible/modules/network/fortimanager/fmgr_secprof_waf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
---
module: fmgr_secprof_waf
version_added: "2.8"
notes:
- Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/).
author:
- Luke Weighall (@lweighall)
- Andrew Welsh (@Ghilli3)
Expand All @@ -43,21 +45,6 @@
required: false
default: root

host:
description:
- The FortiManager's Address.
required: true

username:
description:
- The username associated with the account.
required: true

password:
description:
- The password associated with the username account.
required: true

mode:
description:
- Sets one of three modes for managing the object.
Expand Down Expand Up @@ -1045,18 +1032,12 @@
EXAMPLES = '''
- name: DELETE Profile
fmgr_secprof_waf:
host: "{{inventory_hostname}}"
username: "{{ username }}"
password: "{{ password }}"
name: "Ansible_WAF_Profile"
comment: "Created by Ansible Module TEST"
mode: "delete"

- name: CREATE Profile
fmgr_secprof_waf:
host: "{{inventory_hostname}}"
username: "{{ username }}"
password: "{{ password }}"
name: "Ansible_WAF_Profile"
comment: "Created by Ansible Module TEST"
mode: "set"
Expand All @@ -1070,165 +1051,54 @@
"""

from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.network.fortimanager.fortimanager import AnsibleFortiManager

# check for pyFMG lib
try:
from pyFMG.fortimgr import FortiManager

HAS_PYFMGR = True
except ImportError:
HAS_PYFMGR = False
from ansible.module_utils.connection import Connection
from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler
from ansible.module_utils.network.fortimanager.common import FMGBaseException
from ansible.module_utils.network.fortimanager.common import FMGRCommon
from ansible.module_utils.network.fortimanager.common import FMGRMethods
from ansible.module_utils.network.fortimanager.common import DEFAULT_RESULT_OBJ
from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
from ansible.module_utils.network.fortimanager.common import prepare_dict
from ansible.module_utils.network.fortimanager.common import scrub_dict


###############
# START METHODS
###############


def fmgr_waf_profile_addsetdelete(fmg, paramgram):

def fmgr_waf_profile_modify(fmgr, paramgram):
"""
:param fmgr: The fmgr object instance from fortimanager.py
:type fmgr: class object
:param paramgram: The formatted dictionary of options to process
:type paramgram: dict
:return: The response from the FortiManager
:rtype: dict
"""
mode = paramgram["mode"]
adom = paramgram["adom"]
# INIT A BASIC OBJECTS
response = (-100000, {"msg": "Illegal or malformed paramgram discovered. System Exception"})
response = DEFAULT_RESULT_OBJ
url = ""
datagram = {}

# EVAL THE MODE PARAMETER FOR SET OR ADD
if mode in ['set', 'add', 'update']:
url = '/pm/config/adom/{adom}/obj/waf/profile'.format(adom=adom)
datagram = fmgr_del_none(fmgr_prepare_dict(paramgram))
datagram = scrub_dict(prepare_dict(paramgram))

# EVAL THE MODE PARAMETER FOR DELETE
elif mode == "delete":
# SET THE CORRECT URL FOR DELETE
url = '/pm/config/adom/{adom}/obj/waf/profile/{name}'.format(adom=adom, name=paramgram["name"])
datagram = {}

# IF MODE = SET -- USE THE 'SET' API CALL MODE
if mode == "set":
response = fmg.set(url, datagram)
# IF MODE = UPDATE -- USER THE 'UPDATE' API CALL MODE
elif mode == "update":
response = fmg.update(url, datagram)
# IF MODE = ADD -- USE THE 'ADD' API CALL MODE
elif mode == "add":
response = fmg.add(url, datagram)
# IF MODE = DELETE -- USE THE DELETE URL AND API CALL MODE
elif mode == "delete":
response = fmg.delete(url, datagram)
response = fmgr.process_request(url, datagram, paramgram["mode"])

return response


# ADDITIONAL COMMON FUNCTIONS
# FUNCTION/METHOD FOR LOGGING OUT AND ANALYZING ERROR CODES
def fmgr_logout(fmg, module, msg="NULL", results=(), good_codes=(0,), logout_on_fail=True, logout_on_success=False):
"""
THIS METHOD CONTROLS THE LOGOUT AND ERROR REPORTING AFTER AN METHOD OR FUNCTION RUNS
"""
# pydevd.settrace('10.0.0.122', port=54654, stdoutToServer=True, stderrToServer=True)
# VALIDATION ERROR (NO RESULTS, JUST AN EXIT)
if msg != "NULL" and len(results) == 0:
try:
fmg.logout()
except BaseException:
pass
module.fail_json(msg=msg)

# SUBMISSION ERROR
if len(results) > 0:
if msg == "NULL":
try:
msg = results[1]['status']['message']
except BaseException:
msg = "No status message returned from pyFMG. Possible that this was a GET with a tuple result."

if results[0] not in good_codes:
if logout_on_fail:
fmg.logout()
module.fail_json(msg=msg, **results[1])
else:
return msg
else:
if logout_on_success:
fmg.logout()
module.exit_json(msg="API Called worked, but logout handler has been asked to logout on success",
**results[1])
else:
return msg


# FUNCTION/METHOD FOR CONVERTING CIDR TO A NETMASK
# DID NOT USE IP ADDRESS MODULE TO KEEP INCLUDES TO A MINIMUM
def fmgr_cidr_to_netmask(cidr):
cidr = int(cidr)
mask = (0xffffffff >> (32 - cidr)) << (32 - cidr)
return (str((0xff000000 & mask) >> 24) + '.' +
str((0x00ff0000 & mask) >> 16) + '.' +
str((0x0000ff00 & mask) >> 8) + '.' +
str((0x000000ff & mask)))


# utility function: removing keys wih value of None, nothing in playbook for that key
def fmgr_del_none(obj):
if isinstance(obj, dict):
return type(obj)((fmgr_del_none(k), fmgr_del_none(v))
for k, v in obj.items() if k is not None and (v is not None and not fmgr_is_empty_dict(v)))
else:
return obj


# utility function: remove keys that are need for the logic but the FMG API won't accept them
def fmgr_prepare_dict(obj):
list_of_elems = ["mode", "adom", "host", "username", "password"]
if isinstance(obj, dict):
obj = dict((key, fmgr_prepare_dict(value)) for (key, value) in obj.items() if key not in list_of_elems)
return obj


def fmgr_is_empty_dict(obj):
return_val = False
if isinstance(obj, dict):
if len(obj) > 0:
for k, v in obj.items():
if isinstance(v, dict):
if len(v) == 0:
return_val = True
elif len(v) > 0:
for k1, v1 in v.items():
if v1 is None:
return_val = True
elif v1 is not None:
return_val = False
return return_val
elif v is None:
return_val = True
elif v is not None:
return_val = False
return return_val
elif len(obj) == 0:
return_val = True

return return_val


def fmgr_split_comma_strings_into_lists(obj):
if isinstance(obj, dict):
if len(obj) > 0:
for k, v in obj.items():
if isinstance(v, str):
new_list = list()
if "," in v:
new_items = v.split(",")
for item in new_items:
new_list.append(item.strip())
obj[k] = new_list

return obj


#############
# END METHODS
#############
Expand All @@ -1237,9 +1107,6 @@ def fmgr_split_comma_strings_into_lists(obj):
def main():
argument_spec = dict(
adom=dict(type="str", default="root"),
host=dict(required=True, type="str"),
password=dict(fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True, required=True),
username=dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"]), no_log=True, required=True),
mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"),

name=dict(required=False, type="str"),
Expand Down Expand Up @@ -1414,8 +1281,7 @@ def main():

)

module = AnsibleModule(argument_spec, supports_check_mode=False)

module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, )
# MODULE PARAMGRAM
paramgram = {
"mode": module.params["mode"],
Expand Down Expand Up @@ -1586,44 +1452,30 @@ def main():
}
}

list_overrides = ['address-list', 'constraint', 'method', 'signature', 'url-access']
for list_variable in list_overrides:
override_data = list()
try:
override_data = module.params[list_variable]
except BaseException:
pass
try:
if override_data:
del paramgram[list_variable]
paramgram[list_variable] = override_data
except BaseException:
pass

# CHECK IF THE HOST/USERNAME/PW EXISTS, AND IF IT DOES, LOGIN.
host = module.params["host"]
password = module.params["password"]
username = module.params["username"]
if host is None or username is None or password is None:
module.fail_json(msg="Host and username and password are required")

# CHECK IF LOGIN FAILED
fmg = AnsibleFortiManager(module, module.params["host"], module.params["username"], module.params["password"])

response = fmg.login()
if response[1]['status']['code'] != 0:
module.fail_json(msg="Connection to FortiManager Failed")

results = fmgr_waf_profile_addsetdelete(fmg, paramgram)
if results[0] != 0:
fmgr_logout(fmg, module, results=results, good_codes=[0])

fmg.logout()

if results is not None:
return module.exit_json(**results[1])
module.paramgram = paramgram
fmgr = None
if module._socket_path:
connection = Connection(module._socket_path)
fmgr = FortiManagerHandler(connection, module)
fmgr.tools = FMGRCommon()
else:
return module.exit_json(msg="No results were returned from the API call.")
module.fail_json(**FAIL_SOCKET_MSG)

list_overrides = ['address-list', 'constraint', 'method', 'signature', 'url-access']
paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides,
paramgram=paramgram, module=module)

results = DEFAULT_RESULT_OBJ

try:
results = fmgr_waf_profile_modify(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))

except Exception as err:
raise FMGBaseException(err)

return module.exit_json(**results[1])


if __name__ == "__main__":
Expand Down