Select the right lb_network_ip interface using AZ

If the AZ system is being used and there is also a default boot network
set in the main config, the nova driver would not pick the right
interface when translating the amphora info for amps in custom AZs.

This affects the step that fetches the lb_network_ip (the management
address) from nova, immediately before the step that updates the
database with the correct IP. That IP is then used in the rest of the
amphora creation flow, which pulls in that address and uses it to
connect to the amp.

Change-Id: Ie4869035a557ebcddea2fce693067c82fbd2d2a9
This commit is contained in:
Adam Harwell 2020-01-29 22:49:52 -08:00
parent e0ae8a3cac
commit 2c76209003
11 changed files with 99 additions and 48 deletions

View File

@ -69,10 +69,11 @@ class ComputeBase(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def get_amphora(self, compute_id): def get_amphora(self, compute_id, management_network_id=None):
"""Retrieve an amphora object """Retrieve an amphora object
:param compute_id: the id of the desired amphora :param compute_id: the compute id of the desired amphora
:param management_network_id: ID of the management network
:returns: the amphora object :returns: the amphora object
:returns: fault message or None :returns: fault message or None
""" """

View File

@ -63,10 +63,11 @@ class NoopManager(object):
self.computeconfig[compute_id] = (compute_id, 'status') self.computeconfig[compute_id] = (compute_id, 'status')
return constants.UP return constants.UP
def get_amphora(self, compute_id): def get_amphora(self, compute_id, management_network_id=None):
LOG.debug("Compute %s no-op, compute_id %s", LOG.debug("Compute %s no-op, compute_id %s, management_network_id %s",
self.__class__.__name__, compute_id) self.__class__.__name__, compute_id, management_network_id)
self.computeconfig[compute_id] = (compute_id, 'get_amphora') self.computeconfig[(compute_id, management_network_id)] = (
compute_id, management_network_id, 'get_amphora')
return data_models.Amphora( return data_models.Amphora(
compute_id=compute_id, compute_id=compute_id,
status=constants.ACTIVE, status=constants.ACTIVE,
@ -143,8 +144,8 @@ class NoopComputeDriver(driver_base.ComputeBase):
def status(self, compute_id): def status(self, compute_id):
return self.driver.status(compute_id) return self.driver.status(compute_id)
def get_amphora(self, compute_id): def get_amphora(self, compute_id, management_network_id=None):
return self.driver.get_amphora(compute_id) return self.driver.get_amphora(compute_id, management_network_id)
def create_server_group(self, name, policy): def create_server_group(self, name, policy):
return self.driver.create_server_group(name, policy) return self.driver.create_server_group(name, policy)

View File

@ -218,10 +218,11 @@ class VirtualMachineManager(compute_base.ComputeBase):
raise exceptions.ComputeStatusException() raise exceptions.ComputeStatusException()
return constants.DOWN return constants.DOWN
def get_amphora(self, compute_id): def get_amphora(self, compute_id, management_network_id=None):
'''Retrieve the information in nova of a virtual machine. '''Retrieve the information in nova of a virtual machine.
:param amphora_id: virtual machine UUID :param compute_id: virtual machine UUID
:param management_network_id: ID of the management network
:returns: an amphora object :returns: an amphora object
:returns: fault message or None :returns: fault message or None
''' '''
@ -231,12 +232,13 @@ class VirtualMachineManager(compute_base.ComputeBase):
except Exception: except Exception:
LOG.exception("Error retrieving nova virtual machine.") LOG.exception("Error retrieving nova virtual machine.")
raise exceptions.ComputeGetException() raise exceptions.ComputeGetException()
return self._translate_amphora(amphora) return self._translate_amphora(amphora, management_network_id)
def _translate_amphora(self, nova_response): def _translate_amphora(self, nova_response, management_network_id=None):
'''Convert a nova virtual machine into an amphora object. '''Convert a nova virtual machine into an amphora object.
:param nova_response: JSON response from nova :param nova_response: JSON response from nova
:param management_network_id: ID of the management network
:returns: an amphora object :returns: an amphora object
:returns: fault message or None :returns: fault message or None
''' '''
@ -246,19 +248,19 @@ class VirtualMachineManager(compute_base.ComputeBase):
lb_network_ip = None lb_network_ip = None
availability_zone = None availability_zone = None
image_id = None image_id = None
fault = None
if management_network_id:
boot_networks = [management_network_id]
else:
boot_networks = CONF.controller_worker.amp_boot_network_list
try: try:
inf_list = nova_response.interface_list() inf_list = nova_response.interface_list()
no_boot_networks = (
not CONF.controller_worker.amp_boot_network_list)
for interface in inf_list: for interface in inf_list:
net_id = interface.net_id net_id = interface.net_id
is_boot_network = (
net_id in CONF.controller_worker.amp_boot_network_list)
# Pick the first fixed_ip if this is a boot network or if # Pick the first fixed_ip if this is a boot network or if
# there are no boot networks configured (use default network) # there are no boot networks configured (use default network)
if is_boot_network or no_boot_networks: if net_id in boot_networks or not boot_networks:
lb_network_ip = interface.fixed_ips[0]['ip_address'] lb_network_ip = interface.fixed_ips[0]['ip_address']
break break
try: try:

View File

@ -193,7 +193,8 @@ class AmphoraFlows(object):
requires=(constants.AMPHORA_ID, constants.COMPUTE_ID))) requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
create_amp_for_lb_subflow.add(compute_tasks.ComputeActiveWait( create_amp_for_lb_subflow.add(compute_tasks.ComputeActiveWait(
name=sf_name + '-' + constants.COMPUTE_WAIT, name=sf_name + '-' + constants.COMPUTE_WAIT,
requires=(constants.COMPUTE_ID, constants.AMPHORA_ID), requires=(constants.COMPUTE_ID, constants.AMPHORA_ID,
constants.AVAILABILITY_ZONE),
provides=constants.COMPUTE_OBJ)) provides=constants.COMPUTE_OBJ))
create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraInfo( create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraInfo(
name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO, name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO,

View File

@ -204,14 +204,18 @@ class ComputeDelete(BaseComputeTask):
class ComputeActiveWait(BaseComputeTask): class ComputeActiveWait(BaseComputeTask):
"""Wait for the compute driver to mark the amphora active.""" """Wait for the compute driver to mark the amphora active."""
def execute(self, compute_id, amphora_id): def execute(self, compute_id, amphora_id, availability_zone):
"""Wait for the compute driver to mark the amphora active """Wait for the compute driver to mark the amphora active
:raises: Generic exception if the amphora is not active :raises: Generic exception if the amphora is not active
:returns: An amphora object :returns: An amphora object
""" """
if availability_zone:
amp_network = availability_zone.get(constants.MANAGEMENT_NETWORK)
else:
amp_network = None
for i in range(CONF.controller_worker.amp_active_retries): for i in range(CONF.controller_worker.amp_active_retries):
amp, fault = self.compute.get_amphora(compute_id) amp, fault = self.compute.get_amphora(compute_id, amp_network)
if amp.status == constants.ACTIVE: if amp.status == constants.ACTIVE:
if CONF.haproxy_amphora.build_rate_limit != -1: if CONF.haproxy_amphora.build_rate_limit != -1:
self.rate_limit.remove_from_build_req_queue(amphora_id) self.rate_limit.remove_from_build_req_queue(amphora_id)

View File

@ -200,7 +200,8 @@ class AmphoraFlows(object):
requires=(constants.AMPHORA_ID, constants.COMPUTE_ID))) requires=(constants.AMPHORA_ID, constants.COMPUTE_ID)))
create_amp_for_lb_subflow.add(compute_tasks.ComputeActiveWait( create_amp_for_lb_subflow.add(compute_tasks.ComputeActiveWait(
name=sf_name + '-' + constants.COMPUTE_WAIT, name=sf_name + '-' + constants.COMPUTE_WAIT,
requires=(constants.COMPUTE_ID, constants.AMPHORA_ID), requires=(constants.COMPUTE_ID, constants.AMPHORA_ID,
constants.AVAILABILITY_ZONE),
provides=constants.COMPUTE_OBJ)) provides=constants.COMPUTE_OBJ))
create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraInfo( create_amp_for_lb_subflow.add(database_tasks.UpdateAmphoraInfo(
name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO, name=sf_name + '-' + constants.UPDATE_AMPHORA_INFO,

View File

@ -59,6 +59,8 @@ class ComputeCreate(BaseComputeTask):
availability_zone=None): availability_zone=None):
"""Create an amphora """Create an amphora
:param availability_zone: availability zone metadata dictionary
:returns: an amphora :returns: an amphora
""" """
ports = ports or [] ports = ports or []
@ -156,6 +158,8 @@ class CertComputeCreate(ComputeCreate):
availability_zone=None): availability_zone=None):
"""Create an amphora """Create an amphora
:param availability_zone: availability zone metadata dictionary
:returns: an amphora :returns: an amphora
""" """
@ -211,14 +215,22 @@ class ComputeDelete(BaseComputeTask):
class ComputeActiveWait(BaseComputeTask): class ComputeActiveWait(BaseComputeTask):
"""Wait for the compute driver to mark the amphora active.""" """Wait for the compute driver to mark the amphora active."""
def execute(self, compute_id, amphora_id): def execute(self, compute_id, amphora_id, availability_zone):
"""Wait for the compute driver to mark the amphora active """Wait for the compute driver to mark the amphora active
:param compute_id: virtual machine UUID
:param amphora_id: id of the amphora object
:param availability_zone: availability zone metadata dictionary
:raises: Generic exception if the amphora is not active :raises: Generic exception if the amphora is not active
:returns: An amphora object :returns: An amphora object
""" """
if availability_zone:
amp_network = availability_zone.get(constants.MANAGEMENT_NETWORK)
else:
amp_network = None
for i in range(CONF.controller_worker.amp_active_retries): for i in range(CONF.controller_worker.amp_active_retries):
amp, fault = self.compute.get_amphora(compute_id) amp, fault = self.compute.get_amphora(compute_id, amp_network)
if amp.status == constants.ACTIVE: if amp.status == constants.ACTIVE:
if CONF.haproxy_amphora.build_rate_limit != -1: if CONF.haproxy_amphora.build_rate_limit != -1:
self.rate_limit.remove_from_build_req_queue(amphora_id) self.rate_limit.remove_from_build_req_queue(amphora_id)

View File

@ -88,10 +88,12 @@ class TestNoopComputeDriver(base.TestCase):
self.amphora_id]) self.amphora_id])
def test_get_amphora(self): def test_get_amphora(self):
self.driver.get_amphora(self.amphora_id) management_network_id = uuidutils.generate_uuid()
self.assertEqual((self.amphora_id, 'get_amphora'), self.driver.get_amphora(self.amphora_id, management_network_id)
self.driver.driver.computeconfig[ self.assertEqual(
self.amphora_id]) (self.amphora_id, management_network_id, 'get_amphora'),
self.driver.driver.computeconfig[
self.amphora_id, management_network_id])
def test_create_server_group(self): def test_create_server_group(self):
self.driver.create_server_group(self.server_group_name, self.driver.create_server_group(self.server_group_name,

View File

@ -434,15 +434,23 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _amphora_mock, None mock_driver.get_amphora.return_value = _amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) # Test with no AZ
computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
# Test with AZ
mock_driver.reset_mock()
az = {constants.MANAGEMENT_NETWORK: uuidutils.generate_uuid()}
computewait.execute(COMPUTE_ID, AMPHORA_ID, az)
mock_driver.get_amphora.assert_called_once_with(
COMPUTE_ID, az[constants.MANAGEMENT_NETWORK])
# Test with deleted amp
_amphora_mock.status = constants.DELETED _amphora_mock.status = constants.DELETED
self.assertRaises(exceptions.ComputeWaitTimeoutException, self.assertRaises(exceptions.ComputeWaitTimeoutException,
computewait.execute, computewait.execute,
_amphora_mock, AMPHORA_ID) _amphora_mock, AMPHORA_ID, None)
@mock.patch('octavia.controller.worker.amphora_rate_limit' @mock.patch('octavia.controller.worker.amphora_rate_limit'
'.AmphoraBuildRateLimit.remove_from_build_req_queue') '.AmphoraBuildRateLimit.remove_from_build_req_queue')
@ -461,15 +469,15 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _amphora_mock, None mock_driver.get_amphora.return_value = _amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID) computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
_amphora_mock.status = constants.ERROR _amphora_mock.status = constants.ERROR
self.assertRaises(exceptions.ComputeBuildException, self.assertRaises(exceptions.ComputeBuildException,
computewait.execute, computewait.execute,
_amphora_mock, AMPHORA_ID) _amphora_mock, AMPHORA_ID, None)
@mock.patch('octavia.controller.worker.amphora_rate_limit' @mock.patch('octavia.controller.worker.amphora_rate_limit'
'.AmphoraBuildRateLimit.remove_from_build_req_queue') '.AmphoraBuildRateLimit.remove_from_build_req_queue')
@ -486,9 +494,9 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _amphora_mock, None mock_driver.get_amphora.return_value = _amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID) computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
mock_remove_from_build_queue.assert_not_called() mock_remove_from_build_queue.assert_not_called()
@mock.patch('stevedore.driver.DriverManager.driver') @mock.patch('stevedore.driver.DriverManager.driver')

View File

@ -54,10 +54,15 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.AMPHORA, amp_flow.provides) self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides) self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides) self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides) self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertIn(constants.BUILD_TYPE_PRIORITY, amp_flow.requires)
self.assertIn(constants.FLAVOR, amp_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, amp_flow.requires)
self.assertEqual(5, len(amp_flow.provides)) self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(2, len(amp_flow.requires)) self.assertEqual(3, len(amp_flow.requires))
def test_get_create_amphora_flow_cert(self, mock_get_net_driver): def test_get_create_amphora_flow_cert(self, mock_get_net_driver):
self.AmpFlow = amphora_flows.AmphoraFlows() self.AmpFlow = amphora_flows.AmphoraFlows()
@ -69,9 +74,15 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.AMPHORA, amp_flow.provides) self.assertIn(constants.AMPHORA, amp_flow.provides)
self.assertIn(constants.AMPHORA_ID, amp_flow.provides) self.assertIn(constants.AMPHORA_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_ID, amp_flow.provides) self.assertIn(constants.COMPUTE_ID, amp_flow.provides)
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.SERVER_PEM, amp_flow.provides)
self.assertIn(constants.BUILD_TYPE_PRIORITY, amp_flow.requires)
self.assertIn(constants.FLAVOR, amp_flow.requires)
self.assertIn(constants.AVAILABILITY_ZONE, amp_flow.requires)
self.assertEqual(5, len(amp_flow.provides)) self.assertEqual(5, len(amp_flow.provides))
self.assertEqual(2, len(amp_flow.requires)) self.assertEqual(3, len(amp_flow.requires))
def test_get_create_amphora_for_lb_flow(self, mock_get_net_driver): def test_get_create_amphora_for_lb_flow(self, mock_get_net_driver):

View File

@ -447,15 +447,23 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _db_amphora_mock, None mock_driver.get_amphora.return_value = _db_amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) # Test with no AZ
computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
# Test with AZ
mock_driver.reset_mock()
az = {constants.MANAGEMENT_NETWORK: uuidutils.generate_uuid()}
computewait.execute(COMPUTE_ID, AMPHORA_ID, az)
mock_driver.get_amphora.assert_called_once_with(
COMPUTE_ID, az[constants.MANAGEMENT_NETWORK])
# Test with deleted amp
_db_amphora_mock.status = constants.DELETED _db_amphora_mock.status = constants.DELETED
self.assertRaises(exceptions.ComputeWaitTimeoutException, self.assertRaises(exceptions.ComputeWaitTimeoutException,
computewait.execute, computewait.execute,
_db_amphora_mock, AMPHORA_ID) _amphora_mock, AMPHORA_ID, None)
@mock.patch('octavia.controller.worker.amphora_rate_limit' @mock.patch('octavia.controller.worker.amphora_rate_limit'
'.AmphoraBuildRateLimit.remove_from_build_req_queue') '.AmphoraBuildRateLimit.remove_from_build_req_queue')
@ -474,15 +482,15 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _db_amphora_mock, None mock_driver.get_amphora.return_value = _db_amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID) computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
_db_amphora_mock.status = constants.ERROR _db_amphora_mock.status = constants.ERROR
self.assertRaises(exceptions.ComputeBuildException, self.assertRaises(exceptions.ComputeBuildException,
computewait.execute, computewait.execute,
_db_amphora_mock, AMPHORA_ID) _db_amphora_mock, AMPHORA_ID, None)
@mock.patch('octavia.controller.worker.amphora_rate_limit' @mock.patch('octavia.controller.worker.amphora_rate_limit'
'.AmphoraBuildRateLimit.remove_from_build_req_queue') '.AmphoraBuildRateLimit.remove_from_build_req_queue')
@ -499,9 +507,9 @@ class TestComputeTasks(base.TestCase):
mock_driver.get_amphora.return_value = _db_amphora_mock, None mock_driver.get_amphora.return_value = _db_amphora_mock, None
computewait = compute_tasks.ComputeActiveWait() computewait = compute_tasks.ComputeActiveWait()
computewait.execute(COMPUTE_ID, AMPHORA_ID) computewait.execute(COMPUTE_ID, AMPHORA_ID, None)
mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID) mock_driver.get_amphora.assert_called_once_with(COMPUTE_ID, None)
mock_remove_from_build_queue.assert_not_called() mock_remove_from_build_queue.assert_not_called()
@mock.patch('stevedore.driver.DriverManager.driver') @mock.patch('stevedore.driver.DriverManager.driver')