python-openstackclient 8.2.0__py3-none-any.whl → 9.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.
Files changed (233) hide show
  1. openstackclient/api/object_store_v1.py +4 -1
  2. openstackclient/command.py +27 -0
  3. openstackclient/common/availability_zone.py +1 -1
  4. openstackclient/common/clientmanager.py +59 -21
  5. openstackclient/common/configuration.py +1 -1
  6. openstackclient/common/extension.py +1 -1
  7. openstackclient/common/limits.py +1 -1
  8. openstackclient/common/module.py +5 -3
  9. openstackclient/common/project_cleanup.py +10 -8
  10. openstackclient/common/quota.py +54 -23
  11. openstackclient/common/versions.py +1 -2
  12. openstackclient/compute/v2/agent.py +1 -1
  13. openstackclient/compute/v2/aggregate.py +6 -5
  14. openstackclient/compute/v2/console.py +5 -3
  15. openstackclient/compute/v2/console_connection.py +1 -1
  16. openstackclient/compute/v2/flavor.py +1 -1
  17. openstackclient/compute/v2/host.py +1 -1
  18. openstackclient/compute/v2/hypervisor.py +1 -1
  19. openstackclient/compute/v2/hypervisor_stats.py +1 -1
  20. openstackclient/compute/v2/keypair.py +1 -1
  21. openstackclient/compute/v2/server.py +78 -29
  22. openstackclient/compute/v2/server_backup.py +1 -1
  23. openstackclient/compute/v2/server_event.py +1 -1
  24. openstackclient/compute/v2/server_group.py +4 -2
  25. openstackclient/compute/v2/server_image.py +1 -1
  26. openstackclient/compute/v2/server_migration.py +1 -1
  27. openstackclient/compute/v2/server_volume.py +1 -1
  28. openstackclient/compute/v2/service.py +1 -1
  29. openstackclient/compute/v2/usage.py +6 -4
  30. openstackclient/identity/common.py +33 -1
  31. openstackclient/identity/v2_0/catalog.py +3 -2
  32. openstackclient/identity/v2_0/ec2creds.py +1 -1
  33. openstackclient/identity/v2_0/endpoint.py +1 -1
  34. openstackclient/identity/v2_0/project.py +17 -7
  35. openstackclient/identity/v2_0/role.py +1 -1
  36. openstackclient/identity/v2_0/role_assignment.py +3 -3
  37. openstackclient/identity/v2_0/service.py +4 -2
  38. openstackclient/identity/v2_0/token.py +1 -1
  39. openstackclient/identity/v2_0/user.py +2 -2
  40. openstackclient/identity/v3/access_rule.py +16 -4
  41. openstackclient/identity/v3/application_credential.py +30 -10
  42. openstackclient/identity/v3/catalog.py +3 -3
  43. openstackclient/identity/v3/consumer.py +1 -1
  44. openstackclient/identity/v3/credential.py +1 -1
  45. openstackclient/identity/v3/domain.py +10 -4
  46. openstackclient/identity/v3/ec2creds.py +1 -1
  47. openstackclient/identity/v3/endpoint.py +33 -12
  48. openstackclient/identity/v3/endpoint_group.py +1 -1
  49. openstackclient/identity/v3/federation_protocol.py +40 -41
  50. openstackclient/identity/v3/group.py +11 -5
  51. openstackclient/identity/v3/identity_provider.py +12 -10
  52. openstackclient/identity/v3/implied_role.py +1 -1
  53. openstackclient/identity/v3/limit.py +86 -85
  54. openstackclient/identity/v3/mapping.py +1 -1
  55. openstackclient/identity/v3/policy.py +1 -1
  56. openstackclient/identity/v3/project.py +191 -115
  57. openstackclient/identity/v3/region.py +1 -1
  58. openstackclient/identity/v3/registered_limit.py +97 -109
  59. openstackclient/identity/v3/role.py +20 -39
  60. openstackclient/identity/v3/role_assignment.py +12 -23
  61. openstackclient/identity/v3/service.py +1 -1
  62. openstackclient/identity/v3/service_provider.py +1 -1
  63. openstackclient/identity/v3/tag.py +1 -11
  64. openstackclient/identity/v3/token.py +3 -2
  65. openstackclient/identity/v3/trust.py +4 -2
  66. openstackclient/identity/v3/unscoped_saml.py +1 -1
  67. openstackclient/identity/v3/user.py +22 -13
  68. openstackclient/image/v1/image.py +19 -16
  69. openstackclient/image/v2/cache.py +1 -1
  70. openstackclient/image/v2/image.py +16 -12
  71. openstackclient/image/v2/info.py +1 -1
  72. openstackclient/image/v2/metadef_namespaces.py +1 -1
  73. openstackclient/image/v2/metadef_objects.py +1 -1
  74. openstackclient/image/v2/metadef_properties.py +3 -2
  75. openstackclient/image/v2/metadef_resource_type_association.py +1 -1
  76. openstackclient/image/v2/metadef_resource_types.py +1 -1
  77. openstackclient/image/v2/task.py +1 -1
  78. openstackclient/network/common.py +10 -9
  79. openstackclient/network/v2/address_group.py +4 -3
  80. openstackclient/network/v2/address_scope.py +8 -6
  81. openstackclient/network/v2/default_security_group_rule.py +9 -8
  82. openstackclient/network/v2/floating_ip.py +16 -9
  83. openstackclient/network/v2/floating_ip_port_forwarding.py +9 -6
  84. openstackclient/network/v2/ip_availability.py +7 -4
  85. openstackclient/network/v2/l3_conntrack_helper.py +11 -4
  86. openstackclient/network/v2/local_ip.py +13 -7
  87. openstackclient/network/v2/local_ip_association.py +7 -4
  88. openstackclient/network/v2/ndp_proxy.py +13 -6
  89. openstackclient/network/v2/network.py +33 -16
  90. openstackclient/network/v2/network_agent.py +5 -5
  91. openstackclient/network/v2/network_auto_allocated_topology.py +1 -1
  92. openstackclient/network/v2/network_flavor.py +1 -1
  93. openstackclient/network/v2/network_flavor_profile.py +1 -1
  94. openstackclient/network/v2/network_meter.py +1 -1
  95. openstackclient/network/v2/network_meter_rule.py +1 -1
  96. openstackclient/network/v2/network_qos_policy.py +7 -5
  97. openstackclient/network/v2/network_qos_rule.py +1 -1
  98. openstackclient/network/v2/network_qos_rule_type.py +1 -1
  99. openstackclient/network/v2/network_rbac.py +8 -5
  100. openstackclient/network/v2/network_segment.py +2 -2
  101. openstackclient/network/v2/network_segment_range.py +13 -6
  102. openstackclient/network/v2/network_service_provider.py +1 -1
  103. openstackclient/network/v2/network_trunk.py +65 -42
  104. openstackclient/network/v2/port.py +22 -20
  105. openstackclient/network/v2/router.py +19 -8
  106. openstackclient/network/v2/security_group.py +10 -6
  107. openstackclient/network/v2/security_group_rule.py +11 -5
  108. openstackclient/network/v2/subnet.py +17 -18
  109. openstackclient/network/v2/subnet_pool.py +11 -9
  110. openstackclient/network/v2/taas/__init__.py +0 -0
  111. openstackclient/network/v2/taas/tap_flow.py +245 -0
  112. openstackclient/network/v2/taas/tap_mirror.py +237 -0
  113. openstackclient/network/v2/taas/tap_service.py +211 -0
  114. openstackclient/object/v1/account.py +1 -1
  115. openstackclient/object/v1/container.py +1 -1
  116. openstackclient/object/v1/object.py +1 -1
  117. openstackclient/shell.py +18 -8
  118. openstackclient/tests/functional/identity/v3/test_catalog.py +42 -23
  119. openstackclient/tests/functional/identity/v3/test_limit.py +47 -0
  120. openstackclient/tests/functional/identity/v3/test_role_assignment.py +174 -0
  121. openstackclient/tests/functional/image/v2/test_cache.py +54 -0
  122. openstackclient/tests/functional/image/v2/test_metadef_objects.py +69 -0
  123. openstackclient/tests/functional/image/v2/test_metadef_resource_type.py +55 -0
  124. openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +46 -132
  125. openstackclient/tests/unit/common/test_command.py +1 -1
  126. openstackclient/tests/unit/common/test_extension.py +2 -3
  127. openstackclient/tests/unit/common/test_module.py +14 -7
  128. openstackclient/tests/unit/common/test_quota.py +79 -0
  129. openstackclient/tests/unit/compute/v2/test_aggregate.py +5 -3
  130. openstackclient/tests/unit/compute/v2/test_console.py +1 -4
  131. openstackclient/tests/unit/compute/v2/test_flavor.py +1 -3
  132. openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +1 -9
  133. openstackclient/tests/unit/compute/v2/test_server.py +370 -38
  134. openstackclient/tests/unit/compute/v2/test_server_backup.py +1 -3
  135. openstackclient/tests/unit/compute/v2/test_service.py +1 -3
  136. openstackclient/tests/unit/fakes.py +35 -134
  137. openstackclient/tests/unit/identity/test_common.py +100 -0
  138. openstackclient/tests/unit/identity/v2_0/test_project.py +4 -4
  139. openstackclient/tests/unit/identity/v3/fakes.py +10 -2
  140. openstackclient/tests/unit/identity/v3/test_application_credential.py +3 -3
  141. openstackclient/tests/unit/identity/v3/test_domain.py +1 -1
  142. openstackclient/tests/unit/identity/v3/test_endpoint.py +1 -1
  143. openstackclient/tests/unit/identity/v3/test_group.py +4 -2
  144. openstackclient/tests/unit/identity/v3/test_identity_provider.py +10 -10
  145. openstackclient/tests/unit/identity/v3/test_limit.py +197 -145
  146. openstackclient/tests/unit/identity/v3/test_oauth.py +1 -1
  147. openstackclient/tests/unit/identity/v3/test_project.py +832 -513
  148. openstackclient/tests/unit/identity/v3/test_protocol.py +97 -88
  149. openstackclient/tests/unit/identity/v3/test_registered_limit.py +356 -221
  150. openstackclient/tests/unit/identity/v3/test_role.py +1 -82
  151. openstackclient/tests/unit/identity/v3/test_user.py +7 -51
  152. openstackclient/tests/unit/image/v2/test_image.py +116 -5
  153. openstackclient/tests/unit/network/test_common.py +9 -13
  154. openstackclient/tests/unit/network/v2/taas/__init__.py +0 -0
  155. openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py +276 -0
  156. openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py +288 -0
  157. openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py +271 -0
  158. openstackclient/tests/unit/network/v2/test_address_group.py +19 -22
  159. openstackclient/tests/unit/network/v2/test_address_scope.py +10 -15
  160. openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +38 -49
  161. openstackclient/tests/unit/network/v2/test_floating_ip_network.py +21 -27
  162. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +21 -18
  163. openstackclient/tests/unit/network/v2/test_ip_availability.py +6 -8
  164. openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +6 -15
  165. openstackclient/tests/unit/network/v2/test_local_ip.py +12 -23
  166. openstackclient/tests/unit/network/v2/test_local_ip_association.py +13 -18
  167. openstackclient/tests/unit/network/v2/test_ndp_proxy.py +11 -21
  168. openstackclient/tests/unit/network/v2/test_network.py +41 -37
  169. openstackclient/tests/unit/network/v2/test_network_agent.py +13 -20
  170. openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +5 -8
  171. openstackclient/tests/unit/network/v2/test_network_flavor.py +14 -26
  172. openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +14 -17
  173. openstackclient/tests/unit/network/v2/test_network_meter.py +7 -17
  174. openstackclient/tests/unit/network/v2/test_network_meter_rule.py +10 -20
  175. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +7 -13
  176. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +44 -54
  177. openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +2 -7
  178. openstackclient/tests/unit/network/v2/test_network_rbac.py +21 -36
  179. openstackclient/tests/unit/network/v2/test_network_segment.py +13 -29
  180. openstackclient/tests/unit/network/v2/test_network_segment_range.py +20 -19
  181. openstackclient/tests/unit/network/v2/test_network_service_provider.py +1 -4
  182. openstackclient/tests/unit/network/v2/test_network_trunk.py +52 -47
  183. openstackclient/tests/unit/network/v2/test_port.py +75 -86
  184. openstackclient/tests/unit/network/v2/test_router.py +104 -126
  185. openstackclient/tests/unit/network/v2/test_security_group_network.py +19 -26
  186. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +17 -18
  187. openstackclient/tests/unit/network/v2/test_subnet.py +35 -46
  188. openstackclient/tests/unit/network/v2/test_subnet_pool.py +21 -33
  189. openstackclient/tests/unit/volume/test_find_resource.py +4 -13
  190. openstackclient/tests/unit/volume/v2/test_consistency_group.py +8 -2
  191. openstackclient/tests/unit/volume/v2/test_volume.py +7 -6
  192. openstackclient/tests/unit/volume/v2/test_volume_backup.py +3 -1
  193. openstackclient/tests/unit/volume/v3/test_volume.py +38 -12
  194. openstackclient/tests/unit/volume/v3/test_volume_backup.py +9 -0
  195. openstackclient/volume/client.py +7 -17
  196. openstackclient/volume/v2/backup_record.py +1 -1
  197. openstackclient/volume/v2/consistency_group.py +9 -9
  198. openstackclient/volume/v2/consistency_group_snapshot.py +3 -3
  199. openstackclient/volume/v2/qos_specs.py +3 -3
  200. openstackclient/volume/v2/service.py +1 -1
  201. openstackclient/volume/v2/volume.py +14 -7
  202. openstackclient/volume/v2/volume_backend.py +1 -1
  203. openstackclient/volume/v2/volume_backup.py +7 -5
  204. openstackclient/volume/v2/volume_host.py +1 -2
  205. openstackclient/volume/v2/volume_snapshot.py +4 -4
  206. openstackclient/volume/v2/volume_transfer_request.py +3 -3
  207. openstackclient/volume/v2/volume_type.py +16 -11
  208. openstackclient/volume/v3/block_storage_cleanup.py +1 -1
  209. openstackclient/volume/v3/block_storage_cluster.py +1 -1
  210. openstackclient/volume/v3/block_storage_log_level.py +1 -1
  211. openstackclient/volume/v3/block_storage_manage.py +1 -1
  212. openstackclient/volume/v3/block_storage_resource_filter.py +1 -1
  213. openstackclient/volume/v3/service.py +1 -1
  214. openstackclient/volume/v3/volume.py +16 -9
  215. openstackclient/volume/v3/volume_attachment.py +6 -5
  216. openstackclient/volume/v3/volume_backup.py +20 -5
  217. openstackclient/volume/v3/volume_group.py +1 -1
  218. openstackclient/volume/v3/volume_group_snapshot.py +1 -1
  219. openstackclient/volume/v3/volume_group_type.py +1 -1
  220. openstackclient/volume/v3/volume_message.py +1 -1
  221. openstackclient/volume/v3/volume_snapshot.py +4 -4
  222. openstackclient/volume/v3/volume_transfer_request.py +3 -3
  223. openstackclient/volume/v3/volume_type.py +20 -14
  224. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info}/METADATA +15 -13
  225. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info}/RECORD +231 -219
  226. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info}/WHEEL +1 -1
  227. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info}/entry_points.txt +15 -0
  228. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info/licenses}/AUTHORS +15 -0
  229. python_openstackclient-9.0.0.dist-info/pbr.json +1 -0
  230. openstackclient/tests/unit/common/test_logs.py +0 -221
  231. python_openstackclient-8.2.0.dist-info/pbr.json +0 -1
  232. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info/licenses}/LICENSE +0 -0
  233. {python_openstackclient-8.2.0.dist-info → python_openstackclient-9.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,237 @@
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
+ import logging
14
+
15
+ from osc_lib.cli import identity as identity_utils
16
+ from osc_lib import exceptions
17
+ from osc_lib import utils as osc_utils
18
+ from osc_lib.utils import columns as column_util
19
+
20
+ from openstackclient import command
21
+ from openstackclient.i18n import _
22
+ from openstackclient.identity import common
23
+ from openstackclient.network.v2 import port as osc_port
24
+ from openstackclient.network.v2.taas import tap_service
25
+
26
+ LOG = logging.getLogger(__name__)
27
+
28
+ TAP_MIRROR = 'tap_mirror'
29
+ TAP_MIRRORS = f'{TAP_MIRROR}s'
30
+
31
+ _attr_map = [
32
+ ('id', 'ID', column_util.LIST_BOTH),
33
+ ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY),
34
+ ('name', 'Name', column_util.LIST_BOTH),
35
+ ('port_id', 'Port', column_util.LIST_BOTH),
36
+ ('directions', 'Directions', column_util.LIST_LONG_ONLY),
37
+ ('remote_ip', 'Remote IP', column_util.LIST_BOTH),
38
+ ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY),
39
+ ]
40
+
41
+
42
+ def _get_columns(item):
43
+ column_map: dict[str, str] = {}
44
+ hidden_columns = ['location', 'tenant_id']
45
+ return osc_utils.get_osc_show_columns_for_sdk_resource(
46
+ item, column_map, hidden_columns
47
+ )
48
+
49
+
50
+ class CreateTapMirror(command.ShowOne):
51
+ _description = _("Create a new tap mirror.")
52
+
53
+ def get_parser(self, prog_name):
54
+ parser = super().get_parser(prog_name)
55
+ identity_utils.add_project_owner_option_to_parser(parser)
56
+ tap_service._add_updatable_args(parser)
57
+ parser.add_argument(
58
+ '--port',
59
+ dest='port_id',
60
+ required=True,
61
+ metavar="PORT",
62
+ help=_('Port (name or ID) to which the Tap Mirror is connected.'),
63
+ )
64
+ parser.add_argument(
65
+ '--directions',
66
+ dest='directions',
67
+ action=osc_port.JSONKeyValueAction,
68
+ required=True,
69
+ help=_(
70
+ 'Dictionary of direction and tunnel_id. Valid directions are: '
71
+ 'IN and OUT.'
72
+ ),
73
+ )
74
+ parser.add_argument(
75
+ '--remote-ip',
76
+ dest='remote_ip',
77
+ required=True,
78
+ help=_(
79
+ 'Remote IP address for the tap mirror (remote end of the '
80
+ 'GRE or ERSPAN v1 tunnel).'
81
+ ),
82
+ )
83
+ parser.add_argument(
84
+ '--mirror-type',
85
+ dest='mirror_type',
86
+ required=True,
87
+ help=_('Mirror type. Valid values are: gre and erspanv1.'),
88
+ )
89
+ return parser
90
+
91
+ def take_action(self, parsed_args):
92
+ client = self.app.client_manager.network
93
+ attrs = {}
94
+ if parsed_args.name is not None:
95
+ attrs['name'] = parsed_args.name
96
+ if parsed_args.description is not None:
97
+ attrs['description'] = parsed_args.description
98
+ if parsed_args.port_id is not None:
99
+ port_id = client.find_port(
100
+ parsed_args.port_id, ignore_missing=False
101
+ ).id
102
+ attrs['port_id'] = port_id
103
+ if parsed_args.directions is not None:
104
+ attrs['directions'] = parsed_args.directions
105
+ if parsed_args.remote_ip is not None:
106
+ attrs['remote_ip'] = parsed_args.remote_ip
107
+ if parsed_args.mirror_type is not None:
108
+ attrs['mirror_type'] = parsed_args.mirror_type
109
+ if 'project' in parsed_args and parsed_args.project is not None:
110
+ attrs['project_id'] = common.find_project(
111
+ self.app.client_manager.identity,
112
+ parsed_args.project,
113
+ parsed_args.project_domain,
114
+ ).id
115
+ obj = client.create_tap_mirror(**attrs)
116
+ display_columns, columns = tap_service._get_columns(obj)
117
+ data = osc_utils.get_dict_properties(obj, columns)
118
+ return display_columns, data
119
+
120
+
121
+ class ListTapMirror(command.Lister):
122
+ _description = _("List tap mirrors.")
123
+
124
+ def get_parser(self, prog_name):
125
+ parser = super().get_parser(prog_name)
126
+ identity_utils.add_project_owner_option_to_parser(parser)
127
+
128
+ return parser
129
+
130
+ def take_action(self, parsed_args):
131
+ client = self.app.client_manager.network
132
+ params = {}
133
+ if parsed_args.project is not None:
134
+ params['project_id'] = common.find_project(
135
+ self.app.client_manager.identity,
136
+ parsed_args.project,
137
+ parsed_args.project_domain,
138
+ ).id
139
+ objs = client.tap_mirrors(retrieve_all=True, params=params)
140
+ headers, columns = column_util.get_column_definitions(
141
+ _attr_map, long_listing=True
142
+ )
143
+ return (
144
+ headers,
145
+ (osc_utils.get_dict_properties(s, columns) for s in objs),
146
+ )
147
+
148
+
149
+ class ShowTapMirror(command.ShowOne):
150
+ _description = _("Show tap mirror details.")
151
+
152
+ def get_parser(self, prog_name):
153
+ parser = super().get_parser(prog_name)
154
+ parser.add_argument(
155
+ TAP_MIRROR,
156
+ metavar=f"<{TAP_MIRROR}>",
157
+ help=_("Tap mirror to display (name or ID)."),
158
+ )
159
+ return parser
160
+
161
+ def take_action(self, parsed_args):
162
+ client = self.app.client_manager.network
163
+ id = client.find_tap_mirror(
164
+ parsed_args.tap_mirror, ignore_missing=False
165
+ ).id
166
+ obj = client.get_tap_mirror(id)
167
+ display_columns, columns = tap_service._get_columns(obj)
168
+ data = osc_utils.get_dict_properties(obj, columns)
169
+ return display_columns, data
170
+
171
+
172
+ class DeleteTapMirror(command.Command):
173
+ _description = _("Delete a tap mirror.")
174
+
175
+ def get_parser(self, prog_name):
176
+ parser = super().get_parser(prog_name)
177
+ parser.add_argument(
178
+ TAP_MIRROR,
179
+ metavar=f"<{TAP_MIRROR}>",
180
+ nargs="+",
181
+ help=_("Tap mirror to delete (name or ID)."),
182
+ )
183
+ return parser
184
+
185
+ def take_action(self, parsed_args):
186
+ client = self.app.client_manager.network
187
+ fails = 0
188
+ for id_or_name in parsed_args.tap_mirror:
189
+ try:
190
+ id = client.find_tap_mirror(
191
+ id_or_name, ignore_missing=False
192
+ ).id
193
+
194
+ client.delete_tap_mirror(id)
195
+ LOG.warning("Tap Mirror %(id)s deleted", {'id': id})
196
+ except Exception as e:
197
+ fails += 1
198
+ LOG.error(
199
+ "Failed to delete Tap Mirror with name or ID "
200
+ "'%(id_or_name)s': %(e)s",
201
+ {'id_or_name': id_or_name, 'e': e},
202
+ )
203
+ if fails > 0:
204
+ msg = _("Failed to delete %(fails)s of %(total)s Tap Mirror.") % {
205
+ 'fails': fails,
206
+ 'total': len(parsed_args.tap_mirror),
207
+ }
208
+ raise exceptions.CommandError(msg)
209
+
210
+
211
+ class UpdateTapMirror(command.ShowOne):
212
+ _description = _("Update a tap mirror.")
213
+
214
+ def get_parser(self, prog_name):
215
+ parser = super().get_parser(prog_name)
216
+ parser.add_argument(
217
+ TAP_MIRROR,
218
+ metavar=f"<{TAP_MIRROR}>",
219
+ help=_("Tap mirror to modify (name or ID)."),
220
+ )
221
+ tap_service._add_updatable_args(parser)
222
+ return parser
223
+
224
+ def take_action(self, parsed_args):
225
+ client = self.app.client_manager.network
226
+ original_t_s = client.find_tap_mirror(
227
+ parsed_args.tap_mirror, ignore_missing=False
228
+ ).id
229
+ attrs = {}
230
+ if parsed_args.name is not None:
231
+ attrs['name'] = parsed_args.name
232
+ if parsed_args.description is not None:
233
+ attrs['description'] = parsed_args.description
234
+ obj = client.update_tap_mirror(original_t_s, **attrs)
235
+ display_columns, columns = tap_service._get_columns(obj)
236
+ data = osc_utils.get_dict_properties(obj, columns)
237
+ return display_columns, data
@@ -0,0 +1,211 @@
1
+ # All Rights Reserved 2020
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ # not use this file except in compliance with the License. You may obtain
5
+ # a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+ # License for the specific language governing permissions and limitations
13
+ # under the License.
14
+
15
+ import logging
16
+
17
+ from osc_lib.cli import identity as identity_utils
18
+ from osc_lib import exceptions
19
+ from osc_lib import utils as osc_utils
20
+ from osc_lib.utils import columns as column_util
21
+
22
+ from openstackclient import command
23
+ from openstackclient.i18n import _
24
+ from openstackclient.identity import common
25
+
26
+ LOG = logging.getLogger(__name__)
27
+
28
+ TAP_SERVICE = 'tap_service'
29
+ TAP_SERVICES = f'{TAP_SERVICE}s'
30
+
31
+ _attr_map = [
32
+ ('id', 'ID', column_util.LIST_BOTH),
33
+ ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY),
34
+ ('name', 'Name', column_util.LIST_BOTH),
35
+ ('port_id', 'Port', column_util.LIST_BOTH),
36
+ ('status', 'Status', column_util.LIST_BOTH),
37
+ ]
38
+
39
+
40
+ def _add_updatable_args(parser):
41
+ parser.add_argument('--name', help=_('Name of the tap service.'))
42
+ parser.add_argument(
43
+ '--description', help=_('Description of the tap service.')
44
+ )
45
+
46
+
47
+ def _get_columns(item):
48
+ column_map: dict[str, str] = {}
49
+ hidden_columns = ['location', 'tenant_id']
50
+ return osc_utils.get_osc_show_columns_for_sdk_resource(
51
+ item, column_map, hidden_columns
52
+ )
53
+
54
+
55
+ class CreateTapService(command.ShowOne):
56
+ _description = _("Create a new tap service.")
57
+
58
+ def get_parser(self, prog_name):
59
+ parser = super().get_parser(prog_name)
60
+ identity_utils.add_project_owner_option_to_parser(parser)
61
+ _add_updatable_args(parser)
62
+ parser.add_argument(
63
+ '--port',
64
+ dest='port_id',
65
+ required=True,
66
+ metavar="PORT",
67
+ help=_('Port (name or ID) to connect to the tap service.'),
68
+ )
69
+ return parser
70
+
71
+ def take_action(self, parsed_args):
72
+ client = self.app.client_manager.network
73
+ attrs = {}
74
+ if parsed_args.name is not None:
75
+ attrs['name'] = parsed_args.name
76
+ if parsed_args.description is not None:
77
+ attrs['description'] = parsed_args.description
78
+ if parsed_args.port_id is not None:
79
+ port_id = client.find_port(
80
+ parsed_args.port_id, ignore_missing=False
81
+ ).id
82
+ attrs['port_id'] = port_id
83
+ if 'project' in parsed_args and parsed_args.project is not None:
84
+ attrs['project_id'] = common.find_project(
85
+ self.app.client_manager.identity,
86
+ parsed_args.project,
87
+ parsed_args.project_domain,
88
+ ).id
89
+ obj = client.create_tap_service(**attrs)
90
+ display_columns, columns = _get_columns(obj)
91
+ data = osc_utils.get_dict_properties(obj, columns)
92
+ return display_columns, data
93
+
94
+
95
+ class ListTapService(command.Lister):
96
+ _description = _("List tap services.")
97
+
98
+ def get_parser(self, prog_name):
99
+ parser = super().get_parser(prog_name)
100
+ identity_utils.add_project_owner_option_to_parser(parser)
101
+
102
+ return parser
103
+
104
+ def take_action(self, parsed_args):
105
+ client = self.app.client_manager.network
106
+ params = {}
107
+ if parsed_args.project is not None:
108
+ params['project_id'] = common.find_project(
109
+ self.app.client_manager.identity,
110
+ parsed_args.project,
111
+ parsed_args.project_domain,
112
+ ).id
113
+ objs = client.tap_services(retrieve_all=True, params=params)
114
+ headers, columns = column_util.get_column_definitions(
115
+ _attr_map, long_listing=True
116
+ )
117
+ return (
118
+ headers,
119
+ (osc_utils.get_dict_properties(s, columns) for s in objs),
120
+ )
121
+
122
+
123
+ class ShowTapService(command.ShowOne):
124
+ _description = _("Show tap service details.")
125
+
126
+ def get_parser(self, prog_name):
127
+ parser = super().get_parser(prog_name)
128
+ parser.add_argument(
129
+ TAP_SERVICE,
130
+ metavar=f"<{TAP_SERVICE}>",
131
+ help=_("Tap service to display (name or ID)."),
132
+ )
133
+ return parser
134
+
135
+ def take_action(self, parsed_args):
136
+ client = self.app.client_manager.network
137
+ id = client.find_tap_service(
138
+ parsed_args.tap_service, ignore_missing=False
139
+ ).id
140
+ obj = client.get_tap_service(id)
141
+ display_columns, columns = _get_columns(obj)
142
+ data = osc_utils.get_dict_properties(obj, columns)
143
+ return display_columns, data
144
+
145
+
146
+ class DeleteTapService(command.Command):
147
+ _description = _("Delete a tap service.")
148
+
149
+ def get_parser(self, prog_name):
150
+ parser = super().get_parser(prog_name)
151
+ parser.add_argument(
152
+ TAP_SERVICE,
153
+ metavar=f"<{TAP_SERVICE}>",
154
+ nargs="+",
155
+ help=_("Tap service to delete (name or ID)."),
156
+ )
157
+ return parser
158
+
159
+ def take_action(self, parsed_args):
160
+ client = self.app.client_manager.network
161
+ fails = 0
162
+ for id_or_name in parsed_args.tap_service:
163
+ try:
164
+ id = client.find_tap_service(
165
+ id_or_name, ignore_missing=False
166
+ ).id
167
+
168
+ client.delete_tap_service(id)
169
+ LOG.warning("Tap service %(id)s deleted", {'id': id})
170
+ except Exception as e:
171
+ fails += 1
172
+ LOG.error(
173
+ "Failed to delete tap service with name or ID "
174
+ "'%(id_or_name)s': %(e)s",
175
+ {'id_or_name': id_or_name, 'e': e},
176
+ )
177
+ if fails > 0:
178
+ msg = _("Failed to delete %(fails)s of %(total)s tap service.") % {
179
+ 'fails': fails,
180
+ 'total': len(parsed_args.tap_service),
181
+ }
182
+ raise exceptions.CommandError(msg)
183
+
184
+
185
+ class UpdateTapService(command.ShowOne):
186
+ _description = _("Update a tap service.")
187
+
188
+ def get_parser(self, prog_name):
189
+ parser = super().get_parser(prog_name)
190
+ parser.add_argument(
191
+ TAP_SERVICE,
192
+ metavar=f"<{TAP_SERVICE}>",
193
+ help=_("Tap service to modify (name or ID)."),
194
+ )
195
+ _add_updatable_args(parser)
196
+ return parser
197
+
198
+ def take_action(self, parsed_args):
199
+ client = self.app.client_manager.network
200
+ original_t_s = client.find_tap_service(
201
+ parsed_args.tap_service, ignore_missing=False
202
+ ).id
203
+ attrs = {}
204
+ if parsed_args.name is not None:
205
+ attrs['name'] = parsed_args.name
206
+ if parsed_args.description is not None:
207
+ attrs['description'] = parsed_args.description
208
+ obj = client.update_tap_service(original_t_s, **attrs)
209
+ display_columns, columns = _get_columns(obj)
210
+ data = osc_utils.get_dict_properties(obj, columns)
211
+ return display_columns, data
@@ -15,8 +15,8 @@
15
15
 
16
16
  from osc_lib.cli import format_columns
17
17
  from osc_lib.cli import parseractions
18
- from osc_lib.command import command
19
18
 
19
+ from openstackclient import command
20
20
  from openstackclient.i18n import _
21
21
 
22
22
 
@@ -19,9 +19,9 @@ import logging
19
19
 
20
20
  from osc_lib.cli import format_columns
21
21
  from osc_lib.cli import parseractions
22
- from osc_lib.command import command
23
22
  from osc_lib import utils
24
23
 
24
+ from openstackclient import command
25
25
  from openstackclient.common import pagination
26
26
  from openstackclient.i18n import _
27
27
 
@@ -19,10 +19,10 @@ import logging
19
19
 
20
20
  from osc_lib.cli import format_columns
21
21
  from osc_lib.cli import parseractions
22
- from osc_lib.command import command
23
22
  from osc_lib import exceptions
24
23
  from osc_lib import utils
25
24
 
25
+ from openstackclient import command
26
26
  from openstackclient.common import pagination
27
27
  from openstackclient.i18n import _
28
28
 
openstackclient/shell.py CHANGED
@@ -26,16 +26,27 @@ from osc_lib import shell
26
26
  import openstackclient
27
27
  from openstackclient.common import clientmanager
28
28
 
29
-
30
29
  DEFAULT_DOMAIN = 'default'
30
+ # list of modules that were originally out-of-tree and are now in
31
+ # core OSC
32
+ IGNORED_MODULES = (
33
+ 'neutron_taas.taas_client.osc',
34
+ 'neutronclient.osc.v2.taas',
35
+ )
31
36
 
32
37
 
33
38
  class OpenStackShell(shell.OpenStackShell):
39
+ client_manager: clientmanager.ClientManager
40
+
34
41
  def __init__(self):
42
+ command_manager = commandmanager.CommandManager(
43
+ 'openstack.cli', ignored_modules=IGNORED_MODULES
44
+ )
45
+
35
46
  super().__init__(
36
47
  description=__doc__.strip(),
37
48
  version=openstackclient.__version__,
38
- command_manager=commandmanager.CommandManager('openstack.cli'),
49
+ command_manager=command_manager,
39
50
  deferred_help=True,
40
51
  )
41
52
 
@@ -48,8 +59,10 @@ class OpenStackShell(shell.OpenStackShell):
48
59
  # about them
49
60
  warnings.filterwarnings('ignore', module='openstack')
50
61
 
51
- def build_option_parser(self, description, version):
52
- parser = super().build_option_parser(description, version)
62
+ def build_option_parser(self, description, version, argparse_kwargs=None):
63
+ parser = super().build_option_parser(
64
+ description, version, argparse_kwargs
65
+ )
53
66
  parser = clientmanager.build_plugin_option_parser(parser)
54
67
  parser = auth.build_auth_plugins_option_parser(parser)
55
68
  return parser
@@ -65,10 +78,7 @@ class OpenStackShell(shell.OpenStackShell):
65
78
  self._auth_type = 'password'
66
79
 
67
80
  def _load_plugins(self):
68
- """Load plugins via stevedore
69
-
70
- osc-lib has no opinion on what plugins should be loaded
71
- """
81
+ """Load plugins via stevedore."""
72
82
  # Loop through extensions to get API versions
73
83
  for mod in clientmanager.PLUGIN_MODULES:
74
84
  default_version = getattr(mod, 'DEFAULT_API_VERSION', None)
@@ -10,35 +10,54 @@
10
10
  # License for the specific language governing permissions and limitations
11
11
  # under the License.
12
12
 
13
+
13
14
  from openstackclient.tests.functional.identity.v3 import common
14
15
 
15
16
 
16
17
  class CatalogTests(common.IdentityTests):
17
- def test_catalog_list(self):
18
+ """Functional tests for catalog commands"""
19
+
20
+ def test_catalog(self):
21
+ """Test catalog list and show functionality"""
22
+ # Create a test service for isolated testing
23
+ _dummy_service_name = self._create_dummy_service(add_clean_up=True)
24
+
25
+ # list catalogs
18
26
  raw_output = self.openstack('catalog list')
19
27
  items = self.parse_listing(raw_output)
20
28
  self.assert_table_structure(items, ['Name', 'Type', 'Endpoints'])
21
29
 
22
- def test_catalog_show(self):
23
- """test catalog show command
24
-
25
- The output example:
26
- +-----------+----------------------------------------+
27
- | Field | Value |
28
- +-----------+----------------------------------------+
29
- | endpoints | test1 |
30
- | | public: http://localhost:5000/v2.0 |
31
- | | test1 |
32
- | | internal: http://localhost:5000/v2.0 |
33
- | | test1 |
34
- | | admin: http://localhost:35357/v2.0 |
35
- | | |
36
- | id | e1e68b5ba21a43a39ff1cf58e736c3aa |
37
- | name | keystone |
38
- | type | identity |
39
- +-----------+----------------------------------------+
40
- """
41
- raw_output = self.openstack('catalog show {}'.format('identity'))
30
+ # Verify created service appears in catalog
31
+ service_names = [
32
+ item.get('Name') for item in items if item.get('Name')
33
+ ]
34
+ self.assertIn(
35
+ _dummy_service_name,
36
+ service_names,
37
+ "Created dummy service should be present in catalog",
38
+ )
39
+
40
+ # show service (by name)
41
+ raw_output = self.openstack(f'catalog show {_dummy_service_name}')
42
42
  items = self.parse_show(raw_output)
43
- # items may have multiple endpoint urls with empty key
44
- self.assert_show_fields(items, ['endpoints', 'name', 'type', '', 'id'])
43
+ self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id'])
44
+
45
+ # Extract the type from the dummy service
46
+ _dummy_service_type = next(
47
+ (item['type'] for item in items if 'type' in item), None
48
+ )
49
+
50
+ # show service (by type)
51
+ raw_output = self.openstack(f'catalog show {_dummy_service_type}')
52
+ items = self.parse_show(raw_output)
53
+ self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id'])
54
+
55
+ # show service (non-existent)
56
+ result = self.openstack(
57
+ 'catalog show nonexistent-service-xyz', fail_ok=True
58
+ )
59
+ self.assertEqual(
60
+ '',
61
+ result.strip(),
62
+ "Non-existent service should return empty result",
63
+ )
@@ -100,6 +100,53 @@ class LimitTestCase(common.IdentityTests):
100
100
  self.assert_show_fields(items, self.LIMIT_FIELDS)
101
101
  registered_limit_id = self._create_dummy_registered_limit()
102
102
 
103
+ def test_limit_create_with_project_domain(self):
104
+ registered_limit_id = self._create_dummy_registered_limit()
105
+ raw_output = self.openstack(
106
+ f'registered limit show {registered_limit_id}',
107
+ cloud=SYSTEM_CLOUD,
108
+ )
109
+ items = self.parse_show(raw_output)
110
+ service_id = self._extract_value_from_items('service_id', items)
111
+ resource_name = self._extract_value_from_items('resource_name', items)
112
+
113
+ raw_output = self.openstack(f'service show {service_id}')
114
+ items = self.parse_show(raw_output)
115
+ service_name = self._extract_value_from_items('name', items)
116
+
117
+ project_name = self._create_dummy_project()
118
+ raw_output = self.openstack(
119
+ f'project show {project_name}',
120
+ cloud=SYSTEM_CLOUD,
121
+ )
122
+ items = self.parse_show(raw_output)
123
+ domain_id = self._extract_value_from_items('domain_id', items)
124
+
125
+ params = {
126
+ 'project_name': project_name,
127
+ 'project_domain': domain_id,
128
+ 'service_name': service_name,
129
+ 'resource_name': resource_name,
130
+ 'resource_limit': 15,
131
+ }
132
+ raw_output = self.openstack(
133
+ 'limit create'
134
+ ' --project {project_name}'
135
+ ' --project-domain {project_domain}'
136
+ ' --service {service_name}'
137
+ ' --resource-limit {resource_limit}'
138
+ ' {resource_name}'.format(**params),
139
+ cloud=SYSTEM_CLOUD,
140
+ )
141
+ items = self.parse_show(raw_output)
142
+ limit_id = self._extract_value_from_items('id', items)
143
+ self.addCleanup(
144
+ self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD
145
+ )
146
+
147
+ self.assert_show_fields(items, self.LIMIT_FIELDS)
148
+ registered_limit_id = self._create_dummy_registered_limit()
149
+
103
150
  def test_limit_create_with_service_id(self):
104
151
  self._create_dummy_limit()
105
152