-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcipsearch.py
executable file
·303 lines (230 loc) · 9.78 KB
/
cipsearch.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#!/usr/bin/env python
import paramiko
import getpass
import pyinputplus as pyip
import time
from datetime import datetime
import pandas as pd
from io import StringIO
import concurrent.futures
import tracemalloc
import shelve
'''Performs CUCM phone search based on ip address.
The script presents option to retrieve only registered, unregistered or all phones,
and has option to create a csv file of the output if selected.'''
# ------------------------------------------------------------------------
CMD = 'show risdb query phone\n'
def run_setup():
# for persistent data; saves and retrieves user credentials
# Will prompt user for credentials and for servers running call manager service
# enter servers seperated by comma "," no spaces e.g. 10.10.10.1,10.10.10.2,10.10.10.3
st_setup = pyip.inputYesNo('\nEnter setup ? (yes or no): ')
setup_var = shelve.open('cli_var')
if st_setup == 'yes':
usern = input('username: ')
pphrase = getpass.getpass('password: ')
servers = pyip.inputStr("Enter server IP's seperated by comma ',' :")
servers = servers.split(',')
setup_var['cli_user'] = usern
setup_var['cli_pw'] = pphrase
setup_var['servers'] = servers
setup_var.close()
else:
if ('cli_user' in setup_var) and ('cli_pw' in setup_var):
print('Using saved credentials')
usern = setup_var['cli_user']
pphrase = setup_var['cli_pw']
servers = setup_var['servers']
setup_var.close()
return usern, pphrase, servers
def access_cucm(host, username, password, cmd, port=22, prompt='admin:'):
# Sends command to cucm cli and recieve cli output, returns cli output
recv_data = ''
try: # initiate connection client and connect
connectssh = paramiko.SSHClient()
connectssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
connectssh.connect(host, port, username, password, timeout=10)
time.sleep(.5)
rem_conn = connectssh.invoke_shell()
while True: # wait for prompt
time.sleep(1)
recv_output = rem_conn.recv(8000).decode()
if prompt == recv_output[-6:]:
break
else:
pass
rem_conn.send(cmd) # send command
while True: # recieve till prompt
time.sleep(1)
recv_output = rem_conn.recv(10000000).decode()
recv_data += recv_output
if prompt == recv_output[-6:]:
break
connectssh.close()
except Exception as exc:
recv_data = ''
return recv_data
def rmv_head_tail(data):
''' Removes the table title header and trailer info from cli results'''
try:
data = str(data)
datastr = data.split('\n')
del datastr[0:6]
del datastr[-6:]
return datastr
except Exception as exc:
print(f'Error with head and tail removal: {exc}')
return ('')
def fix_row(row):
''' Edits row to resolve issues with table such as extra field for webex devices
and extra comma's possible in description field. Columns need to be consistent for CSV
to be represented as uniform table or dataframe '''
try:
if 'Automatically created by Webex Hybrid Call Service' in row:
fix = row.replace('unknown, 0', 'unknown')
else:
split_row = row.rsplit(',', 24)
ip_to_end = ','.join(split_row[-24:])
dev_descr = split_row[0].split()
dev_name = dev_descr.pop(0)
descr = (' '.join(dev_descr)).replace(',' , ' ')
new_dev_descr = dev_name + descr
fix = new_dev_descr + ',' + ip_to_end
return(fix)
except Exception as exc:
print(f'Error while fixing row: {exc}')
return ('')
def prepare_table(data):
''' Checks and corrects for inconsistent table columns '''
try:
datastr = rmv_head_tail(data)
for row in datastr:
line = datastr.index(row)
if row.count(',') > 25:
row = fix_row(row)
if row.count(',') > 25:
raise Exception ('error: unable to fix row')
datastr[line] = row
datastr = '\n'.join(datastr)
return datastr
except Exception as exc:
print(f'Error with table data: {exc}')
return ('')
def print_accessing(srvlist):
''' Prints servers that are going to be accessed '''
for serv in srvlist:
time.sleep(1)
print('-> '+ str(serv))
print()
def ask_search_ip(): # get ip address to search
get_ip = input('Enter IP address to search: ')
time.sleep(1)
print(f'\nFind Phone where IP address contains: {get_ip}')
return get_ip
def ask_reg_status(): #choose phone status to display
print('\nSelect Phone status, ', end='')
ask = pyip.inputMenu(['Registered', 'Unregistered' , 'All'], numbered=True)
if ask == 'Registered':
registered = ' reg'
if ask == 'Unregistered':
registered = ' unr'
if ask == 'All':
registered = 'All'
return registered
def ask_to_makefile():
ask = pyip.inputYesNo('\nCreate CSV file of results ? (yes or no): ')
if ask.lower() == 'yes':
makecsv = True
else:
makecsv = False
return makecsv
def concurrent_access(fxn, obj_lst, username,password,cmd):
# Performs concurrent access of servers and executes phone query command
# asks for search ip address, registered phone, and wether to make csv file
srv_echo = obj_lst
with concurrent.futures.ThreadPoolExecutor() as executor:
results = {i: (executor.submit(fxn, i, username,
password,cmd)) for i in obj_lst}
print_accessing(srv_echo)
srch_ip = ask_search_ip()
reg_ph = ask_reg_status()
mk_csv = ask_to_makefile()
print('\nRetrieving please wait...')
return (results, srch_ip, mk_csv, reg_ph)
def check_results(srv_dict):
# check for failed ssh connection by checking result of futures
# retrieves the value of futures and checks if it is an empty string
# returns check status and converted results
check = False
for srv in srv_dict:
data = srv_output[srv].result()
if data == '':
break
else:
check = True
srv_dict[srv] = data
return check, srv_dict
def pd_search(srv_dict, ip_addr, reg):
''' Converts csv string to dataframe
performs search for ip address and returns dataframe of results of search'''
try:
df_agg = pd.DataFrame()
for srv in srv_dict: # goes through dictionary converting values to dataframes and searches them
data = StringIO(srv_dict[srv])
df = pd.read_csv(data)
# setting filter based on response
if reg == ' reg': # this will also catch partially registered phones
filt = ((df[' Ipaddr'].str.contains(ip_addr, na=False)) & (df[' RegStatus'].str.contains('reg')))
if reg == ' unr':
filt = ((df[' Ipaddr'].str.contains(ip_addr, na=False)) & (df[' RegStatus'] == ' unr'))
if reg == 'All':
filt = (df[' Ipaddr'].str.contains(ip_addr, na=False))
df['Server'] = srv # setting 'Server' column with server name
df_search = (df.loc[filt, ['DeviceName', ' Ipaddr', ' RegStatus',' RegStatusChg TimeStamp', ' LastActTimeStamp', 'Server', ' Descr']])
df_agg = df_agg.append(df_search, ignore_index=True) # aggregate results of the search to dataframe
#print(df_agg)
except Exception as exc:
print(f'Panda Error : {exc}')
return (df_agg)
# ----------------------------------------------------------------------------------------------------------------------------------
if __name__ == '__main__':
tracemalloc.start()
try:
print('\n---------- Running CUCM IP Searcher ----------\n')
username, password, cm_servers = run_setup()
print('Accessing..')
print(cm_servers)
now = datetime.now().time() # Display current time
current_time = now.strftime("%H:%M:%S")
print("\nApprox. time of search =", current_time +'\n')
# Get input for seacrhing and presenting search result
ip_search = concurrent_access(access_cucm, cm_servers, username, password, CMD)
print()
srv_output = ip_search[0]
ip_addr = ip_search[1]
make_csv = ip_search[2]
reg_only = ip_search[3]
access, result = check_results(srv_output) # -- check for failed ssh access attempts
if access == True:
# Prepare cucm cli output string to be converted to dataframe
srv_output = result
for server in srv_output:
srv_output[server] = (prepare_table(srv_output[server]))
# Convert to dataframe, search and present results
results_df = pd_search(srv_output, ip_addr, reg_only)
results_df = results_df.sort_values(by=['DeviceName', ' LastActTimeStamp'], ascending=True)
total = results_df.shape[0]
print(results_df.to_string(index=False))
if make_csv == True:
results_df.to_csv('ipphones.csv', index=False)
print(f'\nTotal found: {total}')
else:
print('Error: Issue with address information or access credentials')
except Exception as exc:
print(f'Error : {exc}')
print('Exiting..')
# -- end --
print('Done')
peak = (tracemalloc.get_traced_memory())[1]
print('Peak Memory usage: ' + str(peak) + ' bytes')
done = input('Hit Enter to Quit!')