python-openstackclient 8.3.0__py3-none-any.whl → 10.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. openstackclient/__init__.py +2 -6
  2. openstackclient/api/api.py +41 -23
  3. openstackclient/api/compute_v2.py +44 -25
  4. openstackclient/api/object_store_v1.py +75 -97
  5. openstackclient/api/volume_v2.py +2 -1
  6. openstackclient/api/volume_v3.py +2 -1
  7. openstackclient/common/availability_zone.py +58 -42
  8. openstackclient/common/clientmanager.py +56 -29
  9. openstackclient/common/configuration.py +10 -3
  10. openstackclient/common/envvars.py +2 -2
  11. openstackclient/common/extension.py +14 -5
  12. openstackclient/common/limits.py +10 -5
  13. openstackclient/common/module.py +14 -6
  14. openstackclient/common/pagination.py +8 -2
  15. openstackclient/common/progressbar.py +7 -6
  16. openstackclient/common/project_cleanup.py +13 -7
  17. openstackclient/common/quota.py +126 -114
  18. openstackclient/common/versions.py +8 -2
  19. openstackclient/compute/client.py +7 -3
  20. openstackclient/compute/v2/agent.py +17 -10
  21. openstackclient/compute/v2/aggregate.py +36 -22
  22. openstackclient/compute/v2/console.py +14 -8
  23. openstackclient/compute/v2/console_connection.py +11 -3
  24. openstackclient/compute/v2/flavor.py +39 -21
  25. openstackclient/compute/v2/host.py +14 -6
  26. openstackclient/compute/v2/hypervisor.py +14 -5
  27. openstackclient/compute/v2/hypervisor_stats.py +10 -2
  28. openstackclient/compute/v2/keypair.py +29 -14
  29. openstackclient/compute/v2/server.py +251 -171
  30. openstackclient/compute/v2/server_backup.py +10 -4
  31. openstackclient/compute/v2/server_event.py +21 -12
  32. openstackclient/compute/v2/server_group.py +21 -11
  33. openstackclient/compute/v2/server_image.py +19 -10
  34. openstackclient/compute/v2/server_migration.py +24 -10
  35. openstackclient/compute/v2/server_share.py +274 -0
  36. openstackclient/compute/v2/server_volume.py +10 -4
  37. openstackclient/compute/v2/service.py +14 -7
  38. openstackclient/compute/v2/usage.py +26 -21
  39. openstackclient/identity/client.py +8 -3
  40. openstackclient/identity/common.py +103 -41
  41. openstackclient/identity/v2_0/catalog.py +14 -7
  42. openstackclient/identity/v2_0/ec2creds.py +21 -10
  43. openstackclient/identity/v2_0/endpoint.py +23 -11
  44. openstackclient/identity/v2_0/project.py +25 -14
  45. openstackclient/identity/v2_0/role.py +28 -14
  46. openstackclient/identity/v2_0/role_assignment.py +9 -3
  47. openstackclient/identity/v2_0/service.py +26 -12
  48. openstackclient/identity/v2_0/token.py +12 -5
  49. openstackclient/identity/v2_0/user.py +26 -15
  50. openstackclient/identity/v3/access_rule.py +26 -12
  51. openstackclient/identity/v3/application_credential.py +59 -24
  52. openstackclient/identity/v3/catalog.py +14 -7
  53. openstackclient/identity/v3/consumer.py +22 -11
  54. openstackclient/identity/v3/credential.py +36 -16
  55. openstackclient/identity/v3/domain.py +37 -18
  56. openstackclient/identity/v3/ec2creds.py +25 -12
  57. openstackclient/identity/v3/endpoint.py +42 -20
  58. openstackclient/identity/v3/endpoint_group.py +28 -17
  59. openstackclient/identity/v3/federation_protocol.py +71 -50
  60. openstackclient/identity/v3/group.py +55 -32
  61. openstackclient/identity/v3/identity_provider.py +92 -57
  62. openstackclient/identity/v3/implied_role.py +21 -9
  63. openstackclient/identity/v3/limit.py +115 -92
  64. openstackclient/identity/v3/mapping.py +26 -13
  65. openstackclient/identity/v3/policy.py +23 -12
  66. openstackclient/identity/v3/project.py +211 -122
  67. openstackclient/identity/v3/region.py +36 -16
  68. openstackclient/identity/v3/registered_limit.py +116 -109
  69. openstackclient/identity/v3/role.py +61 -31
  70. openstackclient/identity/v3/role_assignment.py +23 -6
  71. openstackclient/identity/v3/service.py +36 -16
  72. openstackclient/identity/v3/service_provider.py +37 -15
  73. openstackclient/identity/v3/tag.py +23 -17
  74. openstackclient/identity/v3/token.py +30 -14
  75. openstackclient/identity/v3/trust.py +32 -14
  76. openstackclient/identity/v3/unscoped_saml.py +10 -2
  77. openstackclient/identity/v3/user.py +49 -26
  78. openstackclient/image/client.py +7 -3
  79. openstackclient/image/v1/image.py +33 -26
  80. openstackclient/image/v2/cache.py +14 -9
  81. openstackclient/image/v2/image.py +76 -49
  82. openstackclient/image/v2/info.py +7 -1
  83. openstackclient/image/v2/metadef_namespaces.py +109 -13
  84. openstackclient/image/v2/metadef_objects.py +28 -15
  85. openstackclient/image/v2/metadef_properties.py +24 -13
  86. openstackclient/image/v2/metadef_resource_type_association.py +14 -7
  87. openstackclient/image/v2/metadef_resource_types.py +7 -1
  88. openstackclient/image/v2/task.py +15 -6
  89. openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po +7 -192
  90. openstackclient/network/client.py +7 -2
  91. openstackclient/network/common.py +16 -241
  92. openstackclient/network/utils.py +36 -22
  93. openstackclient/network/v2/address_group.py +27 -16
  94. openstackclient/network/v2/address_scope.py +24 -13
  95. openstackclient/network/v2/bgpvpn/bgpvpn.py +463 -0
  96. openstackclient/network/v2/bgpvpn/constants.py +30 -0
  97. openstackclient/network/v2/bgpvpn/network_association.py +214 -0
  98. openstackclient/network/v2/bgpvpn/port_association.py +490 -0
  99. openstackclient/network/v2/bgpvpn/router_association.py +288 -0
  100. openstackclient/network/v2/default_security_group_rule.py +19 -10
  101. openstackclient/network/v2/floating_ip.py +110 -159
  102. openstackclient/network/v2/floating_ip_port_forwarding.py +30 -18
  103. openstackclient/network/v2/fwaas/__init__.py +0 -0
  104. openstackclient/network/v2/fwaas/group.py +466 -0
  105. openstackclient/network/v2/fwaas/policy.py +518 -0
  106. openstackclient/network/v2/fwaas/rule.py +574 -0
  107. openstackclient/network/v2/ip_availability.py +13 -5
  108. openstackclient/network/v2/l3_conntrack_helper.py +22 -13
  109. openstackclient/network/v2/local_ip.py +24 -13
  110. openstackclient/network/v2/local_ip_association.py +14 -7
  111. openstackclient/network/v2/ndp_proxy.py +20 -11
  112. openstackclient/network/v2/network.py +129 -196
  113. openstackclient/network/v2/network_agent.py +46 -25
  114. openstackclient/network/v2/network_auto_allocated_topology.py +22 -11
  115. openstackclient/network/v2/network_flavor.py +27 -16
  116. openstackclient/network/v2/network_flavor_profile.py +23 -12
  117. openstackclient/network/v2/network_meter.py +21 -10
  118. openstackclient/network/v2/network_meter_rule.py +21 -11
  119. openstackclient/network/v2/network_qos_policy.py +25 -15
  120. openstackclient/network/v2/network_qos_rule.py +32 -17
  121. openstackclient/network/v2/network_qos_rule_type.py +13 -5
  122. openstackclient/network/v2/network_rbac.py +23 -12
  123. openstackclient/network/v2/network_segment.py +20 -11
  124. openstackclient/network/v2/network_segment_range.py +56 -29
  125. openstackclient/network/v2/network_service_provider.py +7 -1
  126. openstackclient/network/v2/network_trunk.py +38 -22
  127. openstackclient/network/v2/port.py +54 -29
  128. openstackclient/network/v2/router.py +75 -52
  129. openstackclient/network/v2/security_group.py +87 -157
  130. openstackclient/network/v2/security_group_rule.py +100 -280
  131. openstackclient/network/v2/subnet.py +49 -28
  132. openstackclient/network/v2/subnet_pool.py +30 -17
  133. openstackclient/network/v2/taas/tap_flow.py +22 -11
  134. openstackclient/network/v2/taas/tap_mirror.py +22 -11
  135. openstackclient/network/v2/taas/tap_service.py +23 -12
  136. openstackclient/object/client.py +7 -2
  137. openstackclient/object/v1/account.py +13 -6
  138. openstackclient/object/v1/container.py +25 -15
  139. openstackclient/object/v1/object.py +25 -15
  140. openstackclient/py.typed +0 -0
  141. openstackclient/shell.py +46 -10
  142. openstackclient/tests/functional/base.py +55 -20
  143. openstackclient/tests/functional/common/test_extension.py +4 -0
  144. openstackclient/tests/functional/common/test_quota.py +3 -1
  145. openstackclient/tests/functional/compute/v2/common.py +14 -13
  146. openstackclient/tests/functional/compute/v2/test_flavor.py +3 -1
  147. openstackclient/tests/functional/compute/v2/test_server.py +3 -0
  148. openstackclient/tests/functional/identity/v2/common.py +10 -6
  149. openstackclient/tests/functional/identity/v2/test_role.py +4 -4
  150. openstackclient/tests/functional/identity/v3/common.py +25 -19
  151. openstackclient/tests/functional/identity/v3/test_group.py +20 -20
  152. openstackclient/tests/functional/identity/v3/test_idp.py +3 -1
  153. openstackclient/tests/functional/identity/v3/test_limit.py +47 -0
  154. openstackclient/tests/functional/identity/v3/test_project.py +10 -10
  155. openstackclient/tests/functional/identity/v3/test_role.py +18 -18
  156. openstackclient/tests/functional/identity/v3/test_role_assignment.py +12 -12
  157. openstackclient/tests/functional/identity/v3/test_user.py +8 -8
  158. openstackclient/tests/functional/image/base.py +1 -6
  159. openstackclient/tests/functional/image/v2/test_metadef_objects.py +69 -0
  160. openstackclient/tests/functional/network/v2/common.py +5 -2
  161. openstackclient/tests/functional/network/v2/test_floating_ip.py +10 -4
  162. openstackclient/tests/functional/network/v2/test_ip_availability.py +4 -0
  163. openstackclient/tests/functional/network/v2/test_network_meter_rule.py +3 -2
  164. openstackclient/tests/functional/network/v2/test_network_segment.py +5 -0
  165. openstackclient/tests/functional/network/v2/test_subnet.py +13 -9
  166. openstackclient/tests/functional/object/v1/common.py +4 -0
  167. openstackclient/tests/functional/volume/v2/common.py +4 -0
  168. openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +27 -11
  169. openstackclient/tests/functional/volume/v2/test_volume_type.py +2 -2
  170. openstackclient/tests/functional/volume/v3/common.py +4 -0
  171. openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +56 -138
  172. openstackclient/tests/functional/volume/v3/test_volume_type.py +2 -2
  173. openstackclient/tests/unit/common/test_availability_zone.py +35 -49
  174. openstackclient/tests/unit/common/test_extension.py +2 -2
  175. openstackclient/tests/unit/common/test_module.py +12 -7
  176. openstackclient/tests/unit/common/test_project_cleanup.py +3 -1
  177. openstackclient/tests/unit/common/test_quota.py +62 -23
  178. openstackclient/tests/unit/compute/v2/fakes.py +25 -0
  179. openstackclient/tests/unit/compute/v2/test_flavor.py +28 -2
  180. openstackclient/tests/unit/compute/v2/test_keypair.py +6 -6
  181. openstackclient/tests/unit/compute/v2/test_server.py +17 -104
  182. openstackclient/tests/unit/compute/v2/test_server_share.py +287 -0
  183. openstackclient/tests/unit/identity/v3/fakes.py +3 -0
  184. openstackclient/tests/unit/identity/v3/test_group.py +4 -14
  185. openstackclient/tests/unit/identity/v3/test_identity_provider.py +303 -299
  186. openstackclient/tests/unit/identity/v3/test_limit.py +197 -145
  187. openstackclient/tests/unit/identity/v3/test_project.py +831 -512
  188. openstackclient/tests/unit/identity/v3/test_protocol.py +97 -88
  189. openstackclient/tests/unit/identity/v3/test_registered_limit.py +355 -220
  190. openstackclient/tests/unit/identity/v3/test_user.py +4 -4
  191. openstackclient/tests/unit/image/v2/test_image.py +16 -16
  192. openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +105 -6
  193. openstackclient/tests/unit/network/test_common.py +0 -155
  194. openstackclient/tests/unit/network/v2/bgpvpn/__init__.py +0 -0
  195. openstackclient/tests/unit/network/v2/bgpvpn/fakes.py +179 -0
  196. openstackclient/tests/unit/network/v2/bgpvpn/test_bgpvpn.py +584 -0
  197. openstackclient/tests/unit/network/v2/bgpvpn/test_network_association.py +285 -0
  198. openstackclient/tests/unit/network/v2/bgpvpn/test_port_association.py +384 -0
  199. openstackclient/tests/unit/network/v2/bgpvpn/test_router_association.py +297 -0
  200. openstackclient/tests/unit/network/v2/fwaas/__init__.py +0 -0
  201. openstackclient/tests/unit/network/v2/fwaas/test_group.py +897 -0
  202. openstackclient/tests/unit/network/v2/fwaas/test_policy.py +869 -0
  203. openstackclient/tests/unit/network/v2/fwaas/test_rule.py +980 -0
  204. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_flow.py → test_tap_flow.py} +18 -25
  205. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_mirror.py → test_tap_mirror.py} +19 -29
  206. openstackclient/tests/unit/network/v2/taas/{test_osc_tap_service.py → test_tap_service.py} +19 -29
  207. openstackclient/tests/unit/network/v2/test_address_group.py +2 -2
  208. openstackclient/tests/unit/network/v2/{test_floating_ip_network.py → test_floating_ip.py} +3 -2
  209. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +13 -13
  210. openstackclient/tests/unit/network/v2/test_network_agent.py +8 -4
  211. openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +3 -3
  212. openstackclient/tests/unit/network/v2/test_network_flavor.py +2 -2
  213. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +1 -1
  214. openstackclient/tests/unit/network/v2/test_network_qos_rule.py +2 -2
  215. openstackclient/tests/unit/network/v2/test_network_rbac.py +1 -1
  216. openstackclient/tests/unit/network/v2/test_network_segment.py +1 -1
  217. openstackclient/tests/unit/network/v2/test_network_segment_range.py +7 -10
  218. openstackclient/tests/unit/network/v2/test_network_trunk.py +1 -1
  219. openstackclient/tests/unit/network/v2/test_router.py +8 -9
  220. openstackclient/tests/unit/network/v2/{test_security_group_network.py → test_security_group.py} +1 -20
  221. openstackclient/tests/unit/network/v2/{test_security_group_rule_network.py → test_security_group_rule.py} +7 -41
  222. openstackclient/tests/unit/network/v2/test_subnet.py +2 -1
  223. openstackclient/tests/unit/network/v2/test_subnet_pool.py +2 -1
  224. openstackclient/tests/unit/object/v1/fakes.py +8 -7
  225. openstackclient/tests/unit/object/v1/test_container.py +65 -101
  226. openstackclient/tests/unit/object/v1/test_container_all.py +8 -1
  227. openstackclient/tests/unit/object/v1/test_object.py +44 -84
  228. openstackclient/tests/unit/object/v1/test_object_all.py +8 -1
  229. openstackclient/tests/unit/test_hacking.py +108 -0
  230. openstackclient/tests/unit/volume/v2/fakes.py +1 -0
  231. openstackclient/tests/unit/volume/v2/test_consistency_group.py +8 -2
  232. openstackclient/tests/unit/volume/v2/test_volume.py +7 -6
  233. openstackclient/tests/unit/volume/v2/test_volume_backup.py +1 -5
  234. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +2 -1
  235. openstackclient/tests/unit/volume/v2/test_volume_type.py +2 -4
  236. openstackclient/tests/unit/volume/v3/fakes.py +1 -0
  237. openstackclient/tests/unit/volume/v3/test_volume.py +94 -15
  238. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
  239. openstackclient/tests/unit/volume/v3/test_volume_backup.py +1 -5
  240. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +55 -1
  241. openstackclient/tests/unit/volume/v3/test_volume_type.py +2 -4
  242. openstackclient/volume/client.py +7 -3
  243. openstackclient/volume/v2/backup_record.py +15 -6
  244. openstackclient/volume/v2/consistency_group.py +37 -25
  245. openstackclient/volume/v2/consistency_group_snapshot.py +27 -12
  246. openstackclient/volume/v2/qos_specs.py +30 -19
  247. openstackclient/volume/v2/service.py +17 -6
  248. openstackclient/volume/v2/volume.py +69 -34
  249. openstackclient/volume/v2/volume_backend.py +19 -6
  250. openstackclient/volume/v2/volume_backup.py +48 -22
  251. openstackclient/volume/v2/volume_host.py +6 -4
  252. openstackclient/volume/v2/volume_snapshot.py +52 -26
  253. openstackclient/volume/v2/volume_transfer_request.py +33 -15
  254. openstackclient/volume/v2/volume_type.py +46 -27
  255. openstackclient/volume/v3/block_storage_cleanup.py +11 -3
  256. openstackclient/volume/v3/block_storage_cluster.py +19 -7
  257. openstackclient/volume/v3/block_storage_log_level.py +15 -6
  258. openstackclient/volume/v3/block_storage_manage.py +10 -4
  259. openstackclient/volume/v3/block_storage_resource_filter.py +17 -5
  260. openstackclient/volume/v3/service.py +16 -6
  261. openstackclient/volume/v3/volume.py +103 -46
  262. openstackclient/volume/v3/volume_attachment.py +43 -21
  263. openstackclient/volume/v3/volume_backup.py +55 -26
  264. openstackclient/volume/v3/volume_group.py +23 -13
  265. openstackclient/volume/v3/volume_group_snapshot.py +32 -13
  266. openstackclient/volume/v3/volume_group_type.py +26 -13
  267. openstackclient/volume/v3/volume_message.py +15 -7
  268. openstackclient/volume/v3/volume_snapshot.py +71 -34
  269. openstackclient/volume/v3/volume_transfer_request.py +33 -15
  270. openstackclient/volume/v3/volume_type.py +45 -27
  271. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/METADATA +6 -6
  272. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/RECORD +279 -267
  273. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/WHEEL +1 -1
  274. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/entry_points.txt +53 -1
  275. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/AUTHORS +9 -0
  276. python_openstackclient-10.0.0.dist-info/pbr.json +1 -0
  277. openstackclient/api/image_v1.py +0 -69
  278. openstackclient/api/image_v2.py +0 -79
  279. openstackclient/network/v2/floating_ip_pool.py +0 -38
  280. openstackclient/tests/functional/image/v1/test_image.py +0 -97
  281. openstackclient/tests/unit/api/test_image_v1.py +0 -96
  282. openstackclient/tests/unit/api/test_image_v2.py +0 -96
  283. openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +0 -248
  284. openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +0 -49
  285. openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +0 -39
  286. openstackclient/tests/unit/network/v2/test_network_compute.py +0 -404
  287. openstackclient/tests/unit/network/v2/test_security_group_compute.py +0 -392
  288. openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +0 -555
  289. python_openstackclient-8.3.0.dist-info/pbr.json +0 -1
  290. /openstackclient/{tests/functional/image/v1 → network/v2/bgpvpn}/__init__.py +0 -0
  291. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/licenses/LICENSE +0 -0
  292. {python_openstackclient-8.3.0.dist-info → python_openstackclient-10.0.0.dist-info}/top_level.txt +0 -0
@@ -15,9 +15,13 @@
15
15
 
16
16
  """Project action implementations"""
17
17
 
18
+ import argparse
19
+ from collections.abc import Iterable, Sequence
18
20
  import logging
21
+ from typing import Any
19
22
 
20
- from keystoneauth1 import exceptions as ks_exc
23
+ from openstack import exceptions as sdk_exc
24
+ from openstack import utils as sdk_utils
21
25
  from osc_lib.cli import parseractions
22
26
  from osc_lib import exceptions
23
27
  from osc_lib import utils
@@ -30,10 +34,25 @@ from openstackclient.identity.v3 import tag
30
34
  LOG = logging.getLogger(__name__)
31
35
 
32
36
 
37
+ def _format_project(project: Any) -> tuple[tuple[str, ...], Any]:
38
+ # NOTE(0weng): Projects allow unknown attributes in the body, so extract
39
+ # the column names separately.
40
+ (column_headers, columns) = utils.get_osc_show_columns_for_sdk_resource(
41
+ project,
42
+ {'is_enabled': 'enabled'},
43
+ ['links', 'location', 'parents_as_ids', 'subtree_as_ids'],
44
+ )
45
+
46
+ return (
47
+ column_headers,
48
+ utils.get_item_properties(project, columns),
49
+ )
50
+
51
+
33
52
  class CreateProject(command.ShowOne):
34
53
  _description = _("Create new project")
35
54
 
36
- def get_parser(self, prog_name):
55
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
37
56
  parser = super().get_parser(prog_name)
38
57
  parser.add_argument(
39
58
  'name',
@@ -89,23 +108,18 @@ class CreateProject(command.ShowOne):
89
108
  tag.add_tag_option_to_parser_for_create(parser, _('project'))
90
109
  return parser
91
110
 
92
- def take_action(self, parsed_args):
93
- identity_client = self.app.client_manager.identity
94
-
95
- domain = None
96
- if parsed_args.domain:
97
- domain = common.find_domain(identity_client, parsed_args.domain).id
98
-
99
- parent = None
100
- if parsed_args.parent:
101
- parent = utils.find_resource(
102
- identity_client.projects,
103
- parsed_args.parent,
104
- ).id
111
+ def take_action(
112
+ self, parsed_args: argparse.Namespace
113
+ ) -> tuple[Sequence[str], Iterable[Any]]:
114
+ identity_client = sdk_utils.ensure_service_version(
115
+ self.app.client_manager.sdk_connection.identity, '3'
116
+ )
105
117
 
106
118
  kwargs = {}
119
+
107
120
  if parsed_args.properties:
108
121
  kwargs = parsed_args.properties.copy()
122
+
109
123
  if 'is_domain' in kwargs.keys():
110
124
  if kwargs['is_domain'].lower() == "true":
111
125
  kwargs['is_domain'] = True
@@ -114,35 +128,55 @@ class CreateProject(command.ShowOne):
114
128
  elif kwargs['is_domain'].lower() == "none":
115
129
  kwargs['is_domain'] = None
116
130
 
117
- kwargs['tags'] = list(set(parsed_args.tags))
131
+ if parsed_args.description:
132
+ kwargs['description'] = parsed_args.description
133
+
134
+ if parsed_args.name:
135
+ kwargs['name'] = parsed_args.name
136
+
137
+ domain = None
138
+ if parsed_args.domain:
139
+ domain = common.find_domain_id_sdk(
140
+ identity_client, parsed_args.domain
141
+ )
142
+ kwargs['domain_id'] = domain
143
+
144
+ if parsed_args.parent:
145
+ kwargs['parent_id'] = common.find_project_id_sdk(
146
+ identity_client,
147
+ parsed_args.parent,
148
+ domain_name_or_id=domain,
149
+ )
150
+
151
+ kwargs['is_enabled'] = parsed_args.enabled
152
+
153
+ if parsed_args.tags:
154
+ kwargs['tags'] = list(set(parsed_args.tags))
118
155
 
119
- options = {}
120
156
  if parsed_args.immutable is not None:
121
- options['immutable'] = parsed_args.immutable
157
+ kwargs['options'] = {'immutable': parsed_args.immutable}
122
158
 
123
159
  try:
124
- project = identity_client.projects.create(
125
- name=parsed_args.name,
126
- domain=domain,
127
- parent=parent,
128
- description=parsed_args.description,
129
- enabled=parsed_args.enabled,
130
- options=options,
160
+ project = identity_client.create_project(
131
161
  **kwargs,
132
162
  )
133
- except ks_exc.Conflict:
163
+ except sdk_exc.ConflictException:
134
164
  if parsed_args.or_show:
135
- project = utils.find_resource(
136
- identity_client.projects,
137
- parsed_args.name,
138
- domain_id=domain,
139
- )
165
+ if parsed_args.domain:
166
+ project = identity_client.find_project(
167
+ parsed_args.name,
168
+ domain_id=domain,
169
+ ignore_missing=False,
170
+ )
171
+ else:
172
+ project = identity_client.find_project(
173
+ parsed_args.name, ignore_missing=False
174
+ )
140
175
  LOG.info(_('Returning existing project %s'), project.name)
141
176
  else:
142
177
  raise
143
178
 
144
- project._info.pop('links')
145
- return zip(*sorted(project._info.items()))
179
+ return _format_project(project)
146
180
 
147
181
 
148
182
  class DeleteProject(command.Command):
@@ -155,7 +189,7 @@ class DeleteProject(command.Command):
155
189
  "regardless."
156
190
  )
157
191
 
158
- def get_parser(self, prog_name):
192
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
159
193
  parser = super().get_parser(prog_name)
160
194
  parser.add_argument(
161
195
  'projects',
@@ -170,24 +204,22 @@ class DeleteProject(command.Command):
170
204
  )
171
205
  return parser
172
206
 
173
- def take_action(self, parsed_args):
174
- identity_client = self.app.client_manager.identity
207
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
208
+ identity_client = sdk_utils.ensure_service_version(
209
+ self.app.client_manager.sdk_connection.identity, '3'
210
+ )
175
211
 
176
- domain = None
177
- if parsed_args.domain:
178
- domain = common.find_domain(identity_client, parsed_args.domain)
179
212
  errors = 0
180
213
  for project in parsed_args.projects:
181
214
  try:
182
- if domain is not None:
183
- project_obj = utils.find_resource(
184
- identity_client.projects, project, domain_id=domain.id
185
- )
186
- else:
187
- project_obj = utils.find_resource(
188
- identity_client.projects, project
189
- )
190
- identity_client.projects.delete(project_obj.id)
215
+ project = common.find_project_id_sdk(
216
+ identity_client,
217
+ project,
218
+ domain_name_or_id=parsed_args.domain,
219
+ validate_actor_existence=True,
220
+ validate_domain_actor_existence=False,
221
+ )
222
+ identity_client.delete_project(project)
191
223
  except Exception as e:
192
224
  errors += 1
193
225
  LOG.error(
@@ -210,7 +242,7 @@ class DeleteProject(command.Command):
210
242
  class ListProject(command.Lister):
211
243
  _description = _("List projects")
212
244
 
213
- def get_parser(self, prog_name):
245
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
214
246
  parser = super().get_parser(prog_name)
215
247
  parser.add_argument(
216
248
  '--domain',
@@ -267,39 +299,51 @@ class ListProject(command.Lister):
267
299
  tag.add_tag_filtering_option_to_parser(parser, _('projects'))
268
300
  return parser
269
301
 
270
- def take_action(self, parsed_args):
271
- identity_client = self.app.client_manager.identity
272
- columns: tuple[str, ...] = ('ID', 'Name')
302
+ def take_action(
303
+ self, parsed_args: argparse.Namespace
304
+ ) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
305
+ identity_client = sdk_utils.ensure_service_version(
306
+ self.app.client_manager.sdk_connection.identity, '3'
307
+ )
308
+
309
+ column_headers: tuple[str, ...] = ('ID', 'Name')
310
+ if parsed_args.long:
311
+ column_headers += ('Domain ID', 'Description', 'Enabled')
312
+
313
+ columns: tuple[str, ...] = ('id', 'name')
273
314
  if parsed_args.long:
274
- columns += ('Domain ID', 'Description', 'Enabled')
315
+ columns += ('domain_id', 'description', 'is_enabled')
316
+
275
317
  kwargs = {}
276
318
 
277
319
  domain_id = None
278
320
  if parsed_args.domain:
279
- domain_id = common.find_domain(
321
+ domain_id = common.find_domain_id_sdk(
280
322
  identity_client, parsed_args.domain
281
- ).id
282
- kwargs['domain'] = domain_id
323
+ )
324
+ kwargs['domain_id'] = domain_id
283
325
 
284
326
  if parsed_args.parent:
285
- parent_id = common.find_project(
286
- identity_client, parsed_args.parent
287
- ).id
288
- kwargs['parent'] = parent_id
327
+ parent_id = common.find_project_id_sdk(
328
+ identity_client,
329
+ parsed_args.parent,
330
+ domain_name_or_id=domain_id,
331
+ )
332
+ kwargs['parent_id'] = parent_id
289
333
 
334
+ user = None
290
335
  if parsed_args.user:
291
336
  if parsed_args.domain:
292
- user_id = utils.find_resource(
293
- identity_client.users,
337
+ user = common.find_user_id_sdk(
338
+ identity_client,
294
339
  parsed_args.user,
295
- domain_id=domain_id,
296
- ).id
340
+ domain_name_or_id=domain_id,
341
+ )
297
342
  else:
298
- user_id = utils.find_resource(
299
- identity_client.users, parsed_args.user
300
- ).id
301
-
302
- kwargs['user'] = user_id
343
+ user = common.find_user_id_sdk(
344
+ identity_client,
345
+ parsed_args.user,
346
+ )
303
347
 
304
348
  if parsed_args.is_enabled is not None:
305
349
  kwargs['is_enabled'] = parsed_args.is_enabled
@@ -308,39 +352,36 @@ class ListProject(command.Lister):
308
352
 
309
353
  if parsed_args.my_projects:
310
354
  # NOTE(adriant): my-projects supersedes all the other filters.
311
- kwargs = {'user': self.app.client_manager.auth_ref.user_id}
355
+ kwargs = {}
356
+ user = self.app.client_manager.auth_ref.user_id
312
357
 
313
- try:
314
- data = identity_client.projects.list(**kwargs)
315
- except ks_exc.Forbidden:
316
- # NOTE(adriant): if no filters, assume a forbidden is non-admin
317
- # wanting their own project list.
318
- if not kwargs:
319
- user = self.app.client_manager.auth_ref.user_id
320
- data = identity_client.projects.list(user=user)
321
- else:
322
- raise
358
+ if user:
359
+ data = list(identity_client.user_projects(user, **kwargs))
360
+ else:
361
+ try:
362
+ data = list(identity_client.projects(**kwargs))
363
+ except sdk_exc.ForbiddenException:
364
+ # NOTE(adriant): if no filters, assume a forbidden is non-admin
365
+ # wanting their own project list.
366
+ if not kwargs:
367
+ user = self.app.client_manager.auth_ref.user_id
368
+ data = list(identity_client.user_projects(user))
369
+ else:
370
+ raise
323
371
 
324
372
  if parsed_args.sort:
325
- data = utils.sort_items(data, parsed_args.sort)
373
+ data = list(utils.sort_items(data, parsed_args.sort))
326
374
 
327
375
  return (
328
- columns,
329
- (
330
- utils.get_item_properties(
331
- s,
332
- columns,
333
- formatters={},
334
- )
335
- for s in data
336
- ),
376
+ column_headers,
377
+ (utils.get_item_properties(s, columns) for s in data),
337
378
  )
338
379
 
339
380
 
340
381
  class SetProject(command.Command):
341
382
  _description = _("Set project properties")
342
383
 
343
- def get_parser(self, prog_name):
384
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
344
385
  parser = super().get_parser(prog_name)
345
386
  parser.add_argument(
346
387
  'project',
@@ -391,11 +432,9 @@ class SetProject(command.Command):
391
432
  tag.add_tag_option_to_parser_for_set(parser, _('project'))
392
433
  return parser
393
434
 
394
- def take_action(self, parsed_args):
395
- identity_client = self.app.client_manager.identity
396
-
397
- project = common.find_project(
398
- identity_client, parsed_args.project, parsed_args.domain
435
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
436
+ identity_client = sdk_utils.ensure_service_version(
437
+ self.app.client_manager.sdk_connection.identity, '3'
399
438
  )
400
439
 
401
440
  kwargs = {}
@@ -409,15 +448,56 @@ class SetProject(command.Command):
409
448
  kwargs['options'] = {'immutable': parsed_args.immutable}
410
449
  if parsed_args.properties:
411
450
  kwargs.update(parsed_args.properties)
412
- tag.update_tags_in_args(parsed_args, project, kwargs)
413
451
 
414
- identity_client.projects.update(project.id, **kwargs)
452
+ if parsed_args.domain:
453
+ domain = common.find_domain_id_sdk(
454
+ identity_client,
455
+ parsed_args.domain,
456
+ validate_actor_existence=False,
457
+ )
458
+ project = identity_client.find_project(
459
+ parsed_args.project,
460
+ domain_id=domain,
461
+ ignore_missing=True,
462
+ )
463
+ else:
464
+ project = identity_client.find_project(
465
+ parsed_args.project,
466
+ ignore_missing=True,
467
+ )
468
+
469
+ if (
470
+ parsed_args.tags
471
+ or parsed_args.remove_tags
472
+ or parsed_args.clear_tags
473
+ ):
474
+ existing_tags = []
475
+ if project:
476
+ existing_tags = project.tags
477
+
478
+ if parsed_args.clear_tags:
479
+ kwargs['tags'] = []
480
+ else:
481
+ existing_tags_set = set(existing_tags)
482
+ if parsed_args.remove_tags:
483
+ tags = sorted(
484
+ existing_tags_set - set(parsed_args.remove_tags)
485
+ )
486
+ if parsed_args.tags:
487
+ tags = sorted(
488
+ existing_tags_set.union(set(parsed_args.tags))
489
+ )
490
+ kwargs['tags'] = tags
491
+
492
+ project_id = project.id if project else parsed_args.project
493
+
494
+ identity_client.update_project(project_id, **kwargs)
415
495
 
416
496
 
417
497
  class ShowProject(command.ShowOne):
418
498
  _description = _("Display project details")
419
499
 
420
- def get_parser(self, prog_name):
500
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
421
501
  parser = super().get_parser(prog_name)
422
502
  parser.add_argument(
423
503
  'project',
@@ -443,32 +523,41 @@ class ShowProject(command.ShowOne):
443
523
  )
444
524
  return parser
445
525
 
446
- def take_action(self, parsed_args):
447
- identity_client = self.app.client_manager.identity
448
-
449
- project_str = common._get_token_resource(
450
- identity_client, 'project', parsed_args.project, parsed_args.domain
526
+ def take_action(
527
+ self, parsed_args: argparse.Namespace
528
+ ) -> tuple[Sequence[str], Iterable[Any]]:
529
+ identity_client = sdk_utils.ensure_service_version(
530
+ self.app.client_manager.sdk_connection.identity, '3'
451
531
  )
452
532
 
533
+ kwargs: dict[str, Any] = {}
534
+
535
+ domain = None
453
536
  if parsed_args.domain:
454
- domain = common.find_domain(identity_client, parsed_args.domain)
455
- project = utils.find_resource(
456
- identity_client.projects, project_str, domain_id=domain.id
457
- )
458
- else:
459
- project = utils.find_resource(
460
- identity_client.projects, project_str
537
+ domain = common.find_domain_id_sdk(
538
+ identity_client, parsed_args.domain
461
539
  )
462
540
 
463
- if parsed_args.parents or parsed_args.children:
464
- # NOTE(RuiChen): utils.find_resource() can't pass kwargs,
465
- # if id query hit the result at first, so call
466
- # identity manager.get() with kwargs directly.
467
- project = identity_client.projects.get(
468
- project.id,
469
- parents_as_ids=parsed_args.parents,
470
- subtree_as_ids=parsed_args.children,
471
- )
541
+ kwargs['domain_id'] = domain
542
+
543
+ # Get project id first; otherwise, find_project() can't find
544
+ # parents/children if only project name was given
545
+ project = common.find_project_id_sdk(
546
+ identity_client,
547
+ parsed_args.project,
548
+ domain_name_or_id=domain,
549
+ validate_actor_existence=False,
550
+ validate_domain_actor_existence=False,
551
+ )
552
+
553
+ # Include these options as query parameters if they are provided
554
+ if parsed_args.parents:
555
+ kwargs['parents_as_ids'] = True
556
+ if parsed_args.children:
557
+ kwargs['subtree_as_ids'] = True
558
+
559
+ project_obj = identity_client.find_project(
560
+ project, **kwargs, ignore_missing=False
561
+ )
472
562
 
473
- project._info.pop('links')
474
- return zip(*sorted(project._info.items()))
563
+ return _format_project(project_obj)
@@ -13,8 +13,12 @@
13
13
 
14
14
  """Identity v3 Region action implementations"""
15
15
 
16
+ import argparse
17
+ from collections.abc import Iterable, Sequence
16
18
  import logging
19
+ from typing import Any
17
20
 
21
+ from openstack import utils as sdk_utils
18
22
  from osc_lib import exceptions
19
23
  from osc_lib import utils
20
24
 
@@ -25,7 +29,7 @@ from openstackclient.i18n import _
25
29
  LOG = logging.getLogger(__name__)
26
30
 
27
31
 
28
- def _format_region(region):
32
+ def _format_region(region: Any) -> tuple[tuple[str, ...], Any]:
29
33
  columns = ('id', 'description', 'parent_region_id')
30
34
  column_headers = ('region', 'description', 'parent_region')
31
35
  return (
@@ -37,7 +41,7 @@ def _format_region(region):
37
41
  class CreateRegion(command.ShowOne):
38
42
  _description = _("Create new region")
39
43
 
40
- def get_parser(self, prog_name):
44
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
41
45
  parser = super().get_parser(prog_name)
42
46
  # NOTE(stevemar): The API supports an optional region ID, but that
43
47
  # seems like poor UX, we will only support user-defined IDs.
@@ -58,8 +62,12 @@ class CreateRegion(command.ShowOne):
58
62
  )
59
63
  return parser
60
64
 
61
- def take_action(self, parsed_args):
62
- identity_client = self.app.client_manager.sdk_connection.identity
65
+ def take_action(
66
+ self, parsed_args: argparse.Namespace
67
+ ) -> tuple[Sequence[str], Iterable[Any]]:
68
+ identity_client = sdk_utils.ensure_service_version(
69
+ self.app.client_manager.sdk_connection.identity, '3'
70
+ )
63
71
 
64
72
  region = identity_client.create_region(
65
73
  id=parsed_args.region,
@@ -73,7 +81,7 @@ class CreateRegion(command.ShowOne):
73
81
  class DeleteRegion(command.Command):
74
82
  _description = _("Delete region(s)")
75
83
 
76
- def get_parser(self, prog_name):
84
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
77
85
  parser = super().get_parser(prog_name)
78
86
  parser.add_argument(
79
87
  'region',
@@ -83,8 +91,10 @@ class DeleteRegion(command.Command):
83
91
  )
84
92
  return parser
85
93
 
86
- def take_action(self, parsed_args):
87
- identity_client = self.app.client_manager.sdk_connection.identity
94
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
95
+ identity_client = sdk_utils.ensure_service_version(
96
+ self.app.client_manager.sdk_connection.identity, '3'
97
+ )
88
98
  result = 0
89
99
  for i in parsed_args.region:
90
100
  try:
@@ -108,7 +118,7 @@ class DeleteRegion(command.Command):
108
118
  class ListRegion(command.Lister):
109
119
  _description = _("List regions")
110
120
 
111
- def get_parser(self, prog_name):
121
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
112
122
  parser = super().get_parser(prog_name)
113
123
  parser.add_argument(
114
124
  '--parent-region',
@@ -117,8 +127,12 @@ class ListRegion(command.Lister):
117
127
  )
118
128
  return parser
119
129
 
120
- def take_action(self, parsed_args):
121
- identity_client = self.app.client_manager.sdk_connection.identity
130
+ def take_action(
131
+ self, parsed_args: argparse.Namespace
132
+ ) -> tuple[tuple[str, ...], Iterable[tuple[Any, ...]]]:
133
+ identity_client = sdk_utils.ensure_service_version(
134
+ self.app.client_manager.sdk_connection.identity, '3'
135
+ )
122
136
 
123
137
  kwargs = {}
124
138
  if parsed_args.parent_region:
@@ -144,7 +158,7 @@ class ListRegion(command.Lister):
144
158
  class SetRegion(command.Command):
145
159
  _description = _("Set region properties")
146
160
 
147
- def get_parser(self, prog_name):
161
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
148
162
  parser = super().get_parser(prog_name)
149
163
  parser.add_argument(
150
164
  'region',
@@ -163,8 +177,10 @@ class SetRegion(command.Command):
163
177
  )
164
178
  return parser
165
179
 
166
- def take_action(self, parsed_args):
167
- identity_client = self.app.client_manager.sdk_connection.identity
180
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
181
+ identity_client = sdk_utils.ensure_service_version(
182
+ self.app.client_manager.sdk_connection.identity, '3'
183
+ )
168
184
 
169
185
  kwargs = {}
170
186
  if parsed_args.description:
@@ -178,7 +194,7 @@ class SetRegion(command.Command):
178
194
  class ShowRegion(command.ShowOne):
179
195
  _description = _("Display region details")
180
196
 
181
- def get_parser(self, prog_name):
197
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
182
198
  parser = super().get_parser(prog_name)
183
199
  parser.add_argument(
184
200
  'region',
@@ -187,8 +203,12 @@ class ShowRegion(command.ShowOne):
187
203
  )
188
204
  return parser
189
205
 
190
- def take_action(self, parsed_args):
191
- identity_client = self.app.client_manager.sdk_connection.identity
206
+ def take_action(
207
+ self, parsed_args: argparse.Namespace
208
+ ) -> tuple[Sequence[str], Iterable[Any]]:
209
+ identity_client = sdk_utils.ensure_service_version(
210
+ self.app.client_manager.sdk_connection.identity, '3'
211
+ )
192
212
 
193
213
  region = identity_client.get_region(parsed_args.region)
194
214