Support listing network availability zones
Update the "os availability zone list" command to support listing network availability zones along with the currently listed compute and volume availability zones. This adds the --network option to the command in order to only list network availability zones. By default, all availability zones are listed. The --long option was also updated to include a "Zone Resource" column which is applicable to network availability zones. Example zone resources include "network" and "router". If the Network API does not support listing availability zones then a warning message will be issued when the --network option is specified. This support requires an updated release of the SDK in order to pull in [1]. [1] https://bugs.launchpad.net/python-openstacksdk/+bug/1532274 Change-Id: I78811d659b793d9d2111ea54665d5fe7e4887264 Closes-Bug: #1534202
This commit is contained in:
parent
f36177ebdd
commit
4d332defbc
@ -2,7 +2,7 @@
|
||||
availability zone
|
||||
=================
|
||||
|
||||
Compute v2, Block Storage v2
|
||||
Block Storage v2, Compute v2, Network v2
|
||||
|
||||
availability zone list
|
||||
----------------------
|
||||
@ -14,6 +14,7 @@ List availability zones and their status
|
||||
|
||||
os availability zone list
|
||||
[--compute]
|
||||
[--network]
|
||||
[--volume]
|
||||
[--long]
|
||||
|
||||
@ -21,6 +22,10 @@ List availability zones and their status
|
||||
|
||||
List compute availability zones
|
||||
|
||||
.. option:: --network
|
||||
|
||||
List network availability zones
|
||||
|
||||
.. option:: --volume
|
||||
|
||||
List volume availability zones
|
||||
|
@ -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**, **Volume**) a logical partition of hosts or block storage services
|
||||
* ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services
|
||||
* ``aggregate``: (**Compute**) a grouping of servers
|
||||
* ``backup``: (**Volume**) a volume copy
|
||||
* ``catalog``: (**Identity**) service catalog
|
||||
|
@ -30,6 +30,8 @@ def _xform_common_availability_zone(az, zone_info):
|
||||
if hasattr(az, 'zoneName'):
|
||||
zone_info['zone_name'] = az.zoneName
|
||||
|
||||
zone_info['zone_resource'] = ''
|
||||
|
||||
|
||||
def _xform_compute_availability_zone(az, include_extra):
|
||||
result = []
|
||||
@ -69,6 +71,18 @@ def _xform_volume_availability_zone(az):
|
||||
return result
|
||||
|
||||
|
||||
def _xform_network_availability_zone(az):
|
||||
result = []
|
||||
zone_info = {}
|
||||
zone_info['zone_name'] = getattr(az, 'name', '')
|
||||
zone_info['zone_status'] = getattr(az, 'state', '')
|
||||
if 'unavailable' == zone_info['zone_status']:
|
||||
zone_info['zone_status'] = 'not available'
|
||||
zone_info['zone_resource'] = getattr(az, 'resource', '')
|
||||
result.append(zone_info)
|
||||
return result
|
||||
|
||||
|
||||
class ListAvailabilityZone(command.Lister):
|
||||
"""List availability zones and their status"""
|
||||
|
||||
@ -79,6 +93,11 @@ class ListAvailabilityZone(command.Lister):
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List compute availability zones')
|
||||
parser.add_argument(
|
||||
'--network',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='List network availability zones')
|
||||
parser.add_argument(
|
||||
'--volume',
|
||||
action='store_true',
|
||||
@ -92,7 +111,7 @@ class ListAvailabilityZone(command.Lister):
|
||||
)
|
||||
return parser
|
||||
|
||||
def get_compute_availability_zones(self, parsed_args):
|
||||
def _get_compute_availability_zones(self, parsed_args):
|
||||
compute_client = self.app.client_manager.compute
|
||||
try:
|
||||
data = compute_client.availability_zones.list()
|
||||
@ -108,36 +127,63 @@ class ListAvailabilityZone(command.Lister):
|
||||
result += _xform_compute_availability_zone(zone, parsed_args.long)
|
||||
return result
|
||||
|
||||
def get_volume_availability_zones(self, parsed_args):
|
||||
def _get_volume_availability_zones(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
data = []
|
||||
try:
|
||||
data = volume_client.availability_zones.list()
|
||||
except Exception:
|
||||
message = "Availability zones list not supported by " \
|
||||
"Block Storage API"
|
||||
self.log.warning(message)
|
||||
except Exception as e:
|
||||
self.log.debug('Volume availability zone exception: ' + str(e))
|
||||
if parsed_args.volume:
|
||||
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
|
||||
|
||||
def _get_network_availability_zones(self, parsed_args):
|
||||
network_client = self.app.client_manager.network
|
||||
data = []
|
||||
try:
|
||||
# Verify that the extension exists.
|
||||
network_client.find_extension('Availability Zone',
|
||||
ignore_missing=False)
|
||||
data = network_client.availability_zones()
|
||||
except Exception as e:
|
||||
self.log.debug('Network availability zone exception: ' + str(e))
|
||||
if parsed_args.network:
|
||||
message = "Availability zones list not supported by " \
|
||||
"Network API"
|
||||
self.log.warning(message)
|
||||
|
||||
result = []
|
||||
for zone in data:
|
||||
result += _xform_network_availability_zone(zone)
|
||||
return result
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
if parsed_args.long:
|
||||
columns = ('Zone Name', 'Zone Status',
|
||||
columns = ('Zone Name', 'Zone Status', 'Zone Resource',
|
||||
'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)
|
||||
show_all = (not parsed_args.compute and
|
||||
not parsed_args.volume and
|
||||
not parsed_args.network)
|
||||
|
||||
result = []
|
||||
if parsed_args.compute or show_all:
|
||||
result += self.get_compute_availability_zones(parsed_args)
|
||||
result += self._get_compute_availability_zones(parsed_args)
|
||||
if parsed_args.volume or show_all:
|
||||
result += self.get_volume_availability_zones(parsed_args)
|
||||
result += self._get_volume_availability_zones(parsed_args)
|
||||
if parsed_args.network or show_all:
|
||||
result += self._get_network_availability_zones(parsed_args)
|
||||
|
||||
return (columns,
|
||||
(utils.get_dict_properties(
|
||||
|
@ -11,11 +11,13 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from openstackclient.common import availability_zone
|
||||
from openstackclient.tests.compute.v2 import fakes as compute_fakes
|
||||
from openstackclient.tests import fakes
|
||||
from openstackclient.tests.network.v2 import fakes as network_fakes
|
||||
from openstackclient.tests import utils
|
||||
from openstackclient.tests.volume.v2 import fakes as volume_fakes
|
||||
|
||||
@ -33,6 +35,7 @@ def _build_compute_az_datalist(compute_az, long_datalist=False):
|
||||
datalist += (
|
||||
compute_az.zoneName,
|
||||
'available',
|
||||
'',
|
||||
host,
|
||||
service,
|
||||
'enabled :-) ' + state['updated_at'],
|
||||
@ -51,6 +54,23 @@ def _build_volume_az_datalist(volume_az, long_datalist=False):
|
||||
datalist = (
|
||||
volume_az.zoneName,
|
||||
'available',
|
||||
'', '', '', '',
|
||||
)
|
||||
return (datalist,)
|
||||
|
||||
|
||||
def _build_network_az_datalist(network_az, long_datalist=False):
|
||||
datalist = ()
|
||||
if not long_datalist:
|
||||
datalist = (
|
||||
network_az.name,
|
||||
network_az.state,
|
||||
)
|
||||
else:
|
||||
datalist = (
|
||||
network_az.name,
|
||||
network_az.state,
|
||||
network_az.resource,
|
||||
'', '', '',
|
||||
)
|
||||
return (datalist,)
|
||||
@ -79,6 +99,16 @@ class TestAvailabilityZone(utils.TestCommand):
|
||||
self.volume_azs_mock = volume_client.availability_zones
|
||||
self.volume_azs_mock.reset_mock()
|
||||
|
||||
network_client = network_fakes.FakeNetworkV2Client(
|
||||
endpoint=fakes.AUTH_URL,
|
||||
token=fakes.AUTH_TOKEN,
|
||||
)
|
||||
self.app.client_manager.network = network_client
|
||||
|
||||
network_client.availability_zones = mock.Mock()
|
||||
network_client.find_extension = mock.Mock()
|
||||
self.network_azs_mock = network_client.availability_zones
|
||||
|
||||
|
||||
class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
@ -86,11 +116,14 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
compute_fakes.FakeAvailabilityZone.create_availability_zones()
|
||||
volume_azs = \
|
||||
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
|
||||
network_azs = \
|
||||
network_fakes.FakeAvailabilityZone.create_availability_zones()
|
||||
|
||||
short_columnslist = ('Zone Name', 'Zone Status')
|
||||
long_columnslist = (
|
||||
'Zone Name',
|
||||
'Zone Status',
|
||||
'Zone Resource',
|
||||
'Host Name',
|
||||
'Service Name',
|
||||
'Service Status',
|
||||
@ -101,6 +134,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
self.compute_azs_mock.list.return_value = self.compute_azs
|
||||
self.volume_azs_mock.list.return_value = self.volume_azs
|
||||
self.network_azs_mock.return_value = self.network_azs
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = availability_zone.ListAvailabilityZone(self.app, None)
|
||||
@ -115,6 +149,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
self.compute_azs_mock.list.assert_called_with()
|
||||
self.volume_azs_mock.list.assert_called_with()
|
||||
self.network_azs_mock.assert_called_with()
|
||||
|
||||
self.assertEqual(self.short_columnslist, columns)
|
||||
datalist = ()
|
||||
@ -122,6 +157,8 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
datalist += _build_compute_az_datalist(compute_az)
|
||||
for volume_az in self.volume_azs:
|
||||
datalist += _build_volume_az_datalist(volume_az)
|
||||
for network_az in self.network_azs:
|
||||
datalist += _build_network_az_datalist(network_az)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_availability_zone_list_long(self):
|
||||
@ -138,6 +175,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
self.compute_azs_mock.list.assert_called_with()
|
||||
self.volume_azs_mock.list.assert_called_with()
|
||||
self.network_azs_mock.assert_called_with()
|
||||
|
||||
self.assertEqual(self.long_columnslist, columns)
|
||||
datalist = ()
|
||||
@ -147,6 +185,9 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
for volume_az in self.volume_azs:
|
||||
datalist += _build_volume_az_datalist(volume_az,
|
||||
long_datalist=True)
|
||||
for network_az in self.network_azs:
|
||||
datalist += _build_network_az_datalist(network_az,
|
||||
long_datalist=True)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
||||
def test_availability_zone_list_compute(self):
|
||||
@ -163,6 +204,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
self.compute_azs_mock.list.assert_called_with()
|
||||
self.volume_azs_mock.list.assert_not_called()
|
||||
self.network_azs_mock.assert_not_called()
|
||||
|
||||
self.assertEqual(self.short_columnslist, columns)
|
||||
datalist = ()
|
||||
@ -184,9 +226,32 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||
|
||||
self.compute_azs_mock.list.assert_not_called()
|
||||
self.volume_azs_mock.list.assert_called_with()
|
||||
self.network_azs_mock.assert_not_called()
|
||||
|
||||
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))
|
||||
|
||||
def test_availability_zone_list_network(self):
|
||||
arglist = [
|
||||
'--network',
|
||||
]
|
||||
verifylist = [
|
||||
('network', 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_not_called()
|
||||
self.network_azs_mock.assert_called_with()
|
||||
|
||||
self.assertEqual(self.short_columnslist, columns)
|
||||
datalist = ()
|
||||
for network_az in self.network_azs:
|
||||
datalist += _build_network_az_datalist(network_az)
|
||||
self.assertEqual(datalist, tuple(data))
|
||||
|
@ -69,6 +69,59 @@ class TestNetworkV2(utils.TestCommand):
|
||||
)
|
||||
|
||||
|
||||
class FakeAvailabilityZone(object):
|
||||
"""Fake one or more network 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 name, state, etc.
|
||||
"""
|
||||
# Set default attributes.
|
||||
availability_zone = {
|
||||
'name': uuid.uuid4().hex,
|
||||
'state': 'available',
|
||||
'resource': 'network',
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
class FakeNetwork(object):
|
||||
"""Fake one or more networks."""
|
||||
|
||||
|
7
releasenotes/notes/bug-1534202-1ba78f0bb744961f.yaml
Normal file
7
releasenotes/notes/bug-1534202-1ba78f0bb744961f.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add network support to `os availability zone list`
|
||||
[Bug `1534202 <https://bugs.launchpad.net/bugs/1534202>`_]
|
||||
|
||||
* New `--network` option to only list network availability zones.
|
Loading…
Reference in New Issue
Block a user