Guest User

Untitled

a guest
Nov 22nd, 2018
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.70 KB | None | 0 0
  1. from netmiko import ConnectHandler
  2. from netmiko.ssh_exception import NetMikoAuthenticationException, NetMikoTimeoutException
  3. from colorama import init
  4. from colorama import Fore
  5. import sys
  6. import re
  7. import getpass
  8.  
  9.  
  10. class HealthCheck:
  11. """ health check - performs traceroute, ping test, and
  12. chosen commands on the devices on the path """
  13.  
  14. def __init__(self):
  15. # variable initialization
  16. self.start_device, self.destination_device = self.ask_user_devices()
  17. self.trace_command = "traceroute " + self.destination_device
  18. self.logname = self.ask_user_for_log()
  19. self.print_screen = self.ask_user_if_print_to_screen()
  20. self.auto_run = self.ask_user_auto_run()
  21. self.user, self.password = self.user_credentials_prompt()
  22. self.traceroute_output = """ """
  23. self.network_device_param = {}
  24. self.net_connect = None
  25. self.hop_list = []
  26. self.command_list = []
  27.  
  28. # log data
  29. self.log = {}
  30.  
  31. def user_credentials_prompt(self):
  32. """ user credentials prompt """
  33.  
  34. print("\nPlease type in your router credentials.")
  35.  
  36. # user credential prompt
  37. user = input('User: ')
  38. password = getpass.getpass('Password: ')
  39. # secret = getpass.getpass('Secret: ')
  40.  
  41. return user, password
  42.  
  43. def ask_user_for_log(self):
  44. """ ask the user what the log file will be named """
  45.  
  46. log_name = input("\nPlease enter a name for the log file: ")
  47.  
  48. if log_name.endswith('.txt'):
  49. return log_name.strip()
  50. else:
  51. return log_name.strip() + '.txt'
  52.  
  53. def user_verify(self, message, sev=0):
  54. """ takes input in form of string and optional severity value. Verifies
  55. whether user entered y or n"""
  56.  
  57. # print in red if severity is 1
  58. if sev == 1:
  59. user_input = input(Fore.RED + message + Fore.WHITE)
  60. else:
  61. user_input = input(message)
  62.  
  63. if user_input.lower() == 'y' or user_input.lower() == 'yes':
  64. return True
  65. else:
  66. return False
  67.  
  68. def ask_user_if_print_to_screen(self):
  69. """ ask user if they want to print output to the screen too """
  70.  
  71. return self.user_verify("\nPrint to the screen? (y/n) ")
  72.  
  73. def ask_user_auto_run(self):
  74. """ ask user if they want to execute script in auto mode """
  75.  
  76. return self.user_verify("\nWould you like to execute script in auto mode? (y/n) ")
  77.  
  78. def validate_address(self, s):
  79. """ validates the address is valid IP or hostname (just abc char for now) """
  80.  
  81. if s == "":
  82. return False
  83.  
  84. if s[0].isalpha():
  85. return True
  86.  
  87. a = s.split('.')
  88. if len(a) != 4:
  89. return False
  90. for x in a:
  91. if not x.isdigit():
  92. return False
  93. i = int(x)
  94. if i < 0 or i > 255:
  95. return False
  96. return True
  97.  
  98. def ask_user_devices(self):
  99. """ ask user for starting device and destination device """
  100.  
  101. start_device = input('Enter starting device address: ').strip(' ')
  102.  
  103. while not self.validate_address(start_device):
  104. start_device = input(Fore.RED + "Please enter a valid IP address or hostname. Enter [q] to quit: "
  105. + Fore.WHITE).lstrip(' ')
  106. if start_device.lower() == 'q':
  107. sys.exit(0)
  108.  
  109. destination_device = input('Enter destination device address: ').strip(' ')
  110.  
  111. while not self.validate_address(destination_device):
  112. destination_device = input(Fore.RED + "Please enter a valid IP address or hostname. Enter [q] to quit: "
  113. + Fore.WHITE).lstrip(' ')
  114. if destination_device.lower() == 'q':
  115. sys.exit(0)
  116.  
  117. return start_device, destination_device
  118.  
  119. def display_trace_warning(self):
  120. """ warns user traceroute is about to run and asks if they want to proceed """
  121.  
  122. if not self.auto_run:
  123. user_message = Fore.CYAN + "\nYou are about to trace the path from " + self.start_device.upper() + " to " \
  124. + self.destination_device
  125. print(user_message)
  126.  
  127. return self.user_verify("\nAre you sure you want to proceed? (y/n) ", 1)
  128.  
  129. return True
  130.  
  131. def display_commands_warning(self):
  132. """ warns user list of commands are about to run and asks if they want to proceed """
  133.  
  134. if not self.auto_run:
  135. user_message = Fore.CYAN + "\nYou are about to run the following commands:"
  136. print(user_message)
  137.  
  138. for command in self.command_list:
  139. print(command)
  140.  
  141. print(Fore.CYAN + "\nOn the following devices:")
  142. for hop in self.hop_list:
  143. print(hop)
  144.  
  145. return self.user_verify("\nAre you sure you want to proceed? (y/n) ", 1)
  146.  
  147. return True
  148.  
  149. def run_traceroute(self):
  150. """ run traceroute method that runs the initial traceroute command on
  151. the starting network device """
  152.  
  153. print(Fore.CYAN + "\nRunning traceroute from " + self.start_device.upper() + " to " +
  154. self.destination_device.upper() + Fore.WHITE)
  155.  
  156. while True:
  157. try:
  158. # build the appropriate device parameters for netmiko
  159. self.network_device_param = {
  160. 'device_type': 'cisco_ios',
  161. 'ip': self.start_device,
  162. 'username': self.user,
  163. 'password': self.password,
  164. 'port': 8181,
  165. 'verbose': False,
  166. }
  167.  
  168. # initialize the connect handler of netmiko
  169. self.net_connect = ConnectHandler(**self.network_device_param)
  170.  
  171. # enter enable mode if required
  172. if self.net_connect.find_prompt().endswith('>'):
  173. self.net_connect.enable()
  174.  
  175. # generate log data structure if key does not exist
  176. self.log[self.start_device] = ''
  177.  
  178. # otherwise assume a normal show command
  179. out = self.net_connect.send_command(self.trace_command)
  180.  
  181. # add to log
  182. self.log[self.start_device] += '\n\n' + self.start_device + "# " + self.trace_command + "\n"
  183. self.log[self.start_device] += out
  184.  
  185. self.traceroute_output = out
  186.  
  187. # print the show command output
  188. if self.print_screen:
  189. user_message = Fore.MAGENTA + "\nRUNNING: " + Fore.WHITE + self.trace_command
  190. print(user_message)
  191. print(out)
  192.  
  193. return self.traceroute_output
  194.  
  195. except NetMikoAuthenticationException:
  196. print("Unable to login")
  197. self.user, self.password = self.user_credentials_prompt()
  198. except NetMikoTimeoutException:
  199. print("Connection Timeout")
  200. self.start_device, self.destination_device = self.ask_user_devices()
  201. except Exception:
  202. print("Unexpected Error")
  203. sys.exit(1)
  204.  
  205. def traceroute_parse(self, traceroute_string):
  206.  
  207. traceroute_text = traceroute_string
  208.  
  209. trace_route = []
  210. ip_list = []
  211. new_output = traceroute_text.split('\n')
  212.  
  213. for line in new_output:
  214. line_output = list(filter(None, filter(None, line.split(' '))))
  215. if len(line_output) != 0 and bool(re.search(r'^\d', line_output[0])):
  216. if not (bool(re.search(r'\D', line_output[0]))):
  217. trace_route.append(line_output)
  218. ip_list.append(line_output[1])
  219. else:
  220. alt = ["alt"] + line_output
  221. trace_route.append(alt)
  222.  
  223. final_route = [v for v in trace_route if not (v[0] == "alt" and v[1] in ip_list)]
  224.  
  225. return final_route
  226.  
  227. def ping_test(self, route_table):
  228.  
  229. hops = route_table
  230. round_trip = []
  231.  
  232. for i in hops:
  233. if i[1] == '*':
  234. print("Hop " + str(hops.index(i) + 1) + " . . . not able to trace")
  235. else:
  236. for index, item in enumerate(i):
  237. if item == "ms" or item == "msec":
  238. round_trip.append(i[index - 1])
  239. elif bool(re.search(r'[ms]|[msec]', item)):
  240. round_trip.append(item.strip("ms"))
  241. average = list(map(float, round_trip))
  242. average = sum(average)/len(average)
  243. print("Hop " + str(hops.index(i) + 1) + " IP address: " + i[1] + " - - - Avg. RTT: " + str('%.3f'
  244. % average) + "ms")
  245. self.hop_list.append(i[1])
  246.  
  247. print("\nHOP LIST:")
  248.  
  249. for hop in self.hop_list:
  250. print(hop)
  251.  
  252. if not self.auto_run:
  253. ping_action = input("\nAbout to ping hops. Press [Enter] to ping devices along path.\n"
  254. "Add [a] or remove [r] devices. Skip [s] to more tests. ")
  255.  
  256. # user add devices
  257. if ping_action.lower() == 'a':
  258. while True:
  259. add_device = input("Enter additional device to ping or [Enter] to continue [q] to quit. : ").strip(' ')
  260. if add_device == '':
  261. break
  262. if add_device.lower() == 'q':
  263. sys.exit(0)
  264. while not self.validate_address(add_device):
  265. add_device = input(Fore.RED + "Please enter a valid IP address or hostname. "
  266. "[Enter] to continue [q] to quit: " + Fore.WHITE).strip(' ')
  267. if add_device.lower() == 'q':
  268. sys.exit(0)
  269. if add_device == '':
  270. break
  271. if add_device == '':
  272. break
  273. if add_device in self.hop_list:
  274. print(Fore.RED + "Device already in list." + Fore.WHITE)
  275. else:
  276. self.hop_list.append(add_device)
  277.  
  278. # user remove devices
  279. elif ping_action.lower() == 'r':
  280. while True:
  281. remove_device = input("Remove devices from ping test or [Enter] to continue [q] to quit : ").strip(' ')
  282. if remove_device == '':
  283. break
  284. elif remove_device.lower() == 'q':
  285. sys.exit(0)
  286. elif remove_device in self.hop_list:
  287. self.hop_list.remove(remove_device)
  288. else:
  289. print(Fore.RED + "Device not in list. " + Fore.WHITE)
  290.  
  291. # skip ping test
  292. elif ping_action.lower() == 's':
  293. return
  294.  
  295. print('\n')
  296.  
  297. # enter enable mode if required
  298. if self.net_connect.find_prompt().endswith('>'):
  299. self.net_connect.enable()
  300.  
  301. for hop in self.hop_list:
  302. out = self.net_connect.send_command("ping " + hop)
  303. if self.print_screen:
  304. print(out)
  305.  
  306. # close existing ssh session
  307. self.net_connect.disconnect()
  308.  
  309. def ask_user_commands(self):
  310. """ automatically runs two basic commands on the devices from the traceroute.
  311. user can add or remove commands """
  312.  
  313. self.command_list = ['show ip interface brief',
  314. 'show module']
  315.  
  316. if not self.auto_run:
  317. while True:
  318. print(Fore.CYAN + "\nAbout to run following commands on all hops:" + Fore.WHITE)
  319. for command in self.command_list:
  320. print(command)
  321.  
  322. commands_action = input("\nType new command to add command or existing command to remove.\n"
  323. "[Enter] to continue, [q] to quit: ")
  324. if commands_action == '':
  325. return self.command_list
  326. elif commands_action.lower() == 'q':
  327. sys.exit(0)
  328. elif commands_action in self.command_list:
  329. self.command_list.remove(commands_action)
  330. else:
  331. self.command_list.append(commands_action)
  332.  
  333. def run_commands(self, commands):
  334.  
  335. for hop in self.hop_list:
  336. user_message = "\nRunning commands on " + hop.upper()
  337. print(Fore.CYAN + user_message + Fore.WHITE)
  338.  
  339. self.network_device_param = {
  340. 'device_type': 'cisco_ios',
  341. 'ip': hop,
  342. 'username': self.user,
  343. 'password': self.password,
  344. #'port': 8181,
  345. 'verbose': False,
  346. }
  347.  
  348. self.log[hop] = ''
  349.  
  350. try:
  351. # initialize the connect handler of netmiko
  352. net_connect = ConnectHandler(**self.network_device_param)
  353.  
  354. # enter enable mode if required
  355. if net_connect.find_prompt().endswith('>'):
  356. net_connect.enable()
  357.  
  358. # iterate through the commands list
  359. for line in commands:
  360.  
  361. # otherwise assume a normal show command
  362. out = net_connect.send_command(line.strip())
  363.  
  364. # add to log
  365. self.log[hop] += '\n\n' + hop + "# " + line.strip() + "\n"
  366. self.log[hop] += out
  367.  
  368. # print the show command output
  369. if self.print_screen:
  370. user_message = Fore.MAGENTA + "\nRUNNING: " + Fore.WHITE + line.strip()
  371. print(user_message)
  372. print(out)
  373.  
  374. # close existing ssh session
  375. net_connect.disconnect()
  376.  
  377. except NetMikoAuthenticationException:
  378. print("Unable to login with credentials")
  379. self.log[hop] += "\n\nUnable to login with credentials"
  380. except NetMikoTimeoutException:
  381. print("Connection Timeout")
  382. self.log[hop] += "\n\nConnection Timeout"
  383. except Exception:
  384. print("Unexpected Error")
  385. self.log[hop] += "\n\nUnexpected Error"
  386.  
  387. def write_log(self):
  388. """ write log to output file """
  389.  
  390. with open(self.logname, 'w') as fn:
  391.  
  392. for device, logdata in self.log.items():
  393. # header information
  394. fn.write("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")
  395. fn.write("Source Device: " + device + "\n")
  396. fn.write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
  397.  
  398. # write actual log data
  399. for line in logdata.splitlines():
  400. fn.write(line + '\n')
  401.  
  402. fn.write('\n')
  403.  
  404.  
  405. def main():
  406. """ main function to run script """
  407.  
  408. # initialize colorama
  409. init(autoreset=True)
  410.  
  411. print(Fore.YELLOW + "# Path Health Check\n")
  412.  
  413. # initialize script
  414. hc_script = HealthCheck()
  415.  
  416. # run traceroute and ping test if user agrees
  417. if hc_script.display_trace_warning():
  418. traceroute_out = hc_script.run_traceroute()
  419. traceroute_tab = hc_script.traceroute_parse(traceroute_out)
  420. hc_script.ping_test(traceroute_tab)
  421. else:
  422. print("Not proceeding.")
  423. sys.exit(0)
  424.  
  425. # ask user commands to run
  426. commands_to_run = hc_script.ask_user_commands()
  427.  
  428. # run commands on devices if user agress
  429. if hc_script.display_commands_warning():
  430. hc_script.run_commands(commands_to_run)
  431. hc_script.write_log()
  432. else:
  433. print("Not proceeding.")
  434. sys.exit(0)
  435.  
  436. # pauses the script at the end to state message
  437. input("\nComplete!")
  438.  
  439.  
  440. if __name__ == '__main__':
  441. main()
Add Comment
Please, Sign In to add comment