Skip to content

Commit 3864cee

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "compute: Add 'server volume update' command"
2 parents 1febc8c + ca7f23d commit 3864cee

7 files changed

Lines changed: 201 additions & 10 deletions

File tree

lower-constraints.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ python-cinderclient==3.3.0
7171
python-dateutil==2.5.3
7272
python-keystoneclient==3.22.0
7373
python-mimeparse==1.6.0
74-
python-novaclient==15.1.0
74+
python-novaclient==17.0.0
7575
python-subunit==1.0.0
7676
pytz==2013.6
7777
PyYAML==3.13

openstackclient/compute/v2/server.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -513,27 +513,27 @@ def get_parser(self, prog_name):
513513
'--tag',
514514
metavar='<tag>',
515515
help=_(
516-
"Tag for the attached volume. "
517-
"(Supported by API versions '2.49' - '2.latest')"
516+
'Tag for the attached volume '
517+
'(supported by --os-compute-api-version 2.49 or above)'
518518
),
519519
)
520+
# TODO(stephenfin): These should be called 'delete-on-termination' and
521+
# 'preserve-on-termination'
520522
termination_group = parser.add_mutually_exclusive_group()
521523
termination_group.add_argument(
522524
'--enable-delete-on-termination',
523525
action='store_true',
524526
help=_(
525-
"Specify if the attached volume should be deleted when the "
526-
"server is destroyed. "
527-
"(Supported by API versions '2.79' - '2.latest')"
527+
'Delete the volume when the server is destroyed '
528+
'(supported by --os-compute-api-version 2.79 or above)'
528529
),
529530
)
530531
termination_group.add_argument(
531532
'--disable-delete-on-termination',
532533
action='store_true',
533534
help=_(
534-
"Specify if the attached volume should not be deleted when "
535-
"the server is destroyed. "
536-
"(Supported by API versions '2.79' - '2.latest')"
535+
'Do not delete the volume when the server is destroyed '
536+
'(supported by --os-compute-api-version 2.79 or above)'
537537
),
538538
)
539539
return parser

openstackclient/compute/v2/server_volume.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from novaclient import api_versions
1818
from osc_lib.command import command
19+
from osc_lib import exceptions
1920
from osc_lib import utils
2021

2122
from openstackclient.i18n import _
@@ -71,3 +72,69 @@ def take_action(self, parsed_args):
7172
) for s in volumes
7273
),
7374
)
75+
76+
77+
class UpdateServerVolume(command.Command):
78+
"""Update a volume attachment on the server."""
79+
80+
def get_parser(self, prog_name):
81+
parser = super(UpdateServerVolume, self).get_parser(prog_name)
82+
parser.add_argument(
83+
'server',
84+
help=_('Server to update volume for (name or ID)'),
85+
)
86+
parser.add_argument(
87+
'volume',
88+
help=_('Volume (ID)'),
89+
)
90+
termination_group = parser.add_mutually_exclusive_group()
91+
termination_group.add_argument(
92+
'--delete-on-termination',
93+
action='store_true',
94+
dest='delete_on_termination',
95+
default=None,
96+
help=_(
97+
'Delete the volume when the server is destroyed '
98+
'(supported by --os-compute-api-version 2.85 or above)'
99+
),
100+
)
101+
termination_group.add_argument(
102+
'--preserve-on-termination',
103+
action='store_false',
104+
dest='delete_on_termination',
105+
help=_(
106+
'Preserve the volume when the server is destroyed '
107+
'(supported by --os-compute-api-version 2.85 or above)'
108+
),
109+
)
110+
return parser
111+
112+
def take_action(self, parsed_args):
113+
114+
compute_client = self.app.client_manager.compute
115+
116+
if parsed_args.delete_on_termination is not None:
117+
if compute_client.api_version < api_versions.APIVersion('2.85'):
118+
msg = _(
119+
'--os-compute-api-version 2.85 or greater is required to '
120+
'support the --(no-)delete-on-termination option'
121+
)
122+
raise exceptions.CommandError(msg)
123+
124+
server = utils.find_resource(
125+
compute_client.servers,
126+
parsed_args.server,
127+
)
128+
129+
# NOTE(stephenfin): This may look silly, and that's because it is.
130+
# This API was originally used only for the swapping volumes, which
131+
# is an internal operation that should only be done by
132+
# orchestration software rather than a human. We're not going to
133+
# expose that, but we are going to expose the ability to change the
134+
# delete on termination behavior.
135+
compute_client.volumes.update_server_volume(
136+
server.id,
137+
parsed_args.volume,
138+
parsed_args.volume,
139+
delete_on_termination=parsed_args.delete_on_termination,
140+
)

openstackclient/tests/unit/compute/v2/test_server_volume.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#
1313

1414
from novaclient import api_versions
15+
from osc_lib import exceptions
1516

1617
from openstackclient.compute.v2 import server_volume
1718
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -165,3 +166,122 @@ def test_server_volume_list_with_delete_on_attachment(self):
165166
)
166167
self.servers_volumes_mock.get_server_volumes.assert_called_once_with(
167168
self.server.id)
169+
170+
171+
class TestServerVolumeUpdate(TestServerVolume):
172+
173+
def setUp(self):
174+
super().setUp()
175+
176+
self.server = compute_fakes.FakeServer.create_one_server()
177+
self.servers_mock.get.return_value = self.server
178+
179+
# Get the command object to test
180+
self.cmd = server_volume.UpdateServerVolume(self.app, None)
181+
182+
def test_server_volume_update(self):
183+
184+
arglist = [
185+
self.server.id,
186+
'foo',
187+
]
188+
verifylist = [
189+
('server', self.server.id),
190+
('volume', 'foo'),
191+
('delete_on_termination', None),
192+
]
193+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
194+
195+
result = self.cmd.take_action(parsed_args)
196+
197+
# This is a no-op
198+
self.servers_volumes_mock.update_server_volume.assert_not_called()
199+
self.assertIsNone(result)
200+
201+
def test_server_volume_update_with_delete_on_termination(self):
202+
self.app.client_manager.compute.api_version = \
203+
api_versions.APIVersion('2.85')
204+
205+
arglist = [
206+
self.server.id,
207+
'foo',
208+
'--delete-on-termination',
209+
]
210+
verifylist = [
211+
('server', self.server.id),
212+
('volume', 'foo'),
213+
('delete_on_termination', True),
214+
]
215+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
216+
217+
result = self.cmd.take_action(parsed_args)
218+
219+
self.servers_volumes_mock.update_server_volume.assert_called_once_with(
220+
self.server.id, 'foo', 'foo',
221+
delete_on_termination=True)
222+
self.assertIsNone(result)
223+
224+
def test_server_volume_update_with_preserve_on_termination(self):
225+
self.app.client_manager.compute.api_version = \
226+
api_versions.APIVersion('2.85')
227+
228+
arglist = [
229+
self.server.id,
230+
'foo',
231+
'--preserve-on-termination',
232+
]
233+
verifylist = [
234+
('server', self.server.id),
235+
('volume', 'foo'),
236+
('delete_on_termination', False),
237+
]
238+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
239+
240+
result = self.cmd.take_action(parsed_args)
241+
242+
self.servers_volumes_mock.update_server_volume.assert_called_once_with(
243+
self.server.id, 'foo', 'foo',
244+
delete_on_termination=False)
245+
self.assertIsNone(result)
246+
247+
def test_server_volume_update_with_delete_on_termination_pre_v285(self):
248+
self.app.client_manager.compute.api_version = \
249+
api_versions.APIVersion('2.84')
250+
251+
arglist = [
252+
self.server.id,
253+
'foo',
254+
'--delete-on-termination',
255+
]
256+
verifylist = [
257+
('server', self.server.id),
258+
('volume', 'foo'),
259+
('delete_on_termination', True),
260+
]
261+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
262+
263+
self.assertRaises(
264+
exceptions.CommandError,
265+
self.cmd.take_action,
266+
parsed_args)
267+
268+
def test_server_volume_update_with_preserve_on_termination_pre_v285(self):
269+
self.app.client_manager.compute.api_version = \
270+
api_versions.APIVersion('2.84')
271+
272+
arglist = [
273+
self.server.id,
274+
'foo',
275+
'--preserve-on-termination',
276+
]
277+
verifylist = [
278+
('server', self.server.id),
279+
('volume', 'foo'),
280+
('delete_on_termination', False),
281+
]
282+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
283+
284+
self.assertRaises(
285+
exceptions.CommandError,
286+
self.cmd.take_action,
287+
parsed_args)

releasenotes/notes/add-server-volume-update-89740dca61596dd1.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ features:
33
- |
44
Add ``server volume list`` command, to list the volumes attached to an
55
instance.
6+
- |
7+
Add ``server volume update`` command, to update the volumes attached to
8+
an instance.

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ openstacksdk>=0.52.0 # Apache-2.0
99
osc-lib>=2.3.0 # Apache-2.0
1010
oslo.i18n>=3.15.3 # Apache-2.0
1111
python-keystoneclient>=3.22.0 # Apache-2.0
12-
python-novaclient>=15.1.0 # Apache-2.0
12+
python-novaclient>=17.0.0 # Apache-2.0
1313
python-cinderclient>=3.3.0 # Apache-2.0
1414
stevedore>=2.0.1 # Apache-2.0

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ openstack.compute.v2 =
154154
server_image_create = openstackclient.compute.v2.server_image:CreateServerImage
155155

156156
server_volume_list = openstackclient.compute.v2.server_volume:ListServerVolume
157+
server_volume_update = openstackclient.compute.v2.server_volume:UpdateServerVolume
157158

158159
usage_list = openstackclient.compute.v2.usage:ListUsage
159160
usage_show = openstackclient.compute.v2.usage:ShowUsage

0 commit comments

Comments
 (0)