2020-03-09 10:11:07 +01:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2017 Red Hat Inc.
|
2022-08-05 12:28:29 +02:00
|
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
|
|
|
|
module: manageiq_alerts
|
|
|
|
|
|
|
|
short_description: Configuration of alerts in ManageIQ
|
|
|
|
extends_documentation_fragment:
|
2023-02-24 09:23:28 +01:00
|
|
|
- community.general.manageiq
|
|
|
|
- community.general.attributes
|
2020-03-09 10:11:07 +01:00
|
|
|
|
2022-01-25 07:50:11 +01:00
|
|
|
author: Elad Alfassa (@elad661) <ealfassa@redhat.com>
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- The manageiq_alerts module supports adding, updating and deleting alerts in ManageIQ.
|
|
|
|
|
2023-02-24 09:23:28 +01:00
|
|
|
attributes:
|
|
|
|
check_mode:
|
|
|
|
support: none
|
|
|
|
diff_mode:
|
|
|
|
support: none
|
|
|
|
|
2020-03-09 10:11:07 +01:00
|
|
|
options:
|
|
|
|
state:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- absent - alert should not exist,
|
|
|
|
- present - alert should exist,
|
2022-09-06 20:42:17 +02:00
|
|
|
required: false
|
2020-03-09 10:11:07 +01:00
|
|
|
choices: ['absent', 'present']
|
|
|
|
default: 'present'
|
|
|
|
description:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- The unique alert description in ManageIQ.
|
|
|
|
- Required when state is "absent" or "present".
|
|
|
|
resource_type:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- The entity type for the alert in ManageIQ. Required when state is "present".
|
|
|
|
choices: ['Vm', 'ContainerNode', 'MiqServer', 'Host', 'Storage', 'EmsCluster',
|
|
|
|
'ExtManagementSystem', 'MiddlewareServer']
|
|
|
|
expression_type:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Expression type.
|
|
|
|
default: hash
|
|
|
|
choices: ["hash", "miq"]
|
|
|
|
expression:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: dict
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- The alert expression for ManageIQ.
|
|
|
|
- Can either be in the "Miq Expression" format or the "Hash Expression format".
|
|
|
|
- Required if state is "present".
|
|
|
|
enabled:
|
|
|
|
description:
|
|
|
|
- Enable or disable the alert. Required if state is "present".
|
|
|
|
type: bool
|
|
|
|
options:
|
2020-11-12 08:28:32 +01:00
|
|
|
type: dict
|
2020-03-09 10:11:07 +01:00
|
|
|
description:
|
|
|
|
- Additional alert options, such as notification type and frequency
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
- name: Add an alert with a "hash expression" to ManageIQ
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.manageiq_alerts:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: present
|
|
|
|
description: Test Alert 01
|
|
|
|
options:
|
|
|
|
notifications:
|
|
|
|
email:
|
|
|
|
to: ["example@example.com"]
|
|
|
|
from: "example@example.com"
|
|
|
|
resource_type: ContainerNode
|
|
|
|
expression:
|
|
|
|
eval_method: hostd_log_threshold
|
|
|
|
mode: internal
|
|
|
|
options: {}
|
|
|
|
enabled: true
|
|
|
|
manageiq_connection:
|
|
|
|
url: 'http://127.0.0.1:3000'
|
|
|
|
username: 'admin'
|
|
|
|
password: 'smartvm'
|
2022-09-06 20:42:17 +02:00
|
|
|
validate_certs: false
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
- name: Add an alert with a "miq expression" to ManageIQ
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.manageiq_alerts:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: present
|
|
|
|
description: Test Alert 02
|
|
|
|
options:
|
|
|
|
notifications:
|
|
|
|
email:
|
|
|
|
to: ["example@example.com"]
|
|
|
|
from: "example@example.com"
|
|
|
|
resource_type: Vm
|
|
|
|
expression_type: miq
|
|
|
|
expression:
|
|
|
|
and:
|
|
|
|
- CONTAINS:
|
|
|
|
tag: Vm.managed-environment
|
|
|
|
value: prod
|
|
|
|
- not:
|
|
|
|
CONTAINS:
|
|
|
|
tag: Vm.host.managed-environment
|
|
|
|
value: prod
|
|
|
|
enabled: true
|
|
|
|
manageiq_connection:
|
|
|
|
url: 'http://127.0.0.1:3000'
|
|
|
|
username: 'admin'
|
|
|
|
password: 'smartvm'
|
2022-09-06 20:42:17 +02:00
|
|
|
validate_certs: false
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
- name: Delete an alert from ManageIQ
|
2020-07-13 21:50:31 +02:00
|
|
|
community.general.manageiq_alerts:
|
2020-03-09 10:11:07 +01:00
|
|
|
state: absent
|
|
|
|
description: Test Alert 01
|
|
|
|
manageiq_connection:
|
|
|
|
url: 'http://127.0.0.1:3000'
|
|
|
|
username: 'admin'
|
|
|
|
password: 'smartvm'
|
2022-09-06 20:42:17 +02:00
|
|
|
validate_certs: false
|
2020-03-09 10:11:07 +01:00
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
'''
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
|
|
|
|
|
|
|
|
|
|
|
class ManageIQAlert(object):
|
|
|
|
""" Represent a ManageIQ alert. Can be initialized with both the format
|
|
|
|
we receive from the server and the format we get from the user.
|
|
|
|
"""
|
|
|
|
def __init__(self, alert):
|
|
|
|
self.description = alert['description']
|
|
|
|
self.db = alert['db']
|
|
|
|
self.enabled = alert['enabled']
|
|
|
|
self.options = alert['options']
|
|
|
|
self.hash_expression = None
|
|
|
|
self.miq_expressipn = None
|
|
|
|
|
|
|
|
if 'hash_expression' in alert:
|
|
|
|
self.hash_expression = alert['hash_expression']
|
|
|
|
if 'miq_expression' in alert:
|
|
|
|
self.miq_expression = alert['miq_expression']
|
|
|
|
if 'exp' in self.miq_expression:
|
|
|
|
# miq_expression is a field that needs a special case, because
|
|
|
|
# it's returned surrounded by a dict named exp even though we don't
|
|
|
|
# send it with that dict.
|
|
|
|
self.miq_expression = self.miq_expression['exp']
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
""" Compare two ManageIQAlert objects
|
|
|
|
"""
|
|
|
|
return self.__dict__ == other.__dict__
|
|
|
|
|
|
|
|
|
|
|
|
class ManageIQAlerts(object):
|
|
|
|
""" Object to execute alert management operations in manageiq.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, manageiq):
|
|
|
|
self.manageiq = manageiq
|
|
|
|
|
|
|
|
self.module = self.manageiq.module
|
|
|
|
self.api_url = self.manageiq.api_url
|
|
|
|
self.client = self.manageiq.client
|
|
|
|
self.alerts_url = '{api_url}/alert_definitions'.format(api_url=self.api_url)
|
|
|
|
|
|
|
|
def get_alerts(self):
|
|
|
|
""" Get all alerts from ManageIQ
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
response = self.client.get(self.alerts_url + '?expand=resources')
|
|
|
|
except Exception as e:
|
|
|
|
self.module.fail_json(msg="Failed to query alerts: {error}".format(error=e))
|
|
|
|
return response.get('resources', [])
|
|
|
|
|
|
|
|
def validate_hash_expression(self, expression):
|
|
|
|
""" Validate a 'hash expression' alert definition
|
|
|
|
"""
|
|
|
|
# hash expressions must have the following fields
|
|
|
|
for key in ['options', 'eval_method', 'mode']:
|
|
|
|
if key not in expression:
|
|
|
|
msg = "Hash expression is missing required field {key}".format(key=key)
|
|
|
|
self.module.fail_json(msg)
|
|
|
|
|
|
|
|
def create_alert_dict(self, params):
|
|
|
|
""" Create a dict representing an alert
|
|
|
|
"""
|
|
|
|
if params['expression_type'] == 'hash':
|
|
|
|
# hash expression supports depends on https://github.com/ManageIQ/manageiq-api/pull/76
|
|
|
|
self.validate_hash_expression(params['expression'])
|
|
|
|
expression_type = 'hash_expression'
|
|
|
|
else:
|
|
|
|
# actually miq_expression, but we call it "expression" for backwards-compatibility
|
|
|
|
expression_type = 'expression'
|
|
|
|
|
|
|
|
# build the alret
|
|
|
|
alert = dict(description=params['description'],
|
|
|
|
db=params['resource_type'],
|
|
|
|
options=params['options'],
|
|
|
|
enabled=params['enabled'])
|
|
|
|
|
|
|
|
# add the actual expression.
|
|
|
|
alert.update({expression_type: params['expression']})
|
|
|
|
|
|
|
|
return alert
|
|
|
|
|
|
|
|
def add_alert(self, alert):
|
|
|
|
""" Add a new alert to ManageIQ
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
result = self.client.post(self.alerts_url, action='create', resource=alert)
|
|
|
|
|
|
|
|
msg = "Alert {description} created successfully: {details}"
|
|
|
|
msg = msg.format(description=alert['description'], details=result)
|
|
|
|
return dict(changed=True, msg=msg)
|
|
|
|
except Exception as e:
|
|
|
|
msg = "Creating alert {description} failed: {error}"
|
|
|
|
if "Resource expression needs be specified" in str(e):
|
|
|
|
# Running on an older version of ManageIQ and trying to create a hash expression
|
|
|
|
msg = msg.format(description=alert['description'],
|
|
|
|
error="Your version of ManageIQ does not support hash_expression")
|
|
|
|
else:
|
|
|
|
msg = msg.format(description=alert['description'], error=e)
|
|
|
|
self.module.fail_json(msg=msg)
|
|
|
|
|
|
|
|
def delete_alert(self, alert):
|
|
|
|
""" Delete an alert
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
result = self.client.post('{url}/{id}'.format(url=self.alerts_url,
|
|
|
|
id=alert['id']),
|
|
|
|
action="delete")
|
|
|
|
msg = "Alert {description} deleted: {details}"
|
|
|
|
msg = msg.format(description=alert['description'], details=result)
|
|
|
|
return dict(changed=True, msg=msg)
|
|
|
|
except Exception as e:
|
|
|
|
msg = "Deleting alert {description} failed: {error}"
|
|
|
|
msg = msg.format(description=alert['description'], error=e)
|
|
|
|
self.module.fail_json(msg=msg)
|
|
|
|
|
|
|
|
def update_alert(self, existing_alert, new_alert):
|
|
|
|
""" Update an existing alert with the values from `new_alert`
|
|
|
|
"""
|
|
|
|
new_alert_obj = ManageIQAlert(new_alert)
|
|
|
|
if new_alert_obj == ManageIQAlert(existing_alert):
|
|
|
|
# no change needed - alerts are identical
|
|
|
|
return dict(changed=False, msg="No update needed")
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
url = '{url}/{id}'.format(url=self.alerts_url, id=existing_alert['id'])
|
|
|
|
result = self.client.post(url, action="edit", resource=new_alert)
|
|
|
|
|
|
|
|
# make sure that the update was indeed successful by comparing
|
|
|
|
# the result to the expected result.
|
|
|
|
if new_alert_obj == ManageIQAlert(result):
|
|
|
|
# success!
|
|
|
|
msg = "Alert {description} updated successfully: {details}"
|
|
|
|
msg = msg.format(description=existing_alert['description'], details=result)
|
|
|
|
|
|
|
|
return dict(changed=True, msg=msg)
|
|
|
|
else:
|
|
|
|
# unexpected result
|
|
|
|
msg = "Updating alert {description} failed, unexpected result {details}"
|
|
|
|
msg = msg.format(description=existing_alert['description'], details=result)
|
|
|
|
|
|
|
|
self.module.fail_json(msg=msg)
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
msg = "Updating alert {description} failed: {error}"
|
|
|
|
if "Resource expression needs be specified" in str(e):
|
|
|
|
# Running on an older version of ManageIQ and trying to update a hash expression
|
|
|
|
msg = msg.format(description=existing_alert['description'],
|
|
|
|
error="Your version of ManageIQ does not support hash_expression")
|
|
|
|
else:
|
|
|
|
msg = msg.format(description=existing_alert['description'], error=e)
|
|
|
|
self.module.fail_json(msg=msg)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
argument_spec = dict(
|
|
|
|
description=dict(type='str'),
|
|
|
|
resource_type=dict(type='str', choices=['Vm',
|
|
|
|
'ContainerNode',
|
|
|
|
'MiqServer',
|
|
|
|
'Host',
|
|
|
|
'Storage',
|
|
|
|
'EmsCluster',
|
|
|
|
'ExtManagementSystem',
|
|
|
|
'MiddlewareServer']),
|
|
|
|
expression_type=dict(type='str', default='hash', choices=['miq', 'hash']),
|
|
|
|
expression=dict(type='dict'),
|
|
|
|
options=dict(type='dict'),
|
|
|
|
enabled=dict(type='bool'),
|
|
|
|
state=dict(required=False, default='present',
|
|
|
|
choices=['present', 'absent']),
|
|
|
|
)
|
|
|
|
# add the manageiq connection arguments to the arguments
|
|
|
|
argument_spec.update(manageiq_argument_spec())
|
|
|
|
|
|
|
|
module = AnsibleModule(argument_spec=argument_spec,
|
|
|
|
required_if=[('state', 'present', ['description',
|
|
|
|
'resource_type',
|
|
|
|
'expression',
|
|
|
|
'enabled',
|
|
|
|
'options']),
|
|
|
|
('state', 'absent', ['description'])])
|
|
|
|
|
|
|
|
state = module.params['state']
|
|
|
|
description = module.params['description']
|
|
|
|
|
|
|
|
manageiq = ManageIQ(module)
|
|
|
|
manageiq_alerts = ManageIQAlerts(manageiq)
|
|
|
|
|
|
|
|
existing_alert = manageiq.find_collection_resource_by("alert_definitions",
|
|
|
|
description=description)
|
|
|
|
|
|
|
|
# we need to add or update the alert
|
|
|
|
if state == "present":
|
|
|
|
alert = manageiq_alerts.create_alert_dict(module.params)
|
|
|
|
|
|
|
|
if not existing_alert:
|
|
|
|
# an alert with this description doesn't exist yet, let's create it
|
|
|
|
res_args = manageiq_alerts.add_alert(alert)
|
|
|
|
else:
|
|
|
|
# an alert with this description exists, we might need to update it
|
|
|
|
res_args = manageiq_alerts.update_alert(existing_alert, alert)
|
|
|
|
|
|
|
|
# this alert should not exist
|
|
|
|
elif state == "absent":
|
|
|
|
# if we have an alert with this description, delete it
|
|
|
|
if existing_alert:
|
|
|
|
res_args = manageiq_alerts.delete_alert(existing_alert)
|
|
|
|
else:
|
|
|
|
# it doesn't exist, and that's okay
|
|
|
|
msg = "Alert '{description}' does not exist in ManageIQ"
|
|
|
|
msg = msg.format(description=description)
|
|
|
|
res_args = dict(changed=False, msg=msg)
|
|
|
|
|
|
|
|
module.exit_json(**res_args)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|