Skip to content

Commit 48697de

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support listing network availability zones"
2 parents e4c0d85 + 4d332de commit 48697de

6 files changed

Lines changed: 188 additions & 12 deletions

File tree

doc/source/command-objects/availability-zone.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
availability zone
33
=================
44

5-
Compute v2, Block Storage v2
5+
Block Storage v2, Compute v2, Network v2
66

77
availability zone list
88
----------------------
@@ -14,13 +14,18 @@ List availability zones and their status
1414
1515
os availability zone list
1616
[--compute]
17+
[--network]
1718
[--volume]
1819
[--long]
1920
2021
.. option:: --compute
2122

2223
List compute availability zones
2324

25+
.. option:: --network
26+
27+
List network availability zones
28+
2429
.. option:: --volume
2530

2631
List volume availability zones

doc/source/commands.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ the API resources will be merged, as in the ``quota`` object that has options
7070
referring to both Compute and Volume quotas.
7171

7272
* ``access token``: (**Identity**) long-lived OAuth-based token
73-
* ``availability zone``: (**Compute**, **Volume**) a logical partition of hosts or block storage services
73+
* ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services
7474
* ``aggregate``: (**Compute**) a grouping of servers
7575
* ``backup``: (**Volume**) a volume copy
7676
* ``catalog``: (**Identity**) service catalog

openstackclient/common/availability_zone.py

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def _xform_common_availability_zone(az, zone_info):
3030
if hasattr(az, 'zoneName'):
3131
zone_info['zone_name'] = az.zoneName
3232

33+
zone_info['zone_resource'] = ''
34+
3335

3436
def _xform_compute_availability_zone(az, include_extra):
3537
result = []
@@ -69,6 +71,18 @@ def _xform_volume_availability_zone(az):
6971
return result
7072

7173

74+
def _xform_network_availability_zone(az):
75+
result = []
76+
zone_info = {}
77+
zone_info['zone_name'] = getattr(az, 'name', '')
78+
zone_info['zone_status'] = getattr(az, 'state', '')
79+
if 'unavailable' == zone_info['zone_status']:
80+
zone_info['zone_status'] = 'not available'
81+
zone_info['zone_resource'] = getattr(az, 'resource', '')
82+
result.append(zone_info)
83+
return result
84+
85+
7286
class ListAvailabilityZone(command.Lister):
7387
"""List availability zones and their status"""
7488

@@ -79,6 +93,11 @@ def get_parser(self, prog_name):
7993
action='store_true',
8094
default=False,
8195
help='List compute availability zones')
96+
parser.add_argument(
97+
'--network',
98+
action='store_true',
99+
default=False,
100+
help='List network availability zones')
82101
parser.add_argument(
83102
'--volume',
84103
action='store_true',
@@ -92,7 +111,7 @@ def get_parser(self, prog_name):
92111
)
93112
return parser
94113

95-
def get_compute_availability_zones(self, parsed_args):
114+
def _get_compute_availability_zones(self, parsed_args):
96115
compute_client = self.app.client_manager.compute
97116
try:
98117
data = compute_client.availability_zones.list()
@@ -108,36 +127,63 @@ def get_compute_availability_zones(self, parsed_args):
108127
result += _xform_compute_availability_zone(zone, parsed_args.long)
109128
return result
110129

111-
def get_volume_availability_zones(self, parsed_args):
130+
def _get_volume_availability_zones(self, parsed_args):
112131
volume_client = self.app.client_manager.volume
132+
data = []
113133
try:
114134
data = volume_client.availability_zones.list()
115-
except Exception:
116-
message = "Availability zones list not supported by " \
117-
"Block Storage API"
118-
self.log.warning(message)
135+
except Exception as e:
136+
self.log.debug('Volume availability zone exception: ' + str(e))
137+
if parsed_args.volume:
138+
message = "Availability zones list not supported by " \
139+
"Block Storage API"
140+
self.log.warning(message)
119141

120142
result = []
121143
for zone in data:
122144
result += _xform_volume_availability_zone(zone)
123145
return result
124146

147+
def _get_network_availability_zones(self, parsed_args):
148+
network_client = self.app.client_manager.network
149+
data = []
150+
try:
151+
# Verify that the extension exists.
152+
network_client.find_extension('Availability Zone',
153+
ignore_missing=False)
154+
data = network_client.availability_zones()
155+
except Exception as e:
156+
self.log.debug('Network availability zone exception: ' + str(e))
157+
if parsed_args.network:
158+
message = "Availability zones list not supported by " \
159+
"Network API"
160+
self.log.warning(message)
161+
162+
result = []
163+
for zone in data:
164+
result += _xform_network_availability_zone(zone)
165+
return result
166+
125167
def take_action(self, parsed_args):
126168

127169
if parsed_args.long:
128-
columns = ('Zone Name', 'Zone Status',
170+
columns = ('Zone Name', 'Zone Status', 'Zone Resource',
129171
'Host Name', 'Service Name', 'Service Status')
130172
else:
131173
columns = ('Zone Name', 'Zone Status')
132174

133175
# Show everything by default.
134-
show_all = (not parsed_args.compute and not parsed_args.volume)
176+
show_all = (not parsed_args.compute and
177+
not parsed_args.volume and
178+
not parsed_args.network)
135179

136180
result = []
137181
if parsed_args.compute or show_all:
138-
result += self.get_compute_availability_zones(parsed_args)
182+
result += self._get_compute_availability_zones(parsed_args)
139183
if parsed_args.volume or show_all:
140-
result += self.get_volume_availability_zones(parsed_args)
184+
result += self._get_volume_availability_zones(parsed_args)
185+
if parsed_args.network or show_all:
186+
result += self._get_network_availability_zones(parsed_args)
141187

142188
return (columns,
143189
(utils.get_dict_properties(

openstackclient/tests/common/test_availability_zone.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
# under the License.
1212
#
1313

14+
import mock
1415
import six
1516

1617
from openstackclient.common import availability_zone
1718
from openstackclient.tests.compute.v2 import fakes as compute_fakes
1819
from openstackclient.tests import fakes
20+
from openstackclient.tests.network.v2 import fakes as network_fakes
1921
from openstackclient.tests import utils
2022
from openstackclient.tests.volume.v2 import fakes as volume_fakes
2123

@@ -33,6 +35,7 @@ def _build_compute_az_datalist(compute_az, long_datalist=False):
3335
datalist += (
3436
compute_az.zoneName,
3537
'available',
38+
'',
3639
host,
3740
service,
3841
'enabled :-) ' + state['updated_at'],
@@ -51,6 +54,23 @@ def _build_volume_az_datalist(volume_az, long_datalist=False):
5154
datalist = (
5255
volume_az.zoneName,
5356
'available',
57+
'', '', '', '',
58+
)
59+
return (datalist,)
60+
61+
62+
def _build_network_az_datalist(network_az, long_datalist=False):
63+
datalist = ()
64+
if not long_datalist:
65+
datalist = (
66+
network_az.name,
67+
network_az.state,
68+
)
69+
else:
70+
datalist = (
71+
network_az.name,
72+
network_az.state,
73+
network_az.resource,
5474
'', '', '',
5575
)
5676
return (datalist,)
@@ -79,18 +99,31 @@ def setUp(self):
7999
self.volume_azs_mock = volume_client.availability_zones
80100
self.volume_azs_mock.reset_mock()
81101

102+
network_client = network_fakes.FakeNetworkV2Client(
103+
endpoint=fakes.AUTH_URL,
104+
token=fakes.AUTH_TOKEN,
105+
)
106+
self.app.client_manager.network = network_client
107+
108+
network_client.availability_zones = mock.Mock()
109+
network_client.find_extension = mock.Mock()
110+
self.network_azs_mock = network_client.availability_zones
111+
82112

83113
class TestAvailabilityZoneList(TestAvailabilityZone):
84114

85115
compute_azs = \
86116
compute_fakes.FakeAvailabilityZone.create_availability_zones()
87117
volume_azs = \
88118
volume_fakes.FakeAvailabilityZone.create_availability_zones(count=1)
119+
network_azs = \
120+
network_fakes.FakeAvailabilityZone.create_availability_zones()
89121

90122
short_columnslist = ('Zone Name', 'Zone Status')
91123
long_columnslist = (
92124
'Zone Name',
93125
'Zone Status',
126+
'Zone Resource',
94127
'Host Name',
95128
'Service Name',
96129
'Service Status',
@@ -101,6 +134,7 @@ def setUp(self):
101134

102135
self.compute_azs_mock.list.return_value = self.compute_azs
103136
self.volume_azs_mock.list.return_value = self.volume_azs
137+
self.network_azs_mock.return_value = self.network_azs
104138

105139
# Get the command object to test
106140
self.cmd = availability_zone.ListAvailabilityZone(self.app, None)
@@ -115,13 +149,16 @@ def test_availability_zone_list_no_options(self):
115149

116150
self.compute_azs_mock.list.assert_called_with()
117151
self.volume_azs_mock.list.assert_called_with()
152+
self.network_azs_mock.assert_called_with()
118153

119154
self.assertEqual(self.short_columnslist, columns)
120155
datalist = ()
121156
for compute_az in self.compute_azs:
122157
datalist += _build_compute_az_datalist(compute_az)
123158
for volume_az in self.volume_azs:
124159
datalist += _build_volume_az_datalist(volume_az)
160+
for network_az in self.network_azs:
161+
datalist += _build_network_az_datalist(network_az)
125162
self.assertEqual(datalist, tuple(data))
126163

127164
def test_availability_zone_list_long(self):
@@ -138,6 +175,7 @@ def test_availability_zone_list_long(self):
138175

139176
self.compute_azs_mock.list.assert_called_with()
140177
self.volume_azs_mock.list.assert_called_with()
178+
self.network_azs_mock.assert_called_with()
141179

142180
self.assertEqual(self.long_columnslist, columns)
143181
datalist = ()
@@ -147,6 +185,9 @@ def test_availability_zone_list_long(self):
147185
for volume_az in self.volume_azs:
148186
datalist += _build_volume_az_datalist(volume_az,
149187
long_datalist=True)
188+
for network_az in self.network_azs:
189+
datalist += _build_network_az_datalist(network_az,
190+
long_datalist=True)
150191
self.assertEqual(datalist, tuple(data))
151192

152193
def test_availability_zone_list_compute(self):
@@ -163,6 +204,7 @@ def test_availability_zone_list_compute(self):
163204

164205
self.compute_azs_mock.list.assert_called_with()
165206
self.volume_azs_mock.list.assert_not_called()
207+
self.network_azs_mock.assert_not_called()
166208

167209
self.assertEqual(self.short_columnslist, columns)
168210
datalist = ()
@@ -184,9 +226,32 @@ def test_availability_zone_list_volume(self):
184226

185227
self.compute_azs_mock.list.assert_not_called()
186228
self.volume_azs_mock.list.assert_called_with()
229+
self.network_azs_mock.assert_not_called()
187230

188231
self.assertEqual(self.short_columnslist, columns)
189232
datalist = ()
190233
for volume_az in self.volume_azs:
191234
datalist += _build_volume_az_datalist(volume_az)
192235
self.assertEqual(datalist, tuple(data))
236+
237+
def test_availability_zone_list_network(self):
238+
arglist = [
239+
'--network',
240+
]
241+
verifylist = [
242+
('network', True),
243+
]
244+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
245+
246+
# DisplayCommandBase.take_action() returns two tuples
247+
columns, data = self.cmd.take_action(parsed_args)
248+
249+
self.compute_azs_mock.list.assert_not_called()
250+
self.volume_azs_mock.list.assert_not_called()
251+
self.network_azs_mock.assert_called_with()
252+
253+
self.assertEqual(self.short_columnslist, columns)
254+
datalist = ()
255+
for network_az in self.network_azs:
256+
datalist += _build_network_az_datalist(network_az)
257+
self.assertEqual(datalist, tuple(data))

openstackclient/tests/network/v2/fakes.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,59 @@ def setUp(self):
6969
)
7070

7171

72+
class FakeAvailabilityZone(object):
73+
"""Fake one or more network availability zones (AZs)."""
74+
75+
@staticmethod
76+
def create_one_availability_zone(attrs={}, methods={}):
77+
"""Create a fake AZ.
78+
79+
:param Dictionary attrs:
80+
A dictionary with all attributes
81+
:param Dictionary methods:
82+
A dictionary with all methods
83+
:return:
84+
A FakeResource object with name, state, etc.
85+
"""
86+
# Set default attributes.
87+
availability_zone = {
88+
'name': uuid.uuid4().hex,
89+
'state': 'available',
90+
'resource': 'network',
91+
}
92+
93+
# Overwrite default attributes.
94+
availability_zone.update(attrs)
95+
96+
availability_zone = fakes.FakeResource(
97+
info=copy.deepcopy(availability_zone),
98+
methods=methods,
99+
loaded=True)
100+
return availability_zone
101+
102+
@staticmethod
103+
def create_availability_zones(attrs={}, methods={}, count=2):
104+
"""Create multiple fake AZs.
105+
106+
:param Dictionary attrs:
107+
A dictionary with all attributes
108+
:param Dictionary methods:
109+
A dictionary with all methods
110+
:param int count:
111+
The number of AZs to fake
112+
:return:
113+
A list of FakeResource objects faking the AZs
114+
"""
115+
availability_zones = []
116+
for i in range(0, count):
117+
availability_zone = \
118+
FakeAvailabilityZone.create_one_availability_zone(
119+
attrs, methods)
120+
availability_zones.append(availability_zone)
121+
122+
return availability_zones
123+
124+
72125
class FakeNetwork(object):
73126
"""Fake one or more networks."""
74127

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Add network support to `os availability zone list`
5+
[Bug `1534202 <https://bugs.launchpad.net/bugs/1534202>`_]
6+
7+
* New `--network` option to only list network availability zones.

0 commit comments

Comments
 (0)