finalized support for Pushover

This commit is contained in:
Antonio Fernandez 2024-10-30 15:43:06 -04:00
parent 2849f54932
commit 94db527500
6 changed files with 397 additions and 69 deletions

View File

@ -66,6 +66,7 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/).
<a href="https://cronitor.io/"><img src="docs/static/cronitor.png" alt="Cronitor" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://cronhub.io/"><img src="docs/static/cronhub.png" alt="Cronhub" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://www.pagerduty.com/"><img src="docs/static/pagerduty.png" alt="PagerDuty" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://www.pushover.net/"><img src="docs/static/pushover.png" alt="Pushover" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://ntfy.sh/"><img src="docs/static/ntfy.png" alt="ntfy" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://grafana.com/oss/loki/"><img src="docs/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://github.com/caronc/apprise/wiki"><img src="docs/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>

View File

@ -1637,40 +1637,48 @@ properties:
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: "0"
A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority.
example: 0
expire:
type: integer
description: |
he expire parameter specifies how many seconds
your notification will continue to be retried
for (every retry seconds).
example: 1200
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to that
device instead of all devices.
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.
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
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.
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
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:
@ -1681,8 +1689,8 @@ properties:
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
A title for the URL specified as the url parameter, otherwise just
the URL is shown.
example: Pushover Link
finish:
type: object
@ -1696,40 +1704,48 @@ properties:
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: "0"
A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority.
example: 0
expire:
type: integer
description: |
he expire parameter specifies how many seconds
your notification will continue to be retried
for (every retry seconds).
example: 1200
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to that
device instead of all devices.
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.
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
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.
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
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:
@ -1740,8 +1756,8 @@ properties:
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
A title for the URL specified as the url parameter, otherwise just
the URL is shown.
example: Pushover Link
fail:
type: object
@ -1754,40 +1770,48 @@ properties:
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: "0"
A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority.
example: 0
expire:
type: integer
description: |
he expire parameter specifies how many seconds
your notification will continue to be retried
for (every retry seconds).
example: 1200
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to that
device instead of all devices.
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.
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
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.
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
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:
@ -1798,8 +1822,8 @@ properties:
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
A title for the URL specified as the url parameter, otherwise just
the URL is shown.
example: Pushover Link
states:
type: array

View File

@ -46,18 +46,25 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
logger.warning(f'{config_filename}: User missing for Pushover')
return
if 'priority' in state_config and state_config['priority'] == 2:
if 'expire' not in state_config:
logger.info(f'{config_filename}: Setting expire to default (10min).')
state_config['expire'] = 1200
if 'retry' not in state_config:
logger.info(f'{config_filename}: Setting retry to default (30sec).')
state_config['retry'] = 30
else:
state_config.pop('expire', None)
state_config.pop('retry', None)
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)

View File

@ -331,20 +331,28 @@ pushover:
start:
message: "Backup <b>Started</b>"
priority: -2
device: "pixel8"
title: "Backup Started"
html: 1
sound: "bike"
ttl: 10
ttl: 10 # Message will be deleted after 10 seconds.
fail:
message: "Backup <font color='#ed4337'>Failed</font>"
priority: -2
message: "Backup <font color='#ff6961'>Failed</font>"
priority: 2 # Requests acknowledgement for messages.
expire: 1200 # Used only for priority 2. Default is 1200 seconds.
retry: 30 # Used only for priority 2. Default is 30 seconds.
device: "pixel8"
title: "Backup Started"
title: "Backup Failed"
html: 1
sound: "siren"
url: "https://ticketing-system.example.com/login"
url_title: "Login to ticketing system"
finish:
message: "Backup <font color='#77dd77'>Finished</font>"
priority: 0
title: "Backup Finished"
html: 1
ttl: 60
url: "https://ticketing-system.example.com/login"
url_title: "Login to ticketing system"
states:
- start
- finish

BIN
docs/static/pushover.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -142,17 +142,115 @@ def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pus
)
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).
def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_no_expiry_or_retry_success():
# This simulates priority level 2 being set but expiry and retry are
# not declared. This should set retry and expiry to their defaults.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {'priority': 1},
'start': {'priority': 2},
}
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': 2,
'retry': 30,
'expire': 1200,
},
).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_emergency_declared_with_expire_no_retry_success():
# This simulates priority level 2 and expiry being set but retry is
# not declared. This should set retry to the default.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {'priority': 2, 'expire': 600},
}
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': 2,
'retry': 30,
'expire': 600,
},
).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_emergency_declared_no_expire_with_retry_success():
# This simulates priority level 2 and retry being set but expire is
# not declared. This should set expire to the default.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {'priority': 2, 'expire': 30},
}
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': 2,
'retry': 30,
'expire': 30,
},
).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_high_declared_expire_and_retry_delared_success():
# This simulates priority level 1, retry and expiry being set. Since expire
# and retry are only used for priority level 2, they should not be included
# in the request sent to Pushover. This test verifies that those are
# stripped from the request.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {'priority': 1, 'expire': 30, 'retry': 30},
}
flexmock(module.logger).should_receive('warning').never()
flexmock(module.requests).should_receive('post').with_args(
@ -174,3 +272,193 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_declared_
monitoring_log_level=1,
dry_run=False,
)
def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example_success():
# Here is a test of what is provided in the monitor-your-backups.md file
# as an 'advanced example'. This test runs the start state.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {
'message': 'Backup <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 1200,
'retry': 30,
'device': 'pixel8',
'title': 'Backup Failed',
'html': 1,
'sound': 'siren',
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
'finish': {
'message': 'Backup <font color="#77dd77">Finished</font>',
'priority': 0,
'title': 'Backup Finished',
'html': 1,
'ttl': 60,
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
}
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': 'Backup <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
).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_fail_state_backup_based_on_documentation_advanced_example_success():
# Here is a test of what is provided in the monitor-your-backups.md file
# as an 'advanced example'. This test runs the fail state.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {
'message': 'Backup <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 1200,
'retry': 30,
'device': 'pixel8',
'title': 'Backup Failed',
'html': 1,
'sound': 'siren',
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
'finish': {
'message': 'Backup <font color="#77dd77">Finished</font>',
'priority': 0,
'title': 'Backup Finished',
'html': 1,
'ttl': 60,
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
}
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': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 1200,
'retry': 30,
'device': 'pixel8',
'title': 'Backup Failed',
'html': 1,
'sound': 'siren',
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
).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_finish_state_backup_based_on_documentation_advanced_example_success():
# Here is a test of what is provided in the monitor-your-backups.md file
# as an 'advanced example'. This test runs the finish state.
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {
'message': 'Backup <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 1200,
'retry': 30,
'device': 'pixel8',
'title': 'Backup Failed',
'html': 1,
'sound': 'siren',
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
'finish': {
'message': 'Backup <font color="#77dd77">Finished</font>',
'priority': 0,
'title': 'Backup Finished',
'html': 1,
'ttl': 60,
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
}
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': 'Backup <font color="#77dd77">Finished</font>',
'priority': 0,
'title': 'Backup Finished',
'html': 1,
'ttl': 60,
'url': 'https://ticketing-system.example.com/login',
'url_title': 'Login to ticketing system',
},
).and_return(flexmock(ok=True)).once()
module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.FINISH,
monitoring_log_level=1,
dry_run=False,
)