diff --git a/NEWS b/NEWS
index 442f4e6c..f7ff6b92 100644
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,8 @@
* Update the "--match-archives" and "--archive" flags to support Borg 2 series names or archive
hashes.
* Add a "--match-archives" flag to the "prune" action.
+ * Add a Zabbix monitoring hook. See the documentation for more information:
+ https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook
1.8.14
* #896: Fix an error in borgmatic rcreate/init on an empty repository directory with Borg 1.4.
diff --git a/README.md b/README.md
index a7000d68..45b48a4e 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,7 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/).
+
diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml
index 9536164a..4dd7c76f 100644
--- a/borgmatic/config/schema.yaml
+++ b/borgmatic/config/schema.yaml
@@ -1622,12 +1622,14 @@ properties:
host:
type: string
description: |
- Host name where the item is stored. Required if "itemid" is not set.
+ Host name where the item is stored. Required if "itemid"
+ is not set.
example: borg-server
key:
type: string
description: |
- Key of the host where the item is stored. Required if "itemid" is not set.
+ Key of the host where the item is stored. Required if
+ "itemid" is not set.
example: borg.status
server:
type: string
@@ -1637,17 +1639,20 @@ properties:
username:
type: string
description: |
- The username used for authentication. Not needed if using an API key.
+ The username used for authentication. Not needed if using
+ an API key.
example: testuser
password:
type: string
description: |
- The password used for authentication. Not needed if using an API key.
+ The password used for authentication. Not needed if using
+ an API key.
example: fakepassword
api_key:
type: string
description: |
- The API key used for authentication. Not needed if using an username/password.
+ The API key used for authentication. Not needed if using
+ an username/password.
example: fakekey
start:
type: object
diff --git a/borgmatic/hooks/zabbix.py b/borgmatic/hooks/zabbix.py
index 13f51b64..a33d77b4 100644
--- a/borgmatic/hooks/zabbix.py
+++ b/borgmatic/hooks/zabbix.py
@@ -1,4 +1,3 @@
-import json
import logging
import requests
@@ -52,23 +51,23 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
logger.warning(f'{config_filename}: Server missing for Zabbix')
return
- # Determine the zabbix method used to store the value: itemid or host/key
+ # Determine the Zabbix method used to store the value: itemid or host/key
if itemid is not None:
logger.info(f'{config_filename}: Updating {itemid} on Zabbix')
data = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"itemid": itemid, "value": value},
- "id": 1,
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'itemid': itemid, 'value': value},
+ 'id': 1,
}
elif (host and key) is not None:
logger.info(f'{config_filename}: Updating Host:{host} and Key:{key} on Zabbix')
data = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"host": host, "key": key, "value": value},
- "id": 1,
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'host': host, 'key': key, 'value': value},
+ 'id': 1,
}
elif host is not None:
@@ -90,13 +89,10 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
elif (username and password) is not None:
logger.info(f'{config_filename}: Using user/pass auth with user {username} for Zabbix')
auth_data = {
- "jsonrpc": "2.0",
- "method": "user.login",
- "params": {
- "username": username,
- "password": password
- },
- "id": 1
+ 'jsonrpc': '2.0',
+ 'method': 'user.login',
+ 'params': {'username': username, 'password': password},
+ 'id': 1,
}
if not dry_run:
logging.getLogger('urllib3').setLevel(logging.ERROR)
@@ -107,6 +103,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
response.raise_for_status()
except requests.exceptions.RequestException as error:
logger.warning(f'{config_filename}: Zabbix error: {error}')
+ return
elif username is not None:
logger.warning(f'{config_filename}: Password missing for Zabbix authentication')
@@ -118,7 +115,6 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
else:
logger.warning(f'{config_filename}: Authentication data missing for Zabbix')
return
-
if not dry_run:
logging.getLogger('urllib3').setLevel(logging.ERROR)
diff --git a/docs/how-to/monitor-your-backups.md b/docs/how-to/monitor-your-backups.md
index 3ea51735..f5007401 100644
--- a/docs/how-to/monitor-your-backups.md
+++ b/docs/how-to/monitor-your-backups.md
@@ -566,14 +566,16 @@ Resend Notification every X times = 1
## Zabbix hook
New in version 1.9.0
-[zabbix](https://www.zabbix.com/) is an open-source monitoring tool used for tracking and managing the performance and availability of networks, servers, and applications in real-time.
+[Zabbix](https://www.zabbix.com/) is an open-source monitoring tool used for
+tracking and managing the performance and availability of networks, servers,
+and applications in real-time.
-This hook does not do any notifications on its own. Instead, it relies on
-your Zabbix instance to notify and perform escalations based on the Zabbix
-configuration. The `states` defined in the configuration will determine which states
-will trigger the hook. The value defined in the configuration of each state is
-used to populate the data of the configured Zabbix item. If none are provided,
-it default to a lower-case string of the state.
+This hook does not do any notifications on its own. Instead, it relies on your
+Zabbix instance to notify and perform escalations based on the Zabbix
+configuration. The `states` defined in the configuration determine which
+states will trigger the hook. The value defined in the configuration of each
+state is used to populate the data of the configured Zabbix item. If none are
+provided, it defaults to a lower-case string of the state.
An example configuration is shown here with all the available options.
@@ -601,19 +603,22 @@ zabbix:
- fail
```
-### Zabbix 7.0+
This hook requires the Zabbix server be running version 7.0+
-Authentication Methods
-Authentication can be accomplished via `api_key` or `username` and `password`.
-If both are declared, `api_key` will be chosen.
-Items The item
-to be updated can be chosen by either declaring the `itemid` or
-`host` and `key`. If both are declared, `itemid` will be chosen.
+### Authentication methods
-Keep in mind that `host` is referring to the 'Host name' on the
-Zabbix host and not the 'Visual name'.
+Authentication can be accomplished via `api_key` or both `username` and
+`password`. If all three are declared, only `api_key` is used.
+
+
+### Items
+
+The item to be updated can be chosen by either declaring the `itemid` or both
+`host` and `key`. If all three are declared, only `itemid` is used.
+
+Keep in mind that `host` is referring to the "Host name" on the Zabbix server
+and not the "Visual name".
## Scripting borgmatic
diff --git a/docs/static/zabbix.png b/docs/static/zabbix.png
new file mode 100644
index 00000000..746ad2bc
Binary files /dev/null and b/docs/static/zabbix.png differ
diff --git a/tests/unit/hooks/test_zabbix.py b/tests/unit/hooks/test_zabbix.py
index 2fe28cca..6f1a641d 100644
--- a/tests/unit/hooks/test_zabbix.py
+++ b/tests/unit/hooks/test_zabbix.py
@@ -1,5 +1,3 @@
-from enum import Enum
-
from flexmock import flexmock
import borgmatic.hooks.monitor
@@ -15,58 +13,70 @@ KEY = 'borg.status'
VALUE = 'fail'
DATA_HOST_KEY = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"host": HOST, "key": KEY, "value": VALUE},
- "id": 1,
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'host': HOST, 'key': KEY, 'value': VALUE},
+ 'id': 1,
}
-DATA_HOST_KEY_WITH_TOKEN = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"host": HOST, "key": KEY, "value": VALUE},
- "id": 1,
- "auth": "3fe6ed01a69ebd79907a120bcd04e494"
+DATA_HOST_KEY_WITH_KEY_VALUE = {
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'host': HOST, 'key': KEY, 'value': VALUE},
+ 'id': 1,
+ 'auth': '3fe6ed01a69ebd79907a120bcd04e494',
}
DATA_ITEMID = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"itemid": ITEMID, "value": VALUE},
- "id": 1,
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'itemid': ITEMID, 'value': VALUE},
+ 'id': 1,
}
-DATA_HOST_KEY_WITH_TOKEN = {
- "jsonrpc": "2.0",
- "method": "history.push",
- "params": {"itemid": ITEMID, "value": VALUE},
- "id": 1,
- "auth": "3fe6ed01a69ebd79907a120bcd04e494"
+DATA_HOST_KEY_WITH_ITEMID = {
+ 'jsonrpc': '2.0',
+ 'method': 'history.push',
+ 'params': {'itemid': ITEMID, 'value': VALUE},
+ 'id': 1,
+ 'auth': '3fe6ed01a69ebd79907a120bcd04e494',
}
DATA_USER_LOGIN = {
- "jsonrpc": "2.0",
- "method": "user.login",
- "params": {"username": USERNAME, "password": PASSWORD},
- "id": 1,
+ 'jsonrpc': '2.0',
+ 'method': 'user.login',
+ 'params': {'username': USERNAME, 'password': PASSWORD},
+ 'id': 1,
}
AUTH_HEADERS_API_KEY = {
'Content-Type': 'application/json-rpc',
- 'Authorization': f'Bearer {API_KEY}'
+ 'Authorization': f'Bearer {API_KEY}',
}
-AUTH_HEADERS_USERNAME_PASSWORD = {
- 'Content-Type': 'application/json-rpc'
-}
+AUTH_HEADERS_USERNAME_PASSWORD = {'Content-Type': 'application/json-rpc'}
+
+
+def test_ping_monitor_with_non_matching_state_exits_early():
+ hook_config = {'api_key': API_KEY}
+ 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_config_with_api_key_only_exit_early():
- # This test should exit early since only providing an API KEY is not enough
+ # This test should exit early since only providing an API KEY is not enough
# for the hook to work
- hook_config = {
- 'api_key': API_KEY
- }
+ hook_config = {'api_key': API_KEY}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -77,13 +87,13 @@ def test_ping_monitor_config_with_api_key_only_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_with_host_only_exit_early():
- # This test should exit early since only providing a HOST is not enough
+ # This test should exit early since only providing a HOST is not enough
# for the hook to work
- hook_config = {
- 'host': HOST
- }
+ hook_config = {'host': HOST}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -94,13 +104,13 @@ def test_ping_monitor_config_with_host_only_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_with_key_only_exit_early():
- # This test should exit early since only providing a KEY is not enough
+ # This test should exit early since only providing a KEY is not enough
# for the hook to work
- hook_config = {
- 'key': KEY
- }
+ hook_config = {'key': KEY}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -111,13 +121,13 @@ def test_ping_monitor_config_with_key_only_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_with_server_only_exit_early():
- # This test should exit early since only providing a SERVER is not enough
+ # This test should exit early since only providing a SERVER is not enough
# for the hook to work
- hook_config = {
- 'server': SERVER
- }
+ hook_config = {'server': SERVER}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -128,14 +138,12 @@ def test_ping_monitor_config_with_server_only_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_user_password_no_zabbix_data_exit_early():
# This test should exit early since there are HOST/KEY or ITEMID provided to publish data to
- hook_config = {
- 'server': SERVER,
- 'username': USERNAME,
- 'password': PASSWORD
- }
+ hook_config = {'server': SERVER, 'username': USERNAME, 'password': PASSWORD}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -146,13 +154,12 @@ def test_ping_monitor_config_user_password_no_zabbix_data_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_api_key_no_zabbix_data_exit_early():
# This test should exit early since there are HOST/KEY or ITEMID provided to publish data to
- hook_config = {
- 'server': SERVER,
- 'api_key': API_KEY
- }
+ hook_config = {'server': SERVER, 'api_key': API_KEY}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -163,14 +170,13 @@ def test_ping_monitor_config_api_key_no_zabbix_data_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_itemid_no_auth_data_exit_early():
- # This test should exit early since there is no authentication provided
+ # This test should exit early since there is no authentication provided
# and Zabbix requires authentication to use it's API
- hook_config = {
- 'server': SERVER,
- 'itemid': ITEMID
- }
+ hook_config = {'server': SERVER, 'itemid': ITEMID}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -181,15 +187,13 @@ def test_ping_monitor_config_itemid_no_auth_data_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_host_and_key_no_auth_data_exit_early():
- # This test should exit early since there is no authentication provided
+ # This test should exit early since there is no authentication provided
# and Zabbix requires authentication to use it's API
- hook_config = {
- 'server': SERVER,
- 'host': HOST,
- 'key': KEY
- }
+ hook_config = {'server': SERVER, 'host': HOST, 'key': KEY}
flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').never()
module.ping_monitor(
hook_config,
@@ -200,15 +204,11 @@ def test_ping_monitor_config_host_and_key_no_auth_data_exit_early():
dry_run=False,
)
+
def test_ping_monitor_config_host_and_key_with_api_key_auth_data_successful():
# This test should simulate a successful POST to a Zabbix server. This test uses API_KEY
# to authenticate and HOST/KEY to know which item to populate in Zabbix.
- hook_config = {
- 'server': SERVER,
- 'host': HOST,
- 'key': KEY,
- 'api_key': API_KEY
- }
+ hook_config = {'server': SERVER, 'host': HOST, 'key': KEY, 'api_key': API_KEY}
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
headers=AUTH_HEADERS_API_KEY,
@@ -225,6 +225,37 @@ def test_ping_monitor_config_host_and_key_with_api_key_auth_data_successful():
dry_run=False,
)
+
+def test_ping_monitor_config_host_and_missing_key_exits_early():
+ hook_config = {'server': SERVER, 'host': HOST, 'api_key': API_KEY}
+ 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_key_and_missing_host_exits_early():
+ hook_config = {'server': SERVER, 'key': KEY, 'api_key': API_KEY}
+ 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_host_and_key_with_username_password_auth_data_successful():
# This test should simulate a successful POST to a Zabbix server. This test uses USERNAME/PASSWORD
# to authenticate and HOST/KEY to know which item to populate in Zabbix.
@@ -233,11 +264,13 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_succe
'host': HOST,
'key': KEY,
'username': USERNAME,
- 'password': PASSWORD
+ 'password': PASSWORD,
}
auth_response = flexmock(ok=True)
- auth_response.should_receive('json').and_return({"jsonrpc":"2.0","result":"3fe6ed01a69ebd79907a120bcd04e494","id":1})
+ auth_response.should_receive('json').and_return(
+ {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
+ )
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
@@ -250,7 +283,7 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_succe
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
headers=AUTH_HEADERS_USERNAME_PASSWORD,
- json=DATA_HOST_KEY_WITH_TOKEN,
+ json=DATA_HOST_KEY_WITH_KEY_VALUE,
).and_return(flexmock(ok=True)).once()
module.ping_monitor(
@@ -262,14 +295,92 @@ def test_ping_monitor_config_host_and_key_with_username_password_auth_data_succe
dry_run=False,
)
+
+def test_ping_monitor_config_host_and_key_with_username_password_auth_data_and_auth_post_error_exits_early():
+ hook_config = {
+ 'server': SERVER,
+ 'host': HOST,
+ 'key': KEY,
+ 'username': USERNAME,
+ 'password': PASSWORD,
+ }
+
+ auth_response = flexmock(ok=False)
+ auth_response.should_receive('json').and_return(
+ {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
+ )
+ auth_response.should_receive('raise_for_status').and_raise(
+ module.requests.ConnectionError
+ ).once()
+
+ flexmock(module.requests).should_receive('post').with_args(
+ f'{SERVER}',
+ headers=AUTH_HEADERS_USERNAME_PASSWORD,
+ json=DATA_USER_LOGIN,
+ ).and_return(auth_response).once()
+ flexmock(module.logger).should_receive('warning').once()
+ flexmock(module.requests).should_receive('post').with_args(
+ f'{SERVER}',
+ headers=AUTH_HEADERS_USERNAME_PASSWORD,
+ json=DATA_HOST_KEY_WITH_KEY_VALUE,
+ ).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_host_and_key_with_username_and_missing_password_exits_early():
+ hook_config = {
+ 'server': SERVER,
+ 'host': HOST,
+ 'key': KEY,
+ 'username': USERNAME,
+ }
+
+ 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_host_and_key_with_passing_and_missing_username_exits_early():
+ hook_config = {
+ 'server': SERVER,
+ 'host': HOST,
+ 'key': KEY,
+ 'password': PASSWORD,
+ }
+
+ 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_itemid_with_api_key_auth_data_successful():
# This test should simulate a successful POST to a Zabbix server. This test uses API_KEY
# to authenticate and HOST/KEY to know which item to populate in Zabbix.
- hook_config = {
- 'server': SERVER,
- 'itemid': ITEMID,
- 'api_key': API_KEY
- }
+ hook_config = {'server': SERVER, 'itemid': ITEMID, 'api_key': API_KEY}
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
headers=AUTH_HEADERS_API_KEY,
@@ -286,18 +397,16 @@ def test_ping_monitor_config_itemid_with_api_key_auth_data_successful():
dry_run=False,
)
+
def test_ping_monitor_config_itemid_with_username_password_auth_data_successful():
# This test should simulate a successful POST to a Zabbix server. This test uses USERNAME/PASSWORD
# to authenticate and HOST/KEY to know which item to populate in Zabbix.
- hook_config = {
- 'server': SERVER,
- 'itemid': ITEMID,
- 'username': USERNAME,
- 'password': PASSWORD
- }
+ hook_config = {'server': SERVER, 'itemid': ITEMID, 'username': USERNAME, 'password': PASSWORD}
auth_response = flexmock(ok=True)
- auth_response.should_receive('json').and_return({"jsonrpc":"2.0","result":"3fe6ed01a69ebd79907a120bcd04e494","id":1})
+ auth_response.should_receive('json').and_return(
+ {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
+ )
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
@@ -310,7 +419,7 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_successful(
flexmock(module.requests).should_receive('post').with_args(
f'{SERVER}',
headers=AUTH_HEADERS_USERNAME_PASSWORD,
- json=DATA_HOST_KEY_WITH_TOKEN,
+ json=DATA_HOST_KEY_WITH_ITEMID,
).and_return(flexmock(ok=True)).once()
module.ping_monitor(
@@ -321,4 +430,39 @@ def test_ping_monitor_config_itemid_with_username_password_auth_data_successful(
monitoring_log_level=1,
dry_run=False,
)
-test_ping_monitor_config_itemid_with_username_password_auth_data_successful()
\ No newline at end of file
+
+
+def test_ping_monitor_config_itemid_with_username_password_auth_data_and_push_post_error_exits_early():
+ hook_config = {'server': SERVER, 'itemid': ITEMID, 'username': USERNAME, 'password': PASSWORD}
+
+ auth_response = flexmock(ok=True)
+ auth_response.should_receive('json').and_return(
+ {'jsonrpc': '2.0', 'result': '3fe6ed01a69ebd79907a120bcd04e494', 'id': 1}
+ )
+
+ flexmock(module.requests).should_receive('post').with_args(
+ f'{SERVER}',
+ headers=AUTH_HEADERS_USERNAME_PASSWORD,
+ json=DATA_USER_LOGIN,
+ ).and_return(auth_response).once()
+
+ push_response = flexmock(ok=False)
+ push_response.should_receive('raise_for_status').and_raise(
+ module.requests.ConnectionError
+ ).once()
+ flexmock(module.requests).should_receive('post').with_args(
+ f'{SERVER}',
+ headers=AUTH_HEADERS_USERNAME_PASSWORD,
+ json=DATA_HOST_KEY_WITH_ITEMID,
+ ).and_return(push_response).once()
+
+ flexmock(module.logger).should_receive('warning').once()
+
+ module.ping_monitor(
+ hook_config,
+ {},
+ 'config.yaml',
+ borgmatic.hooks.monitor.State.FAIL,
+ monitoring_log_level=1,
+ dry_run=False,
+ )