11#!/usr/bin/env python3
22# -*- encoding: utf-8; py-indent-offset: 4 -*-
3+ ## MIT License
4+ ##
5+ ## Copyright (c) 2021 Bash Club
6+ ##
7+ ## Permission is hereby granted, free of charge, to any person obtaining a copy
8+ ## of this software and associated documentation files (the "Software"), to deal
9+ ## in the Software without restriction, including without limitation the rights
10+ ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+ ## copies of the Software, and to permit persons to whom the Software is
12+ ## furnished to do so, subject to the following conditions:
13+ ##
14+ ## The above copyright notice and this permission notice shall be included in all
15+ ## copies or substantial portions of the Software.
16+ ##
17+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+ ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+ ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+ ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+ ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+ ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+ ## SOFTWARE.
324
425###
5- __VERSION__ = 0.75
26+ __VERSION__ = 0.79
627
728import sys
829import socket
@@ -14,7 +35,127 @@ from statistics import mean
1435from collections import defaultdict
1536
1637from pprint import pprint
17-
38+ try :
39+ import cmk .utils .paths
40+ AGENT_TMP_PATH = cmk .utils .paths .Path (cmk .utils .paths .tmp_dir , "agents/agent_unifi" )
41+ except ImportError :
42+ AGENT_TMP_PATH = None
43+
44+ UNIFI_DEVICE_TABLE = {
45+ 'BZ2' : 'UAP' ,
46+ 'BZ2LR' : 'UAP-LR' ,
47+ 'U2HSR' : 'UAP-Outdoor+' ,
48+ 'U2IW' : 'UAP-IW' ,
49+ 'U2L48' : 'UAP-AC-LR' ,
50+ 'U2Lv2' : 'UAP-AC-LR' ,
51+ 'U2M' : 'UAP-Mini' ,
52+ 'U2O' : 'UAP-Outdoor' ,
53+ 'U2S48' : 'UAP-AC' ,
54+ 'U2Sv2' : 'UAP-AC' ,
55+ 'U5O' : 'UAP-Outdoor5' ,
56+ 'U7E' : 'UAP-AC' ,
57+ 'U7EDU' : 'UAP-AC-EDU' ,
58+ 'U7Ev2' : 'UAP-AC' ,
59+ 'U7HD' : 'UAP-AC-HD' ,
60+ 'U7SHD' : 'UAP-AC-SHD' ,
61+ 'U7NHD' : 'UAP-nanoHD' ,
62+ 'UFLHD' : 'UAP-FlexHD' ,
63+ 'UHDIW' : 'UAP-IW-HD' ,
64+ 'UAIW6' : 'U6-IW' ,
65+ 'UAE6' : 'U6-Extender' ,
66+ 'UAL6' : 'U6-Lite' ,
67+ 'UAM6' : 'U6-Mesh' ,
68+ 'UALR6' : 'U6-LR-EA' ,
69+ 'UAP6' : 'U6-LR' ,
70+ 'UALR6v2' : 'U6-LR' ,
71+ 'UALR6v3' : 'U6-LR' ,
72+ 'UCXG' : 'UAP-XG' ,
73+ 'UXSDM' : 'UWB-XG' ,
74+ 'UXBSDM' : 'UWB-XG-BK' ,
75+ 'UCMSH' : 'UAP-XG-Mesh' ,
76+ 'U7IW' : 'UAP-AC-IW' ,
77+ 'U7IWP' : 'UAP-AC-IW-Pro' ,
78+ 'U7MP' : 'UAP-AC-M-Pro' ,
79+ 'U7LR' : 'UAP-AC-LR' ,
80+ 'U7LT' : 'UAP-AC-Lite' ,
81+ 'U7O' : 'UAP-AC-Outdoor' ,
82+ 'U7P' : 'UAP-Pro' ,
83+ 'U7MSH' : 'UAP-AC-M' ,
84+ 'U7PG2' : 'UAP-AC-Pro' ,
85+ 'p2N' : 'PICOM2HP' ,
86+ 'UDMB' : 'UAP-BeaconHD' ,
87+ 'USF5P' : 'USW-Flex' ,
88+ 'US8' : 'US-8' ,
89+ 'US8P60' : 'US-8-60W' ,
90+ 'US8P150' : 'US-8-150W' ,
91+ 'S28150' : 'US-8-150W' ,
92+ 'USC8' : 'US-8' ,
93+ 'USC8P60' : 'US-8-60W' ,
94+ 'USC8P150' : 'US-8-150W' ,
95+ 'US16P150' : 'US-16-150W' ,
96+ 'S216150' : 'US-16-150W' ,
97+ 'US24' : 'US-24-G1' ,
98+ 'US24PRO' : 'USW-Pro-24-PoE' ,
99+ 'US24PRO2' : 'USW-Pro-24' ,
100+ 'US24P250' : 'US-24-250W' ,
101+ 'US24PL2' : 'US-L2-24-PoE' ,
102+ 'US24P500' : 'US-24-500W' ,
103+ 'S224250' : 'US-24-250W' ,
104+ 'S224500' : 'US-24-500W' ,
105+ 'US48' : 'US-48-G1' ,
106+ 'US48PRO' : 'USW-Pro-48-PoE' ,
107+ 'US48PRO2' : 'USW-Pro-48' ,
108+ 'US48P500' : 'US-48-500W' ,
109+ 'US48PL2' : 'US-L2-48-PoE' ,
110+ 'US48P750' : 'US-48-750W' ,
111+ 'S248500' : 'US-48-500W' ,
112+ 'S248750' : 'US-48-750W' ,
113+ 'US6XG150' : 'US-XG-6PoE' ,
114+ 'USMINI' : 'USW-Flex-Mini' ,
115+ 'USXG' : 'US-16-XG' ,
116+ 'USC8P450' : 'USW-Industrial' ,
117+ 'UDC48X6' : 'USW-Leaf' ,
118+ 'USL8A' : 'UniFi Switch Aggregation' ,
119+ 'USAGGPRO' : 'UniFi Switch Aggregation Pro' ,
120+ 'USL8LP' : 'USW-Lite-8-PoE' ,
121+ 'USL8MP' : 'USW-Mission-Critical' ,
122+ 'USL16P' : 'USW-16-PoE' ,
123+ 'USL16LP' : 'USW-Lite-16-PoE' ,
124+ 'USL24' : 'USW-24-G2' ,
125+ 'USL48' : 'USW-48-G2' ,
126+ 'USL24P' : 'USW-24-PoE' ,
127+ 'USL48P' : 'USW-48-PoE' ,
128+ 'UGW3' : 'USG-3P' ,
129+ 'UGW4' : 'USG-Pro-4' ,
130+ 'UGWHD4' : 'USG' ,
131+ 'UGWXG' : 'USG-XG-8' ,
132+ 'UDM' : 'UDM' ,
133+ 'UDMSE' : 'UDM-SE' ,
134+ 'UDMPRO' : 'UDM-Pro' ,
135+ 'UP4' : 'UVP-X' ,
136+ 'UP5' : 'UVP' ,
137+ 'UP5t' : 'UVP-Pro' ,
138+ 'UP7' : 'UVP-Executive' ,
139+ 'UP5c' : 'UVP' ,
140+ 'UP5tc' : 'UVP-Pro' ,
141+ 'UP7c' : 'UVP-Executive' ,
142+ 'UCK' : 'UCK' ,
143+ 'UCK-v2' : 'UCK' ,
144+ 'UCK-v3' : 'UCK' ,
145+ 'UCKG2' : 'UCK-G2' ,
146+ 'UCKP' : 'UCK-G2-Plus' ,
147+ 'UASXG' : 'UAS-XG' ,
148+ 'ULTE' : 'U-LTE' ,
149+ 'ULTEPUS' : 'U-LTE-Pro' ,
150+ 'ULTEPEU' : 'U-LTE-Pro' ,
151+ 'UP1' : 'USP-Plug' ,
152+ 'UP6' : 'USP-Strip' ,
153+ 'USPPDUP' : 'USP - Power Distribution Unit Pro' ,
154+ 'USPRPS' : 'USP-RPS' ,
155+ 'US624P' : 'UniFi6 Switch 24' ,
156+ 'UBB' : 'UBB' ,
157+ 'UXGPRO' : 'UniFi NeXt-Gen Gateway PRO'
158+ }
18159
19160try :
20161 from cmk .special_agents .utils .argument_parsing import create_default_argument_parser
@@ -40,6 +181,9 @@ class unifi_object(object):
40181 if hasattr (self ,"_init" ):
41182 self ._init ()
42183
184+ def __repr__ (self ):
185+ return repr ([(_k ,_v ) for _k ,_v in self .__dict__ .items () if type (_v ) in (int ,str )])
186+
43187########################################
44188######
45189###### S S I D
@@ -51,9 +195,17 @@ class unifi_network_ssid(unifi_object):
51195 self ._UNIFI_SITE = self ._PARENT ._PARENT
52196 for _k ,_v in getattr (self ,"reasons_bar_chart_now" ,{}).items ():
53197 setattr (self ,_k ,_v )
198+ setattr (self ,f"{ self .radio } _num_sta" ,self .num_sta )
199+ setattr (self ,f"{ self .radio } _tcp_packet_loss" ,self .tcp_packet_loss )
200+ setattr (self ,f"{ self .radio } _wifi_retries" ,self .wifi_retries )
201+ setattr (self ,f"{ self .radio } _wifi_latency" ,self .wifi_latency )
202+ setattr (self ,f"{ self .radio } _avg_client_signal" ,self .avg_client_signal )
54203 def __str__ (self ):
55204 _ret = []
56- _unwanted = ["essid" ,"radio" ,"id" ,"t" ,"name" ,"radio_name" ,"wlanconf_id" ,"is_wep" ,"up" ,"site_id" ,"ap_mac" ,"state" ]
205+ _unwanted = ["essid" ,"radio" ,"id" ,"t" ,"name" ,"radio_name" ,"wlanconf_id" ,"is_wep" ,"up" ,"site_id" ,"ap_mac" ,"state" ,
206+ "na_num_sta" ,"ng_num_sta" ,"ng_tcp_packet_loss" ,"na_tcp_packet_loss" ,"na_wifi_retries" ,"ng_wifi_retries" ,
207+ "na_wifi_latency" ,"ng_wifi_latency" ,"na_avg_client_signal" ,"ng_avg_client_signal"
208+ ]
57209 for _k ,_v in self .__dict__ .items ():
58210 if _k .startswith ("_" ) or _k in _unwanted or type (_v ) not in (str ,int ,float ):
59211 continue
@@ -101,6 +253,9 @@ class unifi_network_port(unifi_object):
101253 self .name = self .ifname
102254 if not hasattr (self ,"port_idx" ) and hasattr (self ,"ifname" ):
103255 self .port_idx = int (self .ifname [- 1 ])+ 1 ## ethX
256+
257+ self .portconf = self ._PARENT ._PARENT ._PORTCONFIGS .get (getattr (self ,"portconf_id" ,None ))
258+
104259
105260 def _get_state (self ,state ):
106261 return {
@@ -135,6 +290,7 @@ class unifi_device(unifi_object):
135290 for _k ,_v in getattr (self ,"sys_stats" ,{}).items ():
136291 _k = _k .replace ("-" ,"_" )
137292 setattr (self ,_k ,_v )
293+ self .model_name = UNIFI_DEVICE_TABLE .get (self .model )
138294 if self .type in ("ugw" ,"udm" ):
139295 ## change ip to local ip
140296 self .wan_ip = self .ip
@@ -172,7 +328,7 @@ class unifi_device(unifi_object):
172328
173329 def _get_short_info (self ):
174330 _ret = []
175- _wanted = ["version" ,"ip" ,"mac" ,"serial" ,"model" ,"uptime" ,"upgradeable" ,"num_sta" ]
331+ _wanted = ["version" ,"ip" ,"mac" ,"serial" ,"model" ,"model_name" , " uptime" ,"upgradeable" ,"num_sta" , "adopted" , "state " ]
176332 for _k ,_v in self .__dict__ .items ():
177333 if _k .startswith ("_" ) or _k not in _wanted or type (_v ) not in (str ,int ,float ):
178334 continue
@@ -234,6 +390,8 @@ class unifi_site(unifi_object):
234390
235391 ##pprint(_api.get_data("/stat/rogueap"))
236392 self ._SITE_DEVICES = []
393+ self ._PORTCONFIGS = {}
394+ self ._get_portconfig ()
237395 self ._get_devices ()
238396 _satisfaction = list (filter (
239397 lambda x : x != None ,map (
@@ -242,6 +400,10 @@ class unifi_site(unifi_object):
242400 ))
243401 self .satisfaction = max (0 ,int (mean (_satisfaction )) if _satisfaction else 0 )
244402
403+ def _get_portconfig (self ):
404+ _data = self ._API .get_portconfig (site = self .name )
405+ for _config in _data :
406+ self ._PORTCONFIGS [_config ["_id" ]] = _config .get ("name" )
245407
246408 def _get_devices (self ):
247409 _data = self ._API .get_devices (site = self .name )
@@ -308,7 +470,11 @@ class unifi_controller(unifi_object):
308470
309471 _ret = []
310472 for _ssid ,_obj in _dict .items ():
311- _ret .append ("|" .join ([_ssid ,"num_sta" ,str (sum (map (lambda x : getattr (x ,"num_sta" ,0 ),_obj )))]))
473+ pprint (_obj )
474+ for _key in ("num_sta" ,"ng_num_sta" ,"na_num_sta" ,"ng_tcp_packet_loss" ,"na_tcp_packet_loss" ,"ng_wifi_retries" ,"na_wifi_retries" ,"ng_wifi_latency" ,"na_wifi_latency" ):
475+ _ret .append ("|" .join ([_ssid ,_key ,str (sum (map (lambda x : getattr (x ,_key ,0 ),_obj )))]))
476+ _ret .append ("|" .join ([_ssid ,"ng_avg_client_signal" ,str (mean (map (lambda x : getattr (x ,"ng_avg_client_signal" ,0 ),filter (lambda x : x .radio == "ng" ,_obj ))))]))
477+ _ret .append ("|" .join ([_ssid ,"na_avg_client_signal" ,str (mean (map (lambda x : getattr (x ,"na_avg_client_signal" ,0 ),filter (lambda x : x .radio == "na" ,_obj ))))]))
312478 _ret .append ("|" .join ([_ssid ,"channels" ,"," .join (
313479 sorted (
314480 set (map (lambda x : str (getattr (x ,"channel" ,"0" )),_obj ))
@@ -338,7 +504,7 @@ class unifi_controller(unifi_object):
338504 for _site in self ._UNIFI_SITES :
339505 _ret .append (str (_site ))
340506
341- _ret .append ("<<<unifi_device_shortlist>>>" )
507+ _ret .append ("<<<unifi_device_shortlist:sep(124) >>>" )
342508 for _device in self ._UNIFI_DEVICES :
343509 if _device ._piggy_back :
344510 _ret .append (_device ._get_short_info ())
@@ -351,7 +517,7 @@ class unifi_controller(unifi_object):
351517 if self ._API .PIGGYBACK_ATTRIBUT .lower () != "none" :
352518 ## PIGGYBACK DEVICES ##
353519 for _device in self ._UNIFI_DEVICES :
354- if _device ._piggy_back :
520+ if _device ._piggy_back and _device . adopted :
355521 _ret .append (str (_device ))
356522 return "\n " .join (_ret )
357523
@@ -388,6 +554,9 @@ class unifi_controller_api(object):
388554 def get_sites (self ):
389555 return self .get_data ("/stat/sites" ,site = None )
390556
557+ def get_portconfig (self ,site ):
558+ return self .get_data ("/rest/portconf" ,site = site )
559+
391560 def get_devices (self ,site ):
392561 return self .get_data ("/stat/device" ,site = site )
393562
@@ -409,8 +578,8 @@ class unifi_controller_api(object):
409578 return
410579 raise unifi_api_exception ("Login failed" )
411580
412- def get_data (self ,path ,site = "default" ,method = "GET" ):
413- _json = self .request (method = method ,path = path ,site = site ).json ()
581+ def get_data (self ,path ,site = "default" ,method = "GET" , ** kwargs ):
582+ _json = self .request (method = method ,path = path ,site = site , ** kwargs ).json ()
414583 _meta = _json .get ("meta" ,{})
415584 if _meta .get ("rc" ) == "ok" :
416585 return _json .get ("data" ,[])
@@ -470,11 +639,10 @@ if __name__ == '__main__':
470639
471640 if _api .is_unifios :
472641 print ("AgentOS: UnifiOS" )
473- #pprint(_api.get_data("rest/portconf",site="default",method="GET"))
474- ##pprint(_api.get_data("/stat/rogueap"))
642+ ##pprint(_api.get_data("/stat/rogueap?within=4"))
475643 ##pprint(_api.get_data("/rest/user",site="default",method="GET"))
476644 ##pprint(_api.get_data("/stat/sta",site="default",method="GET"))
477- #sys.exit(0)
645+ ## sys.exit(0)
478646 _controller = unifi_controller (_API = _api )
479647 if args .rawapi == False :
480648 print (_controller )
0 commit comments