|
#!/usr/bin/env python3 |
|
|
|
import json |
|
import argparse |
|
import subprocess |
|
|
|
"""nassoc.py |
|
|
|
This script retrieves wlan clients per AP from a WLC via SNMP. |
|
|
|
snmpwalk must be installed, and PATH execluting this script |
|
must contain the path to snmpwalk. |
|
|
|
It uses following OIDs: |
|
- 1.3.6.1.4.1.9.9.513.1.1.1.1.5 cLApName |
|
- 1.3.6.1.4.1.9.9.513.1.1.1.1.54 cLApAssociatedClientCount |
|
- 1.3.6.1.4.1.9.9.513.1.1.1.1.49 cLApLocation |
|
|
|
These object return values with associating OIDs like |
|
".1.3.6.1.4.1.9.9.513.1.1.1.1.49.204.213.57.137.43.144". 6 octets at |
|
the end of the OID indicates an AP (but I don't know what the octets |
|
mean). I need more investigation on Csico SNMP Object Navigator. |
|
|
|
https://snmp.cloudapps.cisco.com/Support/SNMP/do/BrowseOID.do?local=en&translate=Translate&objectInput=1.3.6.1.4.1.9.9.513.1.1.1.1.54#oidContent |
|
|
|
""" |
|
|
|
|
|
class AP(object): |
|
|
|
def __init__(self, name = None, location = None, nclients = 0): |
|
self.name = name |
|
self.location = location |
|
self.nclients = nclients |
|
|
|
def todict(self): |
|
return { |
|
"name": self.name, |
|
"location": self.location, |
|
"nclients": self.nclients, |
|
} |
|
|
|
|
|
def run_snmpwalk(target, version, community, oid, timeout = 10): |
|
cmd = [ "snmpwalk", "-On", "-v", version, "-c", community, target, oid ] |
|
out = subprocess.check_output(cmd, timeout = timeout).decode() |
|
|
|
# OIDs this script uses is generated for associating APs ondemend. |
|
# Thus, if the target WLC has no associated AP, this message returns. |
|
if "No Such Instance currently exists at this OID" in out: |
|
raise RuntimeError("No Such Instance currently exists at " + oid) |
|
|
|
return out.strip().split("\n") |
|
|
|
def obtain_octs_and_value(line): |
|
""" |
|
parse an output line from snmpwalk. This returns 6 cotets |
|
identifier and value |
|
""" |
|
oid, eq, t, value = line.strip().split(" ") |
|
s = oid.split(".") |
|
apid = ".".join(s[len(s) - 6:]) |
|
|
|
# string are enclosed by "". remove it. |
|
if value[0] == '"': |
|
value = value.replace('"', '') |
|
|
|
return apid, value |
|
|
|
|
|
def main(): |
|
|
|
desc = "retrieve number of clients per AP from WLC" |
|
parser = argparse.ArgumentParser(description = desc) |
|
parser.add_argument("-c", "--community", required = True, |
|
help = "community") |
|
parser.add_argument("-v", "--version", default = "2c", |
|
choices = [ "1", "2c" ], |
|
help = "snmp version, default 2c") |
|
parser.add_argument("-t", "--timeout", type = int, default = 5, |
|
help = "timeout") |
|
parser.add_argument("-j", "--json", action = "store_true", default = False, |
|
help = "output in JSON format") |
|
parser.add_argument("target", |
|
help = "SNMP walk target") |
|
|
|
|
|
args = parser.parse_args() |
|
|
|
aps = {} # { "6oct id": AP, "6oct id": AP, ... } |
|
|
|
# 1. obtain AP names and make AP instances |
|
lines = run_snmpwalk(args.target, args.version, args.community, |
|
"1.3.6.1.4.1.9.9.513.1.1.1.1.5", |
|
timeout = args.timeout) |
|
for line in lines: |
|
apid, name = obtain_octs_and_value(line) |
|
aps[apid] = AP(name = name) |
|
|
|
# 2. obtain number of associated clients |
|
lines = run_snmpwalk(args.target, args.version, args.community, |
|
"1.3.6.1.4.1.9.9.513.1.1.1.1.54", |
|
timeout = args.timeout) |
|
for line in lines: |
|
apid, nclients = obtain_octs_and_value(line) |
|
aps[apid].nclients = int(nclients) |
|
|
|
# 3. obtain AP locations |
|
lines = run_snmpwalk(args.target, args.version, args.community, |
|
"1.3.6.1.4.1.9.9.513.1.1.1.1.49", |
|
timeout = args.timeout) |
|
for line in lines: |
|
apid, location = obtain_octs_and_value(line) |
|
aps[apid].location = location |
|
|
|
|
|
# print in JSON format |
|
if args.json: |
|
l = [] |
|
for v in aps.values(): |
|
l.append(v.todict()) |
|
print(json.dumps(l, indent = 4)) |
|
return |
|
|
|
# print in a simple way |
|
|
|
for ap in sorted(aps.values(), key = lambda ap: ap.name): |
|
print("{} ({}): {}".format(ap.name, ap.location, ap.nclients)) |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
main() |