mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-30 07:28:52 +00:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
358
plugins/modules/monitoring/sensu/sensu_check.py
Normal file
358
plugins/modules/monitoring/sensu/sensu_check.py
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Anders Ingemann <aim@secoya.dk>
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: sensu_check
|
||||
short_description: Manage Sensu checks
|
||||
description:
|
||||
- Manage the checks that should be run on a machine by I(Sensu).
|
||||
- Most options do not have a default and will not be added to the check definition unless specified.
|
||||
- All defaults except I(path), I(state), I(backup) and I(metric) are not managed by this module,
|
||||
- they are simply specified for your convenience.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the check
|
||||
- This is the key that is used to determine whether a check exists
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Whether the check should be present or not
|
||||
choices: [ 'present', 'absent' ]
|
||||
default: present
|
||||
path:
|
||||
description:
|
||||
- Path to the json file of the check to be added/removed.
|
||||
- Will be created if it does not exist (unless I(state=absent)).
|
||||
- The parent folders need to exist when I(state=present), otherwise an error will be thrown
|
||||
default: /etc/sensu/conf.d/checks.json
|
||||
backup:
|
||||
description:
|
||||
- Create a backup file (if yes), including the timestamp information so
|
||||
- you can get the original file back if you somehow clobbered it incorrectly.
|
||||
type: bool
|
||||
default: 'no'
|
||||
command:
|
||||
description:
|
||||
- Path to the sensu check to run (not required when I(state=absent))
|
||||
required: true
|
||||
handlers:
|
||||
description:
|
||||
- List of handlers to notify when the check fails
|
||||
default: []
|
||||
subscribers:
|
||||
description:
|
||||
- List of subscribers/channels this check should run for
|
||||
- See sensu_subscribers to subscribe a machine to a channel
|
||||
default: []
|
||||
interval:
|
||||
description:
|
||||
- Check interval in seconds
|
||||
timeout:
|
||||
description:
|
||||
- Timeout for the check
|
||||
default: 10
|
||||
ttl:
|
||||
description:
|
||||
- Time to live in seconds until the check is considered stale
|
||||
handle:
|
||||
description:
|
||||
- Whether the check should be handled or not
|
||||
type: bool
|
||||
default: 'yes'
|
||||
subdue_begin:
|
||||
description:
|
||||
- When to disable handling of check failures
|
||||
subdue_end:
|
||||
description:
|
||||
- When to enable handling of check failures
|
||||
dependencies:
|
||||
description:
|
||||
- Other checks this check depends on, if dependencies fail,
|
||||
- handling of this check will be disabled
|
||||
default: []
|
||||
metric:
|
||||
description:
|
||||
- Whether the check is a metric
|
||||
type: bool
|
||||
default: 'no'
|
||||
standalone:
|
||||
description:
|
||||
- Whether the check should be scheduled by the sensu client or server
|
||||
- This option obviates the need for specifying the I(subscribers) option
|
||||
type: bool
|
||||
default: 'no'
|
||||
publish:
|
||||
description:
|
||||
- Whether the check should be scheduled at all.
|
||||
- You can still issue it via the sensu api
|
||||
type: bool
|
||||
default: 'yes'
|
||||
occurrences:
|
||||
description:
|
||||
- Number of event occurrences before the handler should take action
|
||||
default: 1
|
||||
refresh:
|
||||
description:
|
||||
- Number of seconds handlers should wait before taking second action
|
||||
aggregate:
|
||||
description:
|
||||
- Classifies the check as an aggregate check,
|
||||
- making it available via the aggregate API
|
||||
type: bool
|
||||
default: 'no'
|
||||
low_flap_threshold:
|
||||
description:
|
||||
- The low threshold for flap detection
|
||||
high_flap_threshold:
|
||||
description:
|
||||
- The high threshold for flap detection
|
||||
custom:
|
||||
description:
|
||||
- A hash/dictionary of custom parameters for mixing to the configuration.
|
||||
- You can't rewrite others module parameters using this
|
||||
default: {}
|
||||
source:
|
||||
description:
|
||||
- The check source, used to create a JIT Sensu client for an external resource (e.g. a network switch).
|
||||
author: "Anders Ingemann (@andsens)"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Fetch metrics about the CPU load every 60 seconds,
|
||||
# the sensu server has a handler called 'relay' which forwards stats to graphite
|
||||
- name: get cpu metrics
|
||||
sensu_check:
|
||||
name: cpu_load
|
||||
command: /etc/sensu/plugins/system/cpu-mpstat-metrics.rb
|
||||
metric: yes
|
||||
handlers: relay
|
||||
subscribers: common
|
||||
interval: 60
|
||||
|
||||
# Check whether nginx is running
|
||||
- name: check nginx process
|
||||
sensu_check:
|
||||
name: nginx_running
|
||||
command: /etc/sensu/plugins/processes/check-procs.rb -f /var/run/nginx.pid
|
||||
handlers: default
|
||||
subscribers: nginx
|
||||
interval: 60
|
||||
|
||||
# Stop monitoring the disk capacity.
|
||||
# Note that the check will still show up in the sensu dashboard,
|
||||
# to remove it completely you need to issue a DELETE request to the sensu api.
|
||||
- name: check disk
|
||||
sensu_check:
|
||||
name: check_disk_capacity
|
||||
state: absent
|
||||
'''
|
||||
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
def sensu_check(module, path, name, state='present', backup=False):
|
||||
changed = False
|
||||
reasons = []
|
||||
|
||||
stream = None
|
||||
try:
|
||||
try:
|
||||
stream = open(path, 'r')
|
||||
config = json.load(stream)
|
||||
except IOError as e:
|
||||
if e.errno == 2: # File not found, non-fatal
|
||||
if state == 'absent':
|
||||
reasons.append('file did not exist and state is `absent\'')
|
||||
return changed, reasons
|
||||
config = {}
|
||||
else:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
except ValueError:
|
||||
msg = '{path} contains invalid JSON'.format(path=path)
|
||||
module.fail_json(msg=msg)
|
||||
finally:
|
||||
if stream:
|
||||
stream.close()
|
||||
|
||||
if 'checks' not in config:
|
||||
if state == 'absent':
|
||||
reasons.append('`checks\' section did not exist and state is `absent\'')
|
||||
return changed, reasons
|
||||
config['checks'] = {}
|
||||
changed = True
|
||||
reasons.append('`checks\' section did not exist')
|
||||
|
||||
if state == 'absent':
|
||||
if name in config['checks']:
|
||||
del config['checks'][name]
|
||||
changed = True
|
||||
reasons.append('check was present and state is `absent\'')
|
||||
|
||||
if state == 'present':
|
||||
if name not in config['checks']:
|
||||
check = {}
|
||||
config['checks'][name] = check
|
||||
changed = True
|
||||
reasons.append('check was absent and state is `present\'')
|
||||
else:
|
||||
check = config['checks'][name]
|
||||
simple_opts = ['command',
|
||||
'handlers',
|
||||
'subscribers',
|
||||
'interval',
|
||||
'timeout',
|
||||
'ttl',
|
||||
'handle',
|
||||
'dependencies',
|
||||
'standalone',
|
||||
'publish',
|
||||
'occurrences',
|
||||
'refresh',
|
||||
'aggregate',
|
||||
'low_flap_threshold',
|
||||
'high_flap_threshold',
|
||||
'source',
|
||||
]
|
||||
for opt in simple_opts:
|
||||
if module.params[opt] is not None:
|
||||
if opt not in check or check[opt] != module.params[opt]:
|
||||
check[opt] = module.params[opt]
|
||||
changed = True
|
||||
reasons.append('`{opt}\' did not exist or was different'.format(opt=opt))
|
||||
else:
|
||||
if opt in check:
|
||||
del check[opt]
|
||||
changed = True
|
||||
reasons.append('`{opt}\' was removed'.format(opt=opt))
|
||||
|
||||
if module.params['custom']:
|
||||
# Convert to json
|
||||
custom_params = module.params['custom']
|
||||
overwrited_fields = set(custom_params.keys()) & set(simple_opts + ['type', 'subdue', 'subdue_begin', 'subdue_end'])
|
||||
if overwrited_fields:
|
||||
msg = 'You can\'t overwriting standard module parameters via "custom". You are trying overwrite: {opt}'.format(opt=list(overwrited_fields))
|
||||
module.fail_json(msg=msg)
|
||||
|
||||
for k, v in custom_params.items():
|
||||
if k in config['checks'][name]:
|
||||
if not config['checks'][name][k] == v:
|
||||
changed = True
|
||||
reasons.append('`custom param {opt}\' was changed'.format(opt=k))
|
||||
else:
|
||||
changed = True
|
||||
reasons.append('`custom param {opt}\' was added'.format(opt=k))
|
||||
check[k] = v
|
||||
simple_opts += custom_params.keys()
|
||||
|
||||
# Remove obsolete custom params
|
||||
for opt in set(config['checks'][name].keys()) - set(simple_opts + ['type', 'subdue', 'subdue_begin', 'subdue_end']):
|
||||
changed = True
|
||||
reasons.append('`custom param {opt}\' was deleted'.format(opt=opt))
|
||||
del check[opt]
|
||||
|
||||
if module.params['metric']:
|
||||
if 'type' not in check or check['type'] != 'metric':
|
||||
check['type'] = 'metric'
|
||||
changed = True
|
||||
reasons.append('`type\' was not defined or not `metric\'')
|
||||
if not module.params['metric'] and 'type' in check:
|
||||
del check['type']
|
||||
changed = True
|
||||
reasons.append('`type\' was defined')
|
||||
|
||||
if module.params['subdue_begin'] is not None and module.params['subdue_end'] is not None:
|
||||
subdue = {'begin': module.params['subdue_begin'],
|
||||
'end': module.params['subdue_end'],
|
||||
}
|
||||
if 'subdue' not in check or check['subdue'] != subdue:
|
||||
check['subdue'] = subdue
|
||||
changed = True
|
||||
reasons.append('`subdue\' did not exist or was different')
|
||||
else:
|
||||
if 'subdue' in check:
|
||||
del check['subdue']
|
||||
changed = True
|
||||
reasons.append('`subdue\' was removed')
|
||||
|
||||
if changed and not module.check_mode:
|
||||
if backup:
|
||||
module.backup_local(path)
|
||||
try:
|
||||
try:
|
||||
stream = open(path, 'w')
|
||||
stream.write(json.dumps(config, indent=2) + '\n')
|
||||
except IOError as e:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
finally:
|
||||
if stream:
|
||||
stream.close()
|
||||
|
||||
return changed, reasons
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
arg_spec = {'name': {'type': 'str', 'required': True},
|
||||
'path': {'type': 'str', 'default': '/etc/sensu/conf.d/checks.json'},
|
||||
'state': {'type': 'str', 'default': 'present', 'choices': ['present', 'absent']},
|
||||
'backup': {'type': 'bool', 'default': 'no'},
|
||||
'command': {'type': 'str'},
|
||||
'handlers': {'type': 'list'},
|
||||
'subscribers': {'type': 'list'},
|
||||
'interval': {'type': 'int'},
|
||||
'timeout': {'type': 'int'},
|
||||
'ttl': {'type': 'int'},
|
||||
'handle': {'type': 'bool'},
|
||||
'subdue_begin': {'type': 'str'},
|
||||
'subdue_end': {'type': 'str'},
|
||||
'dependencies': {'type': 'list'},
|
||||
'metric': {'type': 'bool', 'default': 'no'},
|
||||
'standalone': {'type': 'bool'},
|
||||
'publish': {'type': 'bool'},
|
||||
'occurrences': {'type': 'int'},
|
||||
'refresh': {'type': 'int'},
|
||||
'aggregate': {'type': 'bool'},
|
||||
'low_flap_threshold': {'type': 'int'},
|
||||
'high_flap_threshold': {'type': 'int'},
|
||||
'custom': {'type': 'dict'},
|
||||
'source': {'type': 'str'},
|
||||
}
|
||||
|
||||
required_together = [['subdue_begin', 'subdue_end']]
|
||||
|
||||
module = AnsibleModule(argument_spec=arg_spec,
|
||||
required_together=required_together,
|
||||
supports_check_mode=True)
|
||||
if module.params['state'] != 'absent' and module.params['command'] is None:
|
||||
module.fail_json(msg="missing required arguments: %s" % ",".join(['command']))
|
||||
|
||||
path = module.params['path']
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
backup = module.params['backup']
|
||||
|
||||
changed, reasons = sensu_check(module, path, name, state, backup)
|
||||
|
||||
module.exit_json(path=path, changed=changed, msg='OK', name=name, reasons=reasons)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
250
plugins/modules/monitoring/sensu/sensu_client.py
Normal file
250
plugins/modules/monitoring/sensu/sensu_client.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# (c) 2017, Red Hat Inc.
|
||||
# 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
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: sensu_client
|
||||
author: "David Moreau Simard (@dmsimard)"
|
||||
short_description: Manages Sensu client configuration
|
||||
description:
|
||||
- Manages Sensu client configuration.
|
||||
- 'For more information, refer to the Sensu documentation: U(https://sensuapp.org/docs/latest/reference/clients.html)'
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether the client should be present or not
|
||||
choices: [ 'present', 'absent' ]
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- A unique name for the client. The name cannot contain special characters or spaces.
|
||||
default: System hostname as determined by Ruby Socket.gethostname (provided by Sensu)
|
||||
address:
|
||||
description:
|
||||
- An address to help identify and reach the client. This is only informational, usually an IP address or hostname.
|
||||
default: Non-loopback IPv4 address as determined by Ruby Socket.ip_address_list (provided by Sensu)
|
||||
subscriptions:
|
||||
description:
|
||||
- An array of client subscriptions, a list of roles and/or responsibilities assigned to the system (e.g. webserver).
|
||||
- These subscriptions determine which monitoring checks are executed by the client, as check requests are sent to subscriptions.
|
||||
- The subscriptions array items must be strings.
|
||||
required: True
|
||||
safe_mode:
|
||||
description:
|
||||
- If safe mode is enabled for the client. Safe mode requires local check definitions in order to accept a check request and execute the check.
|
||||
type: bool
|
||||
default: 'no'
|
||||
redact:
|
||||
description:
|
||||
- Client definition attributes to redact (values) when logging and sending client keepalives.
|
||||
socket:
|
||||
description:
|
||||
- The socket definition scope, used to configure the Sensu client socket.
|
||||
keepalives:
|
||||
description:
|
||||
- If Sensu should monitor keepalives for this client.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
keepalive:
|
||||
description:
|
||||
- The keepalive definition scope, used to configure Sensu client keepalives behavior (e.g. keepalive thresholds, etc).
|
||||
registration:
|
||||
description:
|
||||
- The registration definition scope, used to configure Sensu registration event handlers.
|
||||
deregister:
|
||||
description:
|
||||
- If a deregistration event should be created upon Sensu client process stop.
|
||||
type: bool
|
||||
default: 'no'
|
||||
deregistration:
|
||||
description:
|
||||
- The deregistration definition scope, used to configure automated Sensu client de-registration.
|
||||
ec2:
|
||||
description:
|
||||
- The ec2 definition scope, used to configure the Sensu Enterprise AWS EC2 integration (Sensu Enterprise users only).
|
||||
chef:
|
||||
description:
|
||||
- The chef definition scope, used to configure the Sensu Enterprise Chef integration (Sensu Enterprise users only).
|
||||
puppet:
|
||||
description:
|
||||
- The puppet definition scope, used to configure the Sensu Enterprise Puppet integration (Sensu Enterprise users only).
|
||||
servicenow:
|
||||
description:
|
||||
- The servicenow definition scope, used to configure the Sensu Enterprise ServiceNow integration (Sensu Enterprise users only).
|
||||
notes:
|
||||
- Check mode is supported
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Minimum possible configuration
|
||||
- name: Configure Sensu client
|
||||
sensu_client:
|
||||
subscriptions:
|
||||
- default
|
||||
|
||||
# With customization
|
||||
- name: Configure Sensu client
|
||||
sensu_client:
|
||||
name: "{{ ansible_fqdn }}"
|
||||
address: "{{ ansible_default_ipv4['address'] }}"
|
||||
subscriptions:
|
||||
- default
|
||||
- webserver
|
||||
redact:
|
||||
- password
|
||||
socket:
|
||||
bind: 127.0.0.1
|
||||
port: 3030
|
||||
keepalive:
|
||||
thresholds:
|
||||
warning: 180
|
||||
critical: 300
|
||||
handlers:
|
||||
- email
|
||||
custom:
|
||||
- broadcast: irc
|
||||
occurrences: 3
|
||||
register: client
|
||||
notify:
|
||||
- Restart sensu-client
|
||||
|
||||
- name: Secure Sensu client configuration file
|
||||
file:
|
||||
path: "{{ client['file'] }}"
|
||||
owner: "sensu"
|
||||
group: "sensu"
|
||||
mode: "0600"
|
||||
|
||||
- name: Delete the Sensu client configuration
|
||||
sensu_client:
|
||||
state: "absent"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
config:
|
||||
description: Effective client configuration, when state is present
|
||||
returned: success
|
||||
type: dict
|
||||
sample: {'name': 'client', 'subscriptions': ['default']}
|
||||
file:
|
||||
description: Path to the client configuration file
|
||||
returned: success
|
||||
type: str
|
||||
sample: "/etc/sensu/conf.d/client.json"
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
supports_check_mode=True,
|
||||
argument_spec=dict(
|
||||
state=dict(type='str', required=False, choices=['present', 'absent'], default='present'),
|
||||
name=dict(type='str', required=False),
|
||||
address=dict(type='str', required=False),
|
||||
subscriptions=dict(type='list', required=False),
|
||||
safe_mode=dict(type='bool', required=False, default=False),
|
||||
redact=dict(type='list', required=False),
|
||||
socket=dict(type='dict', required=False),
|
||||
keepalives=dict(type='bool', required=False, default=True),
|
||||
keepalive=dict(type='dict', required=False),
|
||||
registration=dict(type='dict', required=False),
|
||||
deregister=dict(type='bool', required=False),
|
||||
deregistration=dict(type='dict', required=False),
|
||||
ec2=dict(type='dict', required=False),
|
||||
chef=dict(type='dict', required=False),
|
||||
puppet=dict(type='dict', required=False),
|
||||
servicenow=dict(type='dict', required=False)
|
||||
),
|
||||
required_if=[
|
||||
['state', 'present', ['subscriptions']]
|
||||
]
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
path = "/etc/sensu/conf.d/client.json"
|
||||
|
||||
if state == 'absent':
|
||||
if os.path.exists(path):
|
||||
if module.check_mode:
|
||||
msg = '{path} would have been deleted'.format(path=path)
|
||||
module.exit_json(msg=msg, changed=True)
|
||||
else:
|
||||
try:
|
||||
os.remove(path)
|
||||
msg = '{path} deleted successfully'.format(path=path)
|
||||
module.exit_json(msg=msg, changed=True)
|
||||
except OSError as e:
|
||||
msg = 'Exception when trying to delete {path}: {exception}'
|
||||
module.fail_json(
|
||||
msg=msg.format(path=path, exception=str(e)))
|
||||
else:
|
||||
# Idempotency: it's okay if the file doesn't exist
|
||||
msg = '{path} already does not exist'.format(path=path)
|
||||
module.exit_json(msg=msg)
|
||||
|
||||
# Build client configuration from module arguments
|
||||
config = {'client': {}}
|
||||
args = ['name', 'address', 'subscriptions', 'safe_mode', 'redact',
|
||||
'socket', 'keepalives', 'keepalive', 'registration', 'deregister',
|
||||
'deregistration', 'ec2', 'chef', 'puppet', 'servicenow']
|
||||
|
||||
for arg in args:
|
||||
if arg in module.params and module.params[arg] is not None:
|
||||
config['client'][arg] = module.params[arg]
|
||||
|
||||
# Load the current config, if there is one, so we can compare
|
||||
current_config = None
|
||||
try:
|
||||
current_config = json.load(open(path, 'r'))
|
||||
except (IOError, ValueError):
|
||||
# File either doesn't exist or it's invalid JSON
|
||||
pass
|
||||
|
||||
if current_config is not None and current_config == config:
|
||||
# Config is the same, let's not change anything
|
||||
module.exit_json(msg='Client configuration is already up to date',
|
||||
config=config['client'],
|
||||
file=path)
|
||||
|
||||
# Validate that directory exists before trying to write to it
|
||||
if not module.check_mode and not os.path.exists(os.path.dirname(path)):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(path))
|
||||
except OSError as e:
|
||||
module.fail_json(msg='Unable to create {0}: {1}'.format(os.path.dirname(path),
|
||||
str(e)))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(msg='Client configuration would have been updated',
|
||||
changed=True,
|
||||
config=config['client'],
|
||||
file=path)
|
||||
|
||||
try:
|
||||
with open(path, 'w') as client:
|
||||
client.write(json.dumps(config, indent=4))
|
||||
module.exit_json(msg='Client configuration updated',
|
||||
changed=True,
|
||||
config=config['client'],
|
||||
file=path)
|
||||
except (OSError, IOError) as e:
|
||||
module.fail_json(msg='Unable to write file {0}: {1}'.format(path,
|
||||
str(e)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
263
plugins/modules/monitoring/sensu/sensu_handler.py
Normal file
263
plugins/modules/monitoring/sensu/sensu_handler.py
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# (c) 2017, Red Hat Inc.
|
||||
# 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
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: sensu_handler
|
||||
author: "David Moreau Simard (@dmsimard)"
|
||||
short_description: Manages Sensu handler configuration
|
||||
description:
|
||||
- Manages Sensu handler configuration
|
||||
- 'For more information, refer to the Sensu documentation: U(https://sensuapp.org/docs/latest/reference/handlers.html)'
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether the handler should be present or not
|
||||
choices: [ 'present', 'absent' ]
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- A unique name for the handler. The name cannot contain special characters or spaces.
|
||||
required: True
|
||||
type:
|
||||
description:
|
||||
- The handler type
|
||||
choices: [ 'pipe', 'tcp', 'udp', 'transport', 'set' ]
|
||||
required: True
|
||||
filter:
|
||||
description:
|
||||
- The Sensu event filter (name) to use when filtering events for the handler.
|
||||
filters:
|
||||
description:
|
||||
- An array of Sensu event filters (names) to use when filtering events for the handler.
|
||||
- Each array item must be a string.
|
||||
severities:
|
||||
description:
|
||||
- An array of check result severities the handler will handle.
|
||||
- 'NOTE: event resolution bypasses this filtering.'
|
||||
choices: [ 'warning', 'critical', 'unknown' ]
|
||||
mutator:
|
||||
description:
|
||||
- The Sensu event mutator (name) to use to mutate event data for the handler.
|
||||
timeout:
|
||||
description:
|
||||
- The handler execution duration timeout in seconds (hard stop).
|
||||
- Only used by pipe and tcp handler types.
|
||||
default: 10
|
||||
handle_silenced:
|
||||
description:
|
||||
- If events matching one or more silence entries should be handled.
|
||||
type: bool
|
||||
default: 'no'
|
||||
handle_flapping:
|
||||
description:
|
||||
- If events in the flapping state should be handled.
|
||||
type: bool
|
||||
default: 'no'
|
||||
command:
|
||||
description:
|
||||
- The handler command to be executed.
|
||||
- The event data is passed to the process via STDIN.
|
||||
- 'NOTE: the command attribute is only required for Pipe handlers (i.e. handlers configured with "type": "pipe").'
|
||||
socket:
|
||||
description:
|
||||
- The socket definition scope, used to configure the TCP/UDP handler socket.
|
||||
- 'NOTE: the socket attribute is only required for TCP/UDP handlers (i.e. handlers configured with "type": "tcp" or "type": "udp").'
|
||||
pipe:
|
||||
description:
|
||||
- The pipe definition scope, used to configure the Sensu transport pipe.
|
||||
- 'NOTE: the pipe attribute is only required for Transport handlers (i.e. handlers configured with "type": "transport").'
|
||||
handlers:
|
||||
description:
|
||||
- An array of Sensu event handlers (names) to use for events using the handler set.
|
||||
- Each array item must be a string.
|
||||
- 'NOTE: the handlers attribute is only required for handler sets (i.e. handlers configured with "type": "set").'
|
||||
notes:
|
||||
- Check mode is supported
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Configure a handler that sends event data as STDIN (pipe)
|
||||
- name: Configure IRC Sensu handler
|
||||
sensu_handler:
|
||||
name: "irc_handler"
|
||||
type: "pipe"
|
||||
command: "/usr/local/bin/notify-irc.sh"
|
||||
severities:
|
||||
- "ok"
|
||||
- "critical"
|
||||
- "warning"
|
||||
- "unknown"
|
||||
timeout: 15
|
||||
notify:
|
||||
- Restart sensu-client
|
||||
- Restart sensu-server
|
||||
|
||||
# Delete a handler
|
||||
- name: Delete IRC Sensu handler
|
||||
sensu_handler:
|
||||
name: "irc_handler"
|
||||
state: "absent"
|
||||
|
||||
# Example of a TCP handler
|
||||
- name: Configure TCP Sensu handler
|
||||
sensu_handler:
|
||||
name: "tcp_handler"
|
||||
type: "tcp"
|
||||
timeout: 30
|
||||
socket:
|
||||
host: "10.0.1.99"
|
||||
port: 4444
|
||||
register: handler
|
||||
notify:
|
||||
- Restart sensu-client
|
||||
- Restart sensu-server
|
||||
|
||||
- name: Secure Sensu handler configuration file
|
||||
file:
|
||||
path: "{{ handler['file'] }}"
|
||||
owner: "sensu"
|
||||
group: "sensu"
|
||||
mode: "0600"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
config:
|
||||
description: Effective handler configuration, when state is present
|
||||
returned: success
|
||||
type: dict
|
||||
sample: {'name': 'irc', 'type': 'pipe', 'command': '/usr/local/bin/notify-irc.sh'}
|
||||
file:
|
||||
description: Path to the handler configuration file
|
||||
returned: success
|
||||
type: str
|
||||
sample: "/etc/sensu/conf.d/handlers/irc.json"
|
||||
name:
|
||||
description: Name of the handler
|
||||
returned: success
|
||||
type: str
|
||||
sample: "irc"
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
supports_check_mode=True,
|
||||
argument_spec=dict(
|
||||
state=dict(type='str', required=False, choices=['present', 'absent'], default='present'),
|
||||
name=dict(type='str', required=True),
|
||||
type=dict(type='str', required=False, choices=['pipe', 'tcp', 'udp', 'transport', 'set']),
|
||||
filter=dict(type='str', required=False),
|
||||
filters=dict(type='list', required=False),
|
||||
severities=dict(type='list', required=False),
|
||||
mutator=dict(type='str', required=False),
|
||||
timeout=dict(type='int', required=False, default=10),
|
||||
handle_silenced=dict(type='bool', required=False, default=False),
|
||||
handle_flapping=dict(type='bool', required=False, default=False),
|
||||
command=dict(type='str', required=False),
|
||||
socket=dict(type='dict', required=False),
|
||||
pipe=dict(type='dict', required=False),
|
||||
handlers=dict(type='list', required=False),
|
||||
),
|
||||
required_if=[
|
||||
['state', 'present', ['type']],
|
||||
['type', 'pipe', ['command']],
|
||||
['type', 'tcp', ['socket']],
|
||||
['type', 'udp', ['socket']],
|
||||
['type', 'transport', ['pipe']],
|
||||
['type', 'set', ['handlers']]
|
||||
]
|
||||
)
|
||||
|
||||
state = module.params['state']
|
||||
name = module.params['name']
|
||||
path = '/etc/sensu/conf.d/handlers/{0}.json'.format(name)
|
||||
|
||||
if state == 'absent':
|
||||
if os.path.exists(path):
|
||||
if module.check_mode:
|
||||
msg = '{path} would have been deleted'.format(path=path)
|
||||
module.exit_json(msg=msg, changed=True)
|
||||
else:
|
||||
try:
|
||||
os.remove(path)
|
||||
msg = '{path} deleted successfully'.format(path=path)
|
||||
module.exit_json(msg=msg, changed=True)
|
||||
except OSError as e:
|
||||
msg = 'Exception when trying to delete {path}: {exception}'
|
||||
module.fail_json(
|
||||
msg=msg.format(path=path, exception=str(e)))
|
||||
else:
|
||||
# Idempotency: it's okay if the file doesn't exist
|
||||
msg = '{path} already does not exist'.format(path=path)
|
||||
module.exit_json(msg=msg)
|
||||
|
||||
# Build handler configuration from module arguments
|
||||
config = {'handlers': {name: {}}}
|
||||
args = ['type', 'filter', 'filters', 'severities', 'mutator', 'timeout',
|
||||
'handle_silenced', 'handle_flapping', 'command', 'socket',
|
||||
'pipe', 'handlers']
|
||||
|
||||
for arg in args:
|
||||
if arg in module.params and module.params[arg] is not None:
|
||||
config['handlers'][name][arg] = module.params[arg]
|
||||
|
||||
# Load the current config, if there is one, so we can compare
|
||||
current_config = None
|
||||
try:
|
||||
current_config = json.load(open(path, 'r'))
|
||||
except (IOError, ValueError):
|
||||
# File either doesn't exist or it's invalid JSON
|
||||
pass
|
||||
|
||||
if current_config is not None and current_config == config:
|
||||
# Config is the same, let's not change anything
|
||||
module.exit_json(msg='Handler configuration is already up to date',
|
||||
config=config['handlers'][name],
|
||||
file=path,
|
||||
name=name)
|
||||
|
||||
# Validate that directory exists before trying to write to it
|
||||
if not module.check_mode and not os.path.exists(os.path.dirname(path)):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(path))
|
||||
except OSError as e:
|
||||
module.fail_json(msg='Unable to create {0}: {1}'.format(os.path.dirname(path),
|
||||
str(e)))
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(msg='Handler configuration would have been updated',
|
||||
changed=True,
|
||||
config=config['handlers'][name],
|
||||
file=path,
|
||||
name=name)
|
||||
|
||||
try:
|
||||
with open(path, 'w') as handler:
|
||||
handler.write(json.dumps(config, indent=4))
|
||||
module.exit_json(msg='Handler configuration updated',
|
||||
changed=True,
|
||||
config=config['handlers'][name],
|
||||
file=path,
|
||||
name=name)
|
||||
except (OSError, IOError) as e:
|
||||
module.fail_json(msg='Unable to write file {0}: {1}'.format(path,
|
||||
str(e)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
295
plugins/modules/monitoring/sensu/sensu_silence.py
Normal file
295
plugins/modules/monitoring/sensu/sensu_silence.py
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
#!/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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
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:
|
||||
description:
|
||||
- Specifies the check which the silence entry applies to.
|
||||
creator:
|
||||
description:
|
||||
- Specifies the entity responsible for this entry.
|
||||
expire:
|
||||
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:
|
||||
description:
|
||||
- If specified, this free-form string is used to provide context or
|
||||
rationale for the reason this silence entry was created.
|
||||
state:
|
||||
description:
|
||||
- Specifies to create or clear (delete) a silence entry via the Sensu API
|
||||
required: true
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
subscription:
|
||||
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
|
||||
default: []
|
||||
url:
|
||||
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
|
||||
sensu_silence:
|
||||
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
|
||||
sensu_silence:
|
||||
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
|
||||
sensu_silence:
|
||||
subscription: "client:{{ item.key }}"
|
||||
reason: "{{ item.value.reason }}"
|
||||
creator: "{{ ansible_user_id }}"
|
||||
with_dict: "{{ silence }}"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
import json
|
||||
|
||||
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:
|
||||
json_out = json.loads(response.read())
|
||||
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:
|
||||
json_out = json.loads(response.read())
|
||||
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
|
||||
creator == i['creator'])and
|
||||
(
|
||||
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:
|
||||
json_out = json.loads(response.read())
|
||||
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()
|
||||
154
plugins/modules/monitoring/sensu/sensu_subscription.py
Normal file
154
plugins/modules/monitoring/sensu/sensu_subscription.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2014, Anders Ingemann <aim@secoya.dk>
|
||||
# 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
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: sensu_subscription
|
||||
short_description: Manage Sensu subscriptions
|
||||
description:
|
||||
- Manage which I(sensu channels) a machine should subscribe to
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the channel
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Whether the machine should subscribe or unsubscribe from the channel
|
||||
choices: [ 'present', 'absent' ]
|
||||
required: false
|
||||
default: present
|
||||
path:
|
||||
description:
|
||||
- Path to the subscriptions json file
|
||||
required: false
|
||||
default: /etc/sensu/conf.d/subscriptions.json
|
||||
backup:
|
||||
description:
|
||||
- Create a backup file (if yes), including the timestamp information so you
|
||||
- can get the original file back if you somehow clobbered it incorrectly.
|
||||
type: bool
|
||||
required: false
|
||||
default: no
|
||||
requirements: [ ]
|
||||
author: Anders Ingemann (@andsens)
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
reasons:
|
||||
description: the reasons why the module changed or did not change something
|
||||
returned: success
|
||||
type: list
|
||||
sample: ["channel subscription was absent and state is `present'"]
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Subscribe to the nginx channel
|
||||
- name: subscribe to nginx checks
|
||||
sensu_subscription: name=nginx
|
||||
|
||||
# Unsubscribe from the common checks channel
|
||||
- name: unsubscribe from common checks
|
||||
sensu_subscription: name=common state=absent
|
||||
'''
|
||||
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
def sensu_subscription(module, path, name, state='present', backup=False):
|
||||
changed = False
|
||||
reasons = []
|
||||
|
||||
try:
|
||||
config = json.load(open(path))
|
||||
except IOError as e:
|
||||
if e.errno == 2: # File not found, non-fatal
|
||||
if state == 'absent':
|
||||
reasons.append('file did not exist and state is `absent\'')
|
||||
return changed, reasons
|
||||
config = {}
|
||||
else:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
except ValueError:
|
||||
msg = '{path} contains invalid JSON'.format(path=path)
|
||||
module.fail_json(msg=msg)
|
||||
|
||||
if 'client' not in config:
|
||||
if state == 'absent':
|
||||
reasons.append('`client\' did not exist and state is `absent\'')
|
||||
return changed, reasons
|
||||
config['client'] = {}
|
||||
changed = True
|
||||
reasons.append('`client\' did not exist')
|
||||
|
||||
if 'subscriptions' not in config['client']:
|
||||
if state == 'absent':
|
||||
reasons.append('`client.subscriptions\' did not exist and state is `absent\'')
|
||||
return changed, reasons
|
||||
config['client']['subscriptions'] = []
|
||||
changed = True
|
||||
reasons.append('`client.subscriptions\' did not exist')
|
||||
|
||||
if name not in config['client']['subscriptions']:
|
||||
if state == 'absent':
|
||||
reasons.append('channel subscription was absent')
|
||||
return changed, reasons
|
||||
config['client']['subscriptions'].append(name)
|
||||
changed = True
|
||||
reasons.append('channel subscription was absent and state is `present\'')
|
||||
else:
|
||||
if state == 'absent':
|
||||
config['client']['subscriptions'].remove(name)
|
||||
changed = True
|
||||
reasons.append('channel subscription was present and state is `absent\'')
|
||||
|
||||
if changed and not module.check_mode:
|
||||
if backup:
|
||||
module.backup_local(path)
|
||||
try:
|
||||
open(path, 'w').write(json.dumps(config, indent=2) + '\n')
|
||||
except IOError as e:
|
||||
module.fail_json(msg='Failed to write to file %s: %s' % (path, to_native(e)),
|
||||
exception=traceback.format_exc())
|
||||
|
||||
return changed, reasons
|
||||
|
||||
|
||||
def main():
|
||||
arg_spec = {'name': {'type': 'str', 'required': True},
|
||||
'path': {'type': 'str', 'default': '/etc/sensu/conf.d/subscriptions.json'},
|
||||
'state': {'type': 'str', 'default': 'present', 'choices': ['present', 'absent']},
|
||||
'backup': {'type': 'bool', 'default': 'no'},
|
||||
}
|
||||
|
||||
module = AnsibleModule(argument_spec=arg_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
path = module.params['path']
|
||||
name = module.params['name']
|
||||
state = module.params['state']
|
||||
backup = module.params['backup']
|
||||
|
||||
changed, reasons = sensu_subscription(module, path, name, state, backup)
|
||||
|
||||
module.exit_json(path=path, name=name, changed=changed, msg='OK', reasons=reasons)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue