1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 07:51:50 +00:00
community.general/plugins/modules/manageiq_tenant.py
Felix Fontein 236b9c0e04
Sort imports with ruff check --fix (#11400)
Sort imports with ruff check --fix.
2026-01-09 07:40:58 +01:00

523 lines
17 KiB
Python

#!/usr/bin/python
# Copyright (c) 2018, Evert Mulder (base on manageiq_user.py by Daniel Korn <korndaniel1@gmail.com>)
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: manageiq_tenant
short_description: Management of tenants in ManageIQ
extends_documentation_fragment:
- community.general.manageiq
- community.general.attributes
author: Evert Mulder (@evertmulder)
description:
- The manageiq_tenant module supports adding, updating and deleting tenants in ManageIQ.
requirements:
- manageiq-client
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
state:
type: str
description:
- V(absent) - tenant should not exist,
- V(present) - tenant should be.
choices: ['absent', 'present']
default: 'present'
name:
type: str
description:
- The tenant name.
required: true
default:
description:
type: str
description:
- The tenant description.
required: true
default:
parent_id:
type: int
description:
- The ID of the parent tenant. If not supplied the root tenant is used.
- The O(parent_id) takes president over O(parent) when supplied.
default:
parent:
type: str
description:
- The name of the parent tenant. If not supplied and no O(parent_id) is supplied the root tenant is used.
default:
quotas:
type: dict
description:
- The tenant quotas.
- All parameters case sensitive.
- 'Valid attributes are:'
- '- V(cpu_allocated) (int): use null to remove the quota.'
- '- V(mem_allocated) (GB): use null to remove the quota.'
- '- V(storage_allocated) (GB): use null to remove the quota.'
- '- V(vms_allocated) (int): use null to remove the quota.'
- '- V(templates_allocated) (int): use null to remove the quota.'
default: {}
"""
EXAMPLES = r"""
- name: Update the root tenant in ManageIQ
community.general.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 # only do this when you trust the network!
- name: Create a tenant in ManageIQ
community.general.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 # only do this when you trust the network!
- name: Delete a tenant in ManageIQ
community.general.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 # only do this when you trust the network!
- name: Set tenant quota for cpu_allocated, mem_allocated, remove quota for vms_allocated
community.general.manageiq_tenant:
name: 'Dep1'
parent_id: 1
quotas:
- cpu_allocated: 100
- mem_allocated: 50
- vms_allocated:
manageiq_connection:
url: 'http://127.0.0.1:3000'
username: 'admin'
password: 'smartvm'
validate_certs: false # only do this when you trust the network!
- name: Delete a tenant in ManageIQ using a token
community.general.manageiq_tenant:
state: 'absent'
name: 'Dep1'
parent_id: 1
manageiq_connection:
url: 'http://127.0.0.1:3000'
token: 'sometoken'
validate_certs: false # only do this when you trust the network!
"""
RETURN = r"""
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 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=f"Parent tenant with id '{parent_id}' not found in manageiq")
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=f"Parent tenant '{parent}' not found in manageiq")
if len(parent_tenant_res) > 1:
self.module.fail_json(msg=f"Multiple parent tenants not found in manageiq with name '{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 = f"{self.api_url}/tenants/{tenant['id']}"
result = self.client.post(url, action="delete")
except Exception as e:
self.module.fail_json(msg=f"failed to delete tenant {tenant['name']}: {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=f"tenant {tenant['name']} is not changed.", tenant=tenant["_data"])
# try to update tenant
try:
self.client.post(tenant["href"], action="edit", resource=resource)
except Exception as e:
self.module.fail_json(msg=f"failed to update tenant {tenant['name']}: {e}")
return dict(changed=True, msg=f"successfully updated the tenant with id {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=f"missing required argument: {key}")
url = f"{self.api_url}/tenants"
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=f"failed to create tenant {name}: {e}")
return dict(
changed=True, msg=f"successfully created tenant '{name}' with id '{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(f"{tenant['href']}/quotas?expand=resources&filter[]=name={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(f"{tenant['href']}/quotas?expand=resources")
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=f"tenant quota '{quota_key}' does not exist")
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=f"tenant quota {quota_key} already has value {quota_value}")
else:
url = f"{tenant['href']}/quotas/{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=f"failed to update tenant quota {quota_key}: {e}")
return dict(changed=True, msg=f"successfully updated tenant quota {quota_key}")
def create_tenant_quota(self, tenant, quota_key, quota_value):
"""Creates the tenant quotas in manageiq.
Returns:
result
"""
url = f"{tenant['href']}/quotas"
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=f"failed to create tenant quota {quota_key}: {e}")
return dict(changed=True, msg=f"successfully created tenant quota {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=f"failed to delete tenant quota '{quota['name']}': {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(type="int"),
parent=dict(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 = f"tenant '{name}' with parent_id {int(parent_id)} does not exist in manageiq"
else:
msg = f"tenant '{name}' with parent '{parent}' does not exist in manageiq"
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()