mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-08 06:12:51 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (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: manageiq_alert_profiles
|
||||
|
||||
short_description: Configuration of alert profiles for ManageIQ
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Elad Alfassa (@elad661) <ealfassa@redhat.com>
|
||||
description:
|
||||
- The manageiq_alert_profiles module supports adding, updating and deleting alert profiles in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - alert profile should not exist,
|
||||
- present - alert profile should exist,
|
||||
choices: ['absent', 'present']
|
||||
default: 'present'
|
||||
name:
|
||||
description:
|
||||
- The unique alert profile name in ManageIQ.
|
||||
- Required when state is "absent" or "present".
|
||||
resource_type:
|
||||
description:
|
||||
- The resource type for the alert profile in ManageIQ. Required when state is "present".
|
||||
choices: ['Vm', 'ContainerNode', 'MiqServer', 'Host', 'Storage', 'EmsCluster',
|
||||
'ExtManagementSystem', 'MiddlewareServer']
|
||||
alerts:
|
||||
description:
|
||||
- List of alert descriptions to assign to this profile.
|
||||
- Required if state is "present"
|
||||
notes:
|
||||
description:
|
||||
- Optional notes for this profile
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Add an alert profile to ManageIQ
|
||||
manageiq_alert_profiles:
|
||||
state: present
|
||||
name: Test profile
|
||||
resource_type: ContainerNode
|
||||
alerts:
|
||||
- Test Alert 01
|
||||
- Test Alert 02
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete an alert profile from ManageIQ
|
||||
manageiq_alert_profiles:
|
||||
state: absent
|
||||
name: Test profile
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
||||
|
||||
|
||||
class ManageIQAlertProfiles(object):
|
||||
""" Object to execute alert profile 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.url = '{api_url}/alert_definition_profiles'.format(api_url=self.api_url)
|
||||
|
||||
def get_profiles(self):
|
||||
""" Get all alert profiles from ManageIQ
|
||||
"""
|
||||
try:
|
||||
response = self.client.get(self.url + '?expand=alert_definitions,resources')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Failed to query alert profiles: {error}".format(error=e))
|
||||
return response.get('resources') or []
|
||||
|
||||
def get_alerts(self, alert_descriptions):
|
||||
""" Get a list of alert hrefs from a list of alert descriptions
|
||||
"""
|
||||
alerts = []
|
||||
for alert_description in alert_descriptions:
|
||||
alert = self.manageiq.find_collection_resource_or_fail("alert_definitions",
|
||||
description=alert_description)
|
||||
alerts.append(alert['href'])
|
||||
|
||||
return alerts
|
||||
|
||||
def add_profile(self, profile):
|
||||
""" Add a new alert profile to ManageIQ
|
||||
"""
|
||||
# find all alerts to add to the profile
|
||||
# we do this first to fail early if one is missing.
|
||||
alerts = self.get_alerts(profile['alerts'])
|
||||
|
||||
# build the profile dict to send to the server
|
||||
|
||||
profile_dict = dict(name=profile['name'],
|
||||
description=profile['name'],
|
||||
mode=profile['resource_type'])
|
||||
if profile['notes']:
|
||||
profile_dict['set_data'] = dict(notes=profile['notes'])
|
||||
|
||||
# send it to the server
|
||||
try:
|
||||
result = self.client.post(self.url, resource=profile_dict, action="create")
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Creating profile failed {error}".format(error=e))
|
||||
|
||||
# now that it has been created, we can assign the alerts
|
||||
self.assign_or_unassign(result['results'][0], alerts, "assign")
|
||||
|
||||
msg = "Profile {name} created successfully"
|
||||
msg = msg.format(name=profile['name'])
|
||||
return dict(changed=True, msg=msg)
|
||||
|
||||
def delete_profile(self, profile):
|
||||
""" Delete an alert profile from ManageIQ
|
||||
"""
|
||||
try:
|
||||
self.client.post(profile['href'], action="delete")
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Deleting profile failed: {error}".format(error=e))
|
||||
|
||||
msg = "Successfully deleted profile {name}".format(name=profile['name'])
|
||||
return dict(changed=True, msg=msg)
|
||||
|
||||
def get_alert_href(self, alert):
|
||||
""" Get an absolute href for an alert
|
||||
"""
|
||||
return "{url}/alert_definitions/{id}".format(url=self.api_url, id=alert['id'])
|
||||
|
||||
def assign_or_unassign(self, profile, resources, action):
|
||||
""" Assign or unassign alerts to profile, and validate the result.
|
||||
"""
|
||||
alerts = [dict(href=href) for href in resources]
|
||||
|
||||
subcollection_url = profile['href'] + '/alert_definitions'
|
||||
try:
|
||||
result = self.client.post(subcollection_url, resources=alerts, action=action)
|
||||
if len(result['results']) != len(alerts):
|
||||
msg = "Failed to {action} alerts to profile '{name}'," +\
|
||||
"expected {expected} alerts to be {action}ed," +\
|
||||
"but only {changed} were {action}ed"
|
||||
msg = msg.format(action=action,
|
||||
name=profile['name'],
|
||||
expected=len(alerts),
|
||||
changed=result['results'])
|
||||
self.module.fail_json(msg=msg)
|
||||
except Exception as e:
|
||||
msg = "Failed to {action} alerts to profile '{name}': {error}"
|
||||
msg = msg.format(action=action, name=profile['name'], error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
return result['results']
|
||||
|
||||
def update_profile(self, old_profile, desired_profile):
|
||||
""" Update alert profile in ManageIQ
|
||||
"""
|
||||
changed = False
|
||||
# we need to use client.get to query the alert definitions
|
||||
old_profile = self.client.get(old_profile['href'] + '?expand=alert_definitions')
|
||||
|
||||
# figure out which alerts we need to assign / unassign
|
||||
# alerts listed by the user:
|
||||
desired_alerts = set(self.get_alerts(desired_profile['alerts']))
|
||||
|
||||
# alert which currently exist in the profile
|
||||
if 'alert_definitions' in old_profile:
|
||||
# we use get_alert_href to have a direct href to the alert
|
||||
existing_alerts = set([self.get_alert_href(alert) for alert in old_profile['alert_definitions']])
|
||||
else:
|
||||
# no alerts in this profile
|
||||
existing_alerts = set()
|
||||
|
||||
to_add = list(desired_alerts - existing_alerts)
|
||||
to_remove = list(existing_alerts - desired_alerts)
|
||||
|
||||
# assign / unassign the alerts, if needed
|
||||
|
||||
if to_remove:
|
||||
self.assign_or_unassign(old_profile, to_remove, "unassign")
|
||||
changed = True
|
||||
if to_add:
|
||||
self.assign_or_unassign(old_profile, to_add, "assign")
|
||||
changed = True
|
||||
|
||||
# update other properties
|
||||
profile_dict = dict()
|
||||
|
||||
if old_profile['mode'] != desired_profile['resource_type']:
|
||||
# mode needs to be updated
|
||||
profile_dict['mode'] = desired_profile['resource_type']
|
||||
|
||||
# check if notes need to be updated
|
||||
old_notes = old_profile.get('set_data', {}).get('notes')
|
||||
|
||||
if desired_profile['notes'] != old_notes:
|
||||
profile_dict['set_data'] = dict(notes=desired_profile['notes'])
|
||||
|
||||
if profile_dict:
|
||||
# if we have any updated values
|
||||
changed = True
|
||||
try:
|
||||
result = self.client.post(old_profile['href'],
|
||||
resource=profile_dict,
|
||||
action="edit")
|
||||
except Exception as e:
|
||||
msg = "Updating profile '{name}' failed: {error}"
|
||||
msg = msg.format(name=old_profile['name'], error=e)
|
||||
self.module.fail_json(msg=msg, result=result)
|
||||
|
||||
if changed:
|
||||
msg = "Profile {name} updated successfully".format(name=desired_profile['name'])
|
||||
else:
|
||||
msg = "No update needed for profile {name}".format(name=desired_profile['name'])
|
||||
return dict(changed=changed, msg=msg)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(type='str'),
|
||||
resource_type=dict(type='str', choices=['Vm',
|
||||
'ContainerNode',
|
||||
'MiqServer',
|
||||
'Host',
|
||||
'Storage',
|
||||
'EmsCluster',
|
||||
'ExtManagementSystem',
|
||||
'MiddlewareServer']),
|
||||
alerts=dict(type='list'),
|
||||
notes=dict(type='str'),
|
||||
state=dict(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', ['name', 'resource_type']),
|
||||
('state', 'absent', ['name'])])
|
||||
|
||||
state = module.params['state']
|
||||
name = module.params['name']
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
manageiq_alert_profiles = ManageIQAlertProfiles(manageiq)
|
||||
|
||||
existing_profile = manageiq.find_collection_resource_by("alert_definition_profiles",
|
||||
name=name)
|
||||
|
||||
# we need to add or update the alert profile
|
||||
if state == "present":
|
||||
if not existing_profile:
|
||||
# a profile with this name doesn't exist yet, let's create it
|
||||
res_args = manageiq_alert_profiles.add_profile(module.params)
|
||||
else:
|
||||
# a profile with this name exists, we might need to update it
|
||||
res_args = manageiq_alert_profiles.update_profile(existing_profile, module.params)
|
||||
|
||||
# this alert profile should not exist
|
||||
if state == "absent":
|
||||
# if we have an alert profile with this name, delete it
|
||||
if existing_profile:
|
||||
res_args = manageiq_alert_profiles.delete_profile(existing_profile)
|
||||
else:
|
||||
# This alert profile does not exist in ManageIQ, and that's okay
|
||||
msg = "Alert profile '{name}' does not exist in ManageIQ"
|
||||
msg = msg.format(name=name)
|
||||
res_args = dict(changed=False, msg=msg)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
347
plugins/modules/remote_management/manageiq/manageiq_alerts.py
Normal file
347
plugins/modules/remote_management/manageiq/manageiq_alerts.py
Normal file
@@ -0,0 +1,347 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (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: manageiq_alerts
|
||||
|
||||
short_description: Configuration of alerts in ManageIQ
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Elad Alfassa (@elad661) <ealfassa@redhat.com
|
||||
description:
|
||||
- The manageiq_alerts module supports adding, updating and deleting alerts in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - alert should not exist,
|
||||
- present - alert should exist,
|
||||
required: False
|
||||
choices: ['absent', 'present']
|
||||
default: 'present'
|
||||
description:
|
||||
description:
|
||||
- The unique alert description in ManageIQ.
|
||||
- Required when state is "absent" or "present".
|
||||
resource_type:
|
||||
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:
|
||||
description:
|
||||
- Expression type.
|
||||
default: hash
|
||||
choices: ["hash", "miq"]
|
||||
expression:
|
||||
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:
|
||||
description:
|
||||
- Additional alert options, such as notification type and frequency
|
||||
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Add an alert with a "hash expression" to ManageIQ
|
||||
manageiq_alerts:
|
||||
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'
|
||||
validate_certs: False
|
||||
|
||||
- name: Add an alert with a "miq expression" to ManageIQ
|
||||
manageiq_alerts:
|
||||
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'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete an alert from ManageIQ
|
||||
manageiq_alerts:
|
||||
state: absent
|
||||
description: Test Alert 01
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
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()
|
||||
643
plugins/modules/remote_management/manageiq/manageiq_group.py
Normal file
643
plugins/modules/remote_management/manageiq/manageiq_group.py
Normal file
@@ -0,0 +1,643 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2018, Evert Mulder <evertmulder@gmail.com> (base on manageiq_user.py by Daniel Korn <korndaniel1@gmail.com>)
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
|
||||
module: manageiq_group
|
||||
|
||||
short_description: Management of groups in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Evert Mulder (@evertmulder)
|
||||
description:
|
||||
- The manageiq_group module supports adding, updating and deleting groups in ManageIQ.
|
||||
requirements:
|
||||
- manageiq-client
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - group should not exist, present - group should be.
|
||||
choices: ['absent', 'present']
|
||||
default: 'present'
|
||||
description:
|
||||
description:
|
||||
- The group description.
|
||||
required: true
|
||||
default: null
|
||||
role_id:
|
||||
description:
|
||||
- The the group role id
|
||||
required: false
|
||||
default: null
|
||||
role:
|
||||
description:
|
||||
- The the group role name
|
||||
- The C(role_id) has precedence over the C(role) when supplied.
|
||||
required: false
|
||||
default: null
|
||||
tenant_id:
|
||||
description:
|
||||
- The tenant for the group identified by the tenant id.
|
||||
required: false
|
||||
default: null
|
||||
tenant:
|
||||
description:
|
||||
- The tenant for the group identified by the tenant name.
|
||||
- The C(tenant_id) has precedence over the C(tenant) when supplied.
|
||||
- Tenant names are case sensitive.
|
||||
required: false
|
||||
default: null
|
||||
managed_filters:
|
||||
description: The tag values per category
|
||||
type: dict
|
||||
required: false
|
||||
default: null
|
||||
managed_filters_merge_mode:
|
||||
description:
|
||||
- In merge mode existing categories are kept or updated, new categories are added.
|
||||
- In replace mode all categories will be replaced with the supplied C(managed_filters).
|
||||
choices: [ merge, replace ]
|
||||
default: replace
|
||||
belongsto_filters:
|
||||
description: A list of strings with a reference to the allowed host, cluster or folder
|
||||
type: list
|
||||
required: false
|
||||
default: null
|
||||
belongsto_filters_merge_mode:
|
||||
description:
|
||||
- In merge mode existing settings are merged with the supplied C(belongsto_filters).
|
||||
- In replace mode current values are replaced with the supplied C(belongsto_filters).
|
||||
choices: [ merge, replace ]
|
||||
default: replace
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a group in ManageIQ with the role EvmRole-user and tenant 'my_tenant'
|
||||
manageiq_group:
|
||||
description: 'MyGroup-user'
|
||||
role: 'EvmRole-user'
|
||||
tenant: 'my_tenant'
|
||||
manageiq_connection:
|
||||
url: 'https://manageiq_server'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Create a group in ManageIQ with the role EvmRole-user and tenant with tenant_id 4
|
||||
manageiq_group:
|
||||
description: 'MyGroup-user'
|
||||
role: 'EvmRole-user'
|
||||
tenant_id: 4
|
||||
manageiq_connection:
|
||||
url: 'https://manageiq_server'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name:
|
||||
- Create or update a group in ManageIQ with the role EvmRole-user and tenant my_tenant.
|
||||
- Apply 3 prov_max_cpu and 2 department tags to the group.
|
||||
- Limit access to a cluster for the group.
|
||||
manageiq_group:
|
||||
description: 'MyGroup-user'
|
||||
role: 'EvmRole-user'
|
||||
tenant: my_tenant
|
||||
managed_filters:
|
||||
prov_max_cpu:
|
||||
- '1'
|
||||
- '2'
|
||||
- '4'
|
||||
department:
|
||||
- defense
|
||||
- engineering
|
||||
managed_filters_merge_mode: replace
|
||||
belongsto_filters:
|
||||
- "/belongsto/ExtManagementSystem|ProviderName/EmsFolder|Datacenters/EmsFolder|dc_name/EmsFolder|host/EmsCluster|Cluster name"
|
||||
belongsto_filters_merge_mode: merge
|
||||
manageiq_connection:
|
||||
url: 'https://manageiq_server'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete a group in ManageIQ
|
||||
manageiq_group:
|
||||
state: 'absent'
|
||||
description: 'MyGroup-user'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
|
||||
- name: Delete a group in ManageIQ using a token
|
||||
manageiq_group:
|
||||
state: 'absent'
|
||||
description: 'MyGroup-user'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
token: 'sometoken'
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
group:
|
||||
description: The group.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
description:
|
||||
description: The group description
|
||||
returned: success
|
||||
type: str
|
||||
id:
|
||||
description: The group id
|
||||
returned: success
|
||||
type: int
|
||||
group_type:
|
||||
description: The group type, system or user
|
||||
returned: success
|
||||
type: str
|
||||
role:
|
||||
description: The group role name
|
||||
returned: success
|
||||
type: str
|
||||
tenant:
|
||||
description: The group tenant name
|
||||
returned: success
|
||||
type: str
|
||||
managed_filters:
|
||||
description: The tag values per category
|
||||
returned: success
|
||||
type: dict
|
||||
belongsto_filters:
|
||||
description: A list of strings with a reference to the allowed host, cluster or folder
|
||||
returned: success
|
||||
type: list
|
||||
created_on:
|
||||
description: Group creation date
|
||||
returned: success
|
||||
type: str
|
||||
sample: "2018-08-12T08:37:55+00:00"
|
||||
updated_on:
|
||||
description: Group update date
|
||||
returned: success
|
||||
type: int
|
||||
sample: "2018-08-12T08:37:55+00:00"
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
||||
|
||||
|
||||
class ManageIQgroup(object):
|
||||
"""
|
||||
Object to execute group 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
|
||||
|
||||
def group(self, description):
|
||||
""" Search for group object by description.
|
||||
Returns:
|
||||
the group, or None if group was not found.
|
||||
"""
|
||||
groups = self.client.collections.groups.find_by(description=description)
|
||||
if len(groups) == 0:
|
||||
return None
|
||||
else:
|
||||
return groups[0]
|
||||
|
||||
def tenant(self, tenant_id, tenant_name):
|
||||
""" Search for tenant entity by name or id
|
||||
Returns:
|
||||
the tenant entity, None if no id or name was supplied
|
||||
"""
|
||||
|
||||
if tenant_id:
|
||||
tenant = self.client.get_entity('tenants', tenant_id)
|
||||
if not tenant:
|
||||
self.module.fail_json(msg="Tenant with id '%s' not found in manageiq" % str(tenant_id))
|
||||
return tenant
|
||||
else:
|
||||
if tenant_name:
|
||||
tenant_res = self.client.collections.tenants.find_by(name=tenant_name)
|
||||
if not tenant_res:
|
||||
self.module.fail_json(msg="Tenant '%s' not found in manageiq" % tenant_name)
|
||||
if len(tenant_res) > 1:
|
||||
self.module.fail_json(msg="Multiple tenants found in manageiq with name '%s" % tenant_name)
|
||||
tenant = tenant_res[0]
|
||||
return tenant
|
||||
else:
|
||||
# No tenant name or tenant id supplied
|
||||
return None
|
||||
|
||||
def role(self, role_id, role_name):
|
||||
""" Search for a role object by name or id.
|
||||
Returns:
|
||||
the role entity, None no id or name was supplied
|
||||
|
||||
the role, or send a module Fail signal if role not found.
|
||||
"""
|
||||
if role_id:
|
||||
role = self.client.get_entity('roles', role_id)
|
||||
if not role:
|
||||
self.module.fail_json(msg="Role with id '%s' not found in manageiq" % str(role_id))
|
||||
return role
|
||||
else:
|
||||
if role_name:
|
||||
role_res = self.client.collections.roles.find_by(name=role_name)
|
||||
if not role_res:
|
||||
self.module.fail_json(msg="Role '%s' not found in manageiq" % role_name)
|
||||
if len(role_res) > 1:
|
||||
self.module.fail_json(msg="Multiple roles found in manageiq with name '%s" % role_name)
|
||||
return role_res[0]
|
||||
else:
|
||||
# No role name or role id supplied
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def merge_dict_values(norm_current_values, norm_updated_values):
|
||||
""" Create an merged update object for manageiq group filters.
|
||||
|
||||
The input dict contain the tag values per category.
|
||||
If the new values contain the category, all tags for that category are replaced
|
||||
If the new values do not contain the category, the existing tags are kept
|
||||
|
||||
Returns:
|
||||
the nested array with the merged values, used in the update post body
|
||||
"""
|
||||
|
||||
# If no updated values are supplied, in merge mode, the original values must be returned
|
||||
# otherwise the existing tag filters will be removed.
|
||||
if norm_current_values and (not norm_updated_values):
|
||||
return norm_current_values
|
||||
|
||||
# If no existing tag filters exist, use the user supplied values
|
||||
if (not norm_current_values) and norm_updated_values:
|
||||
return norm_updated_values
|
||||
|
||||
# start with norm_current_values's keys and values
|
||||
res = norm_current_values.copy()
|
||||
# replace res with norm_updated_values's keys and values
|
||||
res.update(norm_updated_values)
|
||||
return res
|
||||
|
||||
def delete_group(self, group):
|
||||
""" Deletes a group from manageiq.
|
||||
|
||||
Returns:
|
||||
a dict of:
|
||||
changed: boolean indicating if the entity was updated.
|
||||
msg: a short message describing the operation executed.
|
||||
"""
|
||||
try:
|
||||
url = '%s/groups/%s' % (self.api_url, group['id'])
|
||||
result = self.client.post(url, action='delete')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to delete group %s: %s" % (group['description'], str(e)))
|
||||
|
||||
if result['success'] is False:
|
||||
self.module.fail_json(msg=result['message'])
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="deleted group %s with id %s" % (group['description'], group['id']))
|
||||
|
||||
def edit_group(self, group, description, role, tenant, norm_managed_filters, managed_filters_merge_mode,
|
||||
belongsto_filters, belongsto_filters_merge_mode):
|
||||
""" Edit a manageiq group.
|
||||
|
||||
Returns:
|
||||
a dict of:
|
||||
changed: boolean indicating if the entity was updated.
|
||||
msg: a short message describing the operation executed.
|
||||
"""
|
||||
|
||||
if role or norm_managed_filters or belongsto_filters:
|
||||
group.reload(attributes=['miq_user_role_name', 'entitlement'])
|
||||
|
||||
try:
|
||||
current_role = group['miq_user_role_name']
|
||||
except AttributeError:
|
||||
current_role = None
|
||||
|
||||
changed = False
|
||||
resource = {}
|
||||
|
||||
if description and group['description'] != description:
|
||||
resource['description'] = description
|
||||
changed = True
|
||||
|
||||
if tenant and group['tenant_id'] != tenant['id']:
|
||||
resource['tenant'] = dict(id=tenant['id'])
|
||||
changed = True
|
||||
|
||||
if role and current_role != role['name']:
|
||||
resource['role'] = dict(id=role['id'])
|
||||
changed = True
|
||||
|
||||
if norm_managed_filters or belongsto_filters:
|
||||
|
||||
# Only compare if filters are supplied
|
||||
entitlement = group['entitlement']
|
||||
|
||||
if 'filters' not in entitlement:
|
||||
# No existing filters exist, use supplied filters
|
||||
managed_tag_filters_post_body = self.normalized_managed_tag_filters_to_miq(norm_managed_filters)
|
||||
resource['filters'] = {'managed': managed_tag_filters_post_body, "belongsto": belongsto_filters}
|
||||
changed = True
|
||||
else:
|
||||
current_filters = entitlement['filters']
|
||||
new_filters = self.edit_group_edit_filters(current_filters,
|
||||
norm_managed_filters, managed_filters_merge_mode,
|
||||
belongsto_filters, belongsto_filters_merge_mode)
|
||||
if new_filters:
|
||||
resource['filters'] = new_filters
|
||||
changed = True
|
||||
|
||||
if not changed:
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="group %s is not changed." % group['description'])
|
||||
|
||||
# try to update group
|
||||
try:
|
||||
self.client.post(group['href'], action='edit', resource=resource)
|
||||
changed = True
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to update group %s: %s" % (group['name'], str(e)))
|
||||
|
||||
return dict(
|
||||
changed=changed,
|
||||
msg="successfully updated the group %s with id %s" % (group['description'], group['id']))
|
||||
|
||||
def edit_group_edit_filters(self, current_filters, norm_managed_filters, managed_filters_merge_mode,
|
||||
belongsto_filters, belongsto_filters_merge_mode):
|
||||
""" Edit a manageiq group filters.
|
||||
|
||||
Returns:
|
||||
None if no the group was not updated
|
||||
If the group was updated the post body part for updating the group
|
||||
"""
|
||||
filters_updated = False
|
||||
new_filters_resource = {}
|
||||
|
||||
current_belongsto_set = current_filters.get('belongsto', set())
|
||||
|
||||
if belongsto_filters:
|
||||
new_belongsto_set = set(belongsto_filters)
|
||||
else:
|
||||
new_belongsto_set = set()
|
||||
|
||||
if current_belongsto_set == new_belongsto_set:
|
||||
new_filters_resource['belongsto'] = current_filters['belongsto']
|
||||
else:
|
||||
if belongsto_filters_merge_mode == 'merge':
|
||||
current_belongsto_set.update(new_belongsto_set)
|
||||
new_filters_resource['belongsto'] = list(current_belongsto_set)
|
||||
else:
|
||||
new_filters_resource['belongsto'] = list(new_belongsto_set)
|
||||
filters_updated = True
|
||||
|
||||
# Process belongsto managed filter tags
|
||||
# The input is in the form dict with keys are the categories and the tags are supplied string array
|
||||
# ManageIQ, the current_managed, uses an array of arrays. One array of categories.
|
||||
# We normalize the user input from a dict with arrays to a dict of sorted arrays
|
||||
# We normalize the current manageiq array of arrays also to a dict of sorted arrays so we can compare
|
||||
norm_current_filters = self.manageiq_filters_to_sorted_dict(current_filters)
|
||||
|
||||
if norm_current_filters == norm_managed_filters:
|
||||
if 'managed' in current_filters:
|
||||
new_filters_resource['managed'] = current_filters['managed']
|
||||
else:
|
||||
if managed_filters_merge_mode == 'merge':
|
||||
merged_dict = self.merge_dict_values(norm_current_filters, norm_managed_filters)
|
||||
new_filters_resource['managed'] = self.normalized_managed_tag_filters_to_miq(merged_dict)
|
||||
else:
|
||||
new_filters_resource['managed'] = self.normalized_managed_tag_filters_to_miq(norm_managed_filters)
|
||||
filters_updated = True
|
||||
|
||||
if not filters_updated:
|
||||
return None
|
||||
|
||||
return new_filters_resource
|
||||
|
||||
def create_group(self, description, role, tenant, norm_managed_filters, belongsto_filters):
|
||||
""" Creates the group in manageiq.
|
||||
|
||||
Returns:
|
||||
the created group id, name, created_on timestamp,
|
||||
updated_on timestamp.
|
||||
"""
|
||||
# check for required arguments
|
||||
for key, value in dict(description=description).items():
|
||||
if value in (None, ''):
|
||||
self.module.fail_json(msg="missing required argument: %s" % key)
|
||||
|
||||
url = '%s/groups' % self.api_url
|
||||
|
||||
resource = {'description': description}
|
||||
|
||||
if role is not None:
|
||||
resource['role'] = dict(id=role['id'])
|
||||
|
||||
if tenant is not None:
|
||||
resource['tenant'] = dict(id=tenant['id'])
|
||||
|
||||
if norm_managed_filters or belongsto_filters:
|
||||
managed_tag_filters_post_body = self.normalized_managed_tag_filters_to_miq(norm_managed_filters)
|
||||
resource['filters'] = {'managed': managed_tag_filters_post_body, "belongsto": belongsto_filters}
|
||||
|
||||
try:
|
||||
result = self.client.post(url, action='create', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to create group %s: %s" % (description, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully created group %s" % description,
|
||||
group_id=result['results'][0]['id']
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def normalized_managed_tag_filters_to_miq(norm_managed_filters):
|
||||
if not norm_managed_filters:
|
||||
return None
|
||||
|
||||
return list(norm_managed_filters.values())
|
||||
|
||||
@staticmethod
|
||||
def manageiq_filters_to_sorted_dict(current_filters):
|
||||
current_managed_filters = current_filters.get('managed')
|
||||
if not current_managed_filters:
|
||||
return None
|
||||
|
||||
res = {}
|
||||
for tag_list in current_managed_filters:
|
||||
tag_list.sort()
|
||||
key = tag_list[0].split('/')[2]
|
||||
res[key] = tag_list
|
||||
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def normalize_user_managed_filters_to_sorted_dict(managed_filters, module):
|
||||
if not managed_filters:
|
||||
return None
|
||||
|
||||
res = {}
|
||||
for cat_key in managed_filters:
|
||||
cat_array = []
|
||||
if not isinstance(managed_filters[cat_key], list):
|
||||
module.fail_json(msg='Entry "{0}" of managed_filters must be a list!'.format(cat_key))
|
||||
for tags in managed_filters[cat_key]:
|
||||
miq_managed_tag = "/managed/" + cat_key + "/" + tags
|
||||
cat_array.append(miq_managed_tag)
|
||||
# Do not add empty categories. ManageIQ will remove all categories that are not supplied
|
||||
if cat_array:
|
||||
cat_array.sort()
|
||||
res[cat_key] = cat_array
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def create_result_group(group):
|
||||
""" Creates the ansible result object from a manageiq group entity
|
||||
|
||||
Returns:
|
||||
a dict with the group id, description, role, tenant, filters, group_type, created_on, updated_on
|
||||
"""
|
||||
try:
|
||||
role_name = group['miq_user_role_name']
|
||||
except AttributeError:
|
||||
role_name = None
|
||||
|
||||
managed_filters = None
|
||||
belongsto_filters = None
|
||||
if 'filters' in group['entitlement']:
|
||||
filters = group['entitlement']['filters']
|
||||
belongsto_filters = filters.get('belongsto')
|
||||
group_managed_filters = filters.get('managed')
|
||||
if group_managed_filters:
|
||||
managed_filters = {}
|
||||
for tag_list in group_managed_filters:
|
||||
key = tag_list[0].split('/')[2]
|
||||
tags = []
|
||||
for t in tag_list:
|
||||
tags.append(t.split('/')[3])
|
||||
managed_filters[key] = tags
|
||||
|
||||
return dict(
|
||||
id=group['id'],
|
||||
description=group['description'],
|
||||
role=role_name,
|
||||
tenant=group['tenant']['name'],
|
||||
managed_filters=managed_filters,
|
||||
belongsto_filters=belongsto_filters,
|
||||
group_type=group['group_type'],
|
||||
created_on=group['created_on'],
|
||||
updated_on=group['updated_on'],
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
description=dict(required=True, type='str'),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
role_id=dict(required=False, type='int'),
|
||||
role=dict(required=False, type='str'),
|
||||
tenant_id=dict(required=False, type='int'),
|
||||
tenant=dict(required=False, type='str'),
|
||||
managed_filters=dict(required=False, type='dict'),
|
||||
managed_filters_merge_mode=dict(required=False, choices=['merge', 'replace'], default='replace'),
|
||||
belongsto_filters=dict(required=False, type='list', elements='str'),
|
||||
belongsto_filters_merge_mode=dict(required=False, choices=['merge', 'replace'], default='replace'),
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec
|
||||
)
|
||||
|
||||
description = module.params['description']
|
||||
state = module.params['state']
|
||||
role_id = module.params['role_id']
|
||||
role_name = module.params['role']
|
||||
tenant_id = module.params['tenant_id']
|
||||
tenant_name = module.params['tenant']
|
||||
managed_filters = module.params['managed_filters']
|
||||
managed_filters_merge_mode = module.params['managed_filters_merge_mode']
|
||||
belongsto_filters = module.params['belongsto_filters']
|
||||
belongsto_filters_merge_mode = module.params['belongsto_filters_merge_mode']
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
manageiq_group = ManageIQgroup(manageiq)
|
||||
|
||||
group = manageiq_group.group(description)
|
||||
|
||||
# group should not exist
|
||||
if state == "absent":
|
||||
# if we have a group, delete it
|
||||
if group:
|
||||
res_args = manageiq_group.delete_group(group)
|
||||
# if we do not have a group, nothing to do
|
||||
else:
|
||||
res_args = dict(
|
||||
changed=False,
|
||||
msg="group '%s' does not exist in manageiq" % description)
|
||||
|
||||
# group should exist
|
||||
if state == "present":
|
||||
|
||||
tenant = manageiq_group.tenant(tenant_id, tenant_name)
|
||||
role = manageiq_group.role(role_id, role_name)
|
||||
norm_managed_filters = manageiq_group.normalize_user_managed_filters_to_sorted_dict(managed_filters, module)
|
||||
# if we have a group, edit it
|
||||
if group:
|
||||
res_args = manageiq_group.edit_group(group, description, role, tenant,
|
||||
norm_managed_filters, managed_filters_merge_mode,
|
||||
belongsto_filters, belongsto_filters_merge_mode)
|
||||
|
||||
# if we do not have a group, create it
|
||||
else:
|
||||
res_args = manageiq_group.create_group(description, role, tenant, norm_managed_filters, belongsto_filters)
|
||||
group = manageiq.client.get_entity('groups', res_args['group_id'])
|
||||
|
||||
group.reload(expand='resources', attributes=['miq_user_role_name', 'tenant', 'entitlement'])
|
||||
res_args['group'] = manageiq_group.create_result_group(group)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
347
plugins/modules/remote_management/manageiq/manageiq_policies.py
Normal file
347
plugins/modules/remote_management/manageiq/manageiq_policies.py
Normal file
@@ -0,0 +1,347 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2017, Daniel Korn <korndaniel1@gmail.com>
|
||||
# (c) 2017, Yaacov Zamir <yzamir@redhat.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: manageiq_policies
|
||||
|
||||
short_description: Management of resource policy_profiles in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Daniel Korn (@dkorn)
|
||||
description:
|
||||
- The manageiq_policies module supports adding and deleting policy_profiles in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - policy_profiles should not exist,
|
||||
- present - policy_profiles should exist,
|
||||
- list - list current policy_profiles and policies.
|
||||
choices: ['absent', 'present', 'list']
|
||||
default: 'present'
|
||||
policy_profiles:
|
||||
description:
|
||||
- list of dictionaries, each includes the policy_profile 'name' key.
|
||||
- required if state is present or absent.
|
||||
resource_type:
|
||||
description:
|
||||
- the type of the resource to which the profile should be [un]assigned
|
||||
required: true
|
||||
choices: ['provider', 'host', 'vm', 'blueprint', 'category', 'cluster',
|
||||
'data store', 'group', 'resource pool', 'service', 'service template',
|
||||
'template', 'tenant', 'user']
|
||||
resource_name:
|
||||
description:
|
||||
- the name of the resource to which the profile should be [un]assigned
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Assign new policy_profile for a provider in ManageIQ
|
||||
manageiq_policies:
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
policy_profiles:
|
||||
- name: openscap profile
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Unassign a policy_profile for a provider in ManageIQ
|
||||
manageiq_policies:
|
||||
state: absent
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
policy_profiles:
|
||||
- name: openscap profile
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: List current policy_profile and policies for a provider in ManageIQ
|
||||
manageiq_policies:
|
||||
state: list
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
manageiq_policies:
|
||||
description:
|
||||
- List current policy_profile and policies for a provider in ManageIQ
|
||||
returned: always
|
||||
type: dict
|
||||
sample: '{
|
||||
"changed": false,
|
||||
"profiles": [
|
||||
{
|
||||
"policies": [
|
||||
{
|
||||
"active": true,
|
||||
"description": "OpenSCAP",
|
||||
"name": "openscap policy"
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"description": "Analyse incoming container images",
|
||||
"name": "analyse incoming container images"
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"description": "Schedule compliance after smart state analysis",
|
||||
"name": "schedule compliance after smart state analysis"
|
||||
}
|
||||
],
|
||||
"profile_description": "OpenSCAP profile",
|
||||
"profile_name": "openscap profile"
|
||||
}
|
||||
]
|
||||
}'
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec, manageiq_entities
|
||||
|
||||
|
||||
class ManageIQPolicies(object):
|
||||
"""
|
||||
Object to execute policies management operations of manageiq resources.
|
||||
"""
|
||||
|
||||
def __init__(self, manageiq, resource_type, resource_id):
|
||||
self.manageiq = manageiq
|
||||
|
||||
self.module = self.manageiq.module
|
||||
self.api_url = self.manageiq.api_url
|
||||
self.client = self.manageiq.client
|
||||
|
||||
self.resource_type = resource_type
|
||||
self.resource_id = resource_id
|
||||
self.resource_url = '{api_url}/{resource_type}/{resource_id}'.format(
|
||||
api_url=self.api_url,
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id)
|
||||
|
||||
def query_profile_href(self, profile):
|
||||
""" Add or Update the policy_profile href field
|
||||
|
||||
Example:
|
||||
{name: STR, ...} => {name: STR, href: STR}
|
||||
"""
|
||||
resource = self.manageiq.find_collection_resource_or_fail(
|
||||
"policy_profiles", **profile)
|
||||
return dict(name=profile['name'], href=resource['href'])
|
||||
|
||||
def query_resource_profiles(self):
|
||||
""" Returns a set of the profile objects objects assigned to the resource
|
||||
"""
|
||||
url = '{resource_url}/policy_profiles?expand=resources'
|
||||
try:
|
||||
response = self.client.get(url.format(resource_url=self.resource_url))
|
||||
except Exception as e:
|
||||
msg = "Failed to query {resource_type} policies: {error}".format(
|
||||
resource_type=self.resource_type,
|
||||
error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
resources = response.get('resources', [])
|
||||
|
||||
# clean the returned rest api profile object to look like:
|
||||
# {profile_name: STR, profile_description: STR, policies: ARR<POLICIES>}
|
||||
profiles = [self.clean_profile_object(profile) for profile in resources]
|
||||
|
||||
return profiles
|
||||
|
||||
def query_profile_policies(self, profile_id):
|
||||
""" Returns a set of the policy objects assigned to the resource
|
||||
"""
|
||||
url = '{api_url}/policy_profiles/{profile_id}?expand=policies'
|
||||
try:
|
||||
response = self.client.get(url.format(api_url=self.api_url, profile_id=profile_id))
|
||||
except Exception as e:
|
||||
msg = "Failed to query {resource_type} policies: {error}".format(
|
||||
resource_type=self.resource_type,
|
||||
error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
resources = response.get('policies', [])
|
||||
|
||||
# clean the returned rest api policy object to look like:
|
||||
# {name: STR, description: STR, active: BOOL}
|
||||
policies = [self.clean_policy_object(policy) for policy in resources]
|
||||
|
||||
return policies
|
||||
|
||||
def clean_policy_object(self, policy):
|
||||
""" Clean a policy object to have human readable form of:
|
||||
{
|
||||
name: STR,
|
||||
description: STR,
|
||||
active: BOOL
|
||||
}
|
||||
"""
|
||||
name = policy.get('name')
|
||||
description = policy.get('description')
|
||||
active = policy.get('active')
|
||||
|
||||
return dict(
|
||||
name=name,
|
||||
description=description,
|
||||
active=active)
|
||||
|
||||
def clean_profile_object(self, profile):
|
||||
""" Clean a profile object to have human readable form of:
|
||||
{
|
||||
profile_name: STR,
|
||||
profile_description: STR,
|
||||
policies: ARR<POLICIES>
|
||||
}
|
||||
"""
|
||||
profile_id = profile['id']
|
||||
name = profile.get('name')
|
||||
description = profile.get('description')
|
||||
policies = self.query_profile_policies(profile_id)
|
||||
|
||||
return dict(
|
||||
profile_name=name,
|
||||
profile_description=description,
|
||||
policies=policies)
|
||||
|
||||
def profiles_to_update(self, profiles, action):
|
||||
""" Create a list of policies we need to update in ManageIQ.
|
||||
|
||||
Returns:
|
||||
Whether or not a change took place and a message describing the
|
||||
operation executed.
|
||||
"""
|
||||
profiles_to_post = []
|
||||
assigned_profiles = self.query_resource_profiles()
|
||||
|
||||
# make a list of assigned full profile names strings
|
||||
# e.g. ['openscap profile', ...]
|
||||
assigned_profiles_set = set([profile['profile_name'] for profile in assigned_profiles])
|
||||
|
||||
for profile in profiles:
|
||||
assigned = profile.get('name') in assigned_profiles_set
|
||||
|
||||
if (action == 'unassign' and assigned) or (action == 'assign' and not assigned):
|
||||
# add/update the policy profile href field
|
||||
# {name: STR, ...} => {name: STR, href: STR}
|
||||
profile = self.query_profile_href(profile)
|
||||
profiles_to_post.append(profile)
|
||||
|
||||
return profiles_to_post
|
||||
|
||||
def assign_or_unassign_profiles(self, profiles, action):
|
||||
""" Perform assign/unassign action
|
||||
"""
|
||||
# get a list of profiles needed to be changed
|
||||
profiles_to_post = self.profiles_to_update(profiles, action)
|
||||
if not profiles_to_post:
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="Profiles {profiles} already {action}ed, nothing to do".format(
|
||||
action=action,
|
||||
profiles=profiles))
|
||||
|
||||
# try to assign or unassign profiles to resource
|
||||
url = '{resource_url}/policy_profiles'.format(resource_url=self.resource_url)
|
||||
try:
|
||||
response = self.client.post(url, action=action, resources=profiles_to_post)
|
||||
except Exception as e:
|
||||
msg = "Failed to {action} profile: {error}".format(
|
||||
action=action,
|
||||
error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
# check all entities in result to be successful
|
||||
for result in response['results']:
|
||||
if not result['success']:
|
||||
msg = "Failed to {action}: {message}".format(
|
||||
action=action,
|
||||
message=result['message'])
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
# successfully changed all needed profiles
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="Successfully {action}ed profiles: {profiles}".format(
|
||||
action=action,
|
||||
profiles=profiles))
|
||||
|
||||
|
||||
def main():
|
||||
actions = {'present': 'assign', 'absent': 'unassign', 'list': 'list'}
|
||||
argument_spec = dict(
|
||||
policy_profiles=dict(type='list'),
|
||||
resource_name=dict(required=True, type='str'),
|
||||
resource_type=dict(required=True, type='str',
|
||||
choices=manageiq_entities().keys()),
|
||||
state=dict(required=False, type='str',
|
||||
choices=['present', 'absent', 'list'], default='present'),
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_if=[
|
||||
('state', 'present', ['policy_profiles']),
|
||||
('state', 'absent', ['policy_profiles'])
|
||||
],
|
||||
)
|
||||
|
||||
policy_profiles = module.params['policy_profiles']
|
||||
resource_type_key = module.params['resource_type']
|
||||
resource_name = module.params['resource_name']
|
||||
state = module.params['state']
|
||||
|
||||
# get the action and resource type
|
||||
action = actions[state]
|
||||
resource_type = manageiq_entities()[resource_type_key]
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
|
||||
# query resource id, fail if resource does not exist
|
||||
resource_id = manageiq.find_collection_resource_or_fail(resource_type, name=resource_name)['id']
|
||||
|
||||
manageiq_policies = ManageIQPolicies(manageiq, resource_type, resource_id)
|
||||
|
||||
if action == 'list':
|
||||
# return a list of current profiles for this object
|
||||
current_profiles = manageiq_policies.query_resource_profiles()
|
||||
res_args = dict(changed=False, profiles=current_profiles)
|
||||
else:
|
||||
# assign or unassign the profiles
|
||||
res_args = manageiq_policies.assign_or_unassign_profiles(policy_profiles, action)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
894
plugins/modules/remote_management/manageiq/manageiq_provider.py
Normal file
894
plugins/modules/remote_management/manageiq/manageiq_provider.py
Normal file
@@ -0,0 +1,894 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2017, Daniel Korn <korndaniel1@gmail.com>
|
||||
# (c) 2017, Yaacov Zamir <yzamir@redhat.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: manageiq_provider
|
||||
short_description: Management of provider in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Daniel Korn (@dkorn)
|
||||
description:
|
||||
- The manageiq_provider module supports adding, updating, and deleting provider in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - provider should not exist, present - provider should be present, refresh - provider will be refreshed
|
||||
choices: ['absent', 'present', 'refresh']
|
||||
default: 'present'
|
||||
name:
|
||||
description: The provider's name.
|
||||
required: true
|
||||
type:
|
||||
description: The provider's type.
|
||||
required: true
|
||||
choices: ['Openshift', 'Amazon', 'oVirt', 'VMware', 'Azure', 'Director', 'OpenStack', 'GCE']
|
||||
zone:
|
||||
description: The ManageIQ zone name that will manage the provider.
|
||||
default: 'default'
|
||||
provider_region:
|
||||
description: The provider region name to connect to (e.g. AWS region for Amazon).
|
||||
host_default_vnc_port_start:
|
||||
description: The first port in the host VNC range. defaults to None.
|
||||
host_default_vnc_port_end:
|
||||
description: The last port in the host VNC range. defaults to None.
|
||||
subscription:
|
||||
description: Microsoft Azure subscription ID. defaults to None.
|
||||
project:
|
||||
description: Google Compute Engine Project ID. defaults to None.
|
||||
azure_tenant_id:
|
||||
description: Tenant ID. defaults to None.
|
||||
aliases: [ keystone_v3_domain_id ]
|
||||
tenant_mapping_enabled:
|
||||
type: bool
|
||||
default: 'no'
|
||||
description: Whether to enable mapping of existing tenants. defaults to False.
|
||||
api_version:
|
||||
description: The OpenStack Keystone API version. defaults to None.
|
||||
choices: ['v2', 'v3']
|
||||
|
||||
provider:
|
||||
description: Default endpoint connection information, required if state is true.
|
||||
suboptions:
|
||||
hostname:
|
||||
description: The provider's api hostname.
|
||||
required: true
|
||||
port:
|
||||
description: The provider's api port.
|
||||
userid:
|
||||
description: Provider's api endpoint authentication userid. defaults to None.
|
||||
password:
|
||||
description: Provider's api endpoint authentication password. defaults to None.
|
||||
auth_key:
|
||||
description: Provider's api endpoint authentication bearer token. defaults to None.
|
||||
validate_certs:
|
||||
description: Whether SSL certificates should be verified for HTTPS requests (deprecated). defaults to True.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
security_protocol:
|
||||
description: How SSL certificates should be used for HTTPS requests. defaults to None.
|
||||
choices: ['ssl-with-validation','ssl-with-validation-custom-ca','ssl-without-validation','non-ssl']
|
||||
certificate_authority:
|
||||
description: The CA bundle string with custom certificates. defaults to None.
|
||||
|
||||
metrics:
|
||||
description: Metrics endpoint connection information.
|
||||
suboptions:
|
||||
hostname:
|
||||
description: The provider's api hostname.
|
||||
required: true
|
||||
port:
|
||||
description: The provider's api port.
|
||||
userid:
|
||||
description: Provider's api endpoint authentication userid. defaults to None.
|
||||
password:
|
||||
description: Provider's api endpoint authentication password. defaults to None.
|
||||
auth_key:
|
||||
description: Provider's api endpoint authentication bearer token. defaults to None.
|
||||
validate_certs:
|
||||
description: Whether SSL certificates should be verified for HTTPS requests (deprecated). defaults to True.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
security_protocol:
|
||||
choices: ['ssl-with-validation','ssl-with-validation-custom-ca','ssl-without-validation','non-ssl']
|
||||
description: How SSL certificates should be used for HTTPS requests. defaults to None.
|
||||
certificate_authority:
|
||||
description: The CA bundle string with custom certificates. defaults to None.
|
||||
path:
|
||||
description: Database name for oVirt metrics. Defaults to ovirt_engine_history.
|
||||
default: ovirt_engine_history
|
||||
|
||||
alerts:
|
||||
description: Alerts endpoint connection information.
|
||||
suboptions:
|
||||
hostname:
|
||||
description: The provider's api hostname.
|
||||
required: true
|
||||
port:
|
||||
description: The provider's api port.
|
||||
userid:
|
||||
description: Provider's api endpoint authentication userid. defaults to None.
|
||||
password:
|
||||
description: Provider's api endpoint authentication password. defaults to None.
|
||||
auth_key:
|
||||
description: Provider's api endpoint authentication bearer token. defaults to None.
|
||||
validate_certs:
|
||||
description: Whether SSL certificates should be verified for HTTPS requests (deprecated). defaults to True.
|
||||
default: true
|
||||
security_protocol:
|
||||
choices: ['ssl-with-validation','ssl-with-validation-custom-ca','ssl-without-validation']
|
||||
description: How SSL certificates should be used for HTTPS requests. defaults to None.
|
||||
certificate_authority:
|
||||
description: The CA bundle string with custom certificates. defaults to None.
|
||||
|
||||
ssh_keypair:
|
||||
description: SSH key pair used for SSH connections to all hosts in this provider.
|
||||
suboptions:
|
||||
hostname:
|
||||
description: Director hostname.
|
||||
required: true
|
||||
userid:
|
||||
description: SSH username.
|
||||
auth_key:
|
||||
description: SSH private key.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a new provider in ManageIQ ('Hawkular' metrics)
|
||||
manageiq_provider:
|
||||
name: 'EngLab'
|
||||
type: 'OpenShift'
|
||||
state: 'present'
|
||||
provider:
|
||||
auth_key: 'topSecret'
|
||||
hostname: 'example.com'
|
||||
port: 8443
|
||||
validate_certs: true
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
auth_key: 'topSecret'
|
||||
role: 'hawkular'
|
||||
hostname: 'example.com'
|
||||
port: 443
|
||||
validate_certs: true
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1:80'
|
||||
username: 'admin'
|
||||
password: 'password'
|
||||
validate_certs: true
|
||||
|
||||
|
||||
- name: Update an existing provider named 'EngLab' (defaults to 'Prometheus' metrics)
|
||||
manageiq_provider:
|
||||
name: 'EngLab'
|
||||
type: 'Openshift'
|
||||
state: 'present'
|
||||
provider:
|
||||
auth_key: 'topSecret'
|
||||
hostname: 'next.example.com'
|
||||
port: 8443
|
||||
validate_certs: true
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
auth_key: 'topSecret'
|
||||
hostname: 'next.example.com'
|
||||
port: 443
|
||||
validate_certs: true
|
||||
security_protocol: 'ssl-with-validation-custom-ca'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
username: 'admin'
|
||||
password: 'password'
|
||||
validate_certs: true
|
||||
|
||||
|
||||
- name: Delete a provider in ManageIQ
|
||||
manageiq_provider:
|
||||
name: 'EngLab'
|
||||
type: 'Openshift'
|
||||
state: 'absent'
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
username: 'admin'
|
||||
password: 'password'
|
||||
validate_certs: true
|
||||
|
||||
|
||||
- name: Create a new Amazon provider in ManageIQ using token authentication
|
||||
manageiq_provider:
|
||||
name: 'EngAmazon'
|
||||
type: 'Amazon'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'amazon.example.com'
|
||||
userid: 'hello'
|
||||
password: 'world'
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
token: 'VeryLongToken'
|
||||
validate_certs: true
|
||||
|
||||
|
||||
- name: Create a new oVirt provider in ManageIQ
|
||||
manageiq_provider:
|
||||
name: 'RHEV'
|
||||
type: 'oVirt'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'rhev01.example.com'
|
||||
userid: 'admin@internal'
|
||||
password: 'password'
|
||||
validate_certs: true
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
hostname: 'metrics.example.com'
|
||||
path: 'ovirt_engine_history'
|
||||
userid: 'user_id_metrics'
|
||||
password: 'password_metrics'
|
||||
validate_certs: true
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
username: 'admin'
|
||||
password: 'password'
|
||||
validate_certs: true
|
||||
|
||||
- name: Create a new VMware provider in ManageIQ
|
||||
manageiq_provider:
|
||||
name: 'EngVMware'
|
||||
type: 'VMware'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'vcenter.example.com'
|
||||
host_default_vnc_port_start: 5800
|
||||
host_default_vnc_port_end: 5801
|
||||
userid: 'root'
|
||||
password: 'password'
|
||||
manageiq_connection:
|
||||
url: 'https://127.0.0.1'
|
||||
token: 'VeryLongToken'
|
||||
validate_certs: true
|
||||
|
||||
- name: Create a new Azure provider in ManageIQ
|
||||
manageiq_provider:
|
||||
name: 'EngAzure'
|
||||
type: 'Azure'
|
||||
provider_region: 'northeurope'
|
||||
subscription: 'e272bd74-f661-484f-b223-88dd128a4049'
|
||||
azure_tenant_id: 'e272bd74-f661-484f-b223-88dd128a4048'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'azure.example.com'
|
||||
userid: 'e272bd74-f661-484f-b223-88dd128a4049'
|
||||
password: 'password'
|
||||
manageiq_connection:
|
||||
url: 'https://cf-6af0.rhpds.opentlc.com'
|
||||
username: 'admin'
|
||||
password: 'password'
|
||||
validate_certs: false
|
||||
|
||||
- name: Create a new OpenStack Director provider in ManageIQ with rsa keypair
|
||||
manageiq_provider:
|
||||
name: 'EngDirector'
|
||||
type: 'Director'
|
||||
api_version: 'v3'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'director.example.com'
|
||||
userid: 'admin'
|
||||
password: 'password'
|
||||
security_protocol: 'ssl-with-validation'
|
||||
validate_certs: 'true'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
ssh_keypair:
|
||||
hostname: director.example.com
|
||||
userid: heat-admin
|
||||
auth_key: 'SecretSSHPrivateKey'
|
||||
|
||||
- name: Create a new OpenStack provider in ManageIQ with amqp metrics
|
||||
manageiq_provider:
|
||||
name: 'EngOpenStack'
|
||||
type: 'OpenStack'
|
||||
api_version: 'v3'
|
||||
state: 'present'
|
||||
provider_region: 'europe'
|
||||
tenant_mapping_enabled: 'False'
|
||||
keystone_v3_domain_id: 'mydomain'
|
||||
provider:
|
||||
hostname: 'openstack.example.com'
|
||||
userid: 'admin'
|
||||
password: 'password'
|
||||
security_protocol: 'ssl-with-validation'
|
||||
validate_certs: 'true'
|
||||
certificate_authority: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
FAKECERTsdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu
|
||||
c2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkwHhcNMTcwODIxMTI1NTE5WhcNMjIwODIw
|
||||
MTI1NTIwWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1MDMzMjAxMTkw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDUDnL2tQ2xf/zO7F7hmZ4S
|
||||
ZuwKENdI4IYuWSxye4i3hPhKg6eKPzGzmDNWkIMDOrDAj1EgVSNPtPwsOL8OWvJm
|
||||
AaTjr070D7ZGWWnrrDrWEClBx9Rx/6JAM38RT8Pu7c1hXBm0J81KufSLLYiZ/gOw
|
||||
Znks5v5RUSGcAXvLkBJeATbsbh6fKX0RgQ3fFTvqQaE/r8LxcTN1uehPX1g5AaRa
|
||||
z/SNDHaFtQlE3XcqAAukyMn4N5kdNcuwF3GlQ+tJnJv8SstPkfQcZbTMUQ7I2KpJ
|
||||
ajXnMxmBhV5fCN4rb0QUNCrk2/B+EUMBY4MnxIakqNxnN1kvgI7FBbFgrHUe6QvJ
|
||||
AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAYRV57LUsqznSLZHA77o9+0fQetIE115DYP7wea42PODJI
|
||||
QJ+JETEfoCr0+YOMAbVmznP9GH5cMTKEWHExcIpbMBU7nMZp6A3htcJgF2fgPzOA
|
||||
aTUtzkuVCSrV//mbbYVxoFOc6sR3Br0wBs5+5iz3dBSt7xmgpMzZvqsQl655i051
|
||||
gGSTIY3z5EJmBZBjwuTjal9mMoPGA4eoTPqlITJDHQ2bdCV2oDbc7zqupGrUfZFA
|
||||
qzgieEyGzdCSRwjr1/PibA3bpwHyhD9CGD0PRVVTLhw6h6L5kuN1jA20OfzWxf/o
|
||||
XUsdmRaWiF+l4s6Dcd56SuRp5SGNa2+vP9Of/FX5
|
||||
-----END CERTIFICATE-----
|
||||
metrics:
|
||||
role: amqp
|
||||
hostname: 'amqp.example.com'
|
||||
security_protocol: 'non-ssl'
|
||||
port: 5666
|
||||
userid: admin
|
||||
password: password
|
||||
|
||||
|
||||
- name: Create a new GCE provider in ManageIQ
|
||||
manageiq_provider:
|
||||
name: 'EngGoogle'
|
||||
type: 'GCE'
|
||||
provider_region: 'europe-west1'
|
||||
project: 'project1'
|
||||
state: 'present'
|
||||
provider:
|
||||
hostname: 'gce.example.com'
|
||||
auth_key: 'google_json_key'
|
||||
validate_certs: 'false'
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
||||
|
||||
|
||||
def supported_providers():
|
||||
return dict(
|
||||
Openshift=dict(
|
||||
class_name='ManageIQ::Providers::Openshift::ContainerManager',
|
||||
authtype='bearer',
|
||||
default_role='default',
|
||||
metrics_role='prometheus',
|
||||
alerts_role='prometheus_alerts',
|
||||
),
|
||||
Amazon=dict(
|
||||
class_name='ManageIQ::Providers::Amazon::CloudManager',
|
||||
),
|
||||
oVirt=dict(
|
||||
class_name='ManageIQ::Providers::Redhat::InfraManager',
|
||||
default_role='default',
|
||||
metrics_role='metrics',
|
||||
),
|
||||
VMware=dict(
|
||||
class_name='ManageIQ::Providers::Vmware::InfraManager',
|
||||
),
|
||||
Azure=dict(
|
||||
class_name='ManageIQ::Providers::Azure::CloudManager',
|
||||
),
|
||||
Director=dict(
|
||||
class_name='ManageIQ::Providers::Openstack::InfraManager',
|
||||
ssh_keypair_role="ssh_keypair"
|
||||
),
|
||||
OpenStack=dict(
|
||||
class_name='ManageIQ::Providers::Openstack::CloudManager',
|
||||
),
|
||||
GCE=dict(
|
||||
class_name='ManageIQ::Providers::Google::CloudManager',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def endpoint_list_spec():
|
||||
return dict(
|
||||
provider=dict(type='dict', options=endpoint_argument_spec()),
|
||||
metrics=dict(type='dict', options=endpoint_argument_spec()),
|
||||
alerts=dict(type='dict', options=endpoint_argument_spec()),
|
||||
ssh_keypair=dict(type='dict', options=endpoint_argument_spec()),
|
||||
)
|
||||
|
||||
|
||||
def endpoint_argument_spec():
|
||||
return dict(
|
||||
role=dict(),
|
||||
hostname=dict(required=True),
|
||||
port=dict(type='int'),
|
||||
validate_certs=dict(default=True, type='bool', aliases=['verify_ssl']),
|
||||
certificate_authority=dict(),
|
||||
security_protocol=dict(
|
||||
choices=[
|
||||
'ssl-with-validation',
|
||||
'ssl-with-validation-custom-ca',
|
||||
'ssl-without-validation',
|
||||
'non-ssl',
|
||||
],
|
||||
),
|
||||
userid=dict(),
|
||||
password=dict(no_log=True),
|
||||
auth_key=dict(no_log=True),
|
||||
subscription=dict(no_log=True),
|
||||
project=dict(),
|
||||
uid_ems=dict(),
|
||||
path=dict(),
|
||||
)
|
||||
|
||||
|
||||
def delete_nulls(h):
|
||||
""" Remove null entries from a hash
|
||||
|
||||
Returns:
|
||||
a hash without nulls
|
||||
"""
|
||||
if isinstance(h, list):
|
||||
return map(delete_nulls, h)
|
||||
if isinstance(h, dict):
|
||||
return dict((k, delete_nulls(v)) for k, v in h.items() if v is not None)
|
||||
|
||||
return h
|
||||
|
||||
|
||||
class ManageIQProvider(object):
|
||||
"""
|
||||
Object to execute provider 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
|
||||
|
||||
def class_name_to_type(self, class_name):
|
||||
""" Convert class_name to type
|
||||
|
||||
Returns:
|
||||
the type
|
||||
"""
|
||||
out = [k for k, v in supported_providers().items() if v['class_name'] == class_name]
|
||||
if len(out) == 1:
|
||||
return out[0]
|
||||
|
||||
return None
|
||||
|
||||
def zone_id(self, name):
|
||||
""" Search for zone id by zone name.
|
||||
|
||||
Returns:
|
||||
the zone id, or send a module Fail signal if zone not found.
|
||||
"""
|
||||
zone = self.manageiq.find_collection_resource_by('zones', name=name)
|
||||
if not zone: # zone doesn't exist
|
||||
self.module.fail_json(
|
||||
msg="zone %s does not exist in manageiq" % (name))
|
||||
|
||||
return zone['id']
|
||||
|
||||
def provider(self, name):
|
||||
""" Search for provider object by name.
|
||||
|
||||
Returns:
|
||||
the provider, or None if provider not found.
|
||||
"""
|
||||
return self.manageiq.find_collection_resource_by('providers', name=name)
|
||||
|
||||
def build_connection_configurations(self, provider_type, endpoints):
|
||||
""" Build "connection_configurations" objects from
|
||||
requested endpoints provided by user
|
||||
|
||||
Returns:
|
||||
the user requested provider endpoints list
|
||||
"""
|
||||
connection_configurations = []
|
||||
endpoint_keys = endpoint_list_spec().keys()
|
||||
provider_defaults = supported_providers().get(provider_type, {})
|
||||
|
||||
# get endpoint defaults
|
||||
endpoint = endpoints.get('provider')
|
||||
default_auth_key = endpoint.get('auth_key')
|
||||
|
||||
# build a connection_configuration object for each endpoint
|
||||
for endpoint_key in endpoint_keys:
|
||||
endpoint = endpoints.get(endpoint_key)
|
||||
if endpoint:
|
||||
# get role and authtype
|
||||
role = endpoint.get('role') or provider_defaults.get(endpoint_key + '_role', 'default')
|
||||
if role == 'default':
|
||||
authtype = provider_defaults.get('authtype') or role
|
||||
else:
|
||||
authtype = role
|
||||
|
||||
# set a connection_configuration
|
||||
connection_configurations.append({
|
||||
'endpoint': {
|
||||
'role': role,
|
||||
'hostname': endpoint.get('hostname'),
|
||||
'port': endpoint.get('port'),
|
||||
'verify_ssl': [0, 1][endpoint.get('validate_certs', True)],
|
||||
'security_protocol': endpoint.get('security_protocol'),
|
||||
'certificate_authority': endpoint.get('certificate_authority'),
|
||||
'path': endpoint.get('path'),
|
||||
},
|
||||
'authentication': {
|
||||
'authtype': authtype,
|
||||
'userid': endpoint.get('userid'),
|
||||
'password': endpoint.get('password'),
|
||||
'auth_key': endpoint.get('auth_key') or default_auth_key,
|
||||
}
|
||||
})
|
||||
|
||||
return connection_configurations
|
||||
|
||||
def delete_provider(self, provider):
|
||||
""" Deletes a provider from manageiq.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
try:
|
||||
url = '%s/providers/%s' % (self.api_url, provider['id'])
|
||||
result = self.client.post(url, action='delete')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to delete provider %s: %s" % (provider['name'], str(e)))
|
||||
|
||||
return dict(changed=True, msg=result['message'])
|
||||
|
||||
def edit_provider(self, provider, name, provider_type, endpoints, zone_id, provider_region,
|
||||
host_default_vnc_port_start, host_default_vnc_port_end,
|
||||
subscription, project, uid_ems, tenant_mapping_enabled, api_version):
|
||||
""" Edit a provider from manageiq.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
url = '%s/providers/%s' % (self.api_url, provider['id'])
|
||||
|
||||
resource = dict(
|
||||
name=name,
|
||||
zone={'id': zone_id},
|
||||
provider_region=provider_region,
|
||||
connection_configurations=endpoints,
|
||||
host_default_vnc_port_start=host_default_vnc_port_start,
|
||||
host_default_vnc_port_end=host_default_vnc_port_end,
|
||||
subscription=subscription,
|
||||
project=project,
|
||||
uid_ems=uid_ems,
|
||||
tenant_mapping_enabled=tenant_mapping_enabled,
|
||||
api_version=api_version,
|
||||
)
|
||||
|
||||
# NOTE: we do not check for diff's between requested and current
|
||||
# provider, we always submit endpoints with password or auth_keys,
|
||||
# since we can not compare with current password or auth_key,
|
||||
# every edit request is sent to ManageIQ API without comparing
|
||||
# it to current state.
|
||||
|
||||
# clean nulls, we do not send nulls to the api
|
||||
resource = delete_nulls(resource)
|
||||
|
||||
# try to update provider
|
||||
try:
|
||||
result = self.client.post(url, action='edit', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to update provider %s: %s" % (provider['name'], str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully updated the provider %s: %s" % (provider['name'], result))
|
||||
|
||||
def create_provider(self, name, provider_type, endpoints, zone_id, provider_region,
|
||||
host_default_vnc_port_start, host_default_vnc_port_end,
|
||||
subscription, project, uid_ems, tenant_mapping_enabled, api_version):
|
||||
""" Creates the provider in manageiq.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
resource = dict(
|
||||
name=name,
|
||||
zone={'id': zone_id},
|
||||
provider_region=provider_region,
|
||||
host_default_vnc_port_start=host_default_vnc_port_start,
|
||||
host_default_vnc_port_end=host_default_vnc_port_end,
|
||||
subscription=subscription,
|
||||
project=project,
|
||||
uid_ems=uid_ems,
|
||||
tenant_mapping_enabled=tenant_mapping_enabled,
|
||||
api_version=api_version,
|
||||
connection_configurations=endpoints,
|
||||
)
|
||||
|
||||
# clean nulls, we do not send nulls to the api
|
||||
resource = delete_nulls(resource)
|
||||
|
||||
# try to create a new provider
|
||||
try:
|
||||
url = '%s/providers' % (self.api_url)
|
||||
result = self.client.post(url, type=supported_providers()[provider_type]['class_name'], **resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to create provider %s: %s" % (name, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully created the provider %s: %s" % (name, result['results']))
|
||||
|
||||
def refresh(self, provider, name):
|
||||
""" Trigger provider refresh.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
try:
|
||||
url = '%s/providers/%s' % (self.api_url, provider['id'])
|
||||
result = self.client.post(url, action='refresh')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to refresh provider %s: %s" % (name, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="refreshing provider %s" % name)
|
||||
|
||||
|
||||
def main():
|
||||
zone_id = None
|
||||
endpoints = []
|
||||
argument_spec = dict(
|
||||
state=dict(choices=['absent', 'present', 'refresh'], default='present'),
|
||||
name=dict(required=True),
|
||||
zone=dict(default='default'),
|
||||
provider_region=dict(),
|
||||
host_default_vnc_port_start=dict(),
|
||||
host_default_vnc_port_end=dict(),
|
||||
subscription=dict(),
|
||||
project=dict(),
|
||||
azure_tenant_id=dict(aliases=['keystone_v3_domain_id']),
|
||||
tenant_mapping_enabled=dict(default=False, type='bool'),
|
||||
api_version=dict(choices=['v2', 'v3']),
|
||||
type=dict(choices=supported_providers().keys()),
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
# add the endpoint arguments to the arguments
|
||||
argument_spec.update(endpoint_list_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_if=[
|
||||
('state', 'present', ['provider']),
|
||||
('state', 'refresh', ['name'])],
|
||||
required_together=[
|
||||
['host_default_vnc_port_start', 'host_default_vnc_port_end']
|
||||
],
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
zone_name = module.params['zone']
|
||||
provider_type = module.params['type']
|
||||
raw_endpoints = module.params
|
||||
provider_region = module.params['provider_region']
|
||||
host_default_vnc_port_start = module.params['host_default_vnc_port_start']
|
||||
host_default_vnc_port_end = module.params['host_default_vnc_port_end']
|
||||
subscription = module.params['subscription']
|
||||
uid_ems = module.params['azure_tenant_id']
|
||||
project = module.params['project']
|
||||
tenant_mapping_enabled = module.params['tenant_mapping_enabled']
|
||||
api_version = module.params['api_version']
|
||||
state = module.params['state']
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
manageiq_provider = ManageIQProvider(manageiq)
|
||||
|
||||
provider = manageiq_provider.provider(name)
|
||||
|
||||
# provider should not exist
|
||||
if state == "absent":
|
||||
# if we have a provider, delete it
|
||||
if provider:
|
||||
res_args = manageiq_provider.delete_provider(provider)
|
||||
# if we do not have a provider, nothing to do
|
||||
else:
|
||||
res_args = dict(
|
||||
changed=False,
|
||||
msg="provider %s: does not exist in manageiq" % (name))
|
||||
|
||||
# provider should exist
|
||||
if state == "present":
|
||||
# get data user did not explicitly give
|
||||
if zone_name:
|
||||
zone_id = manageiq_provider.zone_id(zone_name)
|
||||
|
||||
# if we do not have a provider_type, use the current provider_type
|
||||
if provider and not provider_type:
|
||||
provider_type = manageiq_provider.class_name_to_type(provider['type'])
|
||||
|
||||
# check supported_providers types
|
||||
if not provider_type:
|
||||
manageiq_provider.module.fail_json(
|
||||
msg="missing required argument: provider_type")
|
||||
|
||||
# check supported_providers types
|
||||
if provider_type not in supported_providers().keys():
|
||||
manageiq_provider.module.fail_json(
|
||||
msg="provider_type %s is not supported" % (provider_type))
|
||||
|
||||
# build "connection_configurations" objects from user requested endpoints
|
||||
# "provider" is a required endpoint, if we have it, we have endpoints
|
||||
if raw_endpoints.get("provider"):
|
||||
endpoints = manageiq_provider.build_connection_configurations(provider_type, raw_endpoints)
|
||||
|
||||
# if we have a provider, edit it
|
||||
if provider:
|
||||
res_args = manageiq_provider.edit_provider(provider, name, provider_type, endpoints, zone_id, provider_region,
|
||||
host_default_vnc_port_start, host_default_vnc_port_end,
|
||||
subscription, project, uid_ems, tenant_mapping_enabled, api_version)
|
||||
# if we do not have a provider, create it
|
||||
else:
|
||||
res_args = manageiq_provider.create_provider(name, provider_type, endpoints, zone_id, provider_region,
|
||||
host_default_vnc_port_start, host_default_vnc_port_end,
|
||||
subscription, project, uid_ems, tenant_mapping_enabled, api_version)
|
||||
|
||||
# refresh provider (trigger sync)
|
||||
if state == "refresh":
|
||||
if provider:
|
||||
res_args = manageiq_provider.refresh(provider, name)
|
||||
else:
|
||||
res_args = dict(
|
||||
changed=False,
|
||||
msg="provider %s: does not exist in manageiq" % (name))
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
292
plugins/modules/remote_management/manageiq/manageiq_tags.py
Normal file
292
plugins/modules/remote_management/manageiq/manageiq_tags.py
Normal file
@@ -0,0 +1,292 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2017, Daniel Korn <korndaniel1@gmail.com>
|
||||
# (c) 2017, Yaacov Zamir <yzamir@redhat.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: manageiq_tags
|
||||
|
||||
short_description: Management of resource tags in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Daniel Korn (@dkorn)
|
||||
description:
|
||||
- The manageiq_tags module supports adding, updating and deleting tags in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - tags should not exist,
|
||||
- present - tags should exist,
|
||||
- list - list current tags.
|
||||
choices: ['absent', 'present', 'list']
|
||||
default: 'present'
|
||||
tags:
|
||||
description:
|
||||
- tags - list of dictionaries, each includes 'name' and 'category' keys.
|
||||
- required if state is present or absent.
|
||||
resource_type:
|
||||
description:
|
||||
- the relevant resource type in manageiq
|
||||
required: true
|
||||
choices: ['provider', 'host', 'vm', 'blueprint', 'category', 'cluster',
|
||||
'data store', 'group', 'resource pool', 'service', 'service template',
|
||||
'template', 'tenant', 'user']
|
||||
resource_name:
|
||||
description:
|
||||
- the relevant resource name in manageiq
|
||||
required: true
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create new tags for a provider in ManageIQ
|
||||
manageiq_tags:
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
tags:
|
||||
- category: environment
|
||||
name: prod
|
||||
- category: owner
|
||||
name: prod_ops
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Remove tags for a provider in ManageIQ
|
||||
manageiq_tags:
|
||||
state: absent
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
tags:
|
||||
- category: environment
|
||||
name: prod
|
||||
- category: owner
|
||||
name: prod_ops
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: List current tags for a provider in ManageIQ
|
||||
manageiq_tags:
|
||||
state: list
|
||||
resource_name: 'EngLab'
|
||||
resource_type: 'provider'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec, manageiq_entities
|
||||
|
||||
|
||||
def query_resource_id(manageiq, resource_type, resource_name):
|
||||
""" Query the resource name in ManageIQ.
|
||||
|
||||
Returns:
|
||||
the resource id if it exists in manageiq, Fail otherwise.
|
||||
"""
|
||||
resource = manageiq.find_collection_resource_by(resource_type, name=resource_name)
|
||||
if resource:
|
||||
return resource["id"]
|
||||
else:
|
||||
msg = "{resource_name} {resource_type} does not exist in manageiq".format(
|
||||
resource_name=resource_name, resource_type=resource_type)
|
||||
manageiq.module.fail_json(msg=msg)
|
||||
|
||||
|
||||
class ManageIQTags(object):
|
||||
"""
|
||||
Object to execute tags management operations of manageiq resources.
|
||||
"""
|
||||
|
||||
def __init__(self, manageiq, resource_type, resource_id):
|
||||
self.manageiq = manageiq
|
||||
|
||||
self.module = self.manageiq.module
|
||||
self.api_url = self.manageiq.api_url
|
||||
self.client = self.manageiq.client
|
||||
|
||||
self.resource_type = resource_type
|
||||
self.resource_id = resource_id
|
||||
self.resource_url = '{api_url}/{resource_type}/{resource_id}'.format(
|
||||
api_url=self.api_url,
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id)
|
||||
|
||||
def full_tag_name(self, tag):
|
||||
""" Returns the full tag name in manageiq
|
||||
"""
|
||||
return '/managed/{tag_category}/{tag_name}'.format(
|
||||
tag_category=tag['category'],
|
||||
tag_name=tag['name'])
|
||||
|
||||
def clean_tag_object(self, tag):
|
||||
""" Clean a tag object to have human readable form of:
|
||||
{
|
||||
full_name: STR,
|
||||
name: STR,
|
||||
display_name: STR,
|
||||
category: STR
|
||||
}
|
||||
"""
|
||||
full_name = tag.get('name')
|
||||
categorization = tag.get('categorization', {})
|
||||
|
||||
return dict(
|
||||
full_name=full_name,
|
||||
name=categorization.get('name'),
|
||||
display_name=categorization.get('display_name'),
|
||||
category=categorization.get('category', {}).get('name'))
|
||||
|
||||
def query_resource_tags(self):
|
||||
""" Returns a set of the tag objects assigned to the resource
|
||||
"""
|
||||
url = '{resource_url}/tags?expand=resources&attributes=categorization'
|
||||
try:
|
||||
response = self.client.get(url.format(resource_url=self.resource_url))
|
||||
except Exception as e:
|
||||
msg = "Failed to query {resource_type} tags: {error}".format(
|
||||
resource_type=self.resource_type,
|
||||
error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
resources = response.get('resources', [])
|
||||
|
||||
# clean the returned rest api tag object to look like:
|
||||
# {full_name: STR, name: STR, display_name: STR, category: STR}
|
||||
tags = [self.clean_tag_object(tag) for tag in resources]
|
||||
|
||||
return tags
|
||||
|
||||
def tags_to_update(self, tags, action):
|
||||
""" Create a list of tags we need to update in ManageIQ.
|
||||
|
||||
Returns:
|
||||
Whether or not a change took place and a message describing the
|
||||
operation executed.
|
||||
"""
|
||||
tags_to_post = []
|
||||
assigned_tags = self.query_resource_tags()
|
||||
|
||||
# make a list of assigned full tag names strings
|
||||
# e.g. ['/managed/environment/prod', ...]
|
||||
assigned_tags_set = set([tag['full_name'] for tag in assigned_tags])
|
||||
|
||||
for tag in tags:
|
||||
assigned = self.full_tag_name(tag) in assigned_tags_set
|
||||
|
||||
if assigned and action == 'unassign':
|
||||
tags_to_post.append(tag)
|
||||
elif (not assigned) and action == 'assign':
|
||||
tags_to_post.append(tag)
|
||||
|
||||
return tags_to_post
|
||||
|
||||
def assign_or_unassign_tags(self, tags, action):
|
||||
""" Perform assign/unassign action
|
||||
"""
|
||||
# get a list of tags needed to be changed
|
||||
tags_to_post = self.tags_to_update(tags, action)
|
||||
if not tags_to_post:
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="Tags already {action}ed, nothing to do".format(action=action))
|
||||
|
||||
# try to assign or unassign tags to resource
|
||||
url = '{resource_url}/tags'.format(resource_url=self.resource_url)
|
||||
try:
|
||||
response = self.client.post(url, action=action, resources=tags)
|
||||
except Exception as e:
|
||||
msg = "Failed to {action} tag: {error}".format(
|
||||
action=action,
|
||||
error=e)
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
# check all entities in result to be successful
|
||||
for result in response['results']:
|
||||
if not result['success']:
|
||||
msg = "Failed to {action}: {message}".format(
|
||||
action=action,
|
||||
message=result['message'])
|
||||
self.module.fail_json(msg=msg)
|
||||
|
||||
# successfully changed all needed tags
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="Successfully {action}ed tags".format(action=action))
|
||||
|
||||
|
||||
def main():
|
||||
actions = {'present': 'assign', 'absent': 'unassign', 'list': 'list'}
|
||||
argument_spec = dict(
|
||||
tags=dict(type='list'),
|
||||
resource_name=dict(required=True, type='str'),
|
||||
resource_type=dict(required=True, type='str',
|
||||
choices=manageiq_entities().keys()),
|
||||
state=dict(required=False, type='str',
|
||||
choices=['present', 'absent', 'list'], default='present'),
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_if=[
|
||||
('state', 'present', ['tags']),
|
||||
('state', 'absent', ['tags'])
|
||||
],
|
||||
)
|
||||
|
||||
tags = module.params['tags']
|
||||
resource_type_key = module.params['resource_type']
|
||||
resource_name = module.params['resource_name']
|
||||
state = module.params['state']
|
||||
|
||||
# get the action and resource type
|
||||
action = actions[state]
|
||||
resource_type = manageiq_entities()[resource_type_key]
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
|
||||
# query resource id, fail if resource does not exist
|
||||
resource_id = query_resource_id(manageiq, resource_type, resource_name)
|
||||
|
||||
manageiq_tags = ManageIQTags(manageiq, resource_type, resource_id)
|
||||
|
||||
if action == 'list':
|
||||
# return a list of current tags for this object
|
||||
current_tags = manageiq_tags.query_resource_tags()
|
||||
res_args = dict(changed=False, tags=current_tags)
|
||||
else:
|
||||
# assign or unassign the tags
|
||||
res_args = manageiq_tags.assign_or_unassign_tags(tags, action)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
555
plugins/modules/remote_management/manageiq/manageiq_tenant.py
Normal file
555
plugins/modules/remote_management/manageiq/manageiq_tenant.py
Normal file
@@ -0,0 +1,555 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2018, Evert Mulder (base on manageiq_user.py by Daniel Korn <korndaniel1@gmail.com>)
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
|
||||
module: manageiq_tenant
|
||||
|
||||
short_description: Management of tenants in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Evert Mulder (@evertmulder)
|
||||
description:
|
||||
- The manageiq_tenant module supports adding, updating and deleting tenants in ManageIQ.
|
||||
requirements:
|
||||
- manageiq-client
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - tenant should not exist, present - tenant should be.
|
||||
choices: ['absent', 'present']
|
||||
default: 'present'
|
||||
name:
|
||||
description:
|
||||
- The tenant name.
|
||||
required: true
|
||||
default: null
|
||||
description:
|
||||
description:
|
||||
- The tenant description.
|
||||
required: true
|
||||
default: null
|
||||
parent_id:
|
||||
description:
|
||||
- The id of the parent tenant. If not supplied the root tenant is used.
|
||||
- The C(parent_id) takes president over C(parent) when supplied
|
||||
required: false
|
||||
default: null
|
||||
parent:
|
||||
description:
|
||||
- The name of the parent tenant. If not supplied and no C(parent_id) is supplied the root tenant is used.
|
||||
required: false
|
||||
default: null
|
||||
quotas:
|
||||
description:
|
||||
- The tenant quotas.
|
||||
- All parameters case sensitive.
|
||||
- 'Valid attributes are:'
|
||||
- ' - C(cpu_allocated) (int): use null to remove the quota.'
|
||||
- ' - C(mem_allocated) (GB): use null to remove the quota.'
|
||||
- ' - C(storage_allocated) (GB): use null to remove the quota.'
|
||||
- ' - C(vms_allocated) (int): use null to remove the quota.'
|
||||
- ' - C(templates_allocated) (int): use null to remove the quota.'
|
||||
required: false
|
||||
default: null
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Update the root tenant in ManageIQ
|
||||
manageiq_tenant:
|
||||
name: 'My Company'
|
||||
description: 'My company name'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Create a tenant in ManageIQ
|
||||
manageiq_tenant:
|
||||
name: 'Dep1'
|
||||
description: 'Manufacturing department'
|
||||
parent_id: 1
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete a tenant in ManageIQ
|
||||
manageiq_tenant:
|
||||
state: 'absent'
|
||||
name: 'Dep1'
|
||||
parent_id: 1
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Set tenant quota for cpu_allocated, mem_allocated, remove quota for vms_allocated
|
||||
manageiq_tenant:
|
||||
name: 'Dep1'
|
||||
parent_id: 1
|
||||
quotas:
|
||||
- cpu_allocated: 100
|
||||
- mem_allocated: 50
|
||||
- vms_allocated: null
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
|
||||
- name: Delete a tenant in ManageIQ using a token
|
||||
manageiq_tenant:
|
||||
state: 'absent'
|
||||
name: 'Dep1'
|
||||
parent_id: 1
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
token: 'sometoken'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
tenant:
|
||||
description: The tenant.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
id:
|
||||
description: The tenant id
|
||||
returned: success
|
||||
type: int
|
||||
name:
|
||||
description: The tenant name
|
||||
returned: success
|
||||
type: str
|
||||
description:
|
||||
description: The tenant description
|
||||
returned: success
|
||||
type: str
|
||||
parent_id:
|
||||
description: The id of the parent tenant
|
||||
returned: success
|
||||
type: int
|
||||
quotas:
|
||||
description: List of tenant quotas
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
cpu_allocated: 100
|
||||
mem_allocated: 50
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
||||
|
||||
|
||||
class ManageIQTenant(object):
|
||||
"""
|
||||
Object to execute tenant 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
|
||||
|
||||
def tenant(self, name, parent_id, parent):
|
||||
""" Search for tenant object by name and parent_id or parent
|
||||
or the root tenant if no parent or parent_id is supplied.
|
||||
Returns:
|
||||
the parent tenant, None for the root tenant
|
||||
the tenant or None if tenant was not found.
|
||||
"""
|
||||
|
||||
if parent_id:
|
||||
parent_tenant_res = self.client.collections.tenants.find_by(id=parent_id)
|
||||
if not parent_tenant_res:
|
||||
self.module.fail_json(msg="Parent tenant with id '%s' not found in manageiq" % str(parent_id))
|
||||
parent_tenant = parent_tenant_res[0]
|
||||
tenants = self.client.collections.tenants.find_by(name=name)
|
||||
|
||||
for tenant in tenants:
|
||||
try:
|
||||
ancestry = tenant['ancestry']
|
||||
except AttributeError:
|
||||
ancestry = None
|
||||
|
||||
if ancestry:
|
||||
tenant_parent_id = int(ancestry.split("/")[-1])
|
||||
if int(tenant_parent_id) == parent_id:
|
||||
return parent_tenant, tenant
|
||||
|
||||
return parent_tenant, None
|
||||
else:
|
||||
if parent:
|
||||
parent_tenant_res = self.client.collections.tenants.find_by(name=parent)
|
||||
if not parent_tenant_res:
|
||||
self.module.fail_json(msg="Parent tenant '%s' not found in manageiq" % parent)
|
||||
|
||||
if len(parent_tenant_res) > 1:
|
||||
self.module.fail_json(msg="Multiple parent tenants not found in manageiq with name '%s" % parent)
|
||||
|
||||
parent_tenant = parent_tenant_res[0]
|
||||
parent_id = int(parent_tenant['id'])
|
||||
tenants = self.client.collections.tenants.find_by(name=name)
|
||||
|
||||
for tenant in tenants:
|
||||
try:
|
||||
ancestry = tenant['ancestry']
|
||||
except AttributeError:
|
||||
ancestry = None
|
||||
|
||||
if ancestry:
|
||||
tenant_parent_id = int(ancestry.split("/")[-1])
|
||||
if tenant_parent_id == parent_id:
|
||||
return parent_tenant, tenant
|
||||
|
||||
return parent_tenant, None
|
||||
else:
|
||||
# No parent or parent id supplied we select the root tenant
|
||||
return None, self.client.collections.tenants.find_by(ancestry=None)[0]
|
||||
|
||||
def compare_tenant(self, tenant, name, description):
|
||||
""" Compare tenant fields with new field values.
|
||||
|
||||
Returns:
|
||||
false if tenant fields have some difference from new fields, true o/w.
|
||||
"""
|
||||
found_difference = (
|
||||
(name and tenant['name'] != name) or
|
||||
(description and tenant['description'] != description)
|
||||
)
|
||||
|
||||
return not found_difference
|
||||
|
||||
def delete_tenant(self, tenant):
|
||||
""" Deletes a tenant from manageiq.
|
||||
|
||||
Returns:
|
||||
dict with `msg` and `changed`
|
||||
"""
|
||||
try:
|
||||
url = '%s/tenants/%s' % (self.api_url, tenant['id'])
|
||||
result = self.client.post(url, action='delete')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to delete tenant %s: %s" % (tenant['name'], str(e)))
|
||||
|
||||
if result['success'] is False:
|
||||
self.module.fail_json(msg=result['message'])
|
||||
|
||||
return dict(changed=True, msg=result['message'])
|
||||
|
||||
def edit_tenant(self, tenant, name, description):
|
||||
""" Edit a manageiq tenant.
|
||||
|
||||
Returns:
|
||||
dict with `msg` and `changed`
|
||||
"""
|
||||
resource = dict(name=name, description=description, use_config_for_attributes=False)
|
||||
|
||||
# check if we need to update ( compare_tenant is true is no difference found )
|
||||
if self.compare_tenant(tenant, name, description):
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="tenant %s is not changed." % tenant['name'],
|
||||
tenant=tenant['_data'])
|
||||
|
||||
# try to update tenant
|
||||
try:
|
||||
result = self.client.post(tenant['href'], action='edit', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to update tenant %s: %s" % (tenant['name'], str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully updated the tenant with id %s" % (tenant['id']))
|
||||
|
||||
def create_tenant(self, name, description, parent_tenant):
|
||||
""" Creates the tenant in manageiq.
|
||||
|
||||
Returns:
|
||||
dict with `msg`, `changed` and `tenant_id`
|
||||
"""
|
||||
parent_id = parent_tenant['id']
|
||||
# check for required arguments
|
||||
for key, value in dict(name=name, description=description, parent_id=parent_id).items():
|
||||
if value in (None, ''):
|
||||
self.module.fail_json(msg="missing required argument: %s" % key)
|
||||
|
||||
url = '%s/tenants' % self.api_url
|
||||
|
||||
resource = {'name': name, 'description': description, 'parent': {'id': parent_id}}
|
||||
|
||||
try:
|
||||
result = self.client.post(url, action='create', resource=resource)
|
||||
tenant_id = result['results'][0]['id']
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to create tenant %s: %s" % (name, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully created tenant '%s' with id '%s'" % (name, tenant_id),
|
||||
tenant_id=tenant_id)
|
||||
|
||||
def tenant_quota(self, tenant, quota_key):
|
||||
""" Search for tenant quota object by tenant and quota_key.
|
||||
Returns:
|
||||
the quota for the tenant, or None if the tenant quota was not found.
|
||||
"""
|
||||
|
||||
tenant_quotas = self.client.get("%s/quotas?expand=resources&filter[]=name=%s" % (tenant['href'], quota_key))
|
||||
|
||||
return tenant_quotas['resources']
|
||||
|
||||
def tenant_quotas(self, tenant):
|
||||
""" Search for tenant quotas object by tenant.
|
||||
Returns:
|
||||
the quotas for the tenant, or None if no tenant quotas were not found.
|
||||
"""
|
||||
|
||||
tenant_quotas = self.client.get("%s/quotas?expand=resources" % (tenant['href']))
|
||||
|
||||
return tenant_quotas['resources']
|
||||
|
||||
def update_tenant_quotas(self, tenant, quotas):
|
||||
""" Creates the tenant quotas in manageiq.
|
||||
|
||||
Returns:
|
||||
dict with `msg` and `changed`
|
||||
"""
|
||||
|
||||
changed = False
|
||||
messages = []
|
||||
for quota_key, quota_value in quotas.items():
|
||||
current_quota_filtered = self.tenant_quota(tenant, quota_key)
|
||||
if current_quota_filtered:
|
||||
current_quota = current_quota_filtered[0]
|
||||
else:
|
||||
current_quota = None
|
||||
|
||||
if quota_value:
|
||||
# Change the byte values to GB
|
||||
if quota_key in ['storage_allocated', 'mem_allocated']:
|
||||
quota_value_int = int(quota_value) * 1024 * 1024 * 1024
|
||||
else:
|
||||
quota_value_int = int(quota_value)
|
||||
if current_quota:
|
||||
res = self.edit_tenant_quota(tenant, current_quota, quota_key, quota_value_int)
|
||||
else:
|
||||
res = self.create_tenant_quota(tenant, quota_key, quota_value_int)
|
||||
else:
|
||||
if current_quota:
|
||||
res = self.delete_tenant_quota(tenant, current_quota)
|
||||
else:
|
||||
res = dict(changed=False, msg="tenant quota '%s' does not exist" % quota_key)
|
||||
|
||||
if res['changed']:
|
||||
changed = True
|
||||
|
||||
messages.append(res['msg'])
|
||||
|
||||
return dict(
|
||||
changed=changed,
|
||||
msg=', '.join(messages))
|
||||
|
||||
def edit_tenant_quota(self, tenant, current_quota, quota_key, quota_value):
|
||||
""" Update the tenant quotas in manageiq.
|
||||
|
||||
Returns:
|
||||
result
|
||||
"""
|
||||
|
||||
if current_quota['value'] == quota_value:
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="tenant quota %s already has value %s" % (quota_key, quota_value))
|
||||
else:
|
||||
|
||||
url = '%s/quotas/%s' % (tenant['href'], current_quota['id'])
|
||||
resource = {'value': quota_value}
|
||||
try:
|
||||
self.client.post(url, action='edit', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to update tenant quota %s: %s" % (quota_key, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully updated tenant quota %s" % quota_key)
|
||||
|
||||
def create_tenant_quota(self, tenant, quota_key, quota_value):
|
||||
""" Creates the tenant quotas in manageiq.
|
||||
|
||||
Returns:
|
||||
result
|
||||
"""
|
||||
url = '%s/quotas' % (tenant['href'])
|
||||
resource = {'name': quota_key, 'value': quota_value}
|
||||
try:
|
||||
self.client.post(url, action='create', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to create tenant quota %s: %s" % (quota_key, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully created tenant quota %s" % quota_key)
|
||||
|
||||
def delete_tenant_quota(self, tenant, quota):
|
||||
""" deletes the tenant quotas in manageiq.
|
||||
|
||||
Returns:
|
||||
result
|
||||
"""
|
||||
try:
|
||||
result = self.client.post(quota['href'], action='delete')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to delete tenant quota '%s': %s" % (quota['name'], str(e)))
|
||||
|
||||
return dict(changed=True, msg=result['message'])
|
||||
|
||||
def create_tenant_response(self, tenant, parent_tenant):
|
||||
""" Creates the ansible result object from a manageiq tenant entity
|
||||
|
||||
Returns:
|
||||
a dict with the tenant id, name, description, parent id,
|
||||
quota's
|
||||
"""
|
||||
tenant_quotas = self.create_tenant_quotas_response(tenant['tenant_quotas'])
|
||||
|
||||
try:
|
||||
ancestry = tenant['ancestry']
|
||||
tenant_parent_id = ancestry.split("/")[-1]
|
||||
except AttributeError:
|
||||
# The root tenant does not return the ancestry attribute
|
||||
tenant_parent_id = None
|
||||
|
||||
return dict(
|
||||
id=tenant['id'],
|
||||
name=tenant['name'],
|
||||
description=tenant['description'],
|
||||
parent_id=tenant_parent_id,
|
||||
quotas=tenant_quotas
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create_tenant_quotas_response(tenant_quotas):
|
||||
""" Creates the ansible result object from a manageiq tenant_quotas entity
|
||||
|
||||
Returns:
|
||||
a dict with the applied quotas, name and value
|
||||
"""
|
||||
|
||||
if not tenant_quotas:
|
||||
return {}
|
||||
|
||||
result = {}
|
||||
for quota in tenant_quotas:
|
||||
if quota['unit'] == 'bytes':
|
||||
value = float(quota['value']) / (1024 * 1024 * 1024)
|
||||
else:
|
||||
value = quota['value']
|
||||
result[quota['name']] = value
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(required=True, type='str'),
|
||||
description=dict(required=True, type='str'),
|
||||
parent_id=dict(required=False, type='int'),
|
||||
parent=dict(required=False, type='str'),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
quotas=dict(type='dict', default={})
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
description = module.params['description']
|
||||
parent_id = module.params['parent_id']
|
||||
parent = module.params['parent']
|
||||
state = module.params['state']
|
||||
quotas = module.params['quotas']
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
manageiq_tenant = ManageIQTenant(manageiq)
|
||||
|
||||
parent_tenant, tenant = manageiq_tenant.tenant(name, parent_id, parent)
|
||||
|
||||
# tenant should not exist
|
||||
if state == "absent":
|
||||
# if we have a tenant, delete it
|
||||
if tenant:
|
||||
res_args = manageiq_tenant.delete_tenant(tenant)
|
||||
# if we do not have a tenant, nothing to do
|
||||
else:
|
||||
if parent_id:
|
||||
msg = "tenant '%s' with parent_id %i does not exist in manageiq" % (name, parent_id)
|
||||
else:
|
||||
msg = "tenant '%s' with parent '%s' does not exist in manageiq" % (name, parent)
|
||||
|
||||
res_args = dict(
|
||||
changed=False,
|
||||
msg=msg)
|
||||
|
||||
# tenant should exist
|
||||
if state == "present":
|
||||
# if we have a tenant, edit it
|
||||
if tenant:
|
||||
res_args = manageiq_tenant.edit_tenant(tenant, name, description)
|
||||
|
||||
# if we do not have a tenant, create it
|
||||
else:
|
||||
res_args = manageiq_tenant.create_tenant(name, description, parent_tenant)
|
||||
tenant = manageiq.client.get_entity('tenants', res_args['tenant_id'])
|
||||
|
||||
# quotas as supplied and we have a tenant
|
||||
if quotas:
|
||||
tenant_quotas_res = manageiq_tenant.update_tenant_quotas(tenant, quotas)
|
||||
if tenant_quotas_res['changed']:
|
||||
res_args['changed'] = True
|
||||
res_args['tenant_quotas_msg'] = tenant_quotas_res['msg']
|
||||
|
||||
tenant.reload(expand='resources', attributes=['tenant_quotas'])
|
||||
res_args['tenant'] = manageiq_tenant.create_tenant_response(tenant, parent_tenant)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
329
plugins/modules/remote_management/manageiq/manageiq_user.py
Normal file
329
plugins/modules/remote_management/manageiq/manageiq_user.py
Normal file
@@ -0,0 +1,329 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2017, Daniel Korn <korndaniel1@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
|
||||
module: manageiq_user
|
||||
|
||||
short_description: Management of users in ManageIQ.
|
||||
extends_documentation_fragment:
|
||||
- community.general.manageiq
|
||||
|
||||
author: Daniel Korn (@dkorn)
|
||||
description:
|
||||
- The manageiq_user module supports adding, updating and deleting users in ManageIQ.
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- absent - user should not exist, present - user should be.
|
||||
choices: ['absent', 'present']
|
||||
default: 'present'
|
||||
userid:
|
||||
description:
|
||||
- The unique userid in manageiq, often mentioned as username.
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- The users' full name.
|
||||
password:
|
||||
description:
|
||||
- The users' password.
|
||||
group:
|
||||
description:
|
||||
- The name of the group to which the user belongs.
|
||||
email:
|
||||
description:
|
||||
- The users' E-mail address.
|
||||
update_password:
|
||||
default: always
|
||||
choices: ['always', 'on_create']
|
||||
description:
|
||||
- C(always) will update passwords unconditionally. C(on_create) will only set the password for a newly created user.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Create a new user in ManageIQ
|
||||
manageiq_user:
|
||||
userid: 'jdoe'
|
||||
name: 'Jane Doe'
|
||||
password: 'VerySecret'
|
||||
group: 'EvmGroup-user'
|
||||
email: 'jdoe@example.com'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Create a new user in ManageIQ using a token
|
||||
manageiq_user:
|
||||
userid: 'jdoe'
|
||||
name: 'Jane Doe'
|
||||
password: 'VerySecret'
|
||||
group: 'EvmGroup-user'
|
||||
email: 'jdoe@example.com'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
token: 'sometoken'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete a user in ManageIQ
|
||||
manageiq_user:
|
||||
state: 'absent'
|
||||
userid: 'jdoe'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Delete a user in ManageIQ using a token
|
||||
manageiq_user:
|
||||
state: 'absent'
|
||||
userid: 'jdoe'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
token: 'sometoken'
|
||||
validate_certs: False
|
||||
|
||||
- name: Update email of user in ManageIQ
|
||||
manageiq_user:
|
||||
userid: 'jdoe'
|
||||
email: 'jaustine@example.com'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
username: 'admin'
|
||||
password: 'smartvm'
|
||||
validate_certs: False
|
||||
|
||||
- name: Update email of user in ManageIQ using a token
|
||||
manageiq_user:
|
||||
userid: 'jdoe'
|
||||
email: 'jaustine@example.com'
|
||||
manageiq_connection:
|
||||
url: 'http://127.0.0.1:3000'
|
||||
token: 'sometoken'
|
||||
validate_certs: False
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.manageiq import ManageIQ, manageiq_argument_spec
|
||||
|
||||
|
||||
class ManageIQUser(object):
|
||||
"""
|
||||
Object to execute user 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
|
||||
|
||||
def group_id(self, description):
|
||||
""" Search for group id by group description.
|
||||
|
||||
Returns:
|
||||
the group id, or send a module Fail signal if group not found.
|
||||
"""
|
||||
group = self.manageiq.find_collection_resource_by('groups', description=description)
|
||||
if not group: # group doesn't exist
|
||||
self.module.fail_json(
|
||||
msg="group %s does not exist in manageiq" % (description))
|
||||
|
||||
return group['id']
|
||||
|
||||
def user(self, userid):
|
||||
""" Search for user object by userid.
|
||||
|
||||
Returns:
|
||||
the user, or None if user not found.
|
||||
"""
|
||||
return self.manageiq.find_collection_resource_by('users', userid=userid)
|
||||
|
||||
def compare_user(self, user, name, group_id, password, email):
|
||||
""" Compare user fields with new field values.
|
||||
|
||||
Returns:
|
||||
false if user fields have some difference from new fields, true o/w.
|
||||
"""
|
||||
found_difference = (
|
||||
(name and user['name'] != name) or
|
||||
(password is not None) or
|
||||
(email and user['email'] != email) or
|
||||
(group_id and user['current_group_id'] != group_id)
|
||||
)
|
||||
|
||||
return not found_difference
|
||||
|
||||
def delete_user(self, user):
|
||||
""" Deletes a user from manageiq.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
try:
|
||||
url = '%s/users/%s' % (self.api_url, user['id'])
|
||||
result = self.client.post(url, action='delete')
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to delete user %s: %s" % (user['userid'], str(e)))
|
||||
|
||||
return dict(changed=True, msg=result['message'])
|
||||
|
||||
def edit_user(self, user, name, group, password, email):
|
||||
""" Edit a user from manageiq.
|
||||
|
||||
Returns:
|
||||
a short message describing the operation executed.
|
||||
"""
|
||||
group_id = None
|
||||
url = '%s/users/%s' % (self.api_url, user['id'])
|
||||
|
||||
resource = dict(userid=user['userid'])
|
||||
if group is not None:
|
||||
group_id = self.group_id(group)
|
||||
resource['group'] = dict(id=group_id)
|
||||
if name is not None:
|
||||
resource['name'] = name
|
||||
if email is not None:
|
||||
resource['email'] = email
|
||||
|
||||
# if there is a password param, but 'update_password' is 'on_create'
|
||||
# then discard the password (since we're editing an existing user)
|
||||
if self.module.params['update_password'] == 'on_create':
|
||||
password = None
|
||||
if password is not None:
|
||||
resource['password'] = password
|
||||
|
||||
# check if we need to update ( compare_user is true is no difference found )
|
||||
if self.compare_user(user, name, group_id, password, email):
|
||||
return dict(
|
||||
changed=False,
|
||||
msg="user %s is not changed." % (user['userid']))
|
||||
|
||||
# try to update user
|
||||
try:
|
||||
result = self.client.post(url, action='edit', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to update user %s: %s" % (user['userid'], str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully updated the user %s: %s" % (user['userid'], result))
|
||||
|
||||
def create_user(self, userid, name, group, password, email):
|
||||
""" Creates the user in manageiq.
|
||||
|
||||
Returns:
|
||||
the created user id, name, created_on timestamp,
|
||||
updated_on timestamp, userid and current_group_id.
|
||||
"""
|
||||
# check for required arguments
|
||||
for key, value in dict(name=name, group=group, password=password).items():
|
||||
if value in (None, ''):
|
||||
self.module.fail_json(msg="missing required argument: %s" % (key))
|
||||
|
||||
group_id = self.group_id(group)
|
||||
url = '%s/users' % (self.api_url)
|
||||
|
||||
resource = {'userid': userid, 'name': name, 'password': password, 'group': {'id': group_id}}
|
||||
if email is not None:
|
||||
resource['email'] = email
|
||||
|
||||
# try to create a new user
|
||||
try:
|
||||
result = self.client.post(url, action='create', resource=resource)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="failed to create user %s: %s" % (userid, str(e)))
|
||||
|
||||
return dict(
|
||||
changed=True,
|
||||
msg="successfully created the user %s: %s" % (userid, result['results']))
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
userid=dict(required=True, type='str'),
|
||||
name=dict(),
|
||||
password=dict(no_log=True),
|
||||
group=dict(),
|
||||
email=dict(),
|
||||
state=dict(choices=['absent', 'present'], default='present'),
|
||||
update_password=dict(choices=['always', 'on_create'],
|
||||
default='always'),
|
||||
)
|
||||
# add the manageiq connection arguments to the arguments
|
||||
argument_spec.update(manageiq_argument_spec())
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
)
|
||||
|
||||
userid = module.params['userid']
|
||||
name = module.params['name']
|
||||
password = module.params['password']
|
||||
group = module.params['group']
|
||||
email = module.params['email']
|
||||
state = module.params['state']
|
||||
|
||||
manageiq = ManageIQ(module)
|
||||
manageiq_user = ManageIQUser(manageiq)
|
||||
|
||||
user = manageiq_user.user(userid)
|
||||
|
||||
# user should not exist
|
||||
if state == "absent":
|
||||
# if we have a user, delete it
|
||||
if user:
|
||||
res_args = manageiq_user.delete_user(user)
|
||||
# if we do not have a user, nothing to do
|
||||
else:
|
||||
res_args = dict(
|
||||
changed=False,
|
||||
msg="user %s: does not exist in manageiq" % (userid))
|
||||
|
||||
# user should exist
|
||||
if state == "present":
|
||||
# if we have a user, edit it
|
||||
if user:
|
||||
res_args = manageiq_user.edit_user(user, name, group, password, email)
|
||||
# if we do not have a user, create it
|
||||
else:
|
||||
res_args = manageiq_user.create_user(userid, name, group, password, email)
|
||||
|
||||
module.exit_json(**res_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user