Merge "Support listing network availability zones"
This commit is contained in:
commit
48697de28f
@ -2,7 +2,7 @@
|
|||||||
availability zone
|
availability zone
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Compute v2, Block Storage v2
|
Block Storage v2, Compute v2, Network v2
|
||||||
|
|
||||||
availability zone list
|
availability zone list
|
||||||
----------------------
|
----------------------
|
||||||
@ -14,6 +14,7 @@ List availability zones and their status
|
|||||||
|
|
||||||
os availability zone list
|
os availability zone list
|
||||||
[--compute]
|
[--compute]
|
||||||
|
[--network]
|
||||||
[--volume]
|
[--volume]
|
||||||
[--long]
|
[--long]
|
||||||
|
|
||||||
@ -21,6 +22,10 @@ List availability zones and their status
|
|||||||
|
|
||||||
List compute availability zones
|
List compute availability zones
|
||||||
|
|
||||||
|
.. option:: --network
|
||||||
|
|
||||||
|
List network availability zones
|
||||||
|
|
||||||
.. option:: --volume
|
.. option:: --volume
|
||||||
|
|
||||||
List volume availability zones
|
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.
|
referring to both Compute and Volume quotas.
|
||||||
|
|
||||||
* ``access token``: (**Identity**) long-lived OAuth-based token
|
* ``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
|
* ``aggregate``: (**Compute**) a grouping of servers
|
||||||
* ``backup``: (**Volume**) a volume copy
|
* ``backup``: (**Volume**) a volume copy
|
||||||
* ``catalog``: (**Identity**) service catalog
|
* ``catalog``: (**Identity**) service catalog
|
||||||
|
@ -30,6 +30,8 @@ def _xform_common_availability_zone(az, zone_info):
|
|||||||
if hasattr(az, 'zoneName'):
|
if hasattr(az, 'zoneName'):
|
||||||
zone_info['zone_name'] = az.zoneName
|
zone_info['zone_name'] = az.zoneName
|
||||||
|
|
||||||
|
zone_info['zone_resource'] = ''
|
||||||
|
|
||||||
|
|
||||||
def _xform_compute_availability_zone(az, include_extra):
|
def _xform_compute_availability_zone(az, include_extra):
|
||||||
result = []
|
result = []
|
||||||
@ -69,6 +71,18 @@ def _xform_volume_availability_zone(az):
|
|||||||
return result
|
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):
|
class ListAvailabilityZone(command.Lister):
|
||||||
"""List availability zones and their status"""
|
"""List availability zones and their status"""
|
||||||
|
|
||||||
@ -79,6 +93,11 @@ class ListAvailabilityZone(command.Lister):
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help='List compute availability zones')
|
help='List compute availability zones')
|
||||||
|
parser.add_argument(
|
||||||
|
'--network',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='List network availability zones')
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--volume',
|
'--volume',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -92,7 +111,7 @@ class ListAvailabilityZone(command.Lister):
|
|||||||
)
|
)
|
||||||
return parser
|
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
|
compute_client = self.app.client_manager.compute
|
||||||
try:
|
try:
|
||||||
data = compute_client.availability_zones.list()
|
data = compute_client.availability_zones.list()
|
||||||
@ -108,36 +127,63 @@ class ListAvailabilityZone(command.Lister):
|
|||||||
result += _xform_compute_availability_zone(zone, parsed_args.long)
|
result += _xform_compute_availability_zone(zone, parsed_args.long)
|
||||||
return result
|
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
|
volume_client = self.app.client_manager.volume
|
||||||
|
data = []
|
||||||
try:
|
try:
|
||||||
data = volume_client.availability_zones.list()
|
data = volume_client.availability_zones.list()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
message = "Availability zones list not supported by " \
|
self.log.debug('Volume availability zone exception: ' + str(e))
|
||||||
"Block Storage API"
|
if parsed_args.volume:
|
||||||
self.log.warning(message)
|
message = "Availability zones list not supported by " \
|
||||||
|
"Block Storage API"
|
||||||
|
self.log.warning(message)
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for zone in data:
|
for zone in data:
|
||||||
result += _xform_volume_availability_zone(zone)
|
result += _xform_volume_availability_zone(zone)
|
||||||
return result
|
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):
|
def take_action(self, parsed_args):
|
||||||
|
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = ('Zone Name', 'Zone Status',
|
columns = ('Zone Name', 'Zone Status', 'Zone Resource',
|
||||||
'Host Name', 'Service Name', 'Service Status')
|
'Host Name', 'Service Name', 'Service Status')
|
||||||
else:
|
else:
|
||||||
columns = ('Zone Name', 'Zone Status')
|
columns = ('Zone Name', 'Zone Status')
|
||||||
|
|
||||||
# Show everything by default.
|
# 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 = []
|
result = []
|
||||||
if parsed_args.compute or show_all:
|
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:
|
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,
|
return (columns,
|
||||||
(utils.get_dict_properties(
|
(utils.get_dict_properties(
|
||||||
|
@ -11,11 +11,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import mock
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from openstackclient.common import availability_zone
|
from openstackclient.common import availability_zone
|
||||||
from openstackclient.tests.compute.v2 import fakes as compute_fakes
|
from openstackclient.tests.compute.v2 import fakes as compute_fakes
|
||||||
from openstackclient.tests import fakes
|
from openstackclient.tests import fakes
|
||||||
|
from openstackclient.tests.network.v2 import fakes as network_fakes
|
||||||
from openstackclient.tests import utils
|
from openstackclient.tests import utils
|
||||||
from openstackclient.tests.volume.v2 import fakes as volume_fakes
|
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 += (
|
datalist += (
|
||||||
compute_az.zoneName,
|
compute_az.zoneName,
|
||||||
'available',
|
'available',
|
||||||
|
'',
|
||||||
host,
|
host,
|
||||||
service,
|
service,
|
||||||
'enabled :-) ' + state['updated_at'],
|
'enabled :-) ' + state['updated_at'],
|
||||||
@ -51,6 +54,23 @@ def _build_volume_az_datalist(volume_az, long_datalist=False):
|
|||||||
datalist = (
|
datalist = (
|
||||||
volume_az.zoneName,
|
volume_az.zoneName,
|
||||||
'available',
|
'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,)
|
return (datalist,)
|
||||||
@ -79,6 +99,16 @@ class TestAvailabilityZone(utils.TestCommand):
|
|||||||
self.volume_azs_mock = volume_client.availability_zones
|
self.volume_azs_mock = volume_client.availability_zones
|
||||||
self.volume_azs_mock.reset_mock()
|
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):
|
class TestAvailabilityZoneList(TestAvailabilityZone):
|
||||||
|
|
||||||
@ -86,11 +116,14 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
compute_fakes.FakeAvailabilityZone.create_availability_zones()
|
compute_fakes.FakeAvailabilityZone.create_availability_zones()
|
||||||
volume_azs = \
|
volume_azs = \
|
||||||
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
|
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
|
||||||
|
network_azs = \
|
||||||
|
network_fakes.FakeAvailabilityZone.create_availability_zones()
|
||||||
|
|
||||||
short_columnslist = ('Zone Name', 'Zone Status')
|
short_columnslist = ('Zone Name', 'Zone Status')
|
||||||
long_columnslist = (
|
long_columnslist = (
|
||||||
'Zone Name',
|
'Zone Name',
|
||||||
'Zone Status',
|
'Zone Status',
|
||||||
|
'Zone Resource',
|
||||||
'Host Name',
|
'Host Name',
|
||||||
'Service Name',
|
'Service Name',
|
||||||
'Service Status',
|
'Service Status',
|
||||||
@ -101,6 +134,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
|
|
||||||
self.compute_azs_mock.list.return_value = self.compute_azs
|
self.compute_azs_mock.list.return_value = self.compute_azs
|
||||||
self.volume_azs_mock.list.return_value = self.volume_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
|
# Get the command object to test
|
||||||
self.cmd = availability_zone.ListAvailabilityZone(self.app, None)
|
self.cmd = availability_zone.ListAvailabilityZone(self.app, None)
|
||||||
@ -115,6 +149,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
|
|
||||||
self.compute_azs_mock.list.assert_called_with()
|
self.compute_azs_mock.list.assert_called_with()
|
||||||
self.volume_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)
|
self.assertEqual(self.short_columnslist, columns)
|
||||||
datalist = ()
|
datalist = ()
|
||||||
@ -122,6 +157,8 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
datalist += _build_compute_az_datalist(compute_az)
|
datalist += _build_compute_az_datalist(compute_az)
|
||||||
for volume_az in self.volume_azs:
|
for volume_az in self.volume_azs:
|
||||||
datalist += _build_volume_az_datalist(volume_az)
|
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))
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
def test_availability_zone_list_long(self):
|
def test_availability_zone_list_long(self):
|
||||||
@ -138,6 +175,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
|
|
||||||
self.compute_azs_mock.list.assert_called_with()
|
self.compute_azs_mock.list.assert_called_with()
|
||||||
self.volume_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)
|
self.assertEqual(self.long_columnslist, columns)
|
||||||
datalist = ()
|
datalist = ()
|
||||||
@ -147,6 +185,9 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
for volume_az in self.volume_azs:
|
for volume_az in self.volume_azs:
|
||||||
datalist += _build_volume_az_datalist(volume_az,
|
datalist += _build_volume_az_datalist(volume_az,
|
||||||
long_datalist=True)
|
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))
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
def test_availability_zone_list_compute(self):
|
def test_availability_zone_list_compute(self):
|
||||||
@ -163,6 +204,7 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
|
|
||||||
self.compute_azs_mock.list.assert_called_with()
|
self.compute_azs_mock.list.assert_called_with()
|
||||||
self.volume_azs_mock.list.assert_not_called()
|
self.volume_azs_mock.list.assert_not_called()
|
||||||
|
self.network_azs_mock.assert_not_called()
|
||||||
|
|
||||||
self.assertEqual(self.short_columnslist, columns)
|
self.assertEqual(self.short_columnslist, columns)
|
||||||
datalist = ()
|
datalist = ()
|
||||||
@ -184,9 +226,32 @@ class TestAvailabilityZoneList(TestAvailabilityZone):
|
|||||||
|
|
||||||
self.compute_azs_mock.list.assert_not_called()
|
self.compute_azs_mock.list.assert_not_called()
|
||||||
self.volume_azs_mock.list.assert_called_with()
|
self.volume_azs_mock.list.assert_called_with()
|
||||||
|
self.network_azs_mock.assert_not_called()
|
||||||
|
|
||||||
self.assertEqual(self.short_columnslist, columns)
|
self.assertEqual(self.short_columnslist, columns)
|
||||||
datalist = ()
|
datalist = ()
|
||||||
for volume_az in self.volume_azs:
|
for volume_az in self.volume_azs:
|
||||||
datalist += _build_volume_az_datalist(volume_az)
|
datalist += _build_volume_az_datalist(volume_az)
|
||||||
self.assertEqual(datalist, tuple(data))
|
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):
|
class FakeNetwork(object):
|
||||||
"""Fake one or more networks."""
|
"""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