mirror of
https://framagit.org/framasoft/framaspace/argos.git
synced 2025-04-28 18:02:41 +02:00
✨ — Add Apprise as notification way (fix #50)
This commit is contained in:
parent
7998333fc1
commit
6c3c44f5be
7 changed files with 166 additions and 15 deletions
|
@ -5,6 +5,7 @@
|
||||||
- 💄 — Improve email and gotify notifications
|
- 💄 — Improve email and gotify notifications
|
||||||
- ✨ — Add command to test gotify configuration
|
- ✨ — Add command to test gotify configuration
|
||||||
- ✨ — Add nagios command to use as a Nagios probe
|
- ✨ — Add nagios command to use as a Nagios probe
|
||||||
|
- ✨ — Add Apprise as notification way (#50)
|
||||||
|
|
||||||
## 0.3.1
|
## 0.3.1
|
||||||
|
|
||||||
|
|
|
@ -678,6 +678,73 @@ async def test_gotify(config, domain, severity):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@server.command()
|
||||||
|
@click.option(
|
||||||
|
"--config",
|
||||||
|
default="argos-config.yaml",
|
||||||
|
help="Path of the configuration file. "
|
||||||
|
"If ARGOS_YAML_FILE environment variable is set, its value will be used instead.",
|
||||||
|
envvar="ARGOS_YAML_FILE",
|
||||||
|
callback=validate_config_access,
|
||||||
|
)
|
||||||
|
@click.option("--domain", help="Domain for the notification", default="example.org")
|
||||||
|
@click.option("--severity", help="Severity", default="CRITICAL")
|
||||||
|
@click.option(
|
||||||
|
"--apprise-group", help="Apprise group for the notification", required=True
|
||||||
|
)
|
||||||
|
@coroutine
|
||||||
|
async def test_apprise(config, domain, severity, apprise_group):
|
||||||
|
"""Send a test apprise notification"""
|
||||||
|
os.environ["ARGOS_YAML_FILE"] = config
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from argos.logging import set_log_level
|
||||||
|
from argos.server.alerting import notify_with_apprise
|
||||||
|
from argos.server.main import read_config
|
||||||
|
from argos.server.models import Result, Task
|
||||||
|
|
||||||
|
conf = read_config(config)
|
||||||
|
|
||||||
|
if not conf.general.apprise:
|
||||||
|
click.echo("Apprise notifications are not configured, cannot test", err=True)
|
||||||
|
sysexit(1)
|
||||||
|
else:
|
||||||
|
now = datetime.now()
|
||||||
|
task = Task(
|
||||||
|
url=f"https://{domain}",
|
||||||
|
domain=domain,
|
||||||
|
check="body-contains",
|
||||||
|
expected="foo",
|
||||||
|
frequency=1,
|
||||||
|
selected_by="test",
|
||||||
|
selected_at=now,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = Result(
|
||||||
|
submitted_at=now,
|
||||||
|
status="success",
|
||||||
|
context={"foo": "bar"},
|
||||||
|
task=task,
|
||||||
|
agent_id="test",
|
||||||
|
severity="ok",
|
||||||
|
)
|
||||||
|
|
||||||
|
class _FalseRequest:
|
||||||
|
def url_for(*args, **kwargs):
|
||||||
|
return "/url"
|
||||||
|
|
||||||
|
set_log_level("debug")
|
||||||
|
notify_with_apprise(
|
||||||
|
result,
|
||||||
|
task,
|
||||||
|
severity=severity,
|
||||||
|
old_severity="OLD SEVERITY",
|
||||||
|
group=conf.general.apprise[apprise_group],
|
||||||
|
request=_FalseRequest(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@server.command(short_help="Nagios compatible severities report")
|
@server.command(short_help="Nagios compatible severities report")
|
||||||
@click.option(
|
@click.option(
|
||||||
"--config",
|
"--config",
|
||||||
|
|
|
@ -23,7 +23,10 @@ general:
|
||||||
frequency: "1m"
|
frequency: "1m"
|
||||||
# Which way do you want to be warned when a check goes to that severity?
|
# Which way do you want to be warned when a check goes to that severity?
|
||||||
# "local" emits a message in the server log
|
# "local" emits a message in the server log
|
||||||
# You’ll need to configure mail and gotify below to be able to use them here.
|
# You’ll need to configure mail, gotify or apprise below to be able to use
|
||||||
|
# them here.
|
||||||
|
# Use "apprise:john", "apprise:team" (with the quotes!) to use apprise
|
||||||
|
# notification groups.
|
||||||
alerts:
|
alerts:
|
||||||
ok:
|
ok:
|
||||||
- local
|
- local
|
||||||
|
@ -58,6 +61,17 @@ general:
|
||||||
# tokens:
|
# tokens:
|
||||||
# - foo
|
# - foo
|
||||||
# - bar
|
# - bar
|
||||||
|
# See https://github.com/caronc/apprise#productivity-based-notifications
|
||||||
|
# for apprise’s URLs syntax.
|
||||||
|
# You need to surround the URLs with quotes like in the examples below.
|
||||||
|
# Use "apprise:john", "apprise:team" (with the quotes!) in "alerts" settings.
|
||||||
|
# apprise:
|
||||||
|
# john:
|
||||||
|
# - "mastodon://access_key@hostname/@user"
|
||||||
|
# - "matrixs://token@hostname:port/?webhook=matrix"
|
||||||
|
# team:
|
||||||
|
# - "mmosts://user@hostname/authkey"
|
||||||
|
# - "nctalks://user:pass@host/RoomId1/RoomId2/RoomIdN"
|
||||||
|
|
||||||
service:
|
service:
|
||||||
secrets:
|
secrets:
|
||||||
|
|
|
@ -168,6 +168,7 @@ class General(BaseModel):
|
||||||
alerts: Alert
|
alerts: Alert
|
||||||
mail: Optional[Mail] = None
|
mail: Optional[Mail] = None
|
||||||
gotify: Optional[List[GotifyUrl]] = None
|
gotify: Optional[List[GotifyUrl]] = None
|
||||||
|
apprise: Optional[Dict[str, List[str]]] = None
|
||||||
|
|
||||||
@field_validator("frequency", mode="before")
|
@field_validator("frequency", mode="before")
|
||||||
def parse_frequency(cls, value):
|
def parse_frequency(cls, value):
|
||||||
|
|
|
@ -4,14 +4,24 @@ import smtplib
|
||||||
from typing import List
|
from typing import List
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import apprise
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from argos.checks.base import Severity
|
from argos.checks.base import Severity
|
||||||
from argos.logging import logger
|
from argos.logging import logger
|
||||||
from argos.schemas.config import Config, Mail, GotifyUrl
|
from argos.schemas.config import Config, Mail, GotifyUrl
|
||||||
|
|
||||||
# XXX Implement mail alerts https://framagit.org/framasoft/framaspace/argos/-/issues/15
|
|
||||||
# XXX Implement gotify alerts https://framagit.org/framasoft/framaspace/argos/-/issues/16
|
def get_icon_from_severity(severity: str) -> str:
|
||||||
|
icon = "❌"
|
||||||
|
if severity == Severity.OK:
|
||||||
|
icon = "✅"
|
||||||
|
elif severity == Severity.WARNING:
|
||||||
|
icon = "⚠️"
|
||||||
|
elif severity == Severity.UNKNOWN:
|
||||||
|
icon = "❔"
|
||||||
|
|
||||||
|
return icon
|
||||||
|
|
||||||
|
|
||||||
def handle_alert(config: Config, result, task, severity, old_severity, request):
|
def handle_alert(config: Config, result, task, severity, old_severity, request):
|
||||||
|
@ -39,20 +49,52 @@ def handle_alert(config: Config, result, task, severity, old_severity, request):
|
||||||
result, task, severity, old_severity, config.general.gotify, request
|
result, task, severity, old_severity, config.general.gotify, request
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if config.general.apprise is not None:
|
||||||
|
for notif_way in getattr(config.general.alerts, severity):
|
||||||
|
if notif_way.startswith("apprise:"):
|
||||||
|
group = notif_way[8:]
|
||||||
|
notify_with_apprise(
|
||||||
|
result,
|
||||||
|
task,
|
||||||
|
severity,
|
||||||
|
old_severity,
|
||||||
|
config.general.apprise[group],
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def notify_with_apprise(
|
||||||
|
result, task, severity: str, old_severity: str, group: List[str], request
|
||||||
|
) -> None:
|
||||||
|
logger.debug("Will send apprise notification")
|
||||||
|
|
||||||
|
apobj = apprise.Apprise()
|
||||||
|
for channel in group:
|
||||||
|
apobj.add(channel)
|
||||||
|
|
||||||
|
icon = get_icon_from_severity(severity)
|
||||||
|
title = f"[Argos] {icon} {urlparse(task.url).netloc}: status {severity}"
|
||||||
|
msg = f"""\
|
||||||
|
URL: {task.url}
|
||||||
|
Check: {task.check}
|
||||||
|
Status: {severity}
|
||||||
|
Time: {result.submitted_at}
|
||||||
|
Previous status: {old_severity}
|
||||||
|
|
||||||
|
See result on {request.url_for('get_result_view', result_id=result.id)}
|
||||||
|
|
||||||
|
See results of task on {request.url_for('get_task_results_view', task_id=task.id)}#{result.id}
|
||||||
|
"""
|
||||||
|
|
||||||
|
apobj.notify(title=title, body=msg)
|
||||||
|
|
||||||
|
|
||||||
def notify_by_mail(
|
def notify_by_mail(
|
||||||
result, task, severity: str, old_severity: str, config: Mail, request
|
result, task, severity: str, old_severity: str, config: Mail, request
|
||||||
) -> None:
|
) -> None:
|
||||||
logger.debug("Will send mail notification")
|
logger.debug("Will send mail notification")
|
||||||
|
|
||||||
icon = "❌"
|
icon = get_icon_from_severity(severity)
|
||||||
if severity == Severity.OK:
|
|
||||||
icon = "✅"
|
|
||||||
elif severity == Severity.WARNING:
|
|
||||||
icon = "⚠️"
|
|
||||||
elif severity == Severity.UNKNOWN:
|
|
||||||
icon = "❔"
|
|
||||||
|
|
||||||
msg = f"""\
|
msg = f"""\
|
||||||
URL: {task.url}
|
URL: {task.url}
|
||||||
Check: {task.check}
|
Check: {task.check}
|
||||||
|
@ -100,17 +142,14 @@ def notify_with_gotify(
|
||||||
logger.debug("Will send gotify notification")
|
logger.debug("Will send gotify notification")
|
||||||
headers = {"accept": "application/json", "content-type": "application/json"}
|
headers = {"accept": "application/json", "content-type": "application/json"}
|
||||||
|
|
||||||
|
icon = get_icon_from_severity(severity)
|
||||||
priority = 9
|
priority = 9
|
||||||
icon = "❌"
|
|
||||||
if severity == Severity.OK:
|
if severity == Severity.OK:
|
||||||
priority = 1
|
priority = 1
|
||||||
icon = "✅"
|
|
||||||
elif severity == Severity.WARNING:
|
elif severity == Severity.WARNING:
|
||||||
priority = 5
|
priority = 5
|
||||||
icon = "⚠️"
|
|
||||||
elif severity == Severity.UNKNOWN:
|
elif severity == Severity.UNKNOWN:
|
||||||
priority = 5
|
priority = 5
|
||||||
icon = "❔"
|
|
||||||
|
|
||||||
subject = f"{icon} {urlparse(task.url).netloc}: status {severity}"
|
subject = f"{icon} {urlparse(task.url).netloc}: status {severity}"
|
||||||
msg = f"""\
|
msg = f"""\
|
||||||
|
|
28
docs/cli.md
28
docs/cli.md
|
@ -86,6 +86,7 @@ Commands:
|
||||||
nagios Nagios compatible severities report
|
nagios Nagios compatible severities report
|
||||||
reload-config Load or reload tasks’ configuration
|
reload-config Load or reload tasks’ configuration
|
||||||
start Starts the server (use only for testing or development!)
|
start Starts the server (use only for testing or development!)
|
||||||
|
test-apprise Send a test apprise notification
|
||||||
test-gotify Send a test gotify notification
|
test-gotify Send a test gotify notification
|
||||||
test-mail Send a test email
|
test-mail Send a test email
|
||||||
user User management
|
user User management
|
||||||
|
@ -542,3 +543,30 @@ Options:
|
||||||
|
|
||||||
<!--[[[end]]]
|
<!--[[[end]]]
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
#### Test the Apprise settings
|
||||||
|
|
||||||
|
You can verify that your Apprise settings are ok by sending a test notification.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
.. [[[cog
|
||||||
|
help(["server", "test-apprise", "--help"])
|
||||||
|
.. ]]] -->
|
||||||
|
|
||||||
|
```man
|
||||||
|
Usage: argos server test-apprise [OPTIONS]
|
||||||
|
|
||||||
|
Send a test apprise notification
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--config TEXT Path of the configuration file. If ARGOS_YAML_FILE
|
||||||
|
environment variable is set, its value will be used
|
||||||
|
instead.
|
||||||
|
--domain TEXT Domain for the notification
|
||||||
|
--severity TEXT Severity
|
||||||
|
--apprise-group TEXT Apprise group for the notification [required]
|
||||||
|
--help Show this message and exit.
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--[[[end]]]
|
||||||
|
-->
|
||||||
|
|
|
@ -22,6 +22,7 @@ classifiers = [
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alembic>=1.13.0,<1.14",
|
"alembic>=1.13.0,<1.14",
|
||||||
|
"apprise>=1.9.0,<2",
|
||||||
"bcrypt>=4.1.3,<5",
|
"bcrypt>=4.1.3,<5",
|
||||||
"click>=8.1,<9",
|
"click>=8.1,<9",
|
||||||
"fastapi>=0.103,<0.104",
|
"fastapi>=0.103,<0.104",
|
||||||
|
|
Loading…
Reference in a new issue