python-openstackclient 9.0.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.
Files changed (284) hide show
  1. openstackclient/__init__.py +2 -6
  2. openstackclient/api/api.py +41 -23
  3. openstackclient/api/compute_v2.py +44 -25
  4. openstackclient/api/object_store_v1.py +75 -97
  5. openstackclient/api/volume_v2.py +2 -1
  6. openstackclient/api/volume_v3.py +2 -1
  7. openstackclient/common/availability_zone.py +58 -42
  8. openstackclient/common/clientmanager.py +56 -29
  9. openstackclient/common/configuration.py +10 -3
  10. openstackclient/common/envvars.py +2 -2
  11. openstackclient/common/extension.py +14 -5
  12. openstackclient/common/limits.py +10 -5
  13. openstackclient/common/module.py +14 -6
  14. openstackclient/common/pagination.py +8 -2
  15. openstackclient/common/progressbar.py +7 -6
  16. openstackclient/common/project_cleanup.py +13 -7
  17. openstackclient/common/quota.py +97 -99
  18. openstackclient/common/versions.py +8 -2
  19. openstackclient/compute/client.py +7 -3
  20. openstackclient/compute/v2/agent.py +17 -10
  21. openstackclient/compute/v2/aggregate.py +36 -22
  22. openstackclient/compute/v2/console.py +14 -8
  23. openstackclient/compute/v2/console_connection.py +11 -3
  24. openstackclient/compute/v2/flavor.py +39 -21
  25. openstackclient/compute/v2/host.py +14 -6
  26. openstackclient/compute/v2/hypervisor.py +14 -5
  27. openstackclient/compute/v2/hypervisor_stats.py +10 -2
  28. openstackclient/compute/v2/keypair.py +29 -14
  29. openstackclient/compute/v2/server.py +249 -169
  30. openstackclient/compute/v2/server_backup.py +10 -4
  31. openstackclient/compute/v2/server_event.py +21 -12
  32. openstackclient/compute/v2/server_group.py +21 -11
  33. openstackclient/compute/v2/server_image.py +19 -10
  34. openstackclient/compute/v2/server_migration.py +24 -10
  35. openstackclient/compute/v2/server_share.py +274 -0
  36. openstackclient/compute/v2/server_volume.py +10 -4
  37. openstackclient/compute/v2/service.py +14 -7
  38. openstackclient/compute/v2/usage.py +26 -21
  39. openstackclient/identity/client.py +8 -3
  40. openstackclient/identity/common.py +78 -47
  41. openstackclient/identity/v2_0/catalog.py +14 -7
  42. openstackclient/identity/v2_0/ec2creds.py +21 -10
  43. openstackclient/identity/v2_0/endpoint.py +23 -11
  44. openstackclient/identity/v2_0/project.py +25 -14
  45. openstackclient/identity/v2_0/role.py +28 -14
  46. openstackclient/identity/v2_0/role_assignment.py +9 -3
  47. openstackclient/identity/v2_0/service.py +23 -11
  48. openstackclient/identity/v2_0/token.py +12 -5
  49. openstackclient/identity/v2_0/user.py +26 -15
  50. openstackclient/identity/v3/access_rule.py +26 -12
  51. openstackclient/identity/v3/application_credential.py +59 -24
  52. openstackclient/identity/v3/catalog.py +14 -7
  53. openstackclient/identity/v3/consumer.py +22 -11
  54. openstackclient/identity/v3/credential.py +36 -16
  55. openstackclient/identity/v3/domain.py +37 -18
  56. openstackclient/identity/v3/ec2creds.py +25 -12
  57. openstackclient/identity/v3/endpoint.py +42 -20
  58. openstackclient/identity/v3/endpoint_group.py +28 -17
  59. openstackclient/identity/v3/federation_protocol.py +38 -16
  60. openstackclient/identity/v3/group.py +55 -32
  61. openstackclient/identity/v3/identity_provider.py +92 -57
  62. openstackclient/identity/v3/implied_role.py +21 -9
  63. openstackclient/identity/v3/limit.py +38 -16
  64. openstackclient/identity/v3/mapping.py +26 -13
  65. openstackclient/identity/v3/policy.py +23 -12
  66. openstackclient/identity/v3/project.py +43 -23
  67. openstackclient/identity/v3/region.py +36 -16
  68. openstackclient/identity/v3/registered_limit.py +40 -16
  69. openstackclient/identity/v3/role.py +61 -31
  70. openstackclient/identity/v3/role_assignment.py +23 -6
  71. openstackclient/identity/v3/service.py +36 -16
  72. openstackclient/identity/v3/service_provider.py +37 -15
  73. openstackclient/identity/v3/tag.py +23 -6
  74. openstackclient/identity/v3/token.py +30 -14
  75. openstackclient/identity/v3/trust.py +32 -14
  76. openstackclient/identity/v3/unscoped_saml.py +10 -2
  77. openstackclient/identity/v3/user.py +49 -26
  78. openstackclient/image/client.py +7 -3
  79. openstackclient/image/v1/image.py +33 -26
  80. openstackclient/image/v2/cache.py +14 -9
  81. openstackclient/image/v2/image.py +74 -48
  82. openstackclient/image/v2/info.py +7 -1
  83. openstackclient/image/v2/metadef_namespaces.py +109 -13
  84. openstackclient/image/v2/metadef_objects.py +28 -15
  85. openstackclient/image/v2/metadef_properties.py +24 -13
  86. openstackclient/image/v2/metadef_resource_type_association.py +14 -7
  87. openstackclient/image/v2/metadef_resource_types.py +7 -1
  88. openstackclient/image/v2/task.py +15 -6
  89. openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po +7 -192
  90. openstackclient/network/client.py +7 -2
  91. openstackclient/network/common.py +16 -241
  92. openstackclient/network/utils.py +36 -22
  93. openstackclient/network/v2/address_group.py +27 -16
  94. openstackclient/network/v2/address_scope.py +24 -13
  95. openstackclient/network/v2/bgpvpn/bgpvpn.py +463 -0
  96. openstackclient/network/v2/bgpvpn/constants.py +30 -0
  97. openstackclient/network/v2/bgpvpn/network_association.py +214 -0
  98. openstackclient/network/v2/bgpvpn/port_association.py +490 -0
  99. openstackclient/network/v2/bgpvpn/router_association.py +288 -0
  100. openstackclient/network/v2/default_security_group_rule.py +19 -10
  101. openstackclient/network/v2/floating_ip.py +110 -159
  102. openstackclient/network/v2/floating_ip_port_forwarding.py +30 -18
  103. openstackclient/network/v2/fwaas/__init__.py +0 -0
  104. openstackclient/network/v2/fwaas/group.py +466 -0
  105. openstackclient/network/v2/fwaas/policy.py +518 -0
  106. openstackclient/network/v2/fwaas/rule.py +574 -0
  107. openstackclient/network/v2/ip_availability.py +13 -5
  108. openstackclient/network/v2/l3_conntrack_helper.py +22 -13
  109. openstackclient/network/v2/local_ip.py +24 -13
  110. openstackclient/network/v2/local_ip_association.py +14 -7
  111. openstackclient/network/v2/ndp_proxy.py +20 -11
  112. openstackclient/network/v2/network.py +129 -196
  113. openstackclient/network/v2/network_agent.py +46 -25
  114. openstackclient/network/v2/network_auto_allocated_topology.py +22 -11
  115. openstackclient/network/v2/network_flavor.py +27 -16
  116. openstackclient/network/v2/network_flavor_profile.py +23 -12
  117. openstackclient/network/v2/network_meter.py +21 -10
  118. openstackclient/network/v2/network_meter_rule.py +21 -11
  119. openstackclient/network/v2/network_qos_policy.py +25 -15
  120. openstackclient/network/v2/network_qos_rule.py +32 -17
  121. openstackclient/network/v2/network_qos_rule_type.py +13 -5
  122. openstackclient/network/v2/network_rbac.py +23 -12
  123. openstackclient/network/v2/network_segment.py +20 -11
  124. openstackclient/network/v2/network_segment_range.py +56 -29
  125. openstackclient/network/v2/network_service_provider.py +7 -1
  126. openstackclient/network/v2/network_trunk.py +38 -22
  127. openstackclient/network/v2/port.py +54 -29
  128. openstackclient/network/v2/router.py +75 -52
  129. openstackclient/network/v2/security_group.py +87 -157
  130. openstackclient/network/v2/security_group_rule.py +100 -280
  131. openstackclient/network/v2/subnet.py +49 -28
  132. openstackclient/network/v2/subnet_pool.py +30 -17
  133. openstackclient/network/v2/taas/tap_flow.py +22 -11
  134. openstackclient/network/v2/taas/tap_mirror.py +22 -11
  135. openstackclient/network/v2/taas/tap_service.py +23 -12
  136. openstackclient/object/client.py +7 -2
  137. openstackclient/object/v1/account.py +13 -6
  138. openstackclient/object/v1/container.py +25 -15
  139. openstackclient/object/v1/object.py +25 -15
  140. openstackclient/py.typed +0 -0
  141. openstackclient/shell.py +46 -10
  142. openstackclient/tests/functional/base.py +55 -20
  143. openstackclient/tests/functional/common/test_extension.py +4 -0
  144. openstackclient/tests/functional/common/test_quota.py +3 -1
  145. openstackclient/tests/functional/compute/v2/common.py +14 -13
  146. openstackclient/tests/functional/compute/v2/test_flavor.py +3 -1
  147. openstackclient/tests/functional/compute/v2/test_server.py +3 -0
  148. openstackclient/tests/functional/identity/v2/common.py +10 -6
  149. openstackclient/tests/functional/identity/v2/test_role.py +4 -4
  150. openstackclient/tests/functional/identity/v3/common.py +25 -19
  151. openstackclient/tests/functional/identity/v3/test_group.py +20 -20
  152. openstackclient/tests/functional/identity/v3/test_idp.py +3 -1
  153. openstackclient/tests/functional/identity/v3/test_project.py +10 -10
  154. openstackclient/tests/functional/identity/v3/test_role.py +18 -18
  155. openstackclient/tests/functional/identity/v3/test_role_assignment.py +12 -12
  156. openstackclient/tests/functional/identity/v3/test_user.py +8 -8
  157. openstackclient/tests/functional/image/base.py +1 -6
  158. openstackclient/tests/functional/network/v2/common.py +5 -2
  159. openstackclient/tests/functional/network/v2/test_floating_ip.py +10 -4
  160. openstackclient/tests/functional/network/v2/test_ip_availability.py +4 -0
  161. openstackclient/tests/functional/network/v2/test_network_meter_rule.py +3 -2
  162. openstackclient/tests/functional/network/v2/test_network_segment.py +5 -0
  163. openstackclient/tests/functional/network/v2/test_subnet.py +13 -9
  164. openstackclient/tests/functional/object/v1/common.py +4 -0
  165. openstackclient/tests/functional/volume/v2/common.py +4 -0
  166. openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +27 -11
  167. openstackclient/tests/functional/volume/v2/test_volume_type.py +2 -2
  168. openstackclient/tests/functional/volume/v3/common.py +4 -0
  169. openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +11 -7
  170. openstackclient/tests/functional/volume/v3/test_volume_type.py +2 -2
  171. openstackclient/tests/unit/common/test_availability_zone.py +35 -49
  172. openstackclient/tests/unit/common/test_extension.py +2 -2
  173. openstackclient/tests/unit/common/test_module.py +12 -7
  174. openstackclient/tests/unit/common/test_project_cleanup.py +3 -1
  175. openstackclient/tests/unit/common/test_quota.py +6 -26
  176. openstackclient/tests/unit/compute/v2/fakes.py +25 -0
  177. openstackclient/tests/unit/compute/v2/test_flavor.py +28 -2
  178. openstackclient/tests/unit/compute/v2/test_keypair.py +6 -6
  179. openstackclient/tests/unit/compute/v2/test_server.py +11 -96
  180. openstackclient/tests/unit/compute/v2/test_server_share.py +287 -0
  181. openstackclient/tests/unit/identity/v3/fakes.py +3 -0
  182. openstackclient/tests/unit/identity/v3/test_group.py +4 -14
  183. openstackclient/tests/unit/identity/v3/test_identity_provider.py +303 -299
  184. openstackclient/tests/unit/identity/v3/test_user.py +4 -4
  185. openstackclient/tests/unit/image/v2/test_image.py +11 -11
  186. openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +105 -6
  187. openstackclient/tests/unit/network/test_common.py +0 -155
  188. openstackclient/tests/unit/network/v2/bgpvpn/__init__.py +0 -0
  189. openstackclient/tests/unit/network/v2/bgpvpn/fakes.py +179 -0
  190. openstackclient/tests/unit/network/v2/bgpvpn/test_bgpvpn.py +584 -0
  191. openstackclient/tests/unit/network/v2/bgpvpn/test_network_association.py +285 -0
  192. openstackclient/tests/unit/network/v2/bgpvpn/test_port_association.py +384 -0
  193. openstackclient/tests/unit/network/v2/bgpvpn/test_router_association.py +297 -0
  194. openstackclient/tests/unit/network/v2/fwaas/__init__.py +0 -0
  195. openstackclient/tests/unit/network/v2/fwaas/test_group.py +897 -0
  196. openstackclient/tests/unit/network/v2/fwaas/test_policy.py +869 -0
  197. openstackclient/tests/unit/network/v2/fwaas/test_rule.py +980 -0
  198. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_flow.py → test_tap_flow.py} +18 -25
  199. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_mirror.py → test_tap_mirror.py} +19 -29
  200. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_service.py → test_tap_service.py} +19 -29
  201. openstackclient/tests/unit/network/v2/test_address_group.py +2 -2
  202. openstackclient/tests/unit/network/v2/{test_floating_ip_network.py → test_floating_ip.py} +3 -2
  203. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +13 -13
  204. openstackclient/tests/unit/network/v2/test_network_agent.py +8 -4
  205. openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +3 -3
  206. openstackclient/tests/unit/network/v2/test_network_flavor.py +2 -2
  207. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +1 -1
  208. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +2 -2
  209. openstackclient/tests/unit/network/v2/test_network_rbac.py +1 -1
  210. openstackclient/tests/unit/network/v2/test_network_segment.py +1 -1
  211. openstackclient/tests/unit/network/v2/test_network_segment_range.py +7 -10
  212. openstackclient/tests/unit/network/v2/test_network_trunk.py +1 -1
  213. openstackclient/tests/unit/network/v2/test_router.py +8 -9
  214. openstackclient/tests/unit/network/v2/{test_security_group_network.py → test_security_group.py} +1 -20
  215. openstackclient/tests/unit/network/v2/{test_security_group_rule_network.py → test_security_group_rule.py} +7 -41
  216. openstackclient/tests/unit/network/v2/test_subnet.py +2 -1
  217. openstackclient/tests/unit/network/v2/test_subnet_pool.py +2 -1
  218. openstackclient/tests/unit/object/v1/fakes.py +8 -7
  219. openstackclient/tests/unit/object/v1/test_container.py +65 -101
  220. openstackclient/tests/unit/object/v1/test_container_all.py +8 -1
  221. openstackclient/tests/unit/object/v1/test_object.py +44 -84
  222. openstackclient/tests/unit/object/v1/test_object_all.py +8 -1
  223. openstackclient/tests/unit/test_hacking.py +108 -0
  224. openstackclient/tests/unit/volume/v2/fakes.py +1 -0
  225. openstackclient/tests/unit/volume/v2/test_volume_backup.py +1 -5
  226. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +2 -1
  227. openstackclient/tests/unit/volume/v2/test_volume_type.py +2 -4
  228. openstackclient/tests/unit/volume/v3/fakes.py +1 -0
  229. openstackclient/tests/unit/volume/v3/test_volume.py +60 -3
  230. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
  231. openstackclient/tests/unit/volume/v3/test_volume_backup.py +1 -5
  232. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +55 -1
  233. openstackclient/tests/unit/volume/v3/test_volume_type.py +2 -4
  234. openstackclient/volume/client.py +7 -3
  235. openstackclient/volume/v2/backup_record.py +15 -6
  236. openstackclient/volume/v2/consistency_group.py +29 -17
  237. openstackclient/volume/v2/consistency_group_snapshot.py +25 -10
  238. openstackclient/volume/v2/qos_specs.py +28 -17
  239. openstackclient/volume/v2/service.py +17 -6
  240. openstackclient/volume/v2/volume.py +57 -29
  241. openstackclient/volume/v2/volume_backend.py +19 -6
  242. openstackclient/volume/v2/volume_backup.py +46 -20
  243. openstackclient/volume/v2/volume_host.py +6 -4
  244. openstackclient/volume/v2/volume_snapshot.py +50 -24
  245. openstackclient/volume/v2/volume_transfer_request.py +31 -13
  246. openstackclient/volume/v2/volume_type.py +43 -24
  247. openstackclient/volume/v3/block_storage_cleanup.py +11 -3
  248. openstackclient/volume/v3/block_storage_cluster.py +19 -7
  249. openstackclient/volume/v3/block_storage_log_level.py +15 -6
  250. openstackclient/volume/v3/block_storage_manage.py +10 -4
  251. openstackclient/volume/v3/block_storage_resource_filter.py +17 -5
  252. openstackclient/volume/v3/service.py +16 -6
  253. openstackclient/volume/v3/volume.py +89 -39
  254. openstackclient/volume/v3/volume_attachment.py +43 -21
  255. openstackclient/volume/v3/volume_backup.py +53 -24
  256. openstackclient/volume/v3/volume_group.py +23 -13
  257. openstackclient/volume/v3/volume_group_snapshot.py +32 -13
  258. openstackclient/volume/v3/volume_group_type.py +26 -13
  259. openstackclient/volume/v3/volume_message.py +15 -7
  260. openstackclient/volume/v3/volume_snapshot.py +69 -32
  261. openstackclient/volume/v3/volume_transfer_request.py +31 -13
  262. openstackclient/volume/v3/volume_type.py +42 -24
  263. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/METADATA +6 -6
  264. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/RECORD +271 -260
  265. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/WHEEL +1 -1
  266. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/entry_points.txt +53 -1
  267. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/AUTHORS +4 -0
  268. python_openstackclient-10.0.0.dist-info/pbr.json +1 -0
  269. openstackclient/api/image_v1.py +0 -69
  270. openstackclient/api/image_v2.py +0 -79
  271. openstackclient/network/v2/floating_ip_pool.py +0 -38
  272. openstackclient/tests/functional/image/v1/test_image.py +0 -97
  273. openstackclient/tests/unit/api/test_image_v1.py +0 -96
  274. openstackclient/tests/unit/api/test_image_v2.py +0 -96
  275. openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +0 -248
  276. openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +0 -49
  277. openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +0 -39
  278. openstackclient/tests/unit/network/v2/test_network_compute.py +0 -404
  279. openstackclient/tests/unit/network/v2/test_security_group_compute.py +0 -392
  280. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +0 -555
  281. python_openstackclient-9.0.0.dist-info/pbr.json +0 -1
  282. /openstackclient/{tests/functional/image/v1 → network/v2/bgpvpn}/__init__.py +0 -0
  283. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/LICENSE +0 -0
  284. {python_openstackclient-9.0.0.dist-info → python_openstackclient-10.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,285 @@
1
+ # Copyright 2026 Openinfra Foundation
2
+ #
3
+ # All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License. You may obtain
7
+ # a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
16
+ #
17
+
18
+ import operator
19
+ from unittest import mock
20
+
21
+ from osc_lib import exceptions
22
+ from osc_lib import utils as osc_utils
23
+ from osc_lib.utils import columns as column_util
24
+
25
+ from openstackclient.network.v2.bgpvpn import network_association
26
+ from openstackclient.tests.unit.network.v2.bgpvpn import fakes
27
+
28
+
29
+ columns_short = tuple(
30
+ col
31
+ for col, _, listing_mode in network_association._attr_map
32
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_SHORT_ONLY)
33
+ )
34
+ columns_long = tuple(
35
+ col
36
+ for col, _, listing_mode in network_association._attr_map
37
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)
38
+ )
39
+ headers_short = tuple(
40
+ head
41
+ for _, head, listing_mode in network_association._attr_map
42
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_SHORT_ONLY)
43
+ )
44
+ headers_long = tuple(
45
+ head
46
+ for _, head, listing_mode in network_association._attr_map
47
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)
48
+ )
49
+ sorted_attr_map = sorted(
50
+ network_association._attr_map, key=operator.itemgetter(1)
51
+ )
52
+ sorted_columns = tuple(col for col, _, _ in sorted_attr_map)
53
+ sorted_headers = tuple(head for _, head, _ in sorted_attr_map)
54
+
55
+
56
+ def _get_data(attrs, columns=sorted_columns):
57
+ return osc_utils.get_dict_properties(
58
+ attrs, columns, formatters=network_association._formatters
59
+ )
60
+
61
+
62
+ class TestCreateNetAssoc(fakes.TestNeutronClientBgpvpn):
63
+ def setUp(self):
64
+ super().setUp()
65
+ self.network_client.find_bgpvpn.side_effect = (
66
+ lambda name_or_id, **kwargs: {'id': name_or_id}
67
+ )
68
+ self.network_client.find_network.side_effect = (
69
+ lambda name_or_id, **kwargs: {'id': name_or_id}
70
+ )
71
+ self.cmd = network_association.CreateBgpvpnNetAssoc(self.app, None)
72
+
73
+ @mock.patch('osc_lib.cli.identity.find_project')
74
+ def test_create_network_association(self, mock_find_project):
75
+ fake_bgpvpn = fakes.create_one_bgpvpn()
76
+ fake_res = fakes.create_one_resource()
77
+ fake_assoc = fakes.create_one_network_association()
78
+ self.network_client.create_bgpvpn_network_association.return_value = (
79
+ fake_assoc
80
+ )
81
+ mock_find_project.return_value = mock.Mock(
82
+ id=fake_bgpvpn['project_id']
83
+ )
84
+ arglist = [
85
+ fake_bgpvpn['id'],
86
+ fake_res['id'],
87
+ '--project',
88
+ fake_bgpvpn['project_id'],
89
+ ]
90
+ verifylist = [
91
+ ('bgpvpn', fake_bgpvpn['id']),
92
+ ('resource', fake_res['id']),
93
+ ('project', fake_bgpvpn['project_id']),
94
+ ]
95
+
96
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
97
+
98
+ cols, data = self.cmd.take_action(parsed_args)
99
+
100
+ self.network_client.create_bgpvpn_network_association.assert_called_once_with(
101
+ fake_bgpvpn['id'],
102
+ network_id=fake_res['id'],
103
+ project_id='fake_project_id',
104
+ )
105
+ self.assertEqual(sorted_columns, cols)
106
+ self.assertEqual(_get_data(fake_assoc), data)
107
+
108
+
109
+ class TestDeleteNetAssoc(fakes.TestNeutronClientBgpvpn):
110
+ def setUp(self):
111
+ super().setUp()
112
+ self.network_client.find_bgpvpn.side_effect = (
113
+ lambda name_or_id, **kwargs: {'id': name_or_id}
114
+ )
115
+ self.cmd = network_association.DeleteBgpvpnNetAssoc(self.app, None)
116
+
117
+ def test_delete_one_association(self):
118
+ fake_bgpvpn = fakes.create_one_bgpvpn()
119
+ fake_assoc = fakes.create_one_network_association()
120
+ arglist = [
121
+ fake_assoc['id'],
122
+ fake_bgpvpn['id'],
123
+ ]
124
+ verifylist = [
125
+ ('resource_association_ids', [fake_assoc['id']]),
126
+ ('bgpvpn', fake_bgpvpn['id']),
127
+ ]
128
+
129
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
130
+
131
+ result = self.cmd.take_action(parsed_args)
132
+
133
+ self.network_client.delete_bgpvpn_network_association.assert_called_once_with(
134
+ fake_bgpvpn['id'], fake_assoc['id']
135
+ )
136
+ self.assertIsNone(result)
137
+
138
+ def test_delete_multi_association(self):
139
+ count = 3
140
+ fake_bgpvpn = fakes.create_one_bgpvpn()
141
+ fake_assocs = fakes.create_network_associations(count=count)
142
+ fake_assoc_ids = [a['id'] for a in fake_assocs]
143
+ arglist = [*fake_assoc_ids, fake_bgpvpn['id']]
144
+ verifylist = [
145
+ ('resource_association_ids', fake_assoc_ids),
146
+ ('bgpvpn', fake_bgpvpn['id']),
147
+ ]
148
+
149
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
150
+
151
+ result = self.cmd.take_action(parsed_args)
152
+
153
+ self.network_client.delete_bgpvpn_network_association.assert_has_calls(
154
+ [mock.call(fake_bgpvpn['id'], id) for id in fake_assoc_ids]
155
+ )
156
+ self.assertIsNone(result)
157
+
158
+ def test_delete_multi_association_with_unknown(self):
159
+ count = 3
160
+ fake_bgpvpn = fakes.create_one_bgpvpn()
161
+ fake_assocs = fakes.create_network_associations(count=count)
162
+ fake_assoc_ids = [a['id'] for a in fake_assocs]
163
+
164
+ def raise_unknown_resource(bgpvpn_id, association_id):
165
+ if str(count - 2) in association_id:
166
+ raise Exception()
167
+
168
+ self.network_client.delete_bgpvpn_network_association.side_effect = (
169
+ raise_unknown_resource
170
+ )
171
+ arglist = [*fake_assoc_ids, fake_bgpvpn['id']]
172
+ verifylist = [
173
+ ('resource_association_ids', fake_assoc_ids),
174
+ ('bgpvpn', fake_bgpvpn['id']),
175
+ ]
176
+
177
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
178
+
179
+ self.assertRaises(
180
+ exceptions.CommandError, self.cmd.take_action, parsed_args
181
+ )
182
+
183
+ self.network_client.delete_bgpvpn_network_association.assert_has_calls(
184
+ [mock.call(fake_bgpvpn['id'], id) for id in fake_assoc_ids]
185
+ )
186
+
187
+
188
+ class TestListNetAssoc(fakes.TestNeutronClientBgpvpn):
189
+ def setUp(self):
190
+ super().setUp()
191
+ self.network_client.find_bgpvpn.side_effect = (
192
+ lambda name_or_id, **kwargs: {'id': name_or_id}
193
+ )
194
+ self.cmd = network_association.ListBgpvpnNetAssoc(self.app, None)
195
+
196
+ def test_list_network_associations(self):
197
+ count = 3
198
+ fake_bgpvpn = fakes.create_one_bgpvpn()
199
+ fake_assocs = fakes.create_network_associations(count=count)
200
+ self.network_client.bgpvpn_network_associations.return_value = (
201
+ fake_assocs
202
+ )
203
+ arglist = [
204
+ fake_bgpvpn['id'],
205
+ ]
206
+ verifylist = [
207
+ ('bgpvpn', fake_bgpvpn['id']),
208
+ ]
209
+
210
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
211
+
212
+ headers, data = self.cmd.take_action(parsed_args)
213
+
214
+ self.network_client.bgpvpn_network_associations.assert_called_once_with(
215
+ fake_bgpvpn['id'], retrieve_all=True
216
+ )
217
+ self.assertEqual(headers, list(headers_short))
218
+ self.assertEqual(
219
+ list(data),
220
+ [_get_data(a, columns_short) for a in fake_assocs],
221
+ )
222
+
223
+ def test_list_network_associations_long_mode(self):
224
+ count = 3
225
+ fake_bgpvpn = fakes.create_one_bgpvpn()
226
+ fake_assocs = fakes.create_network_associations(count=count)
227
+ self.network_client.bgpvpn_network_associations.return_value = (
228
+ fake_assocs
229
+ )
230
+ arglist = [
231
+ '--long',
232
+ fake_bgpvpn['id'],
233
+ ]
234
+ verifylist = [
235
+ ('long', True),
236
+ ('bgpvpn', fake_bgpvpn['id']),
237
+ ]
238
+
239
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
240
+
241
+ headers, data = self.cmd.take_action(parsed_args)
242
+
243
+ self.network_client.bgpvpn_network_associations.assert_called_once_with(
244
+ fake_bgpvpn['id'], retrieve_all=True
245
+ )
246
+ self.assertEqual(headers, list(headers_long))
247
+ self.assertEqual(
248
+ list(data),
249
+ [_get_data(a, columns_long) for a in fake_assocs],
250
+ )
251
+
252
+
253
+ class TestShowNetAssoc(fakes.TestNeutronClientBgpvpn):
254
+ def setUp(self):
255
+ super().setUp()
256
+ self.network_client.find_bgpvpn.side_effect = (
257
+ lambda name_or_id, **kwargs: {'id': name_or_id}
258
+ )
259
+ self.cmd = network_association.ShowBgpvpnNetAssoc(self.app, None)
260
+
261
+ def test_show_network_association(self):
262
+ fake_bgpvpn = fakes.create_one_bgpvpn()
263
+ fake_assoc = fakes.create_one_network_association()
264
+ self.network_client.get_bgpvpn_network_association.return_value = (
265
+ fake_assoc
266
+ )
267
+
268
+ arglist = [
269
+ fake_assoc['id'],
270
+ fake_bgpvpn['id'],
271
+ ]
272
+ verifylist = [
273
+ ('resource_association_id', fake_assoc['id']),
274
+ ('bgpvpn', fake_bgpvpn['id']),
275
+ ]
276
+
277
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
278
+
279
+ columns, data = self.cmd.take_action(parsed_args)
280
+
281
+ self.network_client.get_bgpvpn_network_association.assert_called_once_with(
282
+ fake_bgpvpn['id'], fake_assoc['id']
283
+ )
284
+ self.assertEqual(sorted_columns, columns)
285
+ self.assertEqual(data, _get_data(fake_assoc))
@@ -0,0 +1,384 @@
1
+ # Copyright 2026 Openinfra Foundation
2
+ #
3
+ # All Rights Reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License. You may obtain
7
+ # a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations
15
+ # under the License.
16
+ #
17
+
18
+ import operator
19
+ from unittest import mock
20
+
21
+ from osc_lib import exceptions
22
+ from osc_lib import utils as osc_utils
23
+ from osc_lib.utils import columns as column_util
24
+
25
+ from openstackclient.network.v2.bgpvpn import port_association
26
+ from openstackclient.tests.unit.network.v2.bgpvpn import fakes
27
+
28
+
29
+ columns_short = tuple(
30
+ col
31
+ for col, _, listing_mode in port_association._attr_map
32
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_SHORT_ONLY)
33
+ )
34
+ columns_long = tuple(
35
+ col
36
+ for col, _, listing_mode in port_association._attr_map
37
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)
38
+ )
39
+ headers_short = tuple(
40
+ head
41
+ for _, head, listing_mode in port_association._attr_map
42
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_SHORT_ONLY)
43
+ )
44
+ headers_long = tuple(
45
+ head
46
+ for _, head, listing_mode in port_association._attr_map
47
+ if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY)
48
+ )
49
+ sorted_attr_map = sorted(
50
+ port_association._attr_map, key=operator.itemgetter(1)
51
+ )
52
+ sorted_columns = tuple(col for col, _, _ in sorted_attr_map)
53
+ sorted_headers = tuple(head for _, head, _ in sorted_attr_map)
54
+
55
+
56
+ def _get_data(attrs, columns=sorted_columns):
57
+ return osc_utils.get_dict_properties(
58
+ attrs, columns, formatters=port_association._formatters
59
+ )
60
+
61
+
62
+ class TestCreatePortAssoc(fakes.TestNeutronClientBgpvpn):
63
+ def setUp(self):
64
+ super().setUp()
65
+ self.cmd = port_association.CreateBgpvpnPortAssoc(self.app, None)
66
+ self.fake_bgpvpn = fakes.create_one_bgpvpn()
67
+ self.fake_port = fakes.create_one_resource()
68
+ self.network_client.find_bgpvpn.side_effect = (
69
+ lambda name_or_id, **kwargs: {'id': name_or_id}
70
+ )
71
+ self.network_client.find_port.side_effect = (
72
+ lambda name_or_id, **kwargs: {'id': name_or_id}
73
+ )
74
+ self.fake_project = mock.Mock(id='fake_project_id')
75
+ self.identity_sdk_client.find_project = mock.Mock(
76
+ return_value=self.fake_project
77
+ )
78
+
79
+ @mock.patch('osc_lib.cli.identity.find_project')
80
+ def test_create_port_association(self, mock_find_project):
81
+ fake_assoc = fakes.create_one_port_association()
82
+ self.network_client.create_bgpvpn_port_association.return_value = (
83
+ fake_assoc
84
+ )
85
+ mock_find_project.return_value = mock.Mock(
86
+ id=self.fake_bgpvpn['project_id']
87
+ )
88
+ arglist = [
89
+ self.fake_bgpvpn['id'],
90
+ self.fake_port['id'],
91
+ '--project',
92
+ self.fake_bgpvpn['project_id'],
93
+ ]
94
+ verifylist = [
95
+ ('bgpvpn', self.fake_bgpvpn['id']),
96
+ ('port', self.fake_port['id']),
97
+ ('project', self.fake_bgpvpn['project_id']),
98
+ ]
99
+
100
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
101
+
102
+ cols, _data = self.cmd.take_action(parsed_args)
103
+
104
+ self.network_client.create_bgpvpn_port_association.assert_called_once_with(
105
+ self.fake_bgpvpn['id'],
106
+ port_id=self.fake_port['id'],
107
+ project_id='fake_project_id',
108
+ routes=[],
109
+ )
110
+ self.assertIn('id', cols)
111
+ self.assertIn('port_id', cols)
112
+
113
+
114
+ class TestSetPortAssoc(fakes.TestNeutronClientBgpvpn):
115
+ def setUp(self):
116
+ super().setUp()
117
+ self.cmd = port_association.SetBgpvpnPortAssoc(self.app, None)
118
+ self.fake_bgpvpn = fakes.create_one_bgpvpn()
119
+ self.network_client.find_bgpvpn.side_effect = (
120
+ lambda name_or_id, **kwargs: {'id': name_or_id}
121
+ )
122
+ self.network_client.find_bgpvpn_port_association.return_value = {
123
+ 'routes': []
124
+ }
125
+
126
+ def test_set_port_association_advertise(self):
127
+ fake_assoc = fakes.create_one_port_association()
128
+ arglist = [
129
+ fake_assoc['id'],
130
+ self.fake_bgpvpn['id'],
131
+ '--advertise-fixed-ips',
132
+ ]
133
+ verifylist = [
134
+ ('port_association_id', fake_assoc['id']),
135
+ ('bgpvpn', self.fake_bgpvpn['id']),
136
+ ]
137
+
138
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
139
+ result = self.cmd.take_action(parsed_args)
140
+
141
+ self.network_client.update_bgpvpn_port_association.assert_called_once_with(
142
+ self.fake_bgpvpn['id'],
143
+ fake_assoc['id'],
144
+ advertise_fixed_ips=True,
145
+ routes=[],
146
+ )
147
+ self.assertIsNone(result)
148
+
149
+ def test_set_port_association_no_advertise(self):
150
+ fake_assoc = fakes.create_one_port_association()
151
+ arglist = [
152
+ fake_assoc['id'],
153
+ self.fake_bgpvpn['id'],
154
+ '--no-advertise-fixed-ips',
155
+ ]
156
+ verifylist = [
157
+ ('port_association_id', fake_assoc['id']),
158
+ ('bgpvpn', self.fake_bgpvpn['id']),
159
+ ]
160
+
161
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
162
+ result = self.cmd.take_action(parsed_args)
163
+
164
+ self.network_client.update_bgpvpn_port_association.assert_called_once_with(
165
+ self.fake_bgpvpn['id'],
166
+ fake_assoc['id'],
167
+ advertise_fixed_ips=False,
168
+ routes=[],
169
+ )
170
+ self.assertIsNone(result)
171
+
172
+
173
+ class TestUnsetPortAssoc(fakes.TestNeutronClientBgpvpn):
174
+ def setUp(self):
175
+ super().setUp()
176
+ self.cmd = port_association.UnsetBgpvpnPortAssoc(self.app, None)
177
+ self.fake_bgpvpn = fakes.create_one_bgpvpn()
178
+ self.network_client.find_bgpvpn.side_effect = (
179
+ lambda name_or_id, **kwargs: {'id': name_or_id}
180
+ )
181
+ self.network_client.find_bgpvpn_port_association.return_value = {
182
+ 'routes': []
183
+ }
184
+
185
+ def test_unset_port_association_advertise(self):
186
+ fake_assoc = fakes.create_one_port_association()
187
+ arglist = [
188
+ fake_assoc['id'],
189
+ self.fake_bgpvpn['id'],
190
+ '--advertise-fixed-ips',
191
+ ]
192
+ verifylist = [
193
+ ('port_association_id', fake_assoc['id']),
194
+ ('bgpvpn', self.fake_bgpvpn['id']),
195
+ ]
196
+
197
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
198
+ result = self.cmd.take_action(parsed_args)
199
+
200
+ self.network_client.update_bgpvpn_port_association.assert_called_once_with(
201
+ self.fake_bgpvpn['id'],
202
+ fake_assoc['id'],
203
+ advertise_fixed_ips=False,
204
+ routes=[],
205
+ )
206
+ self.assertIsNone(result)
207
+
208
+
209
+ class TestDeletePortAssoc(fakes.TestNeutronClientBgpvpn):
210
+ def setUp(self):
211
+ super().setUp()
212
+ self.network_client.find_bgpvpn.side_effect = (
213
+ lambda name_or_id, **kwargs: {'id': name_or_id}
214
+ )
215
+ self.cmd = port_association.DeleteBgpvpnPortAssoc(self.app, None)
216
+
217
+ def test_delete_one_association(self):
218
+ fake_bgpvpn = fakes.create_one_bgpvpn()
219
+ fake_assoc = fakes.create_one_port_association()
220
+ arglist = [
221
+ fake_assoc['id'],
222
+ fake_bgpvpn['id'],
223
+ ]
224
+ verifylist = [
225
+ ('port_association_ids', [fake_assoc['id']]),
226
+ ('bgpvpn', fake_bgpvpn['id']),
227
+ ]
228
+
229
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
230
+
231
+ result = self.cmd.take_action(parsed_args)
232
+
233
+ self.network_client.delete_bgpvpn_port_association.assert_called_once_with(
234
+ fake_bgpvpn['id'], fake_assoc['id']
235
+ )
236
+ self.assertIsNone(result)
237
+
238
+ def test_delete_multi_association(self):
239
+ count = 3
240
+ fake_bgpvpn = fakes.create_one_bgpvpn()
241
+ fake_assocs = fakes.create_port_associations(count=count)
242
+ fake_assoc_ids = [a['id'] for a in fake_assocs]
243
+ arglist = [*fake_assoc_ids, fake_bgpvpn['id']]
244
+ verifylist = [
245
+ ('port_association_ids', fake_assoc_ids),
246
+ ('bgpvpn', fake_bgpvpn['id']),
247
+ ]
248
+
249
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
250
+
251
+ result = self.cmd.take_action(parsed_args)
252
+
253
+ self.network_client.delete_bgpvpn_port_association.assert_has_calls(
254
+ [mock.call(fake_bgpvpn['id'], id) for id in fake_assoc_ids]
255
+ )
256
+ self.assertIsNone(result)
257
+
258
+ def test_delete_multi_association_with_unknown(self):
259
+ count = 3
260
+ fake_bgpvpn = fakes.create_one_bgpvpn()
261
+ fake_assocs = fakes.create_port_associations(count=count)
262
+ fake_assoc_ids = [a['id'] for a in fake_assocs]
263
+
264
+ def raise_unknown_resource(bgpvpn_id, association_id):
265
+ if str(count - 2) in association_id:
266
+ raise Exception()
267
+
268
+ self.network_client.delete_bgpvpn_port_association.side_effect = (
269
+ raise_unknown_resource
270
+ )
271
+ arglist = [*fake_assoc_ids, fake_bgpvpn['id']]
272
+ verifylist = [
273
+ ('port_association_ids', fake_assoc_ids),
274
+ ('bgpvpn', fake_bgpvpn['id']),
275
+ ]
276
+
277
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
278
+
279
+ self.assertRaises(
280
+ exceptions.CommandError, self.cmd.take_action, parsed_args
281
+ )
282
+
283
+ self.network_client.delete_bgpvpn_port_association.assert_has_calls(
284
+ [mock.call(fake_bgpvpn['id'], id) for id in fake_assoc_ids]
285
+ )
286
+
287
+
288
+ class TestListPortAssoc(fakes.TestNeutronClientBgpvpn):
289
+ def setUp(self):
290
+ super().setUp()
291
+ self.network_client.find_bgpvpn.side_effect = (
292
+ lambda name_or_id, **kwargs: {'id': name_or_id}
293
+ )
294
+ self.cmd = port_association.ListBgpvpnPortAssoc(self.app, None)
295
+
296
+ def test_list_port_associations(self):
297
+ count = 3
298
+ fake_bgpvpn = fakes.create_one_bgpvpn()
299
+ fake_assocs = fakes.create_port_associations(count=count)
300
+ self.network_client.bgpvpn_port_associations.return_value = fake_assocs
301
+ arglist = [
302
+ fake_bgpvpn['id'],
303
+ ]
304
+ verifylist = [
305
+ ('bgpvpn', fake_bgpvpn['id']),
306
+ ]
307
+
308
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
309
+
310
+ headers, data = self.cmd.take_action(parsed_args)
311
+
312
+ self.network_client.bgpvpn_port_associations.assert_called_once_with(
313
+ fake_bgpvpn['id'], retrieve_all=True
314
+ )
315
+ self.assertEqual(headers, list(headers_short))
316
+ self.assertEqual(
317
+ list(data),
318
+ [_get_data(a, columns_short) for a in fake_assocs],
319
+ )
320
+
321
+ def test_list_port_associations_long_mode(self):
322
+ count = 3
323
+ fake_bgpvpn = fakes.create_one_bgpvpn()
324
+ fake_assocs = fakes.create_port_associations(count=count)
325
+ self.network_client.bgpvpn_port_associations.return_value = fake_assocs
326
+ arglist = [
327
+ '--long',
328
+ fake_bgpvpn['id'],
329
+ ]
330
+ verifylist = [
331
+ ('long', True),
332
+ ('bgpvpn', fake_bgpvpn['id']),
333
+ ]
334
+
335
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
336
+
337
+ headers, data = self.cmd.take_action(parsed_args)
338
+
339
+ self.network_client.bgpvpn_port_associations.assert_called_once_with(
340
+ fake_bgpvpn['id'], retrieve_all=True
341
+ )
342
+ self.assertEqual(headers, list(headers_long))
343
+ self.assertEqual(
344
+ list(data),
345
+ [_get_data(a, columns_long) for a in fake_assocs],
346
+ )
347
+
348
+
349
+ class TestShowPortAssoc(fakes.TestNeutronClientBgpvpn):
350
+ def setUp(self):
351
+ super().setUp()
352
+ self.network_client.find_bgpvpn.side_effect = (
353
+ lambda name_or_id, **kwargs: {'id': name_or_id}
354
+ )
355
+ self.cmd = port_association.ShowBgpvpnPortAssoc(self.app, None)
356
+
357
+ def test_show_port_association(self):
358
+ fake_bgpvpn = fakes.create_one_bgpvpn()
359
+ fake_assoc = fakes.create_one_port_association()
360
+ self.network_client.get_bgpvpn_port_association.return_value = (
361
+ fake_assoc
362
+ )
363
+
364
+ arglist = [
365
+ fake_assoc['id'],
366
+ fake_bgpvpn['id'],
367
+ ]
368
+ verifylist = [
369
+ ('port_association_id', fake_assoc['id']),
370
+ ('bgpvpn', fake_bgpvpn['id']),
371
+ ]
372
+
373
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
374
+
375
+ cols, _data = self.cmd.take_action(parsed_args)
376
+
377
+ self.network_client.get_bgpvpn_port_association.assert_called_once_with(
378
+ fake_bgpvpn['id'], fake_assoc['id']
379
+ )
380
+ # _transform_resource removes 'routes' and only adds
381
+ # prefix_routes/bgpvpn_routes if routes exist, so with empty
382
+ # routes the show columns won't include those fields
383
+ self.assertIn('id', cols)
384
+ self.assertIn('port_id', cols)