python-openstackclient 8.0.0__py3-none-any.whl → 8.2.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.
- openstackclient/api/compute_v2.py +2 -2
- openstackclient/api/volume_v2.py +60 -0
- openstackclient/api/volume_v3.py +60 -0
- openstackclient/compute/client.py +5 -0
- openstackclient/compute/v2/console.py +7 -0
- openstackclient/compute/v2/console_connection.py +48 -0
- openstackclient/compute/v2/flavor.py +14 -1
- openstackclient/compute/v2/keypair.py +10 -3
- openstackclient/compute/v2/server.py +76 -13
- openstackclient/compute/v2/server_event.py +1 -1
- openstackclient/identity/common.py +85 -11
- openstackclient/identity/v3/application_credential.py +88 -87
- openstackclient/identity/v3/domain.py +67 -49
- openstackclient/identity/v3/group.py +113 -68
- openstackclient/identity/v3/project.py +42 -20
- openstackclient/identity/v3/role.py +7 -2
- openstackclient/identity/v3/user.py +38 -5
- openstackclient/image/client.py +5 -0
- openstackclient/image/v1/image.py +16 -1
- openstackclient/image/v2/cache.py +10 -6
- openstackclient/image/v2/image.py +59 -12
- openstackclient/image/v2/metadef_objects.py +8 -2
- openstackclient/image/v2/metadef_properties.py +9 -2
- openstackclient/network/client.py +0 -6
- openstackclient/network/v2/floating_ip.py +58 -29
- openstackclient/network/v2/network_qos_rule.py +3 -11
- openstackclient/network/v2/port.py +16 -0
- openstackclient/network/v2/router.py +1 -1
- openstackclient/network/v2/security_group.py +49 -7
- openstackclient/network/v2/security_group_rule.py +18 -1
- openstackclient/shell.py +1 -1
- openstackclient/tests/functional/base.py +5 -1
- openstackclient/tests/functional/compute/v2/test_keypair.py +41 -5
- openstackclient/tests/functional/identity/v3/test_access_rule.py +1 -1
- openstackclient/tests/functional/identity/v3/test_application_credential.py +7 -7
- openstackclient/tests/functional/image/v2/test_image.py +36 -14
- openstackclient/tests/functional/volume/v2/test_volume.py +1 -1
- openstackclient/tests/functional/volume/v3/test_volume.py +2 -2
- openstackclient/tests/unit/api/test_volume_v2.py +124 -0
- openstackclient/tests/unit/api/test_volume_v3.py +124 -0
- openstackclient/tests/unit/compute/v2/fakes.py +81 -305
- openstackclient/tests/unit/compute/v2/test_console.py +18 -1
- openstackclient/tests/unit/compute/v2/test_console_connection.py +72 -0
- openstackclient/tests/unit/compute/v2/test_flavor.py +160 -175
- openstackclient/tests/unit/compute/v2/test_keypair.py +12 -5
- openstackclient/tests/unit/compute/v2/test_server.py +211 -97
- openstackclient/tests/unit/compute/v2/test_server_backup.py +32 -71
- openstackclient/tests/unit/compute/v2/test_server_event.py +2 -2
- openstackclient/tests/unit/compute/v2/test_server_image.py +33 -72
- openstackclient/tests/unit/compute/v2/test_server_migration.py +4 -4
- openstackclient/tests/unit/identity/v3/test_application_credential.py +93 -65
- openstackclient/tests/unit/identity/v3/test_domain.py +117 -107
- openstackclient/tests/unit/identity/v3/test_group.py +353 -202
- openstackclient/tests/unit/identity/v3/test_project.py +46 -53
- openstackclient/tests/unit/identity/v3/test_role.py +2 -8
- openstackclient/tests/unit/identity/v3/test_user.py +86 -6
- openstackclient/tests/unit/image/v1/test_image.py +55 -9
- openstackclient/tests/unit/image/v2/test_image.py +128 -58
- openstackclient/tests/unit/image/v2/test_metadef_objects.py +22 -0
- openstackclient/tests/unit/image/v2/test_metadef_properties.py +24 -10
- openstackclient/tests/unit/network/v2/fakes.py +406 -485
- openstackclient/tests/unit/network/v2/test_floating_ip_network.py +13 -19
- openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +2 -2
- openstackclient/tests/unit/network/v2/test_ndp_proxy.py +3 -5
- openstackclient/tests/unit/network/v2/test_network.py +4 -4
- openstackclient/tests/unit/network/v2/test_network_agent.py +15 -29
- openstackclient/tests/unit/network/v2/test_network_qos_policy.py +16 -19
- openstackclient/tests/unit/network/v2/test_network_qos_rule.py +79 -152
- openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +4 -6
- openstackclient/tests/unit/network/v2/test_network_rbac.py +2 -2
- openstackclient/tests/unit/network/v2/test_port.py +57 -17
- openstackclient/tests/unit/network/v2/test_router.py +73 -57
- openstackclient/tests/unit/network/v2/test_security_group_network.py +31 -27
- openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +1 -3
- openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +82 -39
- openstackclient/tests/unit/volume/v2/fakes.py +1 -2
- openstackclient/tests/unit/volume/v2/test_service.py +57 -91
- openstackclient/tests/unit/volume/v2/test_volume.py +466 -410
- openstackclient/tests/unit/volume/v2/test_volume_backup.py +141 -148
- openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +293 -283
- openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +61 -71
- openstackclient/tests/unit/volume/v3/test_service.py +221 -141
- openstackclient/tests/unit/volume/v3/test_volume.py +569 -534
- openstackclient/tests/unit/volume/v3/test_volume_attachment.py +1 -1
- openstackclient/tests/unit/volume/v3/test_volume_backup.py +198 -203
- openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +682 -47
- openstackclient/volume/v2/service.py +41 -38
- openstackclient/volume/v2/volume.py +140 -88
- openstackclient/volume/v2/volume_backup.py +9 -3
- openstackclient/volume/v2/volume_snapshot.py +121 -84
- openstackclient/volume/v3/block_storage_log_level.py +22 -28
- openstackclient/volume/v3/service.py +105 -14
- openstackclient/volume/v3/volume.py +287 -99
- openstackclient/volume/v3/volume_backup.py +24 -19
- openstackclient/volume/v3/volume_group.py +1 -1
- openstackclient/volume/v3/volume_snapshot.py +485 -10
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/AUTHORS +13 -0
- python_openstackclient-8.2.0.dist-info/METADATA +264 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/RECORD +104 -98
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/entry_points.txt +7 -6
- python_openstackclient-8.2.0.dist-info/pbr.json +1 -0
- python_openstackclient-8.0.0.dist-info/METADATA +0 -166
- python_openstackclient-8.0.0.dist-info/pbr.json +0 -1
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/LICENSE +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/WHEEL +0 -0
- {python_openstackclient-8.0.0.dist-info → python_openstackclient-8.2.0.dist-info}/top_level.txt +0 -0
|
@@ -45,33 +45,34 @@ class ListService(command.Lister):
|
|
|
45
45
|
return parser
|
|
46
46
|
|
|
47
47
|
def take_action(self, parsed_args):
|
|
48
|
-
|
|
48
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
49
|
+
|
|
50
|
+
columns: tuple[str, ...] = (
|
|
51
|
+
"binary",
|
|
52
|
+
"host",
|
|
53
|
+
"availability_zone",
|
|
54
|
+
"status",
|
|
55
|
+
"state",
|
|
56
|
+
"updated_at",
|
|
57
|
+
)
|
|
58
|
+
column_names: tuple[str, ...] = (
|
|
59
|
+
"Binary",
|
|
60
|
+
"Host",
|
|
61
|
+
"Zone",
|
|
62
|
+
"Status",
|
|
63
|
+
"State",
|
|
64
|
+
"Updated At",
|
|
65
|
+
)
|
|
49
66
|
|
|
50
67
|
if parsed_args.long:
|
|
51
|
-
columns
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"State",
|
|
57
|
-
"Updated At",
|
|
58
|
-
"Disabled Reason",
|
|
59
|
-
]
|
|
60
|
-
else:
|
|
61
|
-
columns = [
|
|
62
|
-
"Binary",
|
|
63
|
-
"Host",
|
|
64
|
-
"Zone",
|
|
65
|
-
"Status",
|
|
66
|
-
"State",
|
|
67
|
-
"Updated At",
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
data = service_client.services.list(
|
|
71
|
-
parsed_args.host, parsed_args.service
|
|
68
|
+
columns += ("disabled_reason",)
|
|
69
|
+
column_names += ("Disabled Reason",)
|
|
70
|
+
|
|
71
|
+
data = volume_client.services(
|
|
72
|
+
host=parsed_args.host, binary=parsed_args.service
|
|
72
73
|
)
|
|
73
74
|
return (
|
|
74
|
-
|
|
75
|
+
column_names,
|
|
75
76
|
(
|
|
76
77
|
utils.get_item_properties(
|
|
77
78
|
s,
|
|
@@ -87,7 +88,11 @@ class SetService(command.Command):
|
|
|
87
88
|
|
|
88
89
|
def get_parser(self, prog_name):
|
|
89
90
|
parser = super().get_parser(prog_name)
|
|
90
|
-
parser.add_argument(
|
|
91
|
+
parser.add_argument(
|
|
92
|
+
"host",
|
|
93
|
+
metavar="<host>",
|
|
94
|
+
help=_("Name of host"),
|
|
95
|
+
)
|
|
91
96
|
parser.add_argument(
|
|
92
97
|
"service",
|
|
93
98
|
metavar="<service>",
|
|
@@ -118,19 +123,17 @@ class SetService(command.Command):
|
|
|
118
123
|
)
|
|
119
124
|
raise exceptions.CommandError(msg)
|
|
120
125
|
|
|
121
|
-
|
|
126
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
127
|
+
|
|
128
|
+
service = volume_client.find_service(
|
|
129
|
+
parsed_args.service, ignore_missing=False, host=parsed_args.host
|
|
130
|
+
)
|
|
131
|
+
|
|
122
132
|
if parsed_args.enable:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
)
|
|
133
|
+
service.enable(volume_client)
|
|
134
|
+
|
|
126
135
|
if parsed_args.disable:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
parsed_args.disable_reason,
|
|
132
|
-
)
|
|
133
|
-
else:
|
|
134
|
-
service_client.services.disable(
|
|
135
|
-
parsed_args.host, parsed_args.service
|
|
136
|
-
)
|
|
136
|
+
service.disable(
|
|
137
|
+
volume_client,
|
|
138
|
+
reason=parsed_args.disable_reason,
|
|
139
|
+
)
|
|
@@ -18,8 +18,10 @@ import argparse
|
|
|
18
18
|
import copy
|
|
19
19
|
import functools
|
|
20
20
|
import logging
|
|
21
|
+
import typing as ty
|
|
21
22
|
|
|
22
23
|
from cliff import columns as cliff_columns
|
|
24
|
+
from openstack.block_storage.v2 import volume as _volume
|
|
23
25
|
from openstack import exceptions as sdk_exceptions
|
|
24
26
|
from osc_lib.cli import format_columns
|
|
25
27
|
from osc_lib.cli import parseractions
|
|
@@ -27,6 +29,7 @@ from osc_lib.command import command
|
|
|
27
29
|
from osc_lib import exceptions
|
|
28
30
|
from osc_lib import utils
|
|
29
31
|
|
|
32
|
+
from openstackclient.api import volume_v2
|
|
30
33
|
from openstackclient.common import pagination
|
|
31
34
|
from openstackclient.i18n import _
|
|
32
35
|
from openstackclient.identity import common as identity_common
|
|
@@ -89,6 +92,47 @@ class AttachmentsColumn(cliff_columns.FormattableColumn):
|
|
|
89
92
|
return msg
|
|
90
93
|
|
|
91
94
|
|
|
95
|
+
def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]:
|
|
96
|
+
# Some columns returned by openstacksdk should not be shown because they're
|
|
97
|
+
# either irrelevant or duplicates
|
|
98
|
+
ignored_columns = {
|
|
99
|
+
# computed columns
|
|
100
|
+
'location',
|
|
101
|
+
# create-only columns
|
|
102
|
+
'OS-SCH-HNT:scheduler_hints',
|
|
103
|
+
'imageRef',
|
|
104
|
+
# unnecessary columns
|
|
105
|
+
'links',
|
|
106
|
+
}
|
|
107
|
+
optional_columns = {
|
|
108
|
+
# only present if part of a consistency group
|
|
109
|
+
'consistencygroup_id',
|
|
110
|
+
# only present if there are image properties associated
|
|
111
|
+
'volume_image_metadata',
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
info = volume.to_dict(original_names=True)
|
|
115
|
+
data = {}
|
|
116
|
+
for key, value in info.items():
|
|
117
|
+
if key in ignored_columns:
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
if key in optional_columns:
|
|
121
|
+
if info[key] is None:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
data[key] = value
|
|
125
|
+
|
|
126
|
+
data.update(
|
|
127
|
+
{
|
|
128
|
+
'properties': format_columns.DictColumn(data.pop('metadata')),
|
|
129
|
+
'type': data.pop('volume_type'),
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return data
|
|
134
|
+
|
|
135
|
+
|
|
92
136
|
class CreateVolume(command.ShowOne):
|
|
93
137
|
_description = _("Create new volume")
|
|
94
138
|
|
|
@@ -107,7 +151,7 @@ class CreateVolume(command.ShowOne):
|
|
|
107
151
|
)
|
|
108
152
|
raise exceptions.CommandError(msg)
|
|
109
153
|
|
|
110
|
-
def
|
|
154
|
+
def get_parser(self, prog_name):
|
|
111
155
|
parser = super().get_parser(prog_name)
|
|
112
156
|
parser.add_argument(
|
|
113
157
|
"name",
|
|
@@ -169,6 +213,7 @@ class CreateVolume(command.ShowOne):
|
|
|
169
213
|
"--property",
|
|
170
214
|
metavar="<key=value>",
|
|
171
215
|
action=parseractions.KeyValueAction,
|
|
216
|
+
dest="properties",
|
|
172
217
|
help=_(
|
|
173
218
|
"Set a property to this volume "
|
|
174
219
|
"(repeat option to set multiple properties)"
|
|
@@ -189,54 +234,58 @@ class CreateVolume(command.ShowOne):
|
|
|
189
234
|
bootable_group.add_argument(
|
|
190
235
|
"--bootable",
|
|
191
236
|
action="store_true",
|
|
237
|
+
dest="bootable",
|
|
238
|
+
default=None,
|
|
192
239
|
help=_("Mark volume as bootable"),
|
|
193
240
|
)
|
|
194
241
|
bootable_group.add_argument(
|
|
195
242
|
"--non-bootable",
|
|
196
|
-
action="
|
|
243
|
+
action="store_false",
|
|
244
|
+
dest="bootable",
|
|
245
|
+
default=None,
|
|
197
246
|
help=_("Mark volume as non-bootable (default)"),
|
|
198
247
|
)
|
|
199
248
|
readonly_group = parser.add_mutually_exclusive_group()
|
|
200
249
|
readonly_group.add_argument(
|
|
201
250
|
"--read-only",
|
|
202
251
|
action="store_true",
|
|
252
|
+
dest="read_only",
|
|
253
|
+
default=None,
|
|
203
254
|
help=_("Set volume to read-only access mode"),
|
|
204
255
|
)
|
|
205
256
|
readonly_group.add_argument(
|
|
206
257
|
"--read-write",
|
|
207
|
-
action="
|
|
258
|
+
action="store_false",
|
|
259
|
+
dest="read_only",
|
|
260
|
+
default=None,
|
|
208
261
|
help=_("Set volume to read-write access mode (default)"),
|
|
209
262
|
)
|
|
210
|
-
return parser, source_group
|
|
211
|
-
|
|
212
|
-
def get_parser(self, prog_name):
|
|
213
|
-
parser, _ = self._get_parser(prog_name)
|
|
214
263
|
return parser
|
|
215
264
|
|
|
216
265
|
def take_action(self, parsed_args):
|
|
217
|
-
|
|
266
|
+
self._check_size_arg(parsed_args)
|
|
218
267
|
# size is validated in the above call to
|
|
219
268
|
# _check_size_arg where we check that size
|
|
220
269
|
# should be passed if we are not creating a
|
|
221
270
|
# volume from snapshot or source volume
|
|
222
271
|
size = parsed_args.size
|
|
223
272
|
|
|
224
|
-
volume_client = self.app.client_manager.volume
|
|
273
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
225
274
|
image_client = self.app.client_manager.image
|
|
226
275
|
|
|
227
276
|
source_volume = None
|
|
228
277
|
if parsed_args.source:
|
|
229
|
-
source_volume_obj =
|
|
230
|
-
|
|
278
|
+
source_volume_obj = volume_client.find_volume(
|
|
279
|
+
parsed_args.source, ignore_missing=False
|
|
231
280
|
)
|
|
232
281
|
source_volume = source_volume_obj.id
|
|
233
282
|
size = max(size or 0, source_volume_obj.size)
|
|
234
283
|
|
|
235
284
|
consistency_group = None
|
|
236
285
|
if parsed_args.consistency_group:
|
|
237
|
-
consistency_group =
|
|
238
|
-
volume_client
|
|
239
|
-
)
|
|
286
|
+
consistency_group = volume_v2.find_consistency_group(
|
|
287
|
+
volume_client, parsed_args.consistency_group
|
|
288
|
+
)['id']
|
|
240
289
|
|
|
241
290
|
image = None
|
|
242
291
|
if parsed_args.image:
|
|
@@ -246,8 +295,8 @@ class CreateVolume(command.ShowOne):
|
|
|
246
295
|
|
|
247
296
|
snapshot = None
|
|
248
297
|
if parsed_args.snapshot:
|
|
249
|
-
snapshot_obj =
|
|
250
|
-
|
|
298
|
+
snapshot_obj = volume_client.find_snapshot(
|
|
299
|
+
parsed_args.snapshot, ignore_missing=False
|
|
251
300
|
)
|
|
252
301
|
snapshot = snapshot_obj.id
|
|
253
302
|
# Cinder requires a value for size when creating a volume
|
|
@@ -258,31 +307,31 @@ class CreateVolume(command.ShowOne):
|
|
|
258
307
|
# snapshot size.
|
|
259
308
|
size = max(size or 0, snapshot_obj.size)
|
|
260
309
|
|
|
261
|
-
volume = volume_client.
|
|
310
|
+
volume = volume_client.create_volume(
|
|
262
311
|
size=size,
|
|
263
312
|
snapshot_id=snapshot,
|
|
264
313
|
name=parsed_args.name,
|
|
265
314
|
description=parsed_args.description,
|
|
266
315
|
volume_type=parsed_args.type,
|
|
267
316
|
availability_zone=parsed_args.availability_zone,
|
|
268
|
-
metadata=parsed_args.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
317
|
+
metadata=parsed_args.properties,
|
|
318
|
+
image_id=image,
|
|
319
|
+
source_volume_id=source_volume,
|
|
320
|
+
consistency_group_id=consistency_group,
|
|
272
321
|
scheduler_hints=parsed_args.hint,
|
|
273
322
|
)
|
|
274
323
|
|
|
275
|
-
if parsed_args.bootable
|
|
324
|
+
if parsed_args.bootable is not None:
|
|
276
325
|
try:
|
|
277
326
|
if utils.wait_for_status(
|
|
278
|
-
volume_client.
|
|
327
|
+
volume_client.get_volume,
|
|
279
328
|
volume.id,
|
|
280
329
|
success_status=['available'],
|
|
281
330
|
error_status=['error'],
|
|
282
331
|
sleep_time=1,
|
|
283
332
|
):
|
|
284
|
-
volume_client.
|
|
285
|
-
volume
|
|
333
|
+
volume_client.set_volume_bootable_status(
|
|
334
|
+
volume, parsed_args.bootable
|
|
286
335
|
)
|
|
287
336
|
else:
|
|
288
337
|
msg = _(
|
|
@@ -291,17 +340,18 @@ class CreateVolume(command.ShowOne):
|
|
|
291
340
|
raise exceptions.CommandError(msg)
|
|
292
341
|
except Exception as e:
|
|
293
342
|
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
|
294
|
-
|
|
343
|
+
|
|
344
|
+
if parsed_args.read_only is not None:
|
|
295
345
|
try:
|
|
296
346
|
if utils.wait_for_status(
|
|
297
|
-
volume_client.
|
|
347
|
+
volume_client.get_volume,
|
|
298
348
|
volume.id,
|
|
299
349
|
success_status=['available'],
|
|
300
350
|
error_status=['error'],
|
|
301
351
|
sleep_time=1,
|
|
302
352
|
):
|
|
303
|
-
volume_client.
|
|
304
|
-
volume
|
|
353
|
+
volume_client.set_volume_readonly(
|
|
354
|
+
volume, parsed_args.read_only
|
|
305
355
|
)
|
|
306
356
|
else:
|
|
307
357
|
msg = _(
|
|
@@ -315,17 +365,8 @@ class CreateVolume(command.ShowOne):
|
|
|
315
365
|
e,
|
|
316
366
|
)
|
|
317
367
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
{
|
|
321
|
-
'properties': format_columns.DictColumn(
|
|
322
|
-
volume._info.pop('metadata')
|
|
323
|
-
),
|
|
324
|
-
'type': volume._info.pop('volume_type'),
|
|
325
|
-
}
|
|
326
|
-
)
|
|
327
|
-
volume._info.pop("links", None)
|
|
328
|
-
return zip(*sorted(volume._info.items()))
|
|
368
|
+
data = _format_volume(volume)
|
|
369
|
+
return zip(*sorted(data.items()))
|
|
329
370
|
|
|
330
371
|
|
|
331
372
|
class DeleteVolume(command.Command):
|
|
@@ -358,18 +399,19 @@ class DeleteVolume(command.Command):
|
|
|
358
399
|
return parser
|
|
359
400
|
|
|
360
401
|
def take_action(self, parsed_args):
|
|
361
|
-
volume_client = self.app.client_manager.volume
|
|
402
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
362
403
|
result = 0
|
|
363
404
|
|
|
364
|
-
for
|
|
405
|
+
for volume in parsed_args.volumes:
|
|
365
406
|
try:
|
|
366
|
-
volume_obj =
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
407
|
+
volume_obj = volume_client.find_volume(
|
|
408
|
+
volume, ignore_missing=False
|
|
409
|
+
)
|
|
410
|
+
volume_client.delete_volume(
|
|
411
|
+
volume_obj.id,
|
|
412
|
+
force=parsed_args.force,
|
|
413
|
+
cascade=parsed_args.purge,
|
|
414
|
+
)
|
|
373
415
|
except Exception as e:
|
|
374
416
|
result += 1
|
|
375
417
|
LOG.error(
|
|
@@ -377,7 +419,7 @@ class DeleteVolume(command.Command):
|
|
|
377
419
|
"Failed to delete volume with "
|
|
378
420
|
"name or ID '%(volume)s': %(e)s"
|
|
379
421
|
),
|
|
380
|
-
{'volume':
|
|
422
|
+
{'volume': volume, 'e': e},
|
|
381
423
|
)
|
|
382
424
|
|
|
383
425
|
if result > 0:
|
|
@@ -571,13 +613,15 @@ class MigrateVolume(command.Command):
|
|
|
571
613
|
return parser
|
|
572
614
|
|
|
573
615
|
def take_action(self, parsed_args):
|
|
574
|
-
volume_client = self.app.client_manager.volume
|
|
575
|
-
volume =
|
|
576
|
-
|
|
616
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
617
|
+
volume = volume_client.find_volume(
|
|
618
|
+
parsed_args.volume, ignore_missing=False
|
|
619
|
+
)
|
|
620
|
+
volume_client.migrate_volume(
|
|
577
621
|
volume.id,
|
|
578
|
-
parsed_args.host,
|
|
579
|
-
parsed_args.force_host_copy,
|
|
580
|
-
parsed_args.lock_volume,
|
|
622
|
+
host=parsed_args.host,
|
|
623
|
+
force_host_copy=parsed_args.force_host_copy,
|
|
624
|
+
lock_volume=parsed_args.lock_volume,
|
|
581
625
|
)
|
|
582
626
|
|
|
583
627
|
|
|
@@ -622,6 +666,7 @@ class SetVolume(command.Command):
|
|
|
622
666
|
'--property',
|
|
623
667
|
metavar='<key=value>',
|
|
624
668
|
action=parseractions.KeyValueAction,
|
|
669
|
+
dest="properties",
|
|
625
670
|
help=_(
|
|
626
671
|
'Set a property on this volume '
|
|
627
672
|
'(repeat option to set multiple properties)'
|
|
@@ -631,6 +676,7 @@ class SetVolume(command.Command):
|
|
|
631
676
|
'--image-property',
|
|
632
677
|
metavar='<key=value>',
|
|
633
678
|
action=parseractions.KeyValueAction,
|
|
679
|
+
dest="image_properties",
|
|
634
680
|
help=_(
|
|
635
681
|
'Set an image property on this volume '
|
|
636
682
|
'(repeat option to set multiple image properties)'
|
|
@@ -707,22 +753,30 @@ class SetVolume(command.Command):
|
|
|
707
753
|
bootable_group.add_argument(
|
|
708
754
|
"--bootable",
|
|
709
755
|
action="store_true",
|
|
756
|
+
dest="bootable",
|
|
757
|
+
default=None,
|
|
710
758
|
help=_("Mark volume as bootable"),
|
|
711
759
|
)
|
|
712
760
|
bootable_group.add_argument(
|
|
713
761
|
"--non-bootable",
|
|
714
|
-
action="
|
|
762
|
+
action="store_false",
|
|
763
|
+
dest="bootable",
|
|
764
|
+
default=None,
|
|
715
765
|
help=_("Mark volume as non-bootable"),
|
|
716
766
|
)
|
|
717
767
|
readonly_group = parser.add_mutually_exclusive_group()
|
|
718
768
|
readonly_group.add_argument(
|
|
719
769
|
"--read-only",
|
|
720
770
|
action="store_true",
|
|
771
|
+
dest="read_only",
|
|
772
|
+
default=None,
|
|
721
773
|
help=_("Set volume to read-only access mode"),
|
|
722
774
|
)
|
|
723
775
|
readonly_group.add_argument(
|
|
724
776
|
"--read-write",
|
|
725
|
-
action="
|
|
777
|
+
action="store_false",
|
|
778
|
+
dest="read_only",
|
|
779
|
+
default=None,
|
|
726
780
|
help=_("Set volume to read-write access mode"),
|
|
727
781
|
)
|
|
728
782
|
return parser
|
|
@@ -771,28 +825,31 @@ class SetVolume(command.Command):
|
|
|
771
825
|
LOG.error(_("Failed to clean volume properties: %s"), e)
|
|
772
826
|
result += 1
|
|
773
827
|
|
|
774
|
-
if parsed_args.
|
|
828
|
+
if parsed_args.properties:
|
|
775
829
|
try:
|
|
776
830
|
volume_client.volumes.set_metadata(
|
|
777
|
-
volume.id, parsed_args.
|
|
831
|
+
volume.id, parsed_args.properties
|
|
778
832
|
)
|
|
779
833
|
except Exception as e:
|
|
780
|
-
LOG.error(_("Failed to set volume
|
|
834
|
+
LOG.error(_("Failed to set volume properties: %s"), e)
|
|
781
835
|
result += 1
|
|
782
|
-
|
|
836
|
+
|
|
837
|
+
if parsed_args.image_properties:
|
|
783
838
|
try:
|
|
784
839
|
volume_client.volumes.set_image_metadata(
|
|
785
|
-
volume.id, parsed_args.
|
|
840
|
+
volume.id, parsed_args.image_properties
|
|
786
841
|
)
|
|
787
842
|
except Exception as e:
|
|
788
|
-
LOG.error(_("Failed to set image
|
|
843
|
+
LOG.error(_("Failed to set image properties: %s"), e)
|
|
789
844
|
result += 1
|
|
845
|
+
|
|
790
846
|
if parsed_args.state:
|
|
791
847
|
try:
|
|
792
848
|
volume_client.volumes.reset_state(volume.id, parsed_args.state)
|
|
793
849
|
except Exception as e:
|
|
794
850
|
LOG.error(_("Failed to set volume state: %s"), e)
|
|
795
851
|
result += 1
|
|
852
|
+
|
|
796
853
|
if parsed_args.attached:
|
|
797
854
|
try:
|
|
798
855
|
volume_client.volumes.reset_state(
|
|
@@ -801,6 +858,7 @@ class SetVolume(command.Command):
|
|
|
801
858
|
except Exception as e:
|
|
802
859
|
LOG.error(_("Failed to set volume attach-status: %s"), e)
|
|
803
860
|
result += 1
|
|
861
|
+
|
|
804
862
|
if parsed_args.detached:
|
|
805
863
|
try:
|
|
806
864
|
volume_client.volumes.reset_state(
|
|
@@ -809,7 +867,8 @@ class SetVolume(command.Command):
|
|
|
809
867
|
except Exception as e:
|
|
810
868
|
LOG.error(_("Failed to set volume attach-status: %s"), e)
|
|
811
869
|
result += 1
|
|
812
|
-
|
|
870
|
+
|
|
871
|
+
if parsed_args.bootable is not None:
|
|
813
872
|
try:
|
|
814
873
|
volume_client.volumes.set_bootable(
|
|
815
874
|
volume.id, parsed_args.bootable
|
|
@@ -817,7 +876,8 @@ class SetVolume(command.Command):
|
|
|
817
876
|
except Exception as e:
|
|
818
877
|
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
|
819
878
|
result += 1
|
|
820
|
-
|
|
879
|
+
|
|
880
|
+
if parsed_args.read_only is not None:
|
|
821
881
|
try:
|
|
822
882
|
volume_client.volumes.update_readonly_flag(
|
|
823
883
|
volume.id, parsed_args.read_only
|
|
@@ -828,6 +888,7 @@ class SetVolume(command.Command):
|
|
|
828
888
|
e,
|
|
829
889
|
)
|
|
830
890
|
result += 1
|
|
891
|
+
|
|
831
892
|
policy = parsed_args.migration_policy or parsed_args.retype_policy
|
|
832
893
|
if parsed_args.type:
|
|
833
894
|
# get the migration policy
|
|
@@ -894,24 +955,13 @@ class ShowVolume(command.ShowOne):
|
|
|
894
955
|
return parser
|
|
895
956
|
|
|
896
957
|
def take_action(self, parsed_args):
|
|
897
|
-
volume_client = self.app.client_manager.volume
|
|
898
|
-
volume =
|
|
899
|
-
|
|
900
|
-
# Special mapping for columns to make the output easier to read:
|
|
901
|
-
# 'metadata' --> 'properties'
|
|
902
|
-
# 'volume_type' --> 'type'
|
|
903
|
-
volume._info.update(
|
|
904
|
-
{
|
|
905
|
-
'properties': format_columns.DictColumn(
|
|
906
|
-
volume._info.pop('metadata')
|
|
907
|
-
),
|
|
908
|
-
'type': volume._info.pop('volume_type'),
|
|
909
|
-
},
|
|
958
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
959
|
+
volume = volume_client.find_volume(
|
|
960
|
+
parsed_args.volume, ignore_missing=False
|
|
910
961
|
)
|
|
911
962
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
return zip(*sorted(volume._info.items()))
|
|
963
|
+
data = _format_volume(volume)
|
|
964
|
+
return zip(*sorted(data.items()))
|
|
915
965
|
|
|
916
966
|
|
|
917
967
|
class UnsetVolume(command.Command):
|
|
@@ -928,6 +978,7 @@ class UnsetVolume(command.Command):
|
|
|
928
978
|
'--property',
|
|
929
979
|
metavar='<key>',
|
|
930
980
|
action='append',
|
|
981
|
+
dest='properties',
|
|
931
982
|
help=_(
|
|
932
983
|
'Remove a property from volume '
|
|
933
984
|
'(repeat option to remove multiple properties)'
|
|
@@ -937,6 +988,7 @@ class UnsetVolume(command.Command):
|
|
|
937
988
|
'--image-property',
|
|
938
989
|
metavar='<key>',
|
|
939
990
|
action='append',
|
|
991
|
+
dest='image_properties',
|
|
940
992
|
help=_(
|
|
941
993
|
'Remove an image property from volume '
|
|
942
994
|
'(repeat option to remove multiple image properties)'
|
|
@@ -949,22 +1001,22 @@ class UnsetVolume(command.Command):
|
|
|
949
1001
|
volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
|
|
950
1002
|
|
|
951
1003
|
result = 0
|
|
952
|
-
if parsed_args.
|
|
1004
|
+
if parsed_args.properties:
|
|
953
1005
|
try:
|
|
954
1006
|
volume_client.volumes.delete_metadata(
|
|
955
|
-
volume.id, parsed_args.
|
|
1007
|
+
volume.id, parsed_args.properties
|
|
956
1008
|
)
|
|
957
1009
|
except Exception as e:
|
|
958
|
-
LOG.error(_("Failed to unset volume
|
|
1010
|
+
LOG.error(_("Failed to unset volume properties: %s"), e)
|
|
959
1011
|
result += 1
|
|
960
1012
|
|
|
961
|
-
if parsed_args.
|
|
1013
|
+
if parsed_args.image_properties:
|
|
962
1014
|
try:
|
|
963
1015
|
volume_client.volumes.delete_image_metadata(
|
|
964
|
-
volume.id, parsed_args.
|
|
1016
|
+
volume.id, parsed_args.image_properties
|
|
965
1017
|
)
|
|
966
1018
|
except Exception as e:
|
|
967
|
-
LOG.error(_("Failed to unset image
|
|
1019
|
+
LOG.error(_("Failed to unset image properties: %s"), e)
|
|
968
1020
|
result += 1
|
|
969
1021
|
|
|
970
1022
|
if result > 0:
|
|
@@ -417,13 +417,19 @@ class SetVolumeBackup(command.Command):
|
|
|
417
417
|
return parser
|
|
418
418
|
|
|
419
419
|
def take_action(self, parsed_args):
|
|
420
|
-
volume_client = self.app.client_manager.volume
|
|
421
|
-
|
|
420
|
+
volume_client = self.app.client_manager.sdk_connection.volume
|
|
421
|
+
|
|
422
|
+
backup = volume_client.find_backup(
|
|
423
|
+
parsed_args.backup,
|
|
424
|
+
ignore_missing=False,
|
|
425
|
+
)
|
|
422
426
|
|
|
423
427
|
result = 0
|
|
424
428
|
if parsed_args.state:
|
|
425
429
|
try:
|
|
426
|
-
volume_client.
|
|
430
|
+
volume_client.reset_backup_status(
|
|
431
|
+
backup, status=parsed_args.state
|
|
432
|
+
)
|
|
427
433
|
except Exception as e:
|
|
428
434
|
LOG.error(_("Failed to set backup state: %s"), e)
|
|
429
435
|
result += 1
|