From 1872af0884531d4bd7d9943a348f91e286551c7c Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Wed, 20 Apr 2016 10:13:14 -0500 Subject: [PATCH] Add public helper method for cleaning floating ips Some things, like nodepool or utility scripts, need to be able to safely clean floating ips, but doing so requires knowledge of whether the cloud in question is using nova or neutron for floating ips, which shade otherwise hides from the user. Make a helper method that allows the user to do it on the clouds where it is safe to do automatically. Change-Id: I93b0c7d0b0eefdfe0fb1cd4a66cdbba9baabeb09 --- shade/openstackcloud.py | 28 +++++++++++++++++ shade/tests/unit/test_floating_ip_neutron.py | 33 ++++++++++++++++++++ shade/tests/unit/test_floating_ip_nova.py | 13 ++++++++ 3 files changed, 74 insertions(+) diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 76ff9ce99..c5c3785f8 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -3320,6 +3320,34 @@ class OpenStackCloud(object): fip_id=floating_ip_id, msg=str(e))) return True + def delete_unattached_floating_ips(self, wait=False, timeout=60): + """Safely delete unattached floating ips. + + If the cloud can safely purge any unattached floating ips without + race conditions, do so. + + Safely here means a specific thing. It means that you are not running + this while another process that might do a two step create/attach + is running. You can safely run this method while another process + is creating servers and attaching floating IPs to them if either that + process is using add_auto_ip from shade, or is creating the floating + IPs by passing in a server to the create_floating_ip call. + + :param wait: Whether to wait for each IP to be deleted + :param timeout: How long to wait for each IP + + :returns: True if Floating IPs have been deleted, False if not + + :raises: ``OpenStackCloudException``, on operation error. + """ + processed = [] + if self._use_neutron_floating(): + for ip in self.list_floating_ips(): + if not ip['attached']: + processed.append(self.delete_floating_ip( + id=ip['id'], wait=wait, timeout=timeout)) + return all(processed) if processed else False + def _attach_ip_to_server( self, server, floating_ip, fixed_address=None, wait=False, diff --git a/shade/tests/unit/test_floating_ip_neutron.py b/shade/tests/unit/test_floating_ip_neutron.py index d0ebad059..85fb977ea 100644 --- a/shade/tests/unit/test_floating_ip_neutron.py +++ b/shade/tests/unit/test_floating_ip_neutron.py @@ -531,3 +531,36 @@ class TestFloatingIP(base.TestCase): fixed_address='192.0.2.129') self.assertEqual(server, self.fake_server) + + @patch.object(OpenStackCloud, 'delete_floating_ip') + @patch.object(OpenStackCloud, 'list_floating_ips') + @patch.object(OpenStackCloud, '_use_neutron_floating') + def test_cleanup_floating_ips( + self, mock_use_neutron_floating, mock_list_floating_ips, + mock_delete_floating_ip): + mock_use_neutron_floating.return_value = True + floating_ips = [{ + "id": "this-is-a-floating-ip-id", + "fixed_ip_address": None, + "internal_network": None, + "floating_ip_address": "203.0.113.29", + "network": "this-is-a-net-or-pool-id", + "attached": False, + "status": "ACTIVE" + }, { + "id": "this-is-an-attached-floating-ip-id", + "fixed_ip_address": None, + "internal_network": None, + "floating_ip_address": "203.0.113.29", + "network": "this-is-a-net-or-pool-id", + "attached": True, + "status": "ACTIVE" + }] + + mock_list_floating_ips.return_value = floating_ips + + self.client.delete_unattached_floating_ips() + + mock_delete_floating_ip.assert_called_once_with( + id="this-is-a-floating-ip-id", + timeout=60, wait=False) diff --git a/shade/tests/unit/test_floating_ip_nova.py b/shade/tests/unit/test_floating_ip_nova.py index 2ad83b894..34bee06c3 100644 --- a/shade/tests/unit/test_floating_ip_nova.py +++ b/shade/tests/unit/test_floating_ip_nova.py @@ -248,3 +248,16 @@ class TestFloatingIP(base.TestCase): fixed_address='192.0.2.129') self.assertEqual(server, self.fake_server) + + @patch.object(OpenStackCloud, 'delete_floating_ip') + @patch.object(OpenStackCloud, 'list_floating_ips') + @patch.object(OpenStackCloud, '_use_neutron_floating') + def test_cleanup_floating_ips( + self, mock_use_neutron_floating, mock_list_floating_ips, + mock_delete_floating_ip): + mock_use_neutron_floating.return_value = False + + self.client.delete_unattached_floating_ips() + + mock_delete_floating_ip.assert_not_called() + mock_list_floating_ips.assert_not_called()