OVS Mech: Set hybrid plug based on agent config
This adjusts the logic in the OVS mechanism driver to determine what the ovs_hybrid_plug value should be set to in the VIF details. Previously it was based purely on the firewall driver configured on the server side. This prevented a mixed environment where some agents might be running a native OVS firewall driver while others are still based on the IPTables hybrid driver. This patch has the OVS agents report back whether they want hybrid plugging in their configuration dictionary sent during report_state. The OVS agent sets this based on an explicit attribute on the firewall driver requesting OVS hybrid plugging. To maintain backward compat, if an agent doesn't report this, the old logic of basing it off of the server-side config is applied. DocImpact: The server no longer needs to be configured with a firewall driver for OVS. It will read config from agent state reports. Closes-Bug: #1560957 Change-Id: Ie554c2d37ce036e7b51818048153b466eee02913
This commit is contained in:
parent
63e8c6da19
commit
2f17a30ba0
@ -896,6 +896,7 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
|
||||
|
||||
class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver):
|
||||
OVS_HYBRID_TAP_PREFIX = constants.TAP_DEVICE_PREFIX
|
||||
OVS_HYBRID_PLUG_REQUIRED = True
|
||||
|
||||
def _port_chain_name(self, port, direction):
|
||||
return iptables_manager.get_chain_name(
|
||||
|
@ -44,6 +44,7 @@ from neutron.common import ipv6_utils as ipv6
|
||||
from neutron.common import topics
|
||||
from neutron.common import utils as n_utils
|
||||
from neutron import context
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.common import utils as p_utils
|
||||
from neutron.plugins.ml2.drivers.l2pop.rpc_manager import l2population_rpc
|
||||
@ -228,6 +229,34 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
self.enable_tunneling,
|
||||
self.enable_distributed_routing)
|
||||
|
||||
if self.enable_tunneling:
|
||||
self.setup_tunnel_br_flows()
|
||||
|
||||
self.dvr_agent.setup_dvr_flows()
|
||||
|
||||
# Collect additional bridges to monitor
|
||||
self.ancillary_brs = self.setup_ancillary_bridges(
|
||||
ovs_conf.integration_bridge, ovs_conf.tunnel_bridge)
|
||||
|
||||
# In order to keep existed device's local vlan unchanged,
|
||||
# restore local vlan mapping at start
|
||||
self._restore_local_vlan_map()
|
||||
|
||||
# Security group agent support
|
||||
self.sg_agent = sg_rpc.SecurityGroupAgentRpc(self.context,
|
||||
self.sg_plugin_rpc, self.local_vlan_map,
|
||||
defer_refresh_firewall=True, integration_bridge=self.int_br)
|
||||
|
||||
# we default to False to provide backward compat with out of tree
|
||||
# firewall drivers that expect the logic that existed on the Neutron
|
||||
# server which only enabled hybrid plugging based on the use of the
|
||||
# hybrid driver.
|
||||
hybrid_plug = getattr(self.sg_agent.firewall,
|
||||
'OVS_HYBRID_PLUG_REQUIRED', False)
|
||||
self.prevent_arp_spoofing = (
|
||||
agent_conf.prevent_arp_spoofing and
|
||||
not self.sg_agent.firewall.provides_arp_spoofing_protection)
|
||||
|
||||
#TODO(mangelajo): optimize resource_versions to only report
|
||||
# versions about resources which are common,
|
||||
# or which are used by specific extensions.
|
||||
@ -249,7 +278,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
'datapath_type': ovs_conf.datapath_type,
|
||||
'ovs_capabilities': self.ovs.capabilities,
|
||||
'vhostuser_socket_dir':
|
||||
ovs_conf.vhostuser_socket_dir},
|
||||
ovs_conf.vhostuser_socket_dir,
|
||||
portbindings.OVS_HYBRID_PLUG: hybrid_plug},
|
||||
'resource_versions': resources.LOCAL_RESOURCE_VERSIONS,
|
||||
'agent_type': agent_conf.agent_type,
|
||||
'start_flag': True}
|
||||
@ -259,29 +289,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
heartbeat = loopingcall.FixedIntervalLoopingCall(
|
||||
self._report_state)
|
||||
heartbeat.start(interval=report_interval)
|
||||
|
||||
if self.enable_tunneling:
|
||||
self.setup_tunnel_br_flows()
|
||||
|
||||
self.dvr_agent.setup_dvr_flows()
|
||||
|
||||
# Collect additional bridges to monitor
|
||||
self.ancillary_brs = self.setup_ancillary_bridges(
|
||||
ovs_conf.integration_bridge, ovs_conf.tunnel_bridge)
|
||||
|
||||
# In order to keep existed device's local vlan unchanged,
|
||||
# restore local vlan mapping at start
|
||||
self._restore_local_vlan_map()
|
||||
|
||||
# Security group agent support
|
||||
self.sg_agent = sg_rpc.SecurityGroupAgentRpc(self.context,
|
||||
self.sg_plugin_rpc, self.local_vlan_map,
|
||||
defer_refresh_firewall=True, integration_bridge=self.int_br)
|
||||
|
||||
self.prevent_arp_spoofing = (
|
||||
agent_conf.prevent_arp_spoofing and
|
||||
not self.sg_agent.firewall.provides_arp_spoofing_protection)
|
||||
|
||||
# Initialize iteration counter
|
||||
self.iter_num = 0
|
||||
self.run_daemon_loop = True
|
||||
|
@ -85,10 +85,16 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
||||
return self.vif_type
|
||||
|
||||
def get_vif_details(self, agent, context):
|
||||
if (agent['configurations'].get('datapath_type') !=
|
||||
a_const.OVS_DATAPATH_NETDEV):
|
||||
return self.vif_details
|
||||
caps = agent['configurations'].get('ovs_capabilities', {})
|
||||
a_config = agent['configurations']
|
||||
if a_config.get('datapath_type') != a_const.OVS_DATAPATH_NETDEV:
|
||||
details = dict(self.vif_details)
|
||||
hybrid = portbindings.OVS_HYBRID_PLUG
|
||||
if hybrid in a_config:
|
||||
# we only override the vif_details for hybrid pluggin set
|
||||
# in the constuctor if the agent specifically requests it
|
||||
details[hybrid] = a_config[hybrid]
|
||||
return details
|
||||
caps = a_config.get('ovs_capabilities', {})
|
||||
if a_const.OVS_DPDK_VHOST_USER in caps.get('iface_types', []):
|
||||
sock_path = self.agent_vhu_sockpath(agent, context.current['id'])
|
||||
return {
|
||||
|
@ -115,6 +115,10 @@ class TestOvsNeutronAgent(object):
|
||||
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config',
|
||||
new_callable=mock.PropertyMock,
|
||||
return_value={}).start()
|
||||
self.agent = self._make_agent()
|
||||
self.agent.sg_agent = mock.Mock()
|
||||
|
||||
def _make_agent(self):
|
||||
with mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
'setup_integration_br'),\
|
||||
mock.patch.object(self.mod_agent.OVSNeutronAgent,
|
||||
@ -129,10 +133,10 @@ class TestOvsNeutronAgent(object):
|
||||
mock.patch(
|
||||
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
|
||||
return_value=[]):
|
||||
self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(),
|
||||
cfg.CONF)
|
||||
self.agent.tun_br = self.br_tun_cls(br_name='br-tun')
|
||||
self.agent.sg_agent = mock.Mock()
|
||||
agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(),
|
||||
cfg.CONF)
|
||||
agent.tun_br = self.br_tun_cls(br_name='br-tun')
|
||||
return agent
|
||||
|
||||
def _mock_port_bound(self, ofport=None, new_local_vlan=None,
|
||||
old_local_vlan=None, db_get_val=None):
|
||||
@ -933,6 +937,31 @@ class TestOvsNeutronAgent(object):
|
||||
setup_port_filters.assert_called_once_with(
|
||||
set(), port_info.get('updated', set()))
|
||||
|
||||
def test_hybrid_plug_flag_based_on_firewall(self):
|
||||
cfg.CONF.set_default(
|
||||
'firewall_driver',
|
||||
'neutron.agent.firewall.NoopFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
agt = self._make_agent()
|
||||
self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug'])
|
||||
cfg.CONF.set_default(
|
||||
'firewall_driver',
|
||||
'neutron.agent.linux.openvswitch_firewall.OVSFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
with mock.patch('neutron.agent.linux.openvswitch_firewall.'
|
||||
'OVSFirewallDriver.initialize_bridge'):
|
||||
agt = self._make_agent()
|
||||
self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug'])
|
||||
cfg.CONF.set_default(
|
||||
'firewall_driver',
|
||||
'neutron.agent.linux.iptables_firewall.'
|
||||
'OVSHybridIptablesFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
with mock.patch('neutron.agent.linux.iptables_firewall.'
|
||||
'IptablesFirewallDriver._populate_initial_zone_map'):
|
||||
agt = self._make_agent()
|
||||
self.assertTrue(agt.agent_state['configurations']['ovs_hybrid_plug'])
|
||||
|
||||
def test_report_state(self):
|
||||
with mock.patch.object(self.agent.state_rpc,
|
||||
"report_state") as report_st:
|
||||
@ -2077,7 +2106,7 @@ class TestOvsDvrNeutronAgent(object):
|
||||
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
|
||||
return_value=[]):
|
||||
self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(),
|
||||
cfg.CONF)
|
||||
cfg.CONF)
|
||||
self.agent.tun_br = self.br_tun_cls(br_name='br-tun')
|
||||
self.agent.sg_agent = mock.Mock()
|
||||
|
||||
|
@ -17,6 +17,7 @@ from oslo_config import cfg
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers.openvswitch.mech_driver \
|
||||
import mech_openvswitch
|
||||
from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base
|
||||
@ -71,6 +72,49 @@ class OpenvswitchMechanismSGDisabledBaseTestCase(
|
||||
super(OpenvswitchMechanismSGDisabledBaseTestCase, self).setUp()
|
||||
|
||||
|
||||
class OpenvswitchMechanismHybridPlugTestCase(OpenvswitchMechanismBaseTestCase):
|
||||
|
||||
def _make_port_ctx(self, agents):
|
||||
segments = [{api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local'}]
|
||||
return base.FakePortContext(self.AGENT_TYPE, agents, segments,
|
||||
vnic_type=self.VNIC_TYPE)
|
||||
|
||||
def test_backward_compat_with_unreporting_agent(self):
|
||||
hybrid = portbindings.OVS_HYBRID_PLUG
|
||||
# agent didn't report so it should be hybrid based on server config
|
||||
context = self._make_port_ctx(self.AGENTS)
|
||||
self.driver.bind_port(context)
|
||||
self.assertTrue(context._bound_vif_details[hybrid])
|
||||
self.driver.vif_details[hybrid] = False
|
||||
context = self._make_port_ctx(self.AGENTS)
|
||||
self.driver.bind_port(context)
|
||||
self.assertFalse(context._bound_vif_details[hybrid])
|
||||
|
||||
def test_hybrid_plug_true_if_agent_requests(self):
|
||||
hybrid = portbindings.OVS_HYBRID_PLUG
|
||||
# set server side default to false and ensure that hybrid becomes
|
||||
# true if requested by the agent
|
||||
self.driver.vif_details[hybrid] = False
|
||||
agents = [{'alive': True,
|
||||
'configurations': {hybrid: True},
|
||||
'host': 'host'}]
|
||||
context = self._make_port_ctx(agents)
|
||||
self.driver.bind_port(context)
|
||||
self.assertTrue(context._bound_vif_details[hybrid])
|
||||
|
||||
def test_hybrid_plug_false_if_agent_requests(self):
|
||||
hybrid = portbindings.OVS_HYBRID_PLUG
|
||||
# set server side default to true and ensure that hybrid becomes
|
||||
# false if requested by the agent
|
||||
self.driver.vif_details[hybrid] = True
|
||||
agents = [{'alive': True,
|
||||
'configurations': {hybrid: False},
|
||||
'host': 'host'}]
|
||||
context = self._make_port_ctx(agents)
|
||||
self.driver.bind_port(context)
|
||||
self.assertFalse(context._bound_vif_details[hybrid])
|
||||
|
||||
|
||||
class OpenvswitchMechanismGenericTestCase(OpenvswitchMechanismBaseTestCase,
|
||||
base.AgentMechanismGenericTestCase):
|
||||
pass
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
prelude: >
|
||||
The Neutron server no longer needs to be configured
|
||||
with a firewall driver and it can support mixed
|
||||
environments of hybrid iptables firewalls and the
|
||||
pure OVS firewall.
|
||||
features:
|
||||
- The Neutron server now learns the appropriate firewall
|
||||
wiring behavior from each OVS agent so it no longer needs
|
||||
to be configured with the firewall_driver. This means
|
||||
it also supports multiple agents with different types
|
||||
of firewalls.
|
Loading…
Reference in New Issue
Block a user