Skip to content

Commit dc6fe04

Browse files
hehalin-hua-cheng
authored andcommitted
Add list feature to volume v2
"volume list" is not in the v2. Co-Authored-By: Lin Hua Cheng <os.lcheng@gmail.com> implements bp: volume-v2 Change-Id: I9f4585202f5f9ec5f4c091278fc6c4036efb1290
1 parent bd022cb commit dc6fe04

5 files changed

Lines changed: 330 additions & 2 deletions

File tree

‎doc/source/command-objects/volume.rst‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
volume
33
======
44

5-
Volume v1
5+
Volume v1, v2
66

77
volume create
88
-------------

‎openstackclient/tests/volume/v2/fakes.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
import copy
1616
import mock
1717

18+
from openstackclient.tests.compute.v2 import fakes as compute_fakes
1819
from openstackclient.tests import fakes
1920
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
2021
from openstackclient.tests.image.v2 import fakes as image_fakes
2122
from openstackclient.tests import utils
2223

24+
volume_attachment_server = copy.deepcopy(compute_fakes.SERVER)
25+
volume_attachment_server['device'] = 'device'
26+
2327
volume_id = "ce26708d-a7f8-4b4b-9861-4a80256615a6"
2428
volume_name = "fake_volume"
2529
volume_description = "fake description"
@@ -34,7 +38,7 @@
3438
volume_metadata_str = "Alpha='a', Beta='b', Gamma='g'"
3539
volume_snapshot_id = 1
3640
volume_availability_zone = "nova"
37-
volume_attachments = ["fake_attachments"]
41+
volume_attachments = [volume_attachment_server]
3842

3943
VOLUME = {
4044
"id": volume_id,

‎openstackclient/tests/volume/v2/test_volume.py‎

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,220 @@ def test_volume_create_image_name(self):
495495
self.assertEqual(datalist, data)
496496

497497

498+
class TestVolumeList(TestVolume):
499+
500+
def setUp(self):
501+
super(TestVolumeList, self).setUp()
502+
503+
self.volumes_mock.list.return_value = [
504+
fakes.FakeResource(
505+
None,
506+
copy.deepcopy(volume_fakes.VOLUME),
507+
loaded=True,
508+
),
509+
]
510+
511+
self.users_mock.get.return_value = [
512+
fakes.FakeResource(
513+
None,
514+
copy.deepcopy(identity_fakes.USER),
515+
loaded=True,
516+
),
517+
]
518+
519+
self.projects_mock.get.return_value = [
520+
fakes.FakeResource(
521+
None,
522+
copy.deepcopy(identity_fakes.PROJECT),
523+
loaded=True,
524+
),
525+
]
526+
527+
# Get the command object to test
528+
self.cmd = volume.ListVolume(self.app, None)
529+
530+
def test_volume_list_no_options(self):
531+
arglist = []
532+
verifylist = [
533+
('long', False),
534+
('all_projects', False),
535+
('name', None),
536+
('status', None),
537+
]
538+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
539+
540+
columns, data = self.cmd.take_action(parsed_args)
541+
542+
collist = [
543+
'ID',
544+
'Display Name',
545+
'Status',
546+
'Size',
547+
'Attached to',
548+
]
549+
self.assertEqual(collist, columns)
550+
551+
server = volume_fakes.volume_attachment_server['id']
552+
device = volume_fakes.volume_attachment_server['device']
553+
msg = 'Attached to %s on %s ' % (server, device)
554+
datalist = ((
555+
volume_fakes.volume_id,
556+
volume_fakes.volume_name,
557+
volume_fakes.volume_status,
558+
volume_fakes.volume_size,
559+
msg,
560+
), )
561+
self.assertEqual(datalist, tuple(data))
562+
563+
def test_volume_list_all_projects_option(self):
564+
arglist = [
565+
'--all-projects',
566+
]
567+
verifylist = [
568+
('long', False),
569+
('all_projects', True),
570+
('name', None),
571+
('status', None),
572+
]
573+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
574+
575+
columns, data = self.cmd.take_action(parsed_args)
576+
577+
collist = [
578+
'ID',
579+
'Display Name',
580+
'Status',
581+
'Size',
582+
'Attached to',
583+
]
584+
self.assertEqual(collist, columns)
585+
586+
server = volume_fakes.volume_attachment_server['id']
587+
device = volume_fakes.volume_attachment_server['device']
588+
msg = 'Attached to %s on %s ' % (server, device)
589+
datalist = ((
590+
volume_fakes.volume_id,
591+
volume_fakes.volume_name,
592+
volume_fakes.volume_status,
593+
volume_fakes.volume_size,
594+
msg,
595+
), )
596+
self.assertEqual(datalist, tuple(data))
597+
598+
def test_volume_list_name(self):
599+
arglist = [
600+
'--name', volume_fakes.volume_name,
601+
]
602+
verifylist = [
603+
('long', False),
604+
('all_projects', False),
605+
('name', volume_fakes.volume_name),
606+
('status', None),
607+
]
608+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
609+
610+
columns, data = self.cmd.take_action(parsed_args)
611+
612+
collist = (
613+
'ID',
614+
'Display Name',
615+
'Status',
616+
'Size',
617+
'Attached to',
618+
)
619+
self.assertEqual(collist, tuple(columns))
620+
621+
server = volume_fakes.volume_attachment_server['id']
622+
device = volume_fakes.volume_attachment_server['device']
623+
msg = 'Attached to %s on %s ' % (server, device)
624+
625+
datalist = ((
626+
volume_fakes.volume_id,
627+
volume_fakes.volume_name,
628+
volume_fakes.volume_status,
629+
volume_fakes.volume_size,
630+
msg,
631+
), )
632+
self.assertEqual(datalist, tuple(data))
633+
634+
def test_volume_list_status(self):
635+
arglist = [
636+
'--status', volume_fakes.volume_status,
637+
]
638+
verifylist = [
639+
('long', False),
640+
('all_projects', False),
641+
('name', None),
642+
('status', volume_fakes.volume_status),
643+
]
644+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
645+
646+
columns, data = self.cmd.take_action(parsed_args)
647+
648+
collist = (
649+
'ID',
650+
'Display Name',
651+
'Status',
652+
'Size',
653+
'Attached to',
654+
)
655+
self.assertEqual(collist, tuple(columns))
656+
657+
server = volume_fakes.volume_attachment_server['id']
658+
device = volume_fakes.volume_attachment_server['device']
659+
msg = 'Attached to %s on %s ' % (server, device)
660+
datalist = ((
661+
volume_fakes.volume_id,
662+
volume_fakes.volume_name,
663+
volume_fakes.volume_status,
664+
volume_fakes.volume_size,
665+
msg,
666+
), )
667+
self.assertEqual(datalist, tuple(data))
668+
669+
def test_volume_list_long(self):
670+
arglist = [
671+
'--long',
672+
]
673+
verifylist = [
674+
('long', True),
675+
('all_projects', False),
676+
('name', None),
677+
('status', None),
678+
]
679+
680+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
681+
682+
columns, data = self.cmd.take_action(parsed_args)
683+
684+
collist = [
685+
'ID',
686+
'Display Name',
687+
'Status',
688+
'Size',
689+
'Type',
690+
'Bootable',
691+
'Attached to',
692+
'Properties',
693+
]
694+
self.assertEqual(collist, columns)
695+
696+
server = volume_fakes.volume_attachment_server['id']
697+
device = volume_fakes.volume_attachment_server['device']
698+
msg = 'Attached to %s on %s ' % (server, device)
699+
datalist = ((
700+
volume_fakes.volume_id,
701+
volume_fakes.volume_name,
702+
volume_fakes.volume_status,
703+
volume_fakes.volume_size,
704+
volume_fakes.volume_type,
705+
'',
706+
msg,
707+
"Alpha='a', Beta='b', Gamma='g'",
708+
), )
709+
self.assertEqual(datalist, tuple(data))
710+
711+
498712
class TestVolumeShow(TestVolume):
499713
def setUp(self):
500714
super(TestVolumeShow, self).setUp()

‎openstackclient/volume/v2/volume.py‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414

1515
"""Volume V2 Volume action implementations"""
1616

17+
import copy
1718
import logging
19+
import os
1820

1921
from cliff import command
22+
from cliff import lister
2023
from cliff import show
2124
import six
2225

@@ -189,6 +192,112 @@ def take_action(self, parsed_args):
189192
return
190193

191194

195+
class ListVolume(lister.Lister):
196+
"""List volumes"""
197+
198+
log = logging.getLogger(__name__ + '.ListVolume')
199+
200+
def get_parser(self, prog_name):
201+
parser = super(ListVolume, self).get_parser(prog_name)
202+
parser.add_argument(
203+
'--all-projects',
204+
action='store_true',
205+
default=bool(int(os.environ.get("ALL_PROJECTS", 0))),
206+
help='Include all projects (admin only)',
207+
)
208+
parser.add_argument(
209+
'--long',
210+
action='store_true',
211+
default=False,
212+
help='List additional fields in output',
213+
)
214+
parser.add_argument(
215+
'--name',
216+
metavar='<name>',
217+
help='Filter results by name',
218+
)
219+
parser.add_argument(
220+
'--status',
221+
metavar='<status>',
222+
help='Filter results by status',
223+
)
224+
return parser
225+
226+
def take_action(self, parsed_args):
227+
self.log.debug('take_action(%s)', parsed_args)
228+
229+
volume_client = self.app.client_manager.volume
230+
compute_client = self.app.client_manager.compute
231+
232+
def _format_attach(attachments):
233+
"""Return a formatted string of a volume's attached instances
234+
235+
:param volume: a volume.attachments field
236+
:rtype: a string of formatted instances
237+
"""
238+
239+
msg = ''
240+
for attachment in attachments:
241+
server = attachment['id']
242+
if server in server_cache:
243+
server = server_cache[server].name
244+
device = attachment['device']
245+
msg += 'Attached to %s on %s ' % (server, device)
246+
return msg
247+
248+
if parsed_args.long:
249+
columns = [
250+
'ID',
251+
'Name',
252+
'Status',
253+
'Size',
254+
'Volume Type',
255+
'Bootable',
256+
'Attachments',
257+
'Metadata',
258+
]
259+
column_headers = copy.deepcopy(columns)
260+
column_headers[1] = 'Display Name'
261+
column_headers[4] = 'Type'
262+
column_headers[6] = 'Attached to'
263+
column_headers[7] = 'Properties'
264+
else:
265+
columns = [
266+
'ID',
267+
'Name',
268+
'Status',
269+
'Size',
270+
'Attachments',
271+
]
272+
column_headers = copy.deepcopy(columns)
273+
column_headers[1] = 'Display Name'
274+
column_headers[4] = 'Attached to'
275+
276+
# Cache the server list
277+
server_cache = {}
278+
try:
279+
for s in compute_client.servers.list():
280+
server_cache[s.id] = s
281+
except Exception:
282+
# Just forget it if there's any trouble
283+
pass
284+
285+
search_opts = {
286+
'all_projects': parsed_args.all_projects,
287+
'display_name': parsed_args.name,
288+
'status': parsed_args.status,
289+
}
290+
291+
data = volume_client.volumes.list(search_opts=search_opts)
292+
293+
return (column_headers,
294+
(utils.get_item_properties(
295+
s, columns,
296+
formatters={'Metadata': utils.format_dict,
297+
'Attachments': _format_attach},
298+
) for s in data))
299+
300+
192301
class SetVolume(show.ShowOne):
193302
"""Set volume properties"""
194303

‎setup.cfg‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ openstack.volume.v2 =
393393
snapshot_show = openstackclient.volume.v2.snapshot:ShowSnapshot
394394
snapshot_unset = openstackclient.volume.v2.snapshot:UnsetSnapshot
395395

396+
volume_list = openstackclient.volume.v2.volume:ListVolume
396397
volume_create = openstackclient.volume.v2.volume:CreateVolume
397398
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
398399
volume_set = openstackclient.volume.v2.volume:SetVolume

0 commit comments

Comments
 (0)