ifxlookup/plugins/aruba.py

158 lines
8.5 KiB
Python

from servicebase import ServiceBase
import requests
import xml.etree.ElementTree as ElementTree
class ServiceDelegate(ServiceBase) :
_host = None
_api_session = requests.session()
_session_key = None
_query_page_size = 200
_clients_fields = ['sta_mac_address', 'client_ht_phy_type', 'openflow_state', 'client_ip_address', 'client_user_name', 'client_dev_type', 'client_ap_location', 'client_conn_port', 'client_conn_type', 'client_timestamp', 'client_role_name', 'client_active_uac', 'client_standby_uac', 'ap_cluster_name', 'client_health', 'total_moves', 'successful_moves', 'steer_capability', 'ssid', 'ap_name', 'channel', 'channel_str', 'channel_busy', 'tx_time', 'rx_time', 'channel_free', 'channel_interference', 'current_channel_utilization', 'radio_band', 'bssid', 'speed', 'max_negotiated_rate', 'noise_floor', 'radio_ht_phy_type', 'snr', 'total_data_frames', 'total_data_bytes', 'avg_data_rate', 'tx_avg_data_rate', 'rx_avg_data_rate', 'tx_frames_transmitted', 'tx_frames_dropped', 'tx_bytes_transmitted', 'tx_bytes_dropped', 'tx_time_transmitted', 'tx_time_dropped', 'tx_data_transmitted', 'tx_data_dropped', 'tx_data_retried', 'tx_data_transmitted_retried', 'tx_data_bytes_transmitted', 'tx_abs_data_bytes', 'tx_data_bytes_dropped', 'tx_time_data_transmitted', 'tx_time_data_dropped', 'tx_mgmt', 'rx_frames', 'rx_bytes', 'rx_data', 'rx_data_bytes', 'rx_abs_data_bytes', 'rx_data_retried', 'tx_data_frame_rate_dist', 'rx_data_frame_rate_dist', 'tx_data_bytes_rate_dist', 'rx_data_bytes_rate_dist', 'connection_type_classification', 'total_data_throughput', 'tx_data_throughput', 'rx_data_throughput', 'client_auth_type', 'client_auth_subtype', 'client_encrypt_type', 'client_fwd_mode']
_aps_fields = ['mon_ap', 'mon_bssid', 'mon_radio_phy_type', 'mon_ssid', 'mon_radio_band', 'mon_ap_current_channel', 'mon_ht_sec_channel', 'mon_sta_count', 'mon_ap_classification', 'mon_ap_match_conf_level', 'mon_ap_encr', 'mon_ap_encr_auth', 'mon_ap_encr_cipher', 'mon_ap_is_dos', 'mon_ap_type', 'mon_ap_status', 'mon_is_ibss', 'mon_ap_create_time', 'mon_ap_match_type', 'mon_ap_match_method', 'mon_ap_match_name', 'mon_ap_match_time', 'wms_event_count']
_justip_fields = ['client_ip_address']
_aruba_hosts = []
_aruba_basestations = []
def get_arguments(cls) :
"""Returns an array of information used to construct an argumentparser argument."""
return ['-w', '--wifi', 'store_true', 'Return wireless connection information about the subject (aruba)']
def startup(self) :
for requirement in ['host','username','key'] :
if requirement not in self._config or (requirement in self._config and (self._config[requirement] == '' or type(self._config[requirement]) != str)):
self._error.append('Missing required config option ' + requirement)
return
self._host = self._config['host']
self.start_session(self._config['host'],self._config['username'],self._config['key'])
clientsquery = 'backend-observer-sta-19'
clientssortfield = 'client_user_name'
clientsfilter = 'client_conn_type not_equals 0'
clientsdevicetype = 'sta'
apsquery = 'backend-observer-mon_bssid-67'
apssortfield = 'mon_ssid'
apsfilter = 'mon_ap_status equals 1'
apsdevicetype = 'mon_bssid'
self.debug('Retrieving list of Aruba\'s detected basestations...',1)
self._aruba_basestations = self.perform_list_query(apsquery,self._aps_fields,apssortfield,apsfilter,apsdevicetype)
self.debug('Retrieving list of Aruba connected clients...',1)
self._aruba_hosts = self.perform_list_query(clientsquery,self._clients_fields,clientssortfield,clientsfilter,clientsdevicetype)
self.debug('Searching Aruba information...',1)
def lookup(self,subject) :
# We return the first result that matches the subject out of our lists of Aruba information.
# Because this isn't very effective we need to order our search by order of least-likely to
# most-likely for the match.
# Search APs first, since they are less likely to match
# By IP
if 'dns' in self.hints and subject in self.hints['dns'] and self.hints['dns'][subject]['addresses'] :
for ip in self.hints['dns'][subject]['addresses'] :
for entry in self._aruba_basestations :
if ip in entry.values() :
return entry
# By value
for entry in self._aruba_basestations :
lowercase_values = [x.lower() for x in entry.values() if type(x) == str]
all_values = lowercase_values + [x for x in entry.values() if type(x) != str]
for value in all_values :
if subject.lower() in str(value) :
return entry
# Search connected Aruba hosts
# By IP
if 'dns' in self.hints and subject in self.hints['dns'] and self.hints['dns'][subject]['addresses'] :
for ip in self.hints['dns'][subject]['addresses'] :
for entry in self._aruba_hosts :
if ip in entry.values() :
return entry
# By value
for entry in self._aruba_hosts :
lowercase_values = [x.lower() for x in entry.values() if type(x) == str]
all_values = lowercase_values + [x for x in entry.values() if type(x) == str]
for value in all_values :
if subject.lower() in str(value) :
return entry
# Not found
return None
def start_session(self,host,username,key) :
headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
data = 'opcode=login&uid=' + username + '&passwd=' + key
self._api_session.post('https://' + host + '/screens/wms/wms.login',verify=False,headers=headers,data=data)
if 'SESSION' in self._api_session.cookies.get_dict() :
self._session_key = self._api_session.cookies.get_dict()['SESSION']
def perform_list_query(self,queryname,columnlist,sortfield,queryfilter,devicetype) :
""""Performs a list-type XML query against the Aruba UI API"""
# So uncivilized.
# Build the basic object for our list query. Takes the parameters that the Aruba controller needs for it's
# XML api, performs the query, and returns a list of results.
sortdir = 'asc'
aruba_queries = ElementTree.Element('aruba_queries')
query = ElementTree.SubElement(aruba_queries,'query')
qname = ElementTree.SubElement(query,'qname')
qname.text = queryname
type = ElementTree.SubElement(query,'type')
type.text = 'list'
list_query = ElementTree.SubElement(query,'list_query')
device_type = ElementTree.SubElement(list_query,'device_type')
device_type.text = devicetype
requested_columns = ElementTree.SubElement(list_query,'requested_columns')
requested_columns.text = ' '.join(columnlist)
sort_by_field = ElementTree.SubElement(list_query,'sort_by_field')
sort_by_field.text = sortfield
sort_order = ElementTree.SubElement(list_query,'sort_order')
sort_order.text = sortdir
pagination = ElementTree.SubElement(list_query,'pagination')
start_row = ElementTree.SubElement(pagination,'start_row')
num_rows = ElementTree.SubElement(pagination,'num_rows')
filter = ElementTree.SubElement(query,'filter')
global_operator = ElementTree.SubElement(filter,'global_operator')
global_operator.text = 'and'
filter_list = ElementTree.SubElement(filter,'filter_list')
filter_item_entry = ElementTree.SubElement(filter_list,'filter_item_entry')
field_name = ElementTree.SubElement(filter_item_entry,'field_name')
field_name.text = queryfilter.split(' ')[0]
comp_operator = ElementTree.SubElement(filter_item_entry,'comp_operator')
comp_operator.text = queryfilter.split(' ')[1]
value = ElementTree.SubElement(filter_item_entry,'value')
value.text = queryfilter.split(' ')[2]
# Repeat with page size on the query, aggregating results
nextstart = 0
finished = False
allitems = []
while not finished :
start_row.text = str(nextstart)
num_rows.text = str(self._query_page_size)
datapayload = ''.join(['query=',ElementTree.tostring(aruba_queries).decode(),'&UIDARUBA=',self._session_key])
page = self._api_session.post('https://' + self._host + '/screens/cmnutil/execUiQuery.xml',verify=False,timeout=1,data=datapayload)
self.debug('Got a page of results from Aruba host...',3)
if page.status_code == 100 or page.status_code == 200 :
pagecontents = ElementTree.fromstring(page.text)
rows = pagecontents.iter('row')
for row in rows:
if row == {} :
finished = True
item = {}
headers = pagecontents.iter('column_name')
for value, header in zip(row,headers) :
item.update({header.text: value.text})
allitems.append(item)
# Abort when we get no more rows
try :
rows = pagecontents.iter('row')
next(rows)
except Exception :
finished = True
if page.status_code != 100 and page.status_code !=200 :
finished = True
nextstart = nextstart + self._query_page_size
else :
finished = True
self.debug('Retrieved ' + str((len(allitems))) + ' results from query',2)
return allitems