diff --git a/doc/source/command-objects/flavor.rst b/doc/source/command-objects/flavor.rst index 0083da0dec..fa9fd80583 100644 --- a/doc/source/command-objects/flavor.rst +++ b/doc/source/command-objects/flavor.rst @@ -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 ----------- diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index bb89a85b5e..195c9a0de3 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -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)) diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py new file mode 100644 index 0000000000..8f33ccfe71 --- /dev/null +++ b/openstackclient/tests/compute/v2/test_flavor.py @@ -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))