diff --git a/mktxp/cli/output/wifi_out.py b/mktxp/cli/output/wifi_out.py index 9a766df1..a44d6f43 100644 --- a/mktxp/cli/output/wifi_out.py +++ b/mktxp/cli/output/wifi_out.py @@ -14,7 +14,7 @@ from mktxp.flow.processor.output import BaseOutputProcessor from mktxp.datasource.wireless_ds import WirelessMetricsDataSource - +from mktxp.flow.router_entry import RouterEntryWirelessType class WirelessOutput: ''' Wireless Clients CLI Output @@ -42,8 +42,8 @@ def clients_summary(router_entry): output_records = 0 registration_records = len(registration_records) - output_entry = BaseOutputProcessor.OutputWiFiEntry \ - if not WirelessMetricsDataSource.is_legacy(router_entry) else BaseOutputProcessor.OutputWirelessEntry + output_entry = BaseOutputProcessor.OutputWirelessEntry \ + if router_entry.wireless_type in (RouterEntryWirelessType.DUAL, RouterEntryWirelessType.WIRELESS) else BaseOutputProcessor.OutputWiFiEntry output_table = BaseOutputProcessor.output_table(output_entry) for key in dhcp_rt_by_interface.keys(): diff --git a/mktxp/datasource/capsman_ds.py b/mktxp/datasource/capsman_ds.py index 0b3bc812..0f05739e 100644 --- a/mktxp/datasource/capsman_ds.py +++ b/mktxp/datasource/capsman_ds.py @@ -14,24 +14,29 @@ from mktxp.datasource.base_ds import BaseDSProcessor from mktxp.datasource.wireless_ds import WirelessMetricsDataSource - +from mktxp.flow.router_entry import RouterEntryWirelessType class CapsmanInfo: @staticmethod - def capsman_path(router_entry): - if WirelessMetricsDataSource.is_legacy(router_entry): - return '/caps-man' - else: + def capsman_paths(router_entry): + if router_entry.wireless_type == RouterEntryWirelessType.DUAL: + return ['/caps-man', f'/interface/wifi/capsman'] + elif router_entry.wireless_type == RouterEntryWirelessType.WIRELESS: + return ['/caps-man'] + else: wireless_package = WirelessMetricsDataSource.wireless_package(router_entry) - return f'/interface/{wireless_package}/capsman' + return [f'/interface/{wireless_package}/capsman'] @staticmethod - def registration_table_path(router_entry): - if WirelessMetricsDataSource.is_legacy(router_entry): - return '/caps-man/registration-table' - else: + def registration_table_paths(router_entry): + if router_entry.wireless_type == RouterEntryWirelessType.DUAL: + return ['/caps-man/registration-table', f'/interface/wifi/registration-table'] + elif router_entry.wireless_type == RouterEntryWirelessType.WIRELESS: + return ['/caps-man/registration-table'] + else: wireless_package = WirelessMetricsDataSource.wireless_package(router_entry) - return f'/interface/{wireless_package}/registration-table' + return [f'/interface/{wireless_package}/registration-table'] + class CapsmanCapsMetricsDataSource: ''' Caps Metrics data provider @@ -41,8 +46,9 @@ def metric_records(router_entry, *, metric_labels = None): if metric_labels is None: metric_labels = [] try: - capsman_path = CapsmanInfo.capsman_path(router_entry) - remote_caps_records = router_entry.api_connection.router_api().get_resource(f'{capsman_path}/remote-cap').get() + remote_caps_records = [] + for capsman_path in CapsmanInfo.capsman_paths(router_entry): + remote_caps_records.extend(router_entry.api_connection.router_api().get_resource(f'{capsman_path}/remote-cap').get()) return BaseDSProcessor.trimmed_records(router_entry, router_records = remote_caps_records, metric_labels = metric_labels) except Exception as exc: print(f'Error getting CAPsMAN remote caps info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') @@ -57,8 +63,9 @@ def metric_records(router_entry, *, metric_labels = None, add_router_id = True) if metric_labels is None: metric_labels = [] try: - registration_table_path = CapsmanInfo.registration_table_path(router_entry) - registration_table_records = router_entry.api_connection.router_api().get_resource(f'{registration_table_path}').get() + registration_table_records = [] + for registration_table_path in CapsmanInfo.registration_table_paths(router_entry): + registration_table_records.extend(router_entry.api_connection.router_api().get_resource(f'{registration_table_path}').get()) # With wifiwave2, Mikrotik renamed the field 'rx-signal' to 'signal' # For backward compatibility, including both variants @@ -75,10 +82,9 @@ def metric_records(router_entry, *, metric_labels = None, add_router_id = True) class CapsmanInterfacesDatasource: ''' Data provider for CAPsMaN interfaces ''' - @staticmethod def metric_records(router_entry, *, metric_labels = None): - if not WirelessMetricsDataSource.is_legacy(router_entry): + if not router_entry.wireless_type in (RouterEntryWirelessType.DUAL, RouterEntryWirelessType.WIRELESS): return None if metric_labels is None: metric_labels = [] diff --git a/mktxp/datasource/routerboard_ds.py b/mktxp/datasource/routerboard_ds.py index 805ccd30..8a149dad 100644 --- a/mktxp/datasource/routerboard_ds.py +++ b/mktxp/datasource/routerboard_ds.py @@ -29,3 +29,25 @@ def metric_records(router_entry, *, metric_labels = None): print(f'Error getting system routerboard info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') return None + @staticmethod + def firmware_version(router_entry): + try: + version_st = router_entry.api_connection.router_api().get_resource('/system/routerboard').call('print', {'proplist':'current-firmware'})[0] + if version_st.get('current-firmware'): + return version_st['current-firmware'] + return None + except Exception as exc: + print(f'Error getting routerboard current-firmware from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + + @staticmethod + def firmware_version(router_entry): + try: + version_st = router_entry.api_connection.router_api().get_resource('/system/routerboard').call('print', {'proplist':'upgrade-firmware'})[0] + if version_st.get('upgrade-firmware'): + return version_st['upgrade-firmware'] + return None + except Exception as exc: + print(f'Error getting routerboard upgrade-firmware from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') + return None + diff --git a/mktxp/datasource/wireless_ds.py b/mktxp/datasource/wireless_ds.py index ec92368b..82419887 100644 --- a/mktxp/datasource/wireless_ds.py +++ b/mktxp/datasource/wireless_ds.py @@ -11,21 +11,17 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. - from mktxp.datasource.base_ds import BaseDSProcessor from mktxp.datasource.package_ds import PackageMetricsDataSource - +from mktxp.flow.router_entry import RouterEntryWirelessType class WirelessMetricsDataSource: ''' Wireless Metrics data provider ''' - WIFIWAVE2 = 'wifiwave2' WIRELESS = 'wireless' + WIFIWAVE2 = 'wifiwave2' WIFI = 'wifi' - WIFI_PACKAGE = 'wifi-qcom' - WIFI_AC_PACKAGE = 'wifi-qcom-ac' - @staticmethod def metric_records(router_entry, *, metric_labels = None, add_router_id = True): if metric_labels is None: @@ -45,20 +41,13 @@ def metric_records(router_entry, *, metric_labels = None, add_router_id = True): print(f'Error getting wireless registration table info from router{router_entry.router_name}@{router_entry.config_entry.hostname}: {exc}') return None - @staticmethod def wireless_package(router_entry): - if not router_entry.wifi_package: - if PackageMetricsDataSource.is_package_installed(router_entry, package_name = WirelessMetricsDataSource.WIFI_PACKAGE): - router_entry.wifi_package = WirelessMetricsDataSource.WIFI - elif PackageMetricsDataSource.is_package_installed(router_entry, package_name = WirelessMetricsDataSource.WIFI_AC_PACKAGE): - router_entry.wifi_package = WirelessMetricsDataSource.WIFI - elif PackageMetricsDataSource.is_package_installed(router_entry, package_name = WirelessMetricsDataSource.WIFIWAVE2): - router_entry.wifi_package = WirelessMetricsDataSource.WIFIWAVE2 - else: - router_entry.wifi_package = WirelessMetricsDataSource.WIRELESS - return router_entry.wifi_package + if router_entry.wireless_type in (RouterEntryWirelessType.DUAL, RouterEntryWirelessType.WIRELESS): + return WirelessMetricsDataSource.WIRELESS + elif router_entry.wireless_type == RouterEntryWirelessType.WIFIWAVE2: + return WirelessMetricsDataSource.WIFIWAVE2 + else: + return WirelessMetricsDataSource.WIFI + - @staticmethod - def is_legacy(router_entry): - return WirelessMetricsDataSource.wireless_package(router_entry) == WirelessMetricsDataSource.WIRELESS diff --git a/mktxp/flow/processor/output.py b/mktxp/flow/processor/output.py index dbd4cd88..142b8e26 100644 --- a/mktxp/flow/processor/output.py +++ b/mktxp/flow/processor/output.py @@ -50,13 +50,10 @@ def augment_record(router_entry, registration_record, id_key = 'mac_address'): registration_record['rx_bytes'] = registration_record['bytes'].split(',')[1] del registration_record['bytes'] - is_legacy = WirelessMetricsDataSource.is_legacy(router_entry) if registration_record.get('tx_rate'): - registration_record['tx_rate'] = BaseOutputProcessor.parse_bitrates(registration_record['tx_rate']) \ - if not is_legacy else BaseOutputProcessor.parse_rates(registration_record['tx_rate']) + registration_record['tx_rate'] = BaseOutputProcessor.parse_bitrates(registration_record['tx_rate']) if registration_record.get('rx_rate'): - registration_record['rx_rate'] = BaseOutputProcessor.parse_bitrates(registration_record['rx_rate']) \ - if not is_legacy else BaseOutputProcessor.parse_rates(registration_record['rx_rate']) + registration_record['rx_rate'] = BaseOutputProcessor.parse_bitrates(registration_record['rx_rate']) if registration_record.get('uptime'): registration_record['uptime'] = naturaldelta(BaseOutputProcessor.parse_timedelta_seconds(registration_record['uptime']), months=True, minimum_unit='seconds') diff --git a/mktxp/flow/router_entry.py b/mktxp/flow/router_entry.py index 2fad603b..fe4dfb99 100644 --- a/mktxp/flow/router_entry.py +++ b/mktxp/flow/router_entry.py @@ -12,11 +12,26 @@ ## GNU General Public License for more details. +from enum import IntEnum from collections import namedtuple from mktxp.cli.config.config import config_handler, MKTXPConfigKeys, CollectorKeys from mktxp.flow.router_connection import RouterAPIConnection +from mktxp.datasource.package_ds import PackageMetricsDataSource +class RouterEntryWirelessType(IntEnum): + NONE = 0 + WIRELESS = 1 + WIFIWAVE2 = 2 + WIFI = 3 + DUAL = 4 + +class RouterEntryWirelessPackage: + WIFI_PACKAGE = 'wifi-qcom' + WIFI_AC_PACKAGE = 'wifi-qcom-ac' + WIFIWAVE2_PACKAGE = 'wifiwave2' + WIRELESS_PACKAGE = 'wireless' + class RouterEntry: ''' RouterOS Entry ''' @@ -29,7 +44,6 @@ def __init__(self, router_name): MKTXPConfigKeys.ROUTERBOARD_ADDRESS: self.config_entry.hostname } - self.wifi_package = None self.time_spent = { CollectorKeys.IDENTITY_COLLECTOR: 0, CollectorKeys.SYSTEM_RESOURCE_COLLECTOR: 0, CollectorKeys.HEALTH_COLLECTOR: 0, @@ -55,7 +69,24 @@ def __init__(self, router_name): } self._dhcp_entry = None self._dhcp_records = {} - + self._wireless_type = RouterEntryWirelessType.NONE + + @property + def wireless_type(self): + router_entry = self + if self._wireless_type == RouterEntryWirelessType.NONE: + if PackageMetricsDataSource.is_package_installed(router_entry, package_name = RouterEntryWirelessPackage.WIFI_PACKAGE): + self._wireless_type = RouterEntryWirelessType.WIFI + elif PackageMetricsDataSource.is_package_installed(router_entry, package_name = RouterEntryWirelessPackage.WIFI_AC_PACKAGE): + self._wireless_type = RouterEntryWirelessType.WIFI + elif PackageMetricsDataSource.is_package_installed(router_entry, package_name = RouterEntryWirelessPackage.WIFIWAVE2_PACKAGE): + self._wireless_type = RouterEntryWirelessType.WIFIWAVE2 + elif PackageMetricsDataSource.is_package_installed(router_entry, package_name = RouterEntryWirelessPackage.WIRELESS_PACKAGE): + self._wireless_type = RouterEntryWirelessType.DUAL + else: + self._wireless_type = RouterEntryWirelessType.WIRELESS + return self._wireless_type + @property def dhcp_entry(self): if self._dhcp_entry: @@ -95,7 +126,7 @@ def is_ready(self): return is_ready def is_done(self): - self.wifi_package = None self._dhcp_records = {} + self._wireless_type = RouterEntryWirelessType.NONE DHCPCacheEntry = namedtuple('DHCPCacheEntry', ['type', 'record'])