Skip to content

Commit d502627

Browse files
committed
compute: Add 'server volume list' command
This replaces the old 'nova volume-attachments' command. Change-Id: Icb98766f98bd1f2469bdb6df62b4624711f98422 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent b349057 commit d502627

5 files changed

Lines changed: 307 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2020, Red Hat 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+
"""Compute v2 Server action implementations"""
16+
17+
from novaclient import api_versions
18+
from osc_lib.command import command
19+
from osc_lib import utils
20+
21+
from openstackclient.i18n import _
22+
23+
24+
class ListServerVolume(command.Lister):
25+
"""List all the volumes attached to a server."""
26+
27+
def get_parser(self, prog_name):
28+
parser = super().get_parser(prog_name)
29+
parser.add_argument(
30+
'server',
31+
help=_('Server to list volume attachments for (name or ID)'),
32+
)
33+
return parser
34+
35+
def take_action(self, parsed_args):
36+
37+
compute_client = self.app.client_manager.compute
38+
39+
server = utils.find_resource(
40+
compute_client.servers,
41+
parsed_args.server,
42+
)
43+
44+
volumes = compute_client.volumes.get_server_volumes(server.id)
45+
46+
columns = (
47+
'id',
48+
'device',
49+
'serverId',
50+
'volumeId',
51+
)
52+
column_headers = (
53+
'ID',
54+
'Device',
55+
'Server ID',
56+
'Volume ID',
57+
)
58+
if compute_client.api_version >= api_versions.APIVersion('2.70'):
59+
columns += ('tag',)
60+
column_headers += ('Tag',)
61+
62+
if compute_client.api_version >= api_versions.APIVersion('2.79'):
63+
columns += ('delete_on_termination',)
64+
column_headers += ('Delete On Termination?',)
65+
66+
return (
67+
column_headers,
68+
(
69+
utils.get_item_properties(
70+
s, columns, mixed_case_fields=('serverId', 'volumeId')
71+
) for s in volumes
72+
),
73+
)

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,3 +1631,63 @@ def create_server_migrations(attrs=None, methods=None, count=2):
16311631
attrs, methods))
16321632

16331633
return migrations
1634+
1635+
1636+
class FakeVolumeAttachment(object):
1637+
"""Fake one or more volume attachments (BDMs)."""
1638+
1639+
@staticmethod
1640+
def create_one_volume_attachment(attrs=None, methods=None):
1641+
"""Create a fake volume attachment.
1642+
1643+
:param Dictionary attrs:
1644+
A dictionary with all attributes
1645+
:param Dictionary methods:
1646+
A dictionary with all methods
1647+
:return:
1648+
A FakeResource object, with id, device, and so on
1649+
"""
1650+
attrs = attrs or {}
1651+
methods = methods or {}
1652+
1653+
# Set default attributes.
1654+
volume_attachment_info = {
1655+
"id": uuid.uuid4().hex,
1656+
"device": "/dev/sdb",
1657+
"serverId": uuid.uuid4().hex,
1658+
"volumeId": uuid.uuid4().hex,
1659+
# introduced in API microversion 2.70
1660+
"tag": "foo",
1661+
# introduced in API microversion 2.79
1662+
"delete_on_termination": True,
1663+
}
1664+
1665+
# Overwrite default attributes.
1666+
volume_attachment_info.update(attrs)
1667+
1668+
volume_attachment = fakes.FakeResource(
1669+
info=copy.deepcopy(volume_attachment_info),
1670+
methods=methods,
1671+
loaded=True)
1672+
return volume_attachment
1673+
1674+
@staticmethod
1675+
def create_volume_attachments(attrs=None, methods=None, count=2):
1676+
"""Create multiple fake volume attachments (BDMs).
1677+
1678+
:param Dictionary attrs:
1679+
A dictionary with all attributes
1680+
:param Dictionary methods:
1681+
A dictionary with all methods
1682+
:param int count:
1683+
The number of server migrations to fake
1684+
:return:
1685+
A list of FakeResource objects faking the volume attachments.
1686+
"""
1687+
volume_attachments = []
1688+
for i in range(0, count):
1689+
volume_attachments.append(
1690+
FakeVolumeAttachment.create_one_volume_attachment(
1691+
attrs, methods))
1692+
1693+
return volume_attachments
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
from novaclient import api_versions
15+
16+
from openstackclient.compute.v2 import server_volume
17+
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
18+
19+
20+
class TestServerVolume(compute_fakes.TestComputev2):
21+
22+
def setUp(self):
23+
super().setUp()
24+
25+
# Get a shortcut to the compute client ServerManager Mock
26+
self.servers_mock = self.app.client_manager.compute.servers
27+
self.servers_mock.reset_mock()
28+
29+
# Get a shortcut to the compute client VolumeManager mock
30+
self.servers_volumes_mock = self.app.client_manager.compute.volumes
31+
self.servers_volumes_mock.reset_mock()
32+
33+
34+
class TestServerVolumeList(TestServerVolume):
35+
36+
def setUp(self):
37+
super().setUp()
38+
39+
self.server = compute_fakes.FakeServer.create_one_server()
40+
self.volume_attachments = (
41+
compute_fakes.FakeVolumeAttachment.create_volume_attachments())
42+
43+
self.servers_mock.get.return_value = self.server
44+
self.servers_volumes_mock.get_server_volumes.return_value = (
45+
self.volume_attachments)
46+
47+
# Get the command object to test
48+
self.cmd = server_volume.ListServerVolume(self.app, None)
49+
50+
def test_server_volume_list(self):
51+
self.app.client_manager.compute.api_version = \
52+
api_versions.APIVersion('2.1')
53+
54+
arglist = [
55+
self.server.id,
56+
]
57+
verifylist = [
58+
('server', self.server.id),
59+
]
60+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
61+
62+
columns, data = self.cmd.take_action(parsed_args)
63+
64+
self.assertEqual(('ID', 'Device', 'Server ID', 'Volume ID'), columns)
65+
self.assertEqual(
66+
(
67+
(
68+
self.volume_attachments[0].id,
69+
self.volume_attachments[0].device,
70+
self.volume_attachments[0].serverId,
71+
self.volume_attachments[0].volumeId,
72+
),
73+
(
74+
self.volume_attachments[1].id,
75+
self.volume_attachments[1].device,
76+
self.volume_attachments[1].serverId,
77+
self.volume_attachments[1].volumeId,
78+
),
79+
),
80+
tuple(data),
81+
)
82+
self.servers_volumes_mock.get_server_volumes.assert_called_once_with(
83+
self.server.id)
84+
85+
def test_server_volume_list_with_tags(self):
86+
self.app.client_manager.compute.api_version = \
87+
api_versions.APIVersion('2.70')
88+
89+
arglist = [
90+
self.server.id,
91+
]
92+
verifylist = [
93+
('server', self.server.id),
94+
]
95+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
96+
97+
columns, data = self.cmd.take_action(parsed_args)
98+
99+
self.assertEqual(
100+
('ID', 'Device', 'Server ID', 'Volume ID', 'Tag',), columns,
101+
)
102+
self.assertEqual(
103+
(
104+
(
105+
self.volume_attachments[0].id,
106+
self.volume_attachments[0].device,
107+
self.volume_attachments[0].serverId,
108+
self.volume_attachments[0].volumeId,
109+
self.volume_attachments[0].tag,
110+
),
111+
(
112+
self.volume_attachments[1].id,
113+
self.volume_attachments[1].device,
114+
self.volume_attachments[1].serverId,
115+
self.volume_attachments[1].volumeId,
116+
self.volume_attachments[1].tag,
117+
),
118+
),
119+
tuple(data),
120+
)
121+
self.servers_volumes_mock.get_server_volumes.assert_called_once_with(
122+
self.server.id)
123+
124+
def test_server_volume_list_with_delete_on_attachment(self):
125+
self.app.client_manager.compute.api_version = \
126+
api_versions.APIVersion('2.79')
127+
128+
arglist = [
129+
self.server.id,
130+
]
131+
verifylist = [
132+
('server', self.server.id),
133+
]
134+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
135+
136+
columns, data = self.cmd.take_action(parsed_args)
137+
138+
self.assertEqual(
139+
(
140+
'ID', 'Device', 'Server ID', 'Volume ID', 'Tag',
141+
'Delete On Termination?',
142+
),
143+
columns,
144+
)
145+
self.assertEqual(
146+
(
147+
(
148+
self.volume_attachments[0].id,
149+
self.volume_attachments[0].device,
150+
self.volume_attachments[0].serverId,
151+
self.volume_attachments[0].volumeId,
152+
self.volume_attachments[0].tag,
153+
self.volume_attachments[0].delete_on_termination,
154+
),
155+
(
156+
self.volume_attachments[1].id,
157+
self.volume_attachments[1].device,
158+
self.volume_attachments[1].serverId,
159+
self.volume_attachments[1].volumeId,
160+
self.volume_attachments[1].tag,
161+
self.volume_attachments[1].delete_on_termination,
162+
),
163+
),
164+
tuple(data),
165+
)
166+
self.servers_volumes_mock.get_server_volumes.assert_called_once_with(
167+
self.server.id)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
Add ``server volume list`` command, to list the volumes attached to an
5+
instance.

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ openstack.compute.v2 =
153153

154154
server_image_create = openstackclient.compute.v2.server_image:CreateServerImage
155155

156+
server_volume_list = openstackclient.compute.v2.server_volume:ListServerVolume
157+
156158
usage_list = openstackclient.compute.v2.usage:ListUsage
157159
usage_show = openstackclient.compute.v2.usage:ShowUsage
158160

0 commit comments

Comments
 (0)