390 lines
25 KiB
Python
Executable File
390 lines
25 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import os
|
|
import sys
|
|
import datetime
|
|
import pymongo
|
|
import urllib.parse
|
|
import argparse
|
|
from bson.json_util import dumps, loads
|
|
import logging
|
|
import datetime
|
|
|
|
# vpn_audit is not an installable package, update script instance python path to include modules
|
|
fpath = os.path.join(os.path.dirname(__file__), 'vpn_audit')
|
|
sys.path.append(fpath)
|
|
from vpn_audit import *
|
|
|
|
def inventory_tasks(read_device_collection, read_ip_collection, write_collection):
|
|
## retrive all vpn devices from MongoDB
|
|
device_dict = device_record(read_device_collection)
|
|
|
|
## debug, override devices
|
|
# target_devices = ['air-oob-hub01', 'air-vpn09', 'zfr-evry-p2p-agg01']
|
|
# device_dict = {k:v for k, v in device_dict.items() if k in target_devices}
|
|
|
|
## retrieve device management address info from MongoDB
|
|
device_dict = mgmt_address(read_ip_collection, device_dict)
|
|
|
|
## DNS lookup for devices in MongoDB without management address info, populate 'unknown' fields on failed lookup
|
|
# dont resolve tnsi.com, external dns record endpoints dont typically have accessible ssh
|
|
suffix = ['red.tnsi.com', 'corp.tnsi.com', 'blue.tnsi.com', 'open.corp.tnsi.com', 'win2k.corp.tnsi.com', 'reston.tnsi.com', 'csg.tnsi.com', 'tsdlabs.tnsi.com', 'vlab.corp.tnsi.com']
|
|
device_dict = dns_lookup(suffix, device_dict)
|
|
|
|
## debug, pretty print device inventory
|
|
# print(json.dumps(device_dict, indent=4))
|
|
|
|
## write inventory to device table
|
|
write_devices_collection(write_collection, device_dict)
|
|
|
|
## get device os for scrapli driver selection
|
|
find_query = { "session_protocol" : { "$exists" : False } }
|
|
object_ids = document_ids(write_collection, find_query)
|
|
get_os(write_collection, object_ids)
|
|
|
|
## get cisco version
|
|
query_modifier = { "session_protocol" : "ssh", "vendor": "cisco" }
|
|
target_devices = device_names(write_collection, query_modifier, device_name_list = [])
|
|
|
|
def send_commands(collection, device_id, device_name):
|
|
## start new buffered logger for thread
|
|
logger = logging.getLogger('main')
|
|
thread_logger = logging.getLogger(device_name)
|
|
thread_logger.setLevel(logging.INFO)
|
|
memhandler = logging.handlers.MemoryHandler(1024*10, target=logger, flushOnClose=True)
|
|
thread_logger.addHandler(memhandler)
|
|
|
|
## send commands to device, parse, update collection
|
|
commands = {
|
|
"cisco_version": {"command": "show version"}
|
|
}
|
|
for command in commands.keys():
|
|
func_ref = eval(command)
|
|
commands[command].update({'func_ref': func_ref})
|
|
status = device_commands(collection, device_id, commands)
|
|
if 'error' in status:
|
|
thread_logger.error(status)
|
|
# collection['temp'][device_name].drop() # may want to drop temp table
|
|
return
|
|
|
|
## stop buffered logger for thread, flush logs to 'main' logger
|
|
memhandler.close()
|
|
del thread_logger
|
|
|
|
threads = 16
|
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
for d in target_devices:
|
|
device_id = target_devices[d]
|
|
device_name = d
|
|
executor.submit(send_commands, write_collection, device_id, device_name)
|
|
|
|
# def document_update_tasks(collection, device_name):
|
|
def document_update_tasks(read_device_collection, read_ip_collection, write_collection, device_name):
|
|
## init
|
|
logger = logging.getLogger(device_name)
|
|
|
|
## dedupe temp device collection
|
|
# (there can be near identicle documents where the documents have a different 'c_id' value - maybe tunnel renegotiation)
|
|
# logger.info(f"Deduplicate: {collection['temp'][device_name].full_name}")
|
|
logger.info(f"Deduplicate: {write_collection['temp'][device_name].full_name}")
|
|
# mode = 'list'
|
|
# mode = 'show'
|
|
mode = 'delete'
|
|
ignore_schema_keys = ['_id', 'c_id']
|
|
# tmp_collection = collection['temp'][device_name]
|
|
tmp_collection = write_collection['temp'][device_name]
|
|
# deduplicate_collection(collection['temp'][device_name], mode, ignore_schema_keys)
|
|
deduplicate_collection(collection = tmp_collection, mode = mode, ignore_schema_keys = ignore_schema_keys, logger_name = device_name)
|
|
|
|
## lookup NMS mongo(remedy) spoke devices NMS tickets (get remedy 'DeviceRecNum' for each spoke device)
|
|
logger.info(f'Lookup: find peer device ticket {write_collection[device_name].full_name}')
|
|
tmp_collection = write_collection['temp'][device_name]
|
|
spoke_lookup(read_device_collection = read_device_collection, read_ip_collection = read_ip_collection, write_collection = tmp_collection, logger_name = device_name)
|
|
|
|
## lookup NMS mongo(remedy) spoke devices NMS info (use remedy 'DeviceRecNum' to get any required device ticket attributes)
|
|
logger.info(f'Lookup: find peer device attributes {write_collection[device_name].full_name}')
|
|
tmp_collection = write_collection['temp'][device_name]
|
|
device_ticket_lookup(read_device_collection = read_device_collection, write_collection = tmp_collection, logger_name = device_name)
|
|
|
|
## merge temp tables to main device table
|
|
# (in short this mask of keys 'tunnel_qualifier_keys' determines if a valid/full tunnel is configured and has been active in a scrape)
|
|
# (tunnel_qualifier_keys describes the keys required for a tunnel with cisco_vpn_phase1 + cisco_crypto_session, the keys from cisco_crypto_session are only present when cisco_crypto_map describes a matching cisco_vpn_phase2 configuration)
|
|
# (the tunnel may report 'session_status': 'UP-ACTIVE' displaying full phase1/phase2 attributes, 'UP-IDLE' / 'DOWN-NEGOTIATING' will only capture phase1 attributes)
|
|
#
|
|
# (owing to the phase1/phase2 timout values, stale configuration (and other conditions unknown), scrapes may not capture full tunnel configurations)
|
|
# (we may see partial scraped documents with the following data: cisco_vpn_phase1 / cisco_crypto_map / cisco_vpn_phase1 + cisco_crypto_session / cisco_crypto_session / cisco_vpn_phase1 + cisco_crypto_map / cisco_crypto_session + cisco_vpn_phase2)
|
|
# (subsequent scrapes may capture tunnel configuration in an active state (likely withing phase1/2 timeout thresholds) and contain enough of the keys to match the 'tunnel_qualifier_keys' mask)
|
|
# logger.info(f"Merge: {collection['temp'][device_name].full_name} to {collection[device_name].full_name}")
|
|
# src_collection = collection['temp'][device_name]
|
|
# dst_collection = collection[device_name]
|
|
logger.info(f"Merge: {write_collection['temp'][device_name].full_name} to {write_collection[device_name].full_name}")
|
|
src_collection = write_collection['temp'][device_name]
|
|
dst_collection = write_collection[device_name]
|
|
# ignore specific keys in src_document when searching for matching documents in the dst_collection
|
|
# 'crypto_map_interface' is dynamic and only present when data is passing, dont want multiple documents in the dst_collection table for a tunnel with/without this attribute
|
|
# 'ipsec_flow' maybe dynamic, changing when devices pass traffic
|
|
#ignore_src_schema_keys = ['_id', 'c_id', 'crypto_map_interface', 'ipsec_flow']
|
|
ignore_src_schema_keys = ['_id', 'c_id', 'crypto_map_interface', 'ipsec_flow', 'DeviceName', 'Manufacturer', 'Model', 'DeviceRecNum', 'nhrp_nexthop'] # merge records with latest/new scrape fields - 'DeviceName', 'Manufacturer', 'Model', 'DeviceRecNum', 'nhrp_nexthop' - likely not a problem to leave in place
|
|
# exclude keys in the insert/update to the dst_collection
|
|
exclude_dst_schema_keys = ['_id', 'c_id']
|
|
# list of additional key value pairs to add to each document in the dst_collection (the date Type value is interpereted by MongoDB as type ISODate)
|
|
additonal_dst_schema_keypairs = [{'last_modified': datetime.datetime.now(tz=datetime.timezone.utc)}]
|
|
# list of schema keys to match/qualify document in src_collection for merge to dst_collection (optional parameter, when unused everything gets merged)
|
|
tunnel_qualifier_keys = ["local_ip", "p1_ivrf", "peer_ip", "p1_dh_group", "p1_encr_algo", "p1_hash_algo", "p1_auth_type", "p1_status", "local_port", "crypto_session_interface", "peer_port", "p2_fvrf", "peer_vpn_id"]
|
|
merge_to_collection(src_collection = src_collection, dst_collection = dst_collection, ignore_src_schema_keys = ignore_src_schema_keys, exclude_dst_schema_keys = exclude_dst_schema_keys, additonal_dst_schema_keypairs = additonal_dst_schema_keypairs, match_src_schema_keys = tunnel_qualifier_keys, logger_name = device_name)
|
|
|
|
# ## debug - validate merge results, all records in src_collection should be included or excluded from merge - check against merge_to_collection results
|
|
# # mode = 'show'
|
|
# mode = 'stat'
|
|
# ignore_src_schema_keys = ['_id', 'c_id']
|
|
# src_collection = collection['temp'][device_name]
|
|
# dst_collection = collection[device_name]
|
|
# tunnel_qualifier_keys = ["local_ip", "p1_ivrf", "peer_ip", "p1_dh_group", "p1_encr_algo", "p1_hash_algo", "p1_auth_type", "p1_status", "local_port", "crypto_session_interface", "peer_port", "p2_fvrf", "peer_vpn_id"]
|
|
# match_src_schema_keys = tunnel_qualifier_keys
|
|
# diff_collection(src_collection, dst_collection, mode, ignore_src_schema_keys, match_src_schema_keys)
|
|
|
|
## full dedupe device table
|
|
# (ensure there are no 'duplicate' documents that only differ by '_id')
|
|
# (this should not be encountered at this stage)
|
|
# (if there are duplicates they must have the same datestamp indicating an error in the previous merge function)
|
|
# logger.info(f"Deduplicate: {collection[device_name].full_name}")
|
|
logger.info(f"Deduplicate: {write_collection[device_name].full_name}")
|
|
# device_collection = collection[device_name]
|
|
device_collection = write_collection[device_name]
|
|
mode = 'delete'
|
|
ignore_schema_keys = ['_id']
|
|
deduplicate_collection(collection = device_collection, mode = mode, ignore_schema_keys = ignore_schema_keys, logger_name = device_name)
|
|
#deduplicate_collection(collection[device_name], mode, ignore_schema_keys)
|
|
|
|
## capture 'UP-IDLE' / 'DOWN-NEGOTIATING' tunnels, delete if UP-ACTIVE tunnel documents exist
|
|
# (this may occur if an idle tunnel (cisco_vpn_phase1 + cisco_crypto_session) is initially scraped, then the full tunnel establishes +(cisco_vpn_phase2 + cisco_crypto_map) and is captured on a subsequent scrape thus creating two documents)
|
|
# (if the tunnel is idle on the 3rd+ scrape it will be merged into the document with the full tunnel attributes)
|
|
#
|
|
# (the 'idle_connection' mask contains the nearly the same keys as the 'tunnel_qualifier_keys' mask, as listed below. the 'session_status' field is ignored in the query to ensure both idle + active documents are matched)
|
|
# (["local_ip", "p1_ivrf", "peer_ip", "p1_dh_group", "p1_encr_algo", "p1_hash_algo", "p1_auth_type", "p1_status", "local_port", "crypto_session_interface", "session_status", "peer_port", "p2_fvrf", "peer_vpn_id"])
|
|
# print(f'\ndeduplicate_collection collection[{device_name}] - remove matched idle_connection')
|
|
# logger.info(f"Deduplicate: {collection[device_name].full_name} - remove 'UP-IDLE' records that are subset to 'UP-ACTIVE' records")
|
|
logger.info(f"Deduplicate: {write_collection[device_name].full_name} - remove 'UP-IDLE' records that are subset to 'UP-ACTIVE' records")
|
|
# device_collection = collection[device_name]
|
|
device_collection = write_collection[device_name]
|
|
mode = 'delete'
|
|
ignore_schema_keys = ['_id', 'last_modified', 'session_status']
|
|
idle_connection = ["local_ip", "p1_ivrf", "peer_ip", "p1_dh_group", "p1_encr_algo", "p1_hash_algo", "p1_auth_type", "p1_status", "local_port", "crypto_session_interface", "peer_port", "p2_fvrf", "peer_vpn_id"]
|
|
#required_schema_keys = idle_connection
|
|
#deduplicate_collection(collection[device_name], mode, ignore_schema_keys, required_schema_keys)
|
|
deduplicate_collection(collection = device_collection, mode = mode, ignore_schema_keys = ignore_schema_keys, required_schema_keys = idle_connection, logger_name = device_name)
|
|
|
|
## drop temp sorting table (disable for debug)
|
|
write_collection['temp'][device_name].drop()
|
|
|
|
## want to match >1 documents with the following keys as aggregate id_keys {"$group":{"_id": id_keys,"count": {"$sum": 1}}}
|
|
## ["local_ip", "local_port", "peer_ip", "peer_port", "peer_vpn_id"]
|
|
## this should match documents that have had changes to their config such as 'ordered_transform_set' / 'p2_encr_algo' and allow for a document date comparrison on the key 'last_modified'
|
|
## add an additional key to each document 'current_configuration' with a bool value for inclusion/exclusion in the spreadsheet stats and as a rudamentary history to indicate resolved complaince status
|
|
|
|
# def audit_tasks(collection, target_devices):
|
|
def audit_tasks(read_device_collection, read_ip_collection, write_collection, target_devices):
|
|
|
|
# def send_commands(collection, device_id, device_name):
|
|
def send_commands(read_device_collection, read_ip_collection, write_collection, device_id, device_name):
|
|
## start new buffered logger for thread
|
|
logger = logging.getLogger('main')
|
|
thread_logger = logging.getLogger(device_name)
|
|
thread_logger.setLevel(logging.INFO)
|
|
memhandler = logging.handlers.MemoryHandler(1024*10, target=logger, flushOnClose=True)
|
|
thread_logger.addHandler(memhandler)
|
|
|
|
try:
|
|
## send commands to device, parse, update collection - isakmp / ipsec audit (the order of commands is tied to the db record logic)
|
|
commands = {
|
|
"cisco_vpn_phase1": {"command": "show crypto isakmp sa detail"},
|
|
"cisco_crypto_session": {"command": "show crypto session detail"},
|
|
"cisco_vpn_phase2": {"command": "show crypto ipsec sa"},
|
|
"cisco_crypto_map": {"command": "show crypto map"},
|
|
"cisco_isakmp_policy": {"command": "show crypto isakmp policy"},
|
|
"cisco_nhrp_lookup": {"command": "compound"}
|
|
}
|
|
for command in commands.keys():
|
|
func_ref = eval(command)
|
|
commands[command].update({'func_ref': func_ref})
|
|
# status = device_commands(collection, device_id, commands)
|
|
status = device_commands(write_collection, device_id, commands)
|
|
if 'error' in status:
|
|
thread_logger.error(status)
|
|
# collection['temp'][device_name].drop() # may want to drop temp table on error
|
|
return
|
|
|
|
## send commands to device, parse, update collection - 3des audit (the order of commands is tied to the db record logic)
|
|
commands = {
|
|
"cisco_transform_set": {"command": "compound"},
|
|
"triple_des_check": {"command": "compound"}
|
|
}
|
|
for command in commands.keys():
|
|
func_ref = eval(command)
|
|
commands[command].update({'func_ref': func_ref})
|
|
# status = device_commands(collection, device_id, commands)
|
|
status = device_commands(write_collection, device_id, commands)
|
|
if 'error' in status:
|
|
thread_logger.error(status)
|
|
# collection['temp'][device_name].drop() # may want to drop temp table on error
|
|
return
|
|
|
|
## promote qualifying tunnels in 'temp' device collection to device collection
|
|
# document_update_tasks(collection, device_name)
|
|
document_update_tasks(read_device_collection, read_ip_collection, write_collection, device_name)
|
|
|
|
except Exception as e:
|
|
# as buffered logging is in place to try to collate per device/thread log messages, there is no visibility on crash, flush thread log
|
|
memhandler.flush()
|
|
logger.error(f"Exception occurred: {type(e).__name__}", exc_info=True)
|
|
memhandler.close()
|
|
del thread_logger
|
|
|
|
## stop buffered logger for thread, flush logs to 'main' logger
|
|
memhandler.close()
|
|
del thread_logger
|
|
|
|
## main loop - send commands to threadpool
|
|
# device_ids = [i for i in target_devices.values()]
|
|
# device_names = [n for n in target_devices.keys()]
|
|
with ThreadPoolExecutor(max_workers=config.device_threads) as executor:
|
|
for d in target_devices:
|
|
device_id = target_devices[d]
|
|
device_name = d
|
|
# executor.submit(send_commands, collection, device_id, device_name)
|
|
executor.submit(send_commands, read_device_collection, read_ip_collection, write_collection, device_id, device_name)
|
|
|
|
def parser_action(args):
|
|
## main script logic
|
|
logger = logging.getLogger('main')
|
|
match args.mode:
|
|
case 'inventory':
|
|
logger.info('#### Run - argument: inventory ####')
|
|
inventory_tasks(args.arg_lookup_device_collection, args.arg_lookup_ip_collection, args.arg_write_device_collection)
|
|
case 'list':
|
|
logger.info('#### Run - argument: list ####')
|
|
query_modifier = { "session_protocol" : "ssh", "vendor": "cisco" }
|
|
target_devices = device_names(args.arg_write_device_collection, query_modifier, device_name_list = [])
|
|
target_devices_list = [d for d in target_devices.keys()]
|
|
if len(target_devices_list) >0:
|
|
# print(','.join(target_devices_list))
|
|
logger.info(f"{','.join(target_devices_list)}")
|
|
else:
|
|
# print('device table empty, rerun inventory task')
|
|
logger.error('device table empty, rerun inventory task')
|
|
case 'audit':
|
|
if not args.all_devices and args.devices is None:
|
|
print('usage: main.py audit [-h] [-a | -d DEVICES]')
|
|
print('main.py audit: error: argument -d/--devices or argument -a/--all_devices required')
|
|
quit()
|
|
if args.all_devices:
|
|
query_modifier = { "session_protocol" : "ssh", "vendor": "cisco" }
|
|
target_devices = device_names(args.arg_write_device_collection, query_modifier, device_name_list = [])
|
|
# print(dumps(target_devices, indent=4))
|
|
logger.info('#### Run - argument: audit -a ####')
|
|
# audit_tasks(args.arg_write_device_collection, target_devices)
|
|
audit_tasks(args.arg_lookup_device_collection, args.arg_lookup_ip_collection, args.arg_write_device_collection, target_devices)
|
|
elif len(args.devices) >0:
|
|
device_name_list = [d for d in args.devices.split(',')]
|
|
# print(f'target devices\n{device_name_list}')
|
|
query_modifier = { "session_protocol" : "ssh", "vendor": "cisco" }
|
|
target_devices = device_names(args.arg_write_device_collection, query_modifier, device_name_list)
|
|
# print(dumps(target_devices, indent=4))
|
|
invalid_devices = [d for d in device_name_list if d not in target_devices.keys()]
|
|
if len(invalid_devices) >0:
|
|
print(f"device(s) error for {','.join(invalid_devices)}\n")
|
|
for d in invalid_devices:
|
|
if args.arg_write_device_collection.count_documents({'DeviceName': d}) == 0:
|
|
print(f'{d} not in device table, rerun inventory task if you are sure the device exists in remedy')
|
|
logger.error(f'{d} not in device table, rerun inventory task if you are sure the device exists in remedy')
|
|
else:
|
|
result = dumps(args.arg_write_device_collection.find({'DeviceName': d}, {'_id': 0, 'DeviceName': 1, "session_protocol" : 1, "vendor": 1}))
|
|
print(f'{d} does not meet audit requirements {result}')
|
|
logger.error(f'{d} does not meet audit requirements {result}')
|
|
quit()
|
|
logger.info(f'#### Run - argument: audit -d device1,device2,deviceN ####')
|
|
logger.info('Target devices:')
|
|
logger.info(f"{','.join([k for k in target_devices.keys()])}")
|
|
# audit_tasks(args.arg_write_device_collection, target_devices)
|
|
audit_tasks(args.arg_lookup_device_collection, args.arg_lookup_ip_collection, args.arg_write_device_collection, target_devices)
|
|
case 'report':
|
|
spreadsheet = './output.xlsx'
|
|
devices_dict = device_names(args.arg_write_device_collection) # pass query modifier to filter devices in spreadsheet
|
|
#print(dumps(devices_dict, indent=4))
|
|
logger.info(f'#### Run - argument: report ####')
|
|
build_spreadsheet(args.arg_write_device_collection, devices_dict, spreadsheet)
|
|
|
|
def main():
|
|
#### MongoDB sources
|
|
# need some sort of class inherritance setup store the client connection object to instantiate collections owned by the class, then chuck into vpn_mongo and have a dict as the config(that can be kept in json/toml)
|
|
|
|
## TNS MongoDB client connection
|
|
# no firewall rules asblpnxpdev01 -> rstlcnscmgd01:27017 (TNS MongoDB), use quick tunnel
|
|
# screen -S nmsmongo
|
|
# ssh -o "ServerAliveInterval 60" -L 127.0.0.1:27017:rstlcnscmgd01.open.corp.tnsi.com:27017 tseed@airlcinfjmp01.open.corp.tnsi.com
|
|
lookup_mongohost = '127.0.0.1'
|
|
lookup_mongoport = 27017
|
|
lookup_client = pymongo.MongoClient(f'mongodb://{lookup_mongohost}:{lookup_mongoport}/')
|
|
lookup_device_db = lookup_client['jobs']
|
|
lookup_device_collection = lookup_device_db['DEVICE_WS']
|
|
lookup_ip_collection = lookup_device_db['NT_IPAddress_WS']
|
|
|
|
## DEV MongoDB client connection
|
|
# no firewall rules asblpnxpdev01 -> 172.17.213.136:27017 (DEV MongoDB), use quick tunnel
|
|
# screen -S testmongo
|
|
# ssh -o "ServerAliveInterval 60" -J airlcinfjmp01.open.corp.tnsi.com -L 127.0.0.1:27018:127.0.0.1:27017 tseed@172.17.213.136
|
|
write_mongohost = '127.0.0.1'
|
|
write_mongoport = 27018
|
|
write_username = urllib.parse.quote_plus('script')
|
|
write_password = urllib.parse.quote_plus('install1')
|
|
write_client = pymongo.MongoClient(f'mongodb://{write_username}:{write_password}@{write_mongohost}:{write_mongoport}/')
|
|
write_vpn_db = write_client['vpn']
|
|
write_device_collection = write_vpn_db['devices']
|
|
|
|
#### Logger
|
|
logger = logging.getLogger('main')
|
|
logger.setLevel(logging.INFO)
|
|
console = logging.StreamHandler()
|
|
file = logging.FileHandler("main.log")
|
|
logger.addHandler(console)
|
|
logger.addHandler(file)
|
|
formatter = logging.Formatter(
|
|
fmt="%(asctime)s, %(levelname)-8s | %(filename)-15s:%(lineno)-5s | %(threadName)-1s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S")
|
|
console.setFormatter(formatter)
|
|
file.setFormatter(formatter)
|
|
|
|
#### Threading, concurrent device audits
|
|
# device_threads
|
|
# concurrent devices queries (collect), mostly an IO task with lots of delay
|
|
# database tasks are suboptimal retrieving all records for dedupe/merge, consider dropping threads where database performance issues/drops
|
|
# scrape_threads
|
|
# screen scrapes being processed concurrently, mostly a CPU task with lots of loop/split/regex of screen scrapes
|
|
# total threads (nested) - 16 devices * 2 nested scrapes = 32 threads
|
|
# set to 1 / 1 for debug
|
|
config.scrape_threads = os.cpu_count()
|
|
config.device_threads = 32
|
|
|
|
#### Argument parser, run main script logic in parser_action()
|
|
parser = argparse.ArgumentParser(description='Collect VPN tunnel info')
|
|
audit = argparse.ArgumentParser(add_help=False)
|
|
audit_args = audit.add_mutually_exclusive_group()
|
|
report = argparse.ArgumentParser(add_help=False)
|
|
audit_args.add_argument('-a', '--all_devices', action='store_true', help='All target devices in the VPN device table, WARNING this may take a full day to complete')
|
|
audit_args.add_argument('-d', '--devices', action='store', help='Comma separated list of target devices')
|
|
report.add_argument('-e', '--email', action='store', help='Email addresses to send report', required=True)
|
|
sp = parser.add_subparsers(required=True)
|
|
sp_inventory = sp.add_parser('inventory', help='Query NMS MongoDB to generate VPN device table')
|
|
sp_audit = sp.add_parser('audit', parents=[audit], description='Collect tunnel info for target devices, requires argument [-a | -d]', help='Collect tunnel info for target devices')
|
|
sp_report = sp.add_parser('report', parents=[report], description='Generate VPN XLSX report, requires argument [-e]', help='Generate VPN XLSX report')
|
|
sp_list = sp.add_parser('list', description='Return all target devices in VPN device table', help='Return all target devices in VPN device table')
|
|
sp_inventory.set_defaults(func=parser_action, mode='inventory', arg_lookup_device_collection=lookup_device_collection, arg_lookup_ip_collection = lookup_ip_collection, arg_write_device_collection = write_device_collection)
|
|
# sp_audit.set_defaults(func=parser_action, mode='audit', arg_write_device_collection = write_device_collection)
|
|
sp_audit.set_defaults(func=parser_action, mode='audit', arg_lookup_device_collection=lookup_device_collection, arg_lookup_ip_collection = lookup_ip_collection, arg_write_device_collection = write_device_collection)
|
|
sp_report.set_defaults(func=parser_action, mode='report', arg_write_device_collection = write_device_collection)
|
|
sp_list.set_defaults(func=parser_action, mode='list', arg_write_device_collection = write_device_collection)
|
|
args = parser.parse_args()
|
|
args.func(args)
|
|
|
|
if __name__ == "__main__":
|
|
main() |