Advertisement
abbasids

Multiple Network Vendor Pre|Post|Compare Validation Script

Feb 25th, 2024 (edited)
498
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.60 KB | Source Code | 0 0
  1. ## THINGS TO PERFORM PRIOR TO RUNNING THE CONFIG ##
  2.  
  3. # INSTALL PYTHON
  4. # CREATE VIRTUAL ENVIRONMENT
  5.     # sudo pip install virtualenv
  6.     # virtualenv env
  7.     # source env/bin/activate
  8.     # pip install netmiko paramiko datetime requests getpass4
  9.     # pip install --upgrade setuptools
  10.     # sudo apt-get install build-essential libssl-dev libffi-dev -y
  11.  
  12. # CREATE "validation.py" COPY ALL BELOW CODE AND PASTE IT.
  13. # CREATE "devices-file.txt"
  14.     # PUT IN YOUR DEVICES IN THIS FORMAT BY IP "192.168.1.1" OR DNS NAME "DEVICE001"
  15.     # EXAMPLE 1: abc.local.com,fortinet
  16.     # EXAMPLE 2: 192.168.1.1,fortinet
  17.     # EXAMPLE 3: abc.local.com,cisco_ios
  18.     # EXAMPLE 4: 192.168.1.1,cisco_ios
  19.     # EXAMPLE 5: abc.local.com,nxos
  20.     # EXAMPLE 6: 192.168.1.1,nxos
  21. # CREATE "command_list.txt"
  22.     # PUT IN THE COMMANDS LIST YOU WOULD WANT TO RUN
  23.         # CISCO:
  24.             # show run
  25.             # show vrf
  26.             # show port-channel summary
  27.             # show etherchannel summary
  28.         # FORTINET:
  29.             # get interface status
  30.  
  31. ##========================================================================##
  32.  
  33. #! python
  34.  
  35. # MUDULES NEEDS TO BE IMPORTED
  36. from pprint import pprint
  37. from multiprocessing.dummy import Pool as ThreadPool
  38. from netmiko import ConnectHandler
  39. from netmiko.exceptions import NetMikoTimeoutException, AuthenticationException, SSHException
  40. from datetime import datetime
  41. from time import time
  42. from getpass import getpass
  43. import logging
  44. import json
  45. import difflib
  46. import traceback
  47.  
  48. # Configure logging
  49. logging.basicConfig(level=logging.DEBUG,  # Set minimum logging level to DEBUG
  50.                     format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',  # Customize log format
  51.                     datefmt='%Y-%m-%d %H:%M:%S',  # Customize date format
  52.                     handlers=[  # Add handlers for both file and console output
  53.                         logging.FileHandler('network_operations.log'),  # Log messages to a file
  54.                         logging.StreamHandler()  # Also log messages to console
  55.                     ])
  56.  
  57. # Example usage within your script
  58. logger = logging.getLogger(__name__)
  59.  
  60. # At the beginning of your script or main function
  61. logger.info("Script started")
  62.  
  63. # When catching exceptions, use different levels based on severity
  64. try:
  65.     # Attempt to connect to a device
  66.     pass
  67. except NetMikoTimeoutException as e:
  68.     logger.warning(f"Timeout while connecting to device: {e}")  # Use WARNING for recoverable errors
  69. except AuthenticationException as e:
  70.     logger.error(f"Authentication failed: {e}")  # Use ERROR for more severe issues
  71. except Exception as e:
  72.     logger.critical(f"Unexpected error: {e}", exc_info=True)  # Use CRITICAL for very severe issues and log stack trace
  73. #
  74. #
  75. # READ DEVICE LIST FROM A FILE
  76. def read_devices(devices_filename):
  77.     devices = {}  # CREATE DICTIONART FOR STORING DEVICES
  78.     with open(devices_filename) as devices_file:
  79.         for device_line in devices_file:
  80.             device_info = device_line.strip().split(',')  # EXTRACT DEVICE INFORMATION
  81.             device = {
  82.                 'ipaddr': device_info[0],
  83.                 'device_type': device_info[1]
  84.             }
  85.             devices[device['ipaddr']] = device # STORE DEVICE IN THE DEVICES DICTIONARY
  86.                                                 # NOTE THE KEY FOR THE DEVICES DICTIONARY ENTRIES IS "ipaddr"
  87.     print('\n************************** DEVICE LIST ***************************')
  88.     for ipaddr, device in devices.items():
  89.         print(f"IP Address: {ipaddr}, Device Type: {device['device_type']}")
  90.     return devices
  91. #
  92. # CONFIGURATION WORKER FUNCTION (SHARED STRUCTURE FOR PRE AND POST VALIDATION)
  93. def config_worker(device, selection_prefix, username, password):
  94.     max_retries = 3  # Maximum number of retry attempts
  95.     retry_delay = 5  # Time to wait between retries, in seconds
  96.  
  97.     for attempt in range(max_retries):
  98.         try:
  99.             print(f"...{device['ipaddr']} Sending commands")
  100.             # Use device_type from the device dictionary
  101.             session = ConnectHandler(
  102.                 device_type=device['device_type'],
  103.                 ip=device['ipaddr'],
  104.                 username=username,  # Use the passed username
  105.                 password=password  # Use the passed password
  106.             )
  107.             # The rest of your code for sending commands...
  108.             with open('command_list.txt') as f:
  109.                 commands = f.read().splitlines()
  110.                 for command in commands:
  111.                     command = command.strip()
  112.                     print(f"{device['ipaddr']} Sending: {command}")
  113.                     result = session.send_command(command, delay_factor=2)
  114.                     file_name = f"{device['ipaddr']}_{selection_prefix}.txt"
  115.                     with open(file_name, 'a+') as validation2:
  116.                         validation2.write(f'\n{command}\n{result}')
  117.             # If commands are sent successfully, break out of the retry loop
  118.             break
  119.         except NetMikoTimeoutException as e:
  120.             logging.error(f'TimeoutException for {device["ipaddr"]} on attempt {attempt+1} at {datetime.now()}: {str(e)}')
  121.             if attempt < max_retries - 1:  # Check if we are not on the last attempt
  122.                 print(f"Retrying in {retry_delay} seconds...")
  123.                 time.sleep(retry_delay)
  124.             else:
  125.                 print(f"Failed to connect to {device['ipaddr']} after {max_retries} attempts.")
  126.         except AuthenticationException as e:
  127.             logging.error(f'AuthenticationException for {device["ipaddr"]} at {datetime.now()}: {str(e)}')
  128.             # Authentication errors are not typically retried, so break from the loop
  129.             break
  130.         except SSHException as e:
  131.             logging.error(f'SSHException for {device["ipaddr"]} at {datetime.now()}: {str(e)}')
  132.             # Handle general SSH errors, may consider breaking or retrying based on the error
  133.             break
  134.         except Exception as e:  # A generic catch-all for any other exceptions
  135.             logging.error(f'Unhandled exception for {device["ipaddr"]} at {datetime.now()}: {str(e)}')
  136.             break
  137. #
  138. # COMPARISON FUNCTION
  139. def compare_configs():
  140.     try:
  141.         with open("devices-file.txt") as f:
  142.             devices = f.read().splitlines()
  143.         for item in devices:
  144.             # Split the item by commas and keep only the first and last parts for the filename
  145.             parts = item.split(',')
  146.             # Ensure there are enough parts to avoid IndexError
  147.             if len(parts) >= 2:
  148.                 simplified_item = f"{parts[0].split('_')[0]}"
  149.             else:
  150.                 print(f"Invalid format for device info: {item}")
  151.                 continue  # Skip to the next item
  152.  
  153.             config_filename_pre = f"{simplified_item}_pre.txt"
  154.             config_filename_post = f"{simplified_item}_post.txt"
  155.             try:
  156.                 with open(config_filename_pre) as f:
  157.                     text1_lines = f.read().splitlines()
  158.                 with open(config_filename_post) as g:
  159.                     text2_lines = g.read().splitlines()
  160.             except FileNotFoundError as e:
  161.                 print(f"Error opening file: {e}")
  162.                 continue  # Skip this device and continue with the next
  163.            
  164.             difference = difflib.HtmlDiff().make_file(text1_lines, text2_lines, "PRE-VALIDATION", "POST-VALIDATION")
  165.             diff_filename = f"{simplified_item}_difference_report.html"
  166.             with open(diff_filename, 'w') as validation2:
  167.                 validation2.write(difference)
  168.             print(f"Report generated for {simplified_item}: {diff_filename}")
  169.     except Exception as e:
  170.         print(f"An error occurred: {e}")
  171. #
  172. #
  173. # BELOW TEXT IS CREATED IN ASCII FORM
  174. # https://manytools.org/hacker-tools/ascii-banner/
  175. print ('\n')
  176. print("████████╗███████╗██╗  ██╗████████╗    ██╗  ██╗███████╗██████╗ ███████╗   ")
  177. print("╚══██╔══╝██╔════╝╚██╗██╔╝╚══██╔══╝    ██║  ██║██╔════╝██╔══██╗██╔════╝   ")
  178. print("   ██║   █████╗   ╚███╔╝    ██║       ███████║█████╗  ██████╔╝█████╗     ")
  179. print("   ██║   ██╔══╝   ██╔██╗    ██║       ██╔══██║██╔══╝  ██╔══██╗██╔══╝     ")
  180. print("   ██║   ███████╗██╔╝ ██╗   ██║       ██║  ██║███████╗██║  ██║███████╗   ")
  181. print("   ╚═╝   ╚══════╝╚═╝  ╚═╝   ╚═╝       ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚══════╝   ")
  182. print ('\n')
  183. #
  184. #
  185. # Get username and password using getpass
  186. username = input("Enter username: ")  # Username is typically not sensitive, so it's okay to use input()
  187. password = getpass("Enter password: ")  # Use getpass for password to hide it while typing
  188.  
  189. # USER SELECTION AND PROCESSING
  190. devices = read_devices('devices-file.txt')
  191. num_threads = 25 # Adjustable based on need
  192.  
  193. valid_selections = ['1', '2', '3', '4']
  194. Selection = None
  195.  
  196. while Selection not in valid_selections:
  197.     print('\n** SELECT YOUR INPUT FROM BELOW **')
  198.     print('\n       1: PRE-VALIDATION\n       2: POST-VALIDATION\n       3: COMPARE-PRE | POST\n       4: EXIT\n')
  199.     Selection = input("Enter your choice (1, 2, 3, or 4): ")
  200.  
  201.     if Selection not in valid_selections:
  202.         print("Error: Invalid selection. Please choose a valid option.")
  203.  
  204. if Selection == "1":
  205.     selection_prefix = 'pre'
  206. elif Selection == "2":
  207.     selection_prefix = 'post'
  208. elif Selection == "3":
  209.     compare_configs()
  210. elif Selection == "4":
  211.     print("Exiting the script.")
  212.     exit()
  213.  
  214. if Selection in ['1', '2']:
  215.     config_params_list = [(device, username, password) for ipaddr, device in devices.items()]  # Include credentials in the parameters
  216.     starting_time = time()
  217.     print('\n--- Creating threadpool, launching get config threads\n')
  218.     threads = ThreadPool(num_threads)
  219.     results = threads.map(lambda params: config_worker(params[0], selection_prefix, params[1], params[2]), config_params_list)
  220.     threads.close()
  221.     threads.join()
  222.     print('\n---- End get config threadpool, elapsed time=', time() - starting_time)
  223.  
  224. elif Selection == "3":    
  225.     compare_configs( )
  226. else:
  227.     print("Error: Invalid selection. Please choose a valid option.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement