diff --git a/platypush/backend/http/webapp/src/components/panels/Entities/NetworkInterface.vue b/platypush/backend/http/webapp/src/components/panels/Entities/NetworkInterface.vue index 9b7a9150..8401558d 100644 --- a/platypush/backend/http/webapp/src/components/panels/Entities/NetworkInterface.vue +++ b/platypush/backend/http/webapp/src/components/panels/Entities/NetworkInterface.vue @@ -90,6 +90,51 @@
+ +
+
Addresses
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
Family
+
+
+
+
Netmask
+
+
+
+
Broadcast
+
+
+
+
+
@@ -106,6 +151,8 @@ export default { data() { return { isCollapsed: true, + areAddressesCollapsed: true, + displayedAddresses: {}, } }, } @@ -131,6 +178,12 @@ export default { min-height: 3em; cursor: pointer; + @include from($tablet) { + @include until($desktop) { + margin-left: 3.25em; + } + } + &:hover { color: $default-hover-fg; } diff --git a/platypush/entities/system.py b/platypush/entities/system.py index bdadc9dd..8afa6658 100644 --- a/platypush/entities/system.py +++ b/platypush/entities/system.py @@ -198,6 +198,7 @@ if 'network_interface' not in Base.metadata: errors_out = Column(Integer) drop_in = Column(Integer) drop_out = Column(Integer) + addresses = Column(JSON) __mapper_args__ = { 'polymorphic_identity': __tablename__, diff --git a/platypush/message/response/system/__init__.py b/platypush/message/response/system/__init__.py index 4ddf35bd..2078187e 100644 --- a/platypush/message/response/system/__init__.py +++ b/platypush/message/response/system/__init__.py @@ -28,40 +28,6 @@ class SensorResponse(SystemResponse): pass -class NetworkAddressResponse(NetworkResponse): - def __init__( - self, - nic: str, - ipv4_address: Optional[str] = None, - ipv4_netmask: Optional[str] = None, - ipv4_broadcast: Optional[str] = None, - ipv6_address: Optional[str] = None, - ipv6_netmask: Optional[str] = None, - ipv6_broadcast: Optional[str] = None, - mac_address: Optional[str] = None, - mac_broadcast: Optional[str] = None, - ptp: Optional[str] = None, - *args, - **kwargs - ): - super().__init__( - *args, - output={ - 'nic': nic, - 'ipv4_address': ipv4_address, - 'ipv4_netmask': ipv4_netmask, - 'ipv4_broadcast': ipv4_broadcast, - 'ipv6_address': ipv6_address, - 'ipv6_netmask': ipv6_netmask, - 'ipv6_broadcast': ipv6_broadcast, - 'mac_address': mac_address, - 'mac_broadcast': mac_broadcast, - 'ptp': ptp, - }, - **kwargs - ) - - class NetworkInterfaceStatsResponse(NetworkResponse): def __init__( self, nic: str, is_up: bool, duplex: str, speed: int, mtu: int, *args, **kwargs diff --git a/platypush/plugins/system/__init__.py b/platypush/plugins/system/__init__.py index 7e3fdc57..8c5f7a78 100644 --- a/platypush/plugins/system/__init__.py +++ b/platypush/plugins/system/__init__.py @@ -1,5 +1,4 @@ import os -import socket from datetime import datetime from typing import Tuple, Union, List, Optional, Dict @@ -23,7 +22,6 @@ from platypush.entities.system import ( ) from platypush.message.response.system import ( NetworkResponseList, - NetworkAddressResponse, NetworkInterfaceStatsResponse, SensorTemperatureResponse, SensorResponseList, @@ -257,9 +255,14 @@ class SystemPlugin(SensorPlugin, EntityManager): return DiskSchema().dump(self._disk_info(), many=True) def _net_io_counters(self) -> List[NetworkInterface]: + addrs = psutil.net_if_addrs() return NetworkInterfaceSchema().load( # type: ignore [ - {'interface': interface, **stats._asdict()} + { + 'interface': interface, + 'addresses': addrs.get(interface, []), + **stats._asdict(), + } for interface, stats in psutil.net_io_counters(pernic=True).items() if any(bool(val) for val in stats._asdict().values()) ], @@ -328,62 +331,6 @@ class SystemPlugin(SensorPlugin, EntityManager): many=True, ) - @action - def net_addresses( - self, nic: Optional[str] = None - ) -> Union[NetworkAddressResponse, NetworkResponseList]: - """ - Get address info associated to the network interfaces. - - :param nic: Select the stats for a specific network device (e.g. 'eth0'). Default: get stats for all NICs. - :return: :class:`platypush.message.response.system.NetworkAddressResponse` or list of - :class:`platypush.message.response.system.NetworkAddressResponse`. - """ - addrs = psutil.net_if_addrs() - - def _expand_addresses(_nic, _addrs): - args = {'nic': _nic} - - for addr in _addrs: - if addr.family == socket.AddressFamily.AF_INET: - args.update( - { - 'ipv4_address': addr.address, - 'ipv4_netmask': addr.netmask, - 'ipv4_broadcast': addr.broadcast, - } - ) - elif addr.family == socket.AddressFamily.AF_INET6: - args.update( - { - 'ipv6_address': addr.address, - 'ipv6_netmask': addr.netmask, - 'ipv6_broadcast': addr.broadcast, - } - ) - elif addr.family == socket.AddressFamily.AF_PACKET: - args.update( - { - 'mac_address': addr.address, - 'mac_broadcast': addr.broadcast, - } - ) - - if addr.ptp and not args.get('ptp'): - args['ptp'] = addr.ptp - - return NetworkAddressResponse(**args) - - if nic: - addrs = [addr for name, addr in addrs.items() if name == nic] - assert addrs, 'No such network interface: {}'.format(nic) - addr = addrs[0] - return _expand_addresses(nic, addr) - - return NetworkResponseList( - [_expand_addresses(nic, addr) for nic, addr in addrs.items()] - ) - @action def net_stats( self, nic: Optional[str] = None diff --git a/platypush/schemas/system/_network/_base.py b/platypush/schemas/system/_network/_base.py index 41231460..2fca5fc0 100644 --- a/platypush/schemas/system/_network/_base.py +++ b/platypush/schemas/system/_network/_base.py @@ -1,3 +1,5 @@ +from socket import AddressFamily + from marshmallow import pre_load from .._base import SystemBaseSchema @@ -11,6 +13,8 @@ class NetworkInterfaceBaseSchema(SystemBaseSchema): @pre_load def pre_load(self, data: dict, **_) -> dict: data = super().pre_load(data) + + # Custom attribute mappings for in_attr, out_attr in { 'errin': 'errors_in', 'errout': 'errors_out', @@ -20,4 +24,12 @@ class NetworkInterfaceBaseSchema(SystemBaseSchema): if in_attr in data: data[out_attr] = data.pop(in_attr) + # Serialize enum values + for i, addr in enumerate(data.get('addresses', [])): + if hasattr(addr, '_asdict'): + addr = addr._asdict() + if isinstance(addr.get('family'), AddressFamily): + addr['family'] = addr['family'].name + data['addresses'][i] = addr + return data diff --git a/platypush/schemas/system/_network/_model.py b/platypush/schemas/system/_network/_model.py index 98db4e4b..c9bd7cb5 100644 --- a/platypush/schemas/system/_network/_model.py +++ b/platypush/schemas/system/_network/_model.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, field -from typing import Optional +from socket import AddressFamily +from typing import List, Optional @dataclass @@ -80,3 +81,63 @@ class NetworkInterface: }, } ) + + addresses: List['NetworkInterfaceAddress'] = field( + default_factory=list, + metadata={ + 'metadata': { + 'description': 'List of addresses associated to the interface', + 'example': [ + { + 'family': AddressFamily.AF_INET.name, + 'address': '192.168.1.2', + 'netmask': '255.255.255.0', + 'broadcast': '192.168.1.255', + } + ], + }, + }, + ) + + +@dataclass +class NetworkInterfaceAddress: + """ + Network interface address data class. + """ + + family: AddressFamily = field( + metadata={ + 'metadata': { + 'description': 'Address family', + 'example': AddressFamily.AF_INET.name, + } + } + ) + + address: Optional[str] = field( + metadata={ + 'metadata': { + 'description': 'IPv4 or IPv6 address of the interface', + 'example': '192.168.1.2', + } + } + ) + + netmask: Optional[str] = field( + metadata={ + 'metadata': { + 'description': 'Netmask for the interface address', + 'example': '255.255.255.0', + } + } + ) + + broadcast: Optional[str] = field( + metadata={ + 'metadata': { + 'description': 'Broadcast address for the interface', + 'example': '192.168.1.255', + } + } + )