Added debugging infrastrcuture, working on error reporting
This commit is contained in:
parent
d233a12a5e
commit
8d578dc9bf
2
ifxtool
2
ifxtool
@ -159,7 +159,7 @@ if __name__ == "__main__":
|
||||
if arguments and len(arguments) == 4 :
|
||||
parser.add_argument(arguments[0],arguments[1],action=arguments[2],help=arguments[3])
|
||||
parser.add_argument('-a', '--all', action='store_true', help="Return all searchable information about the subject")
|
||||
parser.add_argument('-v', '--debug', action='store_true', help="Include debug information in the output")
|
||||
parser.add_argument('-v', '--debug', action='count', help="Include debug information in the output. Add 'v's for more output.")
|
||||
parser.add_argument('subjects', metavar='subjects', nargs='+', help='IPs or hostnames to look up.')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -14,18 +14,14 @@ class ServiceDelegate(ServiceBase) :
|
||||
def startup(self) :
|
||||
for requirement in ['host','username','key'] :
|
||||
if requirement not in self.config or (requirement in self.config and (self.config[requirement] is '' or type(self.config[requirement]) is not str)):
|
||||
self.return_payload.update({'error':'Missing required config option ' + requirement})
|
||||
self.error.append('Missing required config option ' + requirement)
|
||||
return
|
||||
self.host = self.config['host']
|
||||
self.current_auth_header = self.get_bc_auth_header(self.config['host'],self.config['username'],self.config['key'])
|
||||
self._debug("Searching BlueCat for hosts...",1)
|
||||
|
||||
def perform_lookup(self,host_tuple) :
|
||||
error = None
|
||||
if self.return_payload and 'error' in self.return_payload :
|
||||
error = self.return_payload['error']
|
||||
self.return_payload = {}
|
||||
if error :
|
||||
self.return_payload.update({'error':error})
|
||||
if self.current_auth_header :
|
||||
object = self.search_bc_object(host_tuple)
|
||||
self.return_payload.update({'object':object})
|
||||
@ -39,7 +35,7 @@ class ServiceDelegate(ServiceBase) :
|
||||
if self.host and self.current_auth_header :
|
||||
logout = requests.get("https://" + self.host + "/Services/REST/v1/logout",headers=self.current_auth_header)
|
||||
if not logout.text.endswith("successfully logged out.\"") :
|
||||
self.return_payload.update({'error':'Unable to log out of BlueCat API session'})
|
||||
self.error.append('Unable to log out of BlueCat API session')
|
||||
|
||||
|
||||
def get_bc_auth_header(self,host,username,password) :
|
||||
|
137
plugins/f5.py
137
plugins/f5.py
@ -1,13 +1,16 @@
|
||||
from servicebase import ServiceBase
|
||||
import requests
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
|
||||
class ServiceDelegate(ServiceBase) :
|
||||
|
||||
current_auth_header = None
|
||||
return_payload = {}
|
||||
hosts = None
|
||||
_partition = 'Common'
|
||||
|
||||
def get_arguments(cls) :
|
||||
return ['-cg', '--vip', 'store_true', "Return VIP information about the subject"]
|
||||
@ -15,54 +18,104 @@ class ServiceDelegate(ServiceBase) :
|
||||
def startup(self) :
|
||||
for requirement in ['hosts','username','key'] :
|
||||
if requirement not in self.config or (requirement in self.config and self.config[requirement] is ''):
|
||||
self.return_payload.update({'error':'Missing required config option ' + requirement})
|
||||
self.error.appendupdate('Missing required config option ' + requirement)
|
||||
return
|
||||
self.hosts = self.config['hosts']
|
||||
#self.current_auth_header = self.get_bc_auth_header(self.config['host'],self.config['username'],self.config['key'])
|
||||
self._debug("Logging into F5's and searching for hosts, this make take some time.",1)
|
||||
|
||||
def perform_lookup(self,host_tuple) :
|
||||
error = None
|
||||
if self.return_payload and 'error' in self.return_payload :
|
||||
error = self.return_payload['error']
|
||||
self.return_payload = {}
|
||||
if error :
|
||||
self.return_payload.update({'error':error})
|
||||
self.return_payload.update({'lookupcandidates':self.choose_host(host_tuple)})
|
||||
|
||||
|
||||
return self.return_payload
|
||||
if self.current_auth_header :
|
||||
object = self.search_bc_object(host_tuple)
|
||||
self.return_payload.update({'object':object})
|
||||
parent = self.search_bc_parent_object(host_tuple)
|
||||
self.return_payload.update({'parent':parent})
|
||||
logincandidates = self.choose_host(host_tuple)
|
||||
if logincandidates == {} :
|
||||
# Idk what to do here, do we give up or do we log in to all of them?
|
||||
self.error.append('Unable to find an LB to log into to check this host')
|
||||
return self.return_payload
|
||||
|
||||
for host in logincandidates.keys() :
|
||||
auth_header = self.get_iq_auth_header(host,self.config['username'],self.config['key'])
|
||||
if not auth_header :
|
||||
self.error.append('Unable to log in to ' + host)
|
||||
continue
|
||||
# Get all VIPs
|
||||
if 'vip' not in self.return_payload :
|
||||
vips = self.get_vip(host,auth_header);
|
||||
# If it's a VIP, return it and the host and let's be done
|
||||
if vips :
|
||||
self._debug("Got vips from " + host,3)
|
||||
vip_match = [ x for x in vips if x['destination'].split('/' + self._partition + '/')[1].startswith(host_tuple[2][0])]
|
||||
all_vips = [ x['destination'] for x in vips ]
|
||||
if len(vip_match) > 0 :
|
||||
selected_vip = vip_match[0]
|
||||
self.return_payload.update({'vip': selected_vip})
|
||||
pool_name = selected_vip['pool'].split('/' + self._partition + '/')[1]
|
||||
pool_info = self.get_pool(host,auth_header,pool_name)
|
||||
if pool_info :
|
||||
self.return_payload.update(pool_info)
|
||||
break
|
||||
else :
|
||||
self._debug("Unable to find VIP in VIPs from host " + host,2)
|
||||
else :
|
||||
self._debug("Unable to get VIPs from " + host,2)
|
||||
# If it's a VIP, return it and the host and let's be done
|
||||
# Otherwise, get all nodes.
|
||||
# If it's in the nodes, we have to start all over and find where.
|
||||
# Take all vips and get all pools
|
||||
# Check if address is in the pool. If it is, return the node, pool, vip, and host.
|
||||
if self.return_payload == {} :
|
||||
self.return_payload = None
|
||||
return self.return_payload
|
||||
|
||||
def shutdown(self) :
|
||||
return
|
||||
if self.host and self.current_auth_header :
|
||||
logout = requests.get("https://" + self.host + "/Services/REST/v1/logout",headers=self.current_auth_header)
|
||||
if not logout.text.endswith("successfully logged out.\"") :
|
||||
self.return_payload.update({'error':'Unable to log out of BlueCat API session'})
|
||||
|
||||
|
||||
def get_bc_auth_header(self,host,username,password) :
|
||||
auth_token = requests.get("https://" + host + "/Services/REST/v1/login?username=" + username + "&password=" + password).text[17:71]
|
||||
if auth_token.startswith('BAMAuthToken') :
|
||||
return { "Authorization" : auth_token }
|
||||
def get_iq_auth_header(self,host,username,password) :
|
||||
assumed_login_provider = "tmos"
|
||||
token = None
|
||||
try :
|
||||
payload = { "username" : username, "password" : password, "loginProviderName" : assumed_login_provider }
|
||||
login = requests.post('https://' + host + '/mgmt/shared/authn/login', json=payload,verify=False,timeout=2)
|
||||
if 'token' in json.loads(login.text) :
|
||||
token = json.loads(login.text)['token']['token']
|
||||
except Exception as exception:
|
||||
self._debug("Exception getting auth header for host " + host + ": \n" + str(exception),2)
|
||||
return None
|
||||
if token :
|
||||
return { "X-F5-Auth-Token" : token }
|
||||
else :
|
||||
return None
|
||||
|
||||
def search_bc_object(self,host_tuple) :
|
||||
"""Searches BC for the subject."""
|
||||
response = requests.get("https://" + self.host + "/Services/REST/v1/searchByObjectTypes?keyword=" + host_tuple[2][0] + "&types=IP4Address,IP6Address&start=0&count=10",headers=self.current_auth_header)
|
||||
return self.clean_response(response)
|
||||
def get_vip(self,host,header) :
|
||||
"""Given a host and an authorization header, gets all the vips for that host"""
|
||||
# Please don't give me empty auth headers, I don't want to check it everywhere
|
||||
self._debug("Trying VIP info for " + host,2)
|
||||
try :
|
||||
vips = requests.get("https://" + host + '/mgmt/tm/ltm/virtual',headers=header,verify=False,timeout=10)
|
||||
if vips.status_code == 200 :
|
||||
return json.loads(vips.text)['items']
|
||||
self._debug("Response not OK for " + vips.history[0].url,3)
|
||||
return None
|
||||
except Exception as exception :
|
||||
self._debug("Exception getting VIPs for host " + host + ": \n" + str(exception),2)
|
||||
return None
|
||||
|
||||
def search_bc_parent_object(self,host_tuple) :
|
||||
"""Search for and return information about the objects parent, if any"""
|
||||
if 'object' in self.return_payload and 'id' in self.return_payload['object'] :
|
||||
response = requests.get("https://" + self.host + "/Services/REST/v1/getParent?entityId=" + str(self.return_payload['object']['id']), headers=self.current_auth_header)
|
||||
return self.clean_response(response)
|
||||
def get_pool(self,host,header,pool) :
|
||||
"""Given a host, auth header, and pool, return the pool and it's members from the host"""
|
||||
try :
|
||||
pools = requests.get("https://" + host + "/mgmt/tm/ltm/pool/~" + self._partition + "~" + pool,headers=header,verify=False,timeout=5)
|
||||
if pools.status_code == 200 :
|
||||
poolobj = json.loads(pools.text)
|
||||
members = requests.get("https://" + host + "/mgmt/tm/ltm/pool/~" + self._partition + "~" + pool + "/members",headers=header,verify=False,timeout=10)
|
||||
if members.status_code == 200 :
|
||||
membersobj = json.loads(members.text)['items']
|
||||
return {'pool':poolobj,'members':membersobj}
|
||||
return {'pool':poolobj}
|
||||
elf._debug("Response not OK for " + pool.history[0].url,3)
|
||||
return None
|
||||
except Exception as exception:
|
||||
self._debug("Exception getting pool for host " + host + ": \n" + str(exception),2)
|
||||
return None
|
||||
|
||||
def choose_host(self,host_tuple) :
|
||||
"""Given a host_tuple, return the host that we suspect is responsible for the VIP resides in."""
|
||||
@ -88,21 +141,3 @@ class ServiceDelegate(ServiceBase) :
|
||||
return hosts
|
||||
return hosts
|
||||
|
||||
def clean_response(self,response) :
|
||||
parsed = {}
|
||||
try :
|
||||
parsed = json.loads(response.text)
|
||||
except Exception :
|
||||
return parsed
|
||||
if type(parsed) is list and len(parsed) > 0 :
|
||||
parsed = parsed[0]
|
||||
if type(parsed) is dict :
|
||||
if 'properties' in parsed :
|
||||
parsed["properties"] = parsed["properties"][:-1]
|
||||
bad_delim_props = [x for x in parsed["properties"].split("|") if x.count('=') > 1]
|
||||
if len(bad_delim_props) > 0 :
|
||||
parsed.update({"unknown":bad_delim_props})
|
||||
attributes = dict(x.split("=") for x in parsed["properties"].split("|") if x.count('=') == 1)
|
||||
parsed.pop("properties", None)
|
||||
parsed.update(attributes)
|
||||
return parsed
|
||||
|
@ -1,7 +1,11 @@
|
||||
import sys
|
||||
|
||||
class ServiceBase :
|
||||
config = {}
|
||||
namespace = {}
|
||||
data = {}
|
||||
error = []
|
||||
warn = []
|
||||
_status = "Ready"
|
||||
_subjects = []
|
||||
def __init__(self,config,namespace,subjects,dossiercopy):
|
||||
@ -27,6 +31,10 @@ class ServiceBase :
|
||||
self.shutdown()
|
||||
return finaldictionary
|
||||
|
||||
def _debug(self,message,verbosity_level) :
|
||||
if self.namespace.debug and self.namespace.debug >= verbosity_level :
|
||||
print(message,file=sys.stderr)
|
||||
|
||||
@classmethod
|
||||
def get_arguments(cls) :
|
||||
"""Returns an array of information used to construct an argumentparser argument."""
|
||||
|
Loading…
Reference in New Issue
Block a user