Merge "Support listing volume availability zones"

This commit is contained in:
Jenkins 2016-01-22 19:23:20 +00:00 committed by Gerrit Code Review
commit ee78fb2f78
6 changed files with 224 additions and 26 deletions

View File

@ -2,7 +2,7 @@
availability zone
=================
Compute v2
Compute v2, Block Storage v2
availability zone list
----------------------
@ -13,8 +13,18 @@ List availability zones and their status
.. code:: bash
os availability zone list
[--compute]
[--volume]
[--long]
.. option:: --compute
List compute availability zones
.. option:: --volume
List volume availability zones
.. option:: --long
List additional fields in output

View File

@ -70,7 +70,7 @@ the API resources will be merged, as in the ``quota`` object that has options
referring to both Compute and Volume quotas.
* ``access token``: (**Identity**) long-lived OAuth-based token
* ``availability zone``: (**Compute**) a logical partition of hosts or block storage services
* ``availability zone``: (**Compute**, **Volume**) a logical partition of hosts or block storage services
* ``aggregate``: (**Compute**) a grouping of servers
* ``backup``: (**Volume**) a volume copy
* ``catalog``: (**Identity**) service catalog

View File

@ -11,7 +11,7 @@
# under the License.
#
"""Compute v2 Availability Zone action implementations"""
"""Availability Zone action implementations"""
import copy
import logging
@ -24,15 +24,19 @@ from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
def _xform_availability_zone(az, include_extra):
result = []
zone_info = {}
def _xform_common_availability_zone(az, zone_info):
if hasattr(az, 'zoneState'):
zone_info['zone_status'] = ('available' if az.zoneState['available']
else 'not available')
if hasattr(az, 'zoneName'):
zone_info['zone_name'] = az.zoneName
def _xform_compute_availability_zone(az, include_extra):
result = []
zone_info = {}
_xform_common_availability_zone(az, zone_info)
if not include_extra:
result.append(zone_info)
return result
@ -58,6 +62,14 @@ def _xform_availability_zone(az, include_extra):
return result
def _xform_volume_availability_zone(az):
result = []
zone_info = {}
_xform_common_availability_zone(az, zone_info)
result.append(zone_info)
return result
class ListAvailabilityZone(lister.Lister):
"""List availability zones and their status"""
@ -65,6 +77,16 @@ class ListAvailabilityZone(lister.Lister):
def get_parser(self, prog_name):
parser = super(ListAvailabilityZone, self).get_parser(prog_name)
parser.add_argument(
'--compute',
action='store_true',
default=False,
help='List compute availability zones')
parser.add_argument(
'--volume',
action='store_true',
default=False,
help='List volume availability zones')
parser.add_argument(
'--long',
action='store_true',
@ -73,15 +95,7 @@ class ListAvailabilityZone(lister.Lister):
)
return parser
@utils.log_method(log)
def take_action(self, parsed_args):
if parsed_args.long:
columns = ('Zone Name', 'Zone Status',
'Host Name', 'Service Name', 'Service Status')
else:
columns = ('Zone Name', 'Zone Status')
def get_compute_availability_zones(self, parsed_args):
compute_client = self.app.client_manager.compute
try:
data = compute_client.availability_zones.list()
@ -94,7 +108,40 @@ class ListAvailabilityZone(lister.Lister):
# Argh, the availability zones are not iterable...
result = []
for zone in data:
result += _xform_availability_zone(zone, parsed_args.long)
result += _xform_compute_availability_zone(zone, parsed_args.long)
return result
def get_volume_availability_zones(self, parsed_args):
volume_client = self.app.client_manager.volume
try:
data = volume_client.availability_zones.list()
except Exception:
message = "Availability zones list not supported by " \
"Block Storage API"
self.log.warning(message)
result = []
for zone in data:
result += _xform_volume_availability_zone(zone)
return result
@utils.log_method(log)
def take_action(self, parsed_args):
if parsed_args.long:
columns = ('Zone Name', 'Zone Status',
'Host Name', 'Service Name', 'Service Status')
else:
columns = ('Zone Name', 'Zone Status')
# Show everything by default.
show_all = (not parsed_args.compute and not parsed_args.volume)
result = []
if parsed_args.compute or show_all:
result += self.get_compute_availability_zones(parsed_args)
if parsed_args.volume or show_all:
result += self.get_volume_availability_zones(parsed_args)
return (columns,
(utils.get_dict_properties(

View File

@ -17,6 +17,7 @@ from openstackclient.common import availability_zone
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests import utils
from openstackclient.tests.volume.v2 import fakes as volume_fakes
def _build_compute_az_datalist(compute_az, long_datalist=False):
@ -39,6 +40,22 @@ def _build_compute_az_datalist(compute_az, long_datalist=False):
return (datalist,)
def _build_volume_az_datalist(volume_az, long_datalist=False):
datalist = ()
if not long_datalist:
datalist = (
volume_az.zoneName,
'available',
)
else:
datalist = (
volume_az.zoneName,
'available',
'', '', '',
)
return (datalist,)
class TestAvailabilityZone(utils.TestCommand):
def setUp(self):
@ -53,16 +70,37 @@ class TestAvailabilityZone(utils.TestCommand):
self.compute_azs_mock = compute_client.availability_zones
self.compute_azs_mock.reset_mock()
volume_client = volume_fakes.FakeVolumeClient(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
self.app.client_manager.volume = volume_client
self.volume_azs_mock = volume_client.availability_zones
self.volume_azs_mock.reset_mock()
class TestAvailabilityZoneList(TestAvailabilityZone):
compute_azs = \
compute_fakes.FakeAvailabilityZone.create_availability_zones()
volume_azs = \
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
short_columnslist = ('Zone Name', 'Zone Status')
long_columnslist = (
'Zone Name',
'Zone Status',
'Host Name',
'Service Name',
'Service Status',
)
def setUp(self):
super(TestAvailabilityZoneList, self).setUp()
self.compute_azs_mock.list.return_value = self.compute_azs
self.volume_azs_mock.list.return_value = self.volume_azs
# Get the command object to test
self.cmd = availability_zone.ListAvailabilityZone(self.app, None)
@ -76,12 +114,14 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
columns, data = self.cmd.take_action(parsed_args)
self.compute_azs_mock.list.assert_called_with()
self.volume_azs_mock.list.assert_called_with()
columnslist = ('Zone Name', 'Zone Status')
self.assertEqual(columnslist, columns)
self.assertEqual(self.short_columnslist, columns)
datalist = ()
for compute_az in self.compute_azs:
datalist += _build_compute_az_datalist(compute_az)
for volume_az in self.volume_azs:
datalist += _build_volume_az_datalist(volume_az)
self.assertEqual(datalist, tuple(data))
def test_availability_zone_list_long(self):
@ -97,17 +137,56 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
columns, data = self.cmd.take_action(parsed_args)
self.compute_azs_mock.list.assert_called_with()
self.volume_azs_mock.list.assert_called_with()
columnslist = (
'Zone Name',
'Zone Status',
'Host Name',
'Service Name',
'Service Status',
)
self.assertEqual(columnslist, columns)
self.assertEqual(self.long_columnslist, columns)
datalist = ()
for compute_az in self.compute_azs:
datalist += _build_compute_az_datalist(compute_az,
long_datalist=True)
for volume_az in self.volume_azs:
datalist += _build_volume_az_datalist(volume_az,
long_datalist=True)
self.assertEqual(datalist, tuple(data))
def test_availability_zone_list_compute(self):
arglist = [
'--compute',
]
verifylist = [
('compute', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.compute_azs_mock.list.assert_called_with()
self.volume_azs_mock.list.assert_not_called()
self.assertEqual(self.short_columnslist, columns)
datalist = ()
for compute_az in self.compute_azs:
datalist += _build_compute_az_datalist(compute_az)
self.assertEqual(datalist, tuple(data))
def test_availability_zone_list_volume(self):
arglist = [
'--volume',
]
verifylist = [
('volume', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.compute_azs_mock.list.assert_not_called()
self.volume_azs_mock.list.assert_called_with()
self.assertEqual(self.short_columnslist, columns)
datalist = ()
for volume_az in self.volume_azs:
datalist += _build_volume_az_datalist(volume_az)
self.assertEqual(datalist, tuple(data))

View File

@ -202,6 +202,8 @@ class FakeVolumeClient(object):
self.restores.resource_class = fakes.FakeResource(None, {})
self.qos_specs = mock.Mock()
self.qos_specs.resource_class = fakes.FakeResource(None, {})
self.availability_zones = mock.Mock()
self.availability_zones.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@ -304,3 +306,55 @@ class FakeVolume(object):
volumes = FakeVolume.create_volumes(count)
return mock.MagicMock(side_effect=volumes)
class FakeAvailabilityZone(object):
"""Fake one or more volume availability zones (AZs)."""
@staticmethod
def create_one_availability_zone(attrs={}, methods={}):
"""Create a fake AZ.
:param Dictionary attrs:
A dictionary with all attributes
:param Dictionary methods:
A dictionary with all methods
:return:
A FakeResource object with zoneName, zoneState, etc.
"""
# Set default attributes.
availability_zone = {
'zoneName': uuid.uuid4().hex,
'zoneState': {'available': True},
}
# Overwrite default attributes.
availability_zone.update(attrs)
availability_zone = fakes.FakeResource(
info=copy.deepcopy(availability_zone),
methods=methods,
loaded=True)
return availability_zone
@staticmethod
def create_availability_zones(attrs={}, methods={}, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
A dictionary with all attributes
:param Dictionary methods:
A dictionary with all methods
:param int count:
The number of AZs to fake
:return:
A list of FakeResource objects faking the AZs
"""
availability_zones = []
for i in range(0, count):
availability_zone = \
FakeAvailabilityZone.create_one_availability_zone(
attrs, methods)
availability_zones.append(availability_zone)
return availability_zones

View File

@ -0,0 +1,8 @@
---
features:
- |
Add volume support to `os availability zone list`
[Bug `1532945 <https://bugs.launchpad.net/bugs/1532945>`_]
* New `--compute` option to only list compute availability zones.
* New `--volume` option to only list volume availability zones.