2020-03-09 10:11:07 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# (c) 2017, Steven Bambling <smbambling@gmail.com>
|
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: sensu_silence
|
|
|
|
author: Steven Bambling (@smbambling)
|
|
|
|
short_description: Manage Sensu silence entries
|
|
|
|
description:
|
|
|
|
- Create and clear (delete) a silence entries via the Sensu API
|
|
|
|
for subscriptions and checks.
|
|
|
|
options:
|
|
|
|
check:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Specifies the check which the silence entry applies to.
|
|
|
|
creator:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Specifies the entity responsible for this entry.
|
|
|
|
expire:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: int
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- If specified, the silence entry will be automatically cleared
|
|
|
|
after this number of seconds.
|
|
|
|
expire_on_resolve:
|
|
|
|
description:
|
|
|
|
- If specified as true, the silence entry will be automatically
|
|
|
|
cleared once the condition it is silencing is resolved.
|
|
|
|
type: bool
|
|
|
|
reason:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- If specified, this free-form string is used to provide context or
|
|
|
|
rationale for the reason this silence entry was created.
|
|
|
|
state:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Specifies to create or clear (delete) a silence entry via the Sensu API
|
|
|
|
default: present
|
|
|
|
choices: ['present', 'absent']
|
|
|
|
subscription:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Specifies the subscription which the silence entry applies to.
|
|
|
|
- To create a silence entry for a client prepend C(client:) to client name.
|
|
|
|
Example - C(client:server1.example.dev)
|
|
|
|
required: true
|
|
|
|
url:
|
2020-11-23 22:07:03 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Specifies the URL of the Sensu monitoring host server.
|
|
|
|
required: false
|
|
|
|
default: http://127.0.01:4567
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
# Silence ALL checks for a given client
|
|
|
|
- name: Silence server1.example.dev
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.sensu_silence:
|
2020-03-09 10:11:07 +01:00
|
|
|
subscription: client:server1.example.dev
|
|
|
|
creator: "{{ ansible_user_id }}"
|
|
|
|
reason: Performing maintenance
|
|
|
|
|
|
|
|
# Silence specific check for a client
|
|
|
|
- name: Silence CPU_Usage check for server1.example.dev
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.sensu_silence:
|
2020-03-09 10:11:07 +01:00
|
|
|
subscription: client:server1.example.dev
|
|
|
|
check: CPU_Usage
|
|
|
|
creator: "{{ ansible_user_id }}"
|
|
|
|
reason: Investigation alert issue
|
|
|
|
|
|
|
|
# Silence multiple clients from a dict
|
|
|
|
silence:
|
|
|
|
server1.example.dev:
|
|
|
|
reason: 'Deployment in progress'
|
|
|
|
server2.example.dev:
|
|
|
|
reason: 'Deployment in progress'
|
|
|
|
|
|
|
|
- name: Silence several clients from a dict
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.sensu_silence:
|
2020-03-09 10:11:07 +01:00
|
|
|
subscription: "client:{{ item.key }}"
|
|
|
|
reason: "{{ item.value.reason }}"
|
|
|
|
creator: "{{ ansible_user_id }}"
|
|
|
|
with_dict: "{{ silence }}"
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
'''
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
2021-02-03 07:52:59 +01:00
|
|
|
from ansible.module_utils._text import to_native
|
2020-03-09 10:11:07 +01:00
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.urls import fetch_url
|
|
|
|
|
|
|
|
|
|
|
|
def query(module, url, check, subscription):
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
}
|
|
|
|
|
|
|
|
url = url + '/silenced'
|
|
|
|
|
|
|
|
request_data = {
|
|
|
|
'check': check,
|
|
|
|
'subscription': subscription,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Remove keys with None value
|
|
|
|
for k, v in dict(request_data).items():
|
|
|
|
if v is None:
|
|
|
|
del request_data[k]
|
|
|
|
|
|
|
|
response, info = fetch_url(
|
|
|
|
module, url, method='GET',
|
|
|
|
headers=headers, data=json.dumps(request_data)
|
|
|
|
)
|
|
|
|
|
|
|
|
if info['status'] == 500:
|
|
|
|
module.fail_json(
|
|
|
|
msg="Failed to query silence %s. Reason: %s" % (subscription, info)
|
|
|
|
)
|
|
|
|
|
|
|
|
try:
|
2021-02-03 07:52:59 +01:00
|
|
|
json_out = json.loads(to_native(response.read()))
|
2020-03-09 10:11:07 +01:00
|
|
|
except Exception:
|
|
|
|
json_out = ""
|
|
|
|
|
|
|
|
return False, json_out, False
|
|
|
|
|
|
|
|
|
|
|
|
def clear(module, url, check, subscription):
|
|
|
|
# Test if silence exists before clearing
|
|
|
|
(rc, out, changed) = query(module, url, check, subscription)
|
|
|
|
|
|
|
|
d = dict((i['subscription'], i['check']) for i in out)
|
|
|
|
subscription_exists = subscription in d
|
|
|
|
if check and subscription_exists:
|
|
|
|
exists = (check == d[subscription])
|
|
|
|
else:
|
|
|
|
exists = subscription_exists
|
|
|
|
|
|
|
|
# If check/subscription doesn't exist
|
|
|
|
# exit with changed state of False
|
|
|
|
if not exists:
|
|
|
|
return False, out, changed
|
|
|
|
|
|
|
|
# module.check_mode is inherited from the AnsibleMOdule class
|
|
|
|
if not module.check_mode:
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
}
|
|
|
|
|
|
|
|
url = url + '/silenced/clear'
|
|
|
|
|
|
|
|
request_data = {
|
|
|
|
'check': check,
|
|
|
|
'subscription': subscription,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Remove keys with None value
|
|
|
|
for k, v in dict(request_data).items():
|
|
|
|
if v is None:
|
|
|
|
del request_data[k]
|
|
|
|
|
|
|
|
response, info = fetch_url(
|
|
|
|
module, url, method='POST',
|
|
|
|
headers=headers, data=json.dumps(request_data)
|
|
|
|
)
|
|
|
|
|
|
|
|
if info['status'] != 204:
|
|
|
|
module.fail_json(
|
|
|
|
msg="Failed to silence %s. Reason: %s" % (subscription, info)
|
|
|
|
)
|
|
|
|
|
|
|
|
try:
|
2021-02-03 07:52:59 +01:00
|
|
|
json_out = json.loads(to_native(response.read()))
|
2020-03-09 10:11:07 +01:00
|
|
|
except Exception:
|
|
|
|
json_out = ""
|
|
|
|
|
|
|
|
return False, json_out, True
|
|
|
|
return False, out, True
|
|
|
|
|
|
|
|
|
|
|
|
def create(
|
|
|
|
module, url, check, creator, expire,
|
|
|
|
expire_on_resolve, reason, subscription):
|
|
|
|
(rc, out, changed) = query(module, url, check, subscription)
|
|
|
|
for i in out:
|
|
|
|
if (i['subscription'] == subscription):
|
|
|
|
if (
|
|
|
|
(check is None or check == i['check']) and
|
|
|
|
(
|
|
|
|
creator == '' or
|
2020-05-15 13:35:41 +02:00
|
|
|
creator == i['creator']) and
|
2020-03-09 10:11:07 +01:00
|
|
|
(
|
|
|
|
reason == '' or
|
|
|
|
reason == i['reason']) and
|
|
|
|
(
|
|
|
|
expire is None or expire == i['expire']) and
|
|
|
|
(
|
|
|
|
expire_on_resolve is None or
|
|
|
|
expire_on_resolve == i['expire_on_resolve']
|
|
|
|
)
|
|
|
|
):
|
|
|
|
return False, out, False
|
|
|
|
|
|
|
|
# module.check_mode is inherited from the AnsibleMOdule class
|
|
|
|
if not module.check_mode:
|
|
|
|
headers = {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
}
|
|
|
|
|
|
|
|
url = url + '/silenced'
|
|
|
|
|
|
|
|
request_data = {
|
|
|
|
'check': check,
|
|
|
|
'creator': creator,
|
|
|
|
'expire': expire,
|
|
|
|
'expire_on_resolve': expire_on_resolve,
|
|
|
|
'reason': reason,
|
|
|
|
'subscription': subscription,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Remove keys with None value
|
|
|
|
for k, v in dict(request_data).items():
|
|
|
|
if v is None:
|
|
|
|
del request_data[k]
|
|
|
|
|
|
|
|
response, info = fetch_url(
|
|
|
|
module, url, method='POST',
|
|
|
|
headers=headers, data=json.dumps(request_data)
|
|
|
|
)
|
|
|
|
|
|
|
|
if info['status'] != 201:
|
|
|
|
module.fail_json(
|
|
|
|
msg="Failed to silence %s. Reason: %s" %
|
|
|
|
(subscription, info['msg'])
|
|
|
|
)
|
|
|
|
|
|
|
|
try:
|
2021-02-03 07:52:59 +01:00
|
|
|
json_out = json.loads(to_native(response.read()))
|
2020-03-09 10:11:07 +01:00
|
|
|
except Exception:
|
|
|
|
json_out = ""
|
|
|
|
|
|
|
|
return False, json_out, True
|
|
|
|
return False, out, True
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=dict(
|
|
|
|
check=dict(required=False),
|
|
|
|
creator=dict(required=False),
|
|
|
|
expire=dict(type='int', required=False),
|
|
|
|
expire_on_resolve=dict(type='bool', required=False),
|
|
|
|
reason=dict(required=False),
|
|
|
|
state=dict(default='present', choices=['present', 'absent']),
|
|
|
|
subscription=dict(required=True),
|
|
|
|
url=dict(required=False, default='http://127.0.01:4567'),
|
|
|
|
),
|
|
|
|
supports_check_mode=True
|
|
|
|
)
|
|
|
|
|
|
|
|
url = module.params['url']
|
|
|
|
check = module.params['check']
|
|
|
|
creator = module.params['creator']
|
|
|
|
expire = module.params['expire']
|
|
|
|
expire_on_resolve = module.params['expire_on_resolve']
|
|
|
|
reason = module.params['reason']
|
|
|
|
subscription = module.params['subscription']
|
|
|
|
state = module.params['state']
|
|
|
|
|
|
|
|
if state == 'present':
|
|
|
|
(rc, out, changed) = create(
|
|
|
|
module, url, check, creator,
|
|
|
|
expire, expire_on_resolve, reason, subscription
|
|
|
|
)
|
|
|
|
|
|
|
|
if state == 'absent':
|
|
|
|
(rc, out, changed) = clear(module, url, check, subscription)
|
|
|
|
|
|
|
|
if rc != 0:
|
|
|
|
module.fail_json(msg="failed", result=out)
|
|
|
|
module.exit_json(msg="success", result=out, changed=changed)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|