diff --git a/ifxtool b/ifxtool index 95d7c8c..e5b85d3 100755 --- a/ifxtool +++ b/ifxtool @@ -24,7 +24,7 @@ class IFXTool(): warninfo = {} namespace = '' servicedelegates = [] - totalconfig = {} + totalconfig = None _internal_messages = {} _subjecttuples = [] _dnsmaps = {} @@ -35,7 +35,6 @@ class IFXTool(): 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 : @@ -50,10 +49,12 @@ class IFXTool(): 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."}) + except Exception as exception : + 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.'}) + self.errorinfo.update({'config': 'Config file ' + self.configdir + '/' + self.configfilename + ' is empty, populating with a template.'}) def dispatch(self) : @@ -77,7 +78,7 @@ class IFXTool(): 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])]) + immutablesubjecttuple = tuple([subjecttuple[0].lower(),tuple(subjecttuple[1]),tuple(subjecttuple[2])]) self._dnsmaps.update({immutablesubjecttuple:item}) self._subjecttuples.append(immutablesubjecttuple) diff --git a/plugins/aruba.py b/plugins/aruba.py index 2511919..9359c4a 100644 --- a/plugins/aruba.py +++ b/plugins/aruba.py @@ -1,14 +1,124 @@ from servicebase import ServiceBase +import requests +from xml.etree.ElementTree import Element, SubElement, Comment, tostring +import xml.etree.ElementTree as ElementTree + class ServiceDelegate(ServiceBase) : + host = None + api_session = requests.session() + session_key = None + query_page_size = 200 + bigdata_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'] + justip_fields = ['client_ip_address'] + aruba_hosts = [] + def get_arguments(cls) : """Returns an array of information used to construct an argumentparser argument.""" - # [ ,,, ] - # Example return: [ '-n', '--net', 'store_true', "Return network information about the subject" ] return ['-w', '--wifi', 'store_true', "Return wireless connection information about the subject"] - def get_ap_list() : - """Returns the access points available from the aruba controllers.""" - pass + 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.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']) + listquery = 'backend-observer-sta-19' + sortfield = 'client_user_name' + self._debug("Retrieving list of Aruba hosts...",1) + self.perform_list_query(listquery,self.bigdata_fields,sortfield) + self._debug("Searching Aruba hosts...",1) + + def perform_lookup(self,host_tuple) : + # Lookup by IP + for ip in host_tuple[2] : + for entry in self.aruba_hosts : + if ip in entry.values() : + return entry + # Lookup by hostname + for entry in self.aruba_hosts : + if host_tuple[0] in entry.values() : + return entry + return {} + + 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) : + """Performs a list-type XML query against the Aruba UI API""" + # So uncivilized. + # Build the basic object for our list query + sortdir = 'asc' + aruba_queries = Element('aruba_queries') + query = SubElement(aruba_queries,'query') + qname = SubElement(query,'qname') + qname.text = queryname + type = SubElement(query,'type') + type.text = 'list' + + list_query = SubElement(query,'list_query') + device_type = SubElement(list_query,'device_type') + device_type.text = 'sta' + requested_columns = SubElement(list_query,'requested_columns') + requested_columns.text = ' '.join(self.bigdata_fields) + sort_by_field = SubElement(list_query,'sort_by_field') + sort_by_field.text = sortfield + sort_order = SubElement(list_query,'sort_order') + sort_order.text = sortdir + pagination = SubElement(list_query,'pagination') + start_row = SubElement(pagination,'start_row') + num_rows = SubElement(pagination,'num_rows') + + filter = SubElement(query,'filter') + global_operator = SubElement(filter,'global_operator') + global_operator.text = 'and' + filter_list = SubElement(filter,'filter_list') + filter_item_entry = SubElement(filter_list,'filter_item_entry') + field_name = SubElement(filter_item_entry,'field_name') + field_name.text = 'client_conn_type' + comp_operator = SubElement(filter_item_entry,'comp_operator') + comp_operator.text = 'not_equals' + value = SubElement(filter_item_entry,'value') + value.text = '0' + + # Repeat with page size on the query, aggregating results + nextstart = 0 + finish = False + allitems = [] + while not finish : + start_row.text = str(nextstart) + num_rows.text = str(self.query_page_size) + datapayload = "".join(['query=',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 == {} : + finish = Tru + 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 : + finish = True + if page.status_code != 100 and page.status_code !=200 : + finish = True + nextstart = nextstart + self.query_page_size + else : + finish = True + self.aruba_hosts = allitems + self._debug("Retrieved " + str((len(allitems))) + " hosts from Aruba host",2)