From 86222fd4c4a05f0ca18bea145e4e2a6f35dc9c60 Mon Sep 17 00:00:00 2001 From: ftntcorecse <43451990+ftntcorecse@users.noreply.github.com> Date: Tue, 20 Nov 2018 23:27:04 -0700 Subject: [PATCH] Fortinet FortiManager HA Configuration Module (#46085) * fmgr_ha PR candidate * PR candidate * PR candidate * PR candidate * PR candidate * Fixing Edits. * Fixing Edits. * Fixing Edits. * Fixing Edits. * Fixing Authors --- .../modules/network/fortimanager/fmgr_ha.py | 404 +++++++++++++++++ .../fortimanager/fixtures/test_fmgr_ha.json | 312 +++++++++++++ .../network/fortimanager/test_fmgr_ha.py | 424 ++++++++++++++++++ 3 files changed, 1140 insertions(+) create mode 100644 lib/ansible/modules/network/fortimanager/fmgr_ha.py create mode 100644 test/units/modules/network/fortimanager/fixtures/test_fmgr_ha.json create mode 100644 test/units/modules/network/fortimanager/test_fmgr_ha.py diff --git a/lib/ansible/modules/network/fortimanager/fmgr_ha.py b/lib/ansible/modules/network/fortimanager/fmgr_ha.py new file mode 100644 index 0000000000..2ea708c932 --- /dev/null +++ b/lib/ansible/modules/network/fortimanager/fmgr_ha.py @@ -0,0 +1,404 @@ +#!/usr/bin/python +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community" +} + +DOCUMENTATION = ''' +--- +module: fmgr_ha +version_added: "2.8" +author: + - Luke Weighall (@lweighall) + - Andrew Welsh (@Ghilli3) + - Jim Huber (@p4r4n0y1ng) +short_description: Manages the High-Availability State of FortiManager Clusters and Nodes. +description: Change HA state or settings of FortiManager nodes (Standalone/Master/Slave). + +options: + host: + description: + - The FortiManager's address. + required: true + username: + description: + - The username to log into the FortiManager. + required: true + password: + description: + - The password associated with the username account. + required: false + fmgr_ha_mode: + description: + - Sets the role of the FortiManager host for HA. + required: false + choices: ["standalone", "master", "slave"] + fmgr_ha_peer_ipv4: + description: + - Sets the IPv4 address of a HA peer. + required: false + fmgr_ha_peer_ipv6: + description: + - Sets the IPv6 address of a HA peer. + required: false + fmgr_ha_peer_sn: + description: + - Sets the HA Peer Serial Number. + required: false + fmgr_ha_peer_status: + description: + - Sets the peer status to enable or disable. + required: false + choices: ["enable", "disable"] + fmgr_ha_cluster_pw: + description: + - Sets the password for the HA cluster. Only required once. System remembers between HA mode switches. + required: false + fmgr_ha_cluster_id: + description: + - Sets the ID number of the HA cluster. Defaults to 1. + required: false + default: 1 + fmgr_ha_hb_threshold: + description: + - Sets heartbeat lost threshold (1-255). + required: false + default: 3 + fmgr_ha_hb_interval: + description: + - Sets the heartbeat interval (1-255). + required: false + default: 5 + fmgr_ha_file_quota: + description: + - Sets the File quota in MB (2048-20480). + required: false + default: 4096 +''' + + +EXAMPLES = ''' +- name: SET FORTIMANAGER HA NODE TO MASTER + fmgr_ha: + host: "{{inventory_hostname}}" + username: "{{ username }}" + password: "{{ password }}" + fmgr_ha_mode: "master" + +- name: SET FORTIMANAGER HA NODE TO SLAVE + fmgr_ha: + host: "{{inventory_hostname}}" + username: "{{ username }}" + password: "{{ password }}" + fmgr_ha_mode: "slave" + +- name: SET FORTIMANAGER HA NODE TO STANDALONE + fmgr_ha: + host: "{{inventory_hostname}}" + username: "{{ username }}" + password: "{{ password }}" + fmgr_ha_mode: "standalone" + +- name: ADD FORTIMANAGER HA PEER + fmgr_ha: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + fmgr_ha_peer_ipv4: "192.168.1.254" + fmgr_ha_peer_sn: "FMG-VM1234567890" + fmgr_ha_peer_status: "enable" + +- name: CREATE CLUSTER ON MASTER + fmgr_ha: + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + fmgr_ha_mode: "master" + fmgr_ha_cluster_pw: "fortinet" + fmgr_ha_cluster_id: "1" + fmgr_ha_hb_threshold: "10" + fmgr_ha_hb_interval: "15" + fmgr_ha_file_quota: "2048" +''' +RETURN = """ +api_result: + description: full API response, includes status code and message + returned: always + type: string +""" + +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 + + +def fmgr_set_ha_mode(fmg, paramgram): + """ + This method is used set the HA mode of a FortiManager Node + """ + + if paramgram["fmgr_ha_cluster_pw"] is not None and str.lower(paramgram["fmgr_ha_mode"]) != "standalone": + datagram = { + "mode": paramgram["fmgr_ha_mode"], + "file-quota": paramgram["fmgr_ha_file_quota"], + "hb-interval": paramgram["fmgr_ha_hb_interval"], + "hb-lost-threshold": paramgram["fmgr_ha_hb_threshold"], + "password": paramgram["fmgr_ha_cluster_pw"], + "clusterid": paramgram["fmgr_ha_cluster_id"] + } + elif str.lower(paramgram["fmgr_ha_mode"]) == "standalone": + datagram = { + "mode": paramgram["fmgr_ha_mode"], + "file-quota": paramgram["fmgr_ha_file_quota"], + "hb-interval": paramgram["fmgr_ha_hb_interval"], + "hb-lost-threshold": paramgram["fmgr_ha_hb_threshold"], + "clusterid": paramgram["fmgr_ha_cluster_id"] + } + + url = '/cli/global/system/ha' + response = fmg.set(url, datagram) + return response + + +def fmgr_get_ha_peer_list(fmg): + """ + This method is used GET the HA PEERS of a FortiManager Node + """ + + datagram = { + "method": "get" + } + + url = '/cli/global/system/ha/peer/' + response = fmg.get(url, datagram) + return response + + +def fmgr_set_ha_peer(fmg, paramgram): + """ + This method is used GET the HA PEERS of a FortiManager Node + """ + + datagram = { + "ip": paramgram["fmgr_ha_peer_ipv4"], + "ip6": paramgram["fmgr_ha_peer_ipv6"], + "serial-number": paramgram["fmgr_ha_peer_sn"], + "status": paramgram["fmgr_ha_peer_status"], + "id": paramgram["peer_id"] + } + + url = '/cli/global/system/ha/peer/' + response = fmg.set(url, datagram) + 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 + """ + + # VALIDATION ERROR (NO RESULTS, JUST AN EXIT) + if msg != "NULL" and len(results) == 0: + try: + fmg.logout() + except: + pass + module.fail_json(msg=msg) + + # SUBMISSION ERROR + if len(results) > 0: + if msg == "NULL": + try: + msg = results[1]['status']['message'] + except: + 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 + else: + if logout_on_success: + fmg.logout() + module.exit_json(msg=msg, **results[1]) + else: + return + + +def main(): + argument_spec = dict( + host=dict(required=True, type="str"), + password=dict(fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True), + username=dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"]), no_log=True), + fmgr_ha_mode=dict(required=False, type="str", choices=["standalone", "master", "slave"]), + fmgr_ha_cluster_pw=dict(required=False, type="str", no_log=True), + fmgr_ha_peer_status=dict(required=False, type="str", choices=["enable", "disable"]), + fmgr_ha_peer_sn=dict(required=False, type="str"), + fmgr_ha_peer_ipv4=dict(required=False, type="str"), + fmgr_ha_peer_ipv6=dict(required=False, type="str"), + fmgr_ha_hb_threshold=dict(required=False, type="int", default=3), + fmgr_ha_hb_interval=dict(required=False, type="int", default=5), + fmgr_ha_file_quota=dict(required=False, type="int", default=4096), + fmgr_ha_cluster_id=dict(required=False, type="int", default=1) + ) + + module = AnsibleModule(argument_spec, supports_check_mode=True,) + + # VALIDATE PARAMS BEFORE ATTEMPTING TO CONNECT + paramgram = { + "fmgr_ha_mode": module.params["fmgr_ha_mode"], + "fmgr_ha_cluster_pw": module.params["fmgr_ha_cluster_pw"], + "fmgr_ha_peer_status": module.params["fmgr_ha_peer_status"], + "fmgr_ha_peer_sn": module.params["fmgr_ha_peer_sn"], + "fmgr_ha_peer_ipv4": module.params["fmgr_ha_peer_ipv4"], + "fmgr_ha_peer_ipv6": module.params["fmgr_ha_peer_ipv6"], + "fmgr_ha_hb_threshold": module.params["fmgr_ha_hb_threshold"], + "fmgr_ha_hb_interval": module.params["fmgr_ha_hb_interval"], + "fmgr_ha_file_quota": module.params["fmgr_ha_file_quota"], + "fmgr_ha_cluster_id": module.params["fmgr_ha_cluster_id"], + } + # INIT FLAGS AND COUNTERS + get_ha_peers = 0 + # validate required arguments are passed; not used in argument_spec to allow params to be called from provider + # check if params are set + if module.params["host"] is None or module.params["username"] 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") + else: + # START SESSION LOGIC + # IF THE PEER SN DEFINED, BUT THE IPS ARE NOT, THEN QUIT + if paramgram["fmgr_ha_peer_sn"] is not None: + # CHANGE GET_HA_PEERS TO SHOW INTENT TO EDIT PEERS + get_ha_peers = 1 + # DOUBLE CHECK THAT THE REST OF THE NEEDED PARAMETERS ARE THERE + if paramgram["fmgr_ha_peer_ipv4"] is None and paramgram["fmgr_ha_peer_ipv6"] is None: + fmgr_logout(fmg, module, msg="HA Peer Serial Number is defined but the " + "IPv4 and IPv6 fields are empty." + " Fill in the IPv4 or v6 parameters in the playbook") + + # IF THE PEER IPS ARE DEFINED, BUT NOT THE SERIAL NUMBER, THEN QUIT + if paramgram["fmgr_ha_peer_ipv4"] is not None or paramgram["fmgr_ha_peer_ipv6"] is not None: + # CHANGE GET_HA_PEERS TO SHOW INTENT TO EDIT PEERS + get_ha_peers = 1 + # DOUBLE CHECK THAT THE REST OF THE NEEDED PARAMETERS ARE THERE + if paramgram["fmgr_ha_peer_sn"] is None: + fmgr_logout(fmg, module, msg="HA Peer IP Address is defined, but not the Peer Serial Number. " + "Fill in the SN parameter in the playbook.") + + # IF THE PEER STATUS IS SET, BUT THE SERIAL NUMBER OR IP FIELDS AREN'T THERE, THEN EXIT + if paramgram["fmgr_ha_peer_status"] is not None: + # CHANGE GET_HA_PEERS TO SHOW INTENT TO EDIT PEERS + get_ha_peers = 1 + # DOUBLE CHECK THAT THE REST OF THE NEEDED PARAMETERS ARE THERE + if paramgram["fmgr_ha_peer_ipv4"] is None and paramgram["fmgr_ha_peer_sn"] is None: + if paramgram["fmgr_ha_peer_sn"] is None and paramgram["fmgr_ha_peer_ipv6"] is None: + fmgr_logout(fmg, module, msg="HA Peer Status was defined, but nothing " + "to identify the peer was set. " + "Fill in one of" + " three parameters peer_ipv4 or v6 or serial_num") + + # IF HA MODE IS NOT NULL, SWITCH THAT + if paramgram["fmgr_ha_mode"] is not None: + if (str.lower(paramgram["fmgr_ha_mode"]) != "standalone" and paramgram["fmgr_ha_cluster_pw"] is not None)\ + or str.lower(paramgram["fmgr_ha_mode"]) == "standalone": + results = fmgr_set_ha_mode(fmg, paramgram) + if results[0] != 0: + fmgr_logout(fmg, module, results=results, good_codes=[0], + msg="Failed to edit HA configuration the HA Peer") + elif str.lower(paramgram["fmgr_ha_mode"]) != "standalone" and \ + paramgram["fmgr_ha_mode"] is not None and paramgram["fmgr_ha_cluster_pw"] is None: + fmgr_logout(fmg, module, msg="If setting HA Mode of MASTER or SLAVE, " + "you must specify a cluster password") + + # IF GET_HA_PEERS IS ENABLED, LETS PROCESS THE PEERS + + if get_ha_peers == 1: + # GET THE CURRENT LIST OF PEERS FROM THE NODE + peers = fmgr_get_ha_peer_list(fmg) + # GET LENGTH OF RETURNED PEERS LIST AND ADD ONE FOR THE NEXT ID + paramgram["next_peer_id"] = len(peers[1]) + 1 + # SET THE ACTUAL NUMBER OF PEERS + num_of_peers = len(peers[1]) + # SET THE PEER ID FOR DISABLE METHOD + paramgram["peer_id"] = len(peers) - 1 + # SET THE PEER LOOPCOUNT TO 1 TO START THE LOOP + peer_loopcount = 1 + + # LOOP THROUGH PEERS TO FIND THE SERIAL NUMBER MATCH TO GET THE RIGHT PEER ID + # IDEA BEING WE DON'T WANT TO SUBMIT A BAD peer_id THAT DOESN'T JIVE WITH CURRENT DB ON FMG + # SO LETS SEARCH FOR IT, AND IF WE FIND IT, WE WILL CHANGE THE PEER ID VARIABLES TO MATCH + # IF NOT FOUND, LIFE GOES ON AND WE ASSUME THAT WE'RE ADDING A PEER + # AT WHICH POINT THE next_peer_id VARIABLE WILL HAVE THE RIGHT PRIMARY KEY + + if paramgram["fmgr_ha_peer_sn"] is not None: + while peer_loopcount <= num_of_peers: + # GET THE SERIAL NUMBER FOR CURRENT PEER IN LOOP TO COMPARE TO SN IN PLAYBOOK + try: + sn_compare = peers[1][peer_loopcount - 1]["serial-number"] + # IF THE SN IN THE PEERS MATCHES THE PLAYBOOK SN, SET THE IDS + if sn_compare == paramgram["fmgr_ha_peer_sn"]: + paramgram["peer_id"] = peer_loopcount + paramgram["next_peer_id"] = paramgram["peer_id"] + except: + pass + # ADVANCE THE LOOP AND REPEAT UNTIL DONE + peer_loopcount += 1 + + # IF THE PEER STATUS ISN'T IN THE PLAYBOOK, ASSUME ITS ENABLE + if paramgram["fmgr_ha_peer_status"] is None: + paramgram["fmgr_ha_peer_status"] = "enable" + + # IF THE PEER STATUS IS ENABLE, USE THE next_peer_id IN THE API CALL FOR THE ID + if paramgram["fmgr_ha_peer_status"] == "enable": + results = fmgr_set_ha_peer(fmg, paramgram) + if results[0] != 0: + fmgr_logout(fmg, module, results=results, good_codes=[0], msg="Failed to Enable the HA Peer") + + # IF THE PEER STATUS IS DISABLE, WE HAVE TO HANDLE THAT A BIT DIFFERENTLY + # JUST USING TWO DIFFERENT peer_id 's HERE + if paramgram["fmgr_ha_peer_status"] == "disable": + results = fmgr_set_ha_peer(fmg, paramgram) + if results[0] != 0: + fmgr_logout(fmg, module, results=results, good_codes=[0], msg="Failed to Disable the HA Peer") + + fmg.logout() + return module.exit_json(**results[1]) + + +if __name__ == "__main__": + main() diff --git a/test/units/modules/network/fortimanager/fixtures/test_fmgr_ha.json b/test/units/modules/network/fortimanager/fixtures/test_fmgr_ha.json new file mode 100644 index 0000000000..8572e35e4d --- /dev/null +++ b/test/units/modules/network/fortimanager/fixtures/test_fmgr_ha.json @@ -0,0 +1,312 @@ +{ + "fmgr_set_ha_peer": [ + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha/peer/" + }, + "paramgram_used": { + "fmgr_ha_peer_sn": "FMG-VM0A17004505", + "next_peer_id": 1, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_peer_status": "enable", + "fmgr_ha_file_quota": 4096, + "fmgr_ha_cluster_id": 2, + "peer_id": 1, + "fmgr_ha_peer_ipv4": "10.7.220.35", + "fmgr_ha_hb_interval": 5, + "fmgr_ha_mode": "slave" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "fmgr_ha_peer_sn": "FMG-VM0A17005528", + "next_peer_id": 1, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": null, + "fmgr_ha_hb_interval": 5, + "fmgr_ha_cluster_id": 1, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": "enable", + "peer_id": 1, + "fmgr_ha_peer_ipv4": "10.7.220.36", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": null + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha/peer/" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "fmgr_ha_peer_sn": "FMG-VM0A17005528", + "next_peer_id": 1, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": null, + "fmgr_ha_hb_interval": 5, + "fmgr_ha_cluster_id": 1, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": "enable", + "peer_id": 1, + "fmgr_ha_peer_ipv4": "10.7.220.36", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": null + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha/peer/" + }, + "post_method": "set" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha/peer/" + }, + "paramgram_used": { + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": "enable", + "fmgr_ha_peer_sn": "FMG-VM0A17004505", + "next_peer_id": 1, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": "slave", + "fmgr_ha_cluster_id": 2, + "peer_id": 1, + "fmgr_ha_peer_ipv4": "10.7.220.35", + "fmgr_ha_hb_interval": 5 + }, + "post_method": "set" + } + ], + "fmgr_get_ha_peer_list": [ + { + "url": "/cli/global/system/ha/peer/", + "raw_response": [ + { + "status": 1, + "ip": "10.7.220.36", + "serial-number": "FMG-VM0A17005528", + "ip6": "::", + "id": 1 + } + ], + "paramgram_used": { + "method": "get" + }, + "post_method": "get" + }, + { + "url": "/cli/global/system/ha/peer/", + "paramgram_used": { + "method": "get" + }, + "raw_response": [ + { + "status": 1, + "ip": "10.7.220.35", + "serial-number": "FMG-VM0A17004505", + "ip6": "::", + "id": 1 + } + ], + "post_method": "get" + } + ], + "fmgr_set_ha_mode": [ + { + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 10, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_peer_status": null, + "fmgr_ha_file_quota": 2048, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_hb_interval": 15, + "fmgr_ha_mode": "master" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "post_method": "set" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_hb_interval": 5, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": null, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": "slave" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "fmgr_ha_peer_sn": "FMG-VM0A17004505", + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_peer_status": "enable", + "fmgr_ha_file_quota": 4096, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_peer_ipv4": "10.7.220.35", + "fmgr_ha_hb_interval": 5, + "fmgr_ha_mode": "slave" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "post_method": "set" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": null, + "fmgr_ha_hb_interval": 5, + "fmgr_ha_cluster_id": 1, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": null, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": "standalone" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": null, + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_peer_status": null, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_cluster_id": 1, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_hb_interval": 5, + "fmgr_ha_mode": "standalone" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "post_method": "set" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 10, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_hb_interval": 15, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_file_quota": 2048, + "fmgr_ha_peer_status": null, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": "master" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "fmgr_ha_peer_sn": null, + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_peer_status": null, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_peer_ipv4": null, + "fmgr_ha_hb_interval": 5, + "fmgr_ha_mode": "slave" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "post_method": "set" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/cli/global/system/ha" + }, + "paramgram_used": { + "fmgr_ha_peer_sn": "FMG-VM0A17004505", + "fmgr_ha_hb_threshold": 3, + "fmgr_ha_cluster_pw": "fortinet", + "fmgr_ha_hb_interval": 5, + "fmgr_ha_cluster_id": 2, + "fmgr_ha_file_quota": 4096, + "fmgr_ha_peer_status": "enable", + "fmgr_ha_peer_ipv4": "10.7.220.35", + "fmgr_ha_peer_ipv6": null, + "fmgr_ha_mode": "slave" + }, + "post_method": "set" + } + ] +} diff --git a/test/units/modules/network/fortimanager/test_fmgr_ha.py b/test/units/modules/network/fortimanager/test_fmgr_ha.py new file mode 100644 index 0000000000..069dc7b545 --- /dev/null +++ b/test/units/modules/network/fortimanager/test_fmgr_ha.py @@ -0,0 +1,424 @@ +# Copyright 2018 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +from pyFMG.fortimgr import FortiManager +import pytest + +try: + from ansible.modules.network.fortimanager import fmgr_ha +except ImportError: + pytest.skip( + "Could not load required modules for testing", + allow_module_level=True) + +fmg_instance = FortiManager("1.1.1.1", "admin", "") + + +def load_fixtures(): + fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') + "/{filename}.json".format( + filename=os.path.splitext(os.path.basename(__file__))[0]) + try: + with open(fixture_path, "r") as fixture_file: + fixture_data = json.load(fixture_file) + except IOError: + return [] + return [fixture_data] + + +@pytest.fixture(scope="function", params=load_fixtures()) +def fixture_data(request): + func_name = request.function.__name__.replace("test_", "") + return request.param.get(func_name, None) + + +def test_fmgr_set_ha_mode(fixture_data, mocker): + mocker.patch( + "pyFMG.fortimgr.FortiManager._post_request", + side_effect=fixture_data) + + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 10, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_file_quota': 2048, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_hb_interval': 15, + 'fmgr_ha_mode': 'master', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 10 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_peer_ipv6: None + # fmgr_ha_peer_status: None + # fmgr_ha_file_quota: 2048 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_peer_ipv4: None + # fmgr_ha_hb_interval: 15 + # fmgr_ha_mode: master + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': 'slave', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_hb_interval: 5 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: None + # fmgr_ha_peer_ipv4: None + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: slave + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': 'FMG-VM0A17004505', + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_peer_status': 'enable', + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_peer_ipv4': '10.7.220.35', + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_mode': 'slave', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: FMG-VM0A17004505 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_peer_ipv6: None + # fmgr_ha_peer_status: enable + # fmgr_ha_file_quota: 4096 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_peer_ipv4: 10.7.220.35 + # fmgr_ha_hb_interval: 5 + # fmgr_ha_mode: slave + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': None, + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_cluster_id': 1, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': 'standalone', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: None + # fmgr_ha_hb_interval: 5 + # fmgr_ha_cluster_id: 1 + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: None + # fmgr_ha_peer_ipv4: None + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: standalone + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': None, + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_cluster_id': 1, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_mode': 'standalone', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: None + # fmgr_ha_peer_ipv6: None + # fmgr_ha_peer_status: None + # fmgr_ha_file_quota: 4096 + # fmgr_ha_cluster_id: 1 + # fmgr_ha_peer_ipv4: None + # fmgr_ha_hb_interval: 5 + # fmgr_ha_mode: standalone + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 10, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_hb_interval': 15, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_file_quota': 2048, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': 'master', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 10 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_hb_interval: 15 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_file_quota: 2048 + # fmgr_ha_peer_status: None + # fmgr_ha_peer_ipv4: None + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: master + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': None, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_peer_status': None, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_peer_ipv4': None, + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_mode': 'slave', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: None + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_peer_ipv6: None + # fmgr_ha_peer_status: None + # fmgr_ha_file_quota: 4096 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_peer_ipv4: None + # fmgr_ha_hb_interval: 5 + # fmgr_ha_mode: slave + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': 'FMG-VM0A17004505', + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_cluster_id': 2, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': 'enable', + 'fmgr_ha_peer_ipv4': '10.7.220.35', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': 'slave', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_mode(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: FMG-VM0A17004505 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_hb_interval: 5 + # fmgr_ha_cluster_id: 2 + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: enable + # fmgr_ha_peer_ipv4: 10.7.220.35 + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: slave + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + + +def test_fmgr_get_ha_peer_list(fixture_data, mocker): + mocker.patch( + "pyFMG.fortimgr.FortiManager._post_request", + side_effect=fixture_data) + + paramgram_used = {'method': 'get', 'mode': 'get'} + output = fmgr_ha.fmgr_get_ha_peer_list(fmg_instance) + # + # method: get + # mode: get + # + assert isinstance(output['raw_response'], list) is True + paramgram_used = {'method': 'get', 'mode': 'get'} + output = fmgr_ha.fmgr_get_ha_peer_list(fmg_instance) + # + # method: get + # mode: get + # + assert isinstance(output['raw_response'], list) is True + + +def test_fmgr_set_ha_peer(fixture_data, mocker): + mocker.patch( + "pyFMG.fortimgr.FortiManager._post_request", + side_effect=fixture_data) + + paramgram_used = { + 'fmgr_ha_peer_sn': 'FMG-VM0A17004505', + 'next_peer_id': 1, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_peer_status': 'enable', + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_cluster_id': 2, + 'peer_id': 1, + 'fmgr_ha_peer_ipv4': '10.7.220.35', + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_mode': 'slave', + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_peer(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: FMG-VM0A17004505 + # next_peer_id: 1 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_peer_ipv6: None + # fmgr_ha_peer_status: enable + # fmgr_ha_file_quota: 4096 + # fmgr_ha_cluster_id: 2 + # peer_id: 1 + # fmgr_ha_peer_ipv4: 10.7.220.35 + # fmgr_ha_hb_interval: 5 + # fmgr_ha_mode: slave + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': 'FMG-VM0A17005528', + 'next_peer_id': 1, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': None, + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_cluster_id': 1, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': 'enable', + 'peer_id': 1, + 'fmgr_ha_peer_ipv4': '10.7.220.36', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': None, + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_peer(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: FMG-VM0A17005528 + # next_peer_id: 1 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: None + # fmgr_ha_hb_interval: 5 + # fmgr_ha_cluster_id: 1 + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: enable + # peer_id: 1 + # fmgr_ha_peer_ipv4: 10.7.220.36 + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: None + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_peer_sn': 'FMG-VM0A17005528', + 'next_peer_id': 1, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': None, + 'fmgr_ha_hb_interval': 5, + 'fmgr_ha_cluster_id': 1, + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': 'enable', + 'peer_id': 1, + 'fmgr_ha_peer_ipv4': '10.7.220.36', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': None, + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_peer(fmg_instance, paramgram_used) + # + # fmgr_ha_peer_sn: FMG-VM0A17005528 + # next_peer_id: 1 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: None + # fmgr_ha_hb_interval: 5 + # fmgr_ha_cluster_id: 1 + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: enable + # peer_id: 1 + # fmgr_ha_peer_ipv4: 10.7.220.36 + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: None + # mode: set + # + assert output['raw_response']['status']['code'] == 0 + paramgram_used = { + 'fmgr_ha_file_quota': 4096, + 'fmgr_ha_peer_status': 'enable', + 'fmgr_ha_peer_sn': 'FMG-VM0A17004505', + 'next_peer_id': 1, + 'fmgr_ha_hb_threshold': 3, + 'fmgr_ha_cluster_pw': 'fortinet', + 'fmgr_ha_peer_ipv6': None, + 'fmgr_ha_mode': 'slave', + 'fmgr_ha_cluster_id': 2, + 'peer_id': 1, + 'fmgr_ha_peer_ipv4': '10.7.220.35', + 'fmgr_ha_hb_interval': 5, + 'mode': 'set'} + output = fmgr_ha.fmgr_set_ha_peer(fmg_instance, paramgram_used) + # + # fmgr_ha_file_quota: 4096 + # fmgr_ha_peer_status: enable + # fmgr_ha_peer_sn: FMG-VM0A17004505 + # next_peer_id: 1 + # fmgr_ha_hb_threshold: 3 + # fmgr_ha_cluster_pw: fortinet + # fmgr_ha_peer_ipv6: None + # fmgr_ha_mode: slave + # fmgr_ha_cluster_id: 2 + # peer_id: 1 + # fmgr_ha_peer_ipv4: 10.7.220.35 + # fmgr_ha_hb_interval: 5 + # mode: set + # + assert output['raw_response']['status']['code'] == 0