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:
parent
7ede7093c4
commit
88f42a86f1
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue