Initial commit

This commit is contained in:
Ansible Core Team
2020-03-09 09:11:07 +00:00
commit aebc1b03fd
4861 changed files with 812621 additions and 0 deletions

View File

@@ -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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()

View 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()