python-openstackclient 8.3.0__py3-none-any.whl → 10.0.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/__init__.py +2 -6
- openstackclient/api/api.py +41 -23
- openstackclient/api/compute_v2.py +44 -25
- openstackclient/api/object_store_v1.py +75 -97
- openstackclient/api/volume_v2.py +2 -1
- openstackclient/api/volume_v3.py +2 -1
- openstackclient/common/availability_zone.py +58 -42
- openstackclient/common/clientmanager.py +56 -29
- openstackclient/common/configuration.py +10 -3
- openstackclient/common/envvars.py +2 -2
- openstackclient/common/extension.py +14 -5
- openstackclient/common/limits.py +10 -5
- openstackclient/common/module.py +14 -6
- openstackclient/common/pagination.py +8 -2
- openstackclient/common/progressbar.py +7 -6
- openstackclient/common/project_cleanup.py +13 -7
- openstackclient/common/quota.py +126 -114
- openstackclient/common/versions.py +8 -2
- openstackclient/compute/client.py +7 -3
- openstackclient/compute/v2/agent.py +17 -10
- openstackclient/compute/v2/aggregate.py +36 -22
- openstackclient/compute/v2/console.py +14 -8
- openstackclient/compute/v2/console_connection.py +11 -3
- openstackclient/compute/v2/flavor.py +39 -21
- openstackclient/compute/v2/host.py +14 -6
- openstackclient/compute/v2/hypervisor.py +14 -5
- openstackclient/compute/v2/hypervisor_stats.py +10 -2
- openstackclient/compute/v2/keypair.py +29 -14
- openstackclient/compute/v2/server.py +251 -171
- openstackclient/compute/v2/server_backup.py +10 -4
- openstackclient/compute/v2/server_event.py +21 -12
- openstackclient/compute/v2/server_group.py +21 -11
- openstackclient/compute/v2/server_image.py +19 -10
- openstackclient/compute/v2/server_migration.py +24 -10
- openstackclient/compute/v2/server_share.py +274 -0
- openstackclient/compute/v2/server_volume.py +10 -4
- openstackclient/compute/v2/service.py +14 -7
- openstackclient/compute/v2/usage.py +26 -21
- openstackclient/identity/client.py +8 -3
- openstackclient/identity/common.py +103 -41
- openstackclient/identity/v2_0/catalog.py +14 -7
- openstackclient/identity/v2_0/ec2creds.py +21 -10
- openstackclient/identity/v2_0/endpoint.py +23 -11
- openstackclient/identity/v2_0/project.py +25 -14
- openstackclient/identity/v2_0/role.py +28 -14
- openstackclient/identity/v2_0/role_assignment.py +9 -3
- openstackclient/identity/v2_0/service.py +26 -12
- openstackclient/identity/v2_0/token.py +12 -5
- openstackclient/identity/v2_0/user.py +26 -15
- openstackclient/identity/v3/access_rule.py +26 -12
- openstackclient/identity/v3/application_credential.py +59 -24
- openstackclient/identity/v3/catalog.py +14 -7
- openstackclient/identity/v3/consumer.py +22 -11
- openstackclient/identity/v3/credential.py +36 -16
- openstackclient/identity/v3/domain.py +37 -18
- openstackclient/identity/v3/ec2creds.py +25 -12
- openstackclient/identity/v3/endpoint.py +42 -20
- openstackclient/identity/v3/endpoint_group.py +28 -17
- openstackclient/identity/v3/federation_protocol.py +71 -50
- openstackclient/identity/v3/group.py +55 -32
- openstackclient/identity/v3/identity_provider.py +92 -57
- openstackclient/identity/v3/implied_role.py +21 -9
- openstackclient/identity/v3/limit.py +115 -92
- openstackclient/identity/v3/mapping.py +26 -13
- openstackclient/identity/v3/policy.py +23 -12
- openstackclient/identity/v3/project.py +211 -122
- openstackclient/identity/v3/region.py +36 -16
- openstackclient/identity/v3/registered_limit.py +116 -109
- openstackclient/identity/v3/role.py +61 -31
- openstackclient/identity/v3/role_assignment.py +23 -6
- openstackclient/identity/v3/service.py +36 -16
- openstackclient/identity/v3/service_provider.py +37 -15
- openstackclient/identity/v3/tag.py +23 -17
- openstackclient/identity/v3/token.py +30 -14
- openstackclient/identity/v3/trust.py +32 -14
- openstackclient/identity/v3/unscoped_saml.py +10 -2
- openstackclient/identity/v3/user.py +49 -26
- openstackclient/image/client.py +7 -3
- openstackclient/image/v1/image.py +33 -26
- openstackclient/image/v2/cache.py +14 -9
- openstackclient/image/v2/image.py +76 -49
- openstackclient/image/v2/info.py +7 -1
- openstackclient/image/v2/metadef_namespaces.py +109 -13
- openstackclient/image/v2/metadef_objects.py +28 -15
- openstackclient/image/v2/metadef_properties.py +24 -13
- openstackclient/image/v2/metadef_resource_type_association.py +14 -7
- openstackclient/image/v2/metadef_resource_types.py +7 -1
- openstackclient/image/v2/task.py +15 -6
- openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po +7 -192
- openstackclient/network/client.py +7 -2
- openstackclient/network/common.py +16 -241
- openstackclient/network/utils.py +36 -22
- openstackclient/network/v2/address_group.py +27 -16
- openstackclient/network/v2/address_scope.py +24 -13
- openstackclient/network/v2/bgpvpn/bgpvpn.py +463 -0
- openstackclient/network/v2/bgpvpn/constants.py +30 -0
- openstackclient/network/v2/bgpvpn/network_association.py +214 -0
- openstackclient/network/v2/bgpvpn/port_association.py +490 -0
- openstackclient/network/v2/bgpvpn/router_association.py +288 -0
- openstackclient/network/v2/default_security_group_rule.py +19 -10
- openstackclient/network/v2/floating_ip.py +110 -159
- openstackclient/network/v2/floating_ip_port_forwarding.py +30 -18
- openstackclient/network/v2/fwaas/__init__.py +0 -0
- openstackclient/network/v2/fwaas/group.py +466 -0
- openstackclient/network/v2/fwaas/policy.py +518 -0
- openstackclient/network/v2/fwaas/rule.py +574 -0
- openstackclient/network/v2/ip_availability.py +13 -5
- openstackclient/network/v2/l3_conntrack_helper.py +22 -13
- openstackclient/network/v2/local_ip.py +24 -13
- openstackclient/network/v2/local_ip_association.py +14 -7
- openstackclient/network/v2/ndp_proxy.py +20 -11
- openstackclient/network/v2/network.py +129 -196
- openstackclient/network/v2/network_agent.py +46 -25
- openstackclient/network/v2/network_auto_allocated_topology.py +22 -11
- openstackclient/network/v2/network_flavor.py +27 -16
- openstackclient/network/v2/network_flavor_profile.py +23 -12
- openstackclient/network/v2/network_meter.py +21 -10
- openstackclient/network/v2/network_meter_rule.py +21 -11
- openstackclient/network/v2/network_qos_policy.py +25 -15
- openstackclient/network/v2/network_qos_rule.py +32 -17
- openstackclient/network/v2/network_qos_rule_type.py +13 -5
- openstackclient/network/v2/network_rbac.py +23 -12
- openstackclient/network/v2/network_segment.py +20 -11
- openstackclient/network/v2/network_segment_range.py +56 -29
- openstackclient/network/v2/network_service_provider.py +7 -1
- openstackclient/network/v2/network_trunk.py +38 -22
- openstackclient/network/v2/port.py +54 -29
- openstackclient/network/v2/router.py +75 -52
- openstackclient/network/v2/security_group.py +87 -157
- openstackclient/network/v2/security_group_rule.py +100 -280
- openstackclient/network/v2/subnet.py +49 -28
- openstackclient/network/v2/subnet_pool.py +30 -17
- openstackclient/network/v2/taas/tap_flow.py +22 -11
- openstackclient/network/v2/taas/tap_mirror.py +22 -11
- openstackclient/network/v2/taas/tap_service.py +23 -12
- openstackclient/object/client.py +7 -2
- openstackclient/object/v1/account.py +13 -6
- openstackclient/object/v1/container.py +25 -15
- openstackclient/object/v1/object.py +25 -15
- openstackclient/py.typed +0 -0
- openstackclient/shell.py +46 -10
- openstackclient/tests/functional/base.py +55 -20
- openstackclient/tests/functional/common/test_extension.py +4 -0
- openstackclient/tests/functional/common/test_quota.py +3 -1
- openstackclient/tests/functional/compute/v2/common.py +14 -13
- openstackclient/tests/functional/compute/v2/test_flavor.py +3 -1
- openstackclient/tests/functional/compute/v2/test_server.py +3 -0
- openstackclient/tests/functional/identity/v2/common.py +10 -6
- openstackclient/tests/functional/identity/v2/test_role.py +4 -4
- openstackclient/tests/functional/identity/v3/common.py +25 -19
- openstackclient/tests/functional/identity/v3/test_group.py +20 -20
- openstackclient/tests/functional/identity/v3/test_idp.py +3 -1
- openstackclient/tests/functional/identity/v3/test_limit.py +47 -0
- openstackclient/tests/functional/identity/v3/test_project.py +10 -10
- openstackclient/tests/functional/identity/v3/test_role.py +18 -18
- openstackclient/tests/functional/identity/v3/test_role_assignment.py +12 -12
- openstackclient/tests/functional/identity/v3/test_user.py +8 -8
- openstackclient/tests/functional/image/base.py +1 -6
- openstackclient/tests/functional/image/v2/test_metadef_objects.py +69 -0
- openstackclient/tests/functional/network/v2/common.py +5 -2
- openstackclient/tests/functional/network/v2/test_floating_ip.py +10 -4
- openstackclient/tests/functional/network/v2/test_ip_availability.py +4 -0
- openstackclient/tests/functional/network/v2/test_network_meter_rule.py +3 -2
- openstackclient/tests/functional/network/v2/test_network_segment.py +5 -0
- openstackclient/tests/functional/network/v2/test_subnet.py +13 -9
- openstackclient/tests/functional/object/v1/common.py +4 -0
- openstackclient/tests/functional/volume/v2/common.py +4 -0
- openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +27 -11
- openstackclient/tests/functional/volume/v2/test_volume_type.py +2 -2
- openstackclient/tests/functional/volume/v3/common.py +4 -0
- openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +56 -138
- openstackclient/tests/functional/volume/v3/test_volume_type.py +2 -2
- openstackclient/tests/unit/common/test_availability_zone.py +35 -49
- openstackclient/tests/unit/common/test_extension.py +2 -2
- openstackclient/tests/unit/common/test_module.py +12 -7
- openstackclient/tests/unit/common/test_project_cleanup.py +3 -1
- openstackclient/tests/unit/common/test_quota.py +62 -23
- openstackclient/tests/unit/compute/v2/fakes.py +25 -0
- openstackclient/tests/unit/compute/v2/test_flavor.py +28 -2
- openstackclient/tests/unit/compute/v2/test_keypair.py +6 -6
- openstackclient/tests/unit/compute/v2/test_server.py +17 -104
- openstackclient/tests/unit/compute/v2/test_server_share.py +287 -0
- openstackclient/tests/unit/identity/v3/fakes.py +3 -0
- openstackclient/tests/unit/identity/v3/test_group.py +4 -14
- openstackclient/tests/unit/identity/v3/test_identity_provider.py +303 -299
- openstackclient/tests/unit/identity/v3/test_limit.py +197 -145
- openstackclient/tests/unit/identity/v3/test_project.py +831 -512
- openstackclient/tests/unit/identity/v3/test_protocol.py +97 -88
- openstackclient/tests/unit/identity/v3/test_registered_limit.py +355 -220
- openstackclient/tests/unit/identity/v3/test_user.py +4 -4
- openstackclient/tests/unit/image/v2/test_image.py +16 -16
- openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +105 -6
- openstackclient/tests/unit/network/test_common.py +0 -155
- openstackclient/tests/unit/network/v2/bgpvpn/__init__.py +0 -0
- openstackclient/tests/unit/network/v2/bgpvpn/fakes.py +179 -0
- openstackclient/tests/unit/network/v2/bgpvpn/test_bgpvpn.py +584 -0
- openstackclient/tests/unit/network/v2/bgpvpn/test_network_association.py +285 -0
- openstackclient/tests/unit/network/v2/bgpvpn/test_port_association.py +384 -0
- openstackclient/tests/unit/network/v2/bgpvpn/test_router_association.py +297 -0
- openstackclient/tests/unit/network/v2/fwaas/__init__.py +0 -0
- openstackclient/tests/unit/network/v2/fwaas/test_group.py +897 -0
- openstackclient/tests/unit/network/v2/fwaas/test_policy.py +869 -0
- openstackclient/tests/unit/network/v2/fwaas/test_rule.py +980 -0
- openstackclient/tests/unit/network/v2/taas/{test_osc_tap_flow.py → test_tap_flow.py} +18 -25
- openstackclient/tests/unit/network/v2/taas/{test_osc_tap_mirror.py → test_tap_mirror.py} +19 -29
- openstackclient/tests/unit/network/v2/taas/{test_osc_tap_service.py → test_tap_service.py} +19 -29
- openstackclient/tests/unit/network/v2/test_address_group.py +2 -2
- openstackclient/tests/unit/network/v2/{test_floating_ip_network.py → test_floating_ip.py} +3 -2
- openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +13 -13
- openstackclient/tests/unit/network/v2/test_network_agent.py +8 -4
- openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +3 -3
- openstackclient/tests/unit/network/v2/test_network_flavor.py +2 -2
- openstackclient/tests/unit/network/v2/test_network_qos_policy.py +1 -1
- openstackclient/tests/unit/network/v2/test_network_qos_rule.py +2 -2
- openstackclient/tests/unit/network/v2/test_network_rbac.py +1 -1
- openstackclient/tests/unit/network/v2/test_network_segment.py +1 -1
- openstackclient/tests/unit/network/v2/test_network_segment_range.py +7 -10
- openstackclient/tests/unit/network/v2/test_network_trunk.py +1 -1
- openstackclient/tests/unit/network/v2/test_router.py +8 -9
- openstackclient/tests/unit/network/v2/{test_security_group_network.py → test_security_group.py} +1 -20
- openstackclient/tests/unit/network/v2/{test_security_group_rule_network.py → test_security_group_rule.py} +7 -41
- openstackclient/tests/unit/network/v2/test_subnet.py +2 -1
- openstackclient/tests/unit/network/v2/test_subnet_pool.py +2 -1
- openstackclient/tests/unit/object/v1/fakes.py +8 -7
- openstackclient/tests/unit/object/v1/test_container.py +65 -101
- openstackclient/tests/unit/object/v1/test_container_all.py +8 -1
- openstackclient/tests/unit/object/v1/test_object.py +44 -84
- openstackclient/tests/unit/object/v1/test_object_all.py +8 -1
- openstackclient/tests/unit/test_hacking.py +108 -0
- openstackclient/tests/unit/volume/v2/fakes.py +1 -0
- openstackclient/tests/unit/volume/v2/test_consistency_group.py +8 -2
- openstackclient/tests/unit/volume/v2/test_volume.py +7 -6
- openstackclient/tests/unit/volume/v2/test_volume_backup.py +1 -5
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +2 -1
- openstackclient/tests/unit/volume/v2/test_volume_type.py +2 -4
- openstackclient/tests/unit/volume/v3/fakes.py +1 -0
- openstackclient/tests/unit/volume/v3/test_volume.py +94 -15
- openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
- openstackclient/tests/unit/volume/v3/test_volume_backup.py +1 -5
- openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +55 -1
- openstackclient/tests/unit/volume/v3/test_volume_type.py +2 -4
- openstackclient/volume/client.py +7 -3
- openstackclient/volume/v2/backup_record.py +15 -6
- openstackclient/volume/v2/consistency_group.py +37 -25
- openstackclient/volume/v2/consistency_group_snapshot.py +27 -12
- openstackclient/volume/v2/qos_specs.py +30 -19
- openstackclient/volume/v2/service.py +17 -6
- openstackclient/volume/v2/volume.py +69 -34
- openstackclient/volume/v2/volume_backend.py +19 -6
- openstackclient/volume/v2/volume_backup.py +48 -22
- openstackclient/volume/v2/volume_host.py +6 -4
- openstackclient/volume/v2/volume_snapshot.py +52 -26
- openstackclient/volume/v2/volume_transfer_request.py +33 -15
- openstackclient/volume/v2/volume_type.py +46 -27
- openstackclient/volume/v3/block_storage_cleanup.py +11 -3
- openstackclient/volume/v3/block_storage_cluster.py +19 -7
- openstackclient/volume/v3/block_storage_log_level.py +15 -6
- openstackclient/volume/v3/block_storage_manage.py +10 -4
- openstackclient/volume/v3/block_storage_resource_filter.py +17 -5
- openstackclient/volume/v3/service.py +16 -6
- openstackclient/volume/v3/volume.py +103 -46
- openstackclient/volume/v3/volume_attachment.py +43 -21
- openstackclient/volume/v3/volume_backup.py +55 -26
- openstackclient/volume/v3/volume_group.py +23 -13
- openstackclient/volume/v3/volume_group_snapshot.py +32 -13
- openstackclient/volume/v3/volume_group_type.py +26 -13
- openstackclient/volume/v3/volume_message.py +15 -7
- openstackclient/volume/v3/volume_snapshot.py +71 -34
- openstackclient/volume/v3/volume_transfer_request.py +33 -15
- openstackclient/volume/v3/volume_type.py +45 -27
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/METADATA +6 -6
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/RECORD +279 -267
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/WHEEL +1 -1
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/entry_points.txt +53 -1
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/AUTHORS +9 -0
- python_openstackclient-10.0.0.dist-info/pbr.json +1 -0
- openstackclient/api/image_v1.py +0 -69
- openstackclient/api/image_v2.py +0 -79
- openstackclient/network/v2/floating_ip_pool.py +0 -38
- openstackclient/tests/functional/image/v1/test_image.py +0 -97
- openstackclient/tests/unit/api/test_image_v1.py +0 -96
- openstackclient/tests/unit/api/test_image_v2.py +0 -96
- openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +0 -248
- openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +0 -49
- openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +0 -39
- openstackclient/tests/unit/network/v2/test_network_compute.py +0 -404
- openstackclient/tests/unit/network/v2/test_security_group_compute.py +0 -392
- openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +0 -555
- python_openstackclient-8.3.0.dist-info/pbr.json +0 -1
- /openstackclient/{tests/functional/image/v1 → network/v2/bgpvpn}/__init__.py +0 -0
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/LICENSE +0 -0
- {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/top_level.txt +0 -0
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
"""Project action implementations"""
|
|
17
17
|
|
|
18
|
+
import argparse
|
|
19
|
+
from collections.abc import Iterable, Sequence
|
|
18
20
|
import logging
|
|
21
|
+
from typing import Any
|
|
19
22
|
|
|
20
|
-
from
|
|
23
|
+
from openstack import exceptions as sdk_exc
|
|
24
|
+
from openstack import utils as sdk_utils
|
|
21
25
|
from osc_lib.cli import parseractions
|
|
22
26
|
from osc_lib import exceptions
|
|
23
27
|
from osc_lib import utils
|
|
@@ -30,10 +34,25 @@ from openstackclient.identity.v3 import tag
|
|
|
30
34
|
LOG = logging.getLogger(__name__)
|
|
31
35
|
|
|
32
36
|
|
|
37
|
+
def _format_project(project: Any) -> tuple[tuple[str, ...], Any]:
|
|
38
|
+
# NOTE(0weng): Projects allow unknown attributes in the body, so extract
|
|
39
|
+
# the column names separately.
|
|
40
|
+
(column_headers, columns) = utils.get_osc_show_columns_for_sdk_resource(
|
|
41
|
+
project,
|
|
42
|
+
{'is_enabled': 'enabled'},
|
|
43
|
+
['links', 'location', 'parents_as_ids', 'subtree_as_ids'],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
column_headers,
|
|
48
|
+
utils.get_item_properties(project, columns),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
33
52
|
class CreateProject(command.ShowOne):
|
|
34
53
|
_description = _("Create new project")
|
|
35
54
|
|
|
36
|
-
def get_parser(self, prog_name):
|
|
55
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
37
56
|
parser = super().get_parser(prog_name)
|
|
38
57
|
parser.add_argument(
|
|
39
58
|
'name',
|
|
@@ -89,23 +108,18 @@ class CreateProject(command.ShowOne):
|
|
|
89
108
|
tag.add_tag_option_to_parser_for_create(parser, _('project'))
|
|
90
109
|
return parser
|
|
91
110
|
|
|
92
|
-
def take_action(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
parent = None
|
|
100
|
-
if parsed_args.parent:
|
|
101
|
-
parent = utils.find_resource(
|
|
102
|
-
identity_client.projects,
|
|
103
|
-
parsed_args.parent,
|
|
104
|
-
).id
|
|
111
|
+
def take_action(
|
|
112
|
+
self, parsed_args: argparse.Namespace
|
|
113
|
+
) -> tuple[Sequence[str], Iterable[Any]]:
|
|
114
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
115
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
116
|
+
)
|
|
105
117
|
|
|
106
118
|
kwargs = {}
|
|
119
|
+
|
|
107
120
|
if parsed_args.properties:
|
|
108
121
|
kwargs = parsed_args.properties.copy()
|
|
122
|
+
|
|
109
123
|
if 'is_domain' in kwargs.keys():
|
|
110
124
|
if kwargs['is_domain'].lower() == "true":
|
|
111
125
|
kwargs['is_domain'] = True
|
|
@@ -114,35 +128,55 @@ class CreateProject(command.ShowOne):
|
|
|
114
128
|
elif kwargs['is_domain'].lower() == "none":
|
|
115
129
|
kwargs['is_domain'] = None
|
|
116
130
|
|
|
117
|
-
|
|
131
|
+
if parsed_args.description:
|
|
132
|
+
kwargs['description'] = parsed_args.description
|
|
133
|
+
|
|
134
|
+
if parsed_args.name:
|
|
135
|
+
kwargs['name'] = parsed_args.name
|
|
136
|
+
|
|
137
|
+
domain = None
|
|
138
|
+
if parsed_args.domain:
|
|
139
|
+
domain = common.find_domain_id_sdk(
|
|
140
|
+
identity_client, parsed_args.domain
|
|
141
|
+
)
|
|
142
|
+
kwargs['domain_id'] = domain
|
|
143
|
+
|
|
144
|
+
if parsed_args.parent:
|
|
145
|
+
kwargs['parent_id'] = common.find_project_id_sdk(
|
|
146
|
+
identity_client,
|
|
147
|
+
parsed_args.parent,
|
|
148
|
+
domain_name_or_id=domain,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
kwargs['is_enabled'] = parsed_args.enabled
|
|
152
|
+
|
|
153
|
+
if parsed_args.tags:
|
|
154
|
+
kwargs['tags'] = list(set(parsed_args.tags))
|
|
118
155
|
|
|
119
|
-
options = {}
|
|
120
156
|
if parsed_args.immutable is not None:
|
|
121
|
-
|
|
157
|
+
kwargs['options'] = {'immutable': parsed_args.immutable}
|
|
122
158
|
|
|
123
159
|
try:
|
|
124
|
-
project = identity_client.
|
|
125
|
-
name=parsed_args.name,
|
|
126
|
-
domain=domain,
|
|
127
|
-
parent=parent,
|
|
128
|
-
description=parsed_args.description,
|
|
129
|
-
enabled=parsed_args.enabled,
|
|
130
|
-
options=options,
|
|
160
|
+
project = identity_client.create_project(
|
|
131
161
|
**kwargs,
|
|
132
162
|
)
|
|
133
|
-
except
|
|
163
|
+
except sdk_exc.ConflictException:
|
|
134
164
|
if parsed_args.or_show:
|
|
135
|
-
|
|
136
|
-
identity_client.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
165
|
+
if parsed_args.domain:
|
|
166
|
+
project = identity_client.find_project(
|
|
167
|
+
parsed_args.name,
|
|
168
|
+
domain_id=domain,
|
|
169
|
+
ignore_missing=False,
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
project = identity_client.find_project(
|
|
173
|
+
parsed_args.name, ignore_missing=False
|
|
174
|
+
)
|
|
140
175
|
LOG.info(_('Returning existing project %s'), project.name)
|
|
141
176
|
else:
|
|
142
177
|
raise
|
|
143
178
|
|
|
144
|
-
project
|
|
145
|
-
return zip(*sorted(project._info.items()))
|
|
179
|
+
return _format_project(project)
|
|
146
180
|
|
|
147
181
|
|
|
148
182
|
class DeleteProject(command.Command):
|
|
@@ -155,7 +189,7 @@ class DeleteProject(command.Command):
|
|
|
155
189
|
"regardless."
|
|
156
190
|
)
|
|
157
191
|
|
|
158
|
-
def get_parser(self, prog_name):
|
|
192
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
159
193
|
parser = super().get_parser(prog_name)
|
|
160
194
|
parser.add_argument(
|
|
161
195
|
'projects',
|
|
@@ -170,24 +204,22 @@ class DeleteProject(command.Command):
|
|
|
170
204
|
)
|
|
171
205
|
return parser
|
|
172
206
|
|
|
173
|
-
def take_action(self, parsed_args):
|
|
174
|
-
identity_client =
|
|
207
|
+
def take_action(self, parsed_args: argparse.Namespace) -> None:
|
|
208
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
209
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
210
|
+
)
|
|
175
211
|
|
|
176
|
-
domain = None
|
|
177
|
-
if parsed_args.domain:
|
|
178
|
-
domain = common.find_domain(identity_client, parsed_args.domain)
|
|
179
212
|
errors = 0
|
|
180
213
|
for project in parsed_args.projects:
|
|
181
214
|
try:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
identity_client.projects.delete(project_obj.id)
|
|
215
|
+
project = common.find_project_id_sdk(
|
|
216
|
+
identity_client,
|
|
217
|
+
project,
|
|
218
|
+
domain_name_or_id=parsed_args.domain,
|
|
219
|
+
validate_actor_existence=True,
|
|
220
|
+
validate_domain_actor_existence=False,
|
|
221
|
+
)
|
|
222
|
+
identity_client.delete_project(project)
|
|
191
223
|
except Exception as e:
|
|
192
224
|
errors += 1
|
|
193
225
|
LOG.error(
|
|
@@ -210,7 +242,7 @@ class DeleteProject(command.Command):
|
|
|
210
242
|
class ListProject(command.Lister):
|
|
211
243
|
_description = _("List projects")
|
|
212
244
|
|
|
213
|
-
def get_parser(self, prog_name):
|
|
245
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
214
246
|
parser = super().get_parser(prog_name)
|
|
215
247
|
parser.add_argument(
|
|
216
248
|
'--domain',
|
|
@@ -267,39 +299,51 @@ class ListProject(command.Lister):
|
|
|
267
299
|
tag.add_tag_filtering_option_to_parser(parser, _('projects'))
|
|
268
300
|
return parser
|
|
269
301
|
|
|
270
|
-
def take_action(
|
|
271
|
-
|
|
272
|
-
|
|
302
|
+
def take_action(
|
|
303
|
+
self, parsed_args: argparse.Namespace
|
|
304
|
+
) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
|
|
305
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
306
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
column_headers: tuple[str, ...] = ('ID', 'Name')
|
|
310
|
+
if parsed_args.long:
|
|
311
|
+
column_headers += ('Domain ID', 'Description', 'Enabled')
|
|
312
|
+
|
|
313
|
+
columns: tuple[str, ...] = ('id', 'name')
|
|
273
314
|
if parsed_args.long:
|
|
274
|
-
columns += ('
|
|
315
|
+
columns += ('domain_id', 'description', 'is_enabled')
|
|
316
|
+
|
|
275
317
|
kwargs = {}
|
|
276
318
|
|
|
277
319
|
domain_id = None
|
|
278
320
|
if parsed_args.domain:
|
|
279
|
-
domain_id = common.
|
|
321
|
+
domain_id = common.find_domain_id_sdk(
|
|
280
322
|
identity_client, parsed_args.domain
|
|
281
|
-
)
|
|
282
|
-
kwargs['
|
|
323
|
+
)
|
|
324
|
+
kwargs['domain_id'] = domain_id
|
|
283
325
|
|
|
284
326
|
if parsed_args.parent:
|
|
285
|
-
parent_id = common.
|
|
286
|
-
identity_client,
|
|
287
|
-
|
|
288
|
-
|
|
327
|
+
parent_id = common.find_project_id_sdk(
|
|
328
|
+
identity_client,
|
|
329
|
+
parsed_args.parent,
|
|
330
|
+
domain_name_or_id=domain_id,
|
|
331
|
+
)
|
|
332
|
+
kwargs['parent_id'] = parent_id
|
|
289
333
|
|
|
334
|
+
user = None
|
|
290
335
|
if parsed_args.user:
|
|
291
336
|
if parsed_args.domain:
|
|
292
|
-
|
|
293
|
-
identity_client
|
|
337
|
+
user = common.find_user_id_sdk(
|
|
338
|
+
identity_client,
|
|
294
339
|
parsed_args.user,
|
|
295
|
-
|
|
296
|
-
)
|
|
340
|
+
domain_name_or_id=domain_id,
|
|
341
|
+
)
|
|
297
342
|
else:
|
|
298
|
-
|
|
299
|
-
identity_client
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
kwargs['user'] = user_id
|
|
343
|
+
user = common.find_user_id_sdk(
|
|
344
|
+
identity_client,
|
|
345
|
+
parsed_args.user,
|
|
346
|
+
)
|
|
303
347
|
|
|
304
348
|
if parsed_args.is_enabled is not None:
|
|
305
349
|
kwargs['is_enabled'] = parsed_args.is_enabled
|
|
@@ -308,39 +352,36 @@ class ListProject(command.Lister):
|
|
|
308
352
|
|
|
309
353
|
if parsed_args.my_projects:
|
|
310
354
|
# NOTE(adriant): my-projects supersedes all the other filters.
|
|
311
|
-
kwargs = {
|
|
355
|
+
kwargs = {}
|
|
356
|
+
user = self.app.client_manager.auth_ref.user_id
|
|
312
357
|
|
|
313
|
-
|
|
314
|
-
data = identity_client.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
358
|
+
if user:
|
|
359
|
+
data = list(identity_client.user_projects(user, **kwargs))
|
|
360
|
+
else:
|
|
361
|
+
try:
|
|
362
|
+
data = list(identity_client.projects(**kwargs))
|
|
363
|
+
except sdk_exc.ForbiddenException:
|
|
364
|
+
# NOTE(adriant): if no filters, assume a forbidden is non-admin
|
|
365
|
+
# wanting their own project list.
|
|
366
|
+
if not kwargs:
|
|
367
|
+
user = self.app.client_manager.auth_ref.user_id
|
|
368
|
+
data = list(identity_client.user_projects(user))
|
|
369
|
+
else:
|
|
370
|
+
raise
|
|
323
371
|
|
|
324
372
|
if parsed_args.sort:
|
|
325
|
-
data = utils.sort_items(data, parsed_args.sort)
|
|
373
|
+
data = list(utils.sort_items(data, parsed_args.sort))
|
|
326
374
|
|
|
327
375
|
return (
|
|
328
|
-
|
|
329
|
-
(
|
|
330
|
-
utils.get_item_properties(
|
|
331
|
-
s,
|
|
332
|
-
columns,
|
|
333
|
-
formatters={},
|
|
334
|
-
)
|
|
335
|
-
for s in data
|
|
336
|
-
),
|
|
376
|
+
column_headers,
|
|
377
|
+
(utils.get_item_properties(s, columns) for s in data),
|
|
337
378
|
)
|
|
338
379
|
|
|
339
380
|
|
|
340
381
|
class SetProject(command.Command):
|
|
341
382
|
_description = _("Set project properties")
|
|
342
383
|
|
|
343
|
-
def get_parser(self, prog_name):
|
|
384
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
344
385
|
parser = super().get_parser(prog_name)
|
|
345
386
|
parser.add_argument(
|
|
346
387
|
'project',
|
|
@@ -391,11 +432,9 @@ class SetProject(command.Command):
|
|
|
391
432
|
tag.add_tag_option_to_parser_for_set(parser, _('project'))
|
|
392
433
|
return parser
|
|
393
434
|
|
|
394
|
-
def take_action(self, parsed_args):
|
|
395
|
-
identity_client =
|
|
396
|
-
|
|
397
|
-
project = common.find_project(
|
|
398
|
-
identity_client, parsed_args.project, parsed_args.domain
|
|
435
|
+
def take_action(self, parsed_args: argparse.Namespace) -> None:
|
|
436
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
437
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
399
438
|
)
|
|
400
439
|
|
|
401
440
|
kwargs = {}
|
|
@@ -409,15 +448,56 @@ class SetProject(command.Command):
|
|
|
409
448
|
kwargs['options'] = {'immutable': parsed_args.immutable}
|
|
410
449
|
if parsed_args.properties:
|
|
411
450
|
kwargs.update(parsed_args.properties)
|
|
412
|
-
tag.update_tags_in_args(parsed_args, project, kwargs)
|
|
413
451
|
|
|
414
|
-
|
|
452
|
+
if parsed_args.domain:
|
|
453
|
+
domain = common.find_domain_id_sdk(
|
|
454
|
+
identity_client,
|
|
455
|
+
parsed_args.domain,
|
|
456
|
+
validate_actor_existence=False,
|
|
457
|
+
)
|
|
458
|
+
project = identity_client.find_project(
|
|
459
|
+
parsed_args.project,
|
|
460
|
+
domain_id=domain,
|
|
461
|
+
ignore_missing=True,
|
|
462
|
+
)
|
|
463
|
+
else:
|
|
464
|
+
project = identity_client.find_project(
|
|
465
|
+
parsed_args.project,
|
|
466
|
+
ignore_missing=True,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
if (
|
|
470
|
+
parsed_args.tags
|
|
471
|
+
or parsed_args.remove_tags
|
|
472
|
+
or parsed_args.clear_tags
|
|
473
|
+
):
|
|
474
|
+
existing_tags = []
|
|
475
|
+
if project:
|
|
476
|
+
existing_tags = project.tags
|
|
477
|
+
|
|
478
|
+
if parsed_args.clear_tags:
|
|
479
|
+
kwargs['tags'] = []
|
|
480
|
+
else:
|
|
481
|
+
existing_tags_set = set(existing_tags)
|
|
482
|
+
if parsed_args.remove_tags:
|
|
483
|
+
tags = sorted(
|
|
484
|
+
existing_tags_set - set(parsed_args.remove_tags)
|
|
485
|
+
)
|
|
486
|
+
if parsed_args.tags:
|
|
487
|
+
tags = sorted(
|
|
488
|
+
existing_tags_set.union(set(parsed_args.tags))
|
|
489
|
+
)
|
|
490
|
+
kwargs['tags'] = tags
|
|
491
|
+
|
|
492
|
+
project_id = project.id if project else parsed_args.project
|
|
493
|
+
|
|
494
|
+
identity_client.update_project(project_id, **kwargs)
|
|
415
495
|
|
|
416
496
|
|
|
417
497
|
class ShowProject(command.ShowOne):
|
|
418
498
|
_description = _("Display project details")
|
|
419
499
|
|
|
420
|
-
def get_parser(self, prog_name):
|
|
500
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
421
501
|
parser = super().get_parser(prog_name)
|
|
422
502
|
parser.add_argument(
|
|
423
503
|
'project',
|
|
@@ -443,32 +523,41 @@ class ShowProject(command.ShowOne):
|
|
|
443
523
|
)
|
|
444
524
|
return parser
|
|
445
525
|
|
|
446
|
-
def take_action(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
526
|
+
def take_action(
|
|
527
|
+
self, parsed_args: argparse.Namespace
|
|
528
|
+
) -> tuple[Sequence[str], Iterable[Any]]:
|
|
529
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
530
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
451
531
|
)
|
|
452
532
|
|
|
533
|
+
kwargs: dict[str, Any] = {}
|
|
534
|
+
|
|
535
|
+
domain = None
|
|
453
536
|
if parsed_args.domain:
|
|
454
|
-
domain = common.
|
|
455
|
-
|
|
456
|
-
identity_client.projects, project_str, domain_id=domain.id
|
|
457
|
-
)
|
|
458
|
-
else:
|
|
459
|
-
project = utils.find_resource(
|
|
460
|
-
identity_client.projects, project_str
|
|
537
|
+
domain = common.find_domain_id_sdk(
|
|
538
|
+
identity_client, parsed_args.domain
|
|
461
539
|
)
|
|
462
540
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
541
|
+
kwargs['domain_id'] = domain
|
|
542
|
+
|
|
543
|
+
# Get project id first; otherwise, find_project() can't find
|
|
544
|
+
# parents/children if only project name was given
|
|
545
|
+
project = common.find_project_id_sdk(
|
|
546
|
+
identity_client,
|
|
547
|
+
parsed_args.project,
|
|
548
|
+
domain_name_or_id=domain,
|
|
549
|
+
validate_actor_existence=False,
|
|
550
|
+
validate_domain_actor_existence=False,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Include these options as query parameters if they are provided
|
|
554
|
+
if parsed_args.parents:
|
|
555
|
+
kwargs['parents_as_ids'] = True
|
|
556
|
+
if parsed_args.children:
|
|
557
|
+
kwargs['subtree_as_ids'] = True
|
|
558
|
+
|
|
559
|
+
project_obj = identity_client.find_project(
|
|
560
|
+
project, **kwargs, ignore_missing=False
|
|
561
|
+
)
|
|
472
562
|
|
|
473
|
-
|
|
474
|
-
return zip(*sorted(project._info.items()))
|
|
563
|
+
return _format_project(project_obj)
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
"""Identity v3 Region action implementations"""
|
|
15
15
|
|
|
16
|
+
import argparse
|
|
17
|
+
from collections.abc import Iterable, Sequence
|
|
16
18
|
import logging
|
|
19
|
+
from typing import Any
|
|
17
20
|
|
|
21
|
+
from openstack import utils as sdk_utils
|
|
18
22
|
from osc_lib import exceptions
|
|
19
23
|
from osc_lib import utils
|
|
20
24
|
|
|
@@ -25,7 +29,7 @@ from openstackclient.i18n import _
|
|
|
25
29
|
LOG = logging.getLogger(__name__)
|
|
26
30
|
|
|
27
31
|
|
|
28
|
-
def _format_region(region):
|
|
32
|
+
def _format_region(region: Any) -> tuple[tuple[str, ...], Any]:
|
|
29
33
|
columns = ('id', 'description', 'parent_region_id')
|
|
30
34
|
column_headers = ('region', 'description', 'parent_region')
|
|
31
35
|
return (
|
|
@@ -37,7 +41,7 @@ def _format_region(region):
|
|
|
37
41
|
class CreateRegion(command.ShowOne):
|
|
38
42
|
_description = _("Create new region")
|
|
39
43
|
|
|
40
|
-
def get_parser(self, prog_name):
|
|
44
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
41
45
|
parser = super().get_parser(prog_name)
|
|
42
46
|
# NOTE(stevemar): The API supports an optional region ID, but that
|
|
43
47
|
# seems like poor UX, we will only support user-defined IDs.
|
|
@@ -58,8 +62,12 @@ class CreateRegion(command.ShowOne):
|
|
|
58
62
|
)
|
|
59
63
|
return parser
|
|
60
64
|
|
|
61
|
-
def take_action(
|
|
62
|
-
|
|
65
|
+
def take_action(
|
|
66
|
+
self, parsed_args: argparse.Namespace
|
|
67
|
+
) -> tuple[Sequence[str], Iterable[Any]]:
|
|
68
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
69
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
70
|
+
)
|
|
63
71
|
|
|
64
72
|
region = identity_client.create_region(
|
|
65
73
|
id=parsed_args.region,
|
|
@@ -73,7 +81,7 @@ class CreateRegion(command.ShowOne):
|
|
|
73
81
|
class DeleteRegion(command.Command):
|
|
74
82
|
_description = _("Delete region(s)")
|
|
75
83
|
|
|
76
|
-
def get_parser(self, prog_name):
|
|
84
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
77
85
|
parser = super().get_parser(prog_name)
|
|
78
86
|
parser.add_argument(
|
|
79
87
|
'region',
|
|
@@ -83,8 +91,10 @@ class DeleteRegion(command.Command):
|
|
|
83
91
|
)
|
|
84
92
|
return parser
|
|
85
93
|
|
|
86
|
-
def take_action(self, parsed_args):
|
|
87
|
-
identity_client =
|
|
94
|
+
def take_action(self, parsed_args: argparse.Namespace) -> None:
|
|
95
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
96
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
97
|
+
)
|
|
88
98
|
result = 0
|
|
89
99
|
for i in parsed_args.region:
|
|
90
100
|
try:
|
|
@@ -108,7 +118,7 @@ class DeleteRegion(command.Command):
|
|
|
108
118
|
class ListRegion(command.Lister):
|
|
109
119
|
_description = _("List regions")
|
|
110
120
|
|
|
111
|
-
def get_parser(self, prog_name):
|
|
121
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
112
122
|
parser = super().get_parser(prog_name)
|
|
113
123
|
parser.add_argument(
|
|
114
124
|
'--parent-region',
|
|
@@ -117,8 +127,12 @@ class ListRegion(command.Lister):
|
|
|
117
127
|
)
|
|
118
128
|
return parser
|
|
119
129
|
|
|
120
|
-
def take_action(
|
|
121
|
-
|
|
130
|
+
def take_action(
|
|
131
|
+
self, parsed_args: argparse.Namespace
|
|
132
|
+
) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
|
|
133
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
134
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
135
|
+
)
|
|
122
136
|
|
|
123
137
|
kwargs = {}
|
|
124
138
|
if parsed_args.parent_region:
|
|
@@ -144,7 +158,7 @@ class ListRegion(command.Lister):
|
|
|
144
158
|
class SetRegion(command.Command):
|
|
145
159
|
_description = _("Set region properties")
|
|
146
160
|
|
|
147
|
-
def get_parser(self, prog_name):
|
|
161
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
148
162
|
parser = super().get_parser(prog_name)
|
|
149
163
|
parser.add_argument(
|
|
150
164
|
'region',
|
|
@@ -163,8 +177,10 @@ class SetRegion(command.Command):
|
|
|
163
177
|
)
|
|
164
178
|
return parser
|
|
165
179
|
|
|
166
|
-
def take_action(self, parsed_args):
|
|
167
|
-
identity_client =
|
|
180
|
+
def take_action(self, parsed_args: argparse.Namespace) -> None:
|
|
181
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
182
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
183
|
+
)
|
|
168
184
|
|
|
169
185
|
kwargs = {}
|
|
170
186
|
if parsed_args.description:
|
|
@@ -178,7 +194,7 @@ class SetRegion(command.Command):
|
|
|
178
194
|
class ShowRegion(command.ShowOne):
|
|
179
195
|
_description = _("Display region details")
|
|
180
196
|
|
|
181
|
-
def get_parser(self, prog_name):
|
|
197
|
+
def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
|
|
182
198
|
parser = super().get_parser(prog_name)
|
|
183
199
|
parser.add_argument(
|
|
184
200
|
'region',
|
|
@@ -187,8 +203,12 @@ class ShowRegion(command.ShowOne):
|
|
|
187
203
|
)
|
|
188
204
|
return parser
|
|
189
205
|
|
|
190
|
-
def take_action(
|
|
191
|
-
|
|
206
|
+
def take_action(
|
|
207
|
+
self, parsed_args: argparse.Namespace
|
|
208
|
+
) -> tuple[Sequence[str], Iterable[Any]]:
|
|
209
|
+
identity_client = sdk_utils.ensure_service_version(
|
|
210
|
+
self.app.client_manager.sdk_connection.identity, '3'
|
|
211
|
+
)
|
|
192
212
|
|
|
193
213
|
region = identity_client.get_region(parsed_args.region)
|
|
194
214
|
|