python-openstackclient 8.1.0__py3-none-any.whl → 8.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openstackclient/api/compute_v2.py +2 -2
- openstackclient/api/volume_v2.py +60 -0
- openstackclient/api/volume_v3.py +60 -0
- openstackclient/compute/v2/flavor.py +14 -1
- openstackclient/compute/v2/server.py +1 -3
- openstackclient/identity/common.py +8 -13
- openstackclient/identity/v3/application_credential.py +86 -85
- openstackclient/identity/v3/domain.py +5 -6
- openstackclient/identity/v3/project.py +25 -20
- openstackclient/identity/v3/role.py +7 -2
- openstackclient/image/v1/image.py +16 -1
- openstackclient/image/v2/cache.py +10 -6
- openstackclient/image/v2/image.py +48 -1
- openstackclient/image/v2/metadef_objects.py +8 -2
- openstackclient/image/v2/metadef_properties.py +9 -2
- openstackclient/network/v2/port.py +16 -0
- openstackclient/network/v2/security_group.py +44 -3
- openstackclient/network/v2/security_group_rule.py +17 -0
- openstackclient/tests/functional/identity/v3/test_access_rule.py +1 -1
- openstackclient/tests/functional/identity/v3/test_application_credential.py +7 -7
- openstackclient/tests/functional/image/v2/test_image.py +36 -14
- openstackclient/tests/functional/volume/v2/test_volume.py +1 -1
- openstackclient/tests/functional/volume/v3/test_volume.py +2 -2
- openstackclient/tests/unit/api/test_volume_v2.py +124 -0
- openstackclient/tests/unit/api/test_volume_v3.py +124 -0
- openstackclient/tests/unit/compute/v2/test_flavor.py +159 -174
- openstackclient/tests/unit/compute/v2/test_server.py +42 -51
- openstackclient/tests/unit/identity/v3/test_application_credential.py +47 -41
- openstackclient/tests/unit/identity/v3/test_domain.py +2 -2
- openstackclient/tests/unit/identity/v3/test_project.py +30 -53
- openstackclient/tests/unit/identity/v3/test_role.py +2 -8
- openstackclient/tests/unit/image/v1/test_image.py +47 -0
- openstackclient/tests/unit/image/v2/test_image.py +79 -9
- openstackclient/tests/unit/image/v2/test_metadef_objects.py +22 -0
- openstackclient/tests/unit/image/v2/test_metadef_properties.py +24 -10
- openstackclient/tests/unit/network/v2/fakes.py +1 -0
- openstackclient/tests/unit/network/v2/test_ndp_proxy.py +2 -2
- openstackclient/tests/unit/network/v2/test_port.py +40 -0
- openstackclient/tests/unit/network/v2/test_security_group_network.py +6 -0
- openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +49 -0
- openstackclient/tests/unit/volume/v2/test_volume.py +358 -305
- openstackclient/tests/unit/volume/v3/test_volume.py +439 -415
- openstackclient/volume/v2/service.py +1 -1
- openstackclient/volume/v2/volume.py +78 -52
- openstackclient/volume/v3/service.py +1 -1
- openstackclient/volume/v3/volume.py +102 -75
- openstackclient/volume/v3/volume_group.py +1 -1
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/AUTHORS +5 -0
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/METADATA +7 -7
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/RECORD +55 -51
- python_openstackclient-8.2.0.dist-info/pbr.json +1 -0
- python_openstackclient-8.1.0.dist-info/pbr.json +0 -1
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/entry_points.txt +0 -0
- {python_openstackclient-8.1.0.dist-info → python_openstackclient-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -64,7 +64,7 @@ def list_security_groups(compute_client, all_projects=None):
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def find_security_group(compute_client, name_or_id):
|
|
67
|
-
"""Find the
|
|
67
|
+
"""Find the security group for a given name or ID
|
|
68
68
|
|
|
69
69
|
https://docs.openstack.org/api-ref/compute/#show-security-group-details
|
|
70
70
|
|
|
@@ -240,7 +240,7 @@ def list_networks(compute_client):
|
|
|
240
240
|
|
|
241
241
|
|
|
242
242
|
def find_network(compute_client, name_or_id):
|
|
243
|
-
"""Find the
|
|
243
|
+
"""Find the network for a given name or ID
|
|
244
244
|
|
|
245
245
|
https://docs.openstack.org/api-ref/compute/#show-network-details
|
|
246
246
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
2
|
+
# not use this file except in compliance with the License. You may obtain
|
|
3
|
+
# a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
9
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
10
|
+
# License for the specific language governing permissions and limitations
|
|
11
|
+
# under the License.
|
|
12
|
+
|
|
13
|
+
"""Volume v2 API Library
|
|
14
|
+
|
|
15
|
+
A collection of wrappers for deprecated Block Storage v2 APIs that are not
|
|
16
|
+
intentionally supported by SDK.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import http
|
|
20
|
+
|
|
21
|
+
from openstack import exceptions as sdk_exceptions
|
|
22
|
+
from osc_lib import exceptions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# consistency groups
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def find_consistency_group(compute_client, name_or_id):
|
|
29
|
+
"""Find the consistency group for a given name or ID
|
|
30
|
+
|
|
31
|
+
https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details
|
|
32
|
+
|
|
33
|
+
:param volume_client: A volume client
|
|
34
|
+
:param name_or_id: The name or ID of the consistency group to look up
|
|
35
|
+
:returns: A consistency group object
|
|
36
|
+
:raises exception.NotFound: If a matching consistency group could not be
|
|
37
|
+
found or more than one match was found
|
|
38
|
+
"""
|
|
39
|
+
response = compute_client.get(f'/consistencygroups/{name_or_id}')
|
|
40
|
+
if response.status_code != http.HTTPStatus.NOT_FOUND:
|
|
41
|
+
# there might be other, non-404 errors
|
|
42
|
+
sdk_exceptions.raise_from_response(response)
|
|
43
|
+
return response.json()['consistencygroup']
|
|
44
|
+
|
|
45
|
+
response = compute_client.get('/consistencygroups')
|
|
46
|
+
sdk_exceptions.raise_from_response(response)
|
|
47
|
+
found = None
|
|
48
|
+
consistency_groups = response.json()['consistencygroups']
|
|
49
|
+
for consistency_group in consistency_groups:
|
|
50
|
+
if consistency_group['name'] == name_or_id:
|
|
51
|
+
if found:
|
|
52
|
+
raise exceptions.NotFound(
|
|
53
|
+
f'multiple matches found for {name_or_id}'
|
|
54
|
+
)
|
|
55
|
+
found = consistency_group
|
|
56
|
+
|
|
57
|
+
if not found:
|
|
58
|
+
raise exceptions.NotFound(f'{name_or_id} not found')
|
|
59
|
+
|
|
60
|
+
return found
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
2
|
+
# not use this file except in compliance with the License. You may obtain
|
|
3
|
+
# a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
9
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
10
|
+
# License for the specific language governing permissions and limitations
|
|
11
|
+
# under the License.
|
|
12
|
+
|
|
13
|
+
"""Volume v3 API Library
|
|
14
|
+
|
|
15
|
+
A collection of wrappers for deprecated Block Storage v3 APIs that are not
|
|
16
|
+
intentionally supported by SDK.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import http
|
|
20
|
+
|
|
21
|
+
from openstack import exceptions as sdk_exceptions
|
|
22
|
+
from osc_lib import exceptions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# consistency groups
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def find_consistency_group(compute_client, name_or_id):
|
|
29
|
+
"""Find the consistency group for a given name or ID
|
|
30
|
+
|
|
31
|
+
https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details
|
|
32
|
+
|
|
33
|
+
:param volume_client: A volume client
|
|
34
|
+
:param name_or_id: The name or ID of the consistency group to look up
|
|
35
|
+
:returns: A consistency group object
|
|
36
|
+
:raises exception.NotFound: If a matching consistency group could not be
|
|
37
|
+
found or more than one match was found
|
|
38
|
+
"""
|
|
39
|
+
response = compute_client.get(f'/consistencygroups/{name_or_id}')
|
|
40
|
+
if response.status_code != http.HTTPStatus.NOT_FOUND:
|
|
41
|
+
# there might be other, non-404 errors
|
|
42
|
+
sdk_exceptions.raise_from_response(response)
|
|
43
|
+
return response.json()['consistencygroup']
|
|
44
|
+
|
|
45
|
+
response = compute_client.get('/consistencygroups')
|
|
46
|
+
sdk_exceptions.raise_from_response(response)
|
|
47
|
+
found = None
|
|
48
|
+
consistency_groups = response.json()['consistencygroups']
|
|
49
|
+
for consistency_group in consistency_groups:
|
|
50
|
+
if consistency_group['name'] == name_or_id:
|
|
51
|
+
if found:
|
|
52
|
+
raise exceptions.NotFound(
|
|
53
|
+
f'multiple matches found for {name_or_id}'
|
|
54
|
+
)
|
|
55
|
+
found = consistency_group
|
|
56
|
+
|
|
57
|
+
if not found:
|
|
58
|
+
raise exceptions.NotFound(f'{name_or_id} not found')
|
|
59
|
+
|
|
60
|
+
return found
|
|
@@ -156,12 +156,25 @@ class CreateFlavor(command.ShowOne):
|
|
|
156
156
|
msg = _("--project is only allowed with --private")
|
|
157
157
|
raise exceptions.CommandError(msg)
|
|
158
158
|
|
|
159
|
+
flavor_id = parsed_args.id
|
|
160
|
+
if parsed_args.id == 'auto':
|
|
161
|
+
# novaclient aliased 'auto' to mean "generate a UUID for me": we
|
|
162
|
+
# do the same to avoid breaking existing users
|
|
163
|
+
flavor_id = None
|
|
164
|
+
|
|
165
|
+
msg = _(
|
|
166
|
+
"Passing '--id auto' is deprecated. Nova will automatically "
|
|
167
|
+
"assign a UUID-like ID if no ID is provided. Omit the '--id' "
|
|
168
|
+
"parameter instead."
|
|
169
|
+
)
|
|
170
|
+
self.log.warning(msg)
|
|
171
|
+
|
|
159
172
|
args = {
|
|
160
173
|
'name': parsed_args.name,
|
|
161
174
|
'ram': parsed_args.ram,
|
|
162
175
|
'vcpus': parsed_args.vcpus,
|
|
163
176
|
'disk': parsed_args.disk,
|
|
164
|
-
'id':
|
|
177
|
+
'id': flavor_id,
|
|
165
178
|
'ephemeral': parsed_args.ephemeral,
|
|
166
179
|
'swap': parsed_args.swap,
|
|
167
180
|
'rxtx_factor': parsed_args.rxtx_factor,
|
|
@@ -2129,13 +2129,11 @@ class CreateServer(command.ShowOne):
|
|
|
2129
2129
|
f.close()
|
|
2130
2130
|
|
|
2131
2131
|
if parsed_args.wait:
|
|
2132
|
-
if utils.wait_for_status(
|
|
2132
|
+
if not utils.wait_for_status(
|
|
2133
2133
|
compute_client.get_server,
|
|
2134
2134
|
server.id,
|
|
2135
2135
|
callback=_show_progress,
|
|
2136
2136
|
):
|
|
2137
|
-
self.app.stdout.write('\n')
|
|
2138
|
-
else:
|
|
2139
2137
|
msg = _('Error creating server: %s') % parsed_args.server_name
|
|
2140
2138
|
raise exceptions.CommandError(msg)
|
|
2141
2139
|
|
|
@@ -348,15 +348,6 @@ def _find_sdk_id(
|
|
|
348
348
|
return resource.id
|
|
349
349
|
|
|
350
350
|
|
|
351
|
-
def get_immutable_options(parsed_args):
|
|
352
|
-
options = {}
|
|
353
|
-
if parsed_args.immutable:
|
|
354
|
-
options['immutable'] = True
|
|
355
|
-
if parsed_args.no_immutable:
|
|
356
|
-
options['immutable'] = False
|
|
357
|
-
return options
|
|
358
|
-
|
|
359
|
-
|
|
360
351
|
def add_user_domain_option_to_parser(parser):
|
|
361
352
|
parser.add_argument(
|
|
362
353
|
'--user-domain',
|
|
@@ -419,17 +410,21 @@ def add_inherited_option_to_parser(parser):
|
|
|
419
410
|
|
|
420
411
|
|
|
421
412
|
def add_resource_option_to_parser(parser):
|
|
422
|
-
|
|
423
|
-
|
|
413
|
+
immutable_group = parser.add_mutually_exclusive_group()
|
|
414
|
+
immutable_group.add_argument(
|
|
424
415
|
'--immutable',
|
|
425
416
|
action='store_true',
|
|
417
|
+
dest='immutable',
|
|
418
|
+
default=None,
|
|
426
419
|
help=_(
|
|
427
420
|
'Make resource immutable. An immutable project may not '
|
|
428
421
|
'be deleted or modified except to remove the immutable flag'
|
|
429
422
|
),
|
|
430
423
|
)
|
|
431
|
-
|
|
424
|
+
immutable_group.add_argument(
|
|
432
425
|
'--no-immutable',
|
|
433
|
-
action='
|
|
426
|
+
action='store_false',
|
|
427
|
+
dest='immutable',
|
|
428
|
+
default=None,
|
|
434
429
|
help=_('Make resource mutable (default)'),
|
|
435
430
|
)
|
|
@@ -20,6 +20,7 @@ import json
|
|
|
20
20
|
import logging
|
|
21
21
|
import uuid
|
|
22
22
|
|
|
23
|
+
from cliff import columns as cliff_columns
|
|
23
24
|
from osc_lib.command import command
|
|
24
25
|
from osc_lib import exceptions
|
|
25
26
|
from osc_lib import utils
|
|
@@ -27,10 +28,84 @@ from osc_lib import utils
|
|
|
27
28
|
from openstackclient.i18n import _
|
|
28
29
|
from openstackclient.identity import common
|
|
29
30
|
|
|
30
|
-
|
|
31
31
|
LOG = logging.getLogger(__name__)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class RolesColumn(cliff_columns.FormattableColumn):
|
|
35
|
+
"""Generate a formatted string of role names."""
|
|
36
|
+
|
|
37
|
+
def human_readable(self):
|
|
38
|
+
return utils.format_list(r['name'] for r in self._value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _format_application_credential(
|
|
42
|
+
application_credential, *, include_secret=False
|
|
43
|
+
):
|
|
44
|
+
column_headers: tuple[str, ...] = (
|
|
45
|
+
'ID',
|
|
46
|
+
'Name',
|
|
47
|
+
'Description',
|
|
48
|
+
'Project ID',
|
|
49
|
+
'Roles',
|
|
50
|
+
'Unrestricted',
|
|
51
|
+
'Access Rules',
|
|
52
|
+
'Expires At',
|
|
53
|
+
)
|
|
54
|
+
columns: tuple[str, ...] = (
|
|
55
|
+
'id',
|
|
56
|
+
'name',
|
|
57
|
+
'description',
|
|
58
|
+
'project_id',
|
|
59
|
+
'roles',
|
|
60
|
+
'unrestricted',
|
|
61
|
+
'access_rules',
|
|
62
|
+
'expires_at',
|
|
63
|
+
)
|
|
64
|
+
if include_secret:
|
|
65
|
+
column_headers += ('Secret',)
|
|
66
|
+
columns += ('secret',)
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
column_headers,
|
|
70
|
+
utils.get_item_properties(
|
|
71
|
+
application_credential, columns, formatters={'roles': RolesColumn}
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _format_application_credentials(application_credentials):
|
|
77
|
+
column_headers = (
|
|
78
|
+
'ID',
|
|
79
|
+
'Name',
|
|
80
|
+
'Description',
|
|
81
|
+
'Project ID',
|
|
82
|
+
'Roles',
|
|
83
|
+
'Unrestricted',
|
|
84
|
+
'Access Rules',
|
|
85
|
+
'Expires At',
|
|
86
|
+
)
|
|
87
|
+
columns = (
|
|
88
|
+
'id',
|
|
89
|
+
'name',
|
|
90
|
+
'description',
|
|
91
|
+
'project_id',
|
|
92
|
+
'roles',
|
|
93
|
+
'unrestricted',
|
|
94
|
+
'access_rules',
|
|
95
|
+
'expires_at',
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
column_headers,
|
|
100
|
+
(
|
|
101
|
+
utils.get_item_properties(
|
|
102
|
+
x, columns, formatters={'roles': RolesColumn}
|
|
103
|
+
)
|
|
104
|
+
for x in application_credentials
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
34
109
|
# TODO(stephenfin): Move this to osc_lib since it's useful elsewhere
|
|
35
110
|
def is_uuid_like(value) -> bool:
|
|
36
111
|
"""Returns validation of a value as a UUID.
|
|
@@ -38,9 +113,6 @@ def is_uuid_like(value) -> bool:
|
|
|
38
113
|
:param val: Value to verify
|
|
39
114
|
:type val: string
|
|
40
115
|
:returns: bool
|
|
41
|
-
|
|
42
|
-
.. versionchanged:: 1.1.1
|
|
43
|
-
Support non-lowercase UUIDs.
|
|
44
116
|
"""
|
|
45
117
|
try:
|
|
46
118
|
formatted_value = (
|
|
@@ -179,31 +251,8 @@ class CreateApplicationCredential(command.ShowOne):
|
|
|
179
251
|
access_rules=access_rules,
|
|
180
252
|
)
|
|
181
253
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
roles = application_credential['roles']
|
|
185
|
-
msg = ' '.join(r['name'] for r in roles)
|
|
186
|
-
application_credential['roles'] = msg
|
|
187
|
-
|
|
188
|
-
columns = (
|
|
189
|
-
'id',
|
|
190
|
-
'name',
|
|
191
|
-
'description',
|
|
192
|
-
'project_id',
|
|
193
|
-
'roles',
|
|
194
|
-
'unrestricted',
|
|
195
|
-
'access_rules',
|
|
196
|
-
'expires_at',
|
|
197
|
-
'secret',
|
|
198
|
-
)
|
|
199
|
-
return (
|
|
200
|
-
columns,
|
|
201
|
-
(
|
|
202
|
-
utils.get_dict_properties(
|
|
203
|
-
application_credential,
|
|
204
|
-
columns,
|
|
205
|
-
)
|
|
206
|
-
),
|
|
254
|
+
return _format_application_credential(
|
|
255
|
+
application_credential, include_secret=True
|
|
207
256
|
)
|
|
208
257
|
|
|
209
258
|
|
|
@@ -252,6 +301,8 @@ class DeleteApplicationCredential(command.Command):
|
|
|
252
301
|
) % {'errors': errors, 'total': total}
|
|
253
302
|
raise exceptions.CommandError(msg)
|
|
254
303
|
|
|
304
|
+
return None
|
|
305
|
+
|
|
255
306
|
|
|
256
307
|
class ListApplicationCredential(command.Lister):
|
|
257
308
|
_description = _("List application credentials")
|
|
@@ -276,39 +327,12 @@ class ListApplicationCredential(command.Lister):
|
|
|
276
327
|
conn = self.app.client_manager.sdk_connection
|
|
277
328
|
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
|
278
329
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
data_formatted = []
|
|
282
|
-
for ac in data:
|
|
283
|
-
# Format roles into something sensible
|
|
284
|
-
roles = ac['roles']
|
|
285
|
-
msg = ' '.join(r['name'] for r in roles)
|
|
286
|
-
ac['roles'] = msg
|
|
287
|
-
|
|
288
|
-
data_formatted.append(ac)
|
|
289
|
-
|
|
290
|
-
columns = (
|
|
291
|
-
'ID',
|
|
292
|
-
'Name',
|
|
293
|
-
'Description',
|
|
294
|
-
'Project ID',
|
|
295
|
-
'Roles',
|
|
296
|
-
'Unrestricted',
|
|
297
|
-
'Access Rules',
|
|
298
|
-
'Expires At',
|
|
299
|
-
)
|
|
300
|
-
return (
|
|
301
|
-
columns,
|
|
302
|
-
(
|
|
303
|
-
utils.get_item_properties(
|
|
304
|
-
s,
|
|
305
|
-
columns,
|
|
306
|
-
formatters={},
|
|
307
|
-
)
|
|
308
|
-
for s in data_formatted
|
|
309
|
-
),
|
|
330
|
+
application_credentials = identity_client.application_credentials(
|
|
331
|
+
user=user_id
|
|
310
332
|
)
|
|
311
333
|
|
|
334
|
+
return _format_application_credentials(application_credentials)
|
|
335
|
+
|
|
312
336
|
|
|
313
337
|
class ShowApplicationCredential(command.ShowOne):
|
|
314
338
|
_description = _("Display application credential details")
|
|
@@ -327,31 +351,8 @@ class ShowApplicationCredential(command.ShowOne):
|
|
|
327
351
|
conn = self.app.client_manager.sdk_connection
|
|
328
352
|
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
|
329
353
|
|
|
330
|
-
|
|
354
|
+
application_credential = identity_client.find_application_credential(
|
|
331
355
|
user_id, parsed_args.application_credential
|
|
332
356
|
)
|
|
333
357
|
|
|
334
|
-
|
|
335
|
-
roles = app_cred['roles']
|
|
336
|
-
msg = ' '.join(r['name'] for r in roles)
|
|
337
|
-
app_cred['roles'] = msg
|
|
338
|
-
|
|
339
|
-
columns = (
|
|
340
|
-
'id',
|
|
341
|
-
'name',
|
|
342
|
-
'description',
|
|
343
|
-
'project_id',
|
|
344
|
-
'roles',
|
|
345
|
-
'unrestricted',
|
|
346
|
-
'access_rules',
|
|
347
|
-
'expires_at',
|
|
348
|
-
)
|
|
349
|
-
return (
|
|
350
|
-
columns,
|
|
351
|
-
(
|
|
352
|
-
utils.get_dict_properties(
|
|
353
|
-
app_cred,
|
|
354
|
-
columns,
|
|
355
|
-
)
|
|
356
|
-
),
|
|
357
|
-
)
|
|
358
|
+
return _format_application_credential(application_credential)
|
|
@@ -94,7 +94,9 @@ class CreateDomain(command.ShowOne):
|
|
|
94
94
|
def take_action(self, parsed_args):
|
|
95
95
|
identity_client = self.app.client_manager.sdk_connection.identity
|
|
96
96
|
|
|
97
|
-
options =
|
|
97
|
+
options = {}
|
|
98
|
+
if parsed_args.immutable is not None:
|
|
99
|
+
options['immutable'] = parsed_args.immutable
|
|
98
100
|
|
|
99
101
|
try:
|
|
100
102
|
domain = identity_client.create_domain(
|
|
@@ -242,13 +244,10 @@ class SetDomain(command.Command):
|
|
|
242
244
|
kwargs['name'] = parsed_args.name
|
|
243
245
|
if parsed_args.description:
|
|
244
246
|
kwargs['description'] = parsed_args.description
|
|
245
|
-
|
|
246
247
|
if parsed_args.is_enabled is not None:
|
|
247
248
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if options:
|
|
251
|
-
kwargs['options'] = options
|
|
249
|
+
if parsed_args.immutable is not None:
|
|
250
|
+
kwargs['options'] = {'immutable': parsed_args.immutable}
|
|
252
251
|
|
|
253
252
|
identity_client.update_domain(domain.id, **kwargs)
|
|
254
253
|
|
|
@@ -59,17 +59,22 @@ class CreateProject(command.ShowOne):
|
|
|
59
59
|
enable_group.add_argument(
|
|
60
60
|
'--enable',
|
|
61
61
|
action='store_true',
|
|
62
|
+
dest='enabled',
|
|
63
|
+
default=True,
|
|
62
64
|
help=_('Enable project'),
|
|
63
65
|
)
|
|
64
66
|
enable_group.add_argument(
|
|
65
67
|
'--disable',
|
|
66
|
-
action='
|
|
68
|
+
action='store_false',
|
|
69
|
+
dest='enabled',
|
|
70
|
+
default=True,
|
|
67
71
|
help=_('Disable project'),
|
|
68
72
|
)
|
|
69
73
|
parser.add_argument(
|
|
70
74
|
'--property',
|
|
71
75
|
metavar='<key=value>',
|
|
72
76
|
action=parseractions.KeyValueAction,
|
|
77
|
+
dest='properties',
|
|
73
78
|
help=_(
|
|
74
79
|
'Add a property to <name> '
|
|
75
80
|
'(repeat option to set multiple properties)'
|
|
@@ -98,15 +103,9 @@ class CreateProject(command.ShowOne):
|
|
|
98
103
|
parsed_args.parent,
|
|
99
104
|
).id
|
|
100
105
|
|
|
101
|
-
enabled = True
|
|
102
|
-
if parsed_args.disable:
|
|
103
|
-
enabled = False
|
|
104
|
-
|
|
105
|
-
options = common.get_immutable_options(parsed_args)
|
|
106
|
-
|
|
107
106
|
kwargs = {}
|
|
108
|
-
if parsed_args.
|
|
109
|
-
kwargs = parsed_args.
|
|
107
|
+
if parsed_args.properties:
|
|
108
|
+
kwargs = parsed_args.properties.copy()
|
|
110
109
|
if 'is_domain' in kwargs.keys():
|
|
111
110
|
if kwargs['is_domain'].lower() == "true":
|
|
112
111
|
kwargs['is_domain'] = True
|
|
@@ -117,13 +116,17 @@ class CreateProject(command.ShowOne):
|
|
|
117
116
|
|
|
118
117
|
kwargs['tags'] = list(set(parsed_args.tags))
|
|
119
118
|
|
|
119
|
+
options = {}
|
|
120
|
+
if parsed_args.immutable is not None:
|
|
121
|
+
options['immutable'] = parsed_args.immutable
|
|
122
|
+
|
|
120
123
|
try:
|
|
121
124
|
project = identity_client.projects.create(
|
|
122
125
|
name=parsed_args.name,
|
|
123
126
|
domain=domain,
|
|
124
127
|
parent=parent,
|
|
125
128
|
description=parsed_args.description,
|
|
126
|
-
enabled=enabled,
|
|
129
|
+
enabled=parsed_args.enabled,
|
|
127
130
|
options=options,
|
|
128
131
|
**kwargs,
|
|
129
132
|
)
|
|
@@ -356,16 +359,21 @@ class SetProject(command.Command):
|
|
|
356
359
|
enable_group.add_argument(
|
|
357
360
|
'--enable',
|
|
358
361
|
action='store_true',
|
|
362
|
+
dest='enabled',
|
|
363
|
+
default=None,
|
|
359
364
|
help=_('Enable project'),
|
|
360
365
|
)
|
|
361
366
|
enable_group.add_argument(
|
|
362
367
|
'--disable',
|
|
363
|
-
action='
|
|
368
|
+
action='store_false',
|
|
369
|
+
dest='enabled',
|
|
370
|
+
default=None,
|
|
364
371
|
help=_('Disable project'),
|
|
365
372
|
)
|
|
366
373
|
parser.add_argument(
|
|
367
374
|
'--property',
|
|
368
375
|
metavar='<key=value>',
|
|
376
|
+
dest='properties',
|
|
369
377
|
action=parseractions.KeyValueAction,
|
|
370
378
|
help=_(
|
|
371
379
|
'Set a property on <project> '
|
|
@@ -388,15 +396,12 @@ class SetProject(command.Command):
|
|
|
388
396
|
kwargs['name'] = parsed_args.name
|
|
389
397
|
if parsed_args.description:
|
|
390
398
|
kwargs['description'] = parsed_args.description
|
|
391
|
-
if parsed_args.
|
|
392
|
-
kwargs['enabled'] =
|
|
393
|
-
if parsed_args.
|
|
394
|
-
kwargs['
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
kwargs['options'] = options
|
|
398
|
-
if parsed_args.property:
|
|
399
|
-
kwargs.update(parsed_args.property)
|
|
399
|
+
if parsed_args.enabled is not None:
|
|
400
|
+
kwargs['enabled'] = parsed_args.enabled
|
|
401
|
+
if parsed_args.immutable is not None:
|
|
402
|
+
kwargs['options'] = {'immutable': parsed_args.immutable}
|
|
403
|
+
if parsed_args.properties:
|
|
404
|
+
kwargs.update(parsed_args.properties)
|
|
400
405
|
tag.update_tags_in_args(parsed_args, project, kwargs)
|
|
401
406
|
|
|
402
407
|
identity_client.projects.update(project.id, **kwargs)
|
|
@@ -334,9 +334,12 @@ class CreateRole(command.ShowOne):
|
|
|
334
334
|
|
|
335
335
|
if parsed_args.name:
|
|
336
336
|
create_kwargs['name'] = parsed_args.name
|
|
337
|
+
|
|
337
338
|
if parsed_args.description:
|
|
338
339
|
create_kwargs['description'] = parsed_args.description
|
|
339
|
-
|
|
340
|
+
|
|
341
|
+
if parsed_args.immutable is not None:
|
|
342
|
+
create_kwargs['options'] = {"immutable": parsed_args.immutable}
|
|
340
343
|
|
|
341
344
|
try:
|
|
342
345
|
role = identity_client.create_role(**create_kwargs)
|
|
@@ -585,7 +588,9 @@ class SetRole(command.Command):
|
|
|
585
588
|
)
|
|
586
589
|
update_kwargs["domain_id"] = domain_id
|
|
587
590
|
|
|
588
|
-
|
|
591
|
+
if parsed_args.immutable is not None:
|
|
592
|
+
update_kwargs["options"] = {"immutable": parsed_args.immutable}
|
|
593
|
+
|
|
589
594
|
role = _find_sdk_id(
|
|
590
595
|
identity_client.find_role,
|
|
591
596
|
name_or_id=parsed_args.role,
|
|
@@ -528,6 +528,16 @@ class SaveImage(command.Command):
|
|
|
528
528
|
|
|
529
529
|
def get_parser(self, prog_name):
|
|
530
530
|
parser = super().get_parser(prog_name)
|
|
531
|
+
parser.add_argument(
|
|
532
|
+
"--chunk-size",
|
|
533
|
+
type=int,
|
|
534
|
+
default=1024,
|
|
535
|
+
metavar="<chunk-size>",
|
|
536
|
+
help=_(
|
|
537
|
+
"Size in bytes to read from the wire and buffer at one "
|
|
538
|
+
"time (default: 1024)"
|
|
539
|
+
),
|
|
540
|
+
)
|
|
531
541
|
parser.add_argument(
|
|
532
542
|
"--file",
|
|
533
543
|
metavar="<filename>",
|
|
@@ -550,7 +560,12 @@ class SaveImage(command.Command):
|
|
|
550
560
|
if output_file is None:
|
|
551
561
|
output_file = getattr(sys.stdout, "buffer", sys.stdout)
|
|
552
562
|
|
|
553
|
-
image_client.download_image(
|
|
563
|
+
image_client.download_image(
|
|
564
|
+
image.id,
|
|
565
|
+
stream=True,
|
|
566
|
+
output=output_file,
|
|
567
|
+
chunk_size=parsed_args.chunk_size,
|
|
568
|
+
)
|
|
554
569
|
|
|
555
570
|
|
|
556
571
|
class SetImage(command.Command):
|
|
@@ -37,14 +37,18 @@ def _format_image_cache(cached_images):
|
|
|
37
37
|
image_obj = copy.deepcopy(image)
|
|
38
38
|
image_obj['state'] = 'cached'
|
|
39
39
|
image_obj['last_accessed'] = (
|
|
40
|
-
datetime.datetime.
|
|
41
|
-
image['last_accessed']
|
|
42
|
-
)
|
|
40
|
+
datetime.datetime.fromtimestamp(
|
|
41
|
+
image['last_accessed'], tz=datetime.timezone.utc
|
|
42
|
+
)
|
|
43
|
+
.replace(tzinfo=None)
|
|
44
|
+
.isoformat()
|
|
43
45
|
)
|
|
44
46
|
image_obj['last_modified'] = (
|
|
45
|
-
datetime.datetime.
|
|
46
|
-
image['last_modified']
|
|
47
|
-
)
|
|
47
|
+
datetime.datetime.fromtimestamp(
|
|
48
|
+
image['last_modified'], tz=datetime.timezone.utc
|
|
49
|
+
)
|
|
50
|
+
.replace(tzinfo=None)
|
|
51
|
+
.isoformat()
|
|
48
52
|
)
|
|
49
53
|
image_list.append(image_obj)
|
|
50
54
|
elif item == "queued_images":
|