python-openstackclient 6.6.1__py3-none-any.whl → 7.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 (337) hide show
  1. openstackclient/api/api.py +4 -4
  2. openstackclient/api/compute_v2.py +352 -638
  3. openstackclient/api/image_v1.py +1 -1
  4. openstackclient/api/object_store_v1.py +3 -4
  5. openstackclient/common/availability_zone.py +1 -1
  6. openstackclient/common/clientmanager.py +16 -4
  7. openstackclient/common/configuration.py +1 -1
  8. openstackclient/common/extension.py +1 -1
  9. openstackclient/common/limits.py +66 -32
  10. openstackclient/common/module.py +3 -3
  11. openstackclient/common/progressbar.py +2 -2
  12. openstackclient/common/project_cleanup.py +5 -2
  13. openstackclient/common/quota.py +281 -410
  14. openstackclient/common/versions.py +1 -1
  15. openstackclient/compute/client.py +7 -116
  16. openstackclient/compute/v2/agent.py +75 -49
  17. openstackclient/compute/v2/aggregate.py +9 -9
  18. openstackclient/compute/v2/console.py +2 -2
  19. openstackclient/compute/v2/flavor.py +6 -6
  20. openstackclient/compute/v2/host.py +38 -33
  21. openstackclient/compute/v2/hypervisor.py +4 -3
  22. openstackclient/compute/v2/keypair.py +7 -8
  23. openstackclient/compute/v2/server.py +478 -396
  24. openstackclient/compute/v2/server_backup.py +1 -1
  25. openstackclient/compute/v2/server_group.py +4 -4
  26. openstackclient/compute/v2/server_image.py +1 -1
  27. openstackclient/compute/v2/server_migration.py +3 -4
  28. openstackclient/compute/v2/service.py +4 -4
  29. openstackclient/compute/v2/usage.py +3 -3
  30. openstackclient/identity/common.py +34 -0
  31. openstackclient/identity/v2_0/catalog.py +2 -2
  32. openstackclient/identity/v2_0/ec2creds.py +4 -4
  33. openstackclient/identity/v2_0/endpoint.py +4 -4
  34. openstackclient/identity/v2_0/project.py +6 -6
  35. openstackclient/identity/v2_0/role.py +5 -5
  36. openstackclient/identity/v2_0/role_assignment.py +1 -1
  37. openstackclient/identity/v2_0/service.py +4 -4
  38. openstackclient/identity/v2_0/token.py +2 -2
  39. openstackclient/identity/v2_0/user.py +7 -7
  40. openstackclient/identity/v3/access_rule.py +3 -3
  41. openstackclient/identity/v3/application_credential.py +127 -45
  42. openstackclient/identity/v3/catalog.py +2 -2
  43. openstackclient/identity/v3/consumer.py +4 -4
  44. openstackclient/identity/v3/credential.py +5 -5
  45. openstackclient/identity/v3/domain.py +5 -5
  46. openstackclient/identity/v3/ec2creds.py +4 -4
  47. openstackclient/identity/v3/endpoint.py +7 -7
  48. openstackclient/identity/v3/endpoint_group.py +8 -10
  49. openstackclient/identity/v3/federation_protocol.py +5 -5
  50. openstackclient/identity/v3/group.py +8 -8
  51. openstackclient/identity/v3/identity_provider.py +5 -5
  52. openstackclient/identity/v3/implied_role.py +3 -3
  53. openstackclient/identity/v3/limit.py +5 -5
  54. openstackclient/identity/v3/mapping.py +5 -5
  55. openstackclient/identity/v3/policy.py +5 -5
  56. openstackclient/identity/v3/project.py +5 -5
  57. openstackclient/identity/v3/region.py +5 -5
  58. openstackclient/identity/v3/registered_limit.py +5 -5
  59. openstackclient/identity/v3/role.py +7 -7
  60. openstackclient/identity/v3/role_assignment.py +92 -140
  61. openstackclient/identity/v3/service.py +64 -34
  62. openstackclient/identity/v3/service_provider.py +4 -4
  63. openstackclient/identity/v3/tag.py +2 -2
  64. openstackclient/identity/v3/token.py +5 -5
  65. openstackclient/identity/v3/trust.py +3 -3
  66. openstackclient/identity/v3/user.py +144 -80
  67. openstackclient/image/client.py +4 -4
  68. openstackclient/image/v1/image.py +8 -9
  69. openstackclient/image/v2/cache.py +12 -10
  70. openstackclient/image/v2/metadef_objects.py +44 -0
  71. openstackclient/image/v2/metadef_resource_type_association.py +189 -0
  72. openstackclient/image/v2/task.py +1 -1
  73. openstackclient/network/common.py +6 -5
  74. openstackclient/network/utils.py +2 -2
  75. openstackclient/network/v2/address_group.py +6 -6
  76. openstackclient/network/v2/address_scope.py +5 -5
  77. openstackclient/network/v2/default_security_group_rule.py +1 -1
  78. openstackclient/network/v2/floating_ip.py +8 -10
  79. openstackclient/network/v2/floating_ip_pool.py +6 -15
  80. openstackclient/network/v2/floating_ip_port_forwarding.py +5 -13
  81. openstackclient/network/v2/ip_availability.py +2 -2
  82. openstackclient/network/v2/l3_conntrack_helper.py +5 -5
  83. openstackclient/network/v2/network.py +8 -8
  84. openstackclient/network/v2/network_agent.py +8 -8
  85. openstackclient/network/v2/network_auto_allocated_topology.py +2 -2
  86. openstackclient/network/v2/network_flavor.py +6 -8
  87. openstackclient/network/v2/network_flavor_profile.py +4 -4
  88. openstackclient/network/v2/network_meter.py +3 -3
  89. openstackclient/network/v2/network_meter_rule.py +3 -3
  90. openstackclient/network/v2/network_qos_policy.py +5 -5
  91. openstackclient/network/v2/network_qos_rule.py +9 -9
  92. openstackclient/network/v2/network_qos_rule_type.py +1 -1
  93. openstackclient/network/v2/network_rbac.py +5 -5
  94. openstackclient/network/v2/network_segment.py +5 -5
  95. openstackclient/network/v2/network_segment_range.py +7 -7
  96. openstackclient/network/v2/network_trunk.py +7 -7
  97. openstackclient/network/v2/port.py +26 -12
  98. openstackclient/network/v2/router.py +403 -54
  99. openstackclient/network/v2/security_group.py +18 -14
  100. openstackclient/network/v2/security_group_rule.py +18 -15
  101. openstackclient/network/v2/subnet.py +15 -8
  102. openstackclient/network/v2/subnet_pool.py +6 -6
  103. openstackclient/object/v1/account.py +2 -2
  104. openstackclient/object/v1/container.py +7 -7
  105. openstackclient/object/v1/object.py +7 -7
  106. openstackclient/shell.py +4 -6
  107. openstackclient/tests/functional/base.py +1 -1
  108. openstackclient/tests/functional/common/test_extension.py +1 -1
  109. openstackclient/tests/functional/common/test_help.py +2 -2
  110. openstackclient/tests/functional/common/test_module.py +1 -1
  111. openstackclient/tests/functional/common/test_quota.py +43 -61
  112. openstackclient/tests/functional/compute/v2/common.py +2 -2
  113. openstackclient/tests/functional/compute/v2/test_flavor.py +2 -2
  114. openstackclient/tests/functional/compute/v2/test_keypair.py +1 -1
  115. openstackclient/tests/functional/compute/v2/test_server.py +5 -5
  116. openstackclient/tests/functional/compute/v2/test_server_event.py +1 -1
  117. openstackclient/tests/functional/identity/v2/common.py +3 -3
  118. openstackclient/tests/functional/identity/v3/common.py +14 -6
  119. openstackclient/tests/functional/identity/v3/test_application_credential.py +13 -19
  120. openstackclient/tests/functional/identity/v3/test_domain.py +1 -3
  121. openstackclient/tests/functional/identity/v3/test_endpoint.py +1 -1
  122. openstackclient/tests/functional/identity/v3/test_idp.py +1 -1
  123. openstackclient/tests/functional/identity/v3/test_limit.py +2 -2
  124. openstackclient/tests/functional/identity/v3/test_region.py +1 -3
  125. openstackclient/tests/functional/identity/v3/test_registered_limit.py +1 -1
  126. openstackclient/tests/functional/identity/v3/test_role.py +2 -2
  127. openstackclient/tests/functional/identity/v3/test_role_assignment.py +210 -0
  128. openstackclient/tests/functional/identity/v3/test_service.py +4 -6
  129. openstackclient/tests/functional/identity/v3/test_service_provider.py +1 -3
  130. openstackclient/tests/functional/image/base.py +1 -1
  131. openstackclient/tests/functional/image/v2/test_image.py +1 -1
  132. openstackclient/tests/functional/image/v2/test_info.py +1 -1
  133. openstackclient/tests/functional/network/v2/common.py +4 -6
  134. openstackclient/tests/functional/network/v2/test_network.py +5 -3
  135. openstackclient/tests/functional/network/v2/test_network_agent.py +7 -5
  136. openstackclient/tests/functional/network/v2/test_network_qos_rule.py +4 -4
  137. openstackclient/tests/functional/network/v2/test_port.py +11 -7
  138. openstackclient/tests/functional/network/v2/test_router.py +2 -2
  139. openstackclient/tests/functional/object/v1/common.py +1 -1
  140. openstackclient/tests/functional/object/v1/test_container.py +3 -3
  141. openstackclient/tests/functional/object/v1/test_object.py +9 -13
  142. openstackclient/tests/functional/volume/base.py +1 -1
  143. openstackclient/tests/functional/volume/v1/test_service.py +1 -1
  144. openstackclient/tests/functional/volume/v1/test_snapshot.py +2 -2
  145. openstackclient/tests/functional/volume/v1/test_transfer_request.py +2 -2
  146. openstackclient/tests/functional/volume/v1/test_volume_type.py +1 -1
  147. openstackclient/tests/functional/volume/v2/test_service.py +2 -2
  148. openstackclient/tests/functional/volume/v2/test_volume_backup.py +2 -2
  149. openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +2 -2
  150. openstackclient/tests/functional/volume/v2/test_volume_type.py +1 -1
  151. openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +2 -2
  152. openstackclient/tests/functional/volume/v3/test_volume_type.py +1 -1
  153. openstackclient/tests/unit/api/fakes.py +1 -1
  154. openstackclient/tests/unit/api/test_api.py +2 -2
  155. openstackclient/tests/unit/api/test_compute_v2.py +522 -707
  156. openstackclient/tests/unit/api/test_image_v1.py +1 -1
  157. openstackclient/tests/unit/api/test_image_v2.py +1 -1
  158. openstackclient/tests/unit/api/test_object_store_v1.py +4 -4
  159. openstackclient/tests/unit/common/test_limits.py +73 -35
  160. openstackclient/tests/unit/common/test_logs.py +2 -2
  161. openstackclient/tests/unit/common/test_module.py +4 -2
  162. openstackclient/tests/unit/common/test_project_cleanup.py +31 -6
  163. openstackclient/tests/unit/common/test_quota.py +490 -630
  164. openstackclient/tests/unit/compute/v2/fakes.py +37 -286
  165. openstackclient/tests/unit/compute/v2/test_agent.py +189 -147
  166. openstackclient/tests/unit/compute/v2/test_aggregate.py +18 -16
  167. openstackclient/tests/unit/compute/v2/test_console.py +4 -5
  168. openstackclient/tests/unit/compute/v2/test_flavor.py +59 -68
  169. openstackclient/tests/unit/compute/v2/test_host.py +83 -54
  170. openstackclient/tests/unit/compute/v2/test_hypervisor.py +28 -31
  171. openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +2 -2
  172. openstackclient/tests/unit/compute/v2/test_keypair.py +65 -50
  173. openstackclient/tests/unit/compute/v2/test_server.py +2895 -2459
  174. openstackclient/tests/unit/compute/v2/test_server_backup.py +1 -1
  175. openstackclient/tests/unit/compute/v2/test_server_event.py +14 -39
  176. openstackclient/tests/unit/compute/v2/test_server_group.py +28 -29
  177. openstackclient/tests/unit/compute/v2/test_server_migration.py +43 -68
  178. openstackclient/tests/unit/compute/v2/test_server_volume.py +17 -34
  179. openstackclient/tests/unit/compute/v2/test_service.py +34 -52
  180. openstackclient/tests/unit/compute/v2/test_usage.py +4 -4
  181. openstackclient/tests/unit/fakes.py +11 -11
  182. openstackclient/tests/unit/identity/v2_0/fakes.py +27 -10
  183. openstackclient/tests/unit/identity/v2_0/test_catalog.py +3 -3
  184. openstackclient/tests/unit/identity/v2_0/test_endpoint.py +7 -7
  185. openstackclient/tests/unit/identity/v2_0/test_project.py +8 -8
  186. openstackclient/tests/unit/identity/v2_0/test_role.py +10 -10
  187. openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +4 -4
  188. openstackclient/tests/unit/identity/v2_0/test_service.py +6 -6
  189. openstackclient/tests/unit/identity/v2_0/test_token.py +4 -4
  190. openstackclient/tests/unit/identity/v2_0/test_user.py +8 -8
  191. openstackclient/tests/unit/identity/v3/fakes.py +59 -20
  192. openstackclient/tests/unit/identity/v3/test_access_rule.py +5 -5
  193. openstackclient/tests/unit/identity/v3/test_application_credential.py +212 -235
  194. openstackclient/tests/unit/identity/v3/test_catalog.py +3 -3
  195. openstackclient/tests/unit/identity/v3/test_consumer.py +7 -8
  196. openstackclient/tests/unit/identity/v3/test_credential.py +9 -9
  197. openstackclient/tests/unit/identity/v3/test_domain.py +8 -8
  198. openstackclient/tests/unit/identity/v3/test_endpoint.py +13 -13
  199. openstackclient/tests/unit/identity/v3/test_endpoint_group.py +12 -14
  200. openstackclient/tests/unit/identity/v3/test_group.py +12 -12
  201. openstackclient/tests/unit/identity/v3/test_identity_provider.py +8 -8
  202. openstackclient/tests/unit/identity/v3/test_implied_role.py +5 -5
  203. openstackclient/tests/unit/identity/v3/test_limit.py +7 -7
  204. openstackclient/tests/unit/identity/v3/test_mappings.py +7 -7
  205. openstackclient/tests/unit/identity/v3/test_oauth.py +5 -5
  206. openstackclient/tests/unit/identity/v3/test_project.py +16 -16
  207. openstackclient/tests/unit/identity/v3/test_protocol.py +7 -7
  208. openstackclient/tests/unit/identity/v3/test_region.py +7 -7
  209. openstackclient/tests/unit/identity/v3/test_registered_limit.py +12 -13
  210. openstackclient/tests/unit/identity/v3/test_role.py +13 -13
  211. openstackclient/tests/unit/identity/v3/test_role_assignment.py +410 -331
  212. openstackclient/tests/unit/identity/v3/test_service.py +93 -97
  213. openstackclient/tests/unit/identity/v3/test_service_provider.py +7 -7
  214. openstackclient/tests/unit/identity/v3/test_token.py +4 -4
  215. openstackclient/tests/unit/identity/v3/test_trust.py +9 -9
  216. openstackclient/tests/unit/identity/v3/test_unscoped_saml.py +4 -4
  217. openstackclient/tests/unit/identity/v3/test_user.py +299 -327
  218. openstackclient/tests/unit/image/v1/test_image.py +6 -6
  219. openstackclient/tests/unit/image/v2/fakes.py +46 -9
  220. openstackclient/tests/unit/image/v2/test_cache.py +2 -2
  221. openstackclient/tests/unit/image/v2/test_image.py +3 -3
  222. openstackclient/tests/unit/image/v2/test_metadef_objects.py +62 -0
  223. openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py +131 -0
  224. openstackclient/tests/unit/integ/base.py +1 -1
  225. openstackclient/tests/unit/integ/cli/test_project.py +4 -4
  226. openstackclient/tests/unit/integ/cli/test_shell.py +7 -7
  227. openstackclient/tests/unit/network/test_common.py +12 -21
  228. openstackclient/tests/unit/network/v2/fakes.py +64 -130
  229. openstackclient/tests/unit/network/v2/test_address_group.py +15 -15
  230. openstackclient/tests/unit/network/v2/test_address_scope.py +13 -13
  231. openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +49 -27
  232. openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +40 -38
  233. openstackclient/tests/unit/network/v2/test_floating_ip_network.py +15 -15
  234. openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +4 -7
  235. openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +3 -5
  236. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +11 -11
  237. openstackclient/tests/unit/network/v2/test_ip_availability.py +6 -6
  238. openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +11 -21
  239. openstackclient/tests/unit/network/v2/test_local_ip.py +7 -7
  240. openstackclient/tests/unit/network/v2/test_local_ip_association.py +3 -5
  241. openstackclient/tests/unit/network/v2/test_ndp_proxy.py +13 -13
  242. openstackclient/tests/unit/network/v2/test_network.py +23 -28
  243. openstackclient/tests/unit/network/v2/test_network_agent.py +17 -21
  244. openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +8 -8
  245. openstackclient/tests/unit/network/v2/test_network_compute.py +66 -65
  246. openstackclient/tests/unit/network/v2/test_network_flavor.py +17 -19
  247. openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +13 -13
  248. openstackclient/tests/unit/network/v2/test_network_meter.py +11 -11
  249. openstackclient/tests/unit/network/v2/test_network_meter_rule.py +11 -11
  250. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +11 -21
  251. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +51 -77
  252. openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +5 -9
  253. openstackclient/tests/unit/network/v2/test_network_rbac.py +12 -12
  254. openstackclient/tests/unit/network/v2/test_network_segment.py +11 -15
  255. openstackclient/tests/unit/network/v2/test_network_segment_range.py +11 -13
  256. openstackclient/tests/unit/network/v2/test_network_service_provider.py +3 -5
  257. openstackclient/tests/unit/network/v2/test_network_trunk.py +11 -11
  258. openstackclient/tests/unit/network/v2/test_port.py +22 -25
  259. openstackclient/tests/unit/network/v2/test_router.py +721 -51
  260. openstackclient/tests/unit/network/v2/test_security_group_compute.py +65 -49
  261. openstackclient/tests/unit/network/v2/test_security_group_network.py +15 -15
  262. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +57 -45
  263. openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +11 -19
  264. openstackclient/tests/unit/network/v2/test_subnet.py +29 -25
  265. openstackclient/tests/unit/network/v2/test_subnet_pool.py +15 -15
  266. openstackclient/tests/unit/object/v1/fakes.py +1 -1
  267. openstackclient/tests/unit/object/v1/test_container.py +5 -5
  268. openstackclient/tests/unit/object/v1/test_container_all.py +6 -6
  269. openstackclient/tests/unit/object/v1/test_object.py +3 -3
  270. openstackclient/tests/unit/object/v1/test_object_all.py +5 -5
  271. openstackclient/tests/unit/test_shell.py +5 -5
  272. openstackclient/tests/unit/utils.py +4 -1
  273. openstackclient/tests/unit/volume/test_find_resource.py +2 -2
  274. openstackclient/tests/unit/volume/v1/fakes.py +5 -6
  275. openstackclient/tests/unit/volume/v1/test_volume.py +5 -4
  276. openstackclient/tests/unit/volume/v2/fakes.py +39 -259
  277. openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py +5 -5
  278. openstackclient/tests/unit/volume/v2/test_qos_specs.py +9 -9
  279. openstackclient/tests/unit/volume/v2/test_volume.py +21 -87
  280. openstackclient/tests/unit/volume/v2/test_volume_backup.py +7 -368
  281. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +1 -1
  282. openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +0 -44
  283. openstackclient/tests/unit/volume/v2/test_volume_type.py +6 -87
  284. openstackclient/tests/unit/volume/v3/fakes.py +505 -22
  285. openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py +2 -3
  286. openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py +10 -11
  287. openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +10 -6
  288. openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +25 -17
  289. openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +6 -32
  290. openstackclient/tests/unit/volume/v3/test_service.py +271 -0
  291. openstackclient/tests/unit/volume/v3/test_volume.py +2177 -33
  292. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +48 -52
  293. openstackclient/tests/unit/volume/v3/test_volume_backup.py +892 -0
  294. openstackclient/tests/unit/volume/v3/test_volume_group.py +19 -20
  295. openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +14 -34
  296. openstackclient/tests/unit/volume/v3/test_volume_group_type.py +13 -16
  297. openstackclient/tests/unit/volume/v3/test_volume_message.py +10 -11
  298. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +161 -0
  299. openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py +425 -0
  300. openstackclient/tests/unit/volume/v3/test_volume_type.py +1109 -0
  301. openstackclient/volume/v1/qos_specs.py +7 -7
  302. openstackclient/volume/v1/service.py +2 -2
  303. openstackclient/volume/v1/volume.py +12 -12
  304. openstackclient/volume/v1/volume_backup.py +7 -7
  305. openstackclient/volume/v1/volume_snapshot.py +8 -8
  306. openstackclient/volume/v1/volume_transfer_request.py +5 -5
  307. openstackclient/volume/v1/volume_type.py +7 -7
  308. openstackclient/volume/v2/backup_record.py +2 -2
  309. openstackclient/volume/v2/consistency_group.py +7 -9
  310. openstackclient/volume/v2/consistency_group_snapshot.py +4 -12
  311. openstackclient/volume/v2/qos_specs.py +7 -7
  312. openstackclient/volume/v2/service.py +2 -2
  313. openstackclient/volume/v2/volume.py +80 -80
  314. openstackclient/volume/v2/volume_backend.py +2 -2
  315. openstackclient/volume/v2/volume_backup.py +7 -217
  316. openstackclient/volume/v2/volume_host.py +2 -2
  317. openstackclient/volume/v2/volume_snapshot.py +8 -8
  318. openstackclient/volume/v2/volume_transfer_request.py +5 -37
  319. openstackclient/volume/v2/volume_type.py +7 -89
  320. openstackclient/volume/v3/service.py +56 -0
  321. openstackclient/volume/v3/volume.py +971 -0
  322. openstackclient/volume/v3/volume_attachment.py +31 -29
  323. openstackclient/volume/v3/volume_backup.py +670 -0
  324. openstackclient/volume/v3/volume_message.py +1 -1
  325. openstackclient/volume/v3/volume_snapshot.py +97 -0
  326. openstackclient/volume/v3/volume_transfer_request.py +233 -0
  327. openstackclient/volume/v3/volume_type.py +967 -0
  328. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/AUTHORS +4 -0
  329. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/METADATA +2 -3
  330. python_openstackclient-7.0.0.dist-info/RECORD +502 -0
  331. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/entry_points.txt +33 -27
  332. python_openstackclient-7.0.0.dist-info/pbr.json +1 -0
  333. python_openstackclient-6.6.1.dist-info/RECORD +0 -489
  334. python_openstackclient-6.6.1.dist-info/pbr.json +0 -1
  335. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/LICENSE +0 -0
  336. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/WHEEL +0 -0
  337. {python_openstackclient-6.6.1.dist-info → python_openstackclient-7.0.0.dist-info}/top_level.txt +0 -0
@@ -14,20 +14,991 @@
14
14
 
15
15
  """Volume V3 Volume action implementations"""
16
16
 
17
+ import argparse
18
+ import copy
19
+ import functools
17
20
  import logging
18
21
 
22
+ from cliff import columns as cliff_columns
23
+ from openstack import exceptions as sdk_exceptions
19
24
  from openstack import utils as sdk_utils
20
25
  from osc_lib.cli import format_columns
26
+ from osc_lib.cli import parseractions
21
27
  from osc_lib.command import command
22
28
  from osc_lib import exceptions
23
29
  from osc_lib import utils
24
30
 
31
+ from openstackclient.common import pagination
25
32
  from openstackclient.i18n import _
33
+ from openstackclient.identity import common as identity_common
34
+ from openstackclient.volume.v2 import volume as volume_v2
26
35
 
27
36
 
28
37
  LOG = logging.getLogger(__name__)
29
38
 
30
39
 
40
+ class KeyValueHintAction(argparse.Action):
41
+ """Uses KeyValueAction or KeyValueAppendAction based on the given key"""
42
+
43
+ APPEND_KEYS = ('same_host', 'different_host')
44
+
45
+ def __init__(self, *args, **kwargs):
46
+ self._key_value_action = parseractions.KeyValueAction(*args, **kwargs)
47
+ self._key_value_append_action = parseractions.KeyValueAppendAction(
48
+ *args, **kwargs
49
+ )
50
+ super().__init__(*args, **kwargs)
51
+
52
+ def __call__(self, parser, namespace, values, option_string=None):
53
+ if values.startswith(self.APPEND_KEYS):
54
+ self._key_value_append_action(
55
+ parser, namespace, values, option_string=option_string
56
+ )
57
+ else:
58
+ self._key_value_action(
59
+ parser, namespace, values, option_string=option_string
60
+ )
61
+
62
+
63
+ class AttachmentsColumn(cliff_columns.FormattableColumn):
64
+ """Formattable column for attachments column.
65
+
66
+ Unlike the parent FormattableColumn class, the initializer of the
67
+ class takes server_cache as the second argument.
68
+ osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
69
+ object with a single parameter "column value", so you need to pass
70
+ a partially initialized class like
71
+ ``functools.partial(AttachmentsColumn, server_cache)``.
72
+ """
73
+
74
+ def __init__(self, value, server_cache=None):
75
+ super().__init__(value)
76
+ self._server_cache = server_cache or {}
77
+
78
+ def human_readable(self):
79
+ """Return a formatted string of a volume's attached instances
80
+
81
+ :rtype: a string of formatted instances
82
+ """
83
+
84
+ msg = ''
85
+ for attachment in self._value:
86
+ server = attachment['server_id']
87
+ if server in self._server_cache.keys():
88
+ server = self._server_cache[server].name
89
+ device = attachment['device']
90
+ msg += f'Attached to {server} on {device} '
91
+ return msg
92
+
93
+
94
+ class CreateVolume(volume_v2.CreateVolume):
95
+ _description = _("Create new volume")
96
+
97
+ @staticmethod
98
+ def _check_size_arg(args):
99
+ """Check whether --size option is required or not.
100
+
101
+ Require size parameter in case if any of the following is not
102
+ specified:
103
+
104
+ * snapshot
105
+ * source volume
106
+ * backup
107
+ * remote source (volume to be managed)
108
+ """
109
+
110
+ if (
111
+ args.snapshot or args.source or args.backup or args.remote_source
112
+ ) is None and args.size is None:
113
+ msg = _(
114
+ "--size is a required option if none of --snapshot, "
115
+ "--backup, --source, or --remote-source are provided."
116
+ )
117
+ raise exceptions.CommandError(msg)
118
+
119
+ def get_parser(self, prog_name):
120
+ parser, source_group = self._get_parser(prog_name)
121
+
122
+ source_group.add_argument(
123
+ "--backup",
124
+ metavar="<backup>",
125
+ help=_(
126
+ "Restore backup to a volume (name or ID) "
127
+ "(supported by --os-volume-api-version 3.47 or later)"
128
+ ),
129
+ )
130
+ source_group.add_argument(
131
+ "--remote-source",
132
+ metavar="<key=value>",
133
+ action=parseractions.KeyValueAction,
134
+ help=_(
135
+ "The attribute(s) of the existing remote volume "
136
+ "(admin required) (repeat option to specify multiple "
137
+ "attributes, e.g.: '--remote-source source-name=test_name "
138
+ "--remote-source source-id=test_id')"
139
+ ),
140
+ )
141
+ parser.add_argument(
142
+ "--host",
143
+ metavar="<host>",
144
+ help=_(
145
+ "Cinder host on which the existing volume resides; "
146
+ "takes the form: host@backend-name#pool. This is only "
147
+ "used along with the --remote-source option."
148
+ ),
149
+ )
150
+ parser.add_argument(
151
+ "--cluster",
152
+ metavar="<cluster>",
153
+ help=_(
154
+ "Cinder cluster on which the existing volume resides; "
155
+ "takes the form: cluster@backend-name#pool. This is only "
156
+ "used along with the --remote-source option. "
157
+ "(supported by --os-volume-api-version 3.16 or above)",
158
+ ),
159
+ )
160
+ return parser
161
+
162
+ def take_action(self, parsed_args):
163
+ CreateVolume._check_size_arg(parsed_args)
164
+ # size is validated in the above call to
165
+ # _check_size_arg where we check that size
166
+ # should be passed if we are not creating a
167
+ # volume from snapshot, backup or source volume
168
+ size = parsed_args.size
169
+
170
+ volume_client_sdk = self.app.client_manager.sdk_connection.volume
171
+ volume_client = self.app.client_manager.volume
172
+ image_client = self.app.client_manager.image
173
+
174
+ if (
175
+ parsed_args.host or parsed_args.cluster
176
+ ) and not parsed_args.remote_source:
177
+ msg = _(
178
+ "The --host and --cluster options are only supported "
179
+ "with --remote-source parameter."
180
+ )
181
+ raise exceptions.CommandError(msg)
182
+
183
+ if parsed_args.backup and not (
184
+ volume_client.api_version.matches('3.47')
185
+ ):
186
+ msg = _(
187
+ "--os-volume-api-version 3.47 or greater is required "
188
+ "to create a volume from backup."
189
+ )
190
+ raise exceptions.CommandError(msg)
191
+
192
+ if parsed_args.remote_source:
193
+ if (
194
+ parsed_args.size
195
+ or parsed_args.consistency_group
196
+ or parsed_args.hint
197
+ or parsed_args.read_only
198
+ or parsed_args.read_write
199
+ ):
200
+ msg = _(
201
+ "The --size, --consistency-group, --hint, --read-only "
202
+ "and --read-write options are not supported with the "
203
+ "--remote-source parameter."
204
+ )
205
+ raise exceptions.CommandError(msg)
206
+ if parsed_args.cluster:
207
+ if not sdk_utils.supports_microversion(
208
+ volume_client_sdk, '3.16'
209
+ ):
210
+ msg = _(
211
+ "--os-volume-api-version 3.16 or greater is required "
212
+ "to support the cluster parameter."
213
+ )
214
+ raise exceptions.CommandError(msg)
215
+ if parsed_args.cluster and parsed_args.host:
216
+ msg = _(
217
+ "Only one of --host or --cluster needs to be specified "
218
+ "to manage a volume."
219
+ )
220
+ raise exceptions.CommandError(msg)
221
+ if not parsed_args.cluster and not parsed_args.host:
222
+ msg = _(
223
+ "One of --host or --cluster needs to be specified to "
224
+ "manage a volume."
225
+ )
226
+ raise exceptions.CommandError(msg)
227
+ volume = volume_client_sdk.manage_volume(
228
+ host=parsed_args.host,
229
+ cluster=parsed_args.cluster,
230
+ ref=parsed_args.remote_source,
231
+ name=parsed_args.name,
232
+ description=parsed_args.description,
233
+ volume_type=parsed_args.type,
234
+ availability_zone=parsed_args.availability_zone,
235
+ metadata=parsed_args.property,
236
+ bootable=parsed_args.bootable,
237
+ )
238
+ return zip(*sorted(volume.items()))
239
+
240
+ source_volume = None
241
+ if parsed_args.source:
242
+ source_volume_obj = utils.find_resource(
243
+ volume_client.volumes, parsed_args.source
244
+ )
245
+ source_volume = source_volume_obj.id
246
+ size = max(size or 0, source_volume_obj.size)
247
+
248
+ consistency_group = None
249
+ if parsed_args.consistency_group:
250
+ consistency_group = utils.find_resource(
251
+ volume_client.consistencygroups, parsed_args.consistency_group
252
+ ).id
253
+
254
+ image = None
255
+ if parsed_args.image:
256
+ image = image_client.find_image(
257
+ parsed_args.image, ignore_missing=False
258
+ ).id
259
+
260
+ snapshot = None
261
+ if parsed_args.snapshot:
262
+ snapshot_obj = utils.find_resource(
263
+ volume_client.volume_snapshots, parsed_args.snapshot
264
+ )
265
+ snapshot = snapshot_obj.id
266
+ # Cinder requires a value for size when creating a volume
267
+ # even if creating from a snapshot. Cinder will create the
268
+ # volume with at least the same size as the snapshot anyway,
269
+ # so since we have the object here, just override the size
270
+ # value if it's either not given or is smaller than the
271
+ # snapshot size.
272
+ size = max(size or 0, snapshot_obj.size)
273
+
274
+ backup = None
275
+ if parsed_args.backup:
276
+ backup_obj = utils.find_resource(
277
+ volume_client.backups, parsed_args.backup
278
+ )
279
+ backup = backup_obj.id
280
+ # As above
281
+ size = max(size or 0, backup_obj.size)
282
+
283
+ volume = volume_client.volumes.create(
284
+ size=size,
285
+ snapshot_id=snapshot,
286
+ name=parsed_args.name,
287
+ description=parsed_args.description,
288
+ volume_type=parsed_args.type,
289
+ availability_zone=parsed_args.availability_zone,
290
+ metadata=parsed_args.property,
291
+ imageRef=image,
292
+ source_volid=source_volume,
293
+ consistencygroup_id=consistency_group,
294
+ scheduler_hints=parsed_args.hint,
295
+ backup_id=backup,
296
+ )
297
+
298
+ if parsed_args.bootable or parsed_args.non_bootable:
299
+ try:
300
+ if utils.wait_for_status(
301
+ volume_client.volumes.get,
302
+ volume.id,
303
+ success_status=['available'],
304
+ error_status=['error'],
305
+ sleep_time=1,
306
+ ):
307
+ volume_client.volumes.set_bootable(
308
+ volume.id, parsed_args.bootable
309
+ )
310
+ else:
311
+ msg = _(
312
+ "Volume status is not available for setting boot "
313
+ "state"
314
+ )
315
+ raise exceptions.CommandError(msg)
316
+ except Exception as e:
317
+ LOG.error(_("Failed to set volume bootable property: %s"), e)
318
+ if parsed_args.read_only or parsed_args.read_write:
319
+ try:
320
+ if utils.wait_for_status(
321
+ volume_client.volumes.get,
322
+ volume.id,
323
+ success_status=['available'],
324
+ error_status=['error'],
325
+ sleep_time=1,
326
+ ):
327
+ volume_client.volumes.update_readonly_flag(
328
+ volume.id, parsed_args.read_only
329
+ )
330
+ else:
331
+ msg = _(
332
+ "Volume status is not available for setting it"
333
+ "read only."
334
+ )
335
+ raise exceptions.CommandError(msg)
336
+ except Exception as e:
337
+ LOG.error(
338
+ _(
339
+ "Failed to set volume read-only access "
340
+ "mode flag: %s"
341
+ ),
342
+ e,
343
+ )
344
+
345
+ # Remove key links from being displayed
346
+ volume._info.update(
347
+ {
348
+ 'properties': format_columns.DictColumn(
349
+ volume._info.pop('metadata')
350
+ ),
351
+ 'type': volume._info.pop('volume_type'),
352
+ }
353
+ )
354
+ volume._info.pop("links", None)
355
+ return zip(*sorted(volume._info.items()))
356
+
357
+
358
+ class DeleteVolume(volume_v2.DeleteVolume):
359
+ _description = _("Delete volume(s)")
360
+
361
+ def get_parser(self, prog_name):
362
+ parser = super().get_parser(prog_name)
363
+ parser.add_argument(
364
+ '--remote',
365
+ action='store_true',
366
+ help=_("Specify this parameter to unmanage a volume."),
367
+ )
368
+ return parser
369
+
370
+ def take_action(self, parsed_args):
371
+ volume_client = self.app.client_manager.volume
372
+ volume_client_sdk = self.app.client_manager.sdk_connection.volume
373
+ result = 0
374
+
375
+ if parsed_args.remote and (parsed_args.force or parsed_args.purge):
376
+ msg = _(
377
+ "The --force and --purge options are not "
378
+ "supported with the --remote parameter."
379
+ )
380
+ raise exceptions.CommandError(msg)
381
+
382
+ for i in parsed_args.volumes:
383
+ try:
384
+ volume_obj = utils.find_resource(volume_client.volumes, i)
385
+ if parsed_args.remote:
386
+ volume_client_sdk.unmanage_volume(volume_obj.id)
387
+ elif parsed_args.force:
388
+ volume_client.volumes.force_delete(volume_obj.id)
389
+ else:
390
+ volume_client.volumes.delete(
391
+ volume_obj.id, cascade=parsed_args.purge
392
+ )
393
+ except Exception as e:
394
+ result += 1
395
+ LOG.error(
396
+ _(
397
+ "Failed to delete volume with "
398
+ "name or ID '%(volume)s': %(e)s"
399
+ ),
400
+ {'volume': i, 'e': e},
401
+ )
402
+
403
+ if result > 0:
404
+ total = len(parsed_args.volumes)
405
+ msg = _("%(result)s of %(total)s volumes failed " "to delete.") % {
406
+ 'result': result,
407
+ 'total': total,
408
+ }
409
+ raise exceptions.CommandError(msg)
410
+
411
+
412
+ class ListVolume(command.Lister):
413
+ _description = _("List volumes")
414
+
415
+ def get_parser(self, prog_name):
416
+ parser = super().get_parser(prog_name)
417
+ parser.add_argument(
418
+ '--project',
419
+ metavar='<project>',
420
+ help=_('Filter results by project (name or ID) (admin only)'),
421
+ )
422
+ identity_common.add_project_domain_option_to_parser(parser)
423
+ parser.add_argument(
424
+ '--user',
425
+ metavar='<user>',
426
+ help=_('Filter results by user (name or ID) (admin only)'),
427
+ )
428
+ identity_common.add_user_domain_option_to_parser(parser)
429
+ parser.add_argument(
430
+ '--name',
431
+ metavar='<name>',
432
+ help=_('Filter results by volume name'),
433
+ )
434
+ parser.add_argument(
435
+ '--status',
436
+ metavar='<status>',
437
+ help=_('Filter results by status'),
438
+ )
439
+ parser.add_argument(
440
+ '--all-projects',
441
+ action='store_true',
442
+ default=False,
443
+ help=_('Include all projects (admin only)'),
444
+ )
445
+ parser.add_argument(
446
+ '--long',
447
+ action='store_true',
448
+ default=False,
449
+ help=_('List additional fields in output'),
450
+ )
451
+ pagination.add_marker_pagination_option_to_parser(parser)
452
+ return parser
453
+
454
+ def take_action(self, parsed_args):
455
+ volume_client = self.app.client_manager.volume
456
+ identity_client = self.app.client_manager.identity
457
+
458
+ if parsed_args.long:
459
+ columns = [
460
+ 'ID',
461
+ 'Name',
462
+ 'Status',
463
+ 'Size',
464
+ 'Volume Type',
465
+ 'Bootable',
466
+ 'Attachments',
467
+ 'Metadata',
468
+ ]
469
+ column_headers = copy.deepcopy(columns)
470
+ column_headers[4] = 'Type'
471
+ column_headers[6] = 'Attached to'
472
+ column_headers[7] = 'Properties'
473
+ else:
474
+ columns = [
475
+ 'ID',
476
+ 'Name',
477
+ 'Status',
478
+ 'Size',
479
+ 'Attachments',
480
+ ]
481
+ column_headers = copy.deepcopy(columns)
482
+ column_headers[4] = 'Attached to'
483
+
484
+ project_id = None
485
+ if parsed_args.project:
486
+ project_id = identity_common.find_project(
487
+ identity_client,
488
+ parsed_args.project,
489
+ parsed_args.project_domain,
490
+ ).id
491
+
492
+ user_id = None
493
+ if parsed_args.user:
494
+ user_id = identity_common.find_user(
495
+ identity_client, parsed_args.user, parsed_args.user_domain
496
+ ).id
497
+
498
+ # set value of 'all_tenants' when using project option
499
+ all_projects = bool(parsed_args.project) or parsed_args.all_projects
500
+
501
+ search_opts = {
502
+ 'all_tenants': all_projects,
503
+ 'project_id': project_id,
504
+ 'user_id': user_id,
505
+ 'name': parsed_args.name,
506
+ 'status': parsed_args.status,
507
+ }
508
+
509
+ data = volume_client.volumes.list(
510
+ search_opts=search_opts,
511
+ marker=parsed_args.marker,
512
+ limit=parsed_args.limit,
513
+ )
514
+
515
+ do_server_list = False
516
+
517
+ for vol in data:
518
+ if vol.status == 'in-use':
519
+ do_server_list = True
520
+ break
521
+
522
+ # Cache the server list
523
+ server_cache = {}
524
+ if do_server_list:
525
+ try:
526
+ compute_client = self.app.client_manager.sdk_connection.compute
527
+ for s in compute_client.servers():
528
+ server_cache[s.id] = s
529
+ except sdk_exceptions.SDKException:
530
+ # Just forget it if there's any trouble
531
+ pass # nosec: B110
532
+ AttachmentsColumnWithCache = functools.partial(
533
+ AttachmentsColumn, server_cache=server_cache
534
+ )
535
+
536
+ column_headers = utils.backward_compat_col_lister(
537
+ column_headers, parsed_args.columns, {'Display Name': 'Name'}
538
+ )
539
+
540
+ return (
541
+ column_headers,
542
+ (
543
+ utils.get_item_properties(
544
+ s,
545
+ columns,
546
+ formatters={
547
+ 'Metadata': format_columns.DictColumn,
548
+ 'Attachments': AttachmentsColumnWithCache,
549
+ },
550
+ )
551
+ for s in data
552
+ ),
553
+ )
554
+
555
+
556
+ class MigrateVolume(command.Command):
557
+ _description = _("Migrate volume to a new host")
558
+
559
+ def get_parser(self, prog_name):
560
+ parser = super().get_parser(prog_name)
561
+ parser.add_argument(
562
+ 'volume',
563
+ metavar="<volume>",
564
+ help=_("Volume to migrate (name or ID)"),
565
+ )
566
+ parser.add_argument(
567
+ '--host',
568
+ metavar="<host>",
569
+ required=True,
570
+ help=_(
571
+ "Destination host (takes the form: host@backend-name#pool)"
572
+ ),
573
+ )
574
+ parser.add_argument(
575
+ '--force-host-copy',
576
+ action="store_true",
577
+ help=_(
578
+ "Enable generic host-based force-migration, "
579
+ "which bypasses driver optimizations"
580
+ ),
581
+ )
582
+ parser.add_argument(
583
+ '--lock-volume',
584
+ action="store_true",
585
+ help=_(
586
+ "If specified, the volume state will be locked "
587
+ "and will not allow a migration to be aborted "
588
+ "(possibly by another operation)"
589
+ ),
590
+ )
591
+ return parser
592
+
593
+ def take_action(self, parsed_args):
594
+ volume_client = self.app.client_manager.volume
595
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
596
+ volume_client.volumes.migrate_volume(
597
+ volume.id,
598
+ parsed_args.host,
599
+ parsed_args.force_host_copy,
600
+ parsed_args.lock_volume,
601
+ )
602
+
603
+
604
+ class SetVolume(command.Command):
605
+ _description = _("Set volume properties")
606
+
607
+ def get_parser(self, prog_name):
608
+ parser = super().get_parser(prog_name)
609
+ parser.add_argument(
610
+ 'volume',
611
+ metavar='<volume>',
612
+ help=_('Volume to modify (name or ID)'),
613
+ )
614
+ parser.add_argument(
615
+ '--name',
616
+ metavar='<name>',
617
+ help=_('New volume name'),
618
+ )
619
+ parser.add_argument(
620
+ '--size',
621
+ metavar='<size>',
622
+ type=int,
623
+ help=_('Extend volume size in GB'),
624
+ )
625
+ parser.add_argument(
626
+ '--description',
627
+ metavar='<description>',
628
+ help=_('New volume description'),
629
+ )
630
+ parser.add_argument(
631
+ "--no-property",
632
+ dest="no_property",
633
+ action="store_true",
634
+ help=_(
635
+ "Remove all properties from <volume> "
636
+ "(specify both --no-property and --property to "
637
+ "remove the current properties before setting "
638
+ "new properties.)"
639
+ ),
640
+ )
641
+ parser.add_argument(
642
+ '--property',
643
+ metavar='<key=value>',
644
+ action=parseractions.KeyValueAction,
645
+ help=_(
646
+ 'Set a property on this volume '
647
+ '(repeat option to set multiple properties)'
648
+ ),
649
+ )
650
+ parser.add_argument(
651
+ '--image-property',
652
+ metavar='<key=value>',
653
+ action=parseractions.KeyValueAction,
654
+ help=_(
655
+ 'Set an image property on this volume '
656
+ '(repeat option to set multiple image properties)'
657
+ ),
658
+ )
659
+ parser.add_argument(
660
+ "--state",
661
+ metavar="<state>",
662
+ choices=[
663
+ 'available',
664
+ 'error',
665
+ 'creating',
666
+ 'deleting',
667
+ 'in-use',
668
+ 'attaching',
669
+ 'detaching',
670
+ 'error_deleting',
671
+ 'maintenance',
672
+ ],
673
+ help=_(
674
+ 'New volume state ("available", "error", "creating", '
675
+ '"deleting", "in-use", "attaching", "detaching", '
676
+ '"error_deleting" or "maintenance") (admin only) '
677
+ '(This option simply changes the state of the volume '
678
+ 'in the database with no regard to actual status, '
679
+ 'exercise caution when using)'
680
+ ),
681
+ )
682
+ attached_group = parser.add_mutually_exclusive_group()
683
+ attached_group.add_argument(
684
+ "--attached",
685
+ action="store_true",
686
+ help=_(
687
+ 'Set volume attachment status to "attached" '
688
+ '(admin only) '
689
+ '(This option simply changes the state of the volume '
690
+ 'in the database with no regard to actual status, '
691
+ 'exercise caution when using)'
692
+ ),
693
+ )
694
+ attached_group.add_argument(
695
+ "--detached",
696
+ action="store_true",
697
+ help=_(
698
+ 'Set volume attachment status to "detached" '
699
+ '(admin only) '
700
+ '(This option simply changes the state of the volume '
701
+ 'in the database with no regard to actual status, '
702
+ 'exercise caution when using)'
703
+ ),
704
+ )
705
+ parser.add_argument(
706
+ '--type',
707
+ metavar='<volume-type>',
708
+ help=_('New volume type (name or ID)'),
709
+ )
710
+ parser.add_argument(
711
+ '--retype-policy',
712
+ metavar='<retype-policy>',
713
+ choices=['never', 'on-demand'],
714
+ help=argparse.SUPPRESS,
715
+ )
716
+ parser.add_argument(
717
+ '--migration-policy',
718
+ metavar='<migration-policy>',
719
+ choices=['never', 'on-demand'],
720
+ help=_(
721
+ 'Migration policy while re-typing volume '
722
+ '("never" or "on-demand", default is "never" ) '
723
+ '(available only when --type option is specified)'
724
+ ),
725
+ )
726
+ bootable_group = parser.add_mutually_exclusive_group()
727
+ bootable_group.add_argument(
728
+ "--bootable",
729
+ action="store_true",
730
+ help=_("Mark volume as bootable"),
731
+ )
732
+ bootable_group.add_argument(
733
+ "--non-bootable",
734
+ action="store_true",
735
+ help=_("Mark volume as non-bootable"),
736
+ )
737
+ readonly_group = parser.add_mutually_exclusive_group()
738
+ readonly_group.add_argument(
739
+ "--read-only",
740
+ action="store_true",
741
+ help=_("Set volume to read-only access mode"),
742
+ )
743
+ readonly_group.add_argument(
744
+ "--read-write",
745
+ action="store_true",
746
+ help=_("Set volume to read-write access mode"),
747
+ )
748
+ return parser
749
+
750
+ def take_action(self, parsed_args):
751
+ volume_client = self.app.client_manager.volume
752
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
753
+
754
+ result = 0
755
+ if parsed_args.retype_policy:
756
+ msg = _(
757
+ "The '--retype-policy' option has been deprecated in favor "
758
+ "of '--migration-policy' option. The '--retype-policy' option "
759
+ "will be removed in a future release. Please use "
760
+ "'--migration-policy' instead."
761
+ )
762
+ self.log.warning(msg)
763
+
764
+ if parsed_args.size:
765
+ try:
766
+ if parsed_args.size <= volume.size:
767
+ msg = (
768
+ _("New size must be greater than %s GB") % volume.size
769
+ )
770
+ raise exceptions.CommandError(msg)
771
+ if (
772
+ volume.status != 'available'
773
+ and not volume_client.api_version.matches('3.42')
774
+ ):
775
+ msg = (
776
+ _(
777
+ "Volume is in %s state, it must be available "
778
+ "before size can be extended"
779
+ )
780
+ % volume.status
781
+ )
782
+ raise exceptions.CommandError(msg)
783
+ volume_client.volumes.extend(volume.id, parsed_args.size)
784
+ except Exception as e:
785
+ LOG.error(_("Failed to set volume size: %s"), e)
786
+ result += 1
787
+
788
+ if parsed_args.no_property:
789
+ try:
790
+ volume_client.volumes.delete_metadata(
791
+ volume.id, volume.metadata.keys()
792
+ )
793
+ except Exception as e:
794
+ LOG.error(_("Failed to clean volume properties: %s"), e)
795
+ result += 1
796
+
797
+ if parsed_args.property:
798
+ try:
799
+ volume_client.volumes.set_metadata(
800
+ volume.id, parsed_args.property
801
+ )
802
+ except Exception as e:
803
+ LOG.error(_("Failed to set volume property: %s"), e)
804
+ result += 1
805
+ if parsed_args.image_property:
806
+ try:
807
+ volume_client.volumes.set_image_metadata(
808
+ volume.id, parsed_args.image_property
809
+ )
810
+ except Exception as e:
811
+ LOG.error(_("Failed to set image property: %s"), e)
812
+ result += 1
813
+ if parsed_args.state:
814
+ try:
815
+ volume_client.volumes.reset_state(volume.id, parsed_args.state)
816
+ except Exception as e:
817
+ LOG.error(_("Failed to set volume state: %s"), e)
818
+ result += 1
819
+ if parsed_args.attached:
820
+ try:
821
+ volume_client.volumes.reset_state(
822
+ volume.id, state=None, attach_status="attached"
823
+ )
824
+ except Exception as e:
825
+ LOG.error(_("Failed to set volume attach-status: %s"), e)
826
+ result += 1
827
+ if parsed_args.detached:
828
+ try:
829
+ volume_client.volumes.reset_state(
830
+ volume.id, state=None, attach_status="detached"
831
+ )
832
+ except Exception as e:
833
+ LOG.error(_("Failed to set volume attach-status: %s"), e)
834
+ result += 1
835
+ if parsed_args.bootable or parsed_args.non_bootable:
836
+ try:
837
+ volume_client.volumes.set_bootable(
838
+ volume.id, parsed_args.bootable
839
+ )
840
+ except Exception as e:
841
+ LOG.error(_("Failed to set volume bootable property: %s"), e)
842
+ result += 1
843
+ if parsed_args.read_only or parsed_args.read_write:
844
+ try:
845
+ volume_client.volumes.update_readonly_flag(
846
+ volume.id, parsed_args.read_only
847
+ )
848
+ except Exception as e:
849
+ LOG.error(
850
+ _(
851
+ "Failed to set volume read-only access "
852
+ "mode flag: %s"
853
+ ),
854
+ e,
855
+ )
856
+ result += 1
857
+ policy = parsed_args.migration_policy or parsed_args.retype_policy
858
+ if parsed_args.type:
859
+ # get the migration policy
860
+ migration_policy = 'never'
861
+ if policy:
862
+ migration_policy = policy
863
+ try:
864
+ # find the volume type
865
+ volume_type = utils.find_resource(
866
+ volume_client.volume_types, parsed_args.type
867
+ )
868
+ # reset to the new volume type
869
+ volume_client.volumes.retype(
870
+ volume.id, volume_type.id, migration_policy
871
+ )
872
+ except Exception as e:
873
+ LOG.error(_("Failed to set volume type: %s"), e)
874
+ result += 1
875
+ elif policy:
876
+ # If the "--migration-policy" is specified without "--type"
877
+ LOG.warning(
878
+ _("'%s' option will not work without '--type' option")
879
+ % (
880
+ '--migration-policy'
881
+ if parsed_args.migration_policy
882
+ else '--retype-policy'
883
+ )
884
+ )
885
+
886
+ kwargs = {}
887
+ if parsed_args.name:
888
+ kwargs['display_name'] = parsed_args.name
889
+ if parsed_args.description:
890
+ kwargs['display_description'] = parsed_args.description
891
+ if kwargs:
892
+ try:
893
+ volume_client.volumes.update(volume.id, **kwargs)
894
+ except Exception as e:
895
+ LOG.error(
896
+ _(
897
+ "Failed to update volume display name "
898
+ "or display description: %s"
899
+ ),
900
+ e,
901
+ )
902
+ result += 1
903
+
904
+ if result > 0:
905
+ raise exceptions.CommandError(
906
+ _("One or more of the " "set operations failed")
907
+ )
908
+
909
+
910
+ class ShowVolume(command.ShowOne):
911
+ _description = _("Display volume details")
912
+
913
+ def get_parser(self, prog_name):
914
+ parser = super().get_parser(prog_name)
915
+ parser.add_argument(
916
+ 'volume',
917
+ metavar="<volume>",
918
+ help=_("Volume to display (name or ID)"),
919
+ )
920
+ return parser
921
+
922
+ def take_action(self, parsed_args):
923
+ volume_client = self.app.client_manager.volume
924
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
925
+
926
+ # Special mapping for columns to make the output easier to read:
927
+ # 'metadata' --> 'properties'
928
+ # 'volume_type' --> 'type'
929
+ volume._info.update(
930
+ {
931
+ 'properties': format_columns.DictColumn(
932
+ volume._info.pop('metadata')
933
+ ),
934
+ 'type': volume._info.pop('volume_type'),
935
+ },
936
+ )
937
+
938
+ # Remove key links from being displayed
939
+ volume._info.pop("links", None)
940
+ return zip(*sorted(volume._info.items()))
941
+
942
+
943
+ class UnsetVolume(command.Command):
944
+ _description = _("Unset volume properties")
945
+
946
+ def get_parser(self, prog_name):
947
+ parser = super().get_parser(prog_name)
948
+ parser.add_argument(
949
+ 'volume',
950
+ metavar='<volume>',
951
+ help=_('Volume to modify (name or ID)'),
952
+ )
953
+ parser.add_argument(
954
+ '--property',
955
+ metavar='<key>',
956
+ action='append',
957
+ help=_(
958
+ 'Remove a property from volume '
959
+ '(repeat option to remove multiple properties)'
960
+ ),
961
+ )
962
+ parser.add_argument(
963
+ '--image-property',
964
+ metavar='<key>',
965
+ action='append',
966
+ help=_(
967
+ 'Remove an image property from volume '
968
+ '(repeat option to remove multiple image properties)'
969
+ ),
970
+ )
971
+ return parser
972
+
973
+ def take_action(self, parsed_args):
974
+ volume_client = self.app.client_manager.volume
975
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
976
+
977
+ result = 0
978
+ if parsed_args.property:
979
+ try:
980
+ volume_client.volumes.delete_metadata(
981
+ volume.id, parsed_args.property
982
+ )
983
+ except Exception as e:
984
+ LOG.error(_("Failed to unset volume property: %s"), e)
985
+ result += 1
986
+
987
+ if parsed_args.image_property:
988
+ try:
989
+ volume_client.volumes.delete_image_metadata(
990
+ volume.id, parsed_args.image_property
991
+ )
992
+ except Exception as e:
993
+ LOG.error(_("Failed to unset image property: %s"), e)
994
+ result += 1
995
+
996
+ if result > 0:
997
+ raise exceptions.CommandError(
998
+ _("One or more of the " "unset operations failed")
999
+ )
1000
+
1001
+
31
1002
  class VolumeSummary(command.ShowOne):
32
1003
  _description = _("Show a summary of all volumes in this deployment.")
33
1004