Use cliff formattable columns in volume v1 commands

Partial-Bug: #1687955
Partially implement blueprint osc-formattable-columns

Change-Id: Ib4c5798171e32a8ddc08a37ee1d416e366a71d76
This commit is contained in:
Akihiro Motoki 2017-05-15 07:55:41 +00:00 committed by Dean Troyer
parent 1a21f02bc7
commit 1af3056e30
13 changed files with 323 additions and 169 deletions

View File

@ -93,7 +93,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
"Alpha='c', Beta='b'",
{'Alpha': 'c', 'Beta': 'b'},
cmd_output['properties']
)
@ -114,7 +114,7 @@ class QosTests(common.BaseVolumeTests):
cmd_output['name']
)
self.assertEqual(
"Beta='b'",
{'Beta': 'b'},
cmd_output['properties']
)

View File

@ -204,7 +204,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
"Alpha='a', Beta='b'",
{'Alpha': 'a', 'Beta': 'b'},
cmd_output["properties"],
)
@ -221,7 +221,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
"Beta='b'",
{'Beta': 'b'},
cmd_output["properties"],
)
@ -236,7 +236,4 @@ class VolumeSnapshotTests(common.BaseVolumeTests):
'volume snapshot show -f json ' +
new_name
))
self.assertNotIn(
"Beta='b'",
cmd_output["properties"],
)
self.assertEqual({}, cmd_output["properties"])

View File

@ -123,7 +123,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
"Alpha='a'",
{'Alpha': 'a'},
cmd_output["properties"],
)
self.assertEqual(
@ -165,7 +165,7 @@ class VolumeTests(common.BaseVolumeTests):
cmd_output["display_description"],
)
self.assertEqual(
"Beta='b', Gamma='c'",
{'Beta': 'b', 'Gamma': 'c'},
cmd_output["properties"],
)
self.assertEqual(
@ -186,7 +186,7 @@ class VolumeTests(common.BaseVolumeTests):
new_name
))
self.assertEqual(
"Gamma='c'",
{'Gamma': 'c'},
cmd_output["properties"],
)

View File

@ -66,8 +66,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
# TODO(amotoki): properties output should be machine-readable
self.assertEqual("a='b', c='d'", cmd_output['properties'])
self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a %s' % name
@ -76,7 +75,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
self.assertEqual("c='d'", cmd_output['properties'])
self.assertEqual({'c': 'd'}, cmd_output['properties'])
def test_volume_type_set_unset_multiple_properties(self):
name = uuid.uuid4().hex
@ -97,7 +96,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
self.assertEqual("a='b', c='d'", cmd_output['properties'])
self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties'])
raw_output = self.openstack(
'volume type unset --property a --property c %s' % name
@ -106,7 +105,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json %s' % name
))
self.assertEqual("", cmd_output['properties'])
self.assertEqual({}, cmd_output['properties'])
def test_multi_delete(self):
vol_type1 = uuid.uuid4().hex
@ -133,34 +132,32 @@ class VolumeTypeTests(common.BaseVolumeTests):
'--encryption-key-size 128 '
'--encryption-control-location front-end ' +
encryption_type))
# TODO(amotoki): encryption output should be machine-readable
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
"control_location='front-end'"]
for attr in expected:
self.assertIn(attr, cmd_output['encryption'])
expected = {'provider': 'LuksEncryptor',
'cipher': 'aes-xts-plain64',
'key_size': 128,
'control_location': 'front-end'}
for attr, value in expected.items():
self.assertEqual(value, cmd_output['encryption'][attr])
# test show encryption type
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + encryption_type))
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
"control_location='front-end'"]
for attr in expected:
self.assertIn(attr, cmd_output['encryption'])
expected = {'provider': 'LuksEncryptor',
'cipher': 'aes-xts-plain64',
'key_size': 128,
'control_location': 'front-end'}
for attr, value in expected.items():
self.assertEqual(value, cmd_output['encryption'][attr])
# test list encryption type
cmd_output = json.loads(self.openstack(
'volume type list -f json --encryption-type'))
encryption_output = [t['Encryption'] for t in cmd_output
if t['Name'] == encryption_type][0]
# TODO(amotoki): encryption output should be machine-readable
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
"control_location='front-end'"]
for attr in expected:
self.assertIn(attr, encryption_output)
expected = {'provider': 'LuksEncryptor',
'cipher': 'aes-xts-plain64',
'key_size': 128,
'control_location': 'front-end'}
for attr, value in expected.items():
self.assertEqual(value, encryption_output[attr])
# test set new encryption type
raw_output = self.openstack(
'volume type set '
@ -185,12 +182,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
"control_location='front-end'"]
for attr in expected:
self.assertIn(attr, cmd_output['encryption'])
expected = {'provider': 'LuksEncryptor',
'cipher': 'aes-xts-plain64',
'key_size': 128,
'control_location': 'front-end'}
for attr, value in expected.items():
self.assertEqual(value, cmd_output['encryption'][attr])
# test unset encryption type
raw_output = self.openstack(
'volume type unset --encryption-type ' + name
@ -199,7 +196,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
cmd_output = json.loads(self.openstack(
'volume type show -f json --encryption-type ' + name
))
self.assertEqual('', cmd_output['encryption'])
self.assertEqual({}, cmd_output['encryption'])
# test delete encryption type
raw_output = self.openstack('volume type delete ' + encryption_type)
self.assertEqual('', raw_output)

View File

@ -17,6 +17,8 @@ import copy
import mock
from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@ -85,7 +87,7 @@ class TestQosCreate(TestQos):
self.new_qos_spec.consumer,
self.new_qos_spec.id,
self.new_qos_spec.name,
utils.format_dict(self.new_qos_spec.specs)
format_columns.DictColumn(self.new_qos_spec.specs)
)
self.qos_mock.create.return_value = self.new_qos_spec
# Get the command object to test
@ -108,7 +110,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_qos_create_with_consumer(self):
arglist = [
@ -128,7 +130,7 @@ class TestQosCreate(TestQos):
{'consumer': self.new_qos_spec.consumer}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_qos_create_with_properties(self):
arglist = [
@ -154,7 +156,7 @@ class TestQosCreate(TestQos):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
class TestQosDelete(TestQos):
@ -326,8 +328,8 @@ class TestQosList(TestQos):
q.id,
q.name,
q.consumer,
qos_association.name,
utils.format_dict(q.specs),
format_columns.ListColumn([qos_association.name]),
format_columns.DictColumn(q.specs),
))
def setUp(self):
@ -349,7 +351,7 @@ class TestQosList(TestQos):
self.qos_mock.list.assert_called_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
self.assertListItemEqual(self.data, list(data))
def test_qos_list_no_association(self):
self.qos_mock.reset_mock()
@ -373,10 +375,10 @@ class TestQosList(TestQos):
self.qos_specs[1].id,
self.qos_specs[1].name,
self.qos_specs[1].consumer,
None,
utils.format_dict(self.qos_specs[1].specs),
format_columns.ListColumn(None),
format_columns.DictColumn(self.qos_specs[1].specs),
)
self.assertEqual(ex_data, list(data))
self.assertListItemEqual(ex_data, list(data))
class TestQosSet(TestQos):
@ -447,13 +449,13 @@ class TestQosShow(TestQos):
)
self.assertEqual(collist, columns)
datalist = (
self.qos_association.name,
format_columns.ListColumn([self.qos_association.name]),
self.qos_spec.consumer,
self.qos_spec.id,
self.qos_spec.name,
utils.format_dict(self.qos_spec.specs),
format_columns.DictColumn(self.qos_spec.specs),
)
self.assertEqual(datalist, tuple(data))
self.assertItemEqual(datalist, tuple(data))
class TestQosUnset(TestQos):

View File

@ -15,6 +15,7 @@
import mock
from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@ -77,7 +78,7 @@ class TestTypeCreate(TestType):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.assertItemEqual(self.data, data)
def test_type_create_with_encryption(self):
encryption_info = {
@ -102,7 +103,7 @@ class TestTypeCreate(TestType):
)
encryption_data = (
self.new_volume_type.description,
utils.format_dict(encryption_info),
format_columns.DictColumn(encryption_info),
self.new_volume_type.id,
True,
self.new_volume_type.name,
@ -138,7 +139,7 @@ class TestTypeCreate(TestType):
body,
)
self.assertEqual(encryption_columns, columns)
self.assertEqual(encryption_data, data)
self.assertItemEqual(encryption_data, data)
class TestTypeDelete(TestType):
@ -246,7 +247,7 @@ class TestTypeList(TestType):
t.id,
t.name,
t.is_public,
utils.format_dict(t.extra_specs),
format_columns.DictColumn(t.extra_specs),
))
def setUp(self):
@ -269,7 +270,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
self.assertListItemEqual(self.data, list(data))
def test_type_list_with_options(self):
arglist = [
@ -283,7 +284,7 @@ class TestTypeList(TestType):
columns, data = self.cmd.take_action(parsed_args)
self.types_mock.list.assert_called_once_with()
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
self.assertListItemEqual(self.data_long, list(data))
def test_type_list_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type(
@ -302,13 +303,16 @@ class TestTypeList(TestType):
self.volume_types[0].id,
self.volume_types[0].name,
self.volume_types[0].is_public,
utils.format_dict(encryption_info),
volume_type.EncryptionInfoColumn(
self.volume_types[0].id,
{self.volume_types[0].id: encryption_info}),
))
encryption_data.append((
self.volume_types[1].id,
self.volume_types[1].name,
self.volume_types[1].is_public,
'-',
volume_type.EncryptionInfoColumn(
self.volume_types[1].id, {}),
))
self.encryption_types_mock.list.return_value = [encryption_type]
@ -324,7 +328,7 @@ class TestTypeList(TestType):
self.encryption_types_mock.list.assert_called_once_with()
self.types_mock.list.assert_called_once_with()
self.assertEqual(encryption_columns, columns)
self.assertEqual(encryption_data, list(data))
self.assertListItemEqual(encryption_data, list(data))
class TestTypeSet(TestType):
@ -443,7 +447,7 @@ class TestTypeShow(TestType):
self.volume_type.id,
True,
self.volume_type.name,
utils.format_dict(self.volume_type.extra_specs)
format_columns.DictColumn(self.volume_type.extra_specs)
)
self.types_mock.get.return_value = self.volume_type
@ -465,7 +469,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.assertItemEqual(self.data, data)
def test_type_show_with_encryption(self):
encryption_type = volume_fakes.FakeType.create_one_encryption_type()
@ -489,11 +493,11 @@ class TestTypeShow(TestType):
)
encryption_data = (
self.volume_type.description,
utils.format_dict(encryption_info),
format_columns.DictColumn(encryption_info),
self.volume_type.id,
True,
self.volume_type.name,
utils.format_dict(self.volume_type.extra_specs)
format_columns.DictColumn(self.volume_type.extra_specs)
)
arglist = [
'--encryption-type',
@ -509,7 +513,7 @@ class TestTypeShow(TestType):
self.types_mock.get.assert_called_with(self.volume_type.id)
self.encryption_types_mock.get.assert_called_with(self.volume_type.id)
self.assertEqual(encryption_columns, columns)
self.assertEqual(encryption_data, data)
self.assertItemEqual(encryption_data, data)
class TestTypeUnset(TestType):
@ -587,3 +591,30 @@ class TestTypeUnset(TestType):
result = self.cmd.take_action(parsed_args)
self.encryption_types_mock.delete.assert_called_with(self.volume_type)
self.assertIsNone(result)
class TestColumns(TestType):
def test_encryption_info_column_with_info(self):
fake_volume_type = volume_fakes.FakeType.create_one_type()
type_id = fake_volume_type.id
encryption_info = {
'provider': 'LuksEncryptor',
'cipher': None,
'key_size': None,
'control_location': 'front-end',
}
col = volume_type.EncryptionInfoColumn(type_id,
{type_id: encryption_info})
self.assertEqual(utils.format_dict(encryption_info),
col.human_readable())
self.assertEqual(encryption_info, col.machine_readable())
def test_encryption_info_column_without_info(self):
fake_volume_type = volume_fakes.FakeType.create_one_type()
type_id = fake_volume_type.id
col = volume_type.EncryptionInfoColumn(type_id, {})
self.assertEqual('-', col.human_readable())
self.assertIsNone(col.machine_readable())

View File

@ -17,6 +17,8 @@ import argparse
import mock
from mock import call
from osc_lib.cli import format_columns
from osc_lib import exceptions
from osc_lib import utils
@ -88,7 +90,7 @@ class TestVolumeCreate(TestVolume):
self.new_volume.display_description,
self.new_volume.id,
self.new_volume.display_name,
utils.format_dict(self.new_volume.metadata),
format_columns.DictColumn(self.new_volume.metadata),
self.new_volume.size,
self.new_volume.snapshot_id,
self.new_volume.status,
@ -134,7 +136,7 @@ class TestVolumeCreate(TestVolume):
None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_options(self):
arglist = [
@ -178,7 +180,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_user_project_id(self):
# Return a project
@ -225,7 +227,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_user_project_name(self):
# Return a project
@ -272,7 +274,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_properties(self):
arglist = [
@ -313,7 +315,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_image_id(self):
image = image_fakes.FakeImage.create_one_image()
@ -356,7 +358,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_image_name(self):
image = image_fakes.FakeImage.create_one_image()
@ -399,7 +401,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_with_source(self):
self.volumes_mock.get.return_value = self.new_volume
@ -429,7 +431,7 @@ class TestVolumeCreate(TestVolume):
None,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_create_with_bootable_and_readonly(self):
arglist = [
@ -467,7 +469,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@ -509,7 +511,7 @@ class TestVolumeCreate(TestVolume):
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, False)
self.volumes_mock.update_readonly_flag.assert_called_with(
@ -561,7 +563,7 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(2, mock_error.call_count)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
self.volumes_mock.set_bootable.assert_called_with(
self.new_volume.id, True)
self.volumes_mock.update_readonly_flag.assert_called_with(
@ -732,16 +734,13 @@ class TestVolumeList(TestVolume):
'Size',
'Attached to',
)
server = _volume.attachments[0]['server_id']
device = _volume.attachments[0]['device']
msg = 'Attached to %s on %s ' % (server, device)
datalist = (
(
_volume.id,
_volume.display_name,
_volume.status,
_volume.size,
msg,
volume.AttachmentsColumn(_volume.attachments),
),
)
@ -767,7 +766,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_name(self):
arglist = [
@ -784,7 +783,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
self.assertEqual(self.datalist, tuple(data))
self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_status(self):
arglist = [
@ -801,7 +800,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
self.assertEqual(self.datalist, tuple(data))
self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_all_projects(self):
arglist = [
@ -818,7 +817,7 @@ class TestVolumeList(TestVolume):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, tuple(columns))
self.assertEqual(self.datalist, tuple(data))
self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_long(self):
arglist = [
@ -855,10 +854,10 @@ class TestVolumeList(TestVolume):
self._volume.size,
self._volume.volume_type,
self._volume.bootable,
self.msg,
utils.format_dict(self._volume.metadata),
volume.AttachmentsColumn(self._volume.attachments),
format_columns.DictColumn(self._volume.metadata),
), )
self.assertEqual(datalist, tuple(data))
self.assertListItemEqual(datalist, tuple(data))
def test_volume_list_with_limit(self):
arglist = [
@ -883,7 +882,7 @@ class TestVolumeList(TestVolume):
'all_tenants': False, }
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
self.assertListItemEqual(self.datalist, tuple(data))
def test_volume_list_negative_limit(self):
arglist = [
@ -1251,7 +1250,7 @@ class TestVolumeShow(TestVolume):
self._volume.display_description,
self._volume.id,
self._volume.display_name,
utils.format_dict(self._volume.metadata),
format_columns.DictColumn(self._volume.metadata),
self._volume.size,
self._volume.snapshot_id,
self._volume.status,
@ -1274,7 +1273,7 @@ class TestVolumeShow(TestVolume):
self.volumes_mock.get.assert_called_with(self._volume.id)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
self.assertItemEqual(self.datalist, data)
def test_volume_show_backward_compatibility(self):
arglist = [
@ -1339,3 +1338,31 @@ class TestVolumeUnset(TestVolume):
self._volume.id, ['myprop']
)
self.assertIsNone(result)
class TestColumns(TestVolume):
def test_attachments_column_without_server_cache(self):
_volume = volume_fakes.FakeVolume.create_one_volume()
server_id = _volume.attachments[0]['server_id']
device = _volume.attachments[0]['device']
col = volume.AttachmentsColumn(_volume.attachments, {})
self.assertEqual('Attached to %s on %s ' % (server_id, device),
col.human_readable())
self.assertEqual(_volume.attachments, col.machine_readable())
def test_attachments_column_with_server_cache(self):
_volume = volume_fakes.FakeVolume.create_one_volume()
server_id = _volume.attachments[0]['server_id']
device = _volume.attachments[0]['device']
fake_server = mock.Mock()
fake_server.name = 'fake-server-name'
server_cache = {server_id: fake_server}
col = volume.AttachmentsColumn(_volume.attachments, server_cache)
self.assertEqual(
'Attached to %s on %s ' % ('fake-server-name', device),
col.human_readable())
self.assertEqual(_volume.attachments, col.machine_readable())

View File

@ -100,7 +100,7 @@ class TestBackupCreate(TestBackup):
self.new_backup.description,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.assertItemEqual(self.data, data)
def test_backup_create_without_name(self):
arglist = [
@ -124,7 +124,7 @@ class TestBackupCreate(TestBackup):
self.new_backup.description,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.assertItemEqual(self.data, data)
class TestBackupDelete(TestBackup):
@ -240,7 +240,7 @@ class TestBackupList(TestBackup):
b.status,
b.size,
b.availability_zone,
b.volume_id,
volume_backup.VolumeIdColumn(b.volume_id),
b.container,
))
@ -277,7 +277,7 @@ class TestBackupList(TestBackup):
search_opts=search_opts,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
self.assertListItemEqual(self.data, list(data))
def test_backup_list_with_options(self):
arglist = [
@ -309,7 +309,7 @@ class TestBackupList(TestBackup):
search_opts=search_opts,
)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
self.assertListItemEqual(self.data_long, list(data))
class TestBackupRestore(TestBackup):
@ -391,4 +391,4 @@ class TestBackupShow(TestBackup):
self.backups_mock.get.assert_called_with(self.backup.id)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
self.assertItemEqual(self.data, data)

View File

@ -17,6 +17,7 @@
import logging
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@ -95,7 +96,8 @@ class CreateQos(command.ShowOne):
qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
qos_spec._info.update(
{'properties': utils.format_dict(qos_spec._info.pop('specs'))}
{'properties':
format_columns.DictColumn(qos_spec._info.pop('specs'))}
)
return zip(*sorted(six.iteritems(qos_spec._info)))
@ -208,8 +210,8 @@ class ListQos(command.Lister):
(utils.get_dict_properties(
s._info, columns,
formatters={
'Specs': utils.format_dict,
'Associations': utils.format_list
'Specs': format_columns.DictColumn,
'Associations': format_columns.ListColumn
},
) for s in qos_specs_list))
@ -265,10 +267,11 @@ class ShowQos(command.ShowOne):
associations = [association.name
for association in qos_associations]
qos_spec._info.update({
'associations': utils.format_list(associations)
'associations': format_columns.ListColumn(associations)
})
qos_spec._info.update(
{'properties': utils.format_dict(qos_spec._info.pop('specs'))})
{'properties':
format_columns.DictColumn(qos_spec._info.pop('specs'))})
return zip(*sorted(six.iteritems(qos_spec._info)))

View File

@ -16,8 +16,11 @@
"""Volume v1 Volume action implementations"""
import argparse
import functools
import logging
from cliff import columns as cliff_columns
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@ -30,6 +33,37 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
class AttachmentsColumn(cliff_columns.FormattableColumn):
"""Formattable column for attachments column.
Unlike the parent FormattableColumn class, the initializer of the
class takes server_cache as the second argument.
osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
object with a single parameter "column value", so you need to pass
a partially initialized class like
``functools.partial(AttachmentsColumn, server_cache)``.
"""
def __init__(self, value, server_cache=None):
super(AttachmentsColumn, self).__init__(value)
self._server_cache = server_cache or {}
def human_readable(self):
"""Return a formatted string of a volume's attached instances
:rtype: a string of formatted instances
"""
msg = ''
for attachment in self._value:
server = attachment['server_id']
if server in self._server_cache.keys():
server = self._server_cache[server].name
device = attachment['device']
msg += 'Attached to %s on %s ' % (server, device)
return msg
def _check_size_arg(args):
"""Check whether --size option is required or not.
@ -207,7 +241,8 @@ class CreateVolume(command.ShowOne):
# Map 'metadata' column to 'properties'
volume._info.update(
{
'properties': utils.format_dict(volume._info.pop('metadata')),
'properties':
format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type'),
},
)
@ -307,22 +342,6 @@ class ListVolume(command.Lister):
volume_client = self.app.client_manager.volume
compute_client = self.app.client_manager.compute
def _format_attach(attachments):
"""Return a formatted string of a volume's attached instances
:param attachments: a volume.attachments field
:rtype: a string of formatted instances
"""
msg = ''
for attachment in attachments:
server = attachment['server_id']
if server in server_cache.keys():
server = server_cache[server].name
device = attachment['device']
msg += 'Attached to %s on %s ' % (server, device)
return msg
if parsed_args.long:
columns = (
'ID',
@ -368,6 +387,8 @@ class ListVolume(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
AttachmentsColumnWithCache = functools.partial(
AttachmentsColumn, server_cache=server_cache)
search_opts = {
'all_tenants': parsed_args.all_projects,
@ -385,8 +406,8 @@ class ListVolume(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
formatters={'Metadata': utils.format_dict,
'Attachments': _format_attach},
formatters={'Metadata': format_columns.DictColumn,
'Attachments': AttachmentsColumnWithCache},
) for s in data))
@ -575,7 +596,8 @@ class ShowVolume(command.ShowOne):
# Map 'metadata' column to 'properties'
volume._info.update(
{
'properties': utils.format_dict(volume._info.pop('metadata')),
'properties':
format_columns.DictColumn(volume._info.pop('metadata')),
'type': volume._info.pop('volume_type'),
},
)

View File

@ -16,8 +16,10 @@
"""Volume v1 Backup action implementations"""
import copy
import functools
import logging
from cliff import columns as cliff_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@ -29,6 +31,33 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
class VolumeIdColumn(cliff_columns.FormattableColumn):
"""Formattable column for volume ID column.
Unlike the parent FormattableColumn class, the initializer of the
class takes volume_cache as the second argument.
osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
object with a single parameter "column value", so you need to pass
a partially initialized class like
``functools.partial(VolumeIdColumn, volume_cache)``.
"""
def __init__(self, value, volume_cache=None):
super(VolumeIdColumn, self).__init__(value)
self._volume_cache = volume_cache or {}
def human_readable(self):
"""Return a volume name if available
:rtype: either the volume ID or name
"""
volume_id = self._value
volume = volume_id
if volume_id in self._volume_cache.keys():
volume = self._volume_cache[volume_id].display_name
return volume
class CreateVolumeBackup(command.ShowOne):
_description = _("Create new volume backup")
@ -149,18 +178,6 @@ class ListVolumeBackup(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
def _format_volume_id(volume_id):
"""Return a volume name if available
:param volume_id: a volume ID
:rtype: either the volume ID or name
"""
volume = volume_id
if volume_id in volume_cache.keys():
volume = volume_cache[volume_id].display_name
return volume
if parsed_args.long:
columns = ['ID', 'Name', 'Description', 'Status', 'Size',
'Availability Zone', 'Volume ID', 'Container']
@ -178,6 +195,8 @@ class ListVolumeBackup(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
VolumeIdColumnWithCache = functools.partial(VolumeIdColumn,
volume_cache=volume_cache)
filter_volume_id = None
if parsed_args.volume:
@ -196,7 +215,7 @@ class ListVolumeBackup(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
formatters={'Volume ID': _format_volume_id},
formatters={'Volume ID': VolumeIdColumnWithCache},
) for s in data))

View File

@ -16,8 +16,11 @@
"""Volume v1 Snapshot action implementations"""
import copy
import functools
import logging
from cliff import columns as cliff_columns
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@ -30,6 +33,33 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
class VolumeIdColumn(cliff_columns.FormattableColumn):
"""Formattable column for volume ID column.
Unlike the parent FormattableColumn class, the initializer of the
class takes volume_cache as the second argument.
osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
object with a single parameter "column value", so you need to pass
a partially initialized class like
``functools.partial(VolumeIdColumn, volume_cache)``.
"""
def __init__(self, value, volume_cache=None):
super(VolumeIdColumn, self).__init__(value)
self._volume_cache = volume_cache or {}
def human_readable(self):
"""Return a volume name if available
:rtype: either the volume ID or name
"""
volume_id = self._value
volume = volume_id
if volume_id in self._volume_cache.keys():
volume = self._volume_cache[volume_id].display_name
return volume
class CreateVolumeSnapshot(command.ShowOne):
_description = _("Create new volume snapshot")
@ -76,7 +106,8 @@ class CreateVolumeSnapshot(command.ShowOne):
)
snapshot._info.update(
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
{'properties':
format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))
@ -160,18 +191,6 @@ class ListVolumeSnapshot(command.Lister):
def take_action(self, parsed_args):
volume_client = self.app.client_manager.volume
def _format_volume_id(volume_id):
"""Return a volume name if available
:param volume_id: a volume ID
:rtype: either the volume ID or name
"""
volume = volume_id
if volume_id in volume_cache.keys():
volume = volume_cache[volume_id].display_name
return volume
if parsed_args.long:
columns = ['ID', 'Display Name', 'Display Description', 'Status',
'Size', 'Created At', 'Volume ID', 'Metadata']
@ -195,6 +214,8 @@ class ListVolumeSnapshot(command.Lister):
except Exception:
# Just forget it if there's any trouble
pass
VolumeIdColumnWithCache = functools.partial(VolumeIdColumn,
volume_cache=volume_cache)
volume_id = None
if parsed_args.volume:
@ -213,8 +234,8 @@ class ListVolumeSnapshot(command.Lister):
return (column_headers,
(utils.get_item_properties(
s, columns,
formatters={'Metadata': utils.format_dict,
'Volume ID': _format_volume_id},
formatters={'Metadata': format_columns.DictColumn,
'Volume ID': VolumeIdColumnWithCache},
) for s in data))
@ -317,7 +338,8 @@ class ShowVolumeSnapshot(command.ShowOne):
parsed_args.snapshot)
snapshot._info.update(
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
{'properties':
format_columns.DictColumn(snapshot._info.pop('metadata'))}
)
return zip(*sorted(six.iteritems(snapshot._info)))

View File

@ -15,8 +15,11 @@
"""Volume v1 Type action implementations"""
import functools
import logging
from cliff import columns as cliff_columns
from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@ -29,6 +32,36 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
class EncryptionInfoColumn(cliff_columns.FormattableColumn):
"""Formattable column for encryption info column.
Unlike the parent FormattableColumn class, the initializer of the
class takes encryption_data as the second argument.
osc_lib.utils.get_item_properties instantiate cliff FormattableColumn
object with a single parameter "column value", so you need to pass
a partially initialized class like
``functools.partial(EncryptionInfoColumn encryption_data)``.
"""
def __init__(self, value, encryption_data=None):
super(EncryptionInfoColumn, self).__init__(value)
self._encryption_data = encryption_data or {}
def _get_encryption_info(self):
type_id = self._value
return self._encryption_data.get(type_id)
def human_readable(self):
encryption_info = self._get_encryption_info()
if encryption_info:
return utils.format_dict(encryption_info)
else:
return '-'
def machine_readable(self):
return self._get_encryption_info()
def _create_encryption_type(volume_client, volume_type, parsed_args):
if not parsed_args.encryption_provider:
msg = _("'--encryption-provider' should be specified while "
@ -110,7 +143,8 @@ class CreateVolumeType(command.ShowOne):
volume_type._info.pop('extra_specs')
if parsed_args.property:
result = volume_type.set_keys(parsed_args.property)
volume_type._info.update({'properties': utils.format_dict(result)})
volume_type._info.update(
{'properties': format_columns.DictColumn(result)})
if (parsed_args.encryption_provider or
parsed_args.encryption_cipher or
parsed_args.encryption_key_size or
@ -125,7 +159,7 @@ class CreateVolumeType(command.ShowOne):
# add encryption info in result
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
{'encryption': utils.format_dict(encryption._info)})
{'encryption': format_columns.DictColumn(encryption._info)})
volume_type._info.pop("os-volume-type-access:is_public", None)
return zip(*sorted(six.iteritems(volume_type._info)))
@ -196,12 +230,7 @@ class ListVolumeType(command.Lister):
column_headers = ['ID', 'Name', 'Is Public']
data = volume_client.volume_types.list()
def _format_encryption_info(type_id, encryption_data=None):
encryption_data = encryption
encryption_info = '-'
if type_id in encryption_data.keys():
encryption_info = encryption_data[type_id]
return encryption_info
formatters = {'Extra Specs': format_columns.DictColumn}
if parsed_args.encryption_type:
encryption = {}
@ -218,18 +247,21 @@ class ListVolumeType(command.Lister):
for key in del_key:
d._info.pop(key, None)
# save the encryption information with their volume type ID
encryption[volume_type_id] = utils.format_dict(d._info)
encryption[volume_type_id] = d._info
# We need to get volume type ID, then show encryption
# information according to the ID, so use "id" to keep
# difference to the real "ID" column.
columns += ['id']
column_headers += ['Encryption']
_EncryptionInfoColumn = functools.partial(
EncryptionInfoColumn, encryption_data=encryption)
formatters['id'] = _EncryptionInfoColumn
return (column_headers,
(utils.get_item_properties(
s, columns,
formatters={'Extra Specs': utils.format_dict,
'id': _format_encryption_info},
formatters=formatters,
) for s in data))
@ -340,7 +372,8 @@ class ShowVolumeType(command.ShowOne):
volume_client = self.app.client_manager.volume
volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type)
properties = utils.format_dict(volume_type._info.pop('extra_specs'))
properties = format_columns.DictColumn(
volume_type._info.pop('extra_specs'))
volume_type._info.update({'properties': properties})
if parsed_args.encryption_type:
# show encryption type information for this volume type
@ -349,7 +382,8 @@ class ShowVolumeType(command.ShowOne):
volume_type.id)
encryption._info.pop("volume_type_id", None)
volume_type._info.update(
{'encryption': utils.format_dict(encryption._info)})
{'encryption':
format_columns.DictColumn(encryption._info)})
except Exception as e:
LOG.error(_("Failed to display the encryption information "
"of this volume type: %s"), e)