0x2142 / switchport-web-dashboard Goto Github PK
View Code? Open in Web Editor NEWA simple web dashboard to display network switch port capacity
License: The Unlicense
A simple web dashboard to display network switch port capacity
License: The Unlicense
Hello Mate,
You are doing great job and I am learning so much from your posts time to time.
now coming to issues I am facing, I have modified codes per my requirement which is very simple to add count of ports with traffic[in_octet] so added 2 more key in below dict.
interfaceStats = {
"total_port": 0,
"up_port": 0,
"down_port": 0,
"disabled_port": 0,
"in_octet_zero": 0,
"in_octet_traffic": 0,
"intop10m": 0,
"intop100m": 0,
"intop1g": 0,
"intop10g": 0,
"intop25g": 0,
"intop40g": 0,
"intop100g": 0,
"intmedcop": 0,
"intmedsfp": 0,
"intmedvirtual": 0,
}
this is under file data_collector.py.
attaching modified code below.
import os
import yaml
from scrapli.driver.core import IOSXEDriver, NXOSDriver, IOSXRDriver
import switchdb
def loadDevices():
"""
Load device inventory from config.yml
"""
print("Loading devices from config file...")
with open("config.yml", "r") as config:
devicelist = yaml.full_load(config)
return devicelist["Devices"]
def connectToDevice(deviceconfig):
"""
Parse device config data & open SSH connection
"""
print("Loading device configuration...")
device = {}
device["host"] = deviceconfig["address"]
device["auth_username"] = deviceconfig["username"]
device["auth_password"] = deviceconfig["password"]
device["auth_strict_key"] = False
device["timeout_socket"] = 10
device["timeout_ops"] = 10
try:
device["port"] = deviceconfig["port"]
except KeyError:
pass
if deviceconfig["type"] == "ios-xe":
conn = IOSXEDriver(**device)
elif deviceconfig["type"] == "nx-os":
conn = NXOSDriver(**device)
try:
print(f"Attempting connection to {device['host']}")
conn.open()
print(f"Successfully connected to {device['host']}")
except Exception as e:
print(f"Failed connection to {device['host']}")
print("Error message is: %s" % e)
return None
return conn
def getInterfaceInfo(device):
"""
Issue 'Show Interfaces' command to device
Process data & populate dict with interface status
"""
# Send command to device
if type(device) == IOSXEDriver:
resp = device.send_command("show interfaces")
if type(device) == NXOSDriver:
resp = device.send_command("show interface")
# Save a copy of the raw output
save_raw_output(resp)
# Parse raw CLI using Genie
intdata = resp.genie_parse_output()
interfaceStats = {
"total_port": 0,
"up_port": 0,
"down_port": 0,
"disabled_port": 0,
"in_octet_zero": 0,
"in_octet_traffic": 0,
"intop10m": 0,
"intop100m": 0,
"intop1g": 0,
"intop10g": 0,
"intop25g": 0,
"intop40g": 0,
"intop100g": 0,
"intmedcop": 0,
"intmedsfp": 0,
"intmedvirtual": 0,
}
# Init dict for detailed interface operational stat collection
intDetailed = {}
# Process each interface
for iface in intdata:
# Skip VLAN / PortChannel Interfaces
if "Ethernet" not in iface:
print(f"found non-ethernet interface: {iface}")
continue
if "GigabitEthernet0/0" in iface:
print(f"found management interface: {iface}")
continue
print(f"Working on interface {iface}")
# Collect detailed interface stats (name, oper status, description, MAC)
intDetailed[iface] = {}
intDetailed[iface]["oper_status"] = intdata[iface]["oper_status"]
try:
intDetailed[iface]["description"] = intdata[iface]["description"]
except:
intDetailed[iface]["description"] = "N/A"
intDetailed[iface]["phys_addr"] = intdata[iface]["phys_address"]
intDetailed[iface]["oper_speed"] = intdata[iface]["bandwidth"]
try:
intDetailed[iface]["in_octet"] = intdata[iface]["counters"]["in_octets"]
except KeyError:
pass
# Count all Ethernet interfaces
interfaceStats["total_port"] += 1
# Count admin-down interfaces
if not intdata[iface]["enabled"]:
interfaceStats["disabled_port"] += 1
# Count 'not connected' interfaces
elif intdata[iface]["enabled"] and intdata[iface]["oper_status"] == "down":
interfaceStats["down_port"] += 1
# Count up / connected interfaces - Then collect current speeds
elif intdata[iface]["enabled"] and intdata[iface]["oper_status"] == "up":
interfaceStats["up_port"] += 1
speed = intdata[iface]["bandwidth"]
if speed == 10000:
interfaceStats["intop10m"] += 1
if speed == 100000:
interfaceStats["intop100m"] += 1
if speed == 1000000:
interfaceStats["intop1g"] += 1
if speed == 10000000:
interfaceStats["intop10g"] += 1
if speed == 25000000:
interfaceStats["intop25g"] += 1
if speed == 40000000:
interfaceStats["intop40g"] += 1
if speed == 100000000:
interfaceStats["intop100g"] += 1
# Count number of interfaces by media type
try:
media = intdata[iface]["media_type"]
if "1000BaseTX" in media:
interfaceStats["intmedcop"] += 1
elif "Virtual" in media:
interfaceStats["intmedvirtual"] += 1
else:
interfaceStats["intmedsfp"] += 1
except KeyError:
interfaceStats["intmedsfp"] += 1
# In_octet == 0
try:
if intdata[iface]['counters']['in_octets'] <= 0:
interfaceStats["in_octet_zero"] += 1
except KeyError:
pass
try:
if intdata[iface]['counters']['in_octets'] > 0:
interfaceStats["in_octet_traffic"] += 1
except KeyError:
pass
# When complete - return int stats list
return interfaceStats, intDetailed
def save_raw_output(data):
"""
Creates a local working directory where all raw CLI
output is stored.
"""
# Create local directory to store raw output
if not os.path.exists("raw_output"):
os.makedirs("raw_output")
# Dump port information to file
with open(f"raw_output/{system_serial}.txt", "w") as a:
a.write(data.result)
def getSystemInfoXE(device):
"""
-- FOR IOS-XE DEVICES --
Issue 'Show Version' command to device
Return serial number, model, current software version
"""
resp = device.send_command("show version")
parsed = resp.genie_parse_output()
sysinfo = {}
sysinfo["serial"] = parsed["version"]["chassis_sn"]
sysinfo["model"] = parsed["version"]["chassis"]
sysinfo["sw_ver"] = parsed["version"]["version"]
global system_serial
system_serial = sysinfo["serial"]
return sysinfo
def getSystemInfoNX(device):
"""
-- FOR NX-OS DEVICES --
Issue 'Show Version' command to device
Return serial number, model, current software version
"""
resp = device.send_command("show version")
parsed = resp.genie_parse_output()
sysinfo = {}
sysinfo["serial"] = parsed["platform"]["hardware"]["processor_board_id"]
sysinfo["model"] = parsed["platform"]["hardware"]["model"]
sysinfo["sw_ver"] = parsed["platform"]["software"]["system_version"]
global system_serial
system_serial = sysinfo["serial"]
return sysinfo
def addDeviceToDB(devicelist):
"""
Update DB entries for each switch from the config file
"""
print("Opening DB connection...")
swDB = switchdb.DB()
# Get a list of current switches in the database
# Compare between new config file - see what should be added/removed
curswitches = swDB.getAllSummary()
currentSwitches = [row[3] for row in curswitches]
newSwitches = [devicelist[switch]["address"] for switch in devicelist]
swRemove = set(currentSwitches).difference(newSwitches)
swAdd = set(newSwitches).difference(currentSwitches)
# If switches to remove, purge from the database
if len(swRemove) > 0:
print(f"Found {len(swRemove)} switches no longer in config file")
for switchIP in swRemove:
print(f"Removing switch ({switchIP}) from DB...")
swDB.deleteSwitch(switchIP)
print("Adding devices to DB...")
for switch in devicelist:
switchIP = devicelist[switch]["address"]
if switchIP in swAdd:
print(f"Adding switch ({switch} / {switchIP}) to DB...")
swDB.addSwitch(str(switch), str(switchIP))
else:
print(f"Switch ({switch} / {switchIP}) already in DB. Skipping...")
swDB.close()
def updateDB(device, ip, sysinfo, portinfo, detailedinfo):
"""
Insert new system & port information
into the database
"""
swDB = switchdb.DB()
print(f"Updating system info for {device} in DB...")
swDB.updateSysInfo(device, ip, sysinfo)
print(f"Updating port info for {device} in DB...")
swDB.updatePorts(device, ip, portinfo)
print(f"Updating detailed port info for {device} in DB...")
swDB.updateInterfaceDetails(device, ip, sysinfo, detailedinfo)
swDB.close()
def updateLastRun():
"""
Call to DB - update last run time
"""
swDB = switchdb.DB()
print("Updating last run time in DB...")
swDB.updateLastRun()
swDB.close()
def updateCheckStatus(device, ip, status):
"""
Update the last_check database field,
which indicates if the check passed or failed
"""
swDB = switchdb.DB()
print(f"Updating check status for {device} to {status}")
swDB.updateStatus(device, ip, status)
swDB.close()
def run():
"""
Primay function to manage device data collection
"""
# Load all of our devices from config, then add to DB
devicelist = loadDevices()
addDeviceToDB(devicelist)
# Iterate through each device for processing
for device in devicelist:
dev = device
ip = devicelist[device]["address"]
# Open device connection
devcon = connectToDevice(devicelist[device])
if devcon:
try:
# Query device for system & port info
if type(devcon) == IOSXEDriver:
sysinfo = getSystemInfoXE(devcon)
if type(devcon) == NXOSDriver:
sysinfo = getSystemInfoNX(devcon)
portinfo, detailedinfo = getInterfaceInfo(devcon)
except Exception as e:
print(f"ERROR: {e}")
updateCheckStatus(device, ip, False)
continue
# Update database with new info
updateDB(dev, ip, sysinfo, portinfo, detailedinfo)
# Update database with interface detail info
# Update if check succeeeded
updateCheckStatus(dev, ip, True)
else:
# Update DB if last check failed
updateCheckStatus(device, ip, False)
# Finally, update the last-run time!
updateLastRun()
if __name__ == "__main__":
run()
And further below is Database file, I had edited codes in DB files but seems I can't get it fixed.
import sqlite3
from datetime import datetime
from sqlite3 import Error
class DB:
def __init__(self):
self.openDB()
self.createDB()
self.initLastUpdate()
def openDB(self):
"""
Open SQLlite DB
"""
self.conn = None
try:
self.conn = sqlite3.connect("./sw-util.db")
except Error as e:
print(e)
def createDB(self):
"""
Create new table to contain switch info & port utilization data
"""
sw_info_table = """ CREATE TABLE IF NOT EXISTS switches (
name text NOT NULL,
serial text DEFAULT "Not Polled Yet",
model text DEFAULT "N/A",
sw_ver text DEFAULT "N/A",
mgmt_ip text NOT NULL PRIMARY KEY,
last_check boolean DEFAULT False,
total_port integer DEFAULT 0,
up_port integer DEFAULT 0,
down_port integer DEFAULT 0,
disabled_port integer DEFAULT 0,
in_octet_zero integer DEFAULT 0,
in_octet_traffic integer DEFAULT 0,
intop10m integer DEFAULT 0,
intop100m integer DEFAULT 0,
intop1g integer DEFAULT 0,
intop10g integer DEFAULT 0,
intop25g integer DEFAULT 0,
intop40g integer DEFAULT 0,
intop100g integer DEFAULT 0,
intmedcop integer DEFAULT 0,
intmedsfp integer DEFAULT 0,
intmedvirt integer DEFAULT 0
); """
last_update_table = """ CREATE TABLE IF NOT EXISTS last_update (
id integer NOT NULL PRIMARY KEY,
lastrun text NOT NULL
); """
interface_detail_table = """ CREATE TABLE IF NOT EXISTS interface_detailed (
sw_name text NOT NULL,
mgmt_ip text NOT NULL,
int_name text NOT NULL,
oper_status text NOT NULL,
description text DEFAULT "N/A",
phys_address text NOT NULL PRIMARY KEY,
serial text NOT NULL,
oper_speed text NOT NULL,
in_octet text NOT NULL
); """
cur = self.conn.cursor()
cur.execute(sw_info_table)
cur.execute(last_update_table)
cur.execute(interface_detail_table)
def addSwitch(self, name, mgmt_ip):
"""
Insert new switch into DB
"""
sql = """ INSERT INTO switches(name,mgmt_ip) values(?,?); """
cur = self.conn.cursor()
try:
cur.execute(sql, (name, mgmt_ip))
self.conn.commit()
except sqlite3.IntegrityError:
print(f"Switch {name} with IP: {mgmt_ip} already exists in DB.")
def updateSysInfo(self, name, mgmt_ip, sysinfo):
"""
Update switch system info:
Model number, software version, and serial number
"""
sql = """ UPDATE switches
SET serial = ?,
model = ?,
sw_ver = ?
WHERE name = ?
AND mgmt_ip = ?;
"""
cur = self.conn.cursor()
cur.execute(
sql, (sysinfo["serial"], sysinfo["model"], sysinfo["sw_ver"], name, mgmt_ip)
)
self.conn.commit()
return
def updatePorts(self, name, mgmt_ip, portinfo):
"""
Update port count information
"""
sql = """ UPDATE switches
SET
total_port = ?,
up_port = ?,
down_port = ?,
disabled_port = ?,
in_octet_zero = ?,
in_octet_traffic = ?,
intop10m = ?,
intop100m = ?,
intop1g = ?,
intop10g = ?,
intop25g = ?,
intop40g = ?,
intop100g = ?,
intmedcop = ?,
intmedsfp = ?,
intmedvirt = ?
WHERE name = ?
AND mgmt_ip = ?;
"""
cur = self.conn.cursor()
cur.execute(
sql,
(
portinfo["total_port"],
portinfo["up_port"],
portinfo["down_port"],
portinfo["disabled_port"],
portinfo["in_octet_zero"],
portinfo["in_octet_traffic"],
portinfo["intop10m"],
portinfo["intop100m"],
portinfo["intop1g"],
portinfo["intop10g"],
portinfo["intop25g"],
portinfo["intop40g"],
portinfo["intop100g"],
portinfo["intmedcop"],
portinfo["intmedsfp"],
portinfo["intmedvirtual"],
name,
mgmt_ip,
),
)
self.conn.commit()
return
def updateInterfaceDetails(self, name, mgmt_ip, sysinfo, portdetails):
"""
Update interface detailed status per switch:
Interface name, operational status, description, and physical address
"""
sql = """ INSERT INTO interface_detailed(
int_name,
oper_status,
description,
phys_address,
oper_speed,
in_octet,
sw_name,
mgmt_ip,
serial
)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(phys_address) DO UPDATE
SET int_name = ?,
oper_status = ?,
description = ?,
phys_address = ?,
oper_speed = ?,
in_octet = ?,
serial = ?
WHERE sw_name = ?
AND mgmt_ip = ?;
"""
cur = self.conn.cursor()
for interface in portdetails:
cur.execute(
sql,
(
interface,
portdetails[interface]["oper_status"],
portdetails[interface]["description"],
portdetails[interface]["phys_addr"],
portdetails[interface]["oper_speed"],
portdetails[interface]["in_octet"],
name,
mgmt_ip,
sysinfo["serial"],
interface,
portdetails[interface]["oper_status"],
portdetails[interface]["description"],
portdetails[interface]["phys_addr"],
portdetails[interface]["oper_speed"],
portdetails[interface]["in_octet"],
sysinfo["serial"],
name,
mgmt_ip,
),
)
self.conn.commit()
return
def getSwitch(self, name, mgmt_ip):
"""
Retrieve switch information
"""
sql = """ SELECT * FROM switches
WHERE name = ? AND mgmt_ip = ?; """
cur = self.conn.cursor()
cur.execute(sql, (name, mgmt_ip))
result = cur.fetchall()
return result
def deleteSwitch(self, mgmt_ip):
"""
Remove switch from database
"""
sql = """ DELETE FROM switches
WHERE mgmt_ip = ?; """
cur = self.conn.cursor()
cur.execute(sql, [mgmt_ip])
result = cur.fetchall()
return result
def getNetworkWideStats(self):
"""
Retrieve network-wide port count information
"""
sql = """ SELECT model, sw_ver, total_port, up_port, down_port,
disabled_port, in_octet_zero, in_octet_traffic, intop10m, intop100m, intop1g, intop10g,
intop25g, intop40g, intop100g,intmedcop, intmedsfp,
intmedvirt FROM switches; """
cur = self.conn.cursor()
cur.execute(sql)
result = cur.fetchall()
return result
def getAllSummary(self):
"""
Retrieve info from ALL switches in DB.
"""
sql = """ SELECT name, serial, sw_ver, mgmt_ip, last_check,
total_port, up_port, down_port, disabled_port
FROM switches; """
cur = self.conn.cursor()
cur.execute(sql)
result = cur.fetchall()
return result
def getSwitchDetail(self, serial):
"""
Retrieve info from ALL switches in DB.
"""
sql = """ SELECT * FROM switches WHERE serial = ?; """
cur = self.conn.cursor()
cur.execute(sql, [serial])
result = cur.fetchall()
return result
def getInterfaceDetail(self, serial):
"""
Retrieve interface detailed info
"""
sql = """ SELECT int_name, description, phys_address, oper_status,
oper_speed, in_octet
FROM interface_detailed WHERE serial = ? """
cur = self.conn.cursor()
cur.execute(sql, [serial])
result = cur.fetchall()
return result
def updateStatus(self, name, mgmt_ip, status):
"""
Update only the last_check column with
whether or not the last polling succeeded
"""
sql = """ UPDATE switches SET last_check = ?
WHERE name = ? AND mgmt_ip = ?; """
cur = self.conn.cursor()
cur.execute(sql, (status, name, mgmt_ip))
self.conn.commit()
print("DB Update completed")
return
def updateLastRun(self):
"""
Updates single entry that contains last run time
"""
sql = """ UPDATE last_update
SET lastrun = ?
WHERE id = 1;
"""
now = datetime.now()
timestamp = now.strftime("%B, %d, %Y %H:%M:%S")
cur = self.conn.cursor()
cur.execute(sql, [timestamp])
self.conn.commit()
return
def getLastUpdate(self):
"""
Return last runtime
"""
sql = """ SELECT lastrun from last_update WHERE id = 1;
"""
cur = self.conn.cursor()
cur.execute(sql)
result = cur.fetchall()
try:
lastupdate = result[0][0]
except:
lastupdate = None
return lastupdate
def initLastUpdate(self):
"""
Initialize data in last_update table
"""
sql = """ INSERT INTO last_update(lastrun) values(?); """
if not self.getLastUpdate():
cur = self.conn.cursor()
cur.execute(sql, ["Never"])
self.conn.commit()
def close(self):
self.conn.close()
I am getting errors which seems like related to html codes in bootstrap but can't overcome it.
attaching error
Traceback (most recent call last):
File "data_collector_v3.py", line 308, in <module>
run()
File "data_collector_v3.py", line 296, in run
updateDB(dev, ip, sysinfo, portinfo, detailedinfo)
File "data_collector_v3.py", line 243, in updateDB
swDB.updatePorts(device, ip, portinfo)
File "/home/vndrtsvp1/VENV/github/switchport-web-dashboard/switchdb.py", line 147, in updatePorts
mgmt_ip,
sqlite3.OperationalError: no such column: in_octet_zero
Thanks in advance for great codes making available to community.
Hello,
I congratulate you for your great effort and work in the development of this project, but you know that I am new to programming and I would like you to help me so that in the tab show all the interfaces (interface name, status, description and macaddress) for each switch in a new table, but I can't do the insert to the table, it just brings me the last value.
Here is my code example added to the file "data_collector.py" :
def getInterfaceDetail(device):
resp = device.send_command("show interfaces")
intdata = resp.genie_parse_output()
for iface in intdata:
if 'Ethernet' not in iface:
#print(f'found non-ethernet interface: {iface}')
continue
if 'GigabitEthernet0/0' in iface:
#print(f'found management interface: {iface}')
continue
print(f"getInterfaceDetail!!!")
inport = {}
inport['iface'] = iface
inport['oper_status'] = intdata[iface]['oper_status']
inport['description'] = intdata[iface]['description']
inport['phys_address'] = intdata[iface]['phys_address']
return inport
{'iface': 'GigabitEthernet1/0/1', 'oper_status': 'down', 'description': 'CONNECTION WITH 1/2/026', 'phys_address': '0064.404c.a701'}
{'iface': 'GigabitEthernet1/0/2', 'oper_status': 'down', 'description': 'CONNECTION WITH 1/6/127', 'phys_address': '0064.404c.a702'}
{'iface': 'GigabitEthernet1/0/3', 'oper_status': 'up', 'description': 'CONNECTION WITH 1/2/045', 'phys_address': '0064.404c.a703'}
{'iface': 'GigabitEthernet1/0/4', 'oper_status': 'up', 'description': 'CONNECTION WITH 1/3/050', 'phys_address': '0064.404c.a704'}
{'iface': 'GigabitEthernet1/0/5', 'oper_status': 'down', 'description': 'CONNECTION WITH 1/2/028', 'phys_address': '0064.404c.a705'}
and this is how it looks in my manually added database:
+----------------------+-------------+-------------------------+----------------+
| iface | oper_status | description | phys_address |
+----------------------+-------------+-------------------------+----------------+
| CNOC_CUICUILCO_SW01 | 10.238.1.1 | NULL | NULL |
| GigabitEthernet1/0/1 | down | CONNECTION WITH 1/2/026 | 0064.404c.a701 |
| GigabitEthernet1/0/2 | down | CONNECTION WITH 1/6/127 | 0064.404c.a702 |
| GigabitEthernet1/0/3 | up | CONNECTION WITH 1/2/045 | 0064.404c.a703 |
| GigabitEthernet1/0/4 | up | CONNECTION WITH 1/3/050 | 0064.404c.a704 |
| GigabitEthernet1/0/5 | down | CONNECTION WITH 1/2/028 | 0064.404c.a705 |
+----------------------+-------------+-------------------------+----------------+
Also I don't know how to update if an interface changes from down to up, or from up to down.
Could you help me please.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.