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
@@ -0,0 +1,389 @@
1
+ # Copyright 2015 iWeb Technologies Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ # not use this file except in compliance with the License. You may obtain
5
+ # a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+ # License for the specific language governing permissions and limitations
13
+ # under the License.
14
+
15
+ """Volume v3 QoS action implementations"""
16
+
17
+ import argparse
18
+ import logging
19
+ from collections.abc import Iterable, Sequence
20
+ from typing import Any
21
+
22
+ from osc_lib.cli import format_columns
23
+ from osc_lib.cli import parseractions
24
+ from osc_lib import exceptions
25
+ from osc_lib import utils
26
+
27
+ from openstackclient import command
28
+ from openstackclient.i18n import _
29
+
30
+
31
+ LOG = logging.getLogger(__name__)
32
+
33
+
34
+ class AssociateQos(command.Command):
35
+ _description = _("Associate a QoS specification to a volume type")
36
+
37
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
38
+ parser = super().get_parser(prog_name)
39
+ parser.add_argument(
40
+ 'qos_spec',
41
+ metavar='<qos-spec>',
42
+ help=_('QoS specification to modify (name or ID)'),
43
+ )
44
+ parser.add_argument(
45
+ 'volume_type',
46
+ metavar='<volume-type>',
47
+ help=_('Volume type to associate the QoS (name or ID)'),
48
+ )
49
+ return parser
50
+
51
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
52
+ volume_client = self.app.client_manager.volume
53
+ qos_spec = utils.find_resource(
54
+ volume_client.qos_specs, parsed_args.qos_spec
55
+ )
56
+ volume_type = utils.find_resource(
57
+ volume_client.volume_types, parsed_args.volume_type
58
+ )
59
+
60
+ volume_client.qos_specs.associate(qos_spec.id, volume_type.id)
61
+
62
+
63
+ class CreateQos(command.ShowOne):
64
+ _description = _("Create new QoS specification")
65
+
66
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
67
+ parser = super().get_parser(prog_name)
68
+ parser.add_argument(
69
+ 'name',
70
+ metavar='<name>',
71
+ help=_('New QoS specification name'),
72
+ )
73
+ consumer_choices = ['front-end', 'back-end', 'both']
74
+ parser.add_argument(
75
+ '--consumer',
76
+ metavar='<consumer>',
77
+ choices=consumer_choices,
78
+ default='both',
79
+ help=(
80
+ _(
81
+ 'Consumer of the QoS. Valid consumers: %s '
82
+ "(defaults to 'both')"
83
+ )
84
+ % utils.format_list(consumer_choices)
85
+ ),
86
+ )
87
+ parser.add_argument(
88
+ '--property',
89
+ metavar='<key=value>',
90
+ action=parseractions.KeyValueAction,
91
+ help=_(
92
+ 'Set a QoS specification property '
93
+ '(repeat option to set multiple properties)'
94
+ ),
95
+ )
96
+ return parser
97
+
98
+ def take_action(
99
+ self, parsed_args: argparse.Namespace
100
+ ) -> tuple[Sequence[str], Iterable[Any]]:
101
+ volume_client = self.app.client_manager.volume
102
+ specs = {}
103
+ specs.update({'consumer': parsed_args.consumer})
104
+
105
+ if parsed_args.property:
106
+ specs.update(parsed_args.property)
107
+
108
+ qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
109
+
110
+ qos_spec._info.update(
111
+ {
112
+ 'properties': format_columns.DictColumn(
113
+ qos_spec._info.pop('specs')
114
+ )
115
+ }
116
+ )
117
+ col_headers, col_data = zip(*sorted(qos_spec._info.items()))
118
+ return col_headers, col_data
119
+
120
+
121
+ class DeleteQos(command.Command):
122
+ _description = _("Delete QoS specification")
123
+
124
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
125
+ parser = super().get_parser(prog_name)
126
+ parser.add_argument(
127
+ 'qos_specs',
128
+ metavar='<qos-spec>',
129
+ nargs="+",
130
+ help=_('QoS specification(s) to delete (name or ID)'),
131
+ )
132
+ parser.add_argument(
133
+ '--force',
134
+ action='store_true',
135
+ default=False,
136
+ help=_("Allow to delete in-use QoS specification(s)"),
137
+ )
138
+ return parser
139
+
140
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
141
+ volume_client = self.app.client_manager.volume
142
+ result = 0
143
+
144
+ for i in parsed_args.qos_specs:
145
+ try:
146
+ qos_spec = utils.find_resource(volume_client.qos_specs, i)
147
+ volume_client.qos_specs.delete(qos_spec.id, parsed_args.force)
148
+ except Exception as e:
149
+ result += 1
150
+ LOG.error(
151
+ _(
152
+ "Failed to delete QoS specification with "
153
+ "name or ID '%(qos)s': %(e)s"
154
+ ),
155
+ {'qos': i, 'e': e},
156
+ )
157
+
158
+ if result > 0:
159
+ total = len(parsed_args.qos_specs)
160
+ msg = _(
161
+ "%(result)s of %(total)s QoS specifications failed to delete."
162
+ ) % {'result': result, 'total': total}
163
+ raise exceptions.CommandError(msg)
164
+
165
+
166
+ class DisassociateQos(command.Command):
167
+ _description = _("Disassociate a QoS specification from a volume type")
168
+
169
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
170
+ parser = super().get_parser(prog_name)
171
+ parser.add_argument(
172
+ 'qos_spec',
173
+ metavar='<qos-spec>',
174
+ help=_('QoS specification to modify (name or ID)'),
175
+ )
176
+ volume_type_group = parser.add_mutually_exclusive_group()
177
+ volume_type_group.add_argument(
178
+ '--volume-type',
179
+ metavar='<volume-type>',
180
+ help=_('Volume type to disassociate the QoS from (name or ID)'),
181
+ )
182
+ volume_type_group.add_argument(
183
+ '--all',
184
+ action='store_true',
185
+ default=False,
186
+ help=_('Disassociate the QoS from every volume type'),
187
+ )
188
+
189
+ return parser
190
+
191
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
192
+ volume_client = self.app.client_manager.volume
193
+ qos_spec = utils.find_resource(
194
+ volume_client.qos_specs, parsed_args.qos_spec
195
+ )
196
+
197
+ if parsed_args.volume_type:
198
+ volume_type = utils.find_resource(
199
+ volume_client.volume_types, parsed_args.volume_type
200
+ )
201
+ volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id)
202
+ elif parsed_args.all:
203
+ volume_client.qos_specs.disassociate_all(qos_spec.id)
204
+
205
+
206
+ class ListQos(command.Lister):
207
+ _description = _("List QoS specifications")
208
+
209
+ def take_action(
210
+ self, parsed_args: argparse.Namespace
211
+ ) -> tuple[Sequence[str], Iterable[tuple[Any, ...]]]:
212
+ volume_client = self.app.client_manager.volume
213
+ qos_specs_list = volume_client.qos_specs.list()
214
+
215
+ for qos in qos_specs_list:
216
+ try:
217
+ qos_associations = volume_client.qos_specs.get_associations(
218
+ qos,
219
+ )
220
+ if qos_associations:
221
+ associations = [
222
+ association.name for association in qos_associations
223
+ ]
224
+ qos._info.update({'associations': associations})
225
+ except Exception as ex:
226
+ if type(ex).__name__ == 'NotFound':
227
+ qos._info.update({'associations': None})
228
+ else:
229
+ raise
230
+
231
+ display_columns = (
232
+ 'ID',
233
+ 'Name',
234
+ 'Consumer',
235
+ 'Associations',
236
+ 'Properties',
237
+ )
238
+
239
+ columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs')
240
+ return (
241
+ display_columns,
242
+ (
243
+ utils.get_dict_properties(
244
+ s._info,
245
+ columns,
246
+ formatters={
247
+ 'Specs': format_columns.DictColumn,
248
+ 'Associations': format_columns.ListColumn,
249
+ },
250
+ )
251
+ for s in qos_specs_list
252
+ ),
253
+ )
254
+
255
+
256
+ class SetQos(command.Command):
257
+ _description = _("Set QoS specification properties")
258
+
259
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
260
+ parser = super().get_parser(prog_name)
261
+ parser.add_argument(
262
+ 'qos_spec',
263
+ metavar='<qos-spec>',
264
+ help=_('QoS specification to modify (name or ID)'),
265
+ )
266
+ parser.add_argument(
267
+ '--no-property',
268
+ dest='no_property',
269
+ action='store_true',
270
+ help=_(
271
+ 'Remove all properties from <qos-spec> '
272
+ '(specify both --no-property and --property to remove the '
273
+ 'current properties before setting new properties)'
274
+ ),
275
+ )
276
+ parser.add_argument(
277
+ '--property',
278
+ metavar='<key=value>',
279
+ action=parseractions.KeyValueAction,
280
+ help=_(
281
+ 'Property to add or modify for this QoS specification '
282
+ '(repeat option to set multiple properties)'
283
+ ),
284
+ )
285
+ return parser
286
+
287
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
288
+ volume_client = self.app.client_manager.volume
289
+ qos_spec = utils.find_resource(
290
+ volume_client.qos_specs, parsed_args.qos_spec
291
+ )
292
+
293
+ result = 0
294
+ if parsed_args.no_property:
295
+ try:
296
+ key_list = list(qos_spec._info['specs'].keys())
297
+ volume_client.qos_specs.unset_keys(qos_spec.id, key_list)
298
+ except Exception as e:
299
+ LOG.error(_("Failed to clean qos properties: %s"), e)
300
+ result += 1
301
+
302
+ if parsed_args.property:
303
+ try:
304
+ volume_client.qos_specs.set_keys(
305
+ qos_spec.id,
306
+ parsed_args.property,
307
+ )
308
+ except Exception as e:
309
+ LOG.error(_("Failed to set qos property: %s"), e)
310
+ result += 1
311
+
312
+ if result > 0:
313
+ raise exceptions.CommandError(
314
+ _("One or more of the set operations failed")
315
+ )
316
+
317
+
318
+ class ShowQos(command.ShowOne):
319
+ _description = _("Display QoS specification details")
320
+
321
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
322
+ parser = super().get_parser(prog_name)
323
+ parser.add_argument(
324
+ 'qos_spec',
325
+ metavar='<qos-spec>',
326
+ help=_('QoS specification to display (name or ID)'),
327
+ )
328
+ return parser
329
+
330
+ def take_action(
331
+ self, parsed_args: argparse.Namespace
332
+ ) -> tuple[Sequence[str], Iterable[Any]]:
333
+ volume_client = self.app.client_manager.volume
334
+ qos_spec = utils.find_resource(
335
+ volume_client.qos_specs, parsed_args.qos_spec
336
+ )
337
+
338
+ qos_associations = volume_client.qos_specs.get_associations(qos_spec)
339
+ if qos_associations:
340
+ associations = [
341
+ association.name for association in qos_associations
342
+ ]
343
+ qos_spec._info.update(
344
+ {'associations': format_columns.ListColumn(associations)}
345
+ )
346
+ qos_spec._info.update(
347
+ {
348
+ 'properties': format_columns.DictColumn(
349
+ qos_spec._info.pop('specs')
350
+ )
351
+ }
352
+ )
353
+
354
+ col_headers, col_data = zip(*sorted(qos_spec._info.items()))
355
+ return col_headers, col_data
356
+
357
+
358
+ class UnsetQos(command.Command):
359
+ _description = _("Unset QoS specification properties")
360
+
361
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
362
+ parser = super().get_parser(prog_name)
363
+ parser.add_argument(
364
+ 'qos_spec',
365
+ metavar='<qos-spec>',
366
+ help=_('QoS specification to modify (name or ID)'),
367
+ )
368
+ parser.add_argument(
369
+ '--property',
370
+ metavar='<key>',
371
+ action='append',
372
+ default=[],
373
+ help=_(
374
+ 'Property to remove from the QoS specification. '
375
+ '(repeat option to unset multiple properties)'
376
+ ),
377
+ )
378
+ return parser
379
+
380
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
381
+ volume_client = self.app.client_manager.volume
382
+ qos_spec = utils.find_resource(
383
+ volume_client.qos_specs, parsed_args.qos_spec
384
+ )
385
+
386
+ if parsed_args.property:
387
+ volume_client.qos_specs.unset_keys(
388
+ qos_spec.id, parsed_args.property
389
+ )
@@ -102,7 +102,7 @@ class AttachmentsColumn(cliff_columns.FormattableColumn[list[Any]]):
102
102
  return msg
103
103
 
104
104
 
105
- def _format_volume(volume: _volume.Volume) -> dict[str, Any]:
105
+ def _format_volume(volume: _volume.Volume) -> dict[str, object]:
106
106
  # Some columns returned by openstacksdk should not be shown because they're
107
107
  # either irrelevant or duplicates
108
108
  ignored_columns = {
@@ -644,7 +644,9 @@ class ListVolume(command.Lister):
644
644
  default=False,
645
645
  help=_('List additional fields in output'),
646
646
  )
647
- pagination.add_marker_pagination_option_to_parser(parser)
647
+ pagination.add_marker_pagination_option_to_parser(
648
+ parser, include_max_items=False
649
+ )
648
650
  return parser
649
651
 
650
652
  def take_action(
@@ -15,6 +15,7 @@ from collections.abc import Iterable, Sequence
15
15
  import logging
16
16
  from typing import Any
17
17
 
18
+ from openstack.block_storage.v3 import attachment as _attachment
18
19
  from openstack import utils as sdk_utils
19
20
  from osc_lib.cli import format_columns
20
21
  from osc_lib import exceptions
@@ -34,7 +35,9 @@ _FILTER_DEPRECATED = _(
34
35
  )
35
36
 
36
37
 
37
- def _format_attachment(attachment: Any) -> tuple[tuple[str, ...], Any]:
38
+ def _format_attachment(
39
+ attachment: _attachment.Attachment,
40
+ ) -> tuple[tuple[str, ...], Iterable[Any]]:
38
41
  columns = (
39
42
  'id',
40
43
  'volume_id',
@@ -484,6 +487,7 @@ class ListVolumeAttachment(command.Lister):
484
487
  search_opts=search_opts,
485
488
  marker=parsed_args.marker,
486
489
  limit=parsed_args.limit,
490
+ max_items=parsed_args.max_items,
487
491
  )
488
492
 
489
493
  column_headers = (
@@ -0,0 +1,130 @@
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ # not use this file except in compliance with the License. You may obtain
4
+ # a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ # License for the specific language governing permissions and limitations
12
+ # under the License.
13
+ #
14
+
15
+ """Storage backend action implementations"""
16
+
17
+ import argparse
18
+ from collections.abc import Iterable, Sequence
19
+ from typing import Any
20
+
21
+ from openstack import utils as sdk_utils
22
+ from osc_lib.cli import format_columns
23
+ from osc_lib import utils
24
+
25
+ from openstackclient import command
26
+ from openstackclient.i18n import _
27
+
28
+
29
+ class ShowCapability(command.Lister):
30
+ _description = _("Show capability command")
31
+
32
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
33
+ parser = super().get_parser(prog_name)
34
+ parser.add_argument(
35
+ "host",
36
+ metavar="<host>",
37
+ help=_("List capabilities of specified host (host@backend-name)"),
38
+ )
39
+ return parser
40
+
41
+ def take_action(
42
+ self, parsed_args: argparse.Namespace
43
+ ) -> tuple[Sequence[str], Iterable[tuple[Any, ...]]]:
44
+ volume_client = sdk_utils.ensure_service_version(
45
+ self.app.client_manager.sdk_connection.volume, '3'
46
+ )
47
+
48
+ columns = [
49
+ 'Title',
50
+ 'Key',
51
+ 'Type',
52
+ 'Description',
53
+ ]
54
+
55
+ data = volume_client.get_capabilities(parsed_args.host)
56
+
57
+ # The get capabilities API is... interesting. We only want the names of
58
+ # the capabilities that can set for a backend through extra specs, so
59
+ # we need to extract out that part of the mess that is returned.
60
+ print_data = []
61
+ keys = data.properties
62
+ for key in keys:
63
+ # Stuff the key into the details to make it easier to output
64
+ capability_data = data.properties[key]
65
+ capability_data['key'] = key
66
+ print_data.append(capability_data)
67
+
68
+ return (
69
+ columns,
70
+ (
71
+ utils.get_dict_properties(
72
+ s,
73
+ columns,
74
+ )
75
+ for s in print_data
76
+ ),
77
+ )
78
+
79
+
80
+ class ListPool(command.Lister):
81
+ _description = _("List pool command")
82
+
83
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
84
+ parser = super().get_parser(prog_name)
85
+ parser.add_argument(
86
+ "--long",
87
+ action="store_true",
88
+ default=False,
89
+ help=_("Show detailed information about pools."),
90
+ )
91
+ # TODO(smcginnis): Starting with Cinder microversion 3.33, user is also
92
+ # able to pass in --filters with a <key>=<value> pair to filter on.
93
+ return parser
94
+
95
+ def take_action(
96
+ self, parsed_args: argparse.Namespace
97
+ ) -> tuple[Sequence[str], Iterable[tuple[Any, ...]]]:
98
+ volume_client = sdk_utils.ensure_service_version(
99
+ self.app.client_manager.sdk_connection.volume, '3'
100
+ )
101
+
102
+ if parsed_args.long:
103
+ columns = [
104
+ 'name',
105
+ 'capabilities',
106
+ ]
107
+
108
+ headers = [
109
+ 'Name',
110
+ 'Capabilities',
111
+ ]
112
+ else:
113
+ columns = [
114
+ 'Name',
115
+ ]
116
+ headers = columns
117
+
118
+ data = volume_client.backend_pools(detailed=parsed_args.long)
119
+ formatters = {'capabilities': format_columns.DictColumn}
120
+ return (
121
+ headers,
122
+ (
123
+ utils.get_item_properties(
124
+ s,
125
+ columns,
126
+ formatters=formatters,
127
+ )
128
+ for s in data
129
+ ),
130
+ )
@@ -394,6 +394,7 @@ class ListVolumeBackup(command.Lister):
394
394
  all_tenants=all_tenants,
395
395
  marker=marker_backup_id,
396
396
  limit=parsed_args.limit,
397
+ max_items=parsed_args.max_items,
397
398
  project_id=project_id,
398
399
  )
399
400
 
@@ -477,9 +478,7 @@ class RestoreVolumeBackup(command.ShowOne):
477
478
  raise exceptions.CommandError(msg % parsed_args.volume)
478
479
 
479
480
  restore = volume_client.restore_backup(
480
- backup.id,
481
- volume_id=volume_id,
482
- name=volume_name,
481
+ backup.id, volume=volume_id, name=volume_name
483
482
  )
484
483
 
485
484
  data = utils.get_dict_properties(restore, columns)
@@ -15,6 +15,7 @@ from collections.abc import Iterable, Sequence
15
15
  import logging
16
16
  from typing import Any
17
17
 
18
+ from openstack.block_storage.v3 import group_snapshot as _group_snapshot
18
19
  from openstack import utils as sdk_utils
19
20
  from osc_lib import exceptions
20
21
  from osc_lib import utils
@@ -27,8 +28,8 @@ LOG = logging.getLogger(__name__)
27
28
 
28
29
 
29
30
  def _format_group_snapshot(
30
- snapshot: Any,
31
- ) -> tuple[Sequence[str], Iterable[Any]]:
31
+ snapshot: _group_snapshot.GroupSnapshot,
32
+ ) -> tuple[tuple[str, ...], Iterable[Any]]:
32
33
  columns = (
33
34
  'id',
34
35
  'status',
@@ -48,10 +49,7 @@ def _format_group_snapshot(
48
49
 
49
50
  return (
50
51
  column_headers,
51
- utils.get_item_properties(
52
- snapshot,
53
- columns,
54
- ),
52
+ utils.get_item_properties(snapshot, columns),
55
53
  )
56
54
 
57
55
 
@@ -412,7 +412,7 @@ class ShowVolumeGroupType(command.ShowOne):
412
412
 
413
413
  group_type = utils.find_resource(
414
414
  volume_client.group_types,
415
- parsed_args.group,
415
+ parsed_args.group_type,
416
416
  )
417
417
 
418
418
  return _format_group_type(group_type)
@@ -0,0 +1,74 @@
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+ # not use this file except in compliance with the License. You may obtain
4
+ # a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+ # License for the specific language governing permissions and limitations
12
+ # under the License.
13
+
14
+ """Volume v3 host action implementations"""
15
+
16
+ import argparse
17
+
18
+ from openstackclient import command
19
+ from openstackclient.i18n import _
20
+
21
+
22
+ class FailoverVolumeHost(command.Command):
23
+ _description = _("Failover volume host to different backend")
24
+
25
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
26
+ parser = super().get_parser(prog_name)
27
+ parser.add_argument(
28
+ "host", metavar="<host-name>", help=_("Name of volume host")
29
+ )
30
+ parser.add_argument(
31
+ "--volume-backend",
32
+ metavar="<backend-id>",
33
+ required=True,
34
+ help=_(
35
+ "The ID of the volume backend replication "
36
+ "target where the host will failover to (required)"
37
+ ),
38
+ )
39
+ return parser
40
+
41
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
42
+ service_client = self.app.client_manager.volume
43
+ service_client.services.failover_host(
44
+ parsed_args.host, parsed_args.volume_backend
45
+ )
46
+
47
+
48
+ class SetVolumeHost(command.Command):
49
+ _description = _("Set volume host properties")
50
+
51
+ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
52
+ parser = super().get_parser(prog_name)
53
+ parser.add_argument(
54
+ "host", metavar="<host-name>", help=_("Name of volume host")
55
+ )
56
+ enabled_group = parser.add_mutually_exclusive_group()
57
+ enabled_group.add_argument(
58
+ "--disable",
59
+ action="store_true",
60
+ help=_("Freeze and disable the specified volume host"),
61
+ )
62
+ enabled_group.add_argument(
63
+ "--enable",
64
+ action="store_true",
65
+ help=_("Thaw and enable the specified volume host"),
66
+ )
67
+ return parser
68
+
69
+ def take_action(self, parsed_args: argparse.Namespace) -> None:
70
+ service_client = self.app.client_manager.volume
71
+ if parsed_args.enable:
72
+ service_client.services.thaw_host(parsed_args.host)
73
+ if parsed_args.disable:
74
+ service_client.services.freeze_host(parsed_args.host)