Skip to content

Commit 5beb089

Browse files
committed
[feature] Added more interface properties to device status
Closes #77
1 parent 77bad54 commit 5beb089

File tree

6 files changed

+168
-15
lines changed

6 files changed

+168
-15
lines changed

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Available Features
2323
------------------
2424

2525
* Collects and displays device status information like uptime, RAM status, CPU load averages,
26-
Interface addresses, WiFi interface status and associated clients, Neighbors information, DHCP Leases, Disk/Flash status
26+
Interface properties and addresses, WiFi interface status and associated clients,
27+
Neighbors information, DHCP Leases, Disk/Flash status
2728
* Collection of monitoring information in a timeseries database (currently only influxdb is supported)
2829
* Monitoring charts for uptime, packet loss, round trip time (latency), associated wifi clients, interface traffic,
2930
RAM usage, CPU load, flash/disk usage

openwisp_monitoring/device/schema.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,20 @@
146146
"required": ["name"],
147147
"properties": {
148148
"name": {"type": "string"},
149+
"type": {"type": "string"},
149150
"uptime": {"type": "integer"},
151+
"up": {"type": "boolean"},
152+
"mac": {"type": "string"},
153+
"mtu": {"type": "integer"},
154+
"txqueuelen": {"type": "integer"},
155+
"speed": {"type": "string"},
156+
"multicast": {"type": "boolean"},
157+
"bridge_members": {
158+
"type": "array",
159+
"items": {"type": "string"},
160+
"additionalItems": False,
161+
},
162+
"stp": {"type": "boolean"},
150163
"statistics": {
151164
"type": "object",
152165
"properties": {

openwisp_monitoring/device/templates/admin/config/device/change_form.html

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,23 @@ <h2>{% trans 'Storage' %}</h2>
112112
{% for interface in device_data.interfaces %}
113113
<div class="inline-group">
114114
<h2>{% trans 'Interface status' %}: {{ interface.name }}</h2>
115-
{% if interface.wireless %}
115+
{% if interface.mac %}
116+
<div class="form-row">
117+
<label>{% trans 'MAC Address' %}:</label>
118+
<div class="readonly">
119+
{{ interface.mac }}
120+
</div>
121+
</div>
122+
{% endif %}
123+
{% if interface.type %}
116124
<div class="form-row">
117125
<label>{% trans 'Type' %}:</label>
118126
<div class="readonly">
119-
{% trans 'Wireless' %}
127+
{{ interface.type }}
120128
</div>
121129
</div>
130+
{% endif %}
131+
{% if interface.wireless %}
122132
<div class="form-row">
123133
<label>{% trans 'Mode' %}:</label>
124134
<div class="readonly">
@@ -170,6 +180,65 @@ <h2>{% trans 'Interface status' %}: {{ interface.name }}</h2>
170180
{{ interface.wireless.clients|length }}
171181
</div>
172182
</div>
183+
{% endif %}
184+
{% endif %}
185+
{% if interface.type == 'ethernet' and interface.speed %}
186+
<div class="form-row">
187+
<label>{% trans 'Speed' %}:</label>
188+
<div class="readonly">
189+
{{ interface.speed }}
190+
</div>
191+
</div>
192+
{% endif %}
193+
{% if interface.bridge_members %}
194+
<div class="form-row">
195+
<label>{% trans 'Bridge Members' %}:</label>
196+
<div class="readonly">
197+
{{ interface.bridge_members|join:", " }}
198+
</div>
199+
</div>
200+
{% endif %}
201+
{% if interface.stp != None %}
202+
<div class="form-row">
203+
<label>{% trans 'Spanning Tree Protocol' %}:</label>
204+
<div class="readonly">
205+
<img src="{% static '/admin/img/' %}icon-{{ interface.stp|yesno:'yes,no,no' }}.svg">
206+
</div>
207+
</div>
208+
{% endif %}
209+
{% if interface.up != None %}
210+
<div class="form-row">
211+
<label>{% trans 'Up' %}:</label>
212+
<div class="readonly">
213+
<img src="{% static '/admin/img/' %}icon-{{ interface.up|yesno:'yes,no,no' }}.svg">
214+
</div>
215+
</div>
216+
{% endif %}
217+
{% if interface.multicast != None %}
218+
<div class="form-row">
219+
<label>{% trans 'Multicast' %}:</label>
220+
<div class="readonly">
221+
<img src="{% static '/admin/img/' %}icon-{{ interface.multicast|yesno:'yes,no,no' }}.svg">
222+
</div>
223+
</div>
224+
{% endif %}
225+
{% if interface.mtu %}
226+
<div class="form-row">
227+
<label>{% trans 'MTU' %}:</label>
228+
<div class="readonly">
229+
{{ interface.mtu }}
230+
</div>
231+
</div>
232+
{% endif %}
233+
{% if interface.txqueuelen != None %}
234+
<div class="form-row">
235+
<label>{% trans 'Transmit Queue Length' %}:</label>
236+
<div class="readonly">
237+
{{ interface.txqueuelen }}
238+
</div>
239+
</div>
240+
{% endif %}
241+
{% if interface.wireless and interface.wireless.clients %}
173242
<table class="wifi-clients">
174243
<thead>
175244
<tr>
@@ -226,7 +295,6 @@ <h2>{% trans 'Interface status' %}: {{ interface.name }}</h2>
226295
{% endfor %}
227296
</table>
228297
{% endif %}
229-
{% endif %}
230298
{% if interface.addresses %}
231299
<table class="addresses">
232300
<thead>

openwisp_monitoring/device/tests/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ def _data(self):
145145
'interfaces': [
146146
{
147147
'name': 'wlan0',
148+
'type': 'wireless',
149+
'up': True,
150+
'mac': '44:d1:fa:4b:38:43',
151+
'txqueuelen': 1000,
152+
'multicast': True,
153+
'mtu': 1500,
148154
'statistics': {
149155
'rx_bytes': 324,
150156
'tx_bytes': 145,
@@ -183,6 +189,12 @@ def _data(self):
183189
},
184190
{
185191
'name': 'wlan1',
192+
'type': 'wireless',
193+
'up': True,
194+
'mac': '44:d1:fa:4b:38:44',
195+
'txqueuelen': 1000,
196+
'multicast': True,
197+
'mtu': 1500,
186198
'statistics': {
187199
'rx_bytes': 2275,
188200
'tx_bytes': 826,

openwisp_monitoring/device/tests/test_admin.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,46 @@ def test_wifi_clients_admin(self):
8181
self.assertEqual(r1.status_code, 200)
8282
self.assertContains(r1, '00:ee:ad:34:f5:3b')
8383

84+
def test_interface_properties_admin(self):
85+
self._login_admin()
86+
dd = self.create_test_adata(no_resources=True)
87+
url = reverse('admin:config_device_change', args=[dd.id])
88+
r1 = self.client.get(url, follow=True)
89+
self.assertEqual(r1.status_code, 200)
90+
self.assertContains(r1, '44:d1:fa:4b:38:44')
91+
self.assertContains(r1, 'Transmit Queue Length')
92+
self.assertContains(r1, 'Up')
93+
self.assertContains(r1, 'Multicast')
94+
self.assertContains(r1, 'MTU')
95+
96+
def test_interface_bridge_admin(self):
97+
self._login_admin()
98+
d = self._create_device(organization=self._create_org())
99+
dd = DeviceData(name='test-device', pk=d.pk)
100+
data = self._data()
101+
del data['resources']
102+
self._post_data(
103+
d.id,
104+
d.key,
105+
{
106+
'type': 'DeviceMonitoring',
107+
'interfaces': [
108+
{
109+
'name': 'br-lan',
110+
'type': 'bridge',
111+
'bridge_members': ['tap0', 'wlan0', 'wlan1'],
112+
'stp': True,
113+
}
114+
],
115+
},
116+
)
117+
url = reverse('admin:config_device_change', args=[dd.id])
118+
r1 = self.client.get(url, follow=True)
119+
self.assertEqual(r1.status_code, 200)
120+
self.assertContains(r1, 'Bridge Members')
121+
self.assertContains(r1, 'tap0, wlan0, wlan1')
122+
self.assertContains(r1, 'Spanning Tree Protocol')
123+
84124
def test_uuid_bug(self):
85125
dd = self.create_test_adata(no_resources=True)
86126
uuid = str(dd.pk).replace('-', '')

openwisp_monitoring/device/tests/test_api.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,22 +165,14 @@ def test_garbage_wireless_clients(self):
165165
d = self._create_device(organization=o)
166166
garbage_interfaces = [
167167
{'name': 'garbage1', 'wireless': {'clients': {}}},
168-
{
169-
'name': 'garbage2',
170-
'wireless': {'clients': [{'what?': 'mac missing'}]},
171-
},
172-
{'name': 'garbage3', 'wireless': {}}
168+
{'name': 'garbage2', 'wireless': {'clients': [{'what?': 'mac missing'}]}},
169+
{'name': 'garbage3', 'wireless': {}},
173170
]
174171
for garbage_interface in garbage_interfaces:
175172
interface = self._data()['interfaces'][0]
176173
interface.update(garbage_interface)
177174
r = self._post_data(
178-
d.id,
179-
d.key,
180-
{
181-
'type': 'DeviceMonitoring',
182-
'interfaces': [interface]
183-
}
175+
d.id, d.key, {'type': 'DeviceMonitoring', 'interfaces': [interface]}
184176
)
185177
with self.subTest(garbage_interface):
186178
self.assertEqual(r.status_code, 400)
@@ -409,3 +401,30 @@ def test_get_device_status_200(self):
409401
self.assertIn('data', r.data)
410402
self.assertIsInstance(r.data['data'], dict)
411403
self.assertEqual(dd.data, r.data['data'])
404+
405+
def test_garbage_interface_properties(self):
406+
o = self._create_org()
407+
d = self._create_device(organization=o)
408+
garbage_interfaces = [
409+
{'type': 1},
410+
{'uptime': 'string'},
411+
{'up': 'up'},
412+
{'mac': 1},
413+
{'mtu': 'string'},
414+
{'txqueuelen': 'string'},
415+
{'speed': 0},
416+
{'multicast': 1},
417+
{'type': 'bridge', 'bridge_members': [1, 2]},
418+
{'stp': 1},
419+
]
420+
number = 1
421+
for garbage_interface in garbage_interfaces:
422+
interface = self._data()['interfaces'][0]
423+
interface.update(garbage_interface)
424+
interface['name'] = f'garbage{number}'
425+
r = self._post_data(
426+
d.id, d.key, {'type': 'DeviceMonitoring', 'interfaces': [interface]}
427+
)
428+
number += 1
429+
with self.subTest(garbage_interface):
430+
self.assertEqual(r.status_code, 400)

0 commit comments

Comments
 (0)