Merge "Add application_credential module"
This commit is contained in:
commit
536fe731f7
9
ci/roles/application_credential/defaults/main.yml
Normal file
9
ci/roles/application_credential/defaults/main.yml
Normal file
@ -0,0 +1,9 @@
|
||||
expected_fields:
|
||||
- description
|
||||
- expires_at
|
||||
- id
|
||||
- name
|
||||
- project_id
|
||||
- roles
|
||||
- secret
|
||||
- unrestricted
|
61
ci/roles/application_credential/tasks/main.yml
Normal file
61
ci/roles/application_credential/tasks/main.yml
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
|
||||
- name: Create application credentials
|
||||
openstack.cloud.application_credential:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_creds
|
||||
description: dummy description
|
||||
register: appcred
|
||||
|
||||
- name: Assert return values of application_credential module
|
||||
assert:
|
||||
that:
|
||||
- appcred is changed
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- expected_fields|difference(appcred.application_credential.keys())|length == 0
|
||||
|
||||
- name: Create the application credential again
|
||||
openstack.cloud.application_credential:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_creds
|
||||
description: dummy description
|
||||
register: appcred
|
||||
|
||||
- name: Assert return values of ansible_credential module
|
||||
assert:
|
||||
that:
|
||||
# credentials are immutable so creating twice will cause delete and create
|
||||
- appcred is changed
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- expected_fields|difference(appcred.application_credential.keys())|length == 0
|
||||
|
||||
- name: Update the application credential again
|
||||
openstack.cloud.application_credential:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_creds
|
||||
description: new description
|
||||
register: appcred
|
||||
|
||||
- name: Assert application credential changed
|
||||
assert:
|
||||
that:
|
||||
- appcred is changed
|
||||
- appcred.application_credential.description == 'new description'
|
||||
|
||||
- name: Get list of all keypairs using application credential
|
||||
openstack.cloud.keypair_info:
|
||||
cloud: "{{ appcred.cloud }}"
|
||||
|
||||
- name: Delete application credential
|
||||
openstack.cloud.application_credential:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_creds
|
||||
register: appcred
|
||||
|
||||
- name: Assert application credential changed
|
||||
assert:
|
||||
that: appcred is changed
|
@ -5,6 +5,7 @@
|
||||
|
||||
roles:
|
||||
- { role: address_scope, tags: address_scope }
|
||||
- { role: application_credential, tags: application_credential }
|
||||
- { role: auth, tags: auth }
|
||||
- { role: catalog_service, tags: catalog_service }
|
||||
- { role: coe_cluster, tags: coe_cluster }
|
||||
|
332
plugins/modules/application_credential.py
Normal file
332
plugins/modules/application_credential.py
Normal file
@ -0,0 +1,332 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024 Red Hat, Inc.
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: application_credential
|
||||
short_description: Manage OpenStack Identity (Keystone) application credentials
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Create or delete an OpenStack Identity (Keystone) application credential.
|
||||
- When the secret parameter is not set a secret will be generated and returned
|
||||
- in the response. Existing credentials cannot be modified so running this module
|
||||
- against an existing credential will result in it being deleted and recreated.
|
||||
- This needs to be taken into account when the secret is generated, as the secret
|
||||
- will change on each run of the module.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the application credential.
|
||||
required: true
|
||||
type: str
|
||||
description:
|
||||
description:
|
||||
- Application credential description.
|
||||
type: str
|
||||
secret:
|
||||
description:
|
||||
- Secret to use for authentication
|
||||
- (if not provided, one will be generated).
|
||||
type: str
|
||||
roles:
|
||||
description:
|
||||
- Roles to authorize (name or ID).
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description: Name of role
|
||||
type: str
|
||||
id:
|
||||
description: ID of role
|
||||
type: str
|
||||
domain_id:
|
||||
description: Domain ID
|
||||
type: str
|
||||
expires_at:
|
||||
description:
|
||||
- Sets an expiration date for the application credential,
|
||||
- format of YYYY-mm-ddTHH:MM:SS
|
||||
- (if not provided, the application credential will not expire).
|
||||
type: str
|
||||
unrestricted:
|
||||
description:
|
||||
- Enable application credential to create and delete other application
|
||||
- credentials and trusts (this is potentially dangerous behavior and is
|
||||
- disabled by default).
|
||||
default: false
|
||||
type: bool
|
||||
access_rules:
|
||||
description:
|
||||
- List of access rules, each containing a request method, path, and service.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
service:
|
||||
description: Name of service endpoint
|
||||
type: str
|
||||
required: true
|
||||
path:
|
||||
description: Path portion of access URL
|
||||
type: str
|
||||
required: true
|
||||
method:
|
||||
description: HTTP method
|
||||
type: str
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Should the resource be present or absent.
|
||||
- Application credentials are immutable so running with an existing present
|
||||
- credential will result in the credential being deleted and recreated.
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
type: str
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Create application credential
|
||||
openstack.cloud.application_credential:
|
||||
cloud: mycloud
|
||||
description: demodescription
|
||||
name: democreds
|
||||
state: present
|
||||
|
||||
- name: Create application credential with expiration, access rules and roles
|
||||
openstack.cloud.application_credential:
|
||||
cloud: mycloud
|
||||
description: demodescription
|
||||
name: democreds
|
||||
access_rules:
|
||||
- service: "compute"
|
||||
path: "/v2.1/servers"
|
||||
method: "GET"
|
||||
expires_at: "2024-02-29T09:29:59"
|
||||
roles:
|
||||
- name: Member
|
||||
state: present
|
||||
|
||||
- name: Delete application credential
|
||||
openstack.cloud.application_credential:
|
||||
cloud: mycloud
|
||||
name: democreds
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
application_credential:
|
||||
description: Dictionary describing the project.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: dict
|
||||
contains:
|
||||
id:
|
||||
description: The ID of the application credential.
|
||||
type: str
|
||||
sample: "2e73d1b4f0cb473f920bd54dfce3c26d"
|
||||
name:
|
||||
description: The name of the application credential.
|
||||
type: str
|
||||
sample: "appcreds"
|
||||
secret:
|
||||
description: Secret to use for authentication
|
||||
(if not provided, returns the generated value).
|
||||
type: str
|
||||
sample: "JxE7LajLY75NZgDH1hfu0N_6xS9hQ-Af40W3"
|
||||
description:
|
||||
description: A description of the application credential's purpose.
|
||||
type: str
|
||||
sample: "App credential"
|
||||
expires_at:
|
||||
description: The expiration time of the application credential in UTC,
|
||||
if one was specified.
|
||||
type: str
|
||||
sample: "2024-02-29T09:29:59.000000"
|
||||
project_id:
|
||||
description: The ID of the project the application credential was created
|
||||
for and that authentication requests using this application
|
||||
credential will be scoped to.
|
||||
type: str
|
||||
sample: "4b633c451ac74233be3721a3635275e5"
|
||||
roles:
|
||||
description: A list of one or more roles that this application credential
|
||||
has associated with its project. A token using this application
|
||||
credential will have these same roles.
|
||||
type: list
|
||||
elements: dict
|
||||
sample: [{"name": "Member"}]
|
||||
access_rules:
|
||||
description: A list of access_rules objects
|
||||
type: list
|
||||
elements: dict
|
||||
sample:
|
||||
- id: "edecb6c791d541a3b458199858470d20"
|
||||
service: "compute"
|
||||
path: "/v2.1/servers"
|
||||
method: "GET"
|
||||
unrestricted:
|
||||
description: A flag indicating whether the application credential may be
|
||||
used for creation or destruction of other application credentials
|
||||
or trusts.
|
||||
type: bool
|
||||
cloud:
|
||||
description: The current cloud config with the username and password replaced
|
||||
with the name and secret of the application credential. This
|
||||
can be passed to the cloud parameter of other tasks, or written
|
||||
to an openstack cloud config file.
|
||||
returned: On success when I(state) is C(present).
|
||||
type: dict
|
||||
sample:
|
||||
auth_type: "v3applicationcredential"
|
||||
auth:
|
||||
auth_url: "https://192.0.2.1/identity"
|
||||
application_credential_secret: "JxE7LajLY75NZgDH1hfu0N_6xS9hQ-Af40W3"
|
||||
application_credential_id: "3e73d1b4f0cb473f920bd54dfce3c26d"
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
|
||||
OpenStackModule,
|
||||
)
|
||||
|
||||
try:
|
||||
import openstack.config
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class IdentityApplicationCredentialModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
description=dict(),
|
||||
secret=dict(no_log=True),
|
||||
roles=dict(
|
||||
type="list",
|
||||
elements="dict",
|
||||
options=dict(name=dict(), id=dict(), domain_id=dict()),
|
||||
),
|
||||
expires_at=dict(),
|
||||
unrestricted=dict(type="bool", default=False),
|
||||
access_rules=dict(
|
||||
type="list",
|
||||
elements="dict",
|
||||
options=dict(
|
||||
service=dict(required=True),
|
||||
path=dict(required=True),
|
||||
method=dict(required=True),
|
||||
),
|
||||
),
|
||||
state=dict(default="present", choices=["absent", "present"]),
|
||||
)
|
||||
module_kwargs = dict()
|
||||
cloud = None
|
||||
|
||||
def openstack_cloud_from_module(self):
|
||||
# Fetch cloud param before it is popped
|
||||
self.cloud = self.params["cloud"]
|
||||
return OpenStackModule.openstack_cloud_from_module(self)
|
||||
|
||||
def run(self):
|
||||
state = self.params["state"]
|
||||
|
||||
creds = self._find()
|
||||
|
||||
if state == "present" and not creds:
|
||||
# Create creds
|
||||
creds = self._create().to_dict(computed=False)
|
||||
cloud_config = self._get_cloud_config(creds)
|
||||
self.exit_json(
|
||||
changed=True, application_credential=creds, cloud=cloud_config
|
||||
)
|
||||
|
||||
elif state == "present" and creds:
|
||||
# Recreate immutable creds
|
||||
self._delete(creds)
|
||||
creds = self._create().to_dict(computed=False)
|
||||
cloud_config = self._get_cloud_config(creds)
|
||||
self.exit_json(
|
||||
changed=True, application_credential=creds, cloud=cloud_config
|
||||
)
|
||||
|
||||
elif state == "absent" and creds:
|
||||
# Delete creds
|
||||
self._delete(creds)
|
||||
self.exit_json(changed=True)
|
||||
|
||||
elif state == "absent" and not creds:
|
||||
# Do nothing
|
||||
self.exit_json(changed=False)
|
||||
|
||||
def _get_user_id(self):
|
||||
return self.conn.session.get_user_id()
|
||||
|
||||
def _create(self):
|
||||
kwargs = dict(
|
||||
(k, self.params[k])
|
||||
for k in [
|
||||
"name",
|
||||
"description",
|
||||
"secret",
|
||||
"expires_at",
|
||||
"unrestricted",
|
||||
"access_rules",
|
||||
]
|
||||
if self.params[k] is not None
|
||||
)
|
||||
|
||||
roles = self.params["roles"]
|
||||
if roles:
|
||||
kwroles = []
|
||||
for role in roles:
|
||||
kwroles.append(
|
||||
dict(
|
||||
(k, role[k])
|
||||
for k in ["name", "id", "domain_id"]
|
||||
if role[k] is not None
|
||||
)
|
||||
)
|
||||
kwargs["roles"] = kwroles
|
||||
|
||||
kwargs["user"] = self._get_user_id()
|
||||
creds = self.conn.identity.create_application_credential(**kwargs)
|
||||
return creds
|
||||
|
||||
def _get_cloud_config(self, creds):
|
||||
cloud_region = openstack.config.OpenStackConfig().get_one(self.cloud)
|
||||
|
||||
conf = cloud_region.config
|
||||
cloud_config = copy.deepcopy(conf)
|
||||
cloud_config["auth_type"] = "v3applicationcredential"
|
||||
cloud_config["auth"] = {
|
||||
"application_credential_id": creds["id"],
|
||||
"application_credential_secret": creds["secret"],
|
||||
"auth_url": conf["auth"]["auth_url"],
|
||||
}
|
||||
|
||||
return cloud_config
|
||||
|
||||
def _delete(self, creds):
|
||||
user = self._get_user_id()
|
||||
self.conn.identity.delete_application_credential(user, creds.id)
|
||||
|
||||
def _find(self):
|
||||
name = self.params["name"]
|
||||
user = self._get_user_id()
|
||||
return self.conn.identity.find_application_credential(
|
||||
user=user, name_or_id=name
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
module = IdentityApplicationCredentialModule()
|
||||
module()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user