Skip to content

Commit 1d41bb4

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Fix floating IP delete and show by IP"
2 parents 7329e64 + 339af2c commit 1d41bb4

5 files changed

Lines changed: 157 additions & 27 deletions

File tree

openstackclient/network/v2/floating_ip.py

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import logging
1717

18+
from openstack import exceptions as sdk_exceptions
19+
from openstack.network.v2 import floating_ip as _floating_ip
1820
from osc_lib import utils
1921

2022
from openstackclient.i18n import _
@@ -79,6 +81,58 @@ def _get_attrs(client_manager, parsed_args):
7981
return attrs
8082

8183

84+
def _find_floating_ip(
85+
session,
86+
ip_cache,
87+
name_or_id,
88+
ignore_missing=True,
89+
**params
90+
):
91+
"""Find a floating IP by IP or ID
92+
93+
The SDK's find_ip() can only locate a floating IP by ID so we have
94+
to do this ourselves.
95+
"""
96+
97+
def _get_one_match(name_or_id):
98+
"""Given a list of results, return the match"""
99+
the_result = None
100+
for maybe_result in ip_cache:
101+
id_value = maybe_result.id
102+
ip_value = maybe_result.floating_ip_address
103+
104+
if (id_value == name_or_id) or (ip_value == name_or_id):
105+
# Only allow one resource to be found. If we already
106+
# found a match, raise an exception to show it.
107+
if the_result is None:
108+
the_result = maybe_result
109+
else:
110+
msg = "More than one %s exists with the name '%s'."
111+
msg = (msg % (_floating_ip.FloatingIP, name_or_id))
112+
raise sdk_exceptions.DuplicateResource(msg)
113+
114+
return the_result
115+
116+
# Try to short-circuit by looking directly for a matching ID.
117+
try:
118+
match = _floating_ip.FloatingIP.existing(id=name_or_id, **params)
119+
return (match.get(session), ip_cache)
120+
except sdk_exceptions.NotFoundException:
121+
pass
122+
123+
if len(ip_cache) == 0:
124+
ip_cache = list(_floating_ip.FloatingIP.list(session, **params))
125+
126+
result = _get_one_match(name_or_id)
127+
if result is not None:
128+
return (result, ip_cache)
129+
130+
if ignore_missing:
131+
return (None, ip_cache)
132+
raise sdk_exceptions.ResourceNotFound(
133+
"No %s found for %s" % (_floating_ip.FloatingIP.__name__, name_or_id))
134+
135+
82136
class CreateFloatingIP(common.NetworkAndComputeShowOne):
83137
_description = _("Create floating IP")
84138

@@ -186,13 +240,28 @@ def update_parser_common(self, parser):
186240
return parser
187241

188242
def take_action_network(self, client, parsed_args):
189-
obj = client.find_ip(self.r, ignore_missing=False)
243+
(obj, self.ip_cache) = _find_floating_ip(
244+
client.session,
245+
self.ip_cache,
246+
self.r,
247+
ignore_missing=False,
248+
)
190249
client.delete_ip(obj)
191250

192251
def take_action_compute(self, client, parsed_args):
193252
obj = utils.find_resource(client.floating_ips, self.r)
194253
client.floating_ips.delete(obj.id)
195254

255+
def take_action(self, parsed_args):
256+
"""Implements a naive cache for the list of floating IPs"""
257+
258+
# NOTE(dtroyer): This really only prevents multiple list()
259+
# calls when performing multiple resource deletes
260+
# in a single command. In an interactive session
261+
# each delete command will call list().
262+
self.ip_cache = []
263+
super(DeleteFloatingIP, self).take_action(parsed_args)
264+
196265

197266
class DeleteIPFloating(DeleteFloatingIP):
198267
_description = _("Delete floating IP(s)")
@@ -390,6 +459,9 @@ def take_action_compute(self, client, parsed_args):
390459
class ShowFloatingIP(common.NetworkAndComputeShowOne):
391460
_description = _("Display floating IP details")
392461

462+
# ip_cache is unused here but is a side effect of _find_floating_ip()
463+
ip_cache = []
464+
393465
def update_parser_common(self, parser):
394466
parser.add_argument(
395467
'floating_ip',
@@ -399,7 +471,12 @@ def update_parser_common(self, parser):
399471
return parser
400472

401473
def take_action_network(self, client, parsed_args):
402-
obj = client.find_ip(parsed_args.floating_ip, ignore_missing=False)
474+
(obj, self.ip_cache) = _find_floating_ip(
475+
client.session,
476+
[],
477+
parsed_args.floating_ip,
478+
ignore_missing=False,
479+
)
403480
display_columns, columns = _get_network_columns(obj)
404481
data = utils.get_item_properties(obj, columns)
405482
return (display_columns, data)

openstackclient/tests/functional/network/v2/test_floating_ip.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
import re
1515
import uuid
1616

17-
import testtools
18-
1917
from openstackclient.tests.functional import base
2018

2119

@@ -25,7 +23,6 @@ class FloatingIpTests(base.TestCase):
2523
NETWORK_NAME = uuid.uuid4().hex
2624

2725
@classmethod
28-
@testtools.skip('broken SDK testing')
2926
def setUpClass(cls):
3027
# Set up some regex for matching below
3128
cls.re_id = re.compile("id\s+\|\s+(\S+)")
@@ -62,7 +59,6 @@ def tearDownClass(cls):
6259
raw_output = cls.openstack('network delete ' + cls.NETWORK_NAME)
6360
cls.assertOutput('', raw_output)
6461

65-
@testtools.skip('broken SDK testing')
6662
def test_floating_ip_delete(self):
6763
"""Test create, delete multiple"""
6864
raw_output = self.openstack(
@@ -93,7 +89,6 @@ def test_floating_ip_delete(self):
9389
raw_output = self.openstack('floating ip delete ' + ip1 + ' ' + ip2)
9490
self.assertOutput('', raw_output)
9591

96-
@testtools.skip('broken SDK testing')
9792
def test_floating_ip_list(self):
9893
"""Test create defaults, list filters, delete"""
9994
raw_output = self.openstack(
@@ -135,7 +130,6 @@ def test_floating_ip_list(self):
135130

136131
# TODO(dtroyer): add more filter tests
137132

138-
@testtools.skip('broken SDK testing')
139133
def test_floating_ip_show(self):
140134
"""Test show"""
141135
raw_output = self.openstack(

openstackclient/tests/unit/network/v2/fakes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
class FakeNetworkV2Client(object):
5353

5454
def __init__(self, **kwargs):
55+
self.session = mock.Mock()
5556
self.extensions = mock.Mock()
5657
self.extensions.resource_class = fakes.FakeResource(None, {})
5758

openstackclient/tests/unit/network/v2/test_floating_ip.py

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,19 @@ def setUp(self):
208208
super(TestDeleteFloatingIPNetwork, self).setUp()
209209

210210
self.network.delete_ip = mock.Mock(return_value=None)
211-
self.network.find_ip = (
212-
network_fakes.FakeFloatingIP.get_floating_ips(self.floating_ips))
213211

214212
# Get the command object to test
215213
self.cmd = floating_ip.DeleteFloatingIP(self.app, self.namespace)
216214

217-
def test_floating_ip_delete(self):
215+
@mock.patch(
216+
"openstackclient.tests.unit.network.v2.test_floating_ip." +
217+
"floating_ip._find_floating_ip"
218+
)
219+
def test_floating_ip_delete(self, find_floating_ip_mock):
220+
find_floating_ip_mock.side_effect = [
221+
(self.floating_ips[0], []),
222+
(self.floating_ips[1], []),
223+
]
218224
arglist = [
219225
self.floating_ips[0].id,
220226
]
@@ -225,12 +231,24 @@ def test_floating_ip_delete(self):
225231

226232
result = self.cmd.take_action(parsed_args)
227233

228-
self.network.find_ip.assert_called_once_with(
229-
self.floating_ips[0].id, ignore_missing=False)
234+
find_floating_ip_mock.assert_called_once_with(
235+
mock.ANY,
236+
[],
237+
self.floating_ips[0].id,
238+
ignore_missing=False,
239+
)
230240
self.network.delete_ip.assert_called_once_with(self.floating_ips[0])
231241
self.assertIsNone(result)
232242

233-
def test_multi_floating_ips_delete(self):
243+
@mock.patch(
244+
"openstackclient.tests.unit.network.v2.test_floating_ip." +
245+
"floating_ip._find_floating_ip"
246+
)
247+
def test_floating_ip_delete_multi(self, find_floating_ip_mock):
248+
find_floating_ip_mock.side_effect = [
249+
(self.floating_ips[0], []),
250+
(self.floating_ips[1], []),
251+
]
234252
arglist = []
235253
verifylist = []
236254

@@ -243,13 +261,37 @@ def test_multi_floating_ips_delete(self):
243261

244262
result = self.cmd.take_action(parsed_args)
245263

264+
calls = [
265+
call(
266+
mock.ANY,
267+
[],
268+
self.floating_ips[0].id,
269+
ignore_missing=False,
270+
),
271+
call(
272+
mock.ANY,
273+
[],
274+
self.floating_ips[1].id,
275+
ignore_missing=False,
276+
),
277+
]
278+
find_floating_ip_mock.assert_has_calls(calls)
279+
246280
calls = []
247281
for f in self.floating_ips:
248282
calls.append(call(f))
249283
self.network.delete_ip.assert_has_calls(calls)
250284
self.assertIsNone(result)
251285

252-
def test_multi_floating_ips_delete_with_exception(self):
286+
@mock.patch(
287+
"openstackclient.tests.unit.network.v2.test_floating_ip." +
288+
"floating_ip._find_floating_ip"
289+
)
290+
def test_floating_ip_delete_multi_exception(self, find_floating_ip_mock):
291+
find_floating_ip_mock.side_effect = [
292+
(self.floating_ips[0], []),
293+
exceptions.CommandError,
294+
]
253295
arglist = [
254296
self.floating_ips[0].id,
255297
'unexist_floating_ip',
@@ -260,21 +302,24 @@ def test_multi_floating_ips_delete_with_exception(self):
260302
]
261303
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
262304

263-
find_mock_result = [self.floating_ips[0], exceptions.CommandError]
264-
self.network.find_ip = (
265-
mock.Mock(side_effect=find_mock_result)
266-
)
267-
268305
try:
269306
self.cmd.take_action(parsed_args)
270307
self.fail('CommandError should be raised.')
271308
except exceptions.CommandError as e:
272309
self.assertEqual('1 of 2 floating_ips failed to delete.', str(e))
273310

274-
self.network.find_ip.assert_any_call(
275-
self.floating_ips[0].id, ignore_missing=False)
276-
self.network.find_ip.assert_any_call(
277-
'unexist_floating_ip', ignore_missing=False)
311+
find_floating_ip_mock.assert_any_call(
312+
mock.ANY,
313+
[],
314+
self.floating_ips[0].id,
315+
ignore_missing=False,
316+
)
317+
find_floating_ip_mock.assert_any_call(
318+
mock.ANY,
319+
[],
320+
'unexist_floating_ip',
321+
ignore_missing=False,
322+
)
278323
self.network.delete_ip.assert_called_once_with(
279324
self.floating_ips[0]
280325
)
@@ -534,7 +579,12 @@ def setUp(self):
534579
# Get the command object to test
535580
self.cmd = floating_ip.ShowFloatingIP(self.app, self.namespace)
536581

537-
def test_floating_ip_show(self):
582+
@mock.patch(
583+
"openstackclient.tests.unit.network.v2.test_floating_ip." +
584+
"floating_ip._find_floating_ip"
585+
)
586+
def test_floating_ip_show(self, find_floating_ip_mock):
587+
find_floating_ip_mock.return_value = (self.floating_ip, [])
538588
arglist = [
539589
self.floating_ip.id,
540590
]
@@ -545,9 +595,11 @@ def test_floating_ip_show(self):
545595

546596
columns, data = self.cmd.take_action(parsed_args)
547597

548-
self.network.find_ip.assert_called_once_with(
598+
find_floating_ip_mock.assert_called_once_with(
599+
mock.ANY,
600+
[],
549601
self.floating_ip.id,
550-
ignore_missing=False
602+
ignore_missing=False,
551603
)
552604
self.assertEqual(self.columns, columns)
553605
self.assertEqual(self.data, data)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
Fix ``floating ip delete`` and ``floating ip show`` to accept IP addresses
5+
in addition to IDs to select floating IPs to delete or show.
6+
[Bug `1656402 <https://bugs.launchpad.net/bugs/1656402>`_

0 commit comments

Comments
 (0)