Skip to content

Commit 5494d15

Browse files
author
Callum Dickinson
committed
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
1 parent fef560e commit 5494d15

File tree

5 files changed

+365
-0
lines changed

5 files changed

+365
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
3+
test_container_unprefixed_name: ansible-test-container
4+
test_container_prefixed_prefix: ansible-prefixed-test-container
5+
test_container_prefixed_num: 2
6+
7+
test_object_data: "Hello, world!"
8+
9+
expected_fields_single:
10+
- bytes
11+
- bytes_used
12+
- content_type
13+
- count
14+
- history_location
15+
- id
16+
- if_none_match
17+
- is_content_type_detected
18+
- is_newest
19+
- meta_temp_url_key
20+
- meta_temp_url_key_2
21+
- name
22+
- object_count
23+
- read_ACL
24+
- storage_policy
25+
- sync_key
26+
- sync_to
27+
- timestamp
28+
- versions_location
29+
- write_ACL
30+
31+
expected_fields_multiple:
32+
- bytes
33+
- bytes_used
34+
- count
35+
- id
36+
- name
37+
- object_count
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
3+
- name: Generate list of containers to create
4+
ansible.builtin.set_fact:
5+
all_test_containers: >-
6+
{{
7+
[test_container_unprefixed_name]
8+
+ (
9+
[test_container_prefixed_prefix + '-']
10+
| product(range(test_container_prefixed_num) | map('string'))
11+
| map('join', '')
12+
)
13+
}}
14+
15+
- name: Run checks
16+
block:
17+
18+
- name: Create all containers
19+
openstack.cloud.object_container:
20+
cloud: "{{ cloud }}"
21+
name: "{{ item }}"
22+
read_ACL: ".r:*,.rlistings"
23+
loop: "{{ all_test_containers }}"
24+
25+
- name: Create an object in all containers
26+
openstack.cloud.object:
27+
cloud: "{{ cloud }}"
28+
container: "{{ item }}"
29+
name: hello.txt
30+
data: "{{ test_object_data }}"
31+
loop: "{{ all_test_containers }}"
32+
33+
- name: Fetch single containers by name
34+
openstack.cloud.object_containers_info:
35+
cloud: "{{ cloud }}"
36+
name: "{{ item }}"
37+
register: single_containers
38+
loop: "{{ all_test_containers }}"
39+
40+
- name: Check that all fields are returned for single containers
41+
ansible.builtin.assert:
42+
that:
43+
- (item.containers | length) == 1
44+
- item.containers[0].name == item.item
45+
- item.containers[0].bytes == (test_object_data | length)
46+
- item.containers[0].read_ACL == ".r:*,.rlistings"
47+
# allow new fields to be introduced but prevent fields from being removed
48+
- (expected_fields_single | difference(item.containers[0].keys()) | length) == 0
49+
quiet: true
50+
loop: "{{ single_containers.results }}"
51+
loop_control:
52+
label: "{{ item.item }}"
53+
54+
- name: Fetch multiple containers by prefix
55+
openstack.cloud.object_containers_info:
56+
cloud: "{{ cloud }}"
57+
prefix: "{{ test_container_prefixed_prefix }}"
58+
register: multiple_containers
59+
60+
- name: Check that the correct number of prefixed containers were returned
61+
ansible.builtin.assert:
62+
that:
63+
- (multiple_containers.containers | length) == test_container_prefixed_num
64+
fail_msg: >-
65+
Incorrect number of containers found
66+
(found {{ multiple_containers.containers | length }},
67+
expected {{ test_container_prefixed_num }})
68+
quiet: true
69+
70+
- name: Check that all prefixed containers exist
71+
ansible.builtin.assert:
72+
that:
73+
- >-
74+
(test_container_prefixed_prefix + '-' + (item | string))
75+
in (multiple_containers.containers | map(attribute='name'))
76+
fail_msg: "Container not found: {{ test_container_prefixed_prefix + '-' + (item | string) }}"
77+
quiet: true
78+
loop: "{{ range(test_container_prefixed_num) | list }}"
79+
loop_control:
80+
label: "{{ test_container_prefixed_prefix + '-' + (item | string) }}"
81+
82+
- name: Check that the expected fields are returned for all prefixed containers
83+
ansible.builtin.assert:
84+
that:
85+
- item.name.startswith(test_container_prefixed_prefix)
86+
# allow new fields to be introduced but prevent fields from being removed
87+
- (expected_fields_multiple | difference(item.keys()) | length) == 0
88+
quiet: true
89+
loop: "{{ multiple_containers.containers | sort(attribute='name') }}"
90+
loop_control:
91+
label: "{{ item.name }}"
92+
93+
- name: Fetch all containers
94+
openstack.cloud.object_containers_info:
95+
cloud: "{{ cloud }}"
96+
register: all_containers
97+
98+
- name: Check that all expected containers were returned
99+
ansible.builtin.assert:
100+
that:
101+
- item in (all_containers.containers | map(attribute='name'))
102+
fail_msg: "Container not found: {{ item }}"
103+
quiet: true
104+
loop: "{{ all_test_containers }}"
105+
106+
- name: Check that the expected fields are returned for all containers
107+
ansible.builtin.assert:
108+
that:
109+
# allow new fields to be introduced but prevent fields from being removed
110+
- (expected_fields_multiple | difference(item.keys()) | length) == 0
111+
quiet: true
112+
loop: "{{ all_containers.containers | selectattr('name', 'in', all_test_containers) }}"
113+
loop_control:
114+
label: "{{ item.name }}"
115+
116+
always:
117+
118+
- name: Delete all containers
119+
openstack.cloud.object_container:
120+
cloud: "{{ cloud }}"
121+
name: "{{ item }}"
122+
state: absent
123+
delete_with_all_objects: true
124+
loop: "{{ all_test_containers }}"

ci/run-collection.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- { role: neutron_rbac_policy, tags: neutron_rbac_policy }
3636
- { role: object, tags: object }
3737
- { role: object_container, tags: object_container }
38+
- { role: object_containers_info, tags: object_containers_info }
3839
- { role: port, tags: port }
3940
- { role: trait, tags: trait }
4041
- { role: trunk, tags: trunk }

meta/runtime.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ action_groups:
5656
- neutron_rbac_policy
5757
- object
5858
- object_container
59+
- object_containers_info
5960
- port
6061
- port_info
6162
- project
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright (c) 2024 Catalyst Cloud Limited
5+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6+
7+
DOCUMENTATION = r"""
8+
---
9+
module: object_containers_info
10+
short_description: Fetch container info from the OpenStack Swift service.
11+
author: OpenStack Ansible SIG
12+
description:
13+
- Fetch container info from the OpenStack Swift service.
14+
options:
15+
name:
16+
description:
17+
- Name of the container
18+
type: str
19+
aliases: ["container"]
20+
prefix:
21+
description:
22+
- Filter containers by prefix
23+
type: str
24+
extends_documentation_fragment:
25+
- openstack.cloud.openstack
26+
"""
27+
28+
EXAMPLES = r"""
29+
- name: List all containers existing on the project
30+
openstack.cloud.object_containers_info:
31+
32+
- name: Retrive a single container by name
33+
openstack.cloud.object_containers_info:
34+
name: test-container
35+
36+
- name: Retrieve and filter containers by prefix
37+
openstack.cloud.object_containers_info:
38+
prefix: test-
39+
"""
40+
41+
RETURN = r"""
42+
containers:
43+
description: List of dictionaries describing matching containers.
44+
returned: always
45+
type: list
46+
elements: dict
47+
contains:
48+
bytes:
49+
description: The total number of bytes that are stored in Object Storage
50+
for the container.
51+
type: int
52+
sample: 5449
53+
bytes_used:
54+
description: The count of bytes used in total.
55+
type: int
56+
sample: 5449
57+
content_type:
58+
description: The MIME type of the list of names.
59+
Only fetched when searching for a container by name.
60+
type: str
61+
sample: null
62+
count:
63+
description: The number of objects in the container.
64+
type: int
65+
sample: 1
66+
history_location:
67+
description: Enables versioning on the container.
68+
Only fetched when searching for a container by name.
69+
type: str
70+
sample: null
71+
id:
72+
description: The ID of the container. Equals I(name).
73+
type: str
74+
sample: "otc"
75+
if_none_match:
76+
description: "In combination with C(Expect: 100-Continue), specify an
77+
C(If-None-Match: *) header to query whether the server
78+
already has a copy of the object before any data is sent.
79+
Only set when searching for a container by name."
80+
type: str
81+
sample: null
82+
is_content_type_detected:
83+
description: If set to C(true), Object Storage guesses the content type
84+
based on the file extension and ignores the value sent in
85+
the Content-Type header, if present.
86+
Only fetched when searching for a container by name.
87+
type: bool
88+
sample: null
89+
is_newest:
90+
description: If set to True, Object Storage queries all replicas to
91+
return the most recent one. If you omit this header, Object
92+
Storage responds faster after it finds one valid replica.
93+
Because setting this header to True is more expensive for
94+
the back end, use it only when it is absolutely needed.
95+
Only fetched when searching for a container by name.
96+
type: bool
97+
sample: null
98+
meta_temp_url_key:
99+
description: The secret key value for temporary URLs. If not set,
100+
this header is not returned by this operation.
101+
Only fetched when searching for a container by name.
102+
type: str
103+
sample: null
104+
meta_temp_url_key_2:
105+
description: A second secret key value for temporary URLs. If not set,
106+
this header is not returned by this operation.
107+
Only fetched when searching for a container by name.
108+
type: str
109+
sample: null
110+
name:
111+
description: The name of the container.
112+
type: str
113+
sample: "otc"
114+
object_count:
115+
description: The number of objects.
116+
type: int
117+
sample: 1
118+
read_ACL:
119+
description: The ACL that grants read access. If not set, this header is
120+
not returned by this operation.
121+
Only fetched when searching for a container by name.
122+
type: str
123+
sample: null
124+
storage_policy:
125+
description: Storage policy used by the container. It is not possible to
126+
change policy of an existing container.
127+
Only fetched when searching for a container by name.
128+
type: str
129+
sample: null
130+
sync_key:
131+
description: The secret key for container synchronization. If not set,
132+
this header is not returned by this operation.
133+
Only fetched when searching for a container by name.
134+
type: str
135+
sample: null
136+
sync_to:
137+
description: The destination for container synchronization. If not set,
138+
this header is not returned by this operation.
139+
Only fetched when searching for a container by name.
140+
type: str
141+
sample: null
142+
timestamp:
143+
description: The timestamp of the transaction.
144+
Only fetched when searching for a container by name.
145+
type: str
146+
sample: null
147+
versions_location:
148+
description: Enables versioning on this container. The value is the name
149+
of another container. You must UTF-8-encode and then
150+
URL-encode the name before you include it in the header. To
151+
disable versioning, set the header to an empty string.
152+
Only fetched when searching for a container by name.
153+
type: str
154+
sample: null
155+
write_ACL:
156+
description: The ACL that grants write access. If not set, this header is
157+
not returned by this operation.
158+
Only fetched when searching for a container by name.
159+
type: str
160+
sample: null
161+
"""
162+
163+
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
164+
165+
166+
class ObjectContainersInfoModule(OpenStackModule):
167+
argument_spec = dict(
168+
name=dict(aliases=["container"]),
169+
prefix=dict(),
170+
)
171+
172+
module_kwargs = dict(
173+
supports_check_mode=True,
174+
)
175+
176+
def run(self):
177+
if self.params["name"]:
178+
containers = [
179+
(
180+
self.conn.object_store.get_container_metadata(
181+
self.params["name"],
182+
).to_dict(computed=False)
183+
),
184+
]
185+
else:
186+
query = {}
187+
if self.params["prefix"]:
188+
query["prefix"] = self.params["prefix"]
189+
containers = [
190+
c.to_dict(computed=False)
191+
for c in self.conn.object_store.containers(**query)
192+
]
193+
self.exit(changed=False, containers=containers)
194+
195+
196+
def main():
197+
module = ObjectContainersInfoModule()
198+
module()
199+
200+
201+
if __name__ == "__main__":
202+
main()

0 commit comments

Comments
 (0)