Update core scanner, parser, SSH client, and UI
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
# parser.py - Parse 'show lldp neighbors' output from FS switches
|
||||
# parser.py - Parse LLDP output from FS, HP/Aruba ProCurve, and Dell switches
|
||||
import re
|
||||
|
||||
|
||||
def normalize_mac(mac_str):
|
||||
"""Normalize any MAC format to XX-XX-XX-XX-XX-XX uppercase (e.g. 649d99-aa5100 → 64-9D-99-AA-51-00)."""
|
||||
clean = re.sub(r'[:\-\.\s]', '', mac_str).upper()
|
||||
if len(clean) != 12:
|
||||
return mac_str.strip()
|
||||
return '-'.join(clean[i:i+2] for i in range(0, 12, 2))
|
||||
|
||||
|
||||
def shorten_interface(iface):
|
||||
"""GigabitEthernet 1/9 -> Gi1/9, TenGigabitEthernet 1/1 -> Te1/1 etc."""
|
||||
replacements = [
|
||||
@@ -67,6 +75,101 @@ def parse_lldp_neighbors(raw_output, local_chassis_id, local_hostname, local_mgm
|
||||
return neighbors
|
||||
|
||||
|
||||
def parse_aruba_procurve_local(raw_output):
|
||||
"""Parse 'show lldp info local-device' from HP/Aruba switch into a dict."""
|
||||
chassis_id = ''
|
||||
system_name = ''
|
||||
system_desc = ''
|
||||
mgmt_ip = ''
|
||||
|
||||
for line in raw_output.splitlines():
|
||||
m = re.search(r'Chassis Id\s*:\s*(.+)', line, re.IGNORECASE)
|
||||
if m:
|
||||
chassis_id = normalize_mac(m.group(1).strip())
|
||||
m = re.search(r'System Name\s*:\s*(.+)', line, re.IGNORECASE)
|
||||
if m:
|
||||
system_name = m.group(1).strip()
|
||||
m = re.search(r'System Description\s*:\s*(.+)', line, re.IGNORECASE)
|
||||
if m:
|
||||
system_desc = m.group(1).strip()
|
||||
m = re.search(r'Address\s*:\s*([\d\.]+)', line, re.IGNORECASE)
|
||||
if m and '.' in m.group(1):
|
||||
mgmt_ip = m.group(1).strip()
|
||||
|
||||
return {'chassis_id': chassis_id, 'system_name': system_name,
|
||||
'system_desc': system_desc, 'mgmt_ip': mgmt_ip}
|
||||
|
||||
|
||||
def parse_aruba_procurve_neighbors(raw_output):
|
||||
"""
|
||||
Parse 'show lldp info remote-device' tabular output from HP/Aruba switch.
|
||||
Columns: LocalPort | ChassisId PortId PortDescr SysName
|
||||
"""
|
||||
neighbors = []
|
||||
in_data = False
|
||||
|
||||
for line in raw_output.splitlines():
|
||||
if re.match(r'\s*-+\s*\+', line):
|
||||
in_data = True
|
||||
continue
|
||||
if not in_data or '|' not in line or not line.strip():
|
||||
continue
|
||||
|
||||
left, right = line.split('|', 1)
|
||||
local_port = left.strip()
|
||||
parts = right.split()
|
||||
if len(parts) < 4:
|
||||
continue
|
||||
|
||||
chassis_id = normalize_mac(parts[0])
|
||||
port_id = parts[1]
|
||||
sys_name = parts[3]
|
||||
|
||||
neighbors.append({
|
||||
'local_port': local_port,
|
||||
'chassis_id': chassis_id,
|
||||
'port_id': port_id,
|
||||
'port_desc': '',
|
||||
'system_name': sys_name,
|
||||
'system_desc': '',
|
||||
'mgmt_ip': '',
|
||||
'capabilities': '',
|
||||
'remote_port': port_id,
|
||||
})
|
||||
|
||||
return neighbors
|
||||
|
||||
|
||||
def parse_neighbor_description(system_desc):
|
||||
"""
|
||||
Extract (model, firmware) from an LLDP system description string.
|
||||
Handles Dell OS10, HP ProCurve, and Aruba ArubaOS-Switch descriptions.
|
||||
Returns ('', '') if unrecognized.
|
||||
"""
|
||||
if not system_desc:
|
||||
return '', ''
|
||||
|
||||
# Dell OS10: "...System Type: S4112F-ON...OS Version: 10.5.4.7..."
|
||||
if 'Dell' in system_desc or 'OS10' in system_desc:
|
||||
model_m = re.search(r'System Type\s*:\s*([\w\-]+)', system_desc)
|
||||
fw_m = re.search(r'OS Version\s*:\s*([\d\s\.]+)', system_desc)
|
||||
model = f"Dell {model_m.group(1)}" if model_m else 'Dell'
|
||||
firmware = re.sub(r'\s+', '', fw_m.group(1)).strip('.') if fw_m else ''
|
||||
return model, firmware
|
||||
|
||||
# HP ProCurve / Aruba ArubaOS-Switch:
|
||||
# "Aruba JL258A 2930F-8G-PoE+-2SFP+ Switch, revision WC.16.10.0012, ROM ..."
|
||||
# "HP J9576A 3800-24G-PoE+-2SFP+ Switch, revision K.16.02.0019, ROM ..."
|
||||
if re.match(r'(?:Aruba|HP)\s', system_desc, re.IGNORECASE):
|
||||
model_m = re.match(r'((?:Aruba|HP)\s+\S+\s+[\w\-\+]+)', system_desc, re.IGNORECASE)
|
||||
fw_m = re.search(r'revision\s+(\S+?)(?:[,\s]|$)', system_desc)
|
||||
model = model_m.group(1) if model_m else system_desc.split(',')[0]
|
||||
firmware = fw_m.group(1) if fw_m else ''
|
||||
return model, firmware
|
||||
|
||||
return '', ''
|
||||
|
||||
|
||||
def parse_hostname_from_prompt(prompt_line):
|
||||
"""Extract hostname from CLI prompt like 'ls-vhls-sw01#'"""
|
||||
m = re.match(r'^([A-Za-z0-9_\-]+)[>#]', prompt_line.strip())
|
||||
@@ -75,14 +178,29 @@ def parse_hostname_from_prompt(prompt_line):
|
||||
|
||||
def parse_mgmt_ip_from_interfaces(raw_output):
|
||||
"""
|
||||
Parse 'show ip interface brief' to find management VLAN IP.
|
||||
Looks for Vlan interfaces with an IP assigned.
|
||||
Returns first Vlan IP found (typically the management VLAN).
|
||||
Parse 'show ip interface brief' to find management IP.
|
||||
Handles 'Vlan100' and 'Vlan 100' (space-separated) formats.
|
||||
Prefers Vlan interfaces; falls back to Management interfaces.
|
||||
"""
|
||||
lines = raw_output.splitlines()
|
||||
for line in lines:
|
||||
# Match lines like: Vlan100 192.168.1.10 YES ...
|
||||
m = re.match(r'\s*(Vlan\S+)\s+([\d\.]+)\s+', line, re.IGNORECASE)
|
||||
if m and not m.group(2).startswith('0.0.0.0'):
|
||||
return m.group(2)
|
||||
return None
|
||||
IPV4 = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
|
||||
fallback = None
|
||||
for line in raw_output.splitlines():
|
||||
line_stripped = line.strip()
|
||||
if not line_stripped:
|
||||
continue
|
||||
# Match lines belonging to a Vlan or Management interface
|
||||
iface_m = re.match(r'(Vlan|Management|Mgmt)\s*\S*', line_stripped, re.IGNORECASE)
|
||||
if not iface_m:
|
||||
continue
|
||||
# Extract first valid IPv4 address anywhere on the line
|
||||
ip_m = IPV4.search(line)
|
||||
if not ip_m:
|
||||
continue
|
||||
ip = ip_m.group(1)
|
||||
if ip.startswith('0.0.0.0') or ip.startswith('127.'):
|
||||
continue
|
||||
if re.match(r'Vlan', iface_m.group(1), re.IGNORECASE):
|
||||
return ip
|
||||
if not fallback:
|
||||
fallback = ip
|
||||
return fallback
|
||||
|
||||
Reference in New Issue
Block a user