Fixes failover flow with namespace driver

This patch updates the haproxy service scripts to handle the case
where the network interfaces have not yet been plugged.  This can
occur in a failover situation.
This patch also makes sure we don't move the management lan interface
into the network namespace.

Closes-Bug: #1509706
Closes-Bug: #1577963
Change-Id: I04d267bd3cdedca11f0350c5255086233cba14ec
This commit is contained in:
Michael Johnson
2016-05-03 22:30:42 +00:00
parent d30d18656e
commit 7ba33e6ee4
26 changed files with 521 additions and 134 deletions

View File

@@ -72,7 +72,7 @@ class TestPlug(base.TestCase):
mock_flask.jsonify.assert_any_call({
'message': 'OK',
'details': 'VIP {vip} plugged on interface {interface}'.format(
vip=FAKE_IP_IPV4, interface=FAKE_INTERFACE)
vip=FAKE_IP_IPV4, interface='eth1')
})
@mock.patch.object(plug, "flask")
@@ -96,7 +96,7 @@ class TestPlug(base.TestCase):
mock_flask.jsonify.assert_any_call({
'message': 'OK',
'details': 'VIP {vip} plugged on interface {interface}'.format(
vip=FAKE_IP_IPV6_EXPANDED, interface=FAKE_INTERFACE)
vip=FAKE_IP_IPV6_EXPANDED, interface='eth1')
})
@mock.patch.object(plug, "flask")
@@ -118,3 +118,17 @@ class TestPlug(base.TestCase):
mac_address=FAKE_MAC_ADDRESS
)
mock_flask.jsonify.assert_any_call({'message': 'Invalid VIP'})
@mock.patch('pyroute2.NetNS')
def test__netns_interface_exists(self, mock_netns):
netns_handle = mock_netns.return_value.__enter__.return_value
netns_handle.get_links.return_value = [{
'attrs': [['IFLA_ADDRESS', '123']]}]
# Interface is found in netns
self.assertTrue(plug._netns_interface_exists('123'))
# Interface is not found in netns
self.assertFalse(plug._netns_interface_exists('321'))

View File

@@ -159,7 +159,7 @@ class TestHaproxyAmphoraLoadBalancerDriverTest(base.TestCase):
amphorae_network_config.get().vip_subnet.cidr = FAKE_CIDR
amphorae_network_config.get().vip_subnet.gateway_ip = FAKE_GATEWAY
amphorae_network_config.get().vrrp_port = self.port
self.driver.post_vip_plug(self.lb, amphorae_network_config)
self.driver.post_vip_plug(self.amp, self.lb, amphorae_network_config)
self.driver.client.plug_vip.assert_called_once_with(
self.amp, self.lb.vip.ip_address, self.subnet_info)

View File

@@ -115,7 +115,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
self.amphora.id, self.port.id)])
def test_post_vip_plug(self):
self.driver.post_vip_plug(self.load_balancer,
self.driver.post_vip_plug(self.amphora, self.load_balancer,
self.amphorae_net_configs)
expected_method_and_args = (self.load_balancer.id,
self.amphorae_net_configs,

View File

@@ -264,12 +264,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(12, len(amp_flow.provides))
self.assertEqual(11, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role=constants.ROLE_MASTER)
@@ -286,12 +283,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(12, len(amp_flow.provides))
self.assertEqual(11, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role=constants.ROLE_BACKUP)
@@ -308,12 +302,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(12, len(amp_flow.provides))
self.assertEqual(11, len(amp_flow.provides))
amp_flow = self.AmpFlow.get_failover_flow(role='BOGUSROLE')
@@ -330,12 +321,9 @@ class TestAmphoraFlows(base.TestCase):
self.assertIn(constants.COMPUTE_OBJ, amp_flow.provides)
self.assertIn(constants.LISTENERS, amp_flow.provides)
self.assertIn(constants.LOADBALANCER, amp_flow.provides)
self.assertIn(constants.MEMBER_PORTS, amp_flow.provides)
self.assertIn(constants.PORTS, amp_flow.provides)
self.assertIn(constants.VIP, amp_flow.provides)
self.assertEqual(2, len(amp_flow.requires))
self.assertEqual(12, len(amp_flow.provides))
self.assertEqual(11, len(amp_flow.provides))
def test_cert_rotate_amphora_flow(self, mock_get_net_driver):
self.AmpFlow = amphora_flows.AmphoraFlows()

View File

@@ -59,7 +59,7 @@ class TestAmphoraDriverTasks(base.TestCase):
def setUp(self):
_LB_mock.amphorae = _amphora_mock
_LB_mock.amphorae = [_amphora_mock]
_LB_mock.id = LB_ID
super(TestAmphoraDriverTasks, self).setUp()
@@ -307,10 +307,40 @@ class TestAmphoraDriverTasks(base.TestCase):
amphorae_net_config_mock = mock.Mock()
amphora_post_vip_plug_obj = amphora_driver_tasks.AmphoraPostVIPPlug()
amphora_post_vip_plug_obj.execute(_LB_mock, amphorae_net_config_mock)
amphora_post_vip_plug_obj.execute(_amphora_mock,
_LB_mock,
amphorae_net_config_mock)
mock_driver.post_vip_plug.assert_called_once_with(
_LB_mock, amphorae_net_config_mock)
_amphora_mock, _LB_mock, amphorae_net_config_mock)
# Test revert
amp = amphora_post_vip_plug_obj.revert(None, _amphora_mock, _LB_mock)
repo.LoadBalancerRepository.update.assert_called_once_with(
_session_mock,
id=LB_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
@mock.patch('octavia.db.repositories.LoadBalancerRepository.update')
def test_amphorae_post_vip_plug(self,
mock_loadbalancer_repo_update,
mock_driver,
mock_generate_uuid,
mock_log,
mock_get_session,
mock_listener_repo_get,
mock_listener_repo_update,
mock_amphora_repo_update):
amphorae_net_config_mock = mock.Mock()
amphora_post_vip_plug_obj = amphora_driver_tasks.AmphoraePostVIPPlug()
amphora_post_vip_plug_obj.execute(_LB_mock,
amphorae_net_config_mock)
mock_driver.post_vip_plug.assert_called_once_with(
_amphora_mock, _LB_mock, amphorae_net_config_mock)
# Test revert
amp = amphora_post_vip_plug_obj.revert(None, _LB_mock)

View File

@@ -490,3 +490,35 @@ class TestNetworkTasks(base.TestCase):
mock_driver.plug_port.assert_any_call(amphora, port1)
mock_driver.plug_port.assert_any_call(amphora, port2)
def test_plug_vip_port(self, mock_get_net_driver):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
vip_port = mock.MagicMock()
vrrp_port = mock.MagicMock()
amphorae_network_config = mock.MagicMock()
amphorae_network_config.get().vip_port = vip_port
amphorae_network_config.get().vrrp_port = vrrp_port
plugvipport = network_tasks.PlugVIPPort()
plugvipport.execute(self.amphora_mock, amphorae_network_config)
mock_driver.plug_port.assert_any_call(self.amphora_mock, vip_port)
mock_driver.plug_port.assert_any_call(self.amphora_mock, vrrp_port)
# test revert
plugvipport.revert(None, self.amphora_mock, amphorae_network_config)
mock_driver.unplug_port.assert_any_call(self.amphora_mock, vip_port)
mock_driver.unplug_port.assert_any_call(self.amphora_mock, vrrp_port)
def test_wait_for_port_detach(self, mock_get_net_driver):
mock_driver = mock.MagicMock()
mock_get_net_driver.return_value = mock_driver
amphora = o_data_models.Amphora(id=AMPHORA_ID,
lb_network_ip=IP_ADDRESS)
waitforportdetach = network_tasks.WaitForPortDetach()
waitforportdetach.execute(amphora)
mock_driver.wait_for_port_detach.assert_called_once_with(amphora)

View File

@@ -17,6 +17,8 @@ import copy
import mock
from neutronclient.common import exceptions as neutron_exceptions
from novaclient.client import exceptions as nova_exceptions
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from oslo_utils import uuidutils
from octavia.common import clients
@@ -52,6 +54,7 @@ class TestAllowedAddressPairsDriver(base.TestCase):
HA_PORT_ID = "8"
HA_IP = "12.0.0.2"
PORT_ID = uuidutils.generate_uuid()
DEVICE_ID = uuidutils.generate_uuid()
def setUp(self):
super(TestAllowedAddressPairsDriver, self).setUp()
@@ -586,8 +589,7 @@ class TestAllowedAddressPairsDriver(base.TestCase):
lb_network_ip=self.LB_NET_IP, ha_port_id=self.HA_PORT_ID,
ha_ip=self.HA_IP)
self.driver.failover_preparation(amphora)
port_update.assert_called_once_with(ports['ports'][1].get('id'),
{'port': {'device_id': ''}})
self.assertFalse(port_update.called)
self.driver.dns_integration_enabled = original_dns_integration_state
def test_failover_preparation_dns_integration(self):
@@ -611,8 +613,7 @@ class TestAllowedAddressPairsDriver(base.TestCase):
ha_ip=self.HA_IP)
self.driver.failover_preparation(amphora)
port_update.assert_called_once_with(ports['ports'][1].get('id'),
{'port': {'dns_name': '',
'device_id': ''}})
{'port': {'dns_name': ''}})
self.driver.dns_integration_enabled = original_dns_integration_state
def _failover_show_port_side_effect(self, port_id):
@@ -712,3 +713,71 @@ class TestAllowedAddressPairsDriver(base.TestCase):
expected_subnet_id = fake_subnet['subnet']['id']
self.assertEqual(expected_subnet_id, config.ha_subnet.id)
self.assertEqual(expected_subnet_id, config.vrrp_subnet.id)
@mock.patch('time.sleep')
def test_wait_for_port_detach(self, mock_sleep):
amphora = data_models.Amphora(
id=self.AMPHORA_ID, load_balancer_id=self.LB_ID,
compute_id=self.COMPUTE_ID, status=self.ACTIVE,
lb_network_ip=self.LB_NET_IP, ha_port_id=self.HA_PORT_ID,
ha_ip=self.HA_IP)
ports = {"ports": [
{"fixed_ips": [{"subnet_id": self.SUBNET_ID_1,
"ip_address": self.IP_ADDRESS_1}],
"id": self.FIXED_IP_ID_1, "network_id": self.NETWORK_ID_1},
{"fixed_ips": [{"subnet_id": self.SUBNET_ID_2,
"ip_address": self.IP_ADDRESS_2}],
"id": self.FIXED_IP_ID_2, "network_id": self.NETWORK_ID_2}]}
show_port_1_without_device_id = {"fixed_ips": [
{"subnet_id": self.SUBNET_ID_1, "ip_address": self.IP_ADDRESS_1}],
"id": self.FIXED_IP_ID_1, "network_id": self.NETWORK_ID_1,
"device_id": ''}
show_port_2_with_device_id = {"fixed_ips": [
{"subnet_id": self.SUBNET_ID_2, "ip_address": self.IP_ADDRESS_2}],
"id": self.FIXED_IP_ID_2, "network_id": self.NETWORK_ID_2,
"device_id": self.DEVICE_ID}
show_port_2_without_device_id = {"fixed_ips": [
{"subnet_id": self.SUBNET_ID_2, "ip_address": self.IP_ADDRESS_2}],
"id": self.FIXED_IP_ID_2, "network_id": self.NETWORK_ID_2,
"device_id": None}
self.driver.neutron_client.list_ports.return_value = ports
port_mock = mock.MagicMock()
port_mock.get = mock.Mock(
side_effect=[show_port_1_without_device_id,
show_port_2_with_device_id,
show_port_2_with_device_id,
show_port_2_without_device_id])
self.driver.neutron_client.show_port.return_value = port_mock
self.driver.wait_for_port_detach(amphora)
self.assertEqual(1, mock_sleep.call_count)
@mock.patch('time.time')
@mock.patch('time.sleep')
def test_wait_for_port_detach_timeout(self, mock_sleep, mock_time):
mock_time.side_effect = [1, 2, 6]
conf = oslo_fixture.Config(cfg.CONF)
conf.config(group="networking", port_detach_timeout=5)
amphora = data_models.Amphora(
id=self.AMPHORA_ID, load_balancer_id=self.LB_ID,
compute_id=self.COMPUTE_ID, status=self.ACTIVE,
lb_network_ip=self.LB_NET_IP, ha_port_id=self.HA_PORT_ID,
ha_ip=self.HA_IP)
ports = {"ports": [
{"fixed_ips": [{"subnet_id": self.SUBNET_ID_1,
"ip_address": self.IP_ADDRESS_1}],
"id": self.FIXED_IP_ID_1, "network_id": self.NETWORK_ID_1},
{"fixed_ips": [{"subnet_id": self.SUBNET_ID_2,
"ip_address": self.IP_ADDRESS_2}],
"id": self.FIXED_IP_ID_2, "network_id": self.NETWORK_ID_2}]}
show_port_1_with_device_id = {"fixed_ips": [
{"subnet_id": self.SUBNET_ID_2, "ip_address": self.IP_ADDRESS_2}],
"id": self.FIXED_IP_ID_2, "network_id": self.NETWORK_ID_2,
"device_id": self.DEVICE_ID}
self.driver.neutron_client.list_ports.return_value = ports
port_mock = mock.MagicMock()
port_mock.get = mock.Mock(
return_value=show_port_1_with_device_id)
self.driver.neutron_client.show_port.return_value = port_mock
self.assertRaises(network_base.TimeoutException,
self.driver.wait_for_port_detach,
amphora)