@ -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 Base Exception:
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
except KeyError :
elif resp_obj [ " workspace-mode " ] == " disabled " :
self . uses_workspace = False
except KeyError :
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
except KeyError :
else :
self . uses_adoms = False
except KeyError :
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
################################