Hotfix for FortiManager Network Connection Plugin (#62534)
* Hotfix for connection manager persistent connection initialization. * PEP8 Fixes * First Round of fixes requested by Qualthos. * Removed _connection check per Mr. Case. * Somehow the wrong version got sent... should be all good. Co-authored-by: Luke Weighall <lweighall@users.noreply.github.com> Co-authored-by: Nathaniel Case <this.is@nathanielca.se>
This commit is contained in:
parent
156dc68e40
commit
33f136292b
1 changed files with 123 additions and 26 deletions
|
@ -17,6 +17,7 @@
|
|||
#
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
|
@ -26,11 +27,10 @@ author:
|
|||
- Andrew Welsh (@Ghilli3)
|
||||
- Jim Huber (@p4r4n0y1ng)
|
||||
httpapi : fortimanager
|
||||
short_description: HttpApi Plugin for Fortinet FortiManager Appliance or VM
|
||||
short_description: HttpApi Plugin for Fortinet FortiManager Appliance or VM.
|
||||
description:
|
||||
- This HttpApi plugin provides methods to connect to Fortinet FortiManager Appliance or VM via JSON RPC API
|
||||
- This HttpApi plugin provides methods to connect to Fortinet FortiManager Appliance or VM via JSON RPC API.
|
||||
version_added: "2.8"
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
|
@ -58,12 +58,15 @@ class HttpApi(HttpApiBase):
|
|||
self._last_url = None
|
||||
self._last_response_raw = None
|
||||
self._locked_adom_list = list()
|
||||
self._locked_adoms_by_user = list()
|
||||
self._uses_workspace = False
|
||||
self._uses_adoms = False
|
||||
self._adom_list = list()
|
||||
self._logged_in_user = None
|
||||
|
||||
def set_become(self, become_context):
|
||||
"""
|
||||
ELEVATION IS NOT REQUIRED ON FORTINET DEVICES - SKIPPED
|
||||
ELEVATION IS NOT REQUIRED ON FORTINET DEVICES - SKIPPED.
|
||||
:param become_context: Unused input.
|
||||
:return: None
|
||||
"""
|
||||
|
@ -71,7 +74,7 @@ class HttpApi(HttpApiBase):
|
|||
|
||||
def update_auth(self, response, response_data):
|
||||
"""
|
||||
TOKENS ARE NOT USED SO NO NEED TO UPDATE AUTH
|
||||
TOKENS ARE NOT USED SO NO NEED TO UPDATE AUTH.
|
||||
:param response: Unused input.
|
||||
:param response_data Unused_input.
|
||||
:return: None
|
||||
|
@ -79,15 +82,17 @@ class HttpApi(HttpApiBase):
|
|||
return None
|
||||
|
||||
def login(self, username, password):
|
||||
|
||||
"""
|
||||
This function will log the plugin into FortiManager, and return the results.
|
||||
:param username: Username of FortiManager Admin
|
||||
:param password: Password of FortiManager Admin
|
||||
|
||||
:return: Dictionary of status, if it logged in or not.
|
||||
:return: Dictionary of status if it logged in or not.
|
||||
"""
|
||||
self._logged_in_user = username
|
||||
self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, "sys/login/user",
|
||||
passwd=password, user=username,))
|
||||
passwd=password, user=username, ))
|
||||
|
||||
if "FortiManager object connected to FortiManager" in self.__str__():
|
||||
# If Login worked, then inspect the FortiManager for Workspace Mode, and it's system information.
|
||||
|
@ -99,9 +104,6 @@ class HttpApi(HttpApiBase):
|
|||
|
||||
def inspect_fmgr(self):
|
||||
# CHECK FOR WORKSPACE MODE TO SEE IF WE HAVE TO ENABLE ADOM LOCKS
|
||||
|
||||
self.check_mode()
|
||||
# CHECK FOR SYSTEM STATUS -- SHOULD RETURN 0
|
||||
status = self.get_system_status()
|
||||
if status[0] == -11:
|
||||
# THE CONNECTION GOT LOST SOMEHOW, REMOVE THE SID AND REPORT BAD LOGIN
|
||||
|
@ -110,6 +112,11 @@ class HttpApi(HttpApiBase):
|
|||
" Exiting")
|
||||
elif status[0] == 0:
|
||||
try:
|
||||
self.check_mode()
|
||||
if self._uses_adoms:
|
||||
self.get_adom_list()
|
||||
if self._uses_workspace:
|
||||
self.get_locked_adom_list()
|
||||
self._connected_fmgr = status[1]
|
||||
self._host = self._connected_fmgr["Hostname"]
|
||||
except BaseException:
|
||||
|
@ -121,7 +128,9 @@ class HttpApi(HttpApiBase):
|
|||
This function will logout of the FortiManager.
|
||||
"""
|
||||
if self.sid is not None:
|
||||
# IF WE WERE USING WORKSPACES, THEN CLEAN UP OUR LOCKS IF THEY STILL EXIST
|
||||
if self.uses_workspace:
|
||||
self.get_lock_info()
|
||||
self.run_unlock()
|
||||
ret_code, response = self.send_request(FMGRMethods.EXEC,
|
||||
self._tools.format_request(FMGRMethods.EXEC, "sys/logout"))
|
||||
|
@ -130,7 +139,7 @@ class HttpApi(HttpApiBase):
|
|||
|
||||
def send_request(self, method, params):
|
||||
"""
|
||||
Responsible for actual sending of data to the connection httpapi base plugin. Does some formatting as well.
|
||||
Responsible for actual sending of data to the connection httpapi base plugin. Does some formatting too.
|
||||
:param params: A formatted dictionary that was returned by self.common_datagram_params()
|
||||
before being called here.
|
||||
:param method: The preferred API Request method (GET, ADD, POST, etc....)
|
||||
|
@ -138,16 +147,20 @@ class HttpApi(HttpApiBase):
|
|||
|
||||
:return: Dictionary of status, if it logged in or not.
|
||||
"""
|
||||
|
||||
try:
|
||||
if self.sid is None and params[0]["url"] != "sys/login/user":
|
||||
raise FMGBaseException("An attempt was made to login with the SID None and URL != login url.")
|
||||
try:
|
||||
self.connection._connect()
|
||||
except Exception as err:
|
||||
raise FMGBaseException(
|
||||
msg="An problem happened with the httpapi plugin self-init connection process. "
|
||||
"Error: " + to_text(err))
|
||||
except IndexError:
|
||||
raise FMGBaseException("An attempt was made at communicating with a FMG with "
|
||||
"no valid session and an incorrectly formatted request.")
|
||||
except Exception:
|
||||
except Exception as err:
|
||||
raise FMGBaseException("An attempt was made at communicating with a FMG with "
|
||||
"no valid session and an unexpected error was discovered.")
|
||||
"no valid session and an unexpected error was discovered. \n Error: " + to_text(err))
|
||||
|
||||
self._update_request_id()
|
||||
json_request = {
|
||||
|
@ -205,7 +218,7 @@ class HttpApi(HttpApiBase):
|
|||
try:
|
||||
if self._connected_fmgr:
|
||||
return self._connected_fmgr
|
||||
except BaseException:
|
||||
except Exception:
|
||||
raise FMGBaseException("Couldn't Retrieve Connected FMGR Stats")
|
||||
|
||||
def get_system_status(self):
|
||||
|
@ -245,7 +258,7 @@ class HttpApi(HttpApiBase):
|
|||
|
||||
def __str__(self):
|
||||
if self.sid is not None and self.connection._url is not None:
|
||||
return "FortiManager object connected to FortiManager: " + str(self.connection._url)
|
||||
return "FortiManager object connected to FortiManager: " + to_text(self.connection._url)
|
||||
return "FortiManager object with no valid connection to a FortiManager appliance."
|
||||
|
||||
##################################
|
||||
|
@ -281,26 +294,32 @@ class HttpApi(HttpApiBase):
|
|||
Checks FortiManager for the use of Workspace mode
|
||||
"""
|
||||
url = "/cli/global/system/global"
|
||||
code, resp_obj = self.send_request(FMGRMethods.GET, self._tools.format_request(FMGRMethods.GET, url,
|
||||
fields=["workspace-mode",
|
||||
"adom-status"]))
|
||||
code, resp_obj = self.send_request(FMGRMethods.GET,
|
||||
self._tools.format_request(FMGRMethods.GET,
|
||||
url,
|
||||
fields=["workspace-mode", "adom-status"]))
|
||||
try:
|
||||
if resp_obj["workspace-mode"] != 0:
|
||||
if resp_obj["workspace-mode"] == "workflow":
|
||||
self.uses_workspace = True
|
||||
elif resp_obj["workspace-mode"] == "disabled":
|
||||
self.uses_workspace = False
|
||||
except KeyError:
|
||||
self.uses_workspace = False
|
||||
raise FMGBaseException(msg="Couldn't determine workspace-mode in the plugin")
|
||||
try:
|
||||
if resp_obj["adom-status"] == 1:
|
||||
if resp_obj["adom-status"] in [1, "enable"]:
|
||||
self.uses_adoms = True
|
||||
else:
|
||||
self.uses_adoms = False
|
||||
except KeyError:
|
||||
self.uses_adoms = False
|
||||
raise FMGBaseException(msg="Couldn't determine adom-status in the plugin")
|
||||
|
||||
def run_unlock(self):
|
||||
"""
|
||||
Checks for ADOM status, if locked, it will unlock
|
||||
"""
|
||||
for adom_locked in self._locked_adom_list:
|
||||
self.unlock_adom(adom_locked)
|
||||
for adom_locked in self._locked_adoms_by_user:
|
||||
adom = adom_locked["adom"]
|
||||
self.unlock_adom(adom)
|
||||
|
||||
def lock_adom(self, adom=None, *args, **kwargs):
|
||||
"""
|
||||
|
@ -350,6 +369,84 @@ class HttpApi(HttpApiBase):
|
|||
url = "/dvmdb/adom/root/workspace/commit"
|
||||
return self.send_request(FMGRMethods.EXEC, self._tools.format_request(FMGRMethods.EXEC, url))
|
||||
|
||||
def get_lock_info(self, adom=None):
|
||||
"""
|
||||
Gets ADOM lock info so it can be displayed with the error messages. Or if determined to be locked by ansible
|
||||
for some reason, then unlock it.
|
||||
"""
|
||||
if not adom or adom == "root":
|
||||
url = "/dvmdb/adom/root/workspace/lockinfo"
|
||||
else:
|
||||
if adom.lower() == "global":
|
||||
url = "/dvmdb/global/workspace/lockinfo/"
|
||||
else:
|
||||
url = "/dvmdb/adom/{adom}/workspace/lockinfo/".format(adom=adom)
|
||||
datagram = {}
|
||||
data = self._tools.format_request(FMGRMethods.GET, url, **datagram)
|
||||
resp_obj = self.send_request(FMGRMethods.GET, data)
|
||||
code = resp_obj[0]
|
||||
if code != 0:
|
||||
self._module.fail_json(msg=("An error occurred trying to get the ADOM Lock Info. "
|
||||
"Error: " + to_text(resp_obj)))
|
||||
elif code == 0:
|
||||
try:
|
||||
if resp_obj[1]["status"]["message"] == "OK":
|
||||
self._lock_info = None
|
||||
except Exception:
|
||||
self._lock_info = resp_obj[1]
|
||||
return resp_obj
|
||||
|
||||
def get_adom_list(self):
|
||||
"""
|
||||
Gets the list of ADOMs for the FortiManager
|
||||
"""
|
||||
if self.uses_adoms:
|
||||
url = "/dvmdb/adom"
|
||||
datagram = {}
|
||||
data = self._tools.format_request(FMGRMethods.GET, url, **datagram)
|
||||
resp_obj = self.send_request(FMGRMethods.GET, data)
|
||||
code = resp_obj[0]
|
||||
if code != 0:
|
||||
self._module.fail_json(msg=("An error occurred trying to get the ADOM Info. "
|
||||
"Error: " + to_text(resp_obj)))
|
||||
elif code == 0:
|
||||
num_of_adoms = len(resp_obj[1])
|
||||
append_list = ['root', ]
|
||||
for adom in resp_obj[1]:
|
||||
if adom["tab_status"] != "":
|
||||
append_list.append(to_text(adom["name"]))
|
||||
self._adom_list = append_list
|
||||
return resp_obj
|
||||
|
||||
def get_locked_adom_list(self):
|
||||
"""
|
||||
Gets the list of locked adoms
|
||||
"""
|
||||
try:
|
||||
locked_list = list()
|
||||
locked_by_user_list = list()
|
||||
for adom in self._adom_list:
|
||||
adom_lock_info = self.get_lock_info(adom=adom)
|
||||
try:
|
||||
if adom_lock_info[1]["status"]["message"] == "OK":
|
||||
continue
|
||||
except IndexError as err:
|
||||
pass
|
||||
try:
|
||||
if adom_lock_info[1][0]["lock_user"]:
|
||||
locked_list.append(to_text(adom))
|
||||
if adom_lock_info[1][0]["lock_user"] == self._logged_in_user:
|
||||
locked_by_user_list.append({"adom": to_text(adom),
|
||||
"user": to_text(adom_lock_info[1][0]["lock_user"])})
|
||||
except Exception as err:
|
||||
raise FMGBaseException(err)
|
||||
self._locked_adom_list = locked_list
|
||||
self._locked_adoms_by_user = locked_by_user_list
|
||||
|
||||
except Exception as err:
|
||||
raise FMGBaseException(msg=("An error occurred while trying to get the locked adom list. Error: "
|
||||
+ to_text(err)))
|
||||
|
||||
################################
|
||||
# END DATABASE LOCK CONTEXT CODE
|
||||
################################
|
||||
|
|
Loading…
Reference in a new issue