Skip to content

Commit

Permalink
Merge pull request #9 from mbugert/dev
Browse files Browse the repository at this point in the history
Integrate changes prior to v0.2.8
  • Loading branch information
mbugert authored Mar 23, 2021
2 parents bba50f1 + 08cf5b0 commit 0aeb51f
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 27 deletions.
2 changes: 1 addition & 1 deletion connectbox_exporter/connectbox_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(
self.timeout = exporter_config[TIMEOUT_SECONDS]

extractors = exporter_config[EXTRACTORS]
self.metric_extractors = [get_metrics_extractor(e) for e in extractors]
self.metric_extractors = [get_metrics_extractor(e, logger) for e in extractors]

def collect(self):
# Collect scrape duration and scrape success for each extractor. Scrape success is initialized with False for
Expand Down
61 changes: 36 additions & 25 deletions connectbox_exporter/xml2metric.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
from datetime import timedelta
from enum import Enum
from logging import Logger
from pathlib import Path
from typing import Iterable, Set, Dict

Expand All @@ -26,8 +27,9 @@ class XmlMetricsExtractor:
PROJECT_ROOT = Path(__file__).parent.parent
SCHEMA_ROOT = PROJECT_ROOT / "resources" / "schema"

def __init__(self, name: str, functions: Set[int]):
def __init__(self, name: str, functions: Set[int], logger: Logger):
self._name = name
self._logger = logger

# create one parser per function, use an XML schema if available
self._parsers = {}
Expand Down Expand Up @@ -67,9 +69,9 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:


class DownstreamStatusExtractor(XmlMetricsExtractor):
def __init__(self):
def __init__(self, logger: Logger):
super(DownstreamStatusExtractor, self).__init__(
DOWNSTREAM, {GET.DOWNSTREAM_TABLE, GET.SIGNAL_TABLE}
DOWNSTREAM, {GET.DOWNSTREAM_TABLE, GET.SIGNAL_TABLE}, logger
)

def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
Expand Down Expand Up @@ -157,8 +159,8 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:


class UpstreamStatusExtractor(XmlMetricsExtractor):
def __init__(self):
super(UpstreamStatusExtractor, self).__init__(UPSTREAM, {GET.UPSTREAM_TABLE})
def __init__(self, logger: Logger):
super(UpstreamStatusExtractor, self).__init__(UPSTREAM, {GET.UPSTREAM_TABLE}, logger)

def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
assert len(raw_xmls) == 1
Expand Down Expand Up @@ -215,8 +217,8 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:


class LanUserExtractor(XmlMetricsExtractor):
def __init__(self):
super(LanUserExtractor, self).__init__(LAN_USERS, {GET.LANUSERTABLE})
def __init__(self, logger: Logger):
super(LanUserExtractor, self).__init__(LAN_USERS, {GET.LANUSERTABLE}, logger)

def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
assert len(raw_xmls) == 1
Expand Down Expand Up @@ -271,8 +273,8 @@ def extract_client(client, target_metric: GaugeMetricFamily):


class TemperatureExtractor(XmlMetricsExtractor):
def __init__(self):
super(TemperatureExtractor, self).__init__(TEMPERATURE, {GET.CMSTATE})
def __init__(self, logger: Logger):
super(TemperatureExtractor, self).__init__(TEMPERATURE, {GET.CMSTATE}, logger)

def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
assert len(raw_xmls) == 1
Expand Down Expand Up @@ -307,12 +309,17 @@ class ProvisioningStatus(Enum):
PARTIAL_SERVICE_DS = "Partial Service (DS only)"
PARTIAL_SERVICE_USDS = "Partial Service (US+DS)"
MODEM_MODE = "Modem Mode"
DS_SCANNING = "DS scanning" # confirmed to exist
US_SCANNING = "US scanning" # probably exists too

# default case for all future unknown provisioning status
UNKNOWN = "unknown"


class DeviceStatusExtractor(XmlMetricsExtractor):
def __init__(self):
def __init__(self, logger: Logger):
super(DeviceStatusExtractor, self).__init__(
DEVICE_STATUS, {GET.GLOBALSETTINGS, GET.CM_SYSTEM_INFO, GET.CMSTATUS}
DEVICE_STATUS, {GET.GLOBALSETTINGS, GET.CM_SYSTEM_INFO, GET.CMSTATUS}, logger
)

def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
Expand All @@ -327,6 +334,11 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
gw_provision_mode = root.find("GwProvisionMode").text
operator_id = root.find("OperatorId").text

# `cm_provision_mode` is known to be None in case `provisioning_status` is "DS scanning". We need to set it to
# some string, otherwise the InfoMetricFamily call fails with AttributeError.
if cm_provision_mode is None:
cm_provision_mode = "Unknown"

# parse cm_system_info
root = etree.fromstring(
raw_xmls[GET.CM_SYSTEM_INFO], parser=self._parsers[GET.CM_SYSTEM_INFO]
Expand Down Expand Up @@ -359,10 +371,10 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
# return an enum-style metric for the provisioning status
try:
enum_provisioning_status = ProvisioningStatus(provisioning_status)
except:
raise ValueError(
f"Unknown provisioning status '{provisioning_status}'. Please open an issue on Github."
)
except ValueError:
self._logger.warning(f"Unknown provisioning status '{provisioning_status}'. Please open an issue on Github.")
enum_provisioning_status = ProvisioningStatus.UNKNOWN

yield StateSetMetricFamily(
"connectbox_provisioning_status",
"Provisioning status description",
Expand All @@ -375,14 +387,12 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
# uptime is reported in a format like "36day(s)15h:24m:58s" which needs parsing
uptime_pattern = r"(\d+)day\(s\)(\d+)h:(\d+)m:(\d+)s"
m = re.fullmatch(uptime_pattern, uptime_as_str)
if m is None:
raise ValueError(
f"Unexpected duration format '{uptime_as_str}', please open an issue on github."
)
uptime_timedelta = timedelta(
days=int(m[1]), hours=int(m[2]), minutes=int(m[3]), seconds=int(m[4])
)
uptime_seconds = uptime_timedelta.total_seconds()
if m is not None:
uptime_timedelta = timedelta(days=int(m[1]), hours=int(m[2]), minutes=int(m[3]), seconds=int(m[4]))
uptime_seconds = uptime_timedelta.total_seconds()
else:
self._logger.warning(f"Unexpected duration format '{uptime_as_str}', please open an issue on github.")
uptime_seconds = -1

yield GaugeMetricFamily(
"connectbox_uptime",
Expand All @@ -392,10 +402,11 @@ def extract(self, raw_xmls: Dict[int, bytes]) -> Iterable[Metric]:
)


def get_metrics_extractor(ident: str):
def get_metrics_extractor(ident: str, logger: Logger):
"""
Factory method for metrics extractors.
:param ident: metric extractor identifier
:param logger: logging logger
:return: extractor instance
"""
extractors = {
Expand All @@ -410,4 +421,4 @@ def get_metrics_extractor(ident: str):
f"Unknown extractor '{ident}', supported are: {', '.join(extractors.keys())}"
)
cls = extractors[ident]
return cls()
return cls(logger)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

setup(
name="connectbox-prometheus",
version="0.2.7",
version="0.2.8",
author="Michael Bugert",
author_email="[email protected]",
description='Prometheus exporter for Compal CH7465LG cable modems, commonly sold as "Connect Box"',
Expand Down

0 comments on commit 0aeb51f

Please sign in to comment.