Skip to content

Commit 4b5f4dc

Browse files
committed
feat(plugins/modules): add uptimerobot_{monitor,mwindow,alert_contact,psp}_info read modules
Read-only counterparts to the four CRUD modules, providing parity with the `utr get …` subcommands and removing the last reason to keep the standalone `utr` CLI installed: - uptimerobot_monitor_info: lists monitors, optional `friendly_name` (exact) and `search` (server-side substring) filters. - uptimerobot_mwindow_info: lists maintenance windows. - uptimerobot_alert_contact_info: lists alert contacts (status / type translated to labels via the centralised module_utils helper). - uptimerobot_psp_info: lists public status pages. Each module is supports_check_mode=True, returns `changed=false`, and ships a `debug` dict (operation, count). Uses the same api_key resolution order as the CRUD modules (param > api_key_file > UPTIMEROBOT_API_KEY env var). Drive-by: aligns the headers of uptimerobot_account_info and uptimerobot_alert_contact with the rest of the LFOps custom modules (`Linuxfabrik GmbH,` Copyright string, single blank line before DOCUMENTATION) and expands their EXAMPLES blocks to cover realistic workflows -- account-quota assertion, stale-contact sweep -- using example.com domains.
1 parent 8e0c1b7 commit 4b5f4dc

6 files changed

Lines changed: 532 additions & 9 deletions

plugins/modules/uptimerobot_account_info.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#!/usr/bin/python
22
# -*- coding: utf-8 -*-
33

4-
# Copyright: (c) 2026, Linuxfabrik, Zurich, Switzerland, https://www.linuxfabrik.ch
4+
# Copyright: (c) 2026, Linuxfabrik GmbH, Zurich, Switzerland, https://www.linuxfabrik.ch
55
# The Unlicense (see LICENSE or https://unlicense.org/)
66

77
from __future__ import absolute_import, division, print_function
88

99
__metaclass__ = type
1010

11-
1211
DOCUMENTATION = r'''
1312
---
1413
module: uptimerobot_account_info
@@ -31,9 +30,30 @@
3130

3231

3332
EXAMPLES = r'''
33+
# 1) Read account quota and current usage (equivalent to `utr get account`).
34+
# The API key comes from ~/.uptimerobot when no parameter is given.
3435
- name: 'Capture UptimeRobot account info'
3536
linuxfabrik.lfops.uptimerobot_account_info:
36-
register: ur_account
37+
register: 'ur_account'
38+
39+
- ansible.builtin.debug:
40+
msg: >-
41+
{{ ur_account.account.up_monitors }}/{{ ur_account.account.monitor_limit }}
42+
monitors used; subscription expires
43+
{{ ur_account.account.subscription_expiry_date }}
44+
45+
# 2) Same call but with an explicit key file (any reachable path is fine).
46+
- linuxfabrik.lfops.uptimerobot_account_info:
47+
api_key_file: '/etc/ansible/secrets/uptimerobot.key'
48+
register: 'ur_account'
49+
50+
# 3) Fail the play early if the account is over 90% of its monitor quota.
51+
- linuxfabrik.lfops.uptimerobot_account_info:
52+
register: 'ur_account'
53+
54+
- ansible.builtin.assert:
55+
that: 'ur_account.account.up_monitors / ur_account.account.monitor_limit < 0.9'
56+
fail_msg: 'UptimeRobot quota is nearly exhausted; bump the plan or delete stale monitors.'
3757
'''
3858

3959

plugins/modules/uptimerobot_alert_contact.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#!/usr/bin/python
22
# -*- coding: utf-8 -*-
33

4-
# Copyright: (c) 2026, Linuxfabrik, Zurich, Switzerland, https://www.linuxfabrik.ch
4+
# Copyright: (c) 2026, Linuxfabrik GmbH, Zurich, Switzerland, https://www.linuxfabrik.ch
55
# The Unlicense (see LICENSE or https://unlicense.org/)
66

77
from __future__ import absolute_import, division, print_function
88

99
__metaclass__ = type
1010

11-
1211
DOCUMENTATION = r'''
1312
---
1413
module: uptimerobot_alert_contact
@@ -48,15 +47,33 @@
4847

4948

5049
EXAMPLES = r'''
51-
- name: 'Delete a stale alert contact by id'
50+
# UptimeRobot's v2 API does not allow CREATING or EDITING alert contacts via
51+
# the API — they must be added in the web UI (which sends an opt-in mail).
52+
# This module is therefore delete-only, useful for sweeping contacts that no
53+
# longer correspond to an active recipient.
54+
55+
# 1) Delete by friendly_name (recommended — survives ID re-numbering).
56+
- name: 'Sweep a stale alert contact by friendly_name'
5257
linuxfabrik.lfops.uptimerobot_alert_contact:
58+
friendly_name: 'old-pager@example.com'
59+
state: 'absent'
60+
61+
# 2) Delete by ID (when you already have it from uptimerobot_alert_contact_info).
62+
- linuxfabrik.lfops.uptimerobot_alert_contact:
5363
id: 7068316
5464
state: 'absent'
5565
56-
- name: 'Delete a stale alert contact by friendly_name'
57-
linuxfabrik.lfops.uptimerobot_alert_contact:
58-
friendly_name: 'old-pager@example.com'
66+
# 3) Drive a sweep from inventory: list everything via the info module, filter
67+
# for the ones you want gone, then delete them.
68+
- linuxfabrik.lfops.uptimerobot_alert_contact_info:
69+
register: 'ur_contacts'
70+
71+
- linuxfabrik.lfops.uptimerobot_alert_contact:
72+
friendly_name: '{{ item.friendly_name }}'
5973
state: 'absent'
74+
loop: '{{ ur_contacts.alert_contacts | selectattr("status", "equalto", "not activated") | list }}'
75+
loop_control:
76+
label: '{{ item.friendly_name }}'
6077
'''
6178

6279

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2026, Linuxfabrik GmbH, Zurich, Switzerland, https://www.linuxfabrik.ch
5+
# The Unlicense (see LICENSE or https://unlicense.org/)
6+
7+
from __future__ import absolute_import, division, print_function
8+
9+
__metaclass__ = type
10+
11+
DOCUMENTATION = r'''
12+
---
13+
module: uptimerobot_alert_contact_info
14+
short_description: List UptimeRobot alert contacts
15+
version_added: '6.1.0'
16+
description:
17+
- Returns the full list of alert contacts on the UptimeRobot account,
18+
with enum-style fields translated to human-readable labels (C(status),
19+
C(type)).
20+
- Equivalent of C(utr get alert_contacts).
21+
- Read-only. Reports C(changed=false).
22+
author:
23+
- Linuxfabrik GmbH, Zurich, Switzerland (info (at) linuxfabrik (dot) ch)
24+
options:
25+
api_key:
26+
description: UptimeRobot API key. See C(uptimerobot_monitor) for the resolution order.
27+
type: str
28+
no_log: true
29+
api_key_file:
30+
description: Path to a file containing the API key. Default C(~/.uptimerobot).
31+
type: str
32+
friendly_name:
33+
description:
34+
- If set, only the alert contact with this exact friendly name is
35+
returned (or none, if no match).
36+
type: str
37+
'''
38+
39+
40+
EXAMPLES = r'''
41+
# 1) Equivalent to `utr get alert_contacts`.
42+
- name: 'Capture all alert contacts'
43+
linuxfabrik.lfops.uptimerobot_alert_contact_info:
44+
register: 'ur_alert_contacts'
45+
46+
- ansible.builtin.debug:
47+
msg: '{{ ur_alert_contacts.alert_contacts | length }} alert contacts on the account'
48+
49+
# 2) Look up a single contact by friendly_name.
50+
- linuxfabrik.lfops.uptimerobot_alert_contact_info:
51+
friendly_name: 'monitoring@example.com'
52+
register: 'ur_contact'
53+
54+
- ansible.builtin.debug:
55+
msg: 'Contact id={{ ur_contact.alert_contacts[0].id }} status={{ ur_contact.alert_contacts[0].status }}'
56+
57+
# 3) Audit: list all contacts that never accepted the opt-in invitation
58+
# (`status == "not activated"`) — typically safe to delete.
59+
- linuxfabrik.lfops.uptimerobot_alert_contact_info:
60+
register: 'ur_all'
61+
62+
- ansible.builtin.debug:
63+
msg: >-
64+
Stale contacts: {{
65+
ur_all.alert_contacts
66+
| selectattr("status", "equalto", "not activated")
67+
| map(attribute="friendly_name")
68+
| list
69+
}}
70+
'''
71+
72+
73+
RETURN = r'''
74+
alert_contacts:
75+
description: List of alert contact dicts (empty list if none matched).
76+
type: list
77+
returned: always
78+
elements: dict
79+
'''
80+
81+
82+
from ansible.module_utils.basic import AnsibleModule
83+
from ansible_collections.linuxfabrik.lfops.plugins.module_utils import uptimerobot as ur
84+
85+
86+
def main():
87+
argument_spec = dict(
88+
api_key=dict(type='str', no_log=True),
89+
api_key_file=dict(type='str'),
90+
friendly_name=dict(type='str'),
91+
)
92+
93+
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
94+
95+
api_key = ur.resolve_api_key(module, module.params.get('api_key'), module.params.get('api_key_file'))
96+
friendly_name = module.params.get('friendly_name')
97+
98+
module.log('uptimerobot_alert_contact_info: fetching alert contacts')
99+
success, contacts = ur.get_alert_contacts(module, api_key)
100+
if not success:
101+
module.fail_json(msg='Could not list alert contacts: {0}'.format(contacts))
102+
103+
if friendly_name:
104+
match = ur.find_by_friendly_name(contacts, friendly_name)
105+
contacts = [match] if match else []
106+
107+
module.exit_json(changed=False, alert_contacts=contacts, debug={
108+
'operation': 'list',
109+
'count': len(contacts),
110+
})
111+
112+
113+
if __name__ == '__main__':
114+
main()
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2026, Linuxfabrik GmbH, Zurich, Switzerland, https://www.linuxfabrik.ch
5+
# The Unlicense (see LICENSE or https://unlicense.org/)
6+
7+
from __future__ import absolute_import, division, print_function
8+
9+
__metaclass__ = type
10+
11+
DOCUMENTATION = r'''
12+
---
13+
module: uptimerobot_monitor_info
14+
short_description: List UptimeRobot monitors
15+
version_added: '6.1.0'
16+
description:
17+
- Returns the full list of monitors on the UptimeRobot account, with
18+
enum-style fields translated to human-readable labels (C(http_method),
19+
C(keyword_type), C(status), C(type), nested C(alert_contacts[].type)).
20+
- Equivalent of C(utr get monitors).
21+
- Read-only. Reports C(changed=false).
22+
author:
23+
- Linuxfabrik GmbH, Zurich, Switzerland (info (at) linuxfabrik (dot) ch)
24+
options:
25+
api_key:
26+
description: UptimeRobot API key. See C(uptimerobot_monitor) for the resolution order.
27+
type: str
28+
no_log: true
29+
api_key_file:
30+
description: Path to a file containing the API key. Default C(~/.uptimerobot).
31+
type: str
32+
friendly_name:
33+
description:
34+
- If set, only the monitor with this exact friendly name is
35+
returned (or none, if no match). Otherwise all monitors are
36+
returned.
37+
type: str
38+
search:
39+
description:
40+
- Optional case-insensitive substring filter forwarded to the API.
41+
type: str
42+
'''
43+
44+
45+
EXAMPLES = r'''
46+
# 1) Quick ad-hoc list, equivalent to `utr get monitors`. The API key is read
47+
# from ~/.uptimerobot when not passed.
48+
- name: 'Capture all monitors'
49+
linuxfabrik.lfops.uptimerobot_monitor_info:
50+
register: 'ur_monitors'
51+
52+
- ansible.builtin.debug:
53+
msg: '{{ ur_monitors.monitors | length }} monitors total'
54+
55+
# 2) Filter by friendly_name (exact match). Returns at most one entry.
56+
- name: 'Capture one monitor by friendly_name'
57+
linuxfabrik.lfops.uptimerobot_monitor_info:
58+
friendly_name: '001 www.example.com'
59+
register: 'ur_monitor'
60+
61+
- ansible.builtin.debug:
62+
msg: 'Status: {{ ur_monitor.monitors[0].status }} ({{ ur_monitor.monitors[0].interval }}s interval)'
63+
64+
# 3) Server-side substring filter — useful for prefixed inventories.
65+
- linuxfabrik.lfops.uptimerobot_monitor_info:
66+
search: '001 '
67+
register: 'ur_001'
68+
69+
- ansible.builtin.debug:
70+
msg: '{{ ur_001.monitors | map(attribute="friendly_name") | list }}'
71+
72+
# 4) Drive a maintenance task: pause every monitor matching a prefix while a
73+
# deployment is in progress (see uptimerobot_monitor for the pause action).
74+
- linuxfabrik.lfops.uptimerobot_monitor_info:
75+
search: '001 '
76+
register: 'ur_to_pause'
77+
78+
- linuxfabrik.lfops.uptimerobot_monitor:
79+
friendly_name: '{{ item.friendly_name }}'
80+
status: 'paused'
81+
state: 'present'
82+
loop: '{{ ur_to_pause.monitors }}'
83+
loop_control:
84+
label: '{{ item.friendly_name }}'
85+
86+
# 5) Reporting — list monitors that are currently down or seem down.
87+
- linuxfabrik.lfops.uptimerobot_monitor_info:
88+
register: 'ur_all'
89+
90+
- ansible.builtin.debug:
91+
msg: >-
92+
Down: {{
93+
ur_all.monitors
94+
| selectattr("status", "in", ["down", "seems_down"])
95+
| map(attribute="friendly_name")
96+
| list
97+
}}
98+
'''
99+
100+
101+
RETURN = r'''
102+
monitors:
103+
description: List of monitor dicts (empty list if none matched).
104+
type: list
105+
returned: always
106+
elements: dict
107+
'''
108+
109+
110+
from ansible.module_utils.basic import AnsibleModule
111+
from ansible_collections.linuxfabrik.lfops.plugins.module_utils import uptimerobot as ur
112+
113+
114+
def main():
115+
argument_spec = dict(
116+
api_key=dict(type='str', no_log=True),
117+
api_key_file=dict(type='str'),
118+
friendly_name=dict(type='str'),
119+
search=dict(type='str'),
120+
)
121+
122+
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
123+
124+
api_key = ur.resolve_api_key(module, module.params.get('api_key'), module.params.get('api_key_file'))
125+
search = module.params.get('search') or None
126+
friendly_name = module.params.get('friendly_name')
127+
128+
module.log('uptimerobot_monitor_info: fetching monitors search={0!r}'.format(search))
129+
success, monitors = ur.get_monitors(module, api_key, search=search)
130+
if not success:
131+
module.fail_json(msg='Could not list monitors: {0}'.format(monitors))
132+
133+
if friendly_name:
134+
match = ur.find_by_friendly_name(monitors, friendly_name)
135+
monitors = [match] if match else []
136+
137+
module.exit_json(changed=False, monitors=monitors, debug={
138+
'operation': 'list',
139+
'count': len(monitors),
140+
})
141+
142+
143+
if __name__ == '__main__':
144+
main()

0 commit comments

Comments
 (0)