Skip to content

Commit

Permalink
Add firmware checks
Browse files Browse the repository at this point in the history
  • Loading branch information
jiuka committed Oct 26, 2024
1 parent dd301e7 commit f568887
Show file tree
Hide file tree
Showing 10 changed files with 606 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pytest
pytest-md
pytest-cov
pytest-emoji
requests-mock
requests-mock
pytest-freezer
113 changes: 113 additions & 0 deletions agent_based/opnsense_firmware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# checkmk_opnsense - Checkmk extension for OPNsense
#
# Copyright (C) 2024 Marius Rieder <marius.rieder@scs.ch>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import json
from datetime import datetime
from cmk.agent_based.v2 import (
AgentSection,
check_levels,
CheckPlugin,
CheckResult,
DiscoveryResult,
render,
Result,
Service,
State,
Metric,
)


def parse_opnsense_firmware(string_table: list[list[str]]) -> dict:
if string_table:
return json.loads(string_table[0][0])
return None


agent_section_opnsense_firmware = AgentSection(
name='opnsense_firmware',
parse_function=parse_opnsense_firmware,
)


def discovery_opnsense_firmware(section: dict | None) -> DiscoveryResult:
if section:
yield Service()


def check_opnsense_firmware(params: dict, section: dict) -> CheckResult:
yield Result(state=State.OK, summary=f"{section['product']['product_series']} ({section['product']['product_nickname']})")

if 'last_check' not in section:
yield Result(state=State.OK, summary=section['status_msg'])
return

last_check = datetime.strptime(section['last_check'], "%a %b %d %X %Z %Y")
last_check_age = (datetime.now() - last_check).seconds
yield from check_levels(
value=last_check_age,
levels_upper=params.get('last_check', None),
metric_name='last_check',
render_func=render.timespan,
label='Last update check',
notice_only=True
)

if section['status'] == 'update':
yield Result(state=State.OK, summary=section['status_msg'])
yield Metric(name='updates', value=len(section['product']['product_check']['upgrade_packages']))


check_plugin_opnsense_firmware = CheckPlugin(
name='opnsense_firmware',
service_name='OPNsense Firmware',
discovery_function=discovery_opnsense_firmware,
check_function=check_opnsense_firmware,
check_default_parameters={},
check_ruleset_name='opnsense_firmware',
)


def discovery_opnsense_business(section: dict | None) -> DiscoveryResult:
if section and section.get('product_id', None) == 'opnsense-business':
yield Service()


def check_opnsense_business(params: dict, section: dict) -> CheckResult:
valid_to = datetime.fromisoformat(section['product']['product_license']['valid_to'])
valid_to_days = (valid_to - datetime.now()).days
yield from check_levels(
value=valid_to_days,
levels_lower=params.get('expiredays', ('fixed', (60, 30))),
metric_name='expiredays',
render_func=lambda x: f"{x} days",
label='License expires in',
)


check_plugin_opnsense_business = CheckPlugin(
name='opnsense_business',
sections=['opnsense_firmware'],
service_name='OPNsense Business',
discovery_function=discovery_opnsense_business,
check_function=check_opnsense_business,
check_default_parameters={},
check_ruleset_name='opnsense_business',
)
2 changes: 1 addition & 1 deletion agent_based/opnsense_vip.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def discovery_opnsense_carp(


def check_opnsense_carp(
params,
params: dict,
section_opnsense_carp: _Section | None,
section_opnsense_vip: _Section | None
) -> CheckResult:
Expand Down
3 changes: 3 additions & 0 deletions lib/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ def api(self):
def main(self, args: Args):
self.args = args

with SectionWriter('opnsense_firmware') as section:
section.append_json(self.api.get('core', 'firmware', 'status'))

with SectionWriter('opnsense_carp') as section:
section.append_json(self.api.getVipStatus['carp'])
with SectionWriter('opnsense_vip') as section:
Expand Down
2 changes: 2 additions & 0 deletions package
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
'opnsense/graphing/opnsense_vip.py',
'opnsense/lib/agent.py',
'opnsense/libexec/agent_opnsense',
'opnsense/agent_based/opnsense_firmware.py',
'opnsense/agent_based/opnsense_gateway.py',
'opnsense/agent_based/opnsense_vip.py',
'opnsense/rulesets/datasource.py',
'opnsense/rulesets/opnsense_firmware.py',
'opnsense/rulesets/opnsense_gateway.py',
'opnsense/rulesets/opnsense_vip.py',
'opnsense/server_side_calls/agent_opnsense.py',
Expand Down
91 changes: 91 additions & 0 deletions rulesets/opnsense_firmware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# checkmk_opnsense - Checkmk extension for OPNsense
#
# Copyright (C) 2024 Marius Rieder <marius.rieder@scs.ch>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from cmk.rulesets.v1 import Help, Title
from cmk.rulesets.v1.form_specs import (
DefaultValue,
DictElement,
Dictionary,
InputHint,
Integer,
LevelDirection,
LevelsType,
SimpleLevels,
TimeMagnitude,
TimeSpan,
)
from cmk.rulesets.v1.rule_specs import CheckParameters, Topic, HostCondition


def _parameter_form_opnsense_firmware():
return Dictionary(
elements={
'last_check': DictElement(
parameter_form=SimpleLevels(
title=Title('Last check age'),
level_direction=LevelDirection.UPPER,
form_spec_template=TimeSpan(displayed_magnitudes=[TimeMagnitude.DAY, TimeMagnitude.HOUR]),
prefill_levels_type=DefaultValue(LevelsType.FIXED),
prefill_fixed_levels=InputHint(value=(7 * 24 * 3600, 10 * 24 * 3600)),
),
required=False,
),
}
)


rule_spec_opnsense_firmware = CheckParameters(
name='opnsense_firmware',
topic=Topic.NETWORKING,
parameter_form=_parameter_form_opnsense_firmware,
title=Title('OPNsense Firmware Update check'),
help_text=Help('This rule configures thresholds for OPNsense Firmware check.'),
condition=HostCondition(),
)


def _parameter_form_opnsense_business():
return Dictionary(
elements={
'expiredays': DictElement(
parameter_form=SimpleLevels(
title=Title('Expiry in days'),
level_direction=LevelDirection.LOWER,
form_spec_template=Integer(
unit_symbol='days'
),
prefill_levels_type=DefaultValue(LevelsType.FIXED),
prefill_fixed_levels=InputHint(value=(60, 30)),
),
required=False,
),
}
)


rule_spec_opnsense_business = CheckParameters(
name='opnsense_business',
topic=Topic.NETWORKING,
parameter_form=_parameter_form_opnsense_business,
title=Title('OPNsense Businiess License'),
help_text=Help('This rule configures thresholds for OPNsense license validity.'),
condition=HostCondition(),
)
28 changes: 28 additions & 0 deletions tests/unit/agent_based/test_data/firmware_info/nostatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"product": {
"product_abi": "24.7",
"product_arch": "amd64",
"product_check": null,
"product_conflicts": "os-firewall os-firewall-devel os-wireguard os-wireguard-devel os-wireguard-go os-wireguard-go-devel",
"product_copyright_owner": "Deciso B.V.",
"product_copyright_url": "https:\/\/www.deciso.com\/",
"product_copyright_years": "2014-2024",
"product_email": "project@opnsense.org",
"product_hash": "f20b6eaa5",
"product_id": "opnsense",
"product_latest": "24.7.7",
"product_license": [],
"product_log": 0,
"product_mirror": "https:\/\/pkg.opnsense.org\/FreeBSD:14:amd64\/24.7",
"product_name": "OPNsense",
"product_nickname": "Thriving Tiger",
"product_repos": "OPNsense (Priority: 11)",
"product_series": "24.7",
"product_tier": "1",
"product_time": "Fri Oct 25 16:58:04 UTC 2024",
"product_version": "24.7.7",
"product_website": "https:\/\/opnsense.org\/"
},
"status_msg": "Firmware status requires to check for update first to provide more information.",
"status": "none"
}
Loading

0 comments on commit f568887

Please sign in to comment.