Delete ports when nova fails to boot VM

This patch adds some code to the exception handler in the instance
manager's boot method to cleanup any created ports if nova fails to boot
the instance.

Change-Id: I459ccd82a367fcd41d4a894b7231c797dc5100e9
Closes-Bug: #1516152
This commit is contained in:
James King 2015-11-27 16:18:17 -05:00
parent 7ede7093c4
commit 88f42a86f1
4 changed files with 47 additions and 1 deletions

View File

@ -365,6 +365,16 @@ class Neutron(object):
return port
def delete_vrrp_port(self, object_id, label='VRRP'):
name = 'AKANDA:%s:%s' % (label, object_id)
response = self.api_client.list_ports(name=name)
port_data = response.get('ports')
if not port_data:
LOG.warning('Unable to find VRRP port to delete with name %s.',
name)
for port in port_data:
self.api_client.delete_port(port['id'])
def create_router_external_port(self, router):
# FIXME: Need to make this smarter in case the switch is full.
network_args = {'network_id': self.conf.external_network_id}

View File

@ -382,6 +382,20 @@ class TestExternalPort(unittest.TestCase):
port = neutron_wrapper.create_router_external_port(self.router)
self.assertEqual(port.id, self.EXTERNAL_PORT_ID)
@mock.patch('akanda.rug.api.neutron.AkandaExtClientWrapper')
def test_delete_vrrp_port(self, client_wrapper):
mock_client = mock.Mock()
mock_client.list_ports.return_value = {
'ports': [self.ROUTER['ports'][0]]
}
client_wrapper.return_value = mock_client
neutron_wrapper = neutron.Neutron(self.conf)
neutron_wrapper.delete_vrrp_port(self.ROUTER['ports'][0]['id'])
mock_client.list_ports.assert_called_once_with(
name='AKANDA:%s:%s' % ('VRRP', self.ROUTER['ports'][0]['id']))
mock_client.delete_port.assert_called_once_with(
self.ROUTER['ports'][0]['id'])
@mock.patch('akanda.rug.api.neutron.AkandaExtClientWrapper')
def test_create_missing_gateway_port(self, client_wrapper):
self.conf.retry_delay = 0

View File

@ -239,6 +239,26 @@ class TestVmManager(unittest.TestCase):
self.conf.boot_timeout
)
@mock.patch('time.sleep')
def test_boot_error_with_port_cleanup(self, sleep):
rtr = mock.sentinel.router
self.ctx.neutron.get_router_detail.return_value = rtr
self.ctx.nova_client.boot_instance.side_effect = RuntimeError
rtr.id = 'R1'
rtr.ports = mock.MagicMock()
rtr.ports.__iter__.return_value = [
fake_mgt_port, fake_int_port, fake_ext_port
]
expected_calls = [
mock.call('1'), mock.call('1', label='MGT'),
mock.call('2'), mock.call('2', label='MGT'),
mock.call('3'), mock.call('3', label='MGT')
]
self.vm_mgr.boot(self.ctx, 'foo')
self.ctx.neutron.delete_vrrp_port.assert_has_calls(expected_calls)
@mock.patch('time.sleep')
@mock.patch('akanda.rug.vm_manager.router_api')
def test_update_state_is_down(self, router_api, sleep):

View File

@ -207,7 +207,9 @@ class VmManager(object):
return
except:
self.log.exception('Router failed to start boot')
# TODO(mark): attempt clean-up of failed ports
for port in self.router_obj.ports:
worker_context.neutron.delete_vrrp_port(port.id)
worker_context.neutron.delete_vrrp_port(port.id, label='MGT')
return
else:
# We have successfully started a (re)boot attempt so