Daniel Dayley
8ac4d52d5f
I need to finish up some of the F5 features but I'm starting on Aruba anyway
171 lines
6.4 KiB
Python
Executable File
171 lines
6.4 KiB
Python
Executable File
#!/usr/local/bin/python3
|
|
# ifxtool, A python command-line client for infrastructure equipment.
|
|
# November 2019 by Daniel Dayley
|
|
|
|
import os
|
|
import sys
|
|
import requests
|
|
import argparse
|
|
import ipaddress
|
|
import socket
|
|
import yaml
|
|
import json
|
|
import re
|
|
import importlib
|
|
|
|
yaml.Dumper.ignore_aliases = lambda *args : True
|
|
|
|
class IFXTool():
|
|
configdir = os.path.expanduser('~/.config')
|
|
configfilename = 'ifxtool.yml'
|
|
servicetemplate = {'hosts':[],'username':'','key':''}
|
|
configtemplate = {'bluecat':servicetemplate,'f5':servicetemplate,'paloalto':servicetemplate,'aruba':servicetemplate,'openvpn':servicetemplate}
|
|
errorinfo = {}
|
|
warninfo = {}
|
|
namespace = ''
|
|
servicedelegates = []
|
|
totalconfig = {}
|
|
_internal_messages = {}
|
|
_subjecttuples = []
|
|
_dnsmaps = {}
|
|
def __init__(self,delegates,arglist) :
|
|
self.servicedelegates = delegates
|
|
self.namespace = arglist
|
|
# Load configuration
|
|
if arglist.config :
|
|
try:
|
|
self.totalconfig = yaml.safe_load(open(os.path.normpath(arglist.config)))
|
|
|
|
except Exception as exception :
|
|
self.errorinfo.update({"config": "There was a problem reading your config file, aborting."})
|
|
else :
|
|
try:
|
|
self.totalconfig = yaml.safe_load(open(self.configdir + '/' + self.configfilename))
|
|
except FileNotFoundError as exception :
|
|
if not os.path.exists(os.path.normpath(self.configdir)):
|
|
os.makedirs(os.path.normpath(self.configdir))
|
|
if not os.path.exists(os.path.normpath(self.configdir + '/' + self.configfilename)):
|
|
with open(self.configdir + '/' + self.configfilename, 'w') as output :
|
|
yaml.dump(self.configtemplate, output)
|
|
self.errorinfo.update({'config': 'Config file ' + self.configdir + '/' + self.configfilename + ' is empty, aborting.'})
|
|
else :
|
|
self.errorinfo.update({"config": "There was a problem reading your config file, aborting."})
|
|
if self.totalconfig == {} :
|
|
with open(self.configdir + '/' + self.configfilename, 'w') as output :
|
|
yaml.dump(self.configtemplate, output)
|
|
self.errorinfo.udpate({'config': 'Config file ' + self.configdir + '/' + self.configfilename + ' is empty, populating with a template.'})
|
|
|
|
def dispatch(self) :
|
|
|
|
# Resolve subject names
|
|
for item in self.namespace.subjects :
|
|
try:
|
|
quicklookup = ipaddress.ip_address(item)
|
|
subjecttuple = socket.gethostbyname_ex(item)
|
|
# Can't make an IP out of item
|
|
except ValueError :
|
|
try :
|
|
# Lookup with system
|
|
subjecttuple = socket.gethostbyname_ex(item)
|
|
except socket.gaierror :
|
|
# Not a valid host or IP
|
|
self.warninfo.update({"resolution": "Unable to resolve " + item})
|
|
#self.namespace.subjects.remove(item)
|
|
#continue
|
|
subjecttuple = [item,[],['']]
|
|
# Can't make IPv4 out of it
|
|
except socket.gaierror :
|
|
subjecttuple = [ipaddress.ip_address(item).reverse_pointer,[],[str(item)]]
|
|
# Convert mutable types to immutable (to be used as dictionary keys)
|
|
immutablesubjecttuple = tuple([subjecttuple[0],tuple(subjecttuple[1]),tuple(subjecttuple[2])])
|
|
self._dnsmaps.update({immutablesubjecttuple:item})
|
|
self._subjecttuples.append(immutablesubjecttuple)
|
|
|
|
# Stop on initialization error
|
|
if self.errorinfo != {} :
|
|
self._internal_messages['error'] = self.errorinfo
|
|
exit(1)
|
|
|
|
# Dispatch service delegates
|
|
delegatereports = {}
|
|
for delegate in self.servicedelegates :
|
|
delegatename = delegate.__module__.split('plugins.')[1]
|
|
if delegatename in self.totalconfig.keys() :
|
|
delegateconfig = self.totalconfig[delegatename]
|
|
else :
|
|
delegateconfig = {}
|
|
delegateinstance = delegate(delegateconfig,self.namespace,self._subjecttuples,delegatereports)
|
|
delegateresponse = delegateinstance._lookup_subjects()
|
|
if delegateinstance.error != [] :
|
|
self.errorinfo.update({delegatename:delegateinstance.error})
|
|
if delegateresponse:
|
|
delegatereports.update({delegatename:delegateresponse})
|
|
|
|
# Format and display results
|
|
if self.warninfo != {} :
|
|
self._internal_messages['warn'] = self.warninfo
|
|
if self.errorinfo != {} :
|
|
self._internal_messages['error'] = self.errorinfo
|
|
delegatereports.update(self._internal_messages)
|
|
self.print_results(delegatereports)
|
|
|
|
|
|
def get_dc_from_hostname(self,hostname) :
|
|
"""Given a hostname, return the datacenter the host resides in."""
|
|
dcmatch = re.search('^[a-zA-Z]*-([a-zA-Z0-9]*)(-.*)*$',hostname)
|
|
if dcmatch :
|
|
return dcmatch.group(1)
|
|
else :
|
|
return ''
|
|
|
|
def print_results(self,dataset) :
|
|
"""Returns the submitted dataset"""
|
|
finalresult = {}
|
|
if 'error' in dataset or 'warn' in dataset :
|
|
finalresult['status'] = {}
|
|
if 'error' in dataset :
|
|
finalresult['status']['error'] = dataset['error']
|
|
if 'warn' in dataset :
|
|
finalresult['status']['warn'] = dataset['warn']
|
|
for subject in self._subjecttuples :
|
|
subjectname = self._dnsmaps[subject]
|
|
subjectdataset = {}
|
|
for plugin in [x for x in dataset if x != 'error' and x != 'warn'] :
|
|
subjectdataset.update({plugin:dataset[plugin][subject]})
|
|
finalresult.update({subjectname:subjectdataset})
|
|
|
|
|
|
if self.namespace.json :
|
|
print(json.dumps(finalresult))
|
|
else :
|
|
yaml.dump(finalresult,sys.stdout)
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# Load Plugins
|
|
plugindir="plugins"
|
|
pluginfiles = [x for x in os.listdir(plugindir) if x != "servicebase.py" and x.endswith('.py')]
|
|
pluginfiles = sorted(pluginfiles)
|
|
importlib.import_module(plugindir)
|
|
delegates = []
|
|
for pluginfile in pluginfiles :
|
|
delegates.append(getattr(importlib.import_module(plugindir+"."+pluginfile.split('.')[0]),"ServiceDelegate"))
|
|
|
|
# Gather Argument options
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('-c', '--config', action='store', help="Specify a config file (~/.config/ifxtool.yml by default)")
|
|
parser.add_argument('-j', '--json', action='store_true', help="Return results as a json object")
|
|
parser.add_argument('-l', '--link', action='store_true', help="Return physical link information about the subject")
|
|
for delegate in delegates :
|
|
arguments = delegate.get_arguments(delegate)
|
|
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='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()
|
|
|
|
# Dispatch master object
|
|
main = IFXTool(delegates,args)
|
|
main.dispatch()
|