python-openstackclient 10.0.0__py3-none-any.whl → 10.1.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 (203) hide show
  1. openstackclient/common/availability_zone.py +1 -1
  2. openstackclient/common/module.py +21 -27
  3. openstackclient/common/pagination.py +42 -4
  4. openstackclient/common/project_cleanup.py +1 -2
  5. openstackclient/common/quota.py +9 -5
  6. openstackclient/compute/v2/flavor.py +3 -1
  7. openstackclient/compute/v2/hypervisor.py +2 -0
  8. openstackclient/compute/v2/keypair.py +6 -2
  9. openstackclient/compute/v2/server.py +21 -12
  10. openstackclient/compute/v2/server_event.py +8 -1
  11. openstackclient/compute/v2/server_group.py +2 -0
  12. openstackclient/compute/v2/server_migration.py +3 -0
  13. openstackclient/compute/v2/server_volume.py +3 -1
  14. openstackclient/compute/v2/service.py +3 -1
  15. openstackclient/compute/v2/usage.py +2 -2
  16. openstackclient/identity/common.py +5 -1
  17. openstackclient/identity/v3/access_rule.py +6 -0
  18. openstackclient/identity/v3/application_credential.py +10 -3
  19. openstackclient/identity/v3/credential.py +4 -2
  20. openstackclient/identity/v3/domain.py +4 -2
  21. openstackclient/identity/v3/endpoint.py +57 -45
  22. openstackclient/identity/v3/federation_protocol.py +7 -5
  23. openstackclient/identity/v3/group.py +11 -10
  24. openstackclient/identity/v3/identity_provider.py +4 -1
  25. openstackclient/identity/v3/limit.py +5 -2
  26. openstackclient/identity/v3/mapping.py +36 -19
  27. openstackclient/identity/v3/project.py +18 -5
  28. openstackclient/identity/v3/region.py +4 -2
  29. openstackclient/identity/v3/registered_limit.py +3 -2
  30. openstackclient/identity/v3/role.py +2 -1
  31. openstackclient/identity/v3/role_assignment.py +3 -2
  32. openstackclient/identity/v3/service.py +4 -2
  33. openstackclient/identity/v3/service_provider.py +4 -2
  34. openstackclient/identity/v3/trust.py +8 -5
  35. openstackclient/identity/v3/user.py +38 -11
  36. openstackclient/image/v2/cache.py +2 -2
  37. openstackclient/image/v2/image.py +15 -9
  38. openstackclient/image/v2/metadef_namespaces.py +11 -10
  39. openstackclient/image/v2/metadef_objects.py +5 -5
  40. openstackclient/image/v2/metadef_properties.py +7 -4
  41. openstackclient/image/v2/task.py +11 -22
  42. openstackclient/network/utils.py +0 -41
  43. openstackclient/network/v2/address_group.py +13 -1
  44. openstackclient/network/v2/address_scope.py +13 -8
  45. openstackclient/network/v2/bgpvpn/bgpvpn.py +33 -19
  46. openstackclient/network/v2/bgpvpn/network_association.py +25 -13
  47. openstackclient/network/v2/bgpvpn/port_association.py +35 -21
  48. openstackclient/network/v2/bgpvpn/router_association.py +27 -14
  49. openstackclient/network/v2/default_security_group_rule.py +14 -6
  50. openstackclient/network/v2/floating_ip.py +12 -4
  51. openstackclient/network/v2/floating_ip_port_forwarding.py +12 -2
  52. openstackclient/network/v2/fwaas/group.py +34 -1
  53. openstackclient/network/v2/fwaas/rule.py +39 -3
  54. openstackclient/network/v2/ip_availability.py +13 -4
  55. openstackclient/network/v2/l3_conntrack_helper.py +14 -1
  56. openstackclient/network/v2/local_ip.py +4 -1
  57. openstackclient/network/v2/local_ip_association.py +4 -1
  58. openstackclient/network/v2/ndp_proxy.py +4 -1
  59. openstackclient/network/v2/network.py +87 -20
  60. openstackclient/network/v2/network_agent.py +32 -10
  61. openstackclient/network/v2/network_auto_allocated_topology.py +6 -5
  62. openstackclient/network/v2/network_flavor.py +19 -6
  63. openstackclient/network/v2/network_flavor_profile.py +20 -6
  64. openstackclient/network/v2/network_meter.py +19 -6
  65. openstackclient/network/v2/network_meter_rule.py +20 -2
  66. openstackclient/network/v2/network_qos_policy.py +15 -7
  67. openstackclient/network/v2/network_qos_rule.py +16 -1
  68. openstackclient/network/v2/network_qos_rule_type.py +16 -5
  69. openstackclient/network/v2/network_rbac.py +12 -5
  70. openstackclient/network/v2/network_segment.py +13 -1
  71. openstackclient/network/v2/network_segment_range.py +15 -3
  72. openstackclient/network/v2/network_trunk.py +4 -1
  73. openstackclient/network/v2/port.py +88 -12
  74. openstackclient/network/v2/router.py +27 -16
  75. openstackclient/network/v2/security_group.py +18 -49
  76. openstackclient/network/v2/security_group_rule.py +18 -5
  77. openstackclient/network/v2/subnet.py +15 -7
  78. openstackclient/network/v2/subnet_pool.py +13 -8
  79. openstackclient/network/v2/taas/tap_flow.py +13 -3
  80. openstackclient/network/v2/taas/tap_mirror.py +7 -4
  81. openstackclient/network/v2/taas/tap_service.py +4 -1
  82. openstackclient/object/v1/container.py +3 -1
  83. openstackclient/object/v1/object.py +3 -1
  84. openstackclient/tests/functional/identity/v3/common.py +34 -0
  85. openstackclient/tests/functional/identity/v3/test_application_credential.py +1 -1
  86. openstackclient/tests/functional/identity/v3/test_mapping.py +81 -0
  87. openstackclient/tests/functional/volume/v3/test_volume_group.py +163 -0
  88. openstackclient/tests/unit/common/test_limits.py +1 -1
  89. openstackclient/tests/unit/common/test_module.py +77 -44
  90. openstackclient/tests/unit/common/test_quota.py +9 -0
  91. openstackclient/tests/unit/compute/v2/fakes.py +1 -57
  92. openstackclient/tests/unit/compute/v2/test_agent.py +4 -4
  93. openstackclient/tests/unit/compute/v2/test_aggregate.py +1 -1
  94. openstackclient/tests/unit/compute/v2/test_console.py +2 -2
  95. openstackclient/tests/unit/compute/v2/test_console_connection.py +1 -1
  96. openstackclient/tests/unit/compute/v2/test_flavor.py +1 -1
  97. openstackclient/tests/unit/compute/v2/test_host.py +3 -3
  98. openstackclient/tests/unit/compute/v2/test_hypervisor.py +2 -2
  99. openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +1 -1
  100. openstackclient/tests/unit/compute/v2/test_keypair.py +1 -1
  101. openstackclient/tests/unit/compute/v2/test_server.py +15 -15
  102. openstackclient/tests/unit/compute/v2/test_server_backup.py +1 -1
  103. openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
  104. openstackclient/tests/unit/compute/v2/test_server_group.py +1 -1
  105. openstackclient/tests/unit/compute/v2/test_server_image.py +1 -1
  106. openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
  107. openstackclient/tests/unit/compute/v2/test_server_share.py +4 -4
  108. openstackclient/tests/unit/compute/v2/test_server_volume.py +2 -2
  109. openstackclient/tests/unit/compute/v2/test_service.py +3 -3
  110. openstackclient/tests/unit/compute/v2/test_usage.py +1 -1
  111. openstackclient/tests/unit/identity/v2_0/fakes.py +3 -7
  112. openstackclient/tests/unit/identity/v2_0/test_endpoint.py +1 -1
  113. openstackclient/tests/unit/identity/v2_0/test_project.py +1 -1
  114. openstackclient/tests/unit/identity/v2_0/test_role.py +1 -1
  115. openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +1 -1
  116. openstackclient/tests/unit/identity/v2_0/test_service.py +1 -1
  117. openstackclient/tests/unit/identity/v2_0/test_token.py +2 -2
  118. openstackclient/tests/unit/identity/v2_0/test_user.py +1 -1
  119. openstackclient/tests/unit/identity/v3/fakes.py +5 -38
  120. openstackclient/tests/unit/identity/v3/test_access_rule.py +3 -3
  121. openstackclient/tests/unit/identity/v3/test_application_credential.py +4 -4
  122. openstackclient/tests/unit/identity/v3/test_credential.py +5 -5
  123. openstackclient/tests/unit/identity/v3/test_domain.py +5 -5
  124. openstackclient/tests/unit/identity/v3/test_endpoint.py +6 -6
  125. openstackclient/tests/unit/identity/v3/test_endpoint_group.py +1 -1
  126. openstackclient/tests/unit/identity/v3/test_group.py +8 -8
  127. openstackclient/tests/unit/identity/v3/test_implied_role.py +1 -1
  128. openstackclient/tests/unit/identity/v3/test_limit.py +5 -5
  129. openstackclient/tests/unit/identity/v3/test_mappings.py +163 -79
  130. openstackclient/tests/unit/identity/v3/test_project.py +28 -5
  131. openstackclient/tests/unit/identity/v3/test_protocol.py +3 -3
  132. openstackclient/tests/unit/identity/v3/test_region.py +5 -5
  133. openstackclient/tests/unit/identity/v3/test_registered_limit.py +5 -5
  134. openstackclient/tests/unit/identity/v3/test_role.py +8 -8
  135. openstackclient/tests/unit/identity/v3/test_role_assignment.py +1 -1
  136. openstackclient/tests/unit/identity/v3/test_service.py +5 -5
  137. openstackclient/tests/unit/identity/v3/test_token.py +2 -2
  138. openstackclient/tests/unit/identity/v3/test_trust.py +4 -4
  139. openstackclient/tests/unit/identity/v3/test_user.py +73 -6
  140. openstackclient/tests/unit/network/v2/fakes.py +5 -77
  141. openstackclient/tests/unit/network/v2/fwaas/test_group.py +28 -2
  142. openstackclient/tests/unit/network/v2/fwaas/test_rule.py +28 -3
  143. openstackclient/tests/unit/network/v2/test_address_group.py +24 -0
  144. openstackclient/tests/unit/network/v2/test_address_scope.py +24 -0
  145. openstackclient/tests/unit/network/v2/test_floating_ip.py +24 -0
  146. openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +24 -0
  147. openstackclient/tests/unit/network/v2/test_ip_availability.py +25 -0
  148. openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +29 -3
  149. openstackclient/tests/unit/network/v2/test_network.py +74 -12
  150. openstackclient/tests/unit/network/v2/test_network_agent.py +50 -1
  151. openstackclient/tests/unit/network/v2/test_network_flavor.py +24 -0
  152. openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +24 -0
  153. openstackclient/tests/unit/network/v2/test_network_meter.py +24 -0
  154. openstackclient/tests/unit/network/v2/test_network_qos_policy.py +24 -0
  155. openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +24 -0
  156. openstackclient/tests/unit/network/v2/test_network_rbac.py +24 -0
  157. openstackclient/tests/unit/network/v2/test_network_segment.py +24 -0
  158. openstackclient/tests/unit/network/v2/test_network_segment_range.py +24 -0
  159. openstackclient/tests/unit/network/v2/test_port.py +166 -0
  160. openstackclient/tests/unit/network/v2/test_router.py +28 -7
  161. openstackclient/tests/unit/network/v2/test_security_group.py +22 -0
  162. openstackclient/tests/unit/network/v2/test_security_group_rule.py +25 -0
  163. openstackclient/tests/unit/network/v2/test_subnet.py +28 -4
  164. openstackclient/tests/unit/network/v2/test_subnet_pool.py +24 -0
  165. openstackclient/tests/unit/volume/v2/fakes.py +20 -140
  166. openstackclient/tests/unit/volume/v2/test_volume_backup.py +5 -9
  167. openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +6 -0
  168. openstackclient/tests/unit/volume/v3/fakes.py +204 -100
  169. openstackclient/tests/unit/volume/v3/test_backup_record.py +114 -0
  170. openstackclient/tests/unit/volume/v3/test_consistency_group.py +720 -0
  171. openstackclient/tests/unit/volume/v3/test_consistency_group_snapshot.py +354 -0
  172. openstackclient/tests/unit/volume/v3/test_qos_specs.py +455 -0
  173. openstackclient/tests/unit/volume/v3/test_volume_attachment.py +2 -0
  174. openstackclient/tests/unit/volume/v3/test_volume_backend.py +158 -0
  175. openstackclient/tests/unit/volume/v3/test_volume_backup.py +5 -9
  176. openstackclient/tests/unit/volume/v3/test_volume_group_type.py +65 -0
  177. openstackclient/tests/unit/volume/v3/test_volume_host.py +115 -0
  178. openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +6 -0
  179. openstackclient/volume/v2/volume.py +4 -2
  180. openstackclient/volume/v2/volume_backup.py +2 -3
  181. openstackclient/volume/v2/volume_snapshot.py +3 -4
  182. openstackclient/volume/v3/backup_record.py +94 -0
  183. openstackclient/volume/v3/consistency_group.py +400 -0
  184. openstackclient/volume/v3/consistency_group_snapshot.py +225 -0
  185. openstackclient/volume/v3/qos_specs.py +389 -0
  186. openstackclient/volume/v3/volume.py +4 -2
  187. openstackclient/volume/v3/volume_attachment.py +5 -1
  188. openstackclient/volume/v3/volume_backend.py +130 -0
  189. openstackclient/volume/v3/volume_backup.py +2 -3
  190. openstackclient/volume/v3/volume_group_snapshot.py +4 -6
  191. openstackclient/volume/v3/volume_group_type.py +1 -1
  192. openstackclient/volume/v3/volume_host.py +74 -0
  193. openstackclient/volume/v3/volume_message.py +3 -1
  194. openstackclient/volume/v3/volume_snapshot.py +2 -1
  195. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/METADATA +3 -4
  196. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/RECORD +202 -188
  197. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/entry_points.txt +24 -24
  198. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/licenses/AUTHORS +5 -0
  199. python_openstackclient-10.1.0.dist-info/pbr.json +1 -0
  200. python_openstackclient-10.0.0.dist-info/pbr.json +0 -1
  201. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/WHEEL +0 -0
  202. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/licenses/LICENSE +0 -0
  203. {python_openstackclient-10.0.0.dist-info → python_openstackclient-10.1.0.dist-info}/top_level.txt +0 -0
@@ -130,7 +130,7 @@ class ListAvailabilityZone(command.Lister):
130
130
  data = list(compute_client.availability_zones(details=True))
131
131
  except sdk_exceptions.ForbiddenException: # policy doesn't allow
132
132
  try:
133
- data = compute_client.availability_zones(details=False)
133
+ data = list(compute_client.availability_zones(details=False))
134
134
  except Exception:
135
135
  raise
136
136
 
@@ -16,6 +16,7 @@
16
16
  """Module action implementation"""
17
17
 
18
18
  import argparse
19
+ import importlib.metadata
19
20
  import sys
20
21
  from collections.abc import Iterable, Sequence
21
22
  from typing import Any
@@ -93,36 +94,29 @@ class ListModule(command.ShowOne):
93
94
  ) -> tuple[Sequence[str], Iterable[Any]]:
94
95
  data = {}
95
96
  # Get module versions
96
- mods = sys.modules
97
- for k in mods.keys():
98
- k = k.split('.')[0]
97
+ package_distributions = importlib.metadata.packages_distributions()
98
+ for k in sys.modules.keys():
99
+ module = k.split('.')[0]
99
100
  # Skip private modules and the modules that had been added,
100
101
  # like: keystoneclient, keystoneclient.exceptions and
101
102
  # keystoneclient.auth
102
- if not k.startswith('_') and k not in data:
103
- # TODO(dtroyer): Need a better way to decide which modules to
104
- # show for the default (not --all) invocation.
105
- # It should be just the things we actually care
106
- # about like client and plugin modules...
107
- if (
108
- parsed_args.all
109
- or
110
- # Handle xxxclient and openstacksdk
111
- (k.endswith('client') or k == 'openstack')
112
- ):
113
- try:
114
- # NOTE(RuiChen): openstacksdk bug/1588823 exist,
115
- # no good way to add __version__ for
116
- # openstack module properly, hard code
117
- # looks bad, but openstacksdk module
118
- # information is important.
119
- if k == 'openstack':
120
- data[k] = mods[k].version.__version__
121
- else:
122
- data[k] = mods[k].__version__
123
- except Exception: # noqa: S110
124
- # Catch all exceptions, just skip it
125
- pass
103
+ if module.startswith('_') or module in data:
104
+ continue
105
+
106
+ # TODO(dtroyer): Need a better way to decide which modules to
107
+ # show for the default (not --all) invocation.
108
+ # It should be just the things we actually care
109
+ # about like client and plugin modules...
110
+ # Handle xxxclient and openstacksdk
111
+ if parsed_args.all or (
112
+ module.endswith('client') or module == 'openstack'
113
+ ):
114
+ try:
115
+ package = package_distributions[module][0]
116
+ data[module] = importlib.metadata.version(package)
117
+ except Exception: # noqa: S110
118
+ # Catch all exceptions, just skip it
119
+ pass
126
120
 
127
121
  col_headers, col_data = zip(*sorted(data.items()))
128
122
  return col_headers, col_data
@@ -21,8 +21,11 @@ from openstackclient.i18n import _
21
21
  # useful
22
22
 
23
23
 
24
+ # TODO(stephenfin): include_max_items should not be necessary once we need to
25
+ # migrate remaining users to openstacksdk
24
26
  def add_marker_pagination_option_to_parser(
25
27
  parser: argparse.ArgumentParser,
28
+ include_max_items: bool = True,
26
29
  ) -> None:
27
30
  """Add marker-based pagination options to the parser.
28
31
 
@@ -39,8 +42,11 @@ def add_marker_pagination_option_to_parser(
39
42
  type=int,
40
43
  action=parseractions.NonNegativeAction,
41
44
  help=_(
42
- 'The maximum number of entries to return. If the value exceeds '
43
- 'the server-defined maximum, then the maximum value will be used.'
45
+ 'The maximum number of entries to return per page. If the value '
46
+ 'exceeds the server-defined maximum, then the server-defined '
47
+ 'value will be used. Note that this controls the page size, not '
48
+ 'the total number of entries returned. Use --max-items to limit '
49
+ 'the total number of entries returned.'
44
50
  ),
45
51
  )
46
52
  parser.add_argument(
@@ -52,10 +58,26 @@ def add_marker_pagination_option_to_parser(
52
58
  'This should be a value that was returned in a previous request.'
53
59
  ),
54
60
  )
61
+ if include_max_items:
62
+ parser.add_argument(
63
+ '--max-items',
64
+ metavar='<max-items>',
65
+ type=int,
66
+ action=parseractions.NonNegativeAction,
67
+ default=None,
68
+ help=_(
69
+ 'The maximum number of entries to return in total, paging '
70
+ 'through multiple requests if needed. Use --limit to control '
71
+ 'the page size.'
72
+ ),
73
+ )
55
74
 
56
75
 
76
+ # TODO(stephenfin): include_max_items should not be necessary once we need to
77
+ # migrate remaining users to openstacksdk
57
78
  def add_offset_pagination_option_to_parser(
58
79
  parser: argparse.ArgumentParser,
80
+ include_max_items: bool = True,
59
81
  ) -> None:
60
82
  """Add offset-based pagination options to the parser.
61
83
 
@@ -71,8 +93,11 @@ def add_offset_pagination_option_to_parser(
71
93
  type=int,
72
94
  action=parseractions.NonNegativeAction,
73
95
  help=_(
74
- 'The maximum number of entries to return. If the value exceeds '
75
- 'the server-defined maximum, then the maximum value will be used.'
96
+ 'The maximum number of entries to return per page. If the value '
97
+ 'exceeds the server-defined maximum, then the maximum value will '
98
+ 'be used. Note that this controls the page size, not the total '
99
+ 'number of entries returned. Use --max-items to limit the total '
100
+ 'number of entries returned.'
76
101
  ),
77
102
  )
78
103
  parser.add_argument(
@@ -86,3 +111,16 @@ def add_offset_pagination_option_to_parser(
86
111
  'return.'
87
112
  ),
88
113
  )
114
+ if include_max_items:
115
+ parser.add_argument(
116
+ '--max-items',
117
+ metavar='<max-items>',
118
+ type=int,
119
+ action=parseractions.NonNegativeAction,
120
+ default=None,
121
+ help=_(
122
+ 'The maximum number of entries to return in total, paging '
123
+ 'through multiple requests if needed. Use --limit to control '
124
+ 'the page size.'
125
+ ),
126
+ )
@@ -105,8 +105,7 @@ class ProjectCleanup(command.Command):
105
105
  project = identity_client.find_project(
106
106
  name_or_id=parsed_args.project, ignore_missing=False
107
107
  )
108
- # FIXME(stephenfin): The type in SDK is wrong
109
- connection = connection.connect_as_project(project) # type: ignore
108
+ connection = connection.connect_as_project(project)
110
109
 
111
110
  if connection:
112
111
  status_queue: queue.Queue[Any] = queue.Queue()
@@ -51,6 +51,7 @@ VOLUME_QUOTAS = {
51
51
  'backups': 'backups',
52
52
  'backup_gigabytes': 'backup-gigabytes',
53
53
  'gigabytes': 'gigabytes',
54
+ 'groups': 'groups',
54
55
  'per_volume_gigabytes': 'per-volume-gigabytes',
55
56
  'snapshots': 'snapshots',
56
57
  'volumes': 'volumes',
@@ -70,6 +71,7 @@ NETWORK_QUOTAS = {
70
71
  'subnet': 'subnets',
71
72
  'port': 'ports',
72
73
  'router': 'routers',
74
+ 'router_route': 'router-routes',
73
75
  'rbac_policy': 'rbac-policies',
74
76
  'subnetpool': 'subnetpools',
75
77
  }
@@ -78,6 +80,7 @@ NETWORK_KEYS = [
78
80
  'floating_ips',
79
81
  'networks',
80
82
  'rbac_policies',
83
+ 'router_routes',
81
84
  'routers',
82
85
  'ports',
83
86
  'security_group_rules',
@@ -102,7 +105,7 @@ def _xform_get_quota(
102
105
  return res
103
106
 
104
107
 
105
- def get_project(app: Any, project: str | None) -> dict[str, str | None]:
108
+ def get_project(app: Any, project: str | None) -> dict[str, str]:
106
109
  if project is not None:
107
110
  identity_client = sdk_utils.ensure_service_version(
108
111
  app.client_manager.sdk_connection.identity, '3'
@@ -112,14 +115,11 @@ def get_project(app: Any, project: str | None) -> dict[str, str | None]:
112
115
  )
113
116
  project_id = found_project.id
114
117
  project_name = found_project.name
115
- elif app.client_manager.auth_ref:
118
+ else:
116
119
  # Get the project from the current auth
117
120
  auth_ref = app.client_manager.auth_ref
118
121
  project_id = auth_ref.project_id
119
122
  project_name = auth_ref.project_name
120
- else:
121
- project_id = None
122
- project_name = None
123
123
 
124
124
  return {
125
125
  'id': project_id,
@@ -376,6 +376,7 @@ class ListQuota(command.Lister):
376
376
  'backups',
377
377
  'backup_gigabytes',
378
378
  'gigabytes',
379
+ 'groups',
379
380
  'per_volume_gigabytes',
380
381
  'snapshots',
381
382
  'volumes',
@@ -385,6 +386,7 @@ class ListQuota(command.Lister):
385
386
  'Backups',
386
387
  'Backup Gigabytes',
387
388
  'Gigabytes',
389
+ 'Groups',
388
390
  'Per Volume Gigabytes',
389
391
  'Snapshots',
390
392
  'Volumes',
@@ -439,6 +441,7 @@ class ListQuota(command.Lister):
439
441
  'networks',
440
442
  'ports',
441
443
  'rbac_policies',
444
+ 'router_routes',
442
445
  'routers',
443
446
  'security_groups',
444
447
  'security_group_rules',
@@ -451,6 +454,7 @@ class ListQuota(command.Lister):
451
454
  'Networks',
452
455
  'Ports',
453
456
  'RBAC Policies',
457
+ 'Router Routes',
454
458
  'Routers',
455
459
  'Security Groups',
456
460
  'Security Group Rules',
@@ -339,6 +339,8 @@ class ListFlavor(command.Lister):
339
339
 
340
340
  if parsed_args.limit:
341
341
  query_attrs['limit'] = parsed_args.limit
342
+ if parsed_args.max_items is not None:
343
+ query_attrs['max_items'] = parsed_args.max_items
342
344
 
343
345
  if parsed_args.limit or parsed_args.marker:
344
346
  # User passed explicit pagination request, switch off SDK
@@ -351,7 +353,7 @@ class ListFlavor(command.Lister):
351
353
  if parsed_args.min_ram:
352
354
  query_attrs['min_ram'] = parsed_args.min_ram
353
355
 
354
- data = list(compute_client.flavors(**query_attrs))
356
+ data = list(compute_client.flavors(**query_attrs)) # type: ignore[arg-type]
355
357
  # Even if server supports 2.61 some policy might stop it sending us
356
358
  # extra_specs. So try to fetch them if they are absent
357
359
  for f in data:
@@ -122,6 +122,8 @@ class ListHypervisor(command.Lister):
122
122
  )
123
123
  raise exceptions.CommandError(msg)
124
124
  list_opts['limit'] = parsed_args.limit
125
+ if parsed_args.max_items is not None:
126
+ list_opts['max_items'] = parsed_args.max_items
125
127
 
126
128
  if parsed_args.matching:
127
129
  list_opts['hypervisor_hostname_pattern'] = parsed_args.matching
@@ -23,6 +23,7 @@ from typing import Any
23
23
 
24
24
  from cryptography.hazmat.primitives.asymmetric import ed25519
25
25
  from cryptography.hazmat.primitives import serialization
26
+ from openstack.compute.v2 import keypair as _keypair
26
27
  from openstack import resource
27
28
  from openstack import utils as sdk_utils
28
29
  from osc_lib import exceptions
@@ -336,7 +337,10 @@ class ListKeypair(command.Lister):
336
337
  raise exceptions.CommandError(msg)
337
338
 
338
339
  kwargs['limit'] = parsed_args.limit
340
+ if parsed_args.max_items is not None:
341
+ kwargs['max_items'] = parsed_args.max_items
339
342
 
343
+ data: list[_keypair.Keypair]
340
344
  if parsed_args.project:
341
345
  if not sdk_utils.supports_microversion(compute_client, '2.10'):
342
346
  msg = _(
@@ -386,9 +390,9 @@ class ListKeypair(command.Lister):
386
390
  )
387
391
  kwargs['user_id'] = user.id
388
392
 
389
- data = compute_client.keypairs(**kwargs)
393
+ data = list(compute_client.keypairs(**kwargs))
390
394
  else:
391
- data = compute_client.keypairs(**kwargs)
395
+ data = list(compute_client.keypairs(**kwargs))
392
396
 
393
397
  columns: tuple[str, ...] = ("Name", "Fingerprint")
394
398
 
@@ -2854,6 +2854,9 @@ class ListServer(command.Lister):
2854
2854
  search_opts['limit'] = parsed_args.limit
2855
2855
  search_opts['paginated'] = False
2856
2856
 
2857
+ if parsed_args.max_items is not None:
2858
+ search_opts['max_items'] = parsed_args.max_items
2859
+
2857
2860
  LOG.debug('search options: %s', search_opts)
2858
2861
 
2859
2862
  if search_opts['changes-before']:
@@ -3102,8 +3105,8 @@ class ListServer(command.Lister):
3102
3105
  else:
3103
3106
  try:
3104
3107
  flavors_list = compute_client.flavors(is_public=None)
3105
- for i in flavors_list:
3106
- flavors[i.id] = i
3108
+ for f in flavors_list:
3109
+ flavors[f.id] = f
3107
3110
  except Exception: # noqa: S110
3108
3111
  # retrieving flavor names is not crucial, so we swallow any
3109
3112
  # exceptions
@@ -3124,29 +3127,33 @@ class ListServer(command.Lister):
3124
3127
  if 'id' in s.image and s.image.id is not None:
3125
3128
  image = images.get(s.image['id'])
3126
3129
  if image:
3127
- s.image_name = image.name
3130
+ setattr(s, 'image_name', image.name)
3128
3131
  s.image_id = s.image['id']
3129
3132
  else:
3130
3133
  # NOTE(melwitt): An server booted from a volume will have no
3131
3134
  # image associated with it. We fill in the Image Name and ID
3132
3135
  # with "N/A (booted from volume)" to help users who want to be
3133
3136
  # able to grep for boot-from-volume servers when using the CLI.
3134
- s.image_name = IMAGE_STRING_FOR_BFV
3137
+ setattr(s, 'image_name', IMAGE_STRING_FOR_BFV)
3135
3138
  s.image_id = IMAGE_STRING_FOR_BFV
3136
3139
 
3137
3140
  if not sdk_utils.supports_microversion(compute_client, '2.47'):
3138
3141
  if s.flavor['id'] in flavors:
3139
- s.flavor_name = flavors[s.flavor['id']].name
3142
+ setattr(s, 'flavor_name', flavors[s.flavor['id']].name)
3140
3143
  s.flavor_id = s.flavor['id']
3141
3144
  else:
3142
- s.flavor_name = s.flavor['original_name']
3145
+ setattr(s, 'flavor_name', s.flavor['original_name'])
3143
3146
 
3144
3147
  # Add a list with security group name as attribute
3145
3148
  for s in data:
3146
3149
  if hasattr(s, 'security_groups') and s.security_groups is not None:
3147
- s.security_groups_name = [x["name"] for x in s.security_groups]
3150
+ setattr(
3151
+ s,
3152
+ 'security_groups_name',
3153
+ [x["name"] for x in s.security_groups],
3154
+ )
3148
3155
  else:
3149
- s.security_groups_name = []
3156
+ setattr(s, 'security_groups_name', [])
3150
3157
 
3151
3158
  # The host_status field contains the status of the compute host the
3152
3159
  # server is on. It is only returned by the API when the nova-api
@@ -4256,7 +4263,9 @@ volume from a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``."""
4256
4263
 
4257
4264
  def take_action(self, parsed_args: argparse.Namespace) -> None:
4258
4265
  compute_client = self.app.client_manager.compute
4259
- volume_client = self.app.client_manager.sdk_connection.volume
4266
+ volume_client = sdk_utils.ensure_service_version(
4267
+ self.app.client_manager.sdk_connection.volume, '3'
4268
+ )
4260
4269
 
4261
4270
  server = compute_client.find_server(
4262
4271
  parsed_args.server,
@@ -4268,8 +4277,8 @@ volume from a server with status ``SHELVED`` or ``SHELVED_OFFLOADED``."""
4268
4277
  )
4269
4278
 
4270
4279
  compute_client.delete_volume_attachment(
4271
- volume,
4272
4280
  server,
4281
+ volume,
4273
4282
  ignore_missing=False,
4274
4283
  )
4275
4284
 
@@ -4919,8 +4928,8 @@ information for the server."""
4919
4928
  )
4920
4929
 
4921
4930
  if parsed_args.diagnostics:
4922
- data = compute_client.get_server_diagnostics(server)
4923
- col_headers, col_data = zip(*sorted(data.items()))
4931
+ diagnostics = compute_client.get_server_diagnostics(server)
4932
+ col_headers, col_data = zip(*sorted(diagnostics.items()))
4924
4933
  return col_headers, col_data
4925
4934
 
4926
4935
  topology = None
@@ -23,6 +23,7 @@ import uuid
23
23
 
24
24
  from cliff import columns
25
25
  import iso8601
26
+ from openstack.compute.v2 import server_action as _server_action
26
27
  from openstack import exceptions as sdk_exceptions
27
28
  from openstack import utils as sdk_utils
28
29
  from osc_lib import exceptions
@@ -67,7 +68,8 @@ class ServerActionEventColumn(columns.FormattableColumn[Any]):
67
68
  objects as we'd like.
68
69
  """
69
70
 
70
- def _format_event(self, event: Any) -> dict[str, Any]:
71
+ @staticmethod
72
+ def _format_event(event: _server_action.ServerAction) -> dict[str, Any]:
71
73
  hidden_columns = ['id', 'name', 'location']
72
74
  _, columns = utils.get_osc_show_columns_for_sdk_resource(
73
75
  event, {}, hidden_columns
@@ -171,6 +173,9 @@ class ListServerEvent(command.Lister):
171
173
  kwargs['limit'] = parsed_args.limit
172
174
  kwargs['paginated'] = False
173
175
 
176
+ if parsed_args.max_items is not None:
177
+ kwargs['max_items'] = parsed_args.max_items
178
+
174
179
  if parsed_args.changes_since:
175
180
  if not sdk_utils.supports_microversion(compute_client, '2.58'):
176
181
  msg = _(
@@ -295,6 +300,8 @@ class ShowServerEvent(command.ShowOne):
295
300
  parsed_args.request_id,
296
301
  server_id,
297
302
  )
303
+ # this is a bug is SDK
304
+ assert server_action is not None
298
305
 
299
306
  column_headers, columns = _get_server_event_columns(
300
307
  server_action,
@@ -219,6 +219,8 @@ class ListServerGroup(command.Lister):
219
219
 
220
220
  if parsed_args.limit:
221
221
  kwargs['limit'] = parsed_args.limit
222
+ if parsed_args.max_items is not None:
223
+ kwargs['max_items'] = parsed_args.max_items
222
224
 
223
225
  data = compute_client.server_groups(**kwargs)
224
226
 
@@ -207,6 +207,9 @@ class ListMigration(command.Lister):
207
207
  search_opts['limit'] = parsed_args.limit
208
208
  search_opts['paginated'] = False
209
209
 
210
+ if parsed_args.max_items is not None:
211
+ search_opts['max_items'] = parsed_args.max_items
212
+
210
213
  if parsed_args.changes_since:
211
214
  if not sdk_utils.supports_microversion(compute_client, "2.59"):
212
215
  msg = _(
@@ -121,7 +121,9 @@ class SetServerVolume(command.Command):
121
121
 
122
122
  def take_action(self, parsed_args: argparse.Namespace) -> None:
123
123
  compute_client = self.app.client_manager.compute
124
- volume_client = self.app.client_manager.sdk_connection.volume
124
+ volume_client = sdk_utils.ensure_service_version(
125
+ self.app.client_manager.sdk_connection.volume, '3'
126
+ )
125
127
 
126
128
  if parsed_args.delete_on_termination is not None:
127
129
  if not sdk_utils.supports_microversion(compute_client, '2.85'):
@@ -290,8 +290,10 @@ class SetService(command.Command):
290
290
  msg = _('--os-compute-api-version 2.11 or later is required')
291
291
  raise exceptions.CommandError(msg)
292
292
  try:
293
+ # FIXME(stephenfin): This is broken between 2.11 and 2.53. What
294
+ # is the expected ID here?
293
295
  compute_client.update_service_forced_down(
294
- service_id,
296
+ service_id, # type: ignore[arg-type]
295
297
  parsed_args.host,
296
298
  parsed_args.service,
297
299
  force_down,
@@ -157,7 +157,7 @@ class ListUsage(command.Lister):
157
157
  )
158
158
 
159
159
  date_cli_format = "%Y-%m-%d"
160
- now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
160
+ now = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
161
161
 
162
162
  if parsed_args.start:
163
163
  start = datetime.datetime.strptime(
@@ -243,7 +243,7 @@ class ShowUsage(command.ShowOne):
243
243
  identity_client = self.app.client_manager.identity
244
244
  compute_client = self.app.client_manager.compute
245
245
  date_cli_format = "%Y-%m-%d"
246
- now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
246
+ now = datetime.datetime.now(datetime.UTC).replace(tzinfo=None)
247
247
 
248
248
  if parsed_args.start:
249
249
  start = datetime.datetime.strptime(
@@ -25,6 +25,8 @@ from keystoneclient.v3 import groups
25
25
  from keystoneclient.v3 import projects
26
26
  from keystoneclient.v3 import users
27
27
  from openstack import exceptions as sdk_exceptions
28
+ from openstack.identity import v3 as identity
29
+ from openstack.identity.v3 import service as _service
28
30
  from osc_lib import exceptions
29
31
  from osc_lib import utils
30
32
 
@@ -78,7 +80,9 @@ def find_service(identity_client: Any, name_type_or_id: str) -> Any:
78
80
  raise exceptions.CommandError(msg % name_type_or_id)
79
81
 
80
82
 
81
- def find_service_sdk(identity_client: Any, name_type_or_id: str) -> Any:
83
+ def find_service_sdk(
84
+ identity_client: identity.Proxy, name_type_or_id: str
85
+ ) -> _service.Service:
82
86
  """Find a service by id, name or type."""
83
87
 
84
88
  try:
@@ -55,6 +55,9 @@ class DeleteAccessRule(command.Command):
55
55
  # this will never happen
56
56
  raise exceptions.CommandError('invalid authentication info')
57
57
  user_id = auth.get_user_id(conn.session)
58
+ if user_id is None:
59
+ # this will never happen
60
+ raise exceptions.CommandError('invalid authentication info')
58
61
 
59
62
  errors = 0
60
63
  for ac in parsed_args.access_rule:
@@ -146,6 +149,9 @@ class ShowAccessRule(command.ShowOne):
146
149
  # this will never happen
147
150
  raise exceptions.CommandError('invalid authentication info')
148
151
  user_id = auth.get_user_id(conn.session)
152
+ if user_id is None:
153
+ # this will never happen
154
+ raise exceptions.CommandError('invalid authentication info')
149
155
 
150
156
  access_rule = identity_client.get_access_rule(
151
157
  user_id, parsed_args.access_rule
@@ -24,6 +24,9 @@ from typing import Any
24
24
  import uuid
25
25
 
26
26
  from cliff import columns as cliff_columns
27
+ from openstack.identity.v3 import (
28
+ application_credential as _application_credential,
29
+ )
27
30
  from openstack import utils as sdk_utils
28
31
  from osc_lib import exceptions
29
32
  from osc_lib import utils
@@ -43,8 +46,10 @@ class RolesColumn(cliff_columns.FormattableColumn[Any]):
43
46
 
44
47
 
45
48
  def _format_application_credential(
46
- application_credential: Any, *, include_secret: bool = False
47
- ) -> tuple[tuple[str, ...], Any]:
49
+ application_credential: _application_credential.ApplicationCredential,
50
+ *,
51
+ include_secret: bool = False,
52
+ ) -> tuple[tuple[str, ...], Iterable[Any]]:
48
53
  column_headers: tuple[str, ...] = (
49
54
  'ID',
50
55
  'Name',
@@ -78,7 +83,9 @@ def _format_application_credential(
78
83
 
79
84
 
80
85
  def _format_application_credentials(
81
- application_credentials: Any,
86
+ application_credentials: Iterable[
87
+ _application_credential.ApplicationCredential
88
+ ],
82
89
  ) -> tuple[tuple[str, ...], Any]:
83
90
  column_headers = (
84
91
  'ID',
@@ -20,6 +20,7 @@ from collections.abc import Iterable, Sequence
20
20
  import logging
21
21
  from typing import Any
22
22
 
23
+ from openstack.identity.v3 import credential as _credential
23
24
  from openstack import utils as sdk_utils
24
25
  from osc_lib import exceptions
25
26
  from osc_lib import utils
@@ -28,11 +29,12 @@ from openstackclient import command
28
29
  from openstackclient.i18n import _
29
30
  from openstackclient.identity import common
30
31
 
31
-
32
32
  LOG = logging.getLogger(__name__)
33
33
 
34
34
 
35
- def _format_credential(credential: Any) -> tuple[tuple[str, ...], Any]:
35
+ def _format_credential(
36
+ credential: _credential.Credential,
37
+ ) -> tuple[tuple[str, ...], Iterable[Any]]:
36
38
  columns = (
37
39
  'blob',
38
40
  'id',
@@ -21,6 +21,7 @@ import logging
21
21
  from typing import Any
22
22
 
23
23
  from openstack import exceptions as sdk_exceptions
24
+ from openstack.identity.v3 import domain as _domain
24
25
  from openstack import utils as sdk_utils
25
26
  from osc_lib import exceptions
26
27
  from osc_lib import utils
@@ -29,11 +30,12 @@ from openstackclient import command
29
30
  from openstackclient.i18n import _
30
31
  from openstackclient.identity import common
31
32
 
32
-
33
33
  LOG = logging.getLogger(__name__)
34
34
 
35
35
 
36
- def _format_domain(domain: Any) -> tuple[tuple[str, ...], Any]:
36
+ def _format_domain(
37
+ domain: _domain.Domain,
38
+ ) -> tuple[tuple[str, ...], tuple[Any, ...]]:
37
39
  columns = (
38
40
  'id',
39
41
  'name',