python-openstackclient 8.0.0__py3-none-any.whl → 8.1.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/compute/client.py +5 -0
- openstackclient/compute/v2/console.py +7 -0
- openstackclient/compute/v2/console_connection.py +48 -0
- openstackclient/compute/v2/keypair.py +10 -3
- openstackclient/compute/v2/server.py +75 -10
- openstackclient/compute/v2/server_event.py +1 -1
- openstackclient/identity/common.py +79 -0
- openstackclient/identity/v3/application_credential.py +2 -2
- openstackclient/identity/v3/domain.py +62 -43
- openstackclient/identity/v3/group.py +113 -68
- openstackclient/identity/v3/project.py +17 -0
- openstackclient/identity/v3/user.py +38 -5
- openstackclient/image/client.py +5 -0
- openstackclient/image/v2/image.py +11 -11
- openstackclient/network/client.py +0 -6
- openstackclient/network/v2/floating_ip.py +58 -29
- openstackclient/network/v2/network_qos_rule.py +3 -11
- openstackclient/network/v2/router.py +1 -1
- openstackclient/network/v2/security_group.py +5 -4
- openstackclient/network/v2/security_group_rule.py +1 -1
- openstackclient/shell.py +1 -1
- openstackclient/tests/functional/base.py +5 -1
- openstackclient/tests/functional/compute/v2/test_keypair.py +41 -5
- openstackclient/tests/unit/compute/v2/fakes.py +81 -305
- openstackclient/tests/unit/compute/v2/test_console.py +18 -1
- openstackclient/tests/unit/compute/v2/test_console_connection.py +72 -0
- openstackclient/tests/unit/compute/v2/test_flavor.py +1 -1
- openstackclient/tests/unit/compute/v2/test_keypair.py +12 -5
- openstackclient/tests/unit/compute/v2/test_server.py +169 -46
- openstackclient/tests/unit/compute/v2/test_server_backup.py +32 -71
- openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
- openstackclient/tests/unit/compute/v2/test_server_image.py +33 -72
- openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
- openstackclient/tests/unit/identity/v3/test_application_credential.py +47 -25
- openstackclient/tests/unit/identity/v3/test_domain.py +115 -105
- openstackclient/tests/unit/identity/v3/test_group.py +353 -202
- openstackclient/tests/unit/identity/v3/test_project.py +16 -0
- openstackclient/tests/unit/identity/v3/test_user.py +86 -6
- openstackclient/tests/unit/image/v1/test_image.py +8 -9
- openstackclient/tests/unit/image/v2/test_image.py +49 -49
- openstackclient/tests/unit/network/v2/fakes.py +405 -485
- openstackclient/tests/unit/network/v2/test_floating_ip_network.py +13 -19
- openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +2 -2
- openstackclient/tests/unit/network/v2/test_ndp_proxy.py +1 -3
- openstackclient/tests/unit/network/v2/test_network.py +4 -4
- openstackclient/tests/unit/network/v2/test_network_agent.py +15 -29
- openstackclient/tests/unit/network/v2/test_network_qos_policy.py +16 -19
- openstackclient/tests/unit/network/v2/test_network_qos_rule.py +79 -152
- openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +4 -6
- openstackclient/tests/unit/network/v2/test_network_rbac.py +2 -2
- openstackclient/tests/unit/network/v2/test_port.py +17 -17
- openstackclient/tests/unit/network/v2/test_router.py +73 -57
- openstackclient/tests/unit/network/v2/test_security_group_network.py +25 -27
- openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +1 -3
- openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +33 -39
- openstackclient/tests/unit/volume/v2/fakes.py +1 -2
- openstackclient/tests/unit/volume/v2/test_service.py +57 -91
- openstackclient/tests/unit/volume/v2/test_volume.py +108 -105
- openstackclient/tests/unit/volume/v2/test_volume_backup.py +141 -148
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +293 -283
- openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +61 -71
- openstackclient/tests/unit/volume/v3/test_service.py +221 -141
- openstackclient/tests/unit/volume/v3/test_volume.py +130 -119
- openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
- openstackclient/tests/unit/volume/v3/test_volume_backup.py +198 -203
- openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +682 -47
- openstackclient/volume/v2/service.py +41 -38
- openstackclient/volume/v2/volume.py +63 -37
- openstackclient/volume/v2/volume_backup.py +9 -3
- openstackclient/volume/v2/volume_snapshot.py +121 -84
- openstackclient/volume/v3/block_storage_log_level.py +22 -28
- openstackclient/volume/v3/service.py +105 -14
- openstackclient/volume/v3/volume.py +200 -39
- openstackclient/volume/v3/volume_backup.py +24 -19
- openstackclient/volume/v3/volume_snapshot.py +485 -10
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/AUTHORS +8 -0
- python_openstackclient-8.1.0.dist-info/METADATA +264 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/RECORD +83 -81
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/entry_points.txt +7 -6
- python_openstackclient-8.1.0.dist-info/pbr.json +1 -0
- python_openstackclient-8.0.0.dist-info/METADATA +0 -166
- python_openstackclient-8.0.0.dist-info/pbr.json +0 -1
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.1.0.dist-info}/top_level.txt +0 -0
|
@@ -106,6 +106,13 @@ class ShowConsoleURL(command.ShowOne):
|
|
|
106
106
|
const='spice-html5',
|
|
107
107
|
help=_("Show SPICE console URL"),
|
|
108
108
|
)
|
|
109
|
+
type_group.add_argument(
|
|
110
|
+
'--spice-direct',
|
|
111
|
+
dest='url_type',
|
|
112
|
+
action='store_const',
|
|
113
|
+
const='spice-direct',
|
|
114
|
+
help=_("Show SPICE direct protocol native console URL"),
|
|
115
|
+
)
|
|
109
116
|
type_group.add_argument(
|
|
110
117
|
'--rdp',
|
|
111
118
|
dest='url_type',
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
|
|
14
|
+
"""Compute v2 Console auth token implementations."""
|
|
15
|
+
|
|
16
|
+
from osc_lib.command import command
|
|
17
|
+
from osc_lib import utils
|
|
18
|
+
|
|
19
|
+
from openstackclient.i18n import _
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _get_console_connection_columns(item):
|
|
23
|
+
column_map: dict[str, str] = {}
|
|
24
|
+
hidden_columns = ['id', 'location', 'name']
|
|
25
|
+
return utils.get_osc_show_columns_for_sdk_resource(
|
|
26
|
+
item, column_map, hidden_columns
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ShowConsoleConnectionInformation(command.ShowOne):
|
|
31
|
+
_description = _("Show server's remote console connection information")
|
|
32
|
+
|
|
33
|
+
def get_parser(self, prog_name):
|
|
34
|
+
parser = super().get_parser(prog_name)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
'token',
|
|
37
|
+
metavar='<token>',
|
|
38
|
+
help=_("Nova console token to lookup"),
|
|
39
|
+
)
|
|
40
|
+
return parser
|
|
41
|
+
|
|
42
|
+
def take_action(self, parsed_args):
|
|
43
|
+
compute_client = self.app.client_manager.compute
|
|
44
|
+
data = compute_client.validate_console_auth_token(parsed_args.token)
|
|
45
|
+
display_columns, columns = _get_console_connection_columns(data)
|
|
46
|
+
data = utils.get_dict_properties(data, columns)
|
|
47
|
+
|
|
48
|
+
return (display_columns, data)
|
|
@@ -300,6 +300,7 @@ class ListKeypair(command.Lister):
|
|
|
300
300
|
def take_action(self, parsed_args):
|
|
301
301
|
compute_client = self.app.client_manager.compute
|
|
302
302
|
identity_client = self.app.client_manager.identity
|
|
303
|
+
identity_sdk_client = self.app.client_manager.sdk_connection.identity
|
|
303
304
|
|
|
304
305
|
kwargs = {}
|
|
305
306
|
|
|
@@ -345,11 +346,17 @@ class ListKeypair(command.Lister):
|
|
|
345
346
|
parsed_args.project,
|
|
346
347
|
parsed_args.project_domain,
|
|
347
348
|
).id
|
|
348
|
-
|
|
349
|
+
assignments = identity_sdk_client.role_assignments(
|
|
350
|
+
scope_project_id=project
|
|
351
|
+
)
|
|
352
|
+
user_ids = set()
|
|
353
|
+
for assignment in assignments:
|
|
354
|
+
if assignment.user:
|
|
355
|
+
user_ids.add(assignment.user['id'])
|
|
349
356
|
|
|
350
357
|
data = []
|
|
351
|
-
for
|
|
352
|
-
kwargs['user_id'] =
|
|
358
|
+
for user_id in user_ids:
|
|
359
|
+
kwargs['user_id'] = user_id
|
|
353
360
|
data.extend(compute_client.keypairs(**kwargs))
|
|
354
361
|
elif parsed_args.user:
|
|
355
362
|
if not sdk_utils.supports_microversion(compute_client, '2.10'):
|
|
@@ -21,7 +21,6 @@ import getpass
|
|
|
21
21
|
import json
|
|
22
22
|
import logging
|
|
23
23
|
import os
|
|
24
|
-
import typing as ty
|
|
25
24
|
|
|
26
25
|
from cliff import columns as cliff_columns
|
|
27
26
|
import iso8601
|
|
@@ -185,6 +184,7 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
|
|
185
184
|
'user_data': 'OS-EXT-SRV-ATTR:user_data',
|
|
186
185
|
'vm_state': 'OS-EXT-STS:vm_state',
|
|
187
186
|
'pinned_availability_zone': 'pinned_availability_zone',
|
|
187
|
+
'scheduler_hints': 'scheduler_hints',
|
|
188
188
|
}
|
|
189
189
|
# Some columns returned by openstacksdk should not be shown because they're
|
|
190
190
|
# either irrelevant or duplicates
|
|
@@ -205,7 +205,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
|
|
205
205
|
'min_count',
|
|
206
206
|
'networks',
|
|
207
207
|
'personality',
|
|
208
|
-
'scheduler_hints',
|
|
209
208
|
# aliases
|
|
210
209
|
'volumes',
|
|
211
210
|
# unnecessary
|
|
@@ -236,6 +235,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
|
|
236
235
|
|
|
237
236
|
info = data
|
|
238
237
|
|
|
238
|
+
# NOTE(dviroel): microversion 2.100 is now retrieving scheduler_hints
|
|
239
|
+
# content from request_spec on detailed responses
|
|
240
|
+
if not sdk_utils.supports_microversion(compute_client, '2.100'):
|
|
241
|
+
info.pop('scheduler_hints', None)
|
|
242
|
+
|
|
239
243
|
# Convert the image blob to a name
|
|
240
244
|
image_info = info.get('image', {})
|
|
241
245
|
if image_info and any(image_info.values()):
|
|
@@ -322,6 +326,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
|
|
|
322
326
|
info['OS-EXT-STS:power_state']
|
|
323
327
|
)
|
|
324
328
|
|
|
329
|
+
if 'scheduler_hints' in info:
|
|
330
|
+
info['scheduler_hints'] = format_columns.DictListColumn(
|
|
331
|
+
info.pop('scheduler_hints', {}),
|
|
332
|
+
)
|
|
333
|
+
|
|
325
334
|
return info
|
|
326
335
|
|
|
327
336
|
|
|
@@ -1864,7 +1873,7 @@ class CreateServer(command.ShowOne):
|
|
|
1864
1873
|
|
|
1865
1874
|
# Default to empty list if nothing was specified and let nova
|
|
1866
1875
|
# decide the default behavior.
|
|
1867
|
-
networks:
|
|
1876
|
+
networks: str | list[dict[str, str]] | None = []
|
|
1868
1877
|
|
|
1869
1878
|
if 'auto' in parsed_args.nics or 'none' in parsed_args.nics:
|
|
1870
1879
|
if len(parsed_args.nics) > 1:
|
|
@@ -1938,9 +1947,9 @@ class CreateServer(command.ShowOne):
|
|
|
1938
1947
|
network['port'] = nic['port-id']
|
|
1939
1948
|
|
|
1940
1949
|
if nic['v4-fixed-ip']:
|
|
1941
|
-
network['
|
|
1950
|
+
network['fixed_ip'] = nic['v4-fixed-ip']
|
|
1942
1951
|
elif nic['v6-fixed-ip']:
|
|
1943
|
-
network['
|
|
1952
|
+
network['fixed_ip'] = nic['v6-fixed-ip']
|
|
1944
1953
|
|
|
1945
1954
|
if nic.get('tag'): # tags are optional
|
|
1946
1955
|
network['tag'] = nic['tag']
|
|
@@ -2834,14 +2843,20 @@ class ListServer(command.Lister):
|
|
|
2834
2843
|
'pinned_availability_zone',
|
|
2835
2844
|
'hypervisor_hostname',
|
|
2836
2845
|
'metadata',
|
|
2846
|
+
'scheduler_hints',
|
|
2837
2847
|
)
|
|
2838
2848
|
column_headers += (
|
|
2839
2849
|
'Availability Zone',
|
|
2840
2850
|
'Pinned Availability Zone',
|
|
2841
2851
|
'Host',
|
|
2842
2852
|
'Properties',
|
|
2853
|
+
'Scheduler Hints',
|
|
2843
2854
|
)
|
|
2844
2855
|
|
|
2856
|
+
if parsed_args.all_projects:
|
|
2857
|
+
columns += ('project_id',)
|
|
2858
|
+
column_headers += ('Project ID',)
|
|
2859
|
+
|
|
2845
2860
|
# support for additional columns
|
|
2846
2861
|
if parsed_args.columns:
|
|
2847
2862
|
for c in parsed_args.columns:
|
|
@@ -2884,6 +2899,12 @@ class ListServer(command.Lister):
|
|
|
2884
2899
|
if c in ('Properties', "properties"):
|
|
2885
2900
|
columns += ('Metadata',)
|
|
2886
2901
|
column_headers += ('Properties',)
|
|
2902
|
+
if c in (
|
|
2903
|
+
'scheduler_hints',
|
|
2904
|
+
"Scheduler Hints",
|
|
2905
|
+
):
|
|
2906
|
+
columns += ('scheduler_hints',)
|
|
2907
|
+
column_headers += ('Scheduler Hints',)
|
|
2887
2908
|
|
|
2888
2909
|
# remove duplicates
|
|
2889
2910
|
column_headers = tuple(dict.fromkeys(column_headers))
|
|
@@ -2986,7 +3007,7 @@ class ListServer(command.Lister):
|
|
|
2986
3007
|
# infrastructure failure situations.
|
|
2987
3008
|
# For those servers with partial constructs we just skip the
|
|
2988
3009
|
# processing of the image and flavor information.
|
|
2989
|
-
if
|
|
3010
|
+
if getattr(s, 'status') == 'UNKNOWN':
|
|
2990
3011
|
continue
|
|
2991
3012
|
|
|
2992
3013
|
if 'id' in s.image and s.image.id is not None:
|
|
@@ -3050,6 +3071,7 @@ class ListServer(command.Lister):
|
|
|
3050
3071
|
'metadata': format_columns.DictColumn,
|
|
3051
3072
|
'security_groups_name': format_columns.ListColumn,
|
|
3052
3073
|
'hypervisor_hostname': HostColumn,
|
|
3074
|
+
'scheduler_hints': format_columns.DictListColumn,
|
|
3053
3075
|
},
|
|
3054
3076
|
)
|
|
3055
3077
|
for s in data
|
|
@@ -3860,9 +3882,15 @@ host."""
|
|
|
3860
3882
|
compute_client.evacuate_server(server, **kwargs)
|
|
3861
3883
|
|
|
3862
3884
|
if parsed_args.wait:
|
|
3885
|
+
orig_status = server.status
|
|
3886
|
+
success = ['ACTIVE']
|
|
3887
|
+
if orig_status == 'SHUTOFF':
|
|
3888
|
+
success.append('SHUTOFF')
|
|
3889
|
+
|
|
3863
3890
|
if utils.wait_for_status(
|
|
3864
3891
|
compute_client.get_server,
|
|
3865
3892
|
server.id,
|
|
3893
|
+
success_status=success,
|
|
3866
3894
|
callback=_show_progress,
|
|
3867
3895
|
):
|
|
3868
3896
|
self.app.stdout.write(_('Complete\n'))
|
|
@@ -4462,15 +4490,27 @@ class SetServer(command.Command):
|
|
|
4462
4490
|
'(repeat option to set multiple properties)'
|
|
4463
4491
|
),
|
|
4464
4492
|
)
|
|
4493
|
+
parser.add_argument(
|
|
4494
|
+
'--auto-approve',
|
|
4495
|
+
action='store_true',
|
|
4496
|
+
help=_(
|
|
4497
|
+
"Allow server state override without asking for confirmation"
|
|
4498
|
+
),
|
|
4499
|
+
)
|
|
4465
4500
|
parser.add_argument(
|
|
4466
4501
|
'--state',
|
|
4467
4502
|
metavar='<state>',
|
|
4468
4503
|
choices=['active', 'error'],
|
|
4469
4504
|
help=_(
|
|
4470
|
-
'New server state
|
|
4471
|
-
'**WARNING**
|
|
4472
|
-
'
|
|
4473
|
-
'
|
|
4505
|
+
'New server state.'
|
|
4506
|
+
'**WARNING** Resetting the state is intended to work around '
|
|
4507
|
+
'servers stuck in an intermediate state, such as deleting. '
|
|
4508
|
+
'If the server is in an error state then this is almost '
|
|
4509
|
+
'never the correct command to run and you should prefer hard '
|
|
4510
|
+
'reboot where possible. In particular, if the server is in '
|
|
4511
|
+
'an error state due to a move operation, setting the state '
|
|
4512
|
+
'can result in instances that are no longer usable. Proceed '
|
|
4513
|
+
'with caution. (admin only)'
|
|
4474
4514
|
),
|
|
4475
4515
|
)
|
|
4476
4516
|
parser.add_argument(
|
|
@@ -4505,6 +4545,20 @@ class SetServer(command.Command):
|
|
|
4505
4545
|
)
|
|
4506
4546
|
return parser
|
|
4507
4547
|
|
|
4548
|
+
@staticmethod
|
|
4549
|
+
def ask_user_yesno(msg):
|
|
4550
|
+
"""Ask user Y/N question
|
|
4551
|
+
|
|
4552
|
+
:param str msg: question text
|
|
4553
|
+
:return bool: User choice
|
|
4554
|
+
"""
|
|
4555
|
+
while True:
|
|
4556
|
+
answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n'))
|
|
4557
|
+
if answer in ('y', 'Y', 'yes'):
|
|
4558
|
+
return True
|
|
4559
|
+
elif answer in ('n', 'N', 'no'):
|
|
4560
|
+
return False
|
|
4561
|
+
|
|
4508
4562
|
def take_action(self, parsed_args):
|
|
4509
4563
|
compute_client = self.app.client_manager.compute
|
|
4510
4564
|
server = compute_client.find_server(
|
|
@@ -4555,6 +4609,17 @@ class SetServer(command.Command):
|
|
|
4555
4609
|
)
|
|
4556
4610
|
|
|
4557
4611
|
if parsed_args.state:
|
|
4612
|
+
if not parsed_args.auto_approve:
|
|
4613
|
+
if not self.ask_user_yesno(
|
|
4614
|
+
_(
|
|
4615
|
+
"Resetting the server state can make it much harder "
|
|
4616
|
+
"to recover a server from an error state. If the "
|
|
4617
|
+
"server is in error status due to a failed move "
|
|
4618
|
+
"operation then this is likely not the correct "
|
|
4619
|
+
"approach to fix the problem. Do you wish to continue?"
|
|
4620
|
+
)
|
|
4621
|
+
):
|
|
4622
|
+
return
|
|
4558
4623
|
compute_client.reset_server_state(server, state=parsed_args.state)
|
|
4559
4624
|
|
|
4560
4625
|
if parsed_args.root_password:
|
|
@@ -82,7 +82,7 @@ class ServerActionEventColumn(columns.FormattableColumn):
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def _get_server_event_columns(item, client):
|
|
85
|
-
hidden_columns = ['name', 'server_id', 'links', 'location']
|
|
85
|
+
hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time']
|
|
86
86
|
|
|
87
87
|
if not sdk_utils.supports_microversion(client, '2.58'):
|
|
88
88
|
# updated_at was introduced in 2.58
|
|
@@ -189,6 +189,16 @@ def find_domain(identity_client, name_or_id):
|
|
|
189
189
|
)
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
def find_domain_id_sdk(
|
|
193
|
+
identity_client, name_or_id, *, validate_actor_existence=True
|
|
194
|
+
):
|
|
195
|
+
return _find_sdk_id(
|
|
196
|
+
identity_client.find_domain,
|
|
197
|
+
name_or_id=name_or_id,
|
|
198
|
+
validate_actor_existence=validate_actor_existence,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
192
202
|
def find_group(identity_client, name_or_id, domain_name_or_id=None):
|
|
193
203
|
if domain_name_or_id is None:
|
|
194
204
|
return _find_identity_resource(
|
|
@@ -204,6 +214,33 @@ def find_group(identity_client, name_or_id, domain_name_or_id=None):
|
|
|
204
214
|
)
|
|
205
215
|
|
|
206
216
|
|
|
217
|
+
def find_group_id_sdk(
|
|
218
|
+
identity_client,
|
|
219
|
+
name_or_id,
|
|
220
|
+
domain_name_or_id=None,
|
|
221
|
+
*,
|
|
222
|
+
validate_actor_existence=True,
|
|
223
|
+
):
|
|
224
|
+
if domain_name_or_id is None:
|
|
225
|
+
return _find_sdk_id(
|
|
226
|
+
identity_client.find_group,
|
|
227
|
+
name_or_id=name_or_id,
|
|
228
|
+
validate_actor_existence=validate_actor_existence,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
domain_id = find_domain_id_sdk(
|
|
232
|
+
identity_client,
|
|
233
|
+
name_or_id=domain_name_or_id,
|
|
234
|
+
validate_actor_existence=validate_actor_existence,
|
|
235
|
+
)
|
|
236
|
+
return _find_sdk_id(
|
|
237
|
+
identity_client.find_group,
|
|
238
|
+
name_or_id=name_or_id,
|
|
239
|
+
validate_actor_existence=validate_actor_existence,
|
|
240
|
+
domain_id=domain_id,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
207
244
|
def find_project(identity_client, name_or_id, domain_name_or_id=None):
|
|
208
245
|
if domain_name_or_id is None:
|
|
209
246
|
return _find_identity_resource(
|
|
@@ -229,6 +266,32 @@ def find_user(identity_client, name_or_id, domain_name_or_id=None):
|
|
|
229
266
|
)
|
|
230
267
|
|
|
231
268
|
|
|
269
|
+
def find_user_id_sdk(
|
|
270
|
+
identity_client,
|
|
271
|
+
name_or_id,
|
|
272
|
+
domain_name_or_id=None,
|
|
273
|
+
*,
|
|
274
|
+
validate_actor_existence=True,
|
|
275
|
+
):
|
|
276
|
+
if domain_name_or_id is None:
|
|
277
|
+
return _find_sdk_id(
|
|
278
|
+
identity_client.find_user,
|
|
279
|
+
name_or_id=name_or_id,
|
|
280
|
+
validate_actor_existence=validate_actor_existence,
|
|
281
|
+
)
|
|
282
|
+
domain_id = find_domain_id_sdk(
|
|
283
|
+
identity_client,
|
|
284
|
+
name_or_id=domain_name_or_id,
|
|
285
|
+
validate_actor_existence=validate_actor_existence,
|
|
286
|
+
)
|
|
287
|
+
return _find_sdk_id(
|
|
288
|
+
identity_client.find_user,
|
|
289
|
+
name_or_id=name_or_id,
|
|
290
|
+
validate_actor_existence=validate_actor_existence,
|
|
291
|
+
domain_id=domain_id,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
232
295
|
def _find_identity_resource(
|
|
233
296
|
identity_client_manager, name_or_id, resource_type, **kwargs
|
|
234
297
|
):
|
|
@@ -269,6 +332,22 @@ def _find_identity_resource(
|
|
|
269
332
|
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
|
|
270
333
|
|
|
271
334
|
|
|
335
|
+
def _find_sdk_id(
|
|
336
|
+
find_command, name_or_id, *, validate_actor_existence=True, **kwargs
|
|
337
|
+
):
|
|
338
|
+
try:
|
|
339
|
+
resource = find_command(
|
|
340
|
+
name_or_id=name_or_id, ignore_missing=False, **kwargs
|
|
341
|
+
)
|
|
342
|
+
except sdk_exceptions.ForbiddenException:
|
|
343
|
+
return name_or_id
|
|
344
|
+
except sdk_exceptions.ResourceNotFound as exc:
|
|
345
|
+
if not validate_actor_existence:
|
|
346
|
+
return name_or_id
|
|
347
|
+
raise exceptions.CommandError from exc
|
|
348
|
+
return resource.id
|
|
349
|
+
|
|
350
|
+
|
|
272
351
|
def get_immutable_options(parsed_args):
|
|
273
352
|
options = {}
|
|
274
353
|
if parsed_args.immutable:
|
|
@@ -269,9 +269,9 @@ class ListApplicationCredential(command.Lister):
|
|
|
269
269
|
def take_action(self, parsed_args):
|
|
270
270
|
identity_client = self.app.client_manager.sdk_connection.identity
|
|
271
271
|
if parsed_args.user:
|
|
272
|
-
user_id = common.
|
|
272
|
+
user_id = common.find_user_id_sdk(
|
|
273
273
|
identity_client, parsed_args.user, parsed_args.user_domain
|
|
274
|
-
)
|
|
274
|
+
)
|
|
275
275
|
else:
|
|
276
276
|
conn = self.app.client_manager.sdk_connection
|
|
277
277
|
user_id = conn.config.get_auth().get_user_id(conn.identity)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import logging
|
|
19
19
|
|
|
20
|
-
from
|
|
20
|
+
from openstack import exceptions as sdk_exceptions
|
|
21
21
|
from osc_lib.command import command
|
|
22
22
|
from osc_lib import exceptions
|
|
23
23
|
from osc_lib import utils
|
|
@@ -29,6 +29,31 @@ from openstackclient.identity import common
|
|
|
29
29
|
LOG = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
def _format_domain(domain):
|
|
33
|
+
columns = (
|
|
34
|
+
'id',
|
|
35
|
+
'name',
|
|
36
|
+
'is_enabled',
|
|
37
|
+
'description',
|
|
38
|
+
'options',
|
|
39
|
+
)
|
|
40
|
+
column_headers = (
|
|
41
|
+
'id',
|
|
42
|
+
'name',
|
|
43
|
+
'enabled',
|
|
44
|
+
'description',
|
|
45
|
+
'options',
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
column_headers,
|
|
50
|
+
utils.get_item_properties(
|
|
51
|
+
domain,
|
|
52
|
+
columns,
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
32
57
|
class CreateDomain(command.ShowOne):
|
|
33
58
|
_description = _("Create new domain")
|
|
34
59
|
|
|
@@ -47,12 +72,15 @@ class CreateDomain(command.ShowOne):
|
|
|
47
72
|
enable_group = parser.add_mutually_exclusive_group()
|
|
48
73
|
enable_group.add_argument(
|
|
49
74
|
'--enable',
|
|
75
|
+
dest='is_enabled',
|
|
50
76
|
action='store_true',
|
|
77
|
+
default=True,
|
|
51
78
|
help=_('Enable domain (default)'),
|
|
52
79
|
)
|
|
53
80
|
enable_group.add_argument(
|
|
54
81
|
'--disable',
|
|
55
|
-
|
|
82
|
+
dest='is_enabled',
|
|
83
|
+
action='store_false',
|
|
56
84
|
help=_('Disable domain'),
|
|
57
85
|
)
|
|
58
86
|
parser.add_argument(
|
|
@@ -64,32 +92,25 @@ class CreateDomain(command.ShowOne):
|
|
|
64
92
|
return parser
|
|
65
93
|
|
|
66
94
|
def take_action(self, parsed_args):
|
|
67
|
-
identity_client = self.app.client_manager.identity
|
|
68
|
-
|
|
69
|
-
enabled = True
|
|
70
|
-
if parsed_args.disable:
|
|
71
|
-
enabled = False
|
|
95
|
+
identity_client = self.app.client_manager.sdk_connection.identity
|
|
72
96
|
|
|
73
97
|
options = common.get_immutable_options(parsed_args)
|
|
74
98
|
|
|
75
99
|
try:
|
|
76
|
-
domain = identity_client.
|
|
100
|
+
domain = identity_client.create_domain(
|
|
77
101
|
name=parsed_args.name,
|
|
78
102
|
description=parsed_args.description,
|
|
79
103
|
options=options,
|
|
80
|
-
|
|
104
|
+
is_enabled=parsed_args.is_enabled,
|
|
81
105
|
)
|
|
82
|
-
except
|
|
106
|
+
except sdk_exceptions.ConflictException:
|
|
83
107
|
if parsed_args.or_show:
|
|
84
|
-
domain =
|
|
85
|
-
identity_client.domains, parsed_args.name
|
|
86
|
-
)
|
|
108
|
+
domain = identity_client.find_domain(parsed_args.name)
|
|
87
109
|
LOG.info(_('Returning existing domain %s'), domain.name)
|
|
88
110
|
else:
|
|
89
111
|
raise
|
|
90
112
|
|
|
91
|
-
domain
|
|
92
|
-
return zip(*sorted(domain._info.items()))
|
|
113
|
+
return _format_domain(domain)
|
|
93
114
|
|
|
94
115
|
|
|
95
116
|
class DeleteDomain(command.Command):
|
|
@@ -106,12 +127,12 @@ class DeleteDomain(command.Command):
|
|
|
106
127
|
return parser
|
|
107
128
|
|
|
108
129
|
def take_action(self, parsed_args):
|
|
109
|
-
identity_client = self.app.client_manager.identity
|
|
130
|
+
identity_client = self.app.client_manager.sdk_connection.identity
|
|
110
131
|
result = 0
|
|
111
132
|
for i in parsed_args.domain:
|
|
112
133
|
try:
|
|
113
|
-
domain =
|
|
114
|
-
identity_client.
|
|
134
|
+
domain = identity_client.find_domain(i, ignore_missing=False)
|
|
135
|
+
identity_client.delete_domain(domain.id)
|
|
115
136
|
except Exception as e:
|
|
116
137
|
result += 1
|
|
117
138
|
LOG.error(
|
|
@@ -143,7 +164,7 @@ class ListDomain(command.Lister):
|
|
|
143
164
|
)
|
|
144
165
|
parser.add_argument(
|
|
145
166
|
'--enabled',
|
|
146
|
-
dest='
|
|
167
|
+
dest='is_enabled',
|
|
147
168
|
action='store_true',
|
|
148
169
|
help=_('The domains that are enabled will be returned'),
|
|
149
170
|
)
|
|
@@ -153,13 +174,17 @@ class ListDomain(command.Lister):
|
|
|
153
174
|
kwargs = {}
|
|
154
175
|
if parsed_args.name:
|
|
155
176
|
kwargs['name'] = parsed_args.name
|
|
156
|
-
if parsed_args.
|
|
157
|
-
kwargs['
|
|
177
|
+
if parsed_args.is_enabled:
|
|
178
|
+
kwargs['is_enabled'] = True
|
|
179
|
+
|
|
180
|
+
columns = ('id', 'name', 'is_enabled', 'description')
|
|
181
|
+
column_headers = ('ID', 'Name', 'Enabled', 'Description')
|
|
182
|
+
data = self.app.client_manager.sdk_connection.identity.domains(
|
|
183
|
+
**kwargs
|
|
184
|
+
)
|
|
158
185
|
|
|
159
|
-
columns = ('ID', 'Name', 'Enabled', 'Description')
|
|
160
|
-
data = self.app.client_manager.identity.domains.list(**kwargs)
|
|
161
186
|
return (
|
|
162
|
-
|
|
187
|
+
column_headers,
|
|
163
188
|
(
|
|
164
189
|
utils.get_item_properties(
|
|
165
190
|
s,
|
|
@@ -194,38 +219,38 @@ class SetDomain(command.Command):
|
|
|
194
219
|
enable_group = parser.add_mutually_exclusive_group()
|
|
195
220
|
enable_group.add_argument(
|
|
196
221
|
'--enable',
|
|
222
|
+
dest='is_enabled',
|
|
197
223
|
action='store_true',
|
|
224
|
+
default=None,
|
|
198
225
|
help=_('Enable domain'),
|
|
199
226
|
)
|
|
200
227
|
enable_group.add_argument(
|
|
201
228
|
'--disable',
|
|
202
|
-
|
|
229
|
+
dest='is_enabled',
|
|
230
|
+
action='store_false',
|
|
231
|
+
default=None,
|
|
203
232
|
help=_('Disable domain'),
|
|
204
233
|
)
|
|
205
234
|
common.add_resource_option_to_parser(parser)
|
|
206
235
|
return parser
|
|
207
236
|
|
|
208
237
|
def take_action(self, parsed_args):
|
|
209
|
-
identity_client = self.app.client_manager.identity
|
|
210
|
-
domain =
|
|
211
|
-
identity_client.domains, parsed_args.domain
|
|
212
|
-
)
|
|
238
|
+
identity_client = self.app.client_manager.sdk_connection.identity
|
|
239
|
+
domain = identity_client.find_domain(parsed_args.domain)
|
|
213
240
|
kwargs = {}
|
|
214
241
|
if parsed_args.name:
|
|
215
242
|
kwargs['name'] = parsed_args.name
|
|
216
243
|
if parsed_args.description:
|
|
217
244
|
kwargs['description'] = parsed_args.description
|
|
218
245
|
|
|
219
|
-
if parsed_args.
|
|
220
|
-
kwargs['
|
|
221
|
-
if parsed_args.disable:
|
|
222
|
-
kwargs['enabled'] = False
|
|
246
|
+
if parsed_args.is_enabled is not None:
|
|
247
|
+
kwargs['is_enabled'] = parsed_args.is_enabled
|
|
223
248
|
|
|
224
249
|
options = common.get_immutable_options(parsed_args)
|
|
225
250
|
if options:
|
|
226
251
|
kwargs['options'] = options
|
|
227
252
|
|
|
228
|
-
identity_client.
|
|
253
|
+
identity_client.update_domain(domain.id, **kwargs)
|
|
229
254
|
|
|
230
255
|
|
|
231
256
|
class ShowDomain(command.ShowOne):
|
|
@@ -241,13 +266,7 @@ class ShowDomain(command.ShowOne):
|
|
|
241
266
|
return parser
|
|
242
267
|
|
|
243
268
|
def take_action(self, parsed_args):
|
|
244
|
-
identity_client = self.app.client_manager.identity
|
|
245
|
-
|
|
246
|
-
domain_str = common._get_token_resource(
|
|
247
|
-
identity_client, 'domain', parsed_args.domain
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
domain = utils.find_resource(identity_client.domains, domain_str)
|
|
269
|
+
identity_client = self.app.client_manager.sdk_connection.identity
|
|
270
|
+
domain = identity_client.find_domain(parsed_args.domain)
|
|
251
271
|
|
|
252
|
-
domain
|
|
253
|
-
return zip(*sorted(domain._info.items()))
|
|
272
|
+
return _format_domain(domain)
|