Added capabilities/options to 'openstack flavor list'

command to match capabilities already provided by
'nova flavor-list':

—public | --private | --all options allows to list
public only (default), private only or all flavors.

—long displays more field. This option also fetches
properties (extra-specs) for each flavor using separate
API call for each flavor and it’s too slow to be done by
default. However as an administrator - sometimes I need
to be able to see them for all flavors.

Removed empty “Extra Specs” column as well as "Swap" and
"RXTX Factor" from default output.

DocImpact
Closes-Bug: #1416780

Change-Id: I30cfa2c75e28daf17b936543a177c23ae1743c37
This commit is contained in:
Igor_Bolotin 2015-01-30 13:04:45 -08:00 committed by Igor_Bolotin
parent 6fd5fc2d27
commit 6d4678185d
3 changed files with 344 additions and 6 deletions

View File

@ -89,6 +89,24 @@ List flavors
.. code:: bash
os flavor list
[--public | --private | --all]
[--long]
.. option:: --public
List only public flavors (default)
.. option:: --private
List only private flavors
.. option:: --all
List all flavors, whether public or private
.. option:: --long
List additional fields in output
flavor show
-----------

View File

@ -152,6 +152,36 @@ class ListFlavor(lister.Lister):
log = logging.getLogger(__name__ + ".ListFlavor")
def get_parser(self, prog_name):
parser = super(ListFlavor, self).get_parser(prog_name)
public_group = parser.add_mutually_exclusive_group()
public_group.add_argument(
"--public",
dest="public",
action="store_true",
default=True,
help="List only public flavors (default)",
)
public_group.add_argument(
"--private",
dest="public",
action="store_false",
help="List only private flavors",
)
public_group.add_argument(
"--all",
dest="all",
action="store_true",
default=False,
help="List all flavors, whether public or private",
)
parser.add_argument(
'--long',
action='store_true',
default=False,
help='List additional fields in output')
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
compute_client = self.app.client_manager.compute
@ -161,16 +191,32 @@ class ListFlavor(lister.Lister):
"RAM",
"Disk",
"Ephemeral",
"Swap",
"VCPUs",
"RXTX Factor",
"Is Public",
"Extra Specs"
)
data = compute_client.flavors.list()
return (columns,
# is_public is ternary - None means give all flavors,
# True is public only and False is private only
# By default Nova assumes True and gives admins public flavors
# and flavors from their own projects only.
is_public = None if parsed_args.all else parsed_args.public
data = compute_client.flavors.list(is_public=is_public)
if parsed_args.long:
columns = columns + (
"Swap",
"RXTX Factor",
"Properties",
)
for f in data:
f.properties = f.get_keys()
column_headers = columns
return (column_headers,
(utils.get_item_properties(
s, columns,
s, columns, formatters={'Properties': utils.format_dict},
) for s in data))

View File

@ -0,0 +1,274 @@
# Copyright 2015 Symantec Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import copy
from openstackclient.compute.v2 import flavor
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
class FakeFlavorResource(fakes.FakeResource):
def get_keys(self):
return {'property': 'value'}
class TestFlavor(compute_fakes.TestComputev2):
def setUp(self):
super(TestFlavor, self).setUp()
# Get a shortcut to the FlavorManager Mock
self.flavors_mock = self.app.client_manager.compute.flavors
self.flavors_mock.reset_mock()
class TestFlavorList(TestFlavor):
def setUp(self):
super(TestFlavorList, self).setUp()
self.flavors_mock.list.return_value = [
FakeFlavorResource(
None,
copy.deepcopy(compute_fakes.FLAVOR),
loaded=True,
),
]
# Get the command object to test
self.cmd = flavor.ListFlavor(self.app, None)
def test_flavor_list_no_options(self):
arglist = []
verifylist = [
('public', True),
('all', False),
('long', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'is_public': True
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
collist = (
'ID',
'Name',
'RAM',
'Disk',
'Ephemeral',
'VCPUs',
'Is Public',
)
self.assertEqual(collist, columns)
datalist = ((
compute_fakes.flavor_id,
compute_fakes.flavor_name,
compute_fakes.flavor_ram,
'',
'',
compute_fakes.flavor_vcpus,
''
), )
self.assertEqual(datalist, tuple(data))
def test_flavor_list_all_flavors(self):
arglist = [
'--all',
]
verifylist = [
('all', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'is_public': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
collist = (
'ID',
'Name',
'RAM',
'Disk',
'Ephemeral',
'VCPUs',
'Is Public',
)
self.assertEqual(collist, columns)
datalist = ((
compute_fakes.flavor_id,
compute_fakes.flavor_name,
compute_fakes.flavor_ram,
'',
'',
compute_fakes.flavor_vcpus,
''
), )
self.assertEqual(datalist, tuple(data))
def test_flavor_list_private_flavors(self):
arglist = [
'--private',
]
verifylist = [
('public', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'is_public': False
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
collist = (
'ID',
'Name',
'RAM',
'Disk',
'Ephemeral',
'VCPUs',
'Is Public',
)
self.assertEqual(collist, columns)
datalist = ((
compute_fakes.flavor_id,
compute_fakes.flavor_name,
compute_fakes.flavor_ram,
'',
'',
compute_fakes.flavor_vcpus,
''
), )
self.assertEqual(datalist, tuple(data))
def test_flavor_list_public_flavors(self):
arglist = [
'--public',
]
verifylist = [
('public', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'is_public': True
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
collist = (
'ID',
'Name',
'RAM',
'Disk',
'Ephemeral',
'VCPUs',
'Is Public',
)
self.assertEqual(collist, columns)
datalist = ((
compute_fakes.flavor_id,
compute_fakes.flavor_name,
compute_fakes.flavor_ram,
'',
'',
compute_fakes.flavor_vcpus,
''
), )
self.assertEqual(datalist, tuple(data))
def test_flavor_list_long(self):
arglist = [
'--long',
]
verifylist = [
('long', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'is_public': True
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
collist = (
'ID',
'Name',
'RAM',
'Disk',
'Ephemeral',
'VCPUs',
'Is Public',
'Swap',
'RXTX Factor',
'Properties'
)
self.assertEqual(collist, columns)
datalist = ((
compute_fakes.flavor_id,
compute_fakes.flavor_name,
compute_fakes.flavor_ram,
'',
'',
compute_fakes.flavor_vcpus,
'',
'',
'',
'property=\'value\''
), )
self.assertEqual(datalist, tuple(data))