Add the object_containers_info module
This adds a module for getting information on one or more object storage containers from OpenStack. The following options are supported: * name - Get details for a single container by name. When this parameter is defined a single container is returned, with extra metadata available (with the same keys and value as in the existing object_container module). * prefix - Search for and return a list of containers by prefix. When searching for containers, only a subset of metadata values are available. When no options are specified, all containers in the project are returned, with the same metadata available as when the prefix option is used. Change-Id: I8ba434a86050f72d8ce85c9e98731f6ef552fc79
This commit is contained in:
parent
fef560eb5b
commit
5494d153b1
37
ci/roles/object_containers_info/defaults/main.yml
Normal file
37
ci/roles/object_containers_info/defaults/main.yml
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
|
||||
test_container_unprefixed_name: ansible-test-container
|
||||
test_container_prefixed_prefix: ansible-prefixed-test-container
|
||||
test_container_prefixed_num: 2
|
||||
|
||||
test_object_data: "Hello, world!"
|
||||
|
||||
expected_fields_single:
|
||||
- bytes
|
||||
- bytes_used
|
||||
- content_type
|
||||
- count
|
||||
- history_location
|
||||
- id
|
||||
- if_none_match
|
||||
- is_content_type_detected
|
||||
- is_newest
|
||||
- meta_temp_url_key
|
||||
- meta_temp_url_key_2
|
||||
- name
|
||||
- object_count
|
||||
- read_ACL
|
||||
- storage_policy
|
||||
- sync_key
|
||||
- sync_to
|
||||
- timestamp
|
||||
- versions_location
|
||||
- write_ACL
|
||||
|
||||
expected_fields_multiple:
|
||||
- bytes
|
||||
- bytes_used
|
||||
- count
|
||||
- id
|
||||
- name
|
||||
- object_count
|
124
ci/roles/object_containers_info/tasks/main.yml
Normal file
124
ci/roles/object_containers_info/tasks/main.yml
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
|
||||
- name: Generate list of containers to create
|
||||
ansible.builtin.set_fact:
|
||||
all_test_containers: >-
|
||||
{{
|
||||
[test_container_unprefixed_name]
|
||||
+ (
|
||||
[test_container_prefixed_prefix + '-']
|
||||
| product(range(test_container_prefixed_num) | map('string'))
|
||||
| map('join', '')
|
||||
)
|
||||
}}
|
||||
|
||||
- name: Run checks
|
||||
block:
|
||||
|
||||
- name: Create all containers
|
||||
openstack.cloud.object_container:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ item }}"
|
||||
read_ACL: ".r:*,.rlistings"
|
||||
loop: "{{ all_test_containers }}"
|
||||
|
||||
- name: Create an object in all containers
|
||||
openstack.cloud.object:
|
||||
cloud: "{{ cloud }}"
|
||||
container: "{{ item }}"
|
||||
name: hello.txt
|
||||
data: "{{ test_object_data }}"
|
||||
loop: "{{ all_test_containers }}"
|
||||
|
||||
- name: Fetch single containers by name
|
||||
openstack.cloud.object_containers_info:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ item }}"
|
||||
register: single_containers
|
||||
loop: "{{ all_test_containers }}"
|
||||
|
||||
- name: Check that all fields are returned for single containers
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (item.containers | length) == 1
|
||||
- item.containers[0].name == item.item
|
||||
- item.containers[0].bytes == (test_object_data | length)
|
||||
- item.containers[0].read_ACL == ".r:*,.rlistings"
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- (expected_fields_single | difference(item.containers[0].keys()) | length) == 0
|
||||
quiet: true
|
||||
loop: "{{ single_containers.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item }}"
|
||||
|
||||
- name: Fetch multiple containers by prefix
|
||||
openstack.cloud.object_containers_info:
|
||||
cloud: "{{ cloud }}"
|
||||
prefix: "{{ test_container_prefixed_prefix }}"
|
||||
register: multiple_containers
|
||||
|
||||
- name: Check that the correct number of prefixed containers were returned
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (multiple_containers.containers | length) == test_container_prefixed_num
|
||||
fail_msg: >-
|
||||
Incorrect number of containers found
|
||||
(found {{ multiple_containers.containers | length }},
|
||||
expected {{ test_container_prefixed_num }})
|
||||
quiet: true
|
||||
|
||||
- name: Check that all prefixed containers exist
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- >-
|
||||
(test_container_prefixed_prefix + '-' + (item | string))
|
||||
in (multiple_containers.containers | map(attribute='name'))
|
||||
fail_msg: "Container not found: {{ test_container_prefixed_prefix + '-' + (item | string) }}"
|
||||
quiet: true
|
||||
loop: "{{ range(test_container_prefixed_num) | list }}"
|
||||
loop_control:
|
||||
label: "{{ test_container_prefixed_prefix + '-' + (item | string) }}"
|
||||
|
||||
- name: Check that the expected fields are returned for all prefixed containers
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item.name.startswith(test_container_prefixed_prefix)
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- (expected_fields_multiple | difference(item.keys()) | length) == 0
|
||||
quiet: true
|
||||
loop: "{{ multiple_containers.containers | sort(attribute='name') }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
|
||||
- name: Fetch all containers
|
||||
openstack.cloud.object_containers_info:
|
||||
cloud: "{{ cloud }}"
|
||||
register: all_containers
|
||||
|
||||
- name: Check that all expected containers were returned
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item in (all_containers.containers | map(attribute='name'))
|
||||
fail_msg: "Container not found: {{ item }}"
|
||||
quiet: true
|
||||
loop: "{{ all_test_containers }}"
|
||||
|
||||
- name: Check that the expected fields are returned for all containers
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
# allow new fields to be introduced but prevent fields from being removed
|
||||
- (expected_fields_multiple | difference(item.keys()) | length) == 0
|
||||
quiet: true
|
||||
loop: "{{ all_containers.containers | selectattr('name', 'in', all_test_containers) }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}"
|
||||
|
||||
always:
|
||||
|
||||
- name: Delete all containers
|
||||
openstack.cloud.object_container:
|
||||
cloud: "{{ cloud }}"
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
delete_with_all_objects: true
|
||||
loop: "{{ all_test_containers }}"
|
@ -35,6 +35,7 @@
|
||||
- { role: neutron_rbac_policy, tags: neutron_rbac_policy }
|
||||
- { role: object, tags: object }
|
||||
- { role: object_container, tags: object_container }
|
||||
- { role: object_containers_info, tags: object_containers_info }
|
||||
- { role: port, tags: port }
|
||||
- { role: trait, tags: trait }
|
||||
- { role: trunk, tags: trunk }
|
||||
|
@ -56,6 +56,7 @@ action_groups:
|
||||
- neutron_rbac_policy
|
||||
- object
|
||||
- object_container
|
||||
- object_containers_info
|
||||
- port
|
||||
- port_info
|
||||
- project
|
||||
|
202
plugins/modules/object_containers_info.py
Normal file
202
plugins/modules/object_containers_info.py
Normal file
@ -0,0 +1,202 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024 Catalyst Cloud Limited
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
---
|
||||
module: object_containers_info
|
||||
short_description: Fetch container info from the OpenStack Swift service.
|
||||
author: OpenStack Ansible SIG
|
||||
description:
|
||||
- Fetch container info from the OpenStack Swift service.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the container
|
||||
type: str
|
||||
aliases: ["container"]
|
||||
prefix:
|
||||
description:
|
||||
- Filter containers by prefix
|
||||
type: str
|
||||
extends_documentation_fragment:
|
||||
- openstack.cloud.openstack
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: List all containers existing on the project
|
||||
openstack.cloud.object_containers_info:
|
||||
|
||||
- name: Retrive a single container by name
|
||||
openstack.cloud.object_containers_info:
|
||||
name: test-container
|
||||
|
||||
- name: Retrieve and filter containers by prefix
|
||||
openstack.cloud.object_containers_info:
|
||||
prefix: test-
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
containers:
|
||||
description: List of dictionaries describing matching containers.
|
||||
returned: always
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
bytes:
|
||||
description: The total number of bytes that are stored in Object Storage
|
||||
for the container.
|
||||
type: int
|
||||
sample: 5449
|
||||
bytes_used:
|
||||
description: The count of bytes used in total.
|
||||
type: int
|
||||
sample: 5449
|
||||
content_type:
|
||||
description: The MIME type of the list of names.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
count:
|
||||
description: The number of objects in the container.
|
||||
type: int
|
||||
sample: 1
|
||||
history_location:
|
||||
description: Enables versioning on the container.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
id:
|
||||
description: The ID of the container. Equals I(name).
|
||||
type: str
|
||||
sample: "otc"
|
||||
if_none_match:
|
||||
description: "In combination with C(Expect: 100-Continue), specify an
|
||||
C(If-None-Match: *) header to query whether the server
|
||||
already has a copy of the object before any data is sent.
|
||||
Only set when searching for a container by name."
|
||||
type: str
|
||||
sample: null
|
||||
is_content_type_detected:
|
||||
description: If set to C(true), Object Storage guesses the content type
|
||||
based on the file extension and ignores the value sent in
|
||||
the Content-Type header, if present.
|
||||
Only fetched when searching for a container by name.
|
||||
type: bool
|
||||
sample: null
|
||||
is_newest:
|
||||
description: If set to True, Object Storage queries all replicas to
|
||||
return the most recent one. If you omit this header, Object
|
||||
Storage responds faster after it finds one valid replica.
|
||||
Because setting this header to True is more expensive for
|
||||
the back end, use it only when it is absolutely needed.
|
||||
Only fetched when searching for a container by name.
|
||||
type: bool
|
||||
sample: null
|
||||
meta_temp_url_key:
|
||||
description: The secret key value for temporary URLs. If not set,
|
||||
this header is not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
meta_temp_url_key_2:
|
||||
description: A second secret key value for temporary URLs. If not set,
|
||||
this header is not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
name:
|
||||
description: The name of the container.
|
||||
type: str
|
||||
sample: "otc"
|
||||
object_count:
|
||||
description: The number of objects.
|
||||
type: int
|
||||
sample: 1
|
||||
read_ACL:
|
||||
description: The ACL that grants read access. If not set, this header is
|
||||
not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
storage_policy:
|
||||
description: Storage policy used by the container. It is not possible to
|
||||
change policy of an existing container.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
sync_key:
|
||||
description: The secret key for container synchronization. If not set,
|
||||
this header is not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
sync_to:
|
||||
description: The destination for container synchronization. If not set,
|
||||
this header is not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
timestamp:
|
||||
description: The timestamp of the transaction.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
versions_location:
|
||||
description: Enables versioning on this container. The value is the name
|
||||
of another container. You must UTF-8-encode and then
|
||||
URL-encode the name before you include it in the header. To
|
||||
disable versioning, set the header to an empty string.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
write_ACL:
|
||||
description: The ACL that grants write access. If not set, this header is
|
||||
not returned by this operation.
|
||||
Only fetched when searching for a container by name.
|
||||
type: str
|
||||
sample: null
|
||||
"""
|
||||
|
||||
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
||||
|
||||
|
||||
class ObjectContainersInfoModule(OpenStackModule):
|
||||
argument_spec = dict(
|
||||
name=dict(aliases=["container"]),
|
||||
prefix=dict(),
|
||||
)
|
||||
|
||||
module_kwargs = dict(
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
if self.params["name"]:
|
||||
containers = [
|
||||
(
|
||||
self.conn.object_store.get_container_metadata(
|
||||
self.params["name"],
|
||||
).to_dict(computed=False)
|
||||
),
|
||||
]
|
||||
else:
|
||||
query = {}
|
||||
if self.params["prefix"]:
|
||||
query["prefix"] = self.params["prefix"]
|
||||
containers = [
|
||||
c.to_dict(computed=False)
|
||||
for c in self.conn.object_store.containers(**query)
|
||||
]
|
||||
self.exit(changed=False, containers=containers)
|
||||
|
||||
|
||||
def main():
|
||||
module = ObjectContainersInfoModule()
|
||||
module()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user