From 5da05941256c45d5795467abd890ac8d99d85456 Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Tue, 24 Sep 2019 14:55:08 +0200 Subject: [PATCH] Create allocated floating IPs for active reservations We allow users to increase the amount of reserved floating IPs even for active reservations. We need to ask Neutron to create them if the reservation has started and on_start() was already executed. Change-Id: I5d35460c82b9a06327445e2ca3bda6075e5b2619 --- blazar/manager/exceptions.py | 4 + .../plugins/floatingips/floatingip_plugin.py | 21 +++ .../floatingips/test_floatingip_plugin.py | 154 +++++++++++++++++- 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/blazar/manager/exceptions.py b/blazar/manager/exceptions.py index c3fbc06c..9883af63 100644 --- a/blazar/manager/exceptions.py +++ b/blazar/manager/exceptions.py @@ -222,3 +222,7 @@ class CantUpdateFloatingIPReservation(exceptions.BlazarException): code = 400 msg_fmt = _("Floating IP reservation cannot be updated with requested " "parameters. %(msg)s") + + +class NeutronClientError(exceptions.BlazarException): + msg_fmt = _("Failed to create Neutron resources for the reservation") diff --git a/blazar/plugins/floatingips/floatingip_plugin.py b/blazar/plugins/floatingips/floatingip_plugin.py index e14e6d4e..77a59bf3 100644 --- a/blazar/plugins/floatingips/floatingip_plugin.py +++ b/blazar/plugins/floatingips/floatingip_plugin.py @@ -90,6 +90,27 @@ class FloatingIpPlugin(base.BasePlugin): if len(fip_ids_to_add) < needed_fips: raise manager_ex.NotEnoughFloatingIPAvailable() + # Create new floating IPs if reservation is active + created_fips = [] + if reservation_status == status.reservation.ACTIVE: + ctx = context.current() + fip_pool = neutron.FloatingIPPool(fip_reservation['network_id']) + for fip_id in fip_ids_to_add: + try: + fip = db_api.floatingip_get(fip_id) + LOG.debug( + 'Creating floating IP {} for reservation {}'.format( + fip['floating_ip_address'], reservation_id)) + fip_pool.create_reserved_floatingip( + fip['subnet_id'], fip['floating_ip_address'], + ctx.project_id, reservation_id) + created_fips.append(fip['floating_ip_address']) + except Exception as e: + for fip_address in created_fips: + fip_pool.delete_reserved_floatingip(fip_address) + err_msg = 'Failed to create floating IP: {}'.format(str(e)) + raise manager_ex.NeutronClientError(err_msg) + for fip_id in fip_ids_to_add: LOG.debug('Adding floating IP {} to reservation {}'.format( fip_id, reservation_id)) diff --git a/blazar/tests/plugins/floatingips/test_floatingip_plugin.py b/blazar/tests/plugins/floatingips/test_floatingip_plugin.py index afd71b05..94fde6c6 100644 --- a/blazar/tests/plugins/floatingips/test_floatingip_plugin.py +++ b/blazar/tests/plugins/floatingips/test_floatingip_plugin.py @@ -320,7 +320,7 @@ class FloatingIpPluginTest(tests.TestCase): u'441c1476-9f8f-4700-9f30-cd9b6fef3509', values) - def test_update_reservation_increase_amount_fips_available(self): + def test_update_pending_reservation_increase_amount_fips_available(self): fip_plugin = floatingip_plugin.FloatingIpPlugin() values = { 'start_date': datetime.datetime(2013, 12, 19, 20, 0), @@ -375,6 +375,158 @@ class FloatingIpPluginTest(tests.TestCase): ] fip_allocation_create.assert_has_calls(calls) + def test_update_active_reservation_increase_amount_fips_available(self): + fip_plugin = floatingip_plugin.FloatingIpPlugin() + values = { + 'start_date': datetime.datetime(2013, 12, 19, 20, 0), + 'end_date': datetime.datetime(2013, 12, 19, 21, 0), + 'resource_type': plugin.RESOURCE_TYPE, + 'amount': 2, + } + lease_get = self.patch(self.db_api, 'lease_get') + lease_get.return_value = { + 'id': '018c1b43-e69e-4aef-a543-09681539cf4c', + 'start_date': datetime.datetime(2013, 12, 19, 20, 0), + 'end_date': datetime.datetime(2013, 12, 19, 21, 0), + } + reservation_get = self.patch(self.db_api, 'reservation_get') + reservation_get.return_value = { + 'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509', + 'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c', + 'resource_id': 'fip-reservation-id-1', + 'resource_type': 'virtual:floatingip', + 'status': 'active', + } + fip_allocation_get_all_by_values = self.patch( + self.db_api, 'fip_allocation_get_all_by_values' + ) + fip_allocation_get_all_by_values.return_value = [{ + 'floatingip_id': 'fip1' + }] + fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get') + fip_reservation_get.return_value = { + 'id': 'fip_resv_id1', + 'amount': 1, + 'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509', + 'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67', + 'required_floatingips': [], + } + matching_fips = self.patch(fip_plugin, '_matching_fips') + matching_fips.return_value = ['fip2'] + fip_reservation_update = self.patch(self.db_api, + 'fip_reservation_update') + fip_get = self.patch(self.db_api, 'floatingip_get') + fip_get.return_value = { + 'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67', + 'subnet_id': 'subnet-id', + 'floating_ip_address': '172.2.24.100' + } + self.set_context(context.BlazarContext(project_id='fake-project-id')) + m = mock.MagicMock() + self.fip_pool.return_value = m + fip_allocation_create = self.patch( + self.db_api, 'fip_allocation_create') + fip_allocation_destroy = self.patch( + self.db_api, 'fip_allocation_destroy') + fip_plugin.update_reservation( + u'441c1476-9f8f-4700-9f30-cd9b6fef3509', + values) + fip_reservation_update.assert_called_once_with( + 'fip_resv_id1', {'amount': 2}) + self.fip_pool.assert_called_once_with( + 'f548089e-fb3e-4013-a043-c5ed809c7a67') + m.create_reserved_floatingip.assert_called_once_with( + 'subnet-id', + '172.2.24.100', + 'fake-project-id', + '441c1476-9f8f-4700-9f30-cd9b6fef3509') + calls = [ + mock.call( + {'floatingip_id': 'fip2', + 'reservation_id': u'441c1476-9f8f-4700-9f30-cd9b6fef3509', + }) + ] + fip_allocation_create.assert_has_calls(calls) + self.assertFalse(fip_allocation_destroy.called) + + def test_update_active_reservation_fip_creation_failure(self): + fip_plugin = floatingip_plugin.FloatingIpPlugin() + values = { + 'start_date': datetime.datetime(2013, 12, 19, 20, 0), + 'end_date': datetime.datetime(2013, 12, 19, 21, 0), + 'resource_type': plugin.RESOURCE_TYPE, + 'amount': 3, + } + lease_get = self.patch(self.db_api, 'lease_get') + lease_get.return_value = { + 'id': '018c1b43-e69e-4aef-a543-09681539cf4c', + 'start_date': datetime.datetime(2013, 12, 19, 20, 0), + 'end_date': datetime.datetime(2013, 12, 19, 21, 0), + } + reservation_get = self.patch(self.db_api, 'reservation_get') + reservation_get.return_value = { + 'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509', + 'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c', + 'resource_id': 'fip-reservation-id-1', + 'resource_type': 'virtual:floatingip', + 'status': 'active', + } + fip_allocation_get_all_by_values = self.patch( + self.db_api, 'fip_allocation_get_all_by_values' + ) + fip_allocation_get_all_by_values.return_value = [{ + 'floatingip_id': 'fip1' + }] + fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get') + fip_reservation_get.return_value = { + 'id': 'fip_resv_id1', + 'amount': 1, + 'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509', + 'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67', + 'required_floatingips': [], + } + matching_fips = self.patch(fip_plugin, '_matching_fips') + matching_fips.return_value = ['fip2', 'fip3'] + fip_get = self.patch(self.db_api, 'floatingip_get') + fip_get.side_effect = ( + { + 'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67', + 'subnet_id': 'subnet-id', + 'floating_ip_address': '172.2.24.100' + }, + { + 'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67', + 'subnet_id': 'subnet-id', + 'floating_ip_address': '172.2.24.101' + } + ) + self.set_context(context.BlazarContext(project_id='fake-project-id')) + m = mock.MagicMock() + m.create_reserved_floatingip.side_effect = (None, Exception()) + self.fip_pool.return_value = m + fip_allocation_create = self.patch( + self.db_api, 'fip_allocation_create') + fip_allocation_destroy = self.patch( + self.db_api, 'fip_allocation_destroy') + self.assertRaises(mgr_exceptions.NeutronClientError, + fip_plugin.update_reservation, + '441c1476-9f8f-4700-9f30-cd9b6fef3509', values) + self.fip_pool.assert_called_once_with( + 'f548089e-fb3e-4013-a043-c5ed809c7a67') + calls = [ + mock.call('subnet-id', + '172.2.24.100', + 'fake-project-id', + '441c1476-9f8f-4700-9f30-cd9b6fef3509'), + mock.call('subnet-id', + '172.2.24.101', + 'fake-project-id', + '441c1476-9f8f-4700-9f30-cd9b6fef3509'), + ] + m.create_reserved_floatingip.assert_has_calls(calls) + self.assertFalse(fip_allocation_create.called) + self.assertFalse(fip_allocation_destroy.called) + def test_update_reservation_increase_amount_fips_unavailable(self): fip_plugin = floatingip_plugin.FloatingIpPlugin() values = {