Mini Shell
"""
This module is used to auto-detect the type of a device in order to automatically create a
Netmiko connection.
The will avoid to hard coding the 'device_type' when using the ConnectHandler factory function
from Netmiko.
Example:
------------------
from netmiko.snmp_autodetect import SNMPDetect
my_snmp = SNMPDetect(hostname='1.1.1.70', user='pysnmp', auth_key='key1', encrypt_key='key2')
device_type = my_snmp.autodetect()
------------------
autodetect will return None if no match.
SNMPDetect class defaults to SNMPv3
Note, pysnmp is a required dependency for SNMPDetect and is intentionally not included in
netmiko requirements. So installation of pysnmp might be required.
"""
import re
try:
from pysnmp.entity.rfc3413.oneliner import cmdgen
except ImportError:
raise ImportError("pysnmp not installed; please install it: 'pip install pysnmp'")
from netmiko.ssh_dispatcher import CLASS_MAPPER
# Higher priority indicates a better match.
SNMP_MAPPER_BASE = {
"arista_eos": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Arista Networks EOS.*", re.IGNORECASE),
"priority": 99,
},
"paloalto_panos": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Palo Alto Networks.*", re.IGNORECASE),
"priority": 99,
},
"hp_comware": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*HP(E)? Comware.*", re.IGNORECASE),
"priority": 99,
},
"hp_procurve": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".ProCurve", re.IGNORECASE),
"priority": 99,
},
"cisco_ios": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Cisco IOS Software,.*", re.IGNORECASE),
"priority": 60,
},
"cisco_xe": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*IOS-XE Software,.*", re.IGNORECASE),
"priority": 99,
},
"cisco_xr": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Cisco IOS XR Software.*", re.IGNORECASE),
"priority": 99,
},
"cisco_asa": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Cisco Adaptive Security Appliance.*", re.IGNORECASE),
"priority": 99,
},
"cisco_nxos": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Cisco NX-OS.*", re.IGNORECASE),
"priority": 99,
},
"cisco_wlc": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Cisco Controller.*", re.IGNORECASE),
"priority": 99,
},
"f5_tmsh": {
"oid": ".1.3.6.1.4.1.3375.2.1.4.1.0",
"expr": re.compile(r".*BIG-IP.*", re.IGNORECASE),
"priority": 99,
},
"fortinet": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r"Forti.*", re.IGNORECASE),
"priority": 80,
},
"checkpoint": {
"oid": ".1.3.6.1.4.1.2620.1.6.16.9.0",
"expr": re.compile(r"CheckPoint"),
"priority": 79,
},
"juniper_junos": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*Juniper.*"),
"priority": 99,
},
"nokia_sros": {
"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*TiMOS.*"),
"priority": 99,
},
}
# Ensure all SNMP device types are supported by Netmiko
SNMP_MAPPER = {}
std_device_types = list(CLASS_MAPPER.keys())
for device_type in std_device_types:
if SNMP_MAPPER_BASE.get(device_type):
SNMP_MAPPER[device_type] = SNMP_MAPPER_BASE[device_type]
class SNMPDetect(object):
"""
The SNMPDetect class tries to automatically determine the device type.
Typically this will use the MIB-2 SysDescr and regular expressions.
Parameters
----------
hostname: str
The name or IP address of the hostname we want to guess the type
snmp_version : str, optional ('v1', 'v2c' or 'v3')
The SNMP version that is running on the device (default: 'v3')
snmp_port : int, optional
The UDP port on which SNMP is listening (default: 161)
community : str, optional
The SNMP read community when using SNMPv2 (default: None)
user : str, optional
The SNMPv3 user for authentication (default: '')
auth_key : str, optional
The SNMPv3 authentication key (default: '')
encrypt_key : str, optional
The SNMPv3 encryption key (default: '')
auth_proto : str, optional ('des', '3des', 'aes128', 'aes192', 'aes256')
The SNMPv3 authentication protocol (default: 'aes128')
encrypt_proto : str, optional ('sha', 'md5')
The SNMPv3 encryption protocol (default: 'sha')
Attributes
----------
hostname: str
The name or IP address of the device we want to guess the type
snmp_version : str
The SNMP version that is running on the device
snmp_port : int
The UDP port on which SNMP is listening
community : str
The SNMP read community when using SNMPv2
user : str
The SNMPv3 user for authentication
auth_key : str
The SNMPv3 authentication key
encrypt_key : str
The SNMPv3 encryption key
auth_proto : str
The SNMPv3 authentication protocol
encrypt_proto : str
The SNMPv3 encryption protocol
Methods
-------
autodetect()
Try to determine the device type.
"""
def __init__(
self,
hostname,
snmp_version="v3",
snmp_port=161,
community=None,
user="",
auth_key="",
encrypt_key="",
auth_proto="sha",
encrypt_proto="aes128",
):
# Check that the SNMP version is matching predefined type or raise ValueError
if snmp_version == "v1" or snmp_version == "v2c":
if not community:
raise ValueError("SNMP version v1/v2c community must be set.")
elif snmp_version == "v3":
if not user:
raise ValueError("SNMP version v3 user and password must be set")
else:
raise ValueError("SNMP version must be set to 'v1', 'v2c' or 'v3'")
# Check that the SNMPv3 auth & priv parameters match allowed types
self._snmp_v3_authentication = {
"sha": cmdgen.usmHMACSHAAuthProtocol,
"md5": cmdgen.usmHMACMD5AuthProtocol,
}
self._snmp_v3_encryption = {
"des": cmdgen.usmDESPrivProtocol,
"3des": cmdgen.usm3DESEDEPrivProtocol,
"aes128": cmdgen.usmAesCfb128Protocol,
"aes192": cmdgen.usmAesCfb192Protocol,
"aes256": cmdgen.usmAesCfb256Protocol,
}
if auth_proto not in self._snmp_v3_authentication.keys():
raise ValueError(
"SNMP V3 'auth_proto' argument must be one of the following: {}".format(
self._snmp_v3_authentication.keys()
)
)
if encrypt_proto not in self._snmp_v3_encryption.keys():
raise ValueError(
"SNMP V3 'encrypt_proto' argument must be one of the following: {}".format(
self._snmp_v3_encryption.keys()
)
)
self.hostname = hostname
self.snmp_version = snmp_version
self.snmp_port = snmp_port
self.community = community
self.user = user
self.auth_key = auth_key
self.encrypt_key = encrypt_key
self.auth_proto = self._snmp_v3_authentication[auth_proto]
self.encryp_proto = self._snmp_v3_encryption[encrypt_proto]
self._response_cache = {}
def _get_snmpv3(self, oid):
"""
Try to send an SNMP GET operation using SNMPv3 for the specified OID.
Parameters
----------
oid : str
The SNMP OID that you want to get.
Returns
-------
string : str
The string as part of the value from the OID you are trying to retrieve.
"""
snmp_target = (self.hostname, self.snmp_port)
cmd_gen = cmdgen.CommandGenerator()
(error_detected, error_status, error_index, snmp_data) = cmd_gen.getCmd(
cmdgen.UsmUserData(
self.user,
self.auth_key,
self.encrypt_key,
authProtocol=self.auth_proto,
privProtocol=self.encryp_proto,
),
cmdgen.UdpTransportTarget(snmp_target, timeout=1.5, retries=2),
oid,
lookupNames=True,
lookupValues=True,
)
if not error_detected and snmp_data[0][1]:
return str(snmp_data[0][1])
return ""
def _get_snmpv2c(self, oid):
"""
Try to send an SNMP GET operation using SNMPv2 for the specified OID.
Parameters
----------
oid : str
The SNMP OID that you want to get.
Returns
-------
string : str
The string as part of the value from the OID you are trying to retrieve.
"""
snmp_target = (self.hostname, self.snmp_port)
cmd_gen = cmdgen.CommandGenerator()
(error_detected, error_status, error_index, snmp_data) = cmd_gen.getCmd(
cmdgen.CommunityData(self.community),
cmdgen.UdpTransportTarget(snmp_target, timeout=1.5, retries=2),
oid,
lookupNames=True,
lookupValues=True,
)
if not error_detected and snmp_data[0][1]:
return str(snmp_data[0][1])
return ""
def _get_snmp(self, oid):
"""Wrapper for generic SNMP call."""
if self.snmp_version in ["v1", "v2c"]:
return self._get_snmpv2c(oid)
else:
return self._get_snmpv3(oid)
def autodetect(self):
"""
Try to guess the device_type using SNMP GET based on the SNMP_MAPPER dict. The type which
is returned is directly matching the name in *netmiko.ssh_dispatcher.CLASS_MAPPER_BASE*
dict.
Thus you can use this name to retrieve automatically the right ConnectionClass
Returns
-------
potential_type : str
The name of the device_type that must be running.
"""
# Convert SNMP_MAPPER to a list and sort by priority
snmp_mapper_list = []
for k, v in SNMP_MAPPER.items():
snmp_mapper_list.append({k: v})
snmp_mapper_list = sorted(
snmp_mapper_list, key=lambda x: list(x.values())[0]["priority"]
)
snmp_mapper_list.reverse()
for entry in snmp_mapper_list:
for device_type, v in entry.items():
oid = v["oid"]
regex = v["expr"]
# Used cache data if we already queryied this OID
if self._response_cache.get(oid):
snmp_response = self._response_cache.get(oid)
else:
snmp_response = self._get_snmp(oid)
self._response_cache[oid] = snmp_response
# See if we had a match
if re.search(regex, snmp_response):
return device_type
return None
Zerion Mini Shell 1.0