223 lines
9.7 KiB
Python
223 lines
9.7 KiB
Python
from dotenv import load_dotenv
|
|
import os
|
|
from scrapli import Scrapli
|
|
from scrapli.driver import GenericDriver
|
|
# from scrapli.driver.core import IOSXEDriver
|
|
import logging
|
|
from logging import handlers
|
|
from bson.json_util import dumps, loads
|
|
|
|
# accept dict of commands and associated functions to parse command output, run commands directly and pass output to parsing function or pass connection dict (compound) to parsing function to run multiple commands
|
|
def device_commands(collection, object_id, commands):
|
|
## init
|
|
load_dotenv()
|
|
sshuser = os.environ.get('SSH_USER')
|
|
sshpass = os.environ.get('SSH_PASSWORD')
|
|
query = {"_id": object_id}
|
|
result = collection.find(query)
|
|
device_record = result[0]
|
|
ip = device_record['IPAddress']
|
|
device_name = device_record['DeviceName']
|
|
scrapli_platform = device_record['scrapli_platform']
|
|
|
|
## log
|
|
logger = logging.getLogger(device_name)
|
|
logger.info(f'Send commands to device - {device_name} - {ip} - {scrapli_platform}')
|
|
|
|
## send commands
|
|
# timeout = 60 # default
|
|
timeout = 120 # australia
|
|
connection = {
|
|
"host": ip,
|
|
"auth_username": sshuser,
|
|
"auth_password": sshpass,
|
|
"auth_secondary": sshpass,
|
|
"auth_strict_key": False,
|
|
"ssh_config_file": "/etc/ssh/ssh_config",
|
|
"platform": scrapli_platform,
|
|
"timeout_socket": timeout,
|
|
"timeout_transport": timeout,
|
|
"timeout_ops": timeout,
|
|
}
|
|
device_commands = commands.copy()
|
|
scrapli_commands_keys = [c for c in device_commands.keys() if not device_commands[c]['command'] == 'compound']
|
|
try:
|
|
with Scrapli(**connection) as conn:
|
|
# send commands over a single socket to avoid session-limits/IDS
|
|
for k in scrapli_commands_keys:
|
|
command = device_commands[k]['command']
|
|
# print(f"sending command '{command}' for {k}")
|
|
logger.info(f"Sending command '{command}' for {k}")
|
|
output = conn.send_command(command)
|
|
device_commands[k].update({'output': output})
|
|
except Exception as e:
|
|
# print(f'exception_type: {type(e).__name__}')
|
|
logger.error(f"Exception occurred: {type(e).__name__}", exc_info=True)
|
|
return f'{device_name} error: scrapli failure {command}'
|
|
|
|
## run scrape processors
|
|
for c in device_commands.keys():
|
|
func = commands[c]['func_ref']
|
|
command_output = commands[c]
|
|
func(collection, command_output, device_record, connection)
|
|
|
|
## success end
|
|
return 'processed'
|
|
|
|
# accept dict of commands and associated functions to parse command output, run commands directly and pass output to parsing function or pass connection dict (compound) to parsing function to run multiple commands
|
|
def device_commandsAA(collection, object_ids, commands):
|
|
# logger = logging.getLogger('main')
|
|
load_dotenv()
|
|
# error_encountered = False
|
|
sshuser = os.environ.get('SSH_USER')
|
|
sshpass = os.environ.get('SSH_PASSWORD')
|
|
query = { "_id" : { "$in" : object_ids } }
|
|
result = collection.find(query)
|
|
for device_record in result:
|
|
ip = device_record['IPAddress']
|
|
device_name = device_record['DeviceName']
|
|
scrapli_platform = device_record['scrapli_platform']
|
|
|
|
# print(f"\nquery device - {device_name} - {ip} - {scrapli_platform}")
|
|
local_logger = logging.getLogger(device_name)
|
|
local_logger.info(f'Send commands to device - {device_name} - {ip} - {scrapli_platform}')
|
|
|
|
# timeout = 60 # default
|
|
# timeout = 120 # australia
|
|
timeout = 180 # RRI in australia/malaysia
|
|
connection = {
|
|
"host": ip,
|
|
"auth_username": sshuser,
|
|
"auth_password": sshpass,
|
|
"auth_secondary": sshpass,
|
|
"auth_strict_key": False,
|
|
"ssh_config_file": "/etc/ssh/ssh_config",
|
|
"platform": scrapli_platform,
|
|
"timeout_socket": timeout,
|
|
"timeout_transport": timeout,
|
|
"timeout_ops": timeout,
|
|
}
|
|
device_commands = commands.copy()
|
|
scrapli_commands_keys = [c for c in device_commands.keys() if not device_commands[c]['command'] == 'compound']
|
|
try:
|
|
with Scrapli(**connection) as conn:
|
|
# send commands over a single socket to avoid session-limits/IDS
|
|
for k in scrapli_commands_keys:
|
|
command = device_commands[k]['command']
|
|
# print(f"sending command '{command}' for {k}")
|
|
local_logger.info(f"Sending command '{command}' for {k}")
|
|
output = conn.send_command(command)
|
|
device_commands[k].update({'output': output})
|
|
except Exception as e:
|
|
# print(f'exception_type: {type(e).__name__}')
|
|
# return f'{device_name} error: scrapli failure'
|
|
local_logger.error(f"Exception occurred: {type(e).__name__}", exc_info=True)
|
|
return f'{device_name} error: scrapli failure {command}'
|
|
# update all commands that didnt run with error status (should not get to this point)
|
|
# for k in scrapli_commands_keys:
|
|
# if 'output' not in device_commands[k]:
|
|
# device_commands[k]['output'] = 'error'
|
|
# error_encountered = True
|
|
# if error_encountered:
|
|
# return f'{device_name} error: scrapli failure'
|
|
# run scrape processors
|
|
for c in device_commands.keys():
|
|
func = commands[c]['func_ref']
|
|
command_output = commands[c]
|
|
func(collection, command_output, device_record, connection)
|
|
return 'processed'
|
|
|
|
def identify_scrapli_platform(osinfo):
|
|
# parse output of 'generic' device type command 'show version' for cisco/junos, will use this to find netmiko ConnectHandler 'device_type' parameter / scrapli_platform
|
|
# Cisco IOS Software, C3900 Software (C3900-UNIVERSALK9-M), Version 15.4(3)M2, RELEASE SOFTWARE (fc2) # first line ios, scrapli platform cisco_iosxe
|
|
# Cisco IOS XE Software, Version 03.16.06.S - Extended Support Release # first line iosxe, scrapli platform cisco_iosxe
|
|
# JUNOS Software Release [12.1X44-D30.4] # 2nd line junos, scrapli platform juniper_junos
|
|
vendors = ['cisco', 'junos']
|
|
cisco = [{'os': 'Cisco IOS Software', 'scrapli_platform': 'cisco_iosxe', 'line': 0},
|
|
{'os': 'Cisco IOS XE Software', 'scrapli_platform': 'cisco_iosxe', 'line': 0}]
|
|
junos = [{'os': 'JUNOS Software Release', 'scrapli_platform': 'juniper_junos', 'line': 2}]
|
|
for v in vendors:
|
|
if v in osinfo.lower():
|
|
vendor = v
|
|
if not 'vendor' in locals():
|
|
vendor = 'unknown'
|
|
match vendor:
|
|
case "cisco":
|
|
# known cisco os
|
|
for i in cisco:
|
|
scrape_line = osinfo.partition('\n')[i['line']]
|
|
if i['os'] in scrape_line:
|
|
scrapli_platform = i['scrapli_platform']
|
|
record = {'scrapli_platform': scrapli_platform, 'vendor': vendor }
|
|
# print(record)
|
|
# unknown cisco os
|
|
if not 'scrapli_platform' in locals():
|
|
scrapli_platform = 'generic'
|
|
record = {'scrapli_platform': scrapli_platform, 'vendor': vendor }
|
|
# print(record)
|
|
case "junos":
|
|
# known junos os
|
|
for i in junos:
|
|
scrape_line = osinfo.partition('\n')[i['line']]
|
|
if i['os'] in scrape_line:
|
|
scrapli_platform = i['scrapli_platform']
|
|
record = {'scrapli_platform': scrapli_platform, 'vendor': vendor }
|
|
# print(record)
|
|
# catch all
|
|
case _:
|
|
scrapli_platform = 'generic'
|
|
record = {'scrapli_platform': scrapli_platform, 'vendor': vendor }
|
|
# print(record)
|
|
return record
|
|
|
|
def get_os(collection, object_ids):
|
|
# print('\nget_os')
|
|
logger = logging.getLogger('main')
|
|
logger.info('Get device OS type, update device records with Scrapli driver')
|
|
load_dotenv()
|
|
sshuser = os.environ.get('SSH_USER')
|
|
sshpass = os.environ.get('SSH_PASSWORD')
|
|
query = { "_id" : { "$in" : object_ids } }
|
|
result = collection.find(query)
|
|
for i in result:
|
|
name = i['DeviceName']
|
|
ip = i['IPAddress']
|
|
id = i['_id']
|
|
if not ip == 'unknown':
|
|
device = {
|
|
"host": ip,
|
|
"auth_username": sshuser,
|
|
"auth_password": sshpass,
|
|
"auth_strict_key": False,
|
|
"ssh_config_file": "/etc/ssh/ssh_config",
|
|
"timeout_socket": 15,
|
|
"timeout_transport": 15,
|
|
"timeout_ops": 15,
|
|
}
|
|
# use generic driver to help identify *any* OS to then select vendor specific drivers
|
|
# may require multiple try statements for commands that identify different OS vendors, show version may not be present on a VA/Sarian?
|
|
try:
|
|
with GenericDriver(**device) as conn:
|
|
# print(conn.ssh_config_file) # modern OS disable legacy KEX/Cipers/etc - the systemwide ssh_config has been compromised to use legacy ciphers, in future provide local ssh_config
|
|
conn.send_command("terminal length 0")
|
|
response = conn.send_command("show version")
|
|
sshresult = True
|
|
osinfo = identify_scrapli_platform(response.result)
|
|
except Exception as e:
|
|
# print(f'{name} connection error, exception_type {type(e).__name__}')
|
|
logger.error(f'{name} connection error, exception_type {type(e).__name__}')
|
|
sshresult = False
|
|
session_message = 'unknown'
|
|
if type(e).__name__ == 'ScrapliAuthenticationFailed':
|
|
session_message = 'ssh failed auth'
|
|
if type(e).__name__ == 'ScrapliTimeout':
|
|
session_message = 'ssh failed connection'
|
|
if sshresult:
|
|
osinfo.update({'session_protocol': 'ssh'})
|
|
filter = {'_id': id}
|
|
record = osinfo
|
|
update = collection.update_one(filter, {'$set': record}, upsert=True)
|
|
else:
|
|
filter = {'_id': id}
|
|
record = {'session_protocol': session_message}
|
|
update = collection.update_one(filter, {'$set': record}, upsert=True) |