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
@@ -10,47 +10,2161 @@
10
10
  # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
11
  # License for the specific language governing permissions and limitations
12
12
  # under the License.
13
- #
14
13
 
15
- import copy
16
- from unittest import mock
14
+ import copy
15
+ from unittest import mock
16
+
17
+ from openstack.block_storage.v3 import block_storage_summary as _summary
18
+ from openstack.block_storage.v3 import snapshot as _snapshot
19
+ from openstack.block_storage.v3 import volume as _volume
20
+ from openstack.test import fakes as sdk_fakes
21
+ from osc_lib.cli import format_columns
22
+ from osc_lib import exceptions
23
+ from osc_lib import utils
24
+
25
+ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
26
+ from openstackclient.tests.unit.image.v2 import fakes as image_fakes
27
+ from openstackclient.tests.unit import utils as test_utils
28
+ from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
29
+ from openstackclient.volume.v3 import volume
30
+
31
+
32
+ # TODO(stephenfin): Combine these two test classes
33
+ class TestVolumeCreateLegacy(volume_fakes.TestVolume):
34
+ project = identity_fakes.FakeProject.create_one_project()
35
+ user = identity_fakes.FakeUser.create_one_user()
36
+
37
+ columns = (
38
+ 'attachments',
39
+ 'availability_zone',
40
+ 'bootable',
41
+ 'description',
42
+ 'id',
43
+ 'name',
44
+ 'properties',
45
+ 'size',
46
+ 'snapshot_id',
47
+ 'status',
48
+ 'type',
49
+ )
50
+
51
+ def setUp(self):
52
+ super().setUp()
53
+
54
+ self.volumes_mock = self.volume_client.volumes
55
+ self.volumes_mock.reset_mock()
56
+
57
+ self.consistencygroups_mock = self.volume_client.consistencygroups
58
+ self.consistencygroups_mock.reset_mock()
59
+
60
+ self.snapshots_mock = self.volume_client.volume_snapshots
61
+ self.snapshots_mock.reset_mock()
62
+
63
+ self.backups_mock = self.volume_client.backups
64
+ self.backups_mock.reset_mock()
65
+
66
+ self.new_volume = volume_fakes.create_one_volume()
67
+ self.volumes_mock.create.return_value = self.new_volume
68
+
69
+ self.datalist = (
70
+ self.new_volume.attachments,
71
+ self.new_volume.availability_zone,
72
+ self.new_volume.bootable,
73
+ self.new_volume.description,
74
+ self.new_volume.id,
75
+ self.new_volume.name,
76
+ format_columns.DictColumn(self.new_volume.metadata),
77
+ self.new_volume.size,
78
+ self.new_volume.snapshot_id,
79
+ self.new_volume.status,
80
+ self.new_volume.volume_type,
81
+ )
82
+
83
+ # Get the command object to test
84
+ self.cmd = volume.CreateVolume(self.app, None)
85
+
86
+ def test_volume_create_min_options(self):
87
+ arglist = [
88
+ '--size',
89
+ str(self.new_volume.size),
90
+ ]
91
+ verifylist = [
92
+ ('size', self.new_volume.size),
93
+ ]
94
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
95
+
96
+ # In base command class ShowOne in cliff, abstract method take_action()
97
+ # returns a two-part tuple with a tuple of column names and a tuple of
98
+ # data to be shown.
99
+ columns, data = self.cmd.take_action(parsed_args)
100
+
101
+ self.volumes_mock.create.assert_called_with(
102
+ size=self.new_volume.size,
103
+ snapshot_id=None,
104
+ name=None,
105
+ description=None,
106
+ volume_type=None,
107
+ availability_zone=None,
108
+ metadata=None,
109
+ imageRef=None,
110
+ source_volid=None,
111
+ consistencygroup_id=None,
112
+ scheduler_hints=None,
113
+ backup_id=None,
114
+ )
115
+
116
+ self.assertEqual(self.columns, columns)
117
+ self.assertCountEqual(self.datalist, data)
118
+
119
+ def test_volume_create_options(self):
120
+ consistency_group = volume_fakes.create_one_consistency_group()
121
+ self.consistencygroups_mock.get.return_value = consistency_group
122
+ arglist = [
123
+ '--size',
124
+ str(self.new_volume.size),
125
+ '--description',
126
+ self.new_volume.description,
127
+ '--type',
128
+ self.new_volume.volume_type,
129
+ '--availability-zone',
130
+ self.new_volume.availability_zone,
131
+ '--consistency-group',
132
+ consistency_group.id,
133
+ '--hint',
134
+ 'k=v',
135
+ self.new_volume.name,
136
+ ]
137
+ verifylist = [
138
+ ('size', self.new_volume.size),
139
+ ('description', self.new_volume.description),
140
+ ('type', self.new_volume.volume_type),
141
+ ('availability_zone', self.new_volume.availability_zone),
142
+ ('consistency_group', consistency_group.id),
143
+ ('hint', {'k': 'v'}),
144
+ ('name', self.new_volume.name),
145
+ ]
146
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
147
+
148
+ # In base command class ShowOne in cliff, abstract method take_action()
149
+ # returns a two-part tuple with a tuple of column names and a tuple of
150
+ # data to be shown.
151
+ columns, data = self.cmd.take_action(parsed_args)
152
+
153
+ self.volumes_mock.create.assert_called_with(
154
+ size=self.new_volume.size,
155
+ snapshot_id=None,
156
+ name=self.new_volume.name,
157
+ description=self.new_volume.description,
158
+ volume_type=self.new_volume.volume_type,
159
+ availability_zone=self.new_volume.availability_zone,
160
+ metadata=None,
161
+ imageRef=None,
162
+ source_volid=None,
163
+ consistencygroup_id=consistency_group.id,
164
+ scheduler_hints={'k': 'v'},
165
+ backup_id=None,
166
+ )
167
+
168
+ self.assertEqual(self.columns, columns)
169
+ self.assertCountEqual(self.datalist, data)
170
+
171
+ def test_volume_create_properties(self):
172
+ arglist = [
173
+ '--property',
174
+ 'Alpha=a',
175
+ '--property',
176
+ 'Beta=b',
177
+ '--size',
178
+ str(self.new_volume.size),
179
+ self.new_volume.name,
180
+ ]
181
+ verifylist = [
182
+ ('property', {'Alpha': 'a', 'Beta': 'b'}),
183
+ ('size', self.new_volume.size),
184
+ ('name', self.new_volume.name),
185
+ ]
186
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
187
+
188
+ # In base command class ShowOne in cliff, abstract method take_action()
189
+ # returns a two-part tuple with a tuple of column names and a tuple of
190
+ # data to be shown.
191
+ columns, data = self.cmd.take_action(parsed_args)
192
+
193
+ self.volumes_mock.create.assert_called_with(
194
+ size=self.new_volume.size,
195
+ snapshot_id=None,
196
+ name=self.new_volume.name,
197
+ description=None,
198
+ volume_type=None,
199
+ availability_zone=None,
200
+ metadata={'Alpha': 'a', 'Beta': 'b'},
201
+ imageRef=None,
202
+ source_volid=None,
203
+ consistencygroup_id=None,
204
+ scheduler_hints=None,
205
+ backup_id=None,
206
+ )
207
+
208
+ self.assertEqual(self.columns, columns)
209
+ self.assertCountEqual(self.datalist, data)
210
+
211
+ def test_volume_create_image_id(self):
212
+ image = image_fakes.create_one_image()
213
+ self.image_client.find_image.return_value = image
214
+
215
+ arglist = [
216
+ '--image',
217
+ image.id,
218
+ '--size',
219
+ str(self.new_volume.size),
220
+ self.new_volume.name,
221
+ ]
222
+ verifylist = [
223
+ ('image', image.id),
224
+ ('size', self.new_volume.size),
225
+ ('name', self.new_volume.name),
226
+ ]
227
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
228
+
229
+ # In base command class ShowOne in cliff, abstract method take_action()
230
+ # returns a two-part tuple with a tuple of column names and a tuple of
231
+ # data to be shown.
232
+ columns, data = self.cmd.take_action(parsed_args)
233
+
234
+ self.volumes_mock.create.assert_called_with(
235
+ size=self.new_volume.size,
236
+ snapshot_id=None,
237
+ name=self.new_volume.name,
238
+ description=None,
239
+ volume_type=None,
240
+ availability_zone=None,
241
+ metadata=None,
242
+ imageRef=image.id,
243
+ source_volid=None,
244
+ consistencygroup_id=None,
245
+ scheduler_hints=None,
246
+ backup_id=None,
247
+ )
248
+
249
+ self.assertEqual(self.columns, columns)
250
+ self.assertCountEqual(self.datalist, data)
251
+
252
+ def test_volume_create_image_name(self):
253
+ image = image_fakes.create_one_image()
254
+ self.image_client.find_image.return_value = image
255
+
256
+ arglist = [
257
+ '--image',
258
+ image.name,
259
+ '--size',
260
+ str(self.new_volume.size),
261
+ self.new_volume.name,
262
+ ]
263
+ verifylist = [
264
+ ('image', image.name),
265
+ ('size', self.new_volume.size),
266
+ ('name', self.new_volume.name),
267
+ ]
268
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
269
+
270
+ # In base command class ShowOne in cliff, abstract method take_action()
271
+ # returns a two-part tuple with a tuple of column names and a tuple of
272
+ # data to be shown.
273
+ columns, data = self.cmd.take_action(parsed_args)
274
+
275
+ self.volumes_mock.create.assert_called_with(
276
+ size=self.new_volume.size,
277
+ snapshot_id=None,
278
+ name=self.new_volume.name,
279
+ description=None,
280
+ volume_type=None,
281
+ availability_zone=None,
282
+ metadata=None,
283
+ imageRef=image.id,
284
+ source_volid=None,
285
+ consistencygroup_id=None,
286
+ scheduler_hints=None,
287
+ backup_id=None,
288
+ )
289
+
290
+ self.assertEqual(self.columns, columns)
291
+ self.assertCountEqual(self.datalist, data)
292
+
293
+ def test_volume_create_with_snapshot(self):
294
+ snapshot = volume_fakes.create_one_snapshot()
295
+ self.new_volume.snapshot_id = snapshot.id
296
+ arglist = [
297
+ '--snapshot',
298
+ self.new_volume.snapshot_id,
299
+ self.new_volume.name,
300
+ ]
301
+ verifylist = [
302
+ ('snapshot', self.new_volume.snapshot_id),
303
+ ('name', self.new_volume.name),
304
+ ]
305
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
306
+
307
+ self.snapshots_mock.get.return_value = snapshot
308
+
309
+ # In base command class ShowOne in cliff, abstract method take_action()
310
+ # returns a two-part tuple with a tuple of column names and a tuple of
311
+ # data to be shown.
312
+ columns, data = self.cmd.take_action(parsed_args)
313
+
314
+ self.volumes_mock.create.assert_called_once_with(
315
+ size=snapshot.size,
316
+ snapshot_id=snapshot.id,
317
+ name=self.new_volume.name,
318
+ description=None,
319
+ volume_type=None,
320
+ availability_zone=None,
321
+ metadata=None,
322
+ imageRef=None,
323
+ source_volid=None,
324
+ consistencygroup_id=None,
325
+ scheduler_hints=None,
326
+ backup_id=None,
327
+ )
328
+
329
+ self.assertEqual(self.columns, columns)
330
+ self.assertCountEqual(self.datalist, data)
331
+
332
+ def test_volume_create_with_backup(self):
333
+ self.set_volume_api_version('3.47')
334
+
335
+ backup = volume_fakes.create_one_backup()
336
+ self.new_volume.backup_id = backup.id
337
+ arglist = [
338
+ '--backup',
339
+ self.new_volume.backup_id,
340
+ self.new_volume.name,
341
+ ]
342
+ verifylist = [
343
+ ('backup', self.new_volume.backup_id),
344
+ ('name', self.new_volume.name),
345
+ ]
346
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
347
+
348
+ self.backups_mock.get.return_value = backup
349
+
350
+ # In base command class ShowOne in cliff, abstract method take_action()
351
+ # returns a two-part tuple with a tuple of column names and a tuple of
352
+ # data to be shown.
353
+ columns, data = self.cmd.take_action(parsed_args)
354
+
355
+ self.volumes_mock.create.assert_called_once_with(
356
+ size=backup.size,
357
+ snapshot_id=None,
358
+ name=self.new_volume.name,
359
+ description=None,
360
+ volume_type=None,
361
+ availability_zone=None,
362
+ metadata=None,
363
+ imageRef=None,
364
+ source_volid=None,
365
+ consistencygroup_id=None,
366
+ scheduler_hints=None,
367
+ backup_id=backup.id,
368
+ )
369
+
370
+ self.assertEqual(self.columns, columns)
371
+ self.assertCountEqual(self.datalist, data)
372
+
373
+ def test_volume_create_with_backup_pre_v347(self):
374
+ backup = volume_fakes.create_one_backup()
375
+ self.new_volume.backup_id = backup.id
376
+ arglist = [
377
+ '--backup',
378
+ self.new_volume.backup_id,
379
+ self.new_volume.name,
380
+ ]
381
+ verifylist = [
382
+ ('backup', self.new_volume.backup_id),
383
+ ('name', self.new_volume.name),
384
+ ]
385
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
386
+
387
+ self.backups_mock.get.return_value = backup
388
+
389
+ exc = self.assertRaises(
390
+ exceptions.CommandError, self.cmd.take_action, parsed_args
391
+ )
392
+ self.assertIn("--os-volume-api-version 3.47 or greater", str(exc))
393
+
394
+ def test_volume_create_with_source_volume(self):
395
+ source_vol = "source_vol"
396
+ arglist = [
397
+ '--source',
398
+ self.new_volume.id,
399
+ source_vol,
400
+ ]
401
+ verifylist = [
402
+ ('source', self.new_volume.id),
403
+ ('name', source_vol),
404
+ ]
405
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
406
+
407
+ self.volumes_mock.get.return_value = self.new_volume
408
+
409
+ # In base command class ShowOne in cliff, abstract method take_action()
410
+ # returns a two-part tuple with a tuple of column names and a tuple of
411
+ # data to be shown.
412
+ columns, data = self.cmd.take_action(parsed_args)
413
+
414
+ self.volumes_mock.create.assert_called_once_with(
415
+ size=self.new_volume.size,
416
+ snapshot_id=None,
417
+ name=source_vol,
418
+ description=None,
419
+ volume_type=None,
420
+ availability_zone=None,
421
+ metadata=None,
422
+ imageRef=None,
423
+ source_volid=self.new_volume.id,
424
+ consistencygroup_id=None,
425
+ scheduler_hints=None,
426
+ backup_id=None,
427
+ )
428
+
429
+ self.assertEqual(self.columns, columns)
430
+ self.assertCountEqual(self.datalist, data)
431
+
432
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
433
+ def test_volume_create_with_bootable_and_readonly(self, mock_wait):
434
+ arglist = [
435
+ '--bootable',
436
+ '--read-only',
437
+ '--size',
438
+ str(self.new_volume.size),
439
+ self.new_volume.name,
440
+ ]
441
+ verifylist = [
442
+ ('bootable', True),
443
+ ('non_bootable', False),
444
+ ('read_only', True),
445
+ ('read_write', False),
446
+ ('size', self.new_volume.size),
447
+ ('name', self.new_volume.name),
448
+ ]
449
+
450
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
451
+
452
+ columns, data = self.cmd.take_action(parsed_args)
453
+
454
+ self.volumes_mock.create.assert_called_with(
455
+ size=self.new_volume.size,
456
+ snapshot_id=None,
457
+ name=self.new_volume.name,
458
+ description=None,
459
+ volume_type=None,
460
+ availability_zone=None,
461
+ metadata=None,
462
+ imageRef=None,
463
+ source_volid=None,
464
+ consistencygroup_id=None,
465
+ scheduler_hints=None,
466
+ backup_id=None,
467
+ )
468
+
469
+ self.assertEqual(self.columns, columns)
470
+ self.assertCountEqual(self.datalist, data)
471
+ self.volumes_mock.set_bootable.assert_called_with(
472
+ self.new_volume.id, True
473
+ )
474
+ self.volumes_mock.update_readonly_flag.assert_called_with(
475
+ self.new_volume.id, True
476
+ )
477
+
478
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
479
+ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait):
480
+ arglist = [
481
+ '--non-bootable',
482
+ '--read-write',
483
+ '--size',
484
+ str(self.new_volume.size),
485
+ self.new_volume.name,
486
+ ]
487
+ verifylist = [
488
+ ('bootable', False),
489
+ ('non_bootable', True),
490
+ ('read_only', False),
491
+ ('read_write', True),
492
+ ('size', self.new_volume.size),
493
+ ('name', self.new_volume.name),
494
+ ]
495
+
496
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
497
+
498
+ columns, data = self.cmd.take_action(parsed_args)
499
+
500
+ self.volumes_mock.create.assert_called_with(
501
+ size=self.new_volume.size,
502
+ snapshot_id=None,
503
+ name=self.new_volume.name,
504
+ description=None,
505
+ volume_type=None,
506
+ availability_zone=None,
507
+ metadata=None,
508
+ imageRef=None,
509
+ source_volid=None,
510
+ consistencygroup_id=None,
511
+ scheduler_hints=None,
512
+ backup_id=None,
513
+ )
514
+
515
+ self.assertEqual(self.columns, columns)
516
+ self.assertCountEqual(self.datalist, data)
517
+ self.volumes_mock.set_bootable.assert_called_with(
518
+ self.new_volume.id, False
519
+ )
520
+ self.volumes_mock.update_readonly_flag.assert_called_with(
521
+ self.new_volume.id, False
522
+ )
523
+
524
+ @mock.patch.object(volume.LOG, 'error')
525
+ @mock.patch.object(utils, 'wait_for_status', return_value=True)
526
+ def test_volume_create_with_bootable_and_readonly_fail(
527
+ self, mock_wait, mock_error
528
+ ):
529
+ self.volumes_mock.set_bootable.side_effect = exceptions.CommandError()
530
+
531
+ self.volumes_mock.update_readonly_flag.side_effect = (
532
+ exceptions.CommandError()
533
+ )
534
+
535
+ arglist = [
536
+ '--bootable',
537
+ '--read-only',
538
+ '--size',
539
+ str(self.new_volume.size),
540
+ self.new_volume.name,
541
+ ]
542
+ verifylist = [
543
+ ('bootable', True),
544
+ ('non_bootable', False),
545
+ ('read_only', True),
546
+ ('read_write', False),
547
+ ('size', self.new_volume.size),
548
+ ('name', self.new_volume.name),
549
+ ]
550
+
551
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
552
+
553
+ columns, data = self.cmd.take_action(parsed_args)
554
+
555
+ self.volumes_mock.create.assert_called_with(
556
+ size=self.new_volume.size,
557
+ snapshot_id=None,
558
+ name=self.new_volume.name,
559
+ description=None,
560
+ volume_type=None,
561
+ availability_zone=None,
562
+ metadata=None,
563
+ imageRef=None,
564
+ source_volid=None,
565
+ consistencygroup_id=None,
566
+ scheduler_hints=None,
567
+ backup_id=None,
568
+ )
569
+
570
+ self.assertEqual(2, mock_error.call_count)
571
+ self.assertEqual(self.columns, columns)
572
+ self.assertCountEqual(self.datalist, data)
573
+ self.volumes_mock.set_bootable.assert_called_with(
574
+ self.new_volume.id, True
575
+ )
576
+ self.volumes_mock.update_readonly_flag.assert_called_with(
577
+ self.new_volume.id, True
578
+ )
579
+
580
+ @mock.patch.object(volume.LOG, 'error')
581
+ @mock.patch.object(utils, 'wait_for_status', return_value=False)
582
+ def test_volume_create_non_available_with_readonly(
583
+ self,
584
+ mock_wait,
585
+ mock_error,
586
+ ):
587
+ arglist = [
588
+ '--non-bootable',
589
+ '--read-only',
590
+ '--size',
591
+ str(self.new_volume.size),
592
+ self.new_volume.name,
593
+ ]
594
+ verifylist = [
595
+ ('bootable', False),
596
+ ('non_bootable', True),
597
+ ('read_only', True),
598
+ ('read_write', False),
599
+ ('size', self.new_volume.size),
600
+ ('name', self.new_volume.name),
601
+ ]
602
+
603
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
604
+
605
+ columns, data = self.cmd.take_action(parsed_args)
606
+
607
+ self.volumes_mock.create.assert_called_with(
608
+ size=self.new_volume.size,
609
+ snapshot_id=None,
610
+ name=self.new_volume.name,
611
+ description=None,
612
+ volume_type=None,
613
+ availability_zone=None,
614
+ metadata=None,
615
+ imageRef=None,
616
+ source_volid=None,
617
+ consistencygroup_id=None,
618
+ scheduler_hints=None,
619
+ backup_id=None,
620
+ )
621
+
622
+ self.assertEqual(2, mock_error.call_count)
623
+ self.assertEqual(self.columns, columns)
624
+ self.assertCountEqual(self.datalist, data)
625
+
626
+ def test_volume_create_without_size(self):
627
+ arglist = [
628
+ self.new_volume.name,
629
+ ]
630
+ verifylist = [
631
+ ('name', self.new_volume.name),
632
+ ]
633
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
634
+
635
+ self.assertRaises(
636
+ exceptions.CommandError, self.cmd.take_action, parsed_args
637
+ )
638
+
639
+ def test_volume_create_with_multi_source(self):
640
+ arglist = [
641
+ '--image',
642
+ 'source_image',
643
+ '--source',
644
+ 'source_volume',
645
+ '--snapshot',
646
+ 'source_snapshot',
647
+ '--size',
648
+ str(self.new_volume.size),
649
+ self.new_volume.name,
650
+ ]
651
+ verifylist = [
652
+ ('image', 'source_image'),
653
+ ('source', 'source_volume'),
654
+ ('snapshot', 'source_snapshot'),
655
+ ('size', self.new_volume.size),
656
+ ('name', self.new_volume.name),
657
+ ]
658
+
659
+ self.assertRaises(
660
+ test_utils.ParserException,
661
+ self.check_parser,
662
+ self.cmd,
663
+ arglist,
664
+ verifylist,
665
+ )
666
+
667
+ def test_volume_create_hints(self):
668
+ """--hint needs to behave differently based on the given hint
669
+
670
+ different_host and same_host need to append to a list if given multiple
671
+ times. All other parameter are strings.
672
+ """
673
+ arglist = [
674
+ '--size',
675
+ str(self.new_volume.size),
676
+ '--hint',
677
+ 'k=v',
678
+ '--hint',
679
+ 'k=v2',
680
+ '--hint',
681
+ 'same_host=v3',
682
+ '--hint',
683
+ 'same_host=v4',
684
+ '--hint',
685
+ 'different_host=v5',
686
+ '--hint',
687
+ 'local_to_instance=v6',
688
+ '--hint',
689
+ 'different_host=v7',
690
+ self.new_volume.name,
691
+ ]
692
+ verifylist = [
693
+ ('size', self.new_volume.size),
694
+ (
695
+ 'hint',
696
+ {
697
+ 'k': 'v2',
698
+ 'same_host': ['v3', 'v4'],
699
+ 'local_to_instance': 'v6',
700
+ 'different_host': ['v5', 'v7'],
701
+ },
702
+ ),
703
+ ('name', self.new_volume.name),
704
+ ]
705
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
706
+
707
+ # In base command class ShowOne in cliff, abstract method take_action()
708
+ # returns a two-part tuple with a tuple of column names and a tuple of
709
+ # data to be shown.
710
+ columns, data = self.cmd.take_action(parsed_args)
711
+
712
+ self.volumes_mock.create.assert_called_with(
713
+ size=self.new_volume.size,
714
+ snapshot_id=None,
715
+ name=self.new_volume.name,
716
+ description=None,
717
+ volume_type=None,
718
+ availability_zone=None,
719
+ metadata=None,
720
+ imageRef=None,
721
+ source_volid=None,
722
+ consistencygroup_id=None,
723
+ scheduler_hints={
724
+ 'k': 'v2',
725
+ 'same_host': ['v3', 'v4'],
726
+ 'local_to_instance': 'v6',
727
+ 'different_host': ['v5', 'v7'],
728
+ },
729
+ backup_id=None,
730
+ )
731
+
732
+ self.assertEqual(self.columns, columns)
733
+ self.assertCountEqual(self.datalist, data)
734
+
735
+
736
+ class TestVolumeCreate(volume_fakes.TestVolume):
737
+ columns = (
738
+ 'attachments',
739
+ 'availability_zone',
740
+ 'consistency_group_id',
741
+ 'created_at',
742
+ 'description',
743
+ 'extended_replication_status',
744
+ 'group_id',
745
+ 'host',
746
+ 'id',
747
+ 'image_id',
748
+ 'is_bootable',
749
+ 'is_encrypted',
750
+ 'is_multiattach',
751
+ 'location',
752
+ 'metadata',
753
+ 'migration_id',
754
+ 'migration_status',
755
+ 'name',
756
+ 'project_id',
757
+ 'provider_id',
758
+ 'replication_driver_data',
759
+ 'replication_status',
760
+ 'scheduler_hints',
761
+ 'size',
762
+ 'snapshot_id',
763
+ 'source_volume_id',
764
+ 'status',
765
+ 'updated_at',
766
+ 'user_id',
767
+ 'volume_image_metadata',
768
+ 'volume_type',
769
+ )
770
+
771
+ def setUp(self):
772
+ super().setUp()
773
+
774
+ self.new_volume = sdk_fakes.generate_fake_resource(
775
+ _volume.Volume, **{'size': 1}
776
+ )
777
+
778
+ self.datalist = (
779
+ self.new_volume.attachments,
780
+ self.new_volume.availability_zone,
781
+ self.new_volume.consistency_group_id,
782
+ self.new_volume.created_at,
783
+ self.new_volume.description,
784
+ self.new_volume.extended_replication_status,
785
+ self.new_volume.group_id,
786
+ self.new_volume.host,
787
+ self.new_volume.id,
788
+ self.new_volume.image_id,
789
+ self.new_volume.is_bootable,
790
+ self.new_volume.is_encrypted,
791
+ self.new_volume.is_multiattach,
792
+ self.new_volume.location,
793
+ self.new_volume.metadata,
794
+ self.new_volume.migration_id,
795
+ self.new_volume.migration_status,
796
+ self.new_volume.name,
797
+ self.new_volume.project_id,
798
+ self.new_volume.provider_id,
799
+ self.new_volume.replication_driver_data,
800
+ self.new_volume.replication_status,
801
+ self.new_volume.scheduler_hints,
802
+ self.new_volume.size,
803
+ self.new_volume.snapshot_id,
804
+ self.new_volume.source_volume_id,
805
+ self.new_volume.status,
806
+ self.new_volume.updated_at,
807
+ self.new_volume.user_id,
808
+ self.new_volume.volume_image_metadata,
809
+ self.new_volume.volume_type,
810
+ )
811
+
812
+ # Get the command object to test
813
+ self.cmd = volume.CreateVolume(self.app, None)
814
+
815
+ def test_volume_create_remote_source(self):
816
+ self.volume_sdk_client.manage_volume.return_value = self.new_volume
817
+
818
+ arglist = [
819
+ '--remote-source',
820
+ 'key=val',
821
+ '--host',
822
+ 'fake_host',
823
+ self.new_volume.name,
824
+ ]
825
+ verifylist = [
826
+ ('remote_source', {'key': 'val'}),
827
+ ('host', 'fake_host'),
828
+ ('name', self.new_volume.name),
829
+ ]
830
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
831
+
832
+ columns, data = self.cmd.take_action(parsed_args)
833
+
834
+ self.volume_sdk_client.manage_volume.assert_called_with(
835
+ host='fake_host',
836
+ ref={'key': 'val'},
837
+ name=parsed_args.name,
838
+ description=parsed_args.description,
839
+ volume_type=parsed_args.type,
840
+ availability_zone=parsed_args.availability_zone,
841
+ metadata=parsed_args.property,
842
+ bootable=parsed_args.bootable,
843
+ cluster=getattr(parsed_args, 'cluster', None),
844
+ )
845
+
846
+ self.assertEqual(self.columns, columns)
847
+ self.assertCountEqual(self.datalist, data)
848
+
849
+ def test_volume_create_remote_source_pre_v316(self):
850
+ self.set_volume_api_version('3.15')
851
+ arglist = [
852
+ '--remote-source',
853
+ 'key=val',
854
+ '--cluster',
855
+ 'fake_cluster',
856
+ self.new_volume.name,
857
+ ]
858
+ verifylist = [
859
+ ('remote_source', {'key': 'val'}),
860
+ ('cluster', 'fake_cluster'),
861
+ ('name', self.new_volume.name),
862
+ ]
863
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
864
+
865
+ exc = self.assertRaises(
866
+ exceptions.CommandError, self.cmd.take_action, parsed_args
867
+ )
868
+ self.assertIn(
869
+ '--os-volume-api-version 3.16 or greater is required', str(exc)
870
+ )
871
+
872
+ def test_volume_create_remote_source_host_and_cluster(self):
873
+ self.set_volume_api_version('3.16')
874
+ arglist = [
875
+ '--remote-source',
876
+ 'key=val',
877
+ '--host',
878
+ 'fake_host',
879
+ '--cluster',
880
+ 'fake_cluster',
881
+ self.new_volume.name,
882
+ ]
883
+ verifylist = [
884
+ ('remote_source', {'key': 'val'}),
885
+ ('host', 'fake_host'),
886
+ ('cluster', 'fake_cluster'),
887
+ ('name', self.new_volume.name),
888
+ ]
889
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
890
+
891
+ exc = self.assertRaises(
892
+ exceptions.CommandError, self.cmd.take_action, parsed_args
893
+ )
894
+ self.assertIn(
895
+ 'Only one of --host or --cluster needs to be specified', str(exc)
896
+ )
897
+
898
+ def test_volume_create_remote_source_no_host_or_cluster(self):
899
+ arglist = [
900
+ '--remote-source',
901
+ 'key=val',
902
+ self.new_volume.name,
903
+ ]
904
+ verifylist = [
905
+ ('remote_source', {'key': 'val'}),
906
+ ('name', self.new_volume.name),
907
+ ]
908
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
909
+
910
+ exc = self.assertRaises(
911
+ exceptions.CommandError, self.cmd.take_action, parsed_args
912
+ )
913
+ self.assertIn(
914
+ 'One of --host or --cluster needs to be specified to ', str(exc)
915
+ )
916
+
917
+ def test_volume_create_remote_source_size(self):
918
+ arglist = [
919
+ '--size',
920
+ str(self.new_volume.size),
921
+ '--remote-source',
922
+ 'key=val',
923
+ self.new_volume.name,
924
+ ]
925
+ verifylist = [
926
+ ('size', self.new_volume.size),
927
+ ('remote_source', {'key': 'val'}),
928
+ ('name', self.new_volume.name),
929
+ ]
930
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
931
+
932
+ exc = self.assertRaises(
933
+ exceptions.CommandError, self.cmd.take_action, parsed_args
934
+ )
935
+ self.assertIn(
936
+ '--size, --consistency-group, --hint, --read-only and '
937
+ '--read-write options are not supported',
938
+ str(exc),
939
+ )
940
+
941
+ def test_volume_create_host_no_remote_source(self):
942
+ arglist = [
943
+ '--size',
944
+ str(self.new_volume.size),
945
+ '--host',
946
+ 'fake_host',
947
+ self.new_volume.name,
948
+ ]
949
+ verifylist = [
950
+ ('size', self.new_volume.size),
951
+ ('host', 'fake_host'),
952
+ ('name', self.new_volume.name),
953
+ ]
954
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
955
+
956
+ exc = self.assertRaises(
957
+ exceptions.CommandError, self.cmd.take_action, parsed_args
958
+ )
959
+ self.assertIn(
960
+ '--host and --cluster options are only supported ',
961
+ str(exc),
962
+ )
963
+
964
+
965
+ class TestVolumeDeleteLegacy(volume_fakes.TestVolume):
966
+ def setUp(self):
967
+ super().setUp()
968
+
969
+ self.volumes_mock = self.volume_client.volumes
970
+ self.volumes_mock.reset_mock()
971
+
972
+ self.volumes_mock.delete.return_value = None
973
+ self.volumes = volume_fakes.create_volumes(count=1)
974
+ self.volumes_mock.get = volume_fakes.get_volumes(self.volumes, 0)
975
+
976
+ # Get the command object to mock
977
+ self.cmd = volume.DeleteVolume(self.app, None)
978
+
979
+ def test_volume_delete_one_volume(self):
980
+ arglist = [self.volumes[0].id]
981
+ verifylist = [
982
+ ("force", False),
983
+ ("purge", False),
984
+ ("volumes", [self.volumes[0].id]),
985
+ ]
986
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
987
+
988
+ result = self.cmd.take_action(parsed_args)
989
+
990
+ self.volumes_mock.delete.assert_called_once_with(
991
+ self.volumes[0].id, cascade=False
992
+ )
993
+ self.assertIsNone(result)
994
+
995
+ def test_volume_delete_multi_volumes(self):
996
+ arglist = [v.id for v in self.volumes]
997
+ verifylist = [
998
+ ('force', False),
999
+ ('purge', False),
1000
+ ('volumes', arglist),
1001
+ ]
1002
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1003
+
1004
+ result = self.cmd.take_action(parsed_args)
1005
+
1006
+ calls = [mock.call(v.id, cascade=False) for v in self.volumes]
1007
+ self.volumes_mock.delete.assert_has_calls(calls)
1008
+ self.assertIsNone(result)
1009
+
1010
+ def test_volume_delete_multi_volumes_with_exception(self):
1011
+ arglist = [
1012
+ self.volumes[0].id,
1013
+ 'unexist_volume',
1014
+ ]
1015
+ verifylist = [
1016
+ ('force', False),
1017
+ ('purge', False),
1018
+ ('volumes', arglist),
1019
+ ]
1020
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1021
+
1022
+ find_mock_result = [self.volumes[0], exceptions.CommandError]
1023
+ with mock.patch.object(
1024
+ utils, 'find_resource', side_effect=find_mock_result
1025
+ ) as find_mock:
1026
+ try:
1027
+ self.cmd.take_action(parsed_args)
1028
+ self.fail('CommandError should be raised.')
1029
+ except exceptions.CommandError as e:
1030
+ self.assertEqual('1 of 2 volumes failed to delete.', str(e))
1031
+
1032
+ find_mock.assert_any_call(self.volumes_mock, self.volumes[0].id)
1033
+ find_mock.assert_any_call(self.volumes_mock, 'unexist_volume')
1034
+
1035
+ self.assertEqual(2, find_mock.call_count)
1036
+ self.volumes_mock.delete.assert_called_once_with(
1037
+ self.volumes[0].id, cascade=False
1038
+ )
1039
+
1040
+ def test_volume_delete_with_purge(self):
1041
+ arglist = [
1042
+ '--purge',
1043
+ self.volumes[0].id,
1044
+ ]
1045
+ verifylist = [
1046
+ ('force', False),
1047
+ ('purge', True),
1048
+ ('volumes', [self.volumes[0].id]),
1049
+ ]
1050
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1051
+
1052
+ result = self.cmd.take_action(parsed_args)
1053
+
1054
+ self.volumes_mock.delete.assert_called_once_with(
1055
+ self.volumes[0].id, cascade=True
1056
+ )
1057
+ self.assertIsNone(result)
1058
+
1059
+ def test_volume_delete_with_force(self):
1060
+ arglist = [
1061
+ '--force',
1062
+ self.volumes[0].id,
1063
+ ]
1064
+ verifylist = [
1065
+ ('force', True),
1066
+ ('purge', False),
1067
+ ('volumes', [self.volumes[0].id]),
1068
+ ]
1069
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1070
+
1071
+ result = self.cmd.take_action(parsed_args)
1072
+
1073
+ self.volumes_mock.force_delete.assert_called_once_with(
1074
+ self.volumes[0].id
1075
+ )
1076
+ self.assertIsNone(result)
1077
+
1078
+
1079
+ class TestVolumeDelete(volume_fakes.TestVolume):
1080
+ def setUp(self):
1081
+ super().setUp()
1082
+
1083
+ self.volumes_mock = self.volume_client.volumes
1084
+ self.volumes_mock.reset_mock()
1085
+ self.volume_sdk_client.unmanage_volume.return_value = None
1086
+
1087
+ # Get the command object to mock
1088
+ self.cmd = volume.DeleteVolume(self.app, None)
1089
+
1090
+ def test_volume_delete_remote(self):
1091
+ vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1})
1092
+ self.volumes_mock.get.return_value = vol
1093
+
1094
+ arglist = ['--remote', vol.id]
1095
+ verifylist = [
1096
+ ("remote", True),
1097
+ ("force", False),
1098
+ ("purge", False),
1099
+ ("volumes", [vol.id]),
1100
+ ]
1101
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1102
+
1103
+ result = self.cmd.take_action(parsed_args)
1104
+
1105
+ self.volume_sdk_client.unmanage_volume.assert_called_once_with(vol.id)
1106
+ self.assertIsNone(result)
1107
+
1108
+ def test_volume_delete_multi_volumes_remote(self):
1109
+ volumes = sdk_fakes.generate_fake_resources(
1110
+ _volume.Volume, count=3, attrs={'size': 1}
1111
+ )
1112
+
1113
+ arglist = ['--remote']
1114
+ arglist += [v.id for v in volumes]
1115
+ verifylist = [
1116
+ ('remote', True),
1117
+ ('force', False),
1118
+ ('purge', False),
1119
+ ('volumes', arglist[1:]),
1120
+ ]
1121
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1122
+
1123
+ result = self.cmd.take_action(parsed_args)
1124
+
1125
+ calls = [mock.call(v.id) for v in volumes]
1126
+ self.volume_sdk_client.unmanage_volume.assert_has_calls(calls)
1127
+ self.assertIsNone(result)
1128
+
1129
+ def test_volume_delete_remote_with_purge(self):
1130
+ vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1})
1131
+
1132
+ arglist = [
1133
+ '--remote',
1134
+ '--purge',
1135
+ vol.id,
1136
+ ]
1137
+ verifylist = [
1138
+ ('remote', True),
1139
+ ('force', False),
1140
+ ('purge', True),
1141
+ ('volumes', [vol.id]),
1142
+ ]
1143
+
1144
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1145
+ exc = self.assertRaises(
1146
+ exceptions.CommandError, self.cmd.take_action, parsed_args
1147
+ )
1148
+ self.assertIn(
1149
+ "The --force and --purge options are not supported with the "
1150
+ "--remote parameter.",
1151
+ str(exc),
1152
+ )
1153
+
1154
+ def test_volume_delete_remote_with_force(self):
1155
+ vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1})
1156
+
1157
+ arglist = [
1158
+ '--remote',
1159
+ '--force',
1160
+ vol.id,
1161
+ ]
1162
+ verifylist = [
1163
+ ('remote', True),
1164
+ ('force', True),
1165
+ ('purge', False),
1166
+ ('volumes', [vol.id]),
1167
+ ]
1168
+
1169
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1170
+ exc = self.assertRaises(
1171
+ exceptions.CommandError, self.cmd.take_action, parsed_args
1172
+ )
1173
+ self.assertIn(
1174
+ "The --force and --purge options are not supported with the "
1175
+ "--remote parameter.",
1176
+ str(exc),
1177
+ )
1178
+
1179
+
1180
+ class TestVolumeList(volume_fakes.TestVolume):
1181
+ project = identity_fakes.FakeProject.create_one_project()
1182
+ user = identity_fakes.FakeUser.create_one_user()
1183
+
1184
+ columns = [
1185
+ 'ID',
1186
+ 'Name',
1187
+ 'Status',
1188
+ 'Size',
1189
+ 'Attached to',
1190
+ ]
1191
+
1192
+ def setUp(self):
1193
+ super().setUp()
1194
+
1195
+ self.volumes_mock = self.volume_client.volumes
1196
+ self.volumes_mock.reset_mock()
1197
+
1198
+ self.projects_mock = self.identity_client.projects
1199
+ self.projects_mock.reset_mock()
1200
+
1201
+ self.users_mock = self.identity_client.users
1202
+ self.users_mock.reset_mock()
1203
+
1204
+ self.mock_volume = volume_fakes.create_one_volume()
1205
+ self.volumes_mock.list.return_value = [self.mock_volume]
1206
+
1207
+ self.users_mock.get.return_value = self.user
1208
+
1209
+ self.projects_mock.get.return_value = self.project
1210
+
1211
+ # Get the command object to test
1212
+ self.cmd = volume.ListVolume(self.app, None)
1213
+
1214
+ def test_volume_list_no_options(self):
1215
+ arglist = []
1216
+ verifylist = [
1217
+ ('long', False),
1218
+ ('all_projects', False),
1219
+ ('name', None),
1220
+ ('status', None),
1221
+ ('marker', None),
1222
+ ('limit', None),
1223
+ ]
1224
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1225
+
1226
+ columns, data = self.cmd.take_action(parsed_args)
1227
+
1228
+ search_opts = {
1229
+ 'all_tenants': False,
1230
+ 'project_id': None,
1231
+ 'user_id': None,
1232
+ 'name': None,
1233
+ 'status': None,
1234
+ }
1235
+ self.volumes_mock.list.assert_called_once_with(
1236
+ search_opts=search_opts,
1237
+ marker=None,
1238
+ limit=None,
1239
+ )
1240
+
1241
+ self.assertEqual(self.columns, columns)
1242
+
1243
+ datalist = (
1244
+ (
1245
+ self.mock_volume.id,
1246
+ self.mock_volume.name,
1247
+ self.mock_volume.status,
1248
+ self.mock_volume.size,
1249
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1250
+ ),
1251
+ )
1252
+ self.assertCountEqual(datalist, tuple(data))
1253
+
1254
+ def test_volume_list_project(self):
1255
+ arglist = [
1256
+ '--project',
1257
+ self.project.name,
1258
+ ]
1259
+ verifylist = [
1260
+ ('project', self.project.name),
1261
+ ('long', False),
1262
+ ('all_projects', False),
1263
+ ('status', None),
1264
+ ('marker', None),
1265
+ ('limit', None),
1266
+ ]
1267
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1268
+
1269
+ columns, data = self.cmd.take_action(parsed_args)
1270
+
1271
+ search_opts = {
1272
+ 'all_tenants': True,
1273
+ 'project_id': self.project.id,
1274
+ 'user_id': None,
1275
+ 'name': None,
1276
+ 'status': None,
1277
+ }
1278
+ self.volumes_mock.list.assert_called_once_with(
1279
+ search_opts=search_opts,
1280
+ marker=None,
1281
+ limit=None,
1282
+ )
1283
+
1284
+ self.assertEqual(self.columns, columns)
1285
+
1286
+ datalist = (
1287
+ (
1288
+ self.mock_volume.id,
1289
+ self.mock_volume.name,
1290
+ self.mock_volume.status,
1291
+ self.mock_volume.size,
1292
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1293
+ ),
1294
+ )
1295
+ self.assertCountEqual(datalist, tuple(data))
1296
+
1297
+ def test_volume_list_project_domain(self):
1298
+ arglist = [
1299
+ '--project',
1300
+ self.project.name,
1301
+ '--project-domain',
1302
+ self.project.domain_id,
1303
+ ]
1304
+ verifylist = [
1305
+ ('project', self.project.name),
1306
+ ('project_domain', self.project.domain_id),
1307
+ ('long', False),
1308
+ ('all_projects', False),
1309
+ ('status', None),
1310
+ ('marker', None),
1311
+ ('limit', None),
1312
+ ]
1313
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1314
+
1315
+ columns, data = self.cmd.take_action(parsed_args)
1316
+
1317
+ search_opts = {
1318
+ 'all_tenants': True,
1319
+ 'project_id': self.project.id,
1320
+ 'user_id': None,
1321
+ 'name': None,
1322
+ 'status': None,
1323
+ }
1324
+ self.volumes_mock.list.assert_called_once_with(
1325
+ search_opts=search_opts,
1326
+ marker=None,
1327
+ limit=None,
1328
+ )
1329
+
1330
+ self.assertEqual(self.columns, columns)
1331
+
1332
+ datalist = (
1333
+ (
1334
+ self.mock_volume.id,
1335
+ self.mock_volume.name,
1336
+ self.mock_volume.status,
1337
+ self.mock_volume.size,
1338
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1339
+ ),
1340
+ )
1341
+ self.assertCountEqual(datalist, tuple(data))
1342
+
1343
+ def test_volume_list_user(self):
1344
+ arglist = [
1345
+ '--user',
1346
+ self.user.name,
1347
+ ]
1348
+ verifylist = [
1349
+ ('user', self.user.name),
1350
+ ('long', False),
1351
+ ('all_projects', False),
1352
+ ('status', None),
1353
+ ('marker', None),
1354
+ ('limit', None),
1355
+ ]
1356
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1357
+
1358
+ columns, data = self.cmd.take_action(parsed_args)
1359
+
1360
+ search_opts = {
1361
+ 'all_tenants': False,
1362
+ 'project_id': None,
1363
+ 'user_id': self.user.id,
1364
+ 'name': None,
1365
+ 'status': None,
1366
+ }
1367
+ self.volumes_mock.list.assert_called_once_with(
1368
+ search_opts=search_opts,
1369
+ marker=None,
1370
+ limit=None,
1371
+ )
1372
+ self.assertEqual(self.columns, columns)
1373
+
1374
+ datalist = (
1375
+ (
1376
+ self.mock_volume.id,
1377
+ self.mock_volume.name,
1378
+ self.mock_volume.status,
1379
+ self.mock_volume.size,
1380
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1381
+ ),
1382
+ )
1383
+ self.assertCountEqual(datalist, tuple(data))
1384
+
1385
+ def test_volume_list_user_domain(self):
1386
+ arglist = [
1387
+ '--user',
1388
+ self.user.name,
1389
+ '--user-domain',
1390
+ self.user.domain_id,
1391
+ ]
1392
+ verifylist = [
1393
+ ('user', self.user.name),
1394
+ ('user_domain', self.user.domain_id),
1395
+ ('long', False),
1396
+ ('all_projects', False),
1397
+ ('status', None),
1398
+ ('marker', None),
1399
+ ('limit', None),
1400
+ ]
1401
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1402
+
1403
+ columns, data = self.cmd.take_action(parsed_args)
1404
+
1405
+ search_opts = {
1406
+ 'all_tenants': False,
1407
+ 'project_id': None,
1408
+ 'user_id': self.user.id,
1409
+ 'name': None,
1410
+ 'status': None,
1411
+ }
1412
+ self.volumes_mock.list.assert_called_once_with(
1413
+ search_opts=search_opts,
1414
+ marker=None,
1415
+ limit=None,
1416
+ )
1417
+
1418
+ self.assertEqual(self.columns, columns)
1419
+
1420
+ datalist = (
1421
+ (
1422
+ self.mock_volume.id,
1423
+ self.mock_volume.name,
1424
+ self.mock_volume.status,
1425
+ self.mock_volume.size,
1426
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1427
+ ),
1428
+ )
1429
+ self.assertCountEqual(datalist, tuple(data))
1430
+
1431
+ def test_volume_list_name(self):
1432
+ arglist = [
1433
+ '--name',
1434
+ self.mock_volume.name,
1435
+ ]
1436
+ verifylist = [
1437
+ ('long', False),
1438
+ ('all_projects', False),
1439
+ ('name', self.mock_volume.name),
1440
+ ('status', None),
1441
+ ('marker', None),
1442
+ ('limit', None),
1443
+ ]
1444
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1445
+
1446
+ columns, data = self.cmd.take_action(parsed_args)
1447
+
1448
+ search_opts = {
1449
+ 'all_tenants': False,
1450
+ 'project_id': None,
1451
+ 'user_id': None,
1452
+ 'name': self.mock_volume.name,
1453
+ 'status': None,
1454
+ }
1455
+ self.volumes_mock.list.assert_called_once_with(
1456
+ search_opts=search_opts,
1457
+ marker=None,
1458
+ limit=None,
1459
+ )
1460
+
1461
+ self.assertEqual(self.columns, columns)
1462
+
1463
+ datalist = (
1464
+ (
1465
+ self.mock_volume.id,
1466
+ self.mock_volume.name,
1467
+ self.mock_volume.status,
1468
+ self.mock_volume.size,
1469
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1470
+ ),
1471
+ )
1472
+ self.assertCountEqual(datalist, tuple(data))
1473
+
1474
+ def test_volume_list_status(self):
1475
+ arglist = [
1476
+ '--status',
1477
+ self.mock_volume.status,
1478
+ ]
1479
+ verifylist = [
1480
+ ('long', False),
1481
+ ('all_projects', False),
1482
+ ('name', None),
1483
+ ('status', self.mock_volume.status),
1484
+ ('marker', None),
1485
+ ('limit', None),
1486
+ ]
1487
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1488
+
1489
+ columns, data = self.cmd.take_action(parsed_args)
17
1490
 
18
- from cinderclient import api_versions
19
- from openstack.block_storage.v3 import block_storage_summary as _summary
20
- from openstack.block_storage.v3 import snapshot as _snapshot
21
- from openstack.block_storage.v3 import volume as _volume
22
- from openstack.test import fakes as sdk_fakes
23
- from openstack import utils as sdk_utils
24
- from osc_lib.cli import format_columns
25
- from osc_lib import exceptions
1491
+ search_opts = {
1492
+ 'all_tenants': False,
1493
+ 'project_id': None,
1494
+ 'user_id': None,
1495
+ 'name': None,
1496
+ 'status': self.mock_volume.status,
1497
+ }
1498
+ self.volumes_mock.list.assert_called_once_with(
1499
+ search_opts=search_opts,
1500
+ marker=None,
1501
+ limit=None,
1502
+ )
26
1503
 
27
- from openstackclient.tests.unit.volume.v3 import fakes
28
- from openstackclient.volume.v3 import volume
1504
+ self.assertEqual(self.columns, columns)
1505
+
1506
+ datalist = (
1507
+ (
1508
+ self.mock_volume.id,
1509
+ self.mock_volume.name,
1510
+ self.mock_volume.status,
1511
+ self.mock_volume.size,
1512
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1513
+ ),
1514
+ )
1515
+ self.assertCountEqual(datalist, tuple(data))
1516
+
1517
+ def test_volume_list_all_projects(self):
1518
+ arglist = [
1519
+ '--all-projects',
1520
+ ]
1521
+ verifylist = [
1522
+ ('long', False),
1523
+ ('all_projects', True),
1524
+ ('name', None),
1525
+ ('status', None),
1526
+ ('marker', None),
1527
+ ('limit', None),
1528
+ ]
1529
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1530
+
1531
+ columns, data = self.cmd.take_action(parsed_args)
1532
+
1533
+ search_opts = {
1534
+ 'all_tenants': True,
1535
+ 'project_id': None,
1536
+ 'user_id': None,
1537
+ 'name': None,
1538
+ 'status': None,
1539
+ }
1540
+ self.volumes_mock.list.assert_called_once_with(
1541
+ search_opts=search_opts,
1542
+ marker=None,
1543
+ limit=None,
1544
+ )
1545
+
1546
+ self.assertEqual(self.columns, columns)
1547
+
1548
+ datalist = (
1549
+ (
1550
+ self.mock_volume.id,
1551
+ self.mock_volume.name,
1552
+ self.mock_volume.status,
1553
+ self.mock_volume.size,
1554
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1555
+ ),
1556
+ )
1557
+ self.assertCountEqual(datalist, tuple(data))
1558
+
1559
+ def test_volume_list_long(self):
1560
+ arglist = [
1561
+ '--long',
1562
+ ]
1563
+ verifylist = [
1564
+ ('long', True),
1565
+ ('all_projects', False),
1566
+ ('name', None),
1567
+ ('status', None),
1568
+ ('marker', None),
1569
+ ('limit', None),
1570
+ ]
1571
+
1572
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1573
+
1574
+ columns, data = self.cmd.take_action(parsed_args)
1575
+
1576
+ search_opts = {
1577
+ 'all_tenants': False,
1578
+ 'project_id': None,
1579
+ 'user_id': None,
1580
+ 'name': None,
1581
+ 'status': None,
1582
+ }
1583
+ self.volumes_mock.list.assert_called_once_with(
1584
+ search_opts=search_opts,
1585
+ marker=None,
1586
+ limit=None,
1587
+ )
1588
+
1589
+ collist = [
1590
+ 'ID',
1591
+ 'Name',
1592
+ 'Status',
1593
+ 'Size',
1594
+ 'Type',
1595
+ 'Bootable',
1596
+ 'Attached to',
1597
+ 'Properties',
1598
+ ]
1599
+ self.assertEqual(collist, columns)
1600
+
1601
+ datalist = (
1602
+ (
1603
+ self.mock_volume.id,
1604
+ self.mock_volume.name,
1605
+ self.mock_volume.status,
1606
+ self.mock_volume.size,
1607
+ self.mock_volume.volume_type,
1608
+ self.mock_volume.bootable,
1609
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1610
+ format_columns.DictColumn(self.mock_volume.metadata),
1611
+ ),
1612
+ )
1613
+ self.assertCountEqual(datalist, tuple(data))
1614
+
1615
+ def test_volume_list_with_marker_and_limit(self):
1616
+ arglist = [
1617
+ "--marker",
1618
+ self.mock_volume.id,
1619
+ "--limit",
1620
+ "2",
1621
+ ]
1622
+ verifylist = [
1623
+ ('long', False),
1624
+ ('all_projects', False),
1625
+ ('name', None),
1626
+ ('status', None),
1627
+ ('marker', self.mock_volume.id),
1628
+ ('limit', 2),
1629
+ ]
1630
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1631
+
1632
+ columns, data = self.cmd.take_action(parsed_args)
1633
+
1634
+ self.assertEqual(self.columns, columns)
1635
+
1636
+ datalist = (
1637
+ (
1638
+ self.mock_volume.id,
1639
+ self.mock_volume.name,
1640
+ self.mock_volume.status,
1641
+ self.mock_volume.size,
1642
+ volume.AttachmentsColumn(self.mock_volume.attachments),
1643
+ ),
1644
+ )
1645
+
1646
+ self.volumes_mock.list.assert_called_once_with(
1647
+ marker=self.mock_volume.id,
1648
+ limit=2,
1649
+ search_opts={
1650
+ 'status': None,
1651
+ 'project_id': None,
1652
+ 'user_id': None,
1653
+ 'name': None,
1654
+ 'all_tenants': False,
1655
+ },
1656
+ )
1657
+ self.assertCountEqual(datalist, tuple(data))
1658
+
1659
+ def test_volume_list_negative_limit(self):
1660
+ arglist = [
1661
+ "--limit",
1662
+ "-2",
1663
+ ]
1664
+ verifylist = [
1665
+ ("limit", -2),
1666
+ ]
1667
+ self.assertRaises(
1668
+ test_utils.ParserException,
1669
+ self.check_parser,
1670
+ self.cmd,
1671
+ arglist,
1672
+ verifylist,
1673
+ )
1674
+
1675
+ def test_volume_list_backward_compatibility(self):
1676
+ arglist = [
1677
+ '-c',
1678
+ 'Display Name',
1679
+ ]
1680
+ verifylist = [
1681
+ ('columns', ['Display Name']),
1682
+ ('long', False),
1683
+ ('all_projects', False),
1684
+ ('name', None),
1685
+ ('status', None),
1686
+ ('marker', None),
1687
+ ('limit', None),
1688
+ ]
1689
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1690
+
1691
+ columns, data = self.cmd.take_action(parsed_args)
1692
+
1693
+ search_opts = {
1694
+ 'all_tenants': False,
1695
+ 'project_id': None,
1696
+ 'user_id': None,
1697
+ 'name': None,
1698
+ 'status': None,
1699
+ }
1700
+ self.volumes_mock.list.assert_called_once_with(
1701
+ search_opts=search_opts,
1702
+ marker=None,
1703
+ limit=None,
1704
+ )
1705
+
1706
+ self.assertIn('Display Name', columns)
1707
+ self.assertNotIn('Name', columns)
1708
+
1709
+ for each_volume in data:
1710
+ self.assertIn(self.mock_volume.name, each_volume)
1711
+
1712
+
1713
+ class TestVolumeMigrate(volume_fakes.TestVolume):
1714
+ _volume = volume_fakes.create_one_volume()
1715
+
1716
+ def setUp(self):
1717
+ super().setUp()
1718
+
1719
+ self.volumes_mock = self.volume_client.volumes
1720
+ self.volumes_mock.reset_mock()
1721
+
1722
+ self.volumes_mock.get.return_value = self._volume
1723
+ self.volumes_mock.migrate_volume.return_value = None
1724
+ # Get the command object to test
1725
+ self.cmd = volume.MigrateVolume(self.app, None)
1726
+
1727
+ def test_volume_migrate(self):
1728
+ arglist = [
1729
+ "--host",
1730
+ "host@backend-name#pool",
1731
+ self._volume.id,
1732
+ ]
1733
+ verifylist = [
1734
+ ("force_host_copy", False),
1735
+ ("lock_volume", False),
1736
+ ("host", "host@backend-name#pool"),
1737
+ ("volume", self._volume.id),
1738
+ ]
1739
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1740
+
1741
+ result = self.cmd.take_action(parsed_args)
1742
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
1743
+ self.volumes_mock.migrate_volume.assert_called_once_with(
1744
+ self._volume.id, "host@backend-name#pool", False, False
1745
+ )
1746
+ self.assertIsNone(result)
1747
+
1748
+ def test_volume_migrate_with_option(self):
1749
+ arglist = [
1750
+ "--force-host-copy",
1751
+ "--lock-volume",
1752
+ "--host",
1753
+ "host@backend-name#pool",
1754
+ self._volume.id,
1755
+ ]
1756
+ verifylist = [
1757
+ ("force_host_copy", True),
1758
+ ("lock_volume", True),
1759
+ ("host", "host@backend-name#pool"),
1760
+ ("volume", self._volume.id),
1761
+ ]
1762
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1763
+
1764
+ result = self.cmd.take_action(parsed_args)
1765
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
1766
+ self.volumes_mock.migrate_volume.assert_called_once_with(
1767
+ self._volume.id, "host@backend-name#pool", True, True
1768
+ )
1769
+ self.assertIsNone(result)
1770
+
1771
+ def test_volume_migrate_without_host(self):
1772
+ arglist = [
1773
+ self._volume.id,
1774
+ ]
1775
+ verifylist = [
1776
+ ("force_host_copy", False),
1777
+ ("lock_volume", False),
1778
+ ("volume", self._volume.id),
1779
+ ]
1780
+
1781
+ self.assertRaises(
1782
+ test_utils.ParserException,
1783
+ self.check_parser,
1784
+ self.cmd,
1785
+ arglist,
1786
+ verifylist,
1787
+ )
1788
+
1789
+
1790
+ class TestVolumeSet(volume_fakes.TestVolume):
1791
+ volume_type = volume_fakes.create_one_volume_type()
1792
+
1793
+ def setUp(self):
1794
+ super().setUp()
1795
+
1796
+ self.volumes_mock = self.volume_client.volumes
1797
+ self.volumes_mock.reset_mock()
1798
+
1799
+ self.types_mock = self.volume_client.volume_types
1800
+ self.types_mock.reset_mock()
1801
+
1802
+ self.new_volume = volume_fakes.create_one_volume()
1803
+ self.volumes_mock.get.return_value = self.new_volume
1804
+ self.types_mock.get.return_value = self.volume_type
1805
+
1806
+ # Get the command object to test
1807
+ self.cmd = volume.SetVolume(self.app, None)
1808
+
1809
+ def test_volume_set_property(self):
1810
+ arglist = [
1811
+ '--property',
1812
+ 'a=b',
1813
+ '--property',
1814
+ 'c=d',
1815
+ self.new_volume.id,
1816
+ ]
1817
+ verifylist = [
1818
+ ('property', {'a': 'b', 'c': 'd'}),
1819
+ ('volume', self.new_volume.id),
1820
+ ('bootable', False),
1821
+ ('non_bootable', False),
1822
+ ]
1823
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1824
+
1825
+ self.cmd.take_action(parsed_args)
1826
+ self.volumes_mock.set_metadata.assert_called_with(
1827
+ self.new_volume.id, parsed_args.property
1828
+ )
1829
+
1830
+ def test_volume_set_image_property(self):
1831
+ arglist = [
1832
+ '--image-property',
1833
+ 'Alpha=a',
1834
+ '--image-property',
1835
+ 'Beta=b',
1836
+ self.new_volume.id,
1837
+ ]
1838
+ verifylist = [
1839
+ ('image_property', {'Alpha': 'a', 'Beta': 'b'}),
1840
+ ('volume', self.new_volume.id),
1841
+ ('bootable', False),
1842
+ ('non_bootable', False),
1843
+ ]
1844
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1845
+
1846
+ # In base command class ShowOne in cliff, abstract method take_action()
1847
+ # returns nothing
1848
+ self.cmd.take_action(parsed_args)
1849
+ self.volumes_mock.set_image_metadata.assert_called_with(
1850
+ self.new_volume.id, parsed_args.image_property
1851
+ )
1852
+
1853
+ def test_volume_set_state(self):
1854
+ arglist = ['--state', 'error', self.new_volume.id]
1855
+ verifylist = [
1856
+ ('read_only', False),
1857
+ ('read_write', False),
1858
+ ('state', 'error'),
1859
+ ('volume', self.new_volume.id),
1860
+ ]
1861
+
1862
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1863
+
1864
+ result = self.cmd.take_action(parsed_args)
1865
+ self.volumes_mock.reset_state.assert_called_with(
1866
+ self.new_volume.id, 'error'
1867
+ )
1868
+ self.volumes_mock.update_readonly_flag.assert_not_called()
1869
+ self.assertIsNone(result)
1870
+
1871
+ def test_volume_set_state_failed(self):
1872
+ self.volumes_mock.reset_state.side_effect = exceptions.CommandError()
1873
+ arglist = ['--state', 'error', self.new_volume.id]
1874
+ verifylist = [('state', 'error'), ('volume', self.new_volume.id)]
1875
+
1876
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1877
+ try:
1878
+ self.cmd.take_action(parsed_args)
1879
+ self.fail('CommandError should be raised.')
1880
+ except exceptions.CommandError as e:
1881
+ self.assertEqual(
1882
+ 'One or more of the set operations failed', str(e)
1883
+ )
1884
+ self.volumes_mock.reset_state.assert_called_with(
1885
+ self.new_volume.id, 'error'
1886
+ )
1887
+
1888
+ def test_volume_set_attached(self):
1889
+ arglist = ['--attached', self.new_volume.id]
1890
+ verifylist = [
1891
+ ('attached', True),
1892
+ ('detached', False),
1893
+ ('volume', self.new_volume.id),
1894
+ ]
1895
+
1896
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1897
+
1898
+ result = self.cmd.take_action(parsed_args)
1899
+ self.volumes_mock.reset_state.assert_called_with(
1900
+ self.new_volume.id, attach_status='attached', state=None
1901
+ )
1902
+ self.assertIsNone(result)
1903
+
1904
+ def test_volume_set_detached(self):
1905
+ arglist = ['--detached', self.new_volume.id]
1906
+ verifylist = [
1907
+ ('attached', False),
1908
+ ('detached', True),
1909
+ ('volume', self.new_volume.id),
1910
+ ]
1911
+
1912
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1913
+
1914
+ result = self.cmd.take_action(parsed_args)
1915
+ self.volumes_mock.reset_state.assert_called_with(
1916
+ self.new_volume.id, attach_status='detached', state=None
1917
+ )
1918
+ self.assertIsNone(result)
1919
+
1920
+ def test_volume_set_bootable(self):
1921
+ arglist = [
1922
+ ['--bootable', self.new_volume.id],
1923
+ ['--non-bootable', self.new_volume.id],
1924
+ ]
1925
+ verifylist = [
1926
+ [
1927
+ ('bootable', True),
1928
+ ('non_bootable', False),
1929
+ ('volume', self.new_volume.id),
1930
+ ],
1931
+ [
1932
+ ('bootable', False),
1933
+ ('non_bootable', True),
1934
+ ('volume', self.new_volume.id),
1935
+ ],
1936
+ ]
1937
+ for index in range(len(arglist)):
1938
+ parsed_args = self.check_parser(
1939
+ self.cmd, arglist[index], verifylist[index]
1940
+ )
1941
+
1942
+ self.cmd.take_action(parsed_args)
1943
+ self.volumes_mock.set_bootable.assert_called_with(
1944
+ self.new_volume.id, verifylist[index][0][1]
1945
+ )
1946
+
1947
+ def test_volume_set_readonly(self):
1948
+ arglist = ['--read-only', self.new_volume.id]
1949
+ verifylist = [
1950
+ ('read_only', True),
1951
+ ('read_write', False),
1952
+ ('volume', self.new_volume.id),
1953
+ ]
1954
+
1955
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1956
+
1957
+ result = self.cmd.take_action(parsed_args)
1958
+ self.volumes_mock.update_readonly_flag.assert_called_once_with(
1959
+ self.new_volume.id, True
1960
+ )
1961
+ self.assertIsNone(result)
1962
+
1963
+ def test_volume_set_read_write(self):
1964
+ arglist = ['--read-write', self.new_volume.id]
1965
+ verifylist = [
1966
+ ('read_only', False),
1967
+ ('read_write', True),
1968
+ ('volume', self.new_volume.id),
1969
+ ]
1970
+
1971
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1972
+
1973
+ result = self.cmd.take_action(parsed_args)
1974
+ self.volumes_mock.update_readonly_flag.assert_called_once_with(
1975
+ self.new_volume.id, False
1976
+ )
1977
+ self.assertIsNone(result)
1978
+
1979
+ def test_volume_set_type(self):
1980
+ arglist = ['--type', self.volume_type.id, self.new_volume.id]
1981
+ verifylist = [
1982
+ ('retype_policy', None),
1983
+ ('type', self.volume_type.id),
1984
+ ('volume', self.new_volume.id),
1985
+ ]
1986
+
1987
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1988
+
1989
+ result = self.cmd.take_action(parsed_args)
1990
+ self.volumes_mock.retype.assert_called_once_with(
1991
+ self.new_volume.id, self.volume_type.id, 'never'
1992
+ )
1993
+ self.assertIsNone(result)
1994
+
1995
+ def test_volume_set_type_with_policy(self):
1996
+ arglist = [
1997
+ '--retype-policy',
1998
+ 'on-demand',
1999
+ '--type',
2000
+ self.volume_type.id,
2001
+ self.new_volume.id,
2002
+ ]
2003
+ verifylist = [
2004
+ ('retype_policy', 'on-demand'),
2005
+ ('type', self.volume_type.id),
2006
+ ('volume', self.new_volume.id),
2007
+ ]
2008
+
2009
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2010
+
2011
+ result = self.cmd.take_action(parsed_args)
2012
+ self.volumes_mock.retype.assert_called_once_with(
2013
+ self.new_volume.id, self.volume_type.id, 'on-demand'
2014
+ )
2015
+ self.assertIsNone(result)
2016
+
2017
+ @mock.patch.object(volume.LOG, 'warning')
2018
+ def test_volume_set_with_only_retype_policy(self, mock_warning):
2019
+ arglist = ['--retype-policy', 'on-demand', self.new_volume.id]
2020
+ verifylist = [
2021
+ ('retype_policy', 'on-demand'),
2022
+ ('volume', self.new_volume.id),
2023
+ ]
2024
+
2025
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2026
+
2027
+ result = self.cmd.take_action(parsed_args)
2028
+ self.volumes_mock.retype.assert_not_called()
2029
+ mock_warning.assert_called_with(
2030
+ "'--retype-policy' option will " "not work without '--type' option"
2031
+ )
2032
+ self.assertIsNone(result)
2033
+
2034
+
2035
+ class TestVolumeShow(volume_fakes.TestVolume):
2036
+ def setUp(self):
2037
+ super().setUp()
2038
+
2039
+ self.volumes_mock = self.volume_client.volumes
2040
+ self.volumes_mock.reset_mock()
2041
+
2042
+ self._volume = volume_fakes.create_one_volume()
2043
+ self.volumes_mock.get.return_value = self._volume
2044
+ # Get the command object to test
2045
+ self.cmd = volume.ShowVolume(self.app, None)
2046
+
2047
+ def test_volume_show(self):
2048
+ arglist = [self._volume.id]
2049
+ verifylist = [("volume", self._volume.id)]
2050
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2051
+
2052
+ columns, data = self.cmd.take_action(parsed_args)
2053
+ self.volumes_mock.get.assert_called_with(self._volume.id)
29
2054
 
2055
+ self.assertEqual(
2056
+ tuple(sorted(self._volume.keys())),
2057
+ columns,
2058
+ )
2059
+ self.assertTupleEqual(
2060
+ (
2061
+ self._volume.attachments,
2062
+ self._volume.availability_zone,
2063
+ self._volume.bootable,
2064
+ self._volume.description,
2065
+ self._volume.id,
2066
+ self._volume.name,
2067
+ format_columns.DictColumn(self._volume.metadata),
2068
+ self._volume.size,
2069
+ self._volume.snapshot_id,
2070
+ self._volume.status,
2071
+ self._volume.volume_type,
2072
+ ),
2073
+ data,
2074
+ )
30
2075
 
31
- class BaseVolumeTest(fakes.TestVolume):
2076
+
2077
+ class TestVolumeUnset(volume_fakes.TestVolume):
32
2078
  def setUp(self):
33
2079
  super().setUp()
34
2080
 
35
- patcher = mock.patch.object(
36
- sdk_utils, 'supports_microversion', return_value=True
2081
+ self.volumes_mock = self.volume_client.volumes
2082
+ self.volumes_mock.reset_mock()
2083
+
2084
+ self.new_volume = volume_fakes.create_one_volume()
2085
+ self.volumes_mock.get.return_value = self.new_volume
2086
+
2087
+ # Get the command object to set property
2088
+ self.cmd_set = volume.SetVolume(self.app, None)
2089
+
2090
+ # Get the command object to unset property
2091
+ self.cmd_unset = volume.UnsetVolume(self.app, None)
2092
+
2093
+ def test_volume_unset_image_property(self):
2094
+ # Arguments for setting image properties
2095
+ arglist = [
2096
+ '--image-property',
2097
+ 'Alpha=a',
2098
+ '--image-property',
2099
+ 'Beta=b',
2100
+ self.new_volume.id,
2101
+ ]
2102
+ verifylist = [
2103
+ ('image_property', {'Alpha': 'a', 'Beta': 'b'}),
2104
+ ('volume', self.new_volume.id),
2105
+ ]
2106
+ parsed_args = self.check_parser(self.cmd_set, arglist, verifylist)
2107
+
2108
+ # In base command class ShowOne in cliff, abstract method take_action()
2109
+ # returns nothing
2110
+ self.cmd_set.take_action(parsed_args)
2111
+
2112
+ # Arguments for unsetting image properties
2113
+ arglist_unset = [
2114
+ '--image-property',
2115
+ 'Alpha',
2116
+ self.new_volume.id,
2117
+ ]
2118
+ verifylist_unset = [
2119
+ ('image_property', ['Alpha']),
2120
+ ('volume', self.new_volume.id),
2121
+ ]
2122
+ parsed_args_unset = self.check_parser(
2123
+ self.cmd_unset, arglist_unset, verifylist_unset
2124
+ )
2125
+
2126
+ # In base command class ShowOne in cliff, abstract method take_action()
2127
+ # returns nothing
2128
+ self.cmd_unset.take_action(parsed_args_unset)
2129
+
2130
+ self.volumes_mock.delete_image_metadata.assert_called_with(
2131
+ self.new_volume.id, parsed_args_unset.image_property
37
2132
  )
38
- self.addCleanup(patcher.stop)
39
- self.supports_microversion_mock = patcher.start()
40
- self._set_mock_microversion(
41
- self.volume_client.api_version.get_string()
2133
+
2134
+ def test_volume_unset_image_property_fail(self):
2135
+ self.volumes_mock.delete_image_metadata.side_effect = (
2136
+ exceptions.CommandError()
42
2137
  )
2138
+ arglist = [
2139
+ '--image-property',
2140
+ 'Alpha',
2141
+ '--property',
2142
+ 'Beta',
2143
+ self.new_volume.id,
2144
+ ]
2145
+ verifylist = [
2146
+ ('image_property', ['Alpha']),
2147
+ ('property', ['Beta']),
2148
+ ('volume', self.new_volume.id),
2149
+ ]
2150
+ parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist)
43
2151
 
44
- def _set_mock_microversion(self, mock_v):
45
- """Set a specific microversion for the mock supports_microversion()."""
46
- self.supports_microversion_mock.reset_mock(return_value=True)
47
- self.supports_microversion_mock.side_effect = (
48
- lambda _, v: api_versions.APIVersion(v)
49
- <= api_versions.APIVersion(mock_v)
2152
+ try:
2153
+ self.cmd_unset.take_action(parsed_args)
2154
+ self.fail('CommandError should be raised.')
2155
+ except exceptions.CommandError as e:
2156
+ self.assertEqual(
2157
+ 'One or more of the unset operations failed', str(e)
2158
+ )
2159
+ self.volumes_mock.delete_image_metadata.assert_called_with(
2160
+ self.new_volume.id, parsed_args.image_property
2161
+ )
2162
+ self.volumes_mock.delete_metadata.assert_called_with(
2163
+ self.new_volume.id, parsed_args.property
50
2164
  )
51
2165
 
52
2166
 
53
- class TestVolumeSummary(BaseVolumeTest):
2167
+ class TestVolumeSummary(volume_fakes.TestVolume):
54
2168
  columns = [
55
2169
  'Total Count',
56
2170
  'Total Size',
@@ -72,7 +2186,7 @@ class TestVolumeSummary(BaseVolumeTest):
72
2186
  self.cmd = volume.VolumeSummary(self.app, None)
73
2187
 
74
2188
  def test_volume_summary(self):
75
- self._set_mock_microversion('3.12')
2189
+ self.set_volume_api_version('3.12')
76
2190
  arglist = [
77
2191
  '--all-projects',
78
2192
  ]
@@ -90,7 +2204,7 @@ class TestVolumeSummary(BaseVolumeTest):
90
2204
  datalist = (2, self.volume_a.size + self.volume_b.size)
91
2205
  self.assertCountEqual(datalist, tuple(data))
92
2206
 
93
- def test_volume_summary_pre_312(self):
2207
+ def test_volume_summary_pre_v312(self):
94
2208
  arglist = [
95
2209
  '--all-projects',
96
2210
  ]
@@ -107,7 +2221,7 @@ class TestVolumeSummary(BaseVolumeTest):
107
2221
  )
108
2222
 
109
2223
  def test_volume_summary_with_metadata(self):
110
- self._set_mock_microversion('3.36')
2224
+ self.set_volume_api_version('3.36')
111
2225
 
112
2226
  metadata = {**self.volume_a.metadata, **self.volume_b.metadata}
113
2227
  self.summary = sdk_fakes.generate_fake_resource(
@@ -143,7 +2257,7 @@ class TestVolumeSummary(BaseVolumeTest):
143
2257
  self.assertCountEqual(datalist, tuple(data))
144
2258
 
145
2259
 
146
- class TestVolumeRevertToSnapshot(BaseVolumeTest):
2260
+ class TestVolumeRevertToSnapshot(volume_fakes.TestVolume):
147
2261
  def setUp(self):
148
2262
  super().setUp()
149
2263
 
@@ -158,7 +2272,7 @@ class TestVolumeRevertToSnapshot(BaseVolumeTest):
158
2272
  # Get the command object to test
159
2273
  self.cmd = volume.VolumeRevertToSnapshot(self.app, None)
160
2274
 
161
- def test_volume_revert_to_snapshot_pre_340(self):
2275
+ def test_volume_revert_to_snapshot_pre_v340(self):
162
2276
  arglist = [
163
2277
  self.snapshot.id,
164
2278
  ]
@@ -175,7 +2289,7 @@ class TestVolumeRevertToSnapshot(BaseVolumeTest):
175
2289
  )
176
2290
 
177
2291
  def test_volume_revert_to_snapshot(self):
178
- self._set_mock_microversion('3.40')
2292
+ self.set_volume_api_version('3.40')
179
2293
  arglist = [
180
2294
  self.snapshot.id,
181
2295
  ]
@@ -198,3 +2312,33 @@ class TestVolumeRevertToSnapshot(BaseVolumeTest):
198
2312
  self.snapshot.id,
199
2313
  ignore_missing=False,
200
2314
  )
2315
+
2316
+
2317
+ class TestColumns(volume_fakes.TestVolume):
2318
+ def test_attachments_column_without_server_cache(self):
2319
+ _volume = volume_fakes.create_one_volume()
2320
+ server_id = _volume.attachments[0]['server_id']
2321
+ device = _volume.attachments[0]['device']
2322
+
2323
+ col = volume.AttachmentsColumn(_volume.attachments, {})
2324
+ self.assertEqual(
2325
+ f'Attached to {server_id} on {device} ',
2326
+ col.human_readable(),
2327
+ )
2328
+ self.assertEqual(_volume.attachments, col.machine_readable())
2329
+
2330
+ def test_attachments_column_with_server_cache(self):
2331
+ _volume = volume_fakes.create_one_volume()
2332
+
2333
+ server_id = _volume.attachments[0]['server_id']
2334
+ device = _volume.attachments[0]['device']
2335
+ fake_server = mock.Mock()
2336
+ fake_server.name = 'fake-server-name'
2337
+ server_cache = {server_id: fake_server}
2338
+
2339
+ col = volume.AttachmentsColumn(_volume.attachments, server_cache)
2340
+ self.assertEqual(
2341
+ 'Attached to {} on {} '.format('fake-server-name', device),
2342
+ col.human_readable(),
2343
+ )
2344
+ self.assertEqual(_volume.attachments, col.machine_readable())