initial pushover commit
This commit is contained in:
parent
129f3e753c
commit
2849f54932
@ -1609,6 +1609,214 @@ properties:
|
||||
example:
|
||||
- start
|
||||
- finish
|
||||
pushover:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
token:
|
||||
type: string
|
||||
description: |
|
||||
Your application's API token.
|
||||
example: 7ms6TXHpTokTou2P6x4SodDeentHRa
|
||||
user:
|
||||
type: string
|
||||
description: |
|
||||
Your user/group key (or that of your target user), viewable
|
||||
when logged into your dashboard: often referred to as
|
||||
USER_KEY in Pushover documentation and code examples.
|
||||
example: hwRwoWsXMBWwgrSecfa9EfPey55WSN
|
||||
start:
|
||||
type: object
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: |
|
||||
Message to be sent to the user or group.
|
||||
example: A backup job has started.
|
||||
priority:
|
||||
type: integer
|
||||
description: |
|
||||
A value of -2, -1, 0 (default), 1 or 2 that
|
||||
indicates the message priority.
|
||||
example: "0"
|
||||
device:
|
||||
type: string
|
||||
description: |
|
||||
The name of one of your devices to send just to that
|
||||
device instead of all devices.
|
||||
example: pixel8
|
||||
html:
|
||||
type: integer
|
||||
description: |
|
||||
Set to 1 to enable HTML parsing of the message. Set
|
||||
to 0 for plain text.
|
||||
example: 1
|
||||
sound:
|
||||
type: string
|
||||
description: |
|
||||
The name of a supported sound to override your
|
||||
default sound choice. All options can be
|
||||
found here: https://pushover.net/api#sounds
|
||||
example: bike
|
||||
title:
|
||||
type: string
|
||||
description: |
|
||||
Your message's title, otherwise your app's
|
||||
name is used.
|
||||
example: A backup job has started.
|
||||
ttl:
|
||||
type: integer
|
||||
description: |
|
||||
The number of seconds that the message will live,
|
||||
before being deleted automatically. The ttl
|
||||
parameter is ignored for messages with a priority
|
||||
value of 2.
|
||||
example: 3600
|
||||
url:
|
||||
type: string
|
||||
description: |
|
||||
A supplementary URL to show with your message.
|
||||
example: https://pushover.net/apps/xxxxx-borgbackup
|
||||
url_title:
|
||||
type: string
|
||||
description: |
|
||||
A title for the URL specified as the url parameter,
|
||||
otherwise just the URL is shown.
|
||||
example: Pushover Link
|
||||
finish:
|
||||
type: object
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: |
|
||||
Message to be sent to the user or group.
|
||||
example: A backup job has started.
|
||||
priority:
|
||||
type: integer
|
||||
description: |
|
||||
A value of -2, -1, 0 (default), 1 or 2 that
|
||||
indicates the message priority.
|
||||
example: "0"
|
||||
device:
|
||||
type: string
|
||||
description: |
|
||||
The name of one of your devices to send just to that
|
||||
device instead of all devices.
|
||||
example: pixel8
|
||||
html:
|
||||
type: integer
|
||||
description: |
|
||||
Set to 1 to enable HTML parsing of the message. Set
|
||||
to 0 for plain text.
|
||||
example: 1
|
||||
sound:
|
||||
type: string
|
||||
description: |
|
||||
The name of a supported sound to override your
|
||||
default sound choice. All options can be
|
||||
found here: https://pushover.net/api#sounds
|
||||
example: bike
|
||||
title:
|
||||
type: string
|
||||
description: |
|
||||
Your message's title, otherwise your app's
|
||||
name is used.
|
||||
example: A backup job has started.
|
||||
ttl:
|
||||
type: integer
|
||||
description: |
|
||||
The number of seconds that the message will live,
|
||||
before being deleted automatically. The ttl
|
||||
parameter is ignored for messages with a priority
|
||||
value of 2.
|
||||
example: 3600
|
||||
url:
|
||||
type: string
|
||||
description: |
|
||||
A supplementary URL to show with your message.
|
||||
example: https://pushover.net/apps/xxxxx-borgbackup
|
||||
url_title:
|
||||
type: string
|
||||
description: |
|
||||
A title for the URL specified as the url parameter,
|
||||
otherwise just the URL is shown.
|
||||
example: Pushover Link
|
||||
fail:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: |
|
||||
Message to be sent to the user or group.
|
||||
example: A backup job has started.
|
||||
priority:
|
||||
type: integer
|
||||
description: |
|
||||
A value of -2, -1, 0 (default), 1 or 2 that
|
||||
indicates the message priority.
|
||||
example: "0"
|
||||
device:
|
||||
type: string
|
||||
description: |
|
||||
The name of one of your devices to send just to that
|
||||
device instead of all devices.
|
||||
example: pixel8
|
||||
html:
|
||||
type: integer
|
||||
description: |
|
||||
Set to 1 to enable HTML parsing of the message. Set
|
||||
to 0 for plain text.
|
||||
example: 1
|
||||
sound:
|
||||
type: string
|
||||
description: |
|
||||
The name of a supported sound to override your
|
||||
default sound choice. All options can be
|
||||
found here: https://pushover.net/api#sounds
|
||||
example: bike
|
||||
title:
|
||||
type: string
|
||||
description: |
|
||||
Your message's title, otherwise your app's
|
||||
name is used.
|
||||
example: A backup job has started.
|
||||
ttl:
|
||||
type: integer
|
||||
description: |
|
||||
The number of seconds that the message will live,
|
||||
before being deleted automatically. The ttl
|
||||
parameter is ignored for messages with a priority
|
||||
value of 2.
|
||||
example: 3600
|
||||
url:
|
||||
type: string
|
||||
description: |
|
||||
A supplementary URL to show with your message.
|
||||
example: https://pushover.net/apps/xxxxx-borgbackup
|
||||
url_title:
|
||||
type: string
|
||||
description: |
|
||||
A title for the URL specified as the url parameter,
|
||||
otherwise just the URL is shown.
|
||||
example: Pushover Link
|
||||
states:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- start
|
||||
- finish
|
||||
- fail
|
||||
uniqueItems: true
|
||||
description: |
|
||||
List of one or more monitoring states to ping for: "start",
|
||||
"finish", and/or "fail". Defaults to pinging for failure
|
||||
only.
|
||||
example:
|
||||
- start
|
||||
- finish
|
||||
zabbix:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
@ -12,6 +12,7 @@ from borgmatic.hooks import (
|
||||
ntfy,
|
||||
pagerduty,
|
||||
postgresql,
|
||||
pushover,
|
||||
sqlite,
|
||||
uptimekuma,
|
||||
zabbix,
|
||||
@ -31,6 +32,7 @@ HOOK_NAME_TO_MODULE = {
|
||||
'ntfy': ntfy,
|
||||
'pagerduty': pagerduty,
|
||||
'postgresql_databases': postgresql,
|
||||
'pushover': pushover,
|
||||
'sqlite_databases': sqlite,
|
||||
'uptime_kuma': uptimekuma,
|
||||
'zabbix': zabbix,
|
||||
|
@ -8,6 +8,7 @@ MONITOR_HOOK_NAMES = (
|
||||
'loki',
|
||||
'ntfy',
|
||||
'pagerduty',
|
||||
'pushover',
|
||||
'uptime_kuma',
|
||||
'zabbix',
|
||||
)
|
||||
|
82
borgmatic/hooks/pushover.py
Normal file
82
borgmatic/hooks/pushover.py
Normal file
@ -0,0 +1,82 @@
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def initialize_monitor(
|
||||
ping_url, config, config_filename, monitoring_log_level, dry_run
|
||||
): # pragma: no cover
|
||||
'''
|
||||
No initialization is necessary for this monitor.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
def ping_monitor(hook_config, config, config_filename, state, monitoring_log_level, dry_run):
|
||||
'''
|
||||
Post a message to the configured Pushover application.
|
||||
If this is a dry run, then don't actually update anything.
|
||||
'''
|
||||
|
||||
run_states = hook_config.get('states', ['fail'])
|
||||
|
||||
if state.name.lower() not in run_states:
|
||||
return
|
||||
|
||||
dry_run_label = ' (dry run; not actually updating)' if dry_run else ''
|
||||
|
||||
state_config = hook_config.get(
|
||||
state.name.lower(),
|
||||
{
|
||||
'message': state.name.lower(),
|
||||
},
|
||||
)
|
||||
|
||||
token = hook_config.get('token')
|
||||
user = hook_config.get('user')
|
||||
|
||||
logger.info(f'{config_filename}: Updating Pushover {dry_run_label}')
|
||||
|
||||
if token is None:
|
||||
logger.warning(f'{config_filename}: Token missing for Pushover')
|
||||
return
|
||||
if user is None:
|
||||
logger.warning(f'{config_filename}: User missing for Pushover')
|
||||
return
|
||||
|
||||
data = {
|
||||
'token': token,
|
||||
'user': user,
|
||||
'message': state.name.lower(), # default to state name. Can be overwritten in state_config loop below.
|
||||
}
|
||||
|
||||
for key in state_config:
|
||||
data[key] = state_config[key]
|
||||
if key == 'priority':
|
||||
if data['priority'] == 2:
|
||||
data['expire'] = 30
|
||||
data['retry'] = 30
|
||||
|
||||
if not dry_run:
|
||||
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
||||
try:
|
||||
response = requests.post(
|
||||
'https://api.pushover.net/1/messages.json',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data=data,
|
||||
)
|
||||
if not response.ok:
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.RequestException as error:
|
||||
logger.warning(f'{config_filename}: Pushover error: {error}')
|
||||
|
||||
|
||||
def destroy_monitor(
|
||||
ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run
|
||||
): # pragma: no cover
|
||||
'''
|
||||
No destruction is necessary for this monitor.
|
||||
'''
|
||||
pass
|
@ -46,6 +46,7 @@ them as backups happen:
|
||||
* [Healthchecks](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#healthchecks-hook)
|
||||
* [ntfy](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook)
|
||||
* [PagerDuty](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook)
|
||||
* [Pushover](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pushover-hook)
|
||||
* [Uptime Kuma](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#uptime-kuma-hook)
|
||||
* [Zabbix](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook)
|
||||
|
||||
@ -290,6 +291,67 @@ If you have any issues with the integration, [please contact
|
||||
us](https://torsion.org/borgmatic/#support-and-contributing).
|
||||
|
||||
|
||||
## Pushover hook
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 1.9.0</span>
|
||||
[Pushover](https://pushover.net) makes it easy to get real-time notifications
|
||||
on your Android, iPhone, iPad, and Desktop (Android Wear and Apple Watch, too!)
|
||||
|
||||
First, create a Pushover account and login on your mobile device. Create an
|
||||
Application in your Pushover dashboard.
|
||||
|
||||
Then, configure borgmatic with your user's unique "User Key" found in your
|
||||
Pushover dashboard and the unique "API Token" from the created Application.
|
||||
|
||||
Here's a basic example:
|
||||
|
||||
|
||||
```yaml
|
||||
pushover:
|
||||
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
|
||||
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
|
||||
```
|
||||
|
||||
|
||||
With this configuration, borgmatic creates a Pushover event for your service
|
||||
whenever backups fail, but only when any of the `create`, `prune`, `compact`,
|
||||
or `check` actions are run. Note that borgmatic does not contact Pushover
|
||||
when a backup starts or when it ends without error.
|
||||
|
||||
You can configure Pushover to have custom parameters declared for borgmatic's
|
||||
`start`, `fail` and `finish` hooks states.
|
||||
|
||||
Here's a more advanced example:
|
||||
|
||||
|
||||
```yaml
|
||||
pushover:
|
||||
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
|
||||
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
|
||||
start:
|
||||
message: "Backup <b>Started</b>"
|
||||
priority: -2
|
||||
device: "pixel8"
|
||||
title: "Backup Started"
|
||||
html: 1
|
||||
sound: "bike"
|
||||
ttl: 10
|
||||
fail:
|
||||
message: "Backup <font color='#ed4337'>Failed</font>"
|
||||
priority: -2
|
||||
device: "pixel8"
|
||||
title: "Backup Started"
|
||||
html: 1
|
||||
sound: "siren"
|
||||
url: "https://ticketing-system.example.com/login"
|
||||
url_title: "Login to ticketing system"
|
||||
states:
|
||||
- start
|
||||
- finish
|
||||
- fail
|
||||
```
|
||||
|
||||
|
||||
## ntfy hook
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 1.6.3</span>
|
||||
|
176
tests/unit/hooks/test_pushover.py
Normal file
176
tests/unit/hooks/test_pushover.py
Normal file
@ -0,0 +1,176 @@
|
||||
from flexmock import flexmock
|
||||
|
||||
import borgmatic.hooks.monitor
|
||||
from borgmatic.hooks import pushover as module
|
||||
|
||||
|
||||
def test_ping_monitor_config_with_token_only_exit_early():
|
||||
# This test should exit early since only providing a token is not enough
|
||||
# for the hook to work
|
||||
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92'}
|
||||
flexmock(module.logger).should_receive('warning').once()
|
||||
flexmock(module.requests).should_receive('post').never()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.FAIL,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_config_with_user_only_exit_early():
|
||||
# This test should exit early since only providing a token is not enough
|
||||
# for the hook to work
|
||||
hook_config = {'user': '983hfe0of902lkjfa2amanfgui'}
|
||||
flexmock(module.logger).should_receive('warning').once()
|
||||
flexmock(module.requests).should_receive('post').never()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.FAIL,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover():
|
||||
# This test should be the minimum working configuration. The "message"
|
||||
# should be auto populated with the default value which is the state name.
|
||||
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
|
||||
flexmock(module.logger).should_receive('warning').never()
|
||||
flexmock(module.requests).should_receive('post').with_args(
|
||||
'https://api.pushover.net/1/messages.json',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data={
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'message': 'fail',
|
||||
},
|
||||
).and_return(flexmock(ok=True)).once()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.FAIL,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_config_with_minimum_config_start_state_backup_not_send_to_pushover_exit_early():
|
||||
# This test should exit early since the hook config does not specify the
|
||||
# 'start' state. Only the 'fail' state is enabled by default.
|
||||
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
|
||||
flexmock(module.logger).should_receive('warning').never()
|
||||
flexmock(module.requests).should_receive('post').never()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.START,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_start_state_backup_default_message_successfully_send_to_pushover():
|
||||
# This test should send a notification to Pushover on backup start
|
||||
# since the state has been configured. It should default to sending
|
||||
# the name of the state as the 'message' since it is not
|
||||
# explicitly declared in the state config.
|
||||
hook_config = {
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'states': {'start', 'fail', 'finish'},
|
||||
}
|
||||
flexmock(module.logger).should_receive('warning').never()
|
||||
flexmock(module.requests).should_receive('post').with_args(
|
||||
'https://api.pushover.net/1/messages.json',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data={
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'message': 'start',
|
||||
},
|
||||
).and_return(flexmock(ok=True)).once()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.START,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pushover():
|
||||
# This test should send a notification to Pushover on backup start
|
||||
# since the state has been configured. It should send a custom
|
||||
# 'message' since it is explicitly declared in the state config.
|
||||
hook_config = {
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'states': {'start', 'fail', 'finish'},
|
||||
'start': {'message': 'custom start message'},
|
||||
}
|
||||
flexmock(module.logger).should_receive('warning').never()
|
||||
flexmock(module.requests).should_receive('post').with_args(
|
||||
'https://api.pushover.net/1/messages.json',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data={
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'message': 'custom start message',
|
||||
},
|
||||
).and_return(flexmock(ok=True)).once()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.START,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
|
||||
def test_ping_monitor_start_state_backup_default_message_with_priority_declared_successfully_send_to_pushover():
|
||||
# This test should send a notification to Pushover on backup start
|
||||
# since the state has been configured. It should default to sending
|
||||
# the name of the state as the 'message' since it is not
|
||||
# explicitly declared in the state config. It should also send
|
||||
# with a priority of 1 (high).
|
||||
hook_config = {
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'states': {'start', 'fail', 'finish'},
|
||||
'start': {'priority': 1},
|
||||
}
|
||||
flexmock(module.logger).should_receive('warning').never()
|
||||
flexmock(module.requests).should_receive('post').with_args(
|
||||
'https://api.pushover.net/1/messages.json',
|
||||
headers={'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data={
|
||||
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
|
||||
'user': '983hfe0of902lkjfa2amanfgui',
|
||||
'message': 'start',
|
||||
'priority': 1,
|
||||
},
|
||||
).and_return(flexmock(ok=True)).once()
|
||||
|
||||
module.ping_monitor(
|
||||
hook_config,
|
||||
{},
|
||||
'config.yaml',
|
||||
borgmatic.hooks.monitor.State.START,
|
||||
monitoring_log_level=1,
|
||||
dry_run=False,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user