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_device_config #52765

Merged
merged 4 commits into from
Mar 4, 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
174 changes: 62 additions & 112 deletions lib/ansible/modules/network/fortimanager/fmgr_device_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
---
module: fmgr_device_config
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,23 +45,12 @@
- The ADOM the configuration should belong to.
required: false
default: root
host:
description:
- The FortiManager's address.
required: true
username:
description:
- The username used to authenticate with the FortiManager.
required: false
password:
description:
- The password associated with the username account.
required: false

device_unique_name:
description:
- The unique device's name that you are editing. A.K.A. Friendly name of the device in FortiManager.
required: True

device_hostname:
description:
- The device's new hostname.
Expand All @@ -70,35 +61,31 @@
- Tells FMGR to attempt to install the config after making it.
required: false
default: disable

interface:
description:
- The interface/port number you are editing.
required: false

interface_ip:
description:
- The IP and subnet of the interface/port you are editing.
required: false

interface_allow_access:
description:
- Specify what protocols are allowed on the interface, comma-sepeareted list (see examples).
- Specify what protocols are allowed on the interface, comma-separated list (see examples).
required: false

'''

EXAMPLES = '''
- name: CHANGE HOSTNAME
fmgr_device_config:
host: "{{inventory_hostname}}"
username: "{{ username }}"
password: "{{ password }}"
device_hostname: "ChangedbyAnsible"
device_unique_name: "FGT1"

- name: EDIT INTERFACE INFORMATION
fmgr_device_config:
host: "{{inventory_hostname}}"
username: "{{ username }}"
password: "{{ password }}"
adom: "root"
device_unique_name: "FGT2"
interface: "port3"
Expand All @@ -107,9 +94,6 @@

- name: INSTALL CONFIG
fmgr_device_config:
host: "{{inventory_hostname}}"
username: "{{ username }}"
password: "{{ password }}"
adom: "root"
device_unique_name: "FGT1"
install_config: "enable"
Expand All @@ -122,33 +106,42 @@
type: str
"""

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.basic import AnsibleModule
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 DEFAULT_RESULT_OBJ
from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG
from ansible.module_utils.network.fortimanager.common import FMGRMethods


def update_device_hostname(fmg, paramgram):
def update_device_hostname(fmgr, paramgram):
"""
Change a device's hostname
: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
"""
datagram = {
"hostname": paramgram["device_hostname"]
}

url = "pm/config/device/{device_name}/global/system/global".format(device_name=paramgram["device_unique_name"])
response = fmg.update(url, datagram)
response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
return response


def update_device_interface(fmg, paramgram):
def update_device_interface(fmgr, paramgram):
"""
Update a device interface IP and allow access
: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
"""
access_list = list()
allow_access_list = paramgram["interface_allow_access"].replace(' ', '')
Expand All @@ -161,13 +154,18 @@ def update_device_interface(fmg, paramgram):

url = "/pm/config/device/{device_name}/global/system/interface" \
"/{interface}".format(device_name=paramgram["device_unique_name"], interface=paramgram["interface"])
response = fmg.update(url, datagram)
response = fmgr.process_request(url, datagram, FMGRMethods.UPDATE)
return response


def exec_config(fmg, paramgram):
def exec_config(fmgr, paramgram):
"""
Update a device interface IP and allow access
: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
"""
datagram = {
"scope": {
Expand All @@ -178,55 +176,13 @@ def exec_config(fmg, paramgram):
}

url = "/securityconsole/install/device"
response = fmg.execute(url, datagram)
response = fmgr.process_request(url, datagram, FMGRMethods.EXEC)
return response


# 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
"""

# VALIDATION ERROR (NO RESULTS, JUST AN EXIT)
if msg != "NULL" and len(results) == 0:
try:
fmg.logout()
except Exception:
pass
module.fail_json(msg=msg)

# SUBMISSION ERROR
if len(results) > 0:
if msg == "NULL":
try:
msg = results[1]['status']['message']
except Exception:
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 = msg + " -- LOGOUT ON FAIL IS OFF, MOVING ON"
return return_msg
else:
if logout_on_success:
fmg.logout()
module.exit_json(msg=msg, **results[1])
else:
return_msg = msg + " -- LOGOUT ON SUCCESS IS OFF, MOVING ON TO REST OF CODE"
return return_msg


def main():
argument_spec = dict(
host=dict(required=True, type="str"),
adom=dict(required=False, type="str", default="root"),
password=dict(fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True),
username=dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])),

device_unique_name=dict(required=True, type="str"),
device_hostname=dict(required=False, type="str"),
interface=dict(required=False, type="str"),
Expand All @@ -235,9 +191,7 @@ def main():
install_config=dict(required=False, type="str", default="disable"),
)

module = AnsibleModule(argument_spec, supports_check_mode=True,)

# handle params passed via provider and insure they are represented as the data type expected by fortimanager
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, )
paramgram = {
"device_unique_name": module.params["device_unique_name"],
"device_hostname": module.params["device_hostname"],
Expand All @@ -247,40 +201,36 @@ def main():
"install_config": module.params["install_config"],
"adom": module.params["adom"]
}

# check if params are set
if module.params["host"] is None or module.params["username"] is None or module.params["password"] is None:
module.fail_json(msg="Host and username are required for connection")

# 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")
module.paramgram = paramgram
fmgr = None
if module._socket_path:
connection = Connection(module._socket_path)
fmgr = FortiManagerHandler(connection, module)
fmgr.tools = FMGRCommon()
else:
module.fail_json(**FAIL_SOCKET_MSG)

# START SESSION LOGIC

# if the device_hostname isn't null, then attempt the api call via method call, store results in variable
# BEGIN MODULE-SPECIFIC LOGIC -- THINGS NEED TO HAPPEN DEPENDING ON THE ENDPOINT AND OPERATION
results = DEFAULT_RESULT_OBJ
try:
if paramgram["device_hostname"] is not None:
# add device
results = update_device_hostname(fmg, paramgram)
if results[0] != 0:
fmgr_logout(fmg, module, msg="Failed to set Hostname", results=results, good_codes=[0])
results = update_device_hostname(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))

if paramgram["interface_ip"] is not None or paramgram["interface_allow_access"] is not None:
results = update_device_interface(fmg, paramgram)
if results[0] != 0:
fmgr_logout(fmg, module, msg="Failed to Update Device Interface", results=results, good_codes=[0])
results = update_device_interface(fmgr, paramgram)
fmgr.govern_response(module=module, results=results,
ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram))

if paramgram["install_config"] == "enable":
# attempt to install the config
results = exec_config(fmg, paramgram)
if results[0] != 0:
fmgr_logout(fmg, module, msg="Failed to Update Device Interface", results=results, good_codes=[0])
results = exec_config(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)

# logout, build in check for future logging capabilities
fmg.logout()
return module.exit_json(**results[1])


Expand Down