Improve agent-based flat/vlan ml2 port binding failure logging
Port binding failure is an error and should be reported as such. Additionally, if a port binding fails on a host due to missing bridge mappings, it's currently quite a mystery to find out. This should be logged instead of requiring users to debug code. Not everyone enjoys debugging Python, as weird as that is! I refactored out the common code in check_segment_for_agent in order to make logging more robust for all agent-based mechanism drivers. The OVS and LB mech drivers already log due to a bridge mappings mismatch and the other agent based mech drivers will now log as well. Closes-Bug: #1377710 Change-Id: I451a0763908adcd845721e8cda7f3189da6c8b81
This commit is contained in:
parent
dbd37747d0
commit
72e285ac1a
|
@ -19,6 +19,7 @@ import six
|
|||
from neutron.extensions import portbindings
|
||||
from neutron.i18n import _LW
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -136,6 +137,26 @@ class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase):
|
|||
return False
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_allowed_network_types(self, agent=None):
|
||||
"""Return the agent's or driver's allowed network types.
|
||||
|
||||
For example: return ('flat', ...). You can also refer to the
|
||||
configuration the given agent exposes.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_mappings(self, agent):
|
||||
"""Return the agent's bridge or interface mappings.
|
||||
|
||||
For example: agent['configurations'].get('bridge_mappings', {}).
|
||||
"""
|
||||
pass
|
||||
|
||||
def physnet_in_mappings(self, physnet, mappings):
|
||||
"""Is the physical network part of the given mappings?"""
|
||||
return physnet in mappings
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
"""Check if segment can be bound for agent.
|
||||
|
||||
|
@ -149,3 +170,41 @@ class SimpleAgentMechanismDriverBase(AgentMechanismDriverBase):
|
|||
determine whether or not the specified network segment can be
|
||||
bound for the agent.
|
||||
"""
|
||||
|
||||
mappings = self.get_mappings(agent)
|
||||
allowed_network_types = self.get_allowed_network_types(agent)
|
||||
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for mappings: %(mappings)s "
|
||||
"with network types: %(network_types)s",
|
||||
{'segment': segment, 'mappings': mappings,
|
||||
'network_types': allowed_network_types})
|
||||
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
if network_type not in allowed_network_types:
|
||||
LOG.debug(
|
||||
'Network %(network_id)s is of type %(network_type)s '
|
||||
'but agent %(agent)s or mechanism driver only '
|
||||
'support %(allowed_network_types)s.',
|
||||
{'network_id': segment['id'],
|
||||
'network_type': network_type,
|
||||
'agent': agent['host'],
|
||||
'allowed_network_types': allowed_network_types})
|
||||
return False
|
||||
|
||||
if network_type in [p_constants.TYPE_FLAT, p_constants.TYPE_VLAN]:
|
||||
physnet = segment[api.PHYSICAL_NETWORK]
|
||||
if not self.physnet_in_mappings(physnet, mappings):
|
||||
LOG.debug(
|
||||
'Network %(network_id)s is connected to physical '
|
||||
'network %(physnet)s, but agent %(agent)s reported '
|
||||
'physical networks %(mappings)s. '
|
||||
'The physical network must be configured on the '
|
||||
'agent if binding is to succeed.',
|
||||
{'network_id': segment['id'],
|
||||
'physnet': physnet,
|
||||
'agent': agent['host'],
|
||||
'mappings': mappings})
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -18,7 +18,7 @@ import re
|
|||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2.drivers import mech_agent
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -39,16 +39,12 @@ class HypervMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
|||
portbindings.VIF_TYPE_HYPERV,
|
||||
{portbindings.CAP_PORT_FILTER: False})
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
mappings = agent['configurations'].get('vswitch_mappings', {})
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for mappings: %(mappings)s",
|
||||
{'segment': segment, 'mappings': mappings})
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
if network_type == 'local':
|
||||
return True
|
||||
elif network_type in ['flat', 'vlan']:
|
||||
for pattern in mappings:
|
||||
if re.match(pattern, segment[api.PHYSICAL_NETWORK]):
|
||||
return True
|
||||
return False
|
||||
def get_allowed_network_types(self, agent=None):
|
||||
return [p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
|
||||
p_constants.TYPE_VLAN]
|
||||
|
||||
def get_mappings(self, agent):
|
||||
return agent['configurations'].get('vswitch_mappings', {})
|
||||
|
||||
def physnet_in_mappings(self, physnet, mappings):
|
||||
return any(re.match(pattern, physnet) for pattern in mappings)
|
||||
|
|
|
@ -16,9 +16,8 @@
|
|||
from neutron.agent import securitygroups_rpc
|
||||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.i18n import _LW
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2.drivers import mech_agent
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -41,25 +40,10 @@ class LinuxbridgeMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
|||
portbindings.VIF_TYPE_BRIDGE,
|
||||
{portbindings.CAP_PORT_FILTER: sg_enabled})
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
mappings = agent['configurations'].get('interface_mappings', {})
|
||||
tunnel_types = agent['configurations'].get('tunnel_types', [])
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for mappings: %(mappings)s "
|
||||
"with tunnel_types: %(tunnel_types)s",
|
||||
{'segment': segment, 'mappings': mappings,
|
||||
'tunnel_types': tunnel_types})
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
if network_type == 'local':
|
||||
return True
|
||||
elif network_type in tunnel_types:
|
||||
return True
|
||||
elif network_type in ['flat', 'vlan']:
|
||||
is_mapping_present = segment[api.PHYSICAL_NETWORK] in mappings
|
||||
if not is_mapping_present:
|
||||
LOG.warn(_LW("Failed to find %(seg)s in mappings %(map)s"),
|
||||
{'seg': segment[api.PHYSICAL_NETWORK],
|
||||
'map': mappings})
|
||||
return is_mapping_present
|
||||
else:
|
||||
return False
|
||||
def get_allowed_network_types(self, agent):
|
||||
return (agent['configurations'].get('tunnel_types', []) +
|
||||
[p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
|
||||
p_constants.TYPE_VLAN])
|
||||
|
||||
def get_mappings(self, agent):
|
||||
return agent['configurations'].get('interface_mappings', {})
|
||||
|
|
|
@ -23,8 +23,7 @@ from neutron.agent import securitygroups_rpc
|
|||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2.drivers import mech_agent
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -49,20 +48,10 @@ class OfagentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
|||
portbindings.VIF_TYPE_OVS,
|
||||
vif_details)
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
interface_mappings = agent['configurations'].get('interface_mappings',
|
||||
{})
|
||||
tunnel_types = agent['configurations'].get('tunnel_types', [])
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for interface_mappings: %(interface_mappings)s "
|
||||
"with tunnel_types: %(tunnel_types)s",
|
||||
{'segment': segment,
|
||||
'interface_mappings': interface_mappings,
|
||||
'tunnel_types': tunnel_types})
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
return (
|
||||
network_type == p_const.TYPE_LOCAL or
|
||||
network_type in tunnel_types or
|
||||
(network_type in [p_const.TYPE_FLAT, p_const.TYPE_VLAN] and
|
||||
segment[api.PHYSICAL_NETWORK] in interface_mappings)
|
||||
)
|
||||
def get_allowed_network_types(self, agent):
|
||||
return (agent['configurations'].get('tunnel_types', []) +
|
||||
[p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
|
||||
p_constants.TYPE_VLAN])
|
||||
|
||||
def get_mappings(self, agent):
|
||||
return dict(agent['configurations'].get('interface_mappings', {}))
|
||||
|
|
|
@ -16,9 +16,8 @@
|
|||
from neutron.agent import securitygroups_rpc
|
||||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.i18n import _LW
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2.drivers import mech_agent
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
@ -43,25 +42,10 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
|||
portbindings.VIF_TYPE_OVS,
|
||||
vif_details)
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
mappings = agent['configurations'].get('bridge_mappings', {})
|
||||
tunnel_types = agent['configurations'].get('tunnel_types', [])
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for mappings: %(mappings)s "
|
||||
"with tunnel_types: %(tunnel_types)s",
|
||||
{'segment': segment, 'mappings': mappings,
|
||||
'tunnel_types': tunnel_types})
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
if network_type == 'local':
|
||||
return True
|
||||
elif network_type in tunnel_types:
|
||||
return True
|
||||
elif network_type in ['flat', 'vlan']:
|
||||
is_mapping_present = segment[api.PHYSICAL_NETWORK] in mappings
|
||||
if not is_mapping_present:
|
||||
LOG.warn(_LW("Failed to find %(seg)s in mappings %(map)s"),
|
||||
{'seg': segment[api.PHYSICAL_NETWORK],
|
||||
'map': mappings})
|
||||
return is_mapping_present
|
||||
else:
|
||||
return False
|
||||
def get_allowed_network_types(self, agent):
|
||||
return (agent['configurations'].get('tunnel_types', []) +
|
||||
[p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
|
||||
p_constants.TYPE_VLAN])
|
||||
|
||||
def get_mappings(self, agent):
|
||||
return agent['configurations'].get('bridge_mappings', {})
|
||||
|
|
|
@ -19,6 +19,7 @@ from oslo.config import cfg
|
|||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.common import constants as p_constants
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers import mech_agent
|
||||
from neutron.plugins.ml2.drivers.mlnx import config # noqa
|
||||
|
@ -49,19 +50,12 @@ class MlnxMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
|
|||
{portbindings.CAP_PORT_FILTER: False},
|
||||
portbindings.VNIC_TYPES)
|
||||
|
||||
def check_segment_for_agent(self, segment, agent):
|
||||
mappings = agent['configurations'].get('interface_mappings', {})
|
||||
LOG.debug("Checking segment: %(segment)s "
|
||||
"for mappings: %(mappings)s ",
|
||||
{'segment': segment, 'mappings': mappings})
|
||||
def get_allowed_network_types(self, agent=None):
|
||||
return [p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
|
||||
p_constants.TYPE_VLAN]
|
||||
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
if network_type == 'local':
|
||||
return True
|
||||
elif network_type in ['flat', 'vlan']:
|
||||
return segment[api.PHYSICAL_NETWORK] in mappings
|
||||
else:
|
||||
return False
|
||||
def get_mappings(self, agent):
|
||||
return agent['configurations'].get('interface_mappings', {})
|
||||
|
||||
def try_to_bind_segment_for_agent(self, context, segment, agent):
|
||||
if self.check_segment_for_agent(segment, agent):
|
||||
|
|
|
@ -21,7 +21,7 @@ from neutron.common import exceptions as exc
|
|||
from neutron.extensions import multiprovidernet as mpnet
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron.i18n import _LE, _LI, _LW
|
||||
from neutron.i18n import _LE, _LI
|
||||
from neutron.openstack.common import log
|
||||
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
||||
from neutron.plugins.ml2 import db
|
||||
|
@ -594,9 +594,9 @@ class MechanismManager(stevedore.named.NamedExtensionManager):
|
|||
"bind_port"),
|
||||
driver.name)
|
||||
binding.vif_type = portbindings.VIF_TYPE_BINDING_FAILED
|
||||
LOG.warning(_LW("Failed to bind port %(port)s on host %(host)s"),
|
||||
{'port': context._port['id'],
|
||||
'host': binding.host})
|
||||
LOG.error(_LE("Failed to bind port %(port)s on host %(host)s"),
|
||||
{'port': context._port['id'],
|
||||
'host': binding.host})
|
||||
|
||||
|
||||
class ExtensionManager(stevedore.named.NamedExtensionManager):
|
||||
|
|
|
@ -33,13 +33,17 @@ class MlnxMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
|||
BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
AGENTS_BAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS},
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'bad_host_1'},
|
||||
{'alive': True,
|
||||
'configurations': BAD_CONFIGS}]
|
||||
'configurations': BAD_CONFIGS,
|
||||
'host': 'bad_host_2'}]
|
||||
|
||||
def setUp(self):
|
||||
super(MlnxMechanismBaseTestCase, self).setUp()
|
||||
|
|
|
@ -38,13 +38,17 @@ class OfagentMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
|||
'tunnel_types': BAD_TUNNEL_TYPES}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
AGENTS_BAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS},
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'bad_host_1'},
|
||||
{'alive': True,
|
||||
'configurations': BAD_CONFIGS}]
|
||||
'configurations': BAD_CONFIGS,
|
||||
'host': 'bad_host_2'}]
|
||||
|
||||
def setUp(self):
|
||||
super(OfagentMechanismBaseTestCase, self).setUp()
|
||||
|
|
|
@ -31,13 +31,17 @@ class HypervMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
|||
BAD_CONFIGS = {'vswitch_mappings': BAD_MAPPINGS}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
AGENTS_BAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS},
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'bad_host_1'},
|
||||
{'alive': True,
|
||||
'configurations': BAD_CONFIGS}]
|
||||
'configurations': BAD_CONFIGS,
|
||||
'host': 'bad_host_2'}]
|
||||
|
||||
def setUp(self):
|
||||
super(HypervMechanismBaseTestCase, self).setUp()
|
||||
|
|
|
@ -35,13 +35,17 @@ class LinuxbridgeMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
|||
'tunnel_types': BAD_TUNNEL_TYPES}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
AGENTS_BAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS},
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'bad_host_1'},
|
||||
{'alive': True,
|
||||
'configurations': BAD_CONFIGS}]
|
||||
'configurations': BAD_CONFIGS,
|
||||
'host': 'bad_host_2'}]
|
||||
|
||||
def setUp(self):
|
||||
super(LinuxbridgeMechanismBaseTestCase, self).setUp()
|
||||
|
|
|
@ -38,13 +38,17 @@ class OpenvswitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
|||
'tunnel_types': BAD_TUNNEL_TYPES}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'host'}]
|
||||
AGENTS_DEAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS}]
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'dead_host'}]
|
||||
AGENTS_BAD = [{'alive': False,
|
||||
'configurations': GOOD_CONFIGS},
|
||||
'configurations': GOOD_CONFIGS,
|
||||
'host': 'bad_host_1'},
|
||||
{'alive': True,
|
||||
'configurations': BAD_CONFIGS}]
|
||||
'configurations': BAD_CONFIGS,
|
||||
'host': 'bad_host_2'}]
|
||||
|
||||
def setUp(self):
|
||||
super(OpenvswitchMechanismBaseTestCase, self).setUp()
|
||||
|
|
Loading…
Reference in New Issue