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.
Files changed (292) 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 +126 -114
  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 +251 -171
  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 +103 -41
  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 +26 -12
  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 +71 -50
  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 +115 -92
  64. openstackclient/identity/v3/mapping.py +26 -13
  65. openstackclient/identity/v3/policy.py +23 -12
  66. openstackclient/identity/v3/project.py +211 -122
  67. openstackclient/identity/v3/region.py +36 -16
  68. openstackclient/identity/v3/registered_limit.py +116 -109
  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 -17
  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 +76 -49
  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_limit.py +47 -0
  154. openstackclient/tests/functional/identity/v3/test_project.py +10 -10
  155. openstackclient/tests/functional/identity/v3/test_role.py +18 -18
  156. openstackclient/tests/functional/identity/v3/test_role_assignment.py +12 -12
  157. openstackclient/tests/functional/identity/v3/test_user.py +8 -8
  158. openstackclient/tests/functional/image/base.py +1 -6
  159. openstackclient/tests/functional/image/v2/test_metadef_objects.py +69 -0
  160. openstackclient/tests/functional/network/v2/common.py +5 -2
  161. openstackclient/tests/functional/network/v2/test_floating_ip.py +10 -4
  162. openstackclient/tests/functional/network/v2/test_ip_availability.py +4 -0
  163. openstackclient/tests/functional/network/v2/test_network_meter_rule.py +3 -2
  164. openstackclient/tests/functional/network/v2/test_network_segment.py +5 -0
  165. openstackclient/tests/functional/network/v2/test_subnet.py +13 -9
  166. openstackclient/tests/functional/object/v1/common.py +4 -0
  167. openstackclient/tests/functional/volume/v2/common.py +4 -0
  168. openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +27 -11
  169. openstackclient/tests/functional/volume/v2/test_volume_type.py +2 -2
  170. openstackclient/tests/functional/volume/v3/common.py +4 -0
  171. openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +56 -138
  172. openstackclient/tests/functional/volume/v3/test_volume_type.py +2 -2
  173. openstackclient/tests/unit/common/test_availability_zone.py +35 -49
  174. openstackclient/tests/unit/common/test_extension.py +2 -2
  175. openstackclient/tests/unit/common/test_module.py +12 -7
  176. openstackclient/tests/unit/common/test_project_cleanup.py +3 -1
  177. openstackclient/tests/unit/common/test_quota.py +62 -23
  178. openstackclient/tests/unit/compute/v2/fakes.py +25 -0
  179. openstackclient/tests/unit/compute/v2/test_flavor.py +28 -2
  180. openstackclient/tests/unit/compute/v2/test_keypair.py +6 -6
  181. openstackclient/tests/unit/compute/v2/test_server.py +17 -104
  182. openstackclient/tests/unit/compute/v2/test_server_share.py +287 -0
  183. openstackclient/tests/unit/identity/v3/fakes.py +3 -0
  184. openstackclient/tests/unit/identity/v3/test_group.py +4 -14
  185. openstackclient/tests/unit/identity/v3/test_identity_provider.py +303 -299
  186. openstackclient/tests/unit/identity/v3/test_limit.py +197 -145
  187. openstackclient/tests/unit/identity/v3/test_project.py +831 -512
  188. openstackclient/tests/unit/identity/v3/test_protocol.py +97 -88
  189. openstackclient/tests/unit/identity/v3/test_registered_limit.py +355 -220
  190. openstackclient/tests/unit/identity/v3/test_user.py +4 -4
  191. openstackclient/tests/unit/image/v2/test_image.py +16 -16
  192. openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +105 -6
  193. openstackclient/tests/unit/network/test_common.py +0 -155
  194. openstackclient/tests/unit/network/v2/bgpvpn/__init__.py +0 -0
  195. openstackclient/tests/unit/network/v2/bgpvpn/fakes.py +179 -0
  196. openstackclient/tests/unit/network/v2/bgpvpn/test_bgpvpn.py +584 -0
  197. openstackclient/tests/unit/network/v2/bgpvpn/test_network_association.py +285 -0
  198. openstackclient/tests/unit/network/v2/bgpvpn/test_port_association.py +384 -0
  199. openstackclient/tests/unit/network/v2/bgpvpn/test_router_association.py +297 -0
  200. openstackclient/tests/unit/network/v2/fwaas/__init__.py +0 -0
  201. openstackclient/tests/unit/network/v2/fwaas/test_group.py +897 -0
  202. openstackclient/tests/unit/network/v2/fwaas/test_policy.py +869 -0
  203. openstackclient/tests/unit/network/v2/fwaas/test_rule.py +980 -0
  204. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_flow.py → test_tap_flow.py} +18 -25
  205. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_mirror.py → test_tap_mirror.py} +19 -29
  206. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_service.py → test_tap_service.py} +19 -29
  207. openstackclient/tests/unit/network/v2/test_address_group.py +2 -2
  208. openstackclient/tests/unit/network/v2/{test_floating_ip_network.py → test_floating_ip.py} +3 -2
  209. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +13 -13
  210. openstackclient/tests/unit/network/v2/test_network_agent.py +8 -4
  211. openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +3 -3
  212. openstackclient/tests/unit/network/v2/test_network_flavor.py +2 -2
  213. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +1 -1
  214. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +2 -2
  215. openstackclient/tests/unit/network/v2/test_network_rbac.py +1 -1
  216. openstackclient/tests/unit/network/v2/test_network_segment.py +1 -1
  217. openstackclient/tests/unit/network/v2/test_network_segment_range.py +7 -10
  218. openstackclient/tests/unit/network/v2/test_network_trunk.py +1 -1
  219. openstackclient/tests/unit/network/v2/test_router.py +8 -9
  220. openstackclient/tests/unit/network/v2/{test_security_group_network.py → test_security_group.py} +1 -20
  221. openstackclient/tests/unit/network/v2/{test_security_group_rule_network.py → test_security_group_rule.py} +7 -41
  222. openstackclient/tests/unit/network/v2/test_subnet.py +2 -1
  223. openstackclient/tests/unit/network/v2/test_subnet_pool.py +2 -1
  224. openstackclient/tests/unit/object/v1/fakes.py +8 -7
  225. openstackclient/tests/unit/object/v1/test_container.py +65 -101
  226. openstackclient/tests/unit/object/v1/test_container_all.py +8 -1
  227. openstackclient/tests/unit/object/v1/test_object.py +44 -84
  228. openstackclient/tests/unit/object/v1/test_object_all.py +8 -1
  229. openstackclient/tests/unit/test_hacking.py +108 -0
  230. openstackclient/tests/unit/volume/v2/fakes.py +1 -0
  231. openstackclient/tests/unit/volume/v2/test_consistency_group.py +8 -2
  232. openstackclient/tests/unit/volume/v2/test_volume.py +7 -6
  233. openstackclient/tests/unit/volume/v2/test_volume_backup.py +1 -5
  234. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +2 -1
  235. openstackclient/tests/unit/volume/v2/test_volume_type.py +2 -4
  236. openstackclient/tests/unit/volume/v3/fakes.py +1 -0
  237. openstackclient/tests/unit/volume/v3/test_volume.py +94 -15
  238. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
  239. openstackclient/tests/unit/volume/v3/test_volume_backup.py +1 -5
  240. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +55 -1
  241. openstackclient/tests/unit/volume/v3/test_volume_type.py +2 -4
  242. openstackclient/volume/client.py +7 -3
  243. openstackclient/volume/v2/backup_record.py +15 -6
  244. openstackclient/volume/v2/consistency_group.py +37 -25
  245. openstackclient/volume/v2/consistency_group_snapshot.py +27 -12
  246. openstackclient/volume/v2/qos_specs.py +30 -19
  247. openstackclient/volume/v2/service.py +17 -6
  248. openstackclient/volume/v2/volume.py +69 -34
  249. openstackclient/volume/v2/volume_backend.py +19 -6
  250. openstackclient/volume/v2/volume_backup.py +48 -22
  251. openstackclient/volume/v2/volume_host.py +6 -4
  252. openstackclient/volume/v2/volume_snapshot.py +52 -26
  253. openstackclient/volume/v2/volume_transfer_request.py +33 -15
  254. openstackclient/volume/v2/volume_type.py +46 -27
  255. openstackclient/volume/v3/block_storage_cleanup.py +11 -3
  256. openstackclient/volume/v3/block_storage_cluster.py +19 -7
  257. openstackclient/volume/v3/block_storage_log_level.py +15 -6
  258. openstackclient/volume/v3/block_storage_manage.py +10 -4
  259. openstackclient/volume/v3/block_storage_resource_filter.py +17 -5
  260. openstackclient/volume/v3/service.py +16 -6
  261. openstackclient/volume/v3/volume.py +103 -46
  262. openstackclient/volume/v3/volume_attachment.py +43 -21
  263. openstackclient/volume/v3/volume_backup.py +55 -26
  264. openstackclient/volume/v3/volume_group.py +23 -13
  265. openstackclient/volume/v3/volume_group_snapshot.py +32 -13
  266. openstackclient/volume/v3/volume_group_type.py +26 -13
  267. openstackclient/volume/v3/volume_message.py +15 -7
  268. openstackclient/volume/v3/volume_snapshot.py +71 -34
  269. openstackclient/volume/v3/volume_transfer_request.py +33 -15
  270. openstackclient/volume/v3/volume_type.py +45 -27
  271. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/METADATA +6 -6
  272. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/RECORD +279 -267
  273. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/WHEEL +1 -1
  274. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/entry_points.txt +53 -1
  275. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/AUTHORS +9 -0
  276. python_openstackclient-10.0.0.dist-info/pbr.json +1 -0
  277. openstackclient/api/image_v1.py +0 -69
  278. openstackclient/api/image_v2.py +0 -79
  279. openstackclient/network/v2/floating_ip_pool.py +0 -38
  280. openstackclient/tests/functional/image/v1/test_image.py +0 -97
  281. openstackclient/tests/unit/api/test_image_v1.py +0 -96
  282. openstackclient/tests/unit/api/test_image_v2.py +0 -96
  283. openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +0 -248
  284. openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +0 -49
  285. openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +0 -39
  286. openstackclient/tests/unit/network/v2/test_network_compute.py +0 -404
  287. openstackclient/tests/unit/network/v2/test_security_group_compute.py +0 -392
  288. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +0 -555
  289. python_openstackclient-8.3.0.dist-info/pbr.json +0 -1
  290. /openstackclient/{tests/functional/image/v1 → network/v2/bgpvpn}/__init__.py +0 -0
  291. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/LICENSE +0 -0
  292. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/top_level.txt +0 -0
@@ -912,7 +912,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
912
912
  arglist = [self.volumes[0].id]
913
913
  verifylist = [
914
914
  ("force", False),
915
- ("purge", False),
915
+ ("cascade", False),
916
916
  ("volumes", [self.volumes[0].id]),
917
917
  ]
918
918
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -931,7 +931,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
931
931
  arglist = [v.id for v in self.volumes]
932
932
  verifylist = [
933
933
  ('force', False),
934
- ('purge', False),
934
+ ('cascade', False),
935
935
  ('volumes', arglist),
936
936
  ]
937
937
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -958,7 +958,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
958
958
  ]
959
959
  verifylist = [
960
960
  ('force', False),
961
- ('purge', False),
961
+ ('cascade', False),
962
962
  ('volumes', [self.volumes[0].id, 'unexist_volume']),
963
963
  ]
964
964
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -989,7 +989,29 @@ class TestVolumeDelete(volume_fakes.TestVolume):
989
989
  ]
990
990
  verifylist = [
991
991
  ('force', False),
992
- ('purge', True),
992
+ ('cascade', True),
993
+ ('volumes', [self.volumes[0].id]),
994
+ ]
995
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
996
+
997
+ result = self.cmd.take_action(parsed_args)
998
+ self.assertIsNone(result)
999
+
1000
+ self.volume_sdk_client.find_volume.assert_called_once_with(
1001
+ self.volumes[0].id, ignore_missing=False
1002
+ )
1003
+ self.volume_sdk_client.delete_volume.assert_called_once_with(
1004
+ self.volumes[0].id, cascade=True, force=False
1005
+ )
1006
+
1007
+ def test_volume_delete_with_cascade(self):
1008
+ arglist = [
1009
+ '--cascade',
1010
+ self.volumes[0].id,
1011
+ ]
1012
+ verifylist = [
1013
+ ('force', False),
1014
+ ('cascade', True),
993
1015
  ('volumes', [self.volumes[0].id]),
994
1016
  ]
995
1017
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1011,7 +1033,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1011
1033
  ]
1012
1034
  verifylist = [
1013
1035
  ('force', True),
1014
- ('purge', False),
1036
+ ('cascade', False),
1015
1037
  ('volumes', [self.volumes[0].id]),
1016
1038
  ]
1017
1039
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1031,7 +1053,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1031
1053
  verifylist = [
1032
1054
  ("remote", True),
1033
1055
  ("force", False),
1034
- ("purge", False),
1056
+ ("cascade", False),
1035
1057
  ("volumes", [self.volumes[0].id]),
1036
1058
  ]
1037
1059
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1052,7 +1074,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1052
1074
  verifylist = [
1053
1075
  ('remote', True),
1054
1076
  ('force', False),
1055
- ('purge', False),
1077
+ ('cascade', False),
1056
1078
  ('volumes', arglist[1:]),
1057
1079
  ]
1058
1080
  parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1077,7 +1099,6 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1077
1099
  verifylist = [
1078
1100
  ('remote', True),
1079
1101
  ('force', False),
1080
- ('purge', True),
1081
1102
  ('volumes', [self.volumes[0].id]),
1082
1103
  ]
1083
1104
 
@@ -1086,7 +1107,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1086
1107
  exceptions.CommandError, self.cmd.take_action, parsed_args
1087
1108
  )
1088
1109
  self.assertIn(
1089
- "The --force and --purge options are not supported with the "
1110
+ "The --force and --cascade options are not supported with the "
1090
1111
  "--remote parameter.",
1091
1112
  str(exc),
1092
1113
  )
@@ -1104,7 +1125,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1104
1125
  verifylist = [
1105
1126
  ('remote', True),
1106
1127
  ('force', True),
1107
- ('purge', False),
1128
+ ('cascade', False),
1108
1129
  ('volumes', [self.volumes[0].id]),
1109
1130
  ]
1110
1131
 
@@ -1113,7 +1134,7 @@ class TestVolumeDelete(volume_fakes.TestVolume):
1113
1134
  exceptions.CommandError, self.cmd.take_action, parsed_args
1114
1135
  )
1115
1136
  self.assertIn(
1116
- "The --force and --purge options are not supported with the "
1137
+ "The --force and --cascade options are not supported with the "
1117
1138
  "--remote parameter.",
1118
1139
  str(exc),
1119
1140
  )
@@ -1702,9 +1723,10 @@ class TestVolumeMigrate(volume_fakes.TestVolume):
1702
1723
  host="host@backend-name#pool",
1703
1724
  force_host_copy=False,
1704
1725
  lock_volume=False,
1726
+ cluster=None,
1705
1727
  )
1706
1728
 
1707
- def test_volume_migrate_with_option(self):
1729
+ def test_volume_migrate_with_host(self):
1708
1730
  arglist = [
1709
1731
  "--force-host-copy",
1710
1732
  "--lock-volume",
@@ -1731,9 +1753,66 @@ class TestVolumeMigrate(volume_fakes.TestVolume):
1731
1753
  host="host@backend-name#pool",
1732
1754
  force_host_copy=True,
1733
1755
  lock_volume=True,
1756
+ cluster=None,
1734
1757
  )
1735
1758
 
1736
- def test_volume_migrate_without_host(self):
1759
+ def test_volume_migrate_with_cluster(self):
1760
+ self.set_volume_api_version('3.16')
1761
+ arglist = [
1762
+ "--cluster",
1763
+ "cluster@backend-name#pool",
1764
+ self.volume.id,
1765
+ ]
1766
+ verifylist = [
1767
+ (
1768
+ "cluster",
1769
+ "cluster@backend-name#pool",
1770
+ ),
1771
+ ("volume", self.volume.id),
1772
+ ]
1773
+
1774
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1775
+
1776
+ result = self.cmd.take_action(parsed_args)
1777
+ self.assertIsNone(result)
1778
+
1779
+ self.volume_sdk_client.find_volume.assert_called_with(
1780
+ self.volume.id, ignore_missing=False
1781
+ )
1782
+ self.volume_sdk_client.migrate_volume.assert_called_once_with(
1783
+ self.volume.id,
1784
+ host=None,
1785
+ force_host_copy=False,
1786
+ lock_volume=False,
1787
+ cluster="cluster@backend-name#pool",
1788
+ )
1789
+
1790
+ def test_volume_migrate_with_cluster_pre_v316(self):
1791
+ self.set_volume_api_version('3.15')
1792
+ arglist = [
1793
+ "--cluster",
1794
+ "cluster@backend-name#pool",
1795
+ self.volume.id,
1796
+ ]
1797
+ verifylist = [
1798
+ (
1799
+ "cluster",
1800
+ "cluster@backend-name#pool",
1801
+ ),
1802
+ ("volume", self.volume.id),
1803
+ ]
1804
+
1805
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1806
+
1807
+ self.assertRaises(
1808
+ exceptions.CommandError,
1809
+ self.cmd.take_action,
1810
+ parsed_args,
1811
+ )
1812
+
1813
+ self.volume_sdk_client.migrate_volume.assert_not_called()
1814
+
1815
+ def test_volume_migrate_without_host_and_cluster(self):
1737
1816
  arglist = [
1738
1817
  self.volume.id,
1739
1818
  ]
@@ -1750,7 +1829,6 @@ class TestVolumeMigrate(volume_fakes.TestVolume):
1750
1829
  arglist,
1751
1830
  verifylist,
1752
1831
  )
1753
-
1754
1832
  self.volume_sdk_client.find_volume.assert_not_called()
1755
1833
  self.volume_sdk_client.migrate_volume.assert_not_called()
1756
1834
 
@@ -1998,7 +2076,8 @@ class TestVolumeSet(volume_fakes.TestVolume):
1998
2076
  result = self.cmd.take_action(parsed_args)
1999
2077
  self.volumes_mock.retype.assert_not_called()
2000
2078
  mock_warning.assert_called_with(
2001
- "'--retype-policy' option will not work without '--type' option"
2079
+ "'%s' option will not work without '--type' option",
2080
+ '--retype-policy',
2002
2081
  )
2003
2082
  self.assertIsNone(result)
2004
2083
 
@@ -23,7 +23,7 @@ class TestVolumeAttachment(volume_fakes.TestVolume):
23
23
  def setUp(self):
24
24
  super().setUp()
25
25
 
26
- self.projects_mock = self.app.client_manager.identity.projects
26
+ self.projects_mock = self.identity_client.projects
27
27
 
28
28
 
29
29
  class TestVolumeAttachmentCreate(TestVolumeAttachment):
@@ -319,11 +319,7 @@ class TestBackupList(volume_fakes.TestVolume):
319
319
  'Incremental',
320
320
  'Created At',
321
321
  )
322
- columns_long = columns + (
323
- 'Availability Zone',
324
- 'Volume',
325
- 'Container',
326
- )
322
+ columns_long = (*columns, 'Availability Zone', 'Volume', 'Container')
327
323
 
328
324
  def setUp(self):
329
325
  super().setUp()
@@ -180,6 +180,59 @@ class TestVolumeSnapshotCreate(volume_fakes.TestVolume):
180
180
  )
181
181
  self.volume_sdk_client.create_snapshot.assert_not_called()
182
182
 
183
+ def test_snapshot_create_pre_v366(self):
184
+ self.set_volume_api_version('3.65')
185
+
186
+ arglist = ["--force", self.snapshot.name]
187
+ verifylist = [("force", True), ("snapshot_name", self.snapshot.name)]
188
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
189
+
190
+ self.cmd.take_action(parsed_args)
191
+
192
+ # force parameter should be passed
193
+ self.volume_sdk_client.create_snapshot.assert_called_with(
194
+ volume_id=self.snapshot.volume_id,
195
+ force=True,
196
+ name=self.snapshot.name,
197
+ description=None,
198
+ metadata=None,
199
+ )
200
+
201
+ def test_snapshot_create_v366_or_later(self):
202
+ self.set_volume_api_version('3.66')
203
+
204
+ arglist = [self.snapshot.name]
205
+ verifylist = [("force", False), ("snapshot_name", self.snapshot.name)]
206
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
207
+
208
+ self.cmd.take_action(parsed_args)
209
+
210
+ # force parameter should not be passed, for >=3.66
211
+ self.volume_sdk_client.create_snapshot.assert_called_with(
212
+ volume_id=self.snapshot.volume_id,
213
+ name=self.snapshot.name,
214
+ description=None,
215
+ metadata=None,
216
+ )
217
+
218
+ def test_snapshot_create_v366_or_later_with_force(self):
219
+ """--force should be ignored for microversion >= 3.66."""
220
+ self.set_volume_api_version('3.66')
221
+
222
+ arglist = ["--force", self.snapshot.name]
223
+ verifylist = [("force", True), ("snapshot_name", self.snapshot.name)]
224
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
225
+
226
+ self.cmd.take_action(parsed_args)
227
+
228
+ # passed but ignored
229
+ self.volume_sdk_client.create_snapshot.assert_called_with(
230
+ volume_id=self.snapshot.volume_id,
231
+ name=self.snapshot.name,
232
+ description=None,
233
+ metadata=None,
234
+ )
235
+
183
236
 
184
237
  class TestVolumeSnapshotDelete(volume_fakes.TestVolume):
185
238
  def setUp(self):
@@ -340,7 +393,8 @@ class TestVolumeSnapshotList(volume_fakes.TestVolume):
340
393
  self.project_mock.get.return_value = self.project
341
394
 
342
395
  self.columns = ("ID", "Name", "Description", "Status", "Size")
343
- self.columns_long = self.columns + (
396
+ self.columns_long = (
397
+ *self.columns,
344
398
  "Created At",
345
399
  "Volume",
346
400
  "Properties",
@@ -331,7 +331,7 @@ class TestTypeList(TestType):
331
331
  "Name",
332
332
  "Is Public",
333
333
  ]
334
- columns_long = columns + ["Description", "Properties"]
334
+ columns_long = [*columns, "Description", "Properties"]
335
335
  data_with_default_type = [(volume_types[0].id, volume_types[0].name, True)]
336
336
  data = []
337
337
  for t in volume_types:
@@ -509,9 +509,7 @@ class TestTypeList(TestType):
509
509
  'key_size': None,
510
510
  'control_location': 'front-end',
511
511
  }
512
- encryption_columns = self.columns + [
513
- "Encryption",
514
- ]
512
+ encryption_columns = [*self.columns, "Encryption"]
515
513
  encryption_data = []
516
514
  encryption_data.append(
517
515
  (
@@ -13,7 +13,9 @@
13
13
  # under the License.
14
14
  #
15
15
 
16
+ import argparse
16
17
  import logging
18
+ from typing import Any
17
19
 
18
20
  from osc_lib import exceptions
19
21
  from osc_lib import utils
@@ -34,7 +36,7 @@ API_VERSIONS = {
34
36
  _volume_api_version = None
35
37
 
36
38
 
37
- def make_client(instance):
39
+ def make_client(instance: Any) -> Any:
38
40
  """Returns a volume service client."""
39
41
 
40
42
  # Defer client imports until we actually need them
@@ -92,7 +94,9 @@ def make_client(instance):
92
94
  return client
93
95
 
94
96
 
95
- def build_option_parser(parser):
97
+ def build_option_parser(
98
+ parser: argparse.ArgumentParser,
99
+ ) -> argparse.ArgumentParser:
96
100
  """Hook to add global options"""
97
101
  parser.add_argument(
98
102
  '--os-volume-api-version',
@@ -104,7 +108,7 @@ def build_option_parser(parser):
104
108
  return parser
105
109
 
106
110
 
107
- def check_api_version(check_version):
111
+ def check_api_version(check_version: str) -> bool:
108
112
  """Validate version supplied by user
109
113
 
110
114
  Returns:
@@ -14,7 +14,10 @@
14
14
 
15
15
  """Volume v2 Backup action implementations"""
16
16
 
17
+ import argparse
17
18
  import logging
19
+ from collections.abc import Iterable, Sequence
20
+ from typing import Any
18
21
 
19
22
  from osc_lib import utils
20
23
 
@@ -33,7 +36,7 @@ Backup information can be imported into a new service instance to be able to
33
36
  restore."""
34
37
  )
35
38
 
36
- def get_parser(self, prog_name):
39
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
37
40
  parser = super().get_parser(prog_name)
38
41
  parser.add_argument(
39
42
  "backup",
@@ -42,7 +45,9 @@ restore."""
42
45
  )
43
46
  return parser
44
47
 
45
- def take_action(self, parsed_args):
48
+ def take_action(
49
+ self, parsed_args: argparse.Namespace
50
+ ) -> tuple[Sequence[str], Iterable[Any]]:
46
51
  volume_client = self.app.client_manager.volume
47
52
  backup = utils.find_resource(volume_client.backups, parsed_args.backup)
48
53
  backup_data = volume_client.backups.export_record(backup.id)
@@ -53,7 +58,8 @@ restore."""
53
58
  backup_data['Backup Service'] = backup_data.pop('backup_service')
54
59
  backup_data['Metadata'] = backup_data.pop('backup_url')
55
60
 
56
- return zip(*sorted(backup_data.items()))
61
+ col_headers, col_data = zip(*sorted(backup_data.items()))
62
+ return col_headers, col_data
57
63
 
58
64
 
59
65
  class ImportBackupRecord(command.ShowOne):
@@ -64,7 +70,7 @@ Exported backup details contain the metadata necessary to restore to a new or
64
70
  rebuilt service instance"""
65
71
  )
66
72
 
67
- def get_parser(self, prog_name):
73
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
68
74
  parser = super().get_parser(prog_name)
69
75
  parser.add_argument(
70
76
  "backup_service",
@@ -78,10 +84,13 @@ rebuilt service instance"""
78
84
  )
79
85
  return parser
80
86
 
81
- def take_action(self, parsed_args):
87
+ def take_action(
88
+ self, parsed_args: argparse.Namespace
89
+ ) -> tuple[Sequence[str], Iterable[Any]]:
82
90
  volume_client = self.app.client_manager.volume
83
91
  backup_data = volume_client.backups.import_record(
84
92
  parsed_args.backup_service, parsed_args.backup_metadata
85
93
  )
86
94
  backup_data.pop('links', None)
87
- return zip(*sorted(backup_data.items()))
95
+ col_headers, col_data = zip(*sorted(backup_data.items()))
96
+ return col_headers, col_data
@@ -16,6 +16,8 @@
16
16
 
17
17
  import argparse
18
18
  import logging
19
+ from collections.abc import Iterable, Sequence
20
+ from typing import Any
19
21
 
20
22
  from osc_lib.cli import format_columns
21
23
  from osc_lib import exceptions
@@ -28,7 +30,9 @@ from openstackclient.i18n import _
28
30
  LOG = logging.getLogger(__name__)
29
31
 
30
32
 
31
- def _find_volumes(parsed_args_volumes, volume_client):
33
+ def _find_volumes(
34
+ parsed_args_volumes: list[str], volume_client: Any
35
+ ) -> tuple[int, str]:
32
36
  result = 0
33
37
  uuid = ''
34
38
  for volume in parsed_args_volumes:
@@ -38,8 +42,8 @@ def _find_volumes(parsed_args_volumes, volume_client):
38
42
  except Exception as e:
39
43
  result += 1
40
44
  LOG.error(
41
- _("Failed to find volume with name or ID '%(volume)s':%(e)s")
42
- % {'volume': volume, 'e': e}
45
+ _("Failed to find volume with name or ID '%(volume)s':%(e)s"),
46
+ {'volume': volume, 'e': e},
43
47
  )
44
48
 
45
49
  return result, uuid
@@ -48,7 +52,7 @@ def _find_volumes(parsed_args_volumes, volume_client):
48
52
  class AddVolumeToConsistencyGroup(command.Command):
49
53
  _description = _("Add volume(s) to consistency group")
50
54
 
51
- def get_parser(self, prog_name):
55
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
52
56
  parser = super().get_parser(prog_name)
53
57
  parser.add_argument(
54
58
  'consistency_group',
@@ -66,15 +70,15 @@ class AddVolumeToConsistencyGroup(command.Command):
66
70
  )
67
71
  return parser
68
72
 
69
- def take_action(self, parsed_args):
73
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
70
74
  volume_client = self.app.client_manager.volume
71
75
  result, add_uuid = _find_volumes(parsed_args.volumes, volume_client)
72
76
 
73
77
  if result > 0:
74
78
  total = len(parsed_args.volumes)
75
79
  LOG.error(
76
- _("%(result)s of %(total)s volumes failed to add.")
77
- % {'result': result, 'total': total}
80
+ _("%(result)s of %(total)s volumes failed to add."),
81
+ {'result': result, 'total': total},
78
82
  )
79
83
 
80
84
  if add_uuid:
@@ -90,7 +94,7 @@ class AddVolumeToConsistencyGroup(command.Command):
90
94
  class CreateConsistencyGroup(command.ShowOne):
91
95
  _description = _("Create new consistency group.")
92
96
 
93
- def get_parser(self, prog_name):
97
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
94
98
  parser = super().get_parser(prog_name)
95
99
  parser.add_argument(
96
100
  "name",
@@ -144,7 +148,9 @@ class CreateConsistencyGroup(command.ShowOne):
144
148
  )
145
149
  return parser
146
150
 
147
- def take_action(self, parsed_args):
151
+ def take_action(
152
+ self, parsed_args: argparse.Namespace
153
+ ) -> tuple[Sequence[str], Iterable[Any]]:
148
154
  volume_client = self.app.client_manager.volume
149
155
  if parsed_args.volume_type:
150
156
  volume_type_id = utils.find_resource(
@@ -186,13 +192,14 @@ class CreateConsistencyGroup(command.ShowOne):
186
192
  )
187
193
  )
188
194
 
189
- return zip(*sorted(consistency_group._info.items()))
195
+ col_headers, col_data = zip(*sorted(consistency_group._info.items()))
196
+ return col_headers, col_data
190
197
 
191
198
 
192
199
  class DeleteConsistencyGroup(command.Command):
193
200
  _description = _("Delete consistency group(s).")
194
201
 
195
- def get_parser(self, prog_name):
202
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
196
203
  parser = super().get_parser(prog_name)
197
204
  parser.add_argument(
198
205
  'consistency_groups',
@@ -208,7 +215,7 @@ class DeleteConsistencyGroup(command.Command):
208
215
  )
209
216
  return parser
210
217
 
211
- def take_action(self, parsed_args):
218
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
212
219
  volume_client = self.app.client_manager.volume
213
220
  result = 0
214
221
 
@@ -226,8 +233,8 @@ class DeleteConsistencyGroup(command.Command):
226
233
  _(
227
234
  "Failed to delete consistency group with "
228
235
  "name or ID '%(consistency_group)s':%(e)s"
229
- )
230
- % {'consistency_group': i, 'e': e}
236
+ ),
237
+ {'consistency_group': i, 'e': e},
231
238
  )
232
239
 
233
240
  if result > 0:
@@ -241,7 +248,7 @@ class DeleteConsistencyGroup(command.Command):
241
248
  class ListConsistencyGroup(command.Lister):
242
249
  _description = _("List consistency groups.")
243
250
 
244
- def get_parser(self, prog_name):
251
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
245
252
  parser = super().get_parser(prog_name)
246
253
  parser.add_argument(
247
254
  '--all-projects',
@@ -258,7 +265,9 @@ class ListConsistencyGroup(command.Lister):
258
265
  )
259
266
  return parser
260
267
 
261
- def take_action(self, parsed_args):
268
+ def take_action(
269
+ self, parsed_args: argparse.Namespace
270
+ ) -> tuple[Sequence[str], Iterable[tuple[Any, ...]]]:
262
271
  if parsed_args.long:
263
272
  columns = [
264
273
  'ID',
@@ -292,7 +301,7 @@ class ListConsistencyGroup(command.Lister):
292
301
  class RemoveVolumeFromConsistencyGroup(command.Command):
293
302
  _description = _("Remove volume(s) from consistency group")
294
303
 
295
- def get_parser(self, prog_name):
304
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
296
305
  parser = super().get_parser(prog_name)
297
306
  parser.add_argument(
298
307
  'consistency_group',
@@ -310,15 +319,15 @@ class RemoveVolumeFromConsistencyGroup(command.Command):
310
319
  )
311
320
  return parser
312
321
 
313
- def take_action(self, parsed_args):
322
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
314
323
  volume_client = self.app.client_manager.volume
315
324
  result, remove_uuid = _find_volumes(parsed_args.volumes, volume_client)
316
325
 
317
326
  if result > 0:
318
327
  total = len(parsed_args.volumes)
319
328
  LOG.error(
320
- _("%(result)s of %(total)s volumes failed to remove.")
321
- % {'result': result, 'total': total}
329
+ _("%(result)s of %(total)s volumes failed to remove."),
330
+ {'result': result, 'total': total},
322
331
  )
323
332
 
324
333
  if remove_uuid:
@@ -334,7 +343,7 @@ class RemoveVolumeFromConsistencyGroup(command.Command):
334
343
  class SetConsistencyGroup(command.Command):
335
344
  _description = _("Set consistency group properties")
336
345
 
337
- def get_parser(self, prog_name):
346
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
338
347
  parser = super().get_parser(prog_name)
339
348
  parser.add_argument(
340
349
  'consistency_group',
@@ -353,7 +362,7 @@ class SetConsistencyGroup(command.Command):
353
362
  )
354
363
  return parser
355
364
 
356
- def take_action(self, parsed_args):
365
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
357
366
  volume_client = self.app.client_manager.volume
358
367
  kwargs = {}
359
368
  if parsed_args.name:
@@ -372,7 +381,7 @@ class SetConsistencyGroup(command.Command):
372
381
  class ShowConsistencyGroup(command.ShowOne):
373
382
  _description = _("Display consistency group details.")
374
383
 
375
- def get_parser(self, prog_name):
384
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
376
385
  parser = super().get_parser(prog_name)
377
386
  parser.add_argument(
378
387
  "consistency_group",
@@ -381,9 +390,12 @@ class ShowConsistencyGroup(command.ShowOne):
381
390
  )
382
391
  return parser
383
392
 
384
- def take_action(self, parsed_args):
393
+ def take_action(
394
+ self, parsed_args: argparse.Namespace
395
+ ) -> tuple[Sequence[str], Iterable[Any]]:
385
396
  volume_client = self.app.client_manager.volume
386
397
  consistency_group = utils.find_resource(
387
398
  volume_client.consistencygroups, parsed_args.consistency_group
388
399
  )
389
- return zip(*sorted(consistency_group._info.items()))
400
+ col_headers, col_data = zip(*sorted(consistency_group._info.items()))
401
+ return col_headers, col_data