diff options
author | Mike Crute <mcrute@gmail.com> | 2013-09-27 11:31:47 -0400 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2013-09-27 11:32:20 -0400 |
commit | d64e26457e6c891304315a7de17db886493e8039 (patch) | |
tree | 4a4513ec443aa10fd05eaf37cce7bc7c56336ac6 | |
parent | 07a941dd1f316a4facb97e2841764f5244225d95 (diff) | |
download | ubntmfi-d64e26457e6c891304315a7de17db886493e8039.tar.bz2 ubntmfi-d64e26457e6c891304315a7de17db886493e8039.tar.xz ubntmfi-d64e26457e6c891304315a7de17db886493e8039.zip |
Adding unifi stats collection
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | collect_ssh.py | 123 | ||||
-rw-r--r-- | parse_dumped_bodies.py | 2 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | utils.py | 16 |
5 files changed, 142 insertions, 1 deletions
@@ -1,2 +1,3 @@ | |||
1 | *.pyc | 1 | *.pyc |
2 | .sandbox_name | 2 | .sandbox_name |
3 | keystore.py | ||
diff --git a/collect_ssh.py b/collect_ssh.py new file mode 100755 index 0000000..607b774 --- /dev/null +++ b/collect_ssh.py | |||
@@ -0,0 +1,123 @@ | |||
1 | #!/usr/bin/env python | ||
2 | |||
3 | import os | ||
4 | import json | ||
5 | import time | ||
6 | import paramiko | ||
7 | import contextlib | ||
8 | import subprocess | ||
9 | |||
10 | from keystore import USERNAME, PASSWORD, HOSTS | ||
11 | |||
12 | |||
13 | RRA_COMMON = [ | ||
14 | "RRA:AVERAGE:0.5:1:800", | ||
15 | "RRA:AVERAGE:0.5:6:800", | ||
16 | "RRA:AVERAGE:0.5:24:800", | ||
17 | "RRA:AVERAGE:0.5:288:800", | ||
18 | "RRA:MAX:0.5:1:800", | ||
19 | "RRA:MAX:0.5:6:800", | ||
20 | "RRA:MAX:0.5:24:800", | ||
21 | "RRA:MAX:0.5:288:800", | ||
22 | ] | ||
23 | |||
24 | RRD_TEMPLATES = { | ||
25 | "interface": [ | ||
26 | "DS:packets_rx:COUNTER:600:0:U", | ||
27 | "DS:packets_tx:COUNTER:600:0:U", | ||
28 | "DS:bytes_rx:COUNTER:600:0:U", | ||
29 | "DS:bytes_tx:COUNTER:600:0:U", | ||
30 | "DS:errors_rx:COUNTER:600:0:U", | ||
31 | "DS:errors_tx:COUNTER:600:0:U", | ||
32 | "DS:dropped_rx:COUNTER:600:0:U", | ||
33 | "DS:dropped_tx:COUNTER:600:0:U", | ||
34 | ], | ||
35 | "network": [ | ||
36 | "DS:packets_rx:COUNTER:600:0:U", | ||
37 | "DS:packets_tx:COUNTER:600:0:U", | ||
38 | "DS:bytes_rx:COUNTER:600:0:U", | ||
39 | "DS:bytes_tx:COUNTER:600:0:U", | ||
40 | "DS:retries_tx:COUNTER:600:0:U", | ||
41 | "DS:stations:GAUGE:600:0:U", | ||
42 | ], | ||
43 | "station": [ | ||
44 | "DS:rssi:GAUGE:600:0:U", | ||
45 | "DS:noise:GAUGE:600:U:U", | ||
46 | "DS:signal:GAUGE:600:U:U", | ||
47 | "DS:power:GAUGE:600:0:U", | ||
48 | "DS:rate_rx:GAUGE:600:0:U", | ||
49 | "DS:rate_tx:GAUGE:600:0:U", | ||
50 | "DS:packets_rx:COUNTER:600:0:U", | ||
51 | "DS:packets_tx:COUNTER:600:0:U", | ||
52 | "DS:bytes_rx:COUNTER:600:0:U", | ||
53 | "DS:bytes_tx:COUNTER:600:0:U", | ||
54 | ], | ||
55 | } | ||
56 | |||
57 | |||
58 | @contextlib.contextmanager | ||
59 | def get_mca_data(host): | ||
60 | client = paramiko.SSHClient() | ||
61 | client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy()) | ||
62 | client.connect(host, username=USERNAME, password=PASSWORD) | ||
63 | _, stdout, _ = client.exec_command("mca-dump") | ||
64 | |||
65 | yield json.loads(stdout.read()) | ||
66 | |||
67 | client.close() | ||
68 | |||
69 | |||
70 | @contextlib.contextmanager | ||
71 | def host_folder(host): | ||
72 | if not os.path.exists(host): | ||
73 | os.mkdir(host) | ||
74 | |||
75 | os.chdir(host) | ||
76 | yield | ||
77 | os.chdir(os.path.dirname(os.getcwd())) | ||
78 | |||
79 | |||
80 | def rrd_update(kind, name, source, *stats): | ||
81 | stats = ":".join(["N"] + list(str(int(source[key])) for key in stats)) | ||
82 | rrd_name = "{}-{}.rrd".format(kind, name) | ||
83 | |||
84 | if not os.path.exists(rrd_name): | ||
85 | command = ["rrdtool", "create", rrd_name] | ||
86 | command.extend(RRD_TEMPLATES[kind]) | ||
87 | command.extend(RRA_COMMON) | ||
88 | subprocess.call(command) | ||
89 | |||
90 | subprocess.call(("rrdtool", "update", rrd_name, stats)) | ||
91 | |||
92 | |||
93 | def update_eth(data): | ||
94 | rrd_update("interface", "eth0", data, "rx_packets", "tx_packets", | ||
95 | "rx_bytes", "tx_bytes", "rx_errors", "tx_errors", "rx_errors", | ||
96 | "tx_errors") | ||
97 | |||
98 | def update_network(data): | ||
99 | rrd_update("network", "{}_{}".format(data["essid"], data["radio"]), data, | ||
100 | "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "tx_retries", | ||
101 | "num_sta") | ||
102 | |||
103 | |||
104 | def update_station(data): | ||
105 | rrd_update("station", data["mac"], data, "rssi", "noise", "signal", | ||
106 | "tx_power", "rx_rate", "tx_rate", "rx_packets", "tx_packets", | ||
107 | "rx_bytes", "tx_bytes") | ||
108 | |||
109 | |||
110 | while True: | ||
111 | for host in HOSTS: | ||
112 | print "Updating {}".format(host) | ||
113 | with host_folder(host), get_mca_data(host) as data: | ||
114 | update_eth(data["if_table"][0]) | ||
115 | |||
116 | for vap in data["vap_table"]: | ||
117 | update_network(vap) | ||
118 | |||
119 | for station in vap["sta_table"]: | ||
120 | update_station(station) | ||
121 | |||
122 | print "=" * 80 | ||
123 | time.sleep(300) | ||
diff --git a/parse_dumped_bodies.py b/parse_dumped_bodies.py index 2bb16a2..4275e68 100644 --- a/parse_dumped_bodies.py +++ b/parse_dumped_bodies.py | |||
@@ -5,7 +5,7 @@ from cStringIO import StringIO | |||
5 | from inform import InformSerializer, Cryptor | 5 | from inform import InformSerializer, Cryptor |
6 | 6 | ||
7 | 7 | ||
8 | PATH = "/Users/mcrute/Desktop/test" | 8 | PATH = "/Users/mcrute/Desktop/test2" |
9 | 9 | ||
10 | 10 | ||
11 | for file in os.listdir(PATH): | 11 | for file in os.listdir(PATH): |
diff --git a/requirements.txt b/requirements.txt index 390e1fe..3c93d5c 100644 --- a/requirements.txt +++ b/requirements.txt | |||
@@ -1,2 +1,3 @@ | |||
1 | tornado==3.1 | 1 | tornado==3.1 |
2 | pycrypto==2.6 | 2 | pycrypto==2.6 |
3 | paramiko==1.11.1 | ||
diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..2226108 --- /dev/null +++ b/utils.py | |||
@@ -0,0 +1,16 @@ | |||
1 | def decode_station_band(sta): | ||
2 | tx = sta["tx_rate"] / 1000 | ||
3 | rx = sta["rx_rate"] / 1000 | ||
4 | is_a = sta.get("is_11a", None) | ||
5 | is_n = sta.get("is_11n", False) | ||
6 | |||
7 | if is_n: | ||
8 | return "N" | ||
9 | |||
10 | if is_a: | ||
11 | return "A" | ||
12 | |||
13 | if not is_n and any((rx > 11, rx > 11)): | ||
14 | return "G" | ||
15 | else: | ||
16 | return "B" | ||