ML2 additions to support DVR
This patch introduces the changes necessary to support DVR at Layer 2 with ML2, and L2pop. Partially-implements: blueprint neutron-ovs-dvr Change-Id: I709d877e7df83728868e68e023e6a37bb2e11704 Authored-by: Vivekanandan Narasimhan <vivekanandan.narasimhan@hp.com>
This commit is contained in:
parent
d85799a899
commit
b13bdf4dea
@ -49,6 +49,7 @@ VIF_DETAILS_VLAN = 'vlan'
|
||||
|
||||
VIF_TYPE_UNBOUND = 'unbound'
|
||||
VIF_TYPE_BINDING_FAILED = 'binding_failed'
|
||||
VIF_TYPE_DISTRIBUTED = 'distributed'
|
||||
VIF_TYPE_IOVISOR = 'iovisor'
|
||||
VIF_TYPE_OVS = 'ovs'
|
||||
VIF_TYPE_IVS = 'ivs'
|
||||
@ -66,7 +67,7 @@ VIF_TYPES = [VIF_TYPE_UNBOUND, VIF_TYPE_BINDING_FAILED, VIF_TYPE_OVS,
|
||||
VIF_TYPE_IVS, VIF_TYPE_BRIDGE, VIF_TYPE_802_QBG,
|
||||
VIF_TYPE_802_QBH, VIF_TYPE_HYPERV, VIF_TYPE_MIDONET,
|
||||
VIF_TYPE_MLNX_DIRECT, VIF_TYPE_MLNX_HOSTDEV, VIF_TYPE_HW_VEB,
|
||||
VIF_TYPE_DVS, VIF_TYPE_OTHER]
|
||||
VIF_TYPE_DVS, VIF_TYPE_OTHER, VIF_TYPE_DISTRIBUTED]
|
||||
|
||||
VNIC_NORMAL = 'normal'
|
||||
VNIC_DIRECT = 'direct'
|
||||
|
@ -119,6 +119,13 @@ def delete_dvr_port_binding(session, port_id, host):
|
||||
delete(synchronize_session=False))
|
||||
|
||||
|
||||
def delete_dvr_port_binding_if_stale(session, binding):
|
||||
if not binding.router_id and binding.status == n_const.PORT_STATUS_DOWN:
|
||||
with session.begin(subtransactions=True):
|
||||
LOG.debug("DVR: Deleting binding %s", binding)
|
||||
session.delete(binding)
|
||||
|
||||
|
||||
def get_port(session, port_id):
|
||||
"""Get port record for update within transcation."""
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import jsonutils
|
||||
from neutron.plugins.ml2 import db
|
||||
@ -149,3 +150,20 @@ class PortContext(MechanismDriverContext, api.PortContext):
|
||||
self._binding.vif_type = vif_type
|
||||
self._binding.vif_details = jsonutils.dumps(vif_details)
|
||||
self._new_port_status = status
|
||||
|
||||
|
||||
class DvrPortContext(PortContext):
|
||||
|
||||
def __init__(self, plugin, plugin_context, port, network, binding,
|
||||
original_port=None):
|
||||
super(DvrPortContext, self).__init__(
|
||||
plugin, plugin_context, port, network, binding,
|
||||
original_port=original_port)
|
||||
|
||||
@property
|
||||
def bound_host(self):
|
||||
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
|
||||
agent_host = self._binding.host
|
||||
else:
|
||||
agent_host = self._port['binding:host_id']
|
||||
return agent_host
|
||||
|
@ -42,17 +42,32 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
LOG.debug(_("Experimental L2 population driver"))
|
||||
self.rpc_ctx = n_context.get_admin_context_without_session()
|
||||
self.migrated_ports = {}
|
||||
self.remove_fdb_entries = {}
|
||||
|
||||
def _get_port_fdb_entries(self, port):
|
||||
return [[port['mac_address'],
|
||||
return [[port['mac_address'], port['device_owner'],
|
||||
ip['ip_address']] for ip in port['fixed_ips']]
|
||||
|
||||
def delete_port_precommit(self, context):
|
||||
port = context.current
|
||||
agent_host = context.host
|
||||
|
||||
if port['id'] not in self.remove_fdb_entries:
|
||||
self.remove_fdb_entries[port['id']] = {}
|
||||
|
||||
self.remove_fdb_entries[port['id']][agent_host] = (
|
||||
self._update_port_down(context, port, agent_host, 1))
|
||||
|
||||
def delete_port_postcommit(self, context):
|
||||
fanout_msg = self._update_port_down(
|
||||
context, context.current, context.host)
|
||||
if fanout_msg:
|
||||
self.L2populationAgentNotify.remove_fdb_entries(
|
||||
self.rpc_ctx, fanout_msg)
|
||||
port = context.current
|
||||
agent_host = context.host
|
||||
if port['id'] in self.remove_fdb_entries:
|
||||
for agent_host in list(self.remove_fdb_entries[port['id']]):
|
||||
self.L2populationAgentNotify.remove_fdb_entries(
|
||||
self.rpc_ctx,
|
||||
self.remove_fdb_entries[port['id']][agent_host])
|
||||
self.remove_fdb_entries[port['id']].pop(agent_host, 0)
|
||||
self.remove_fdb_entries.pop(port['id'], 0)
|
||||
|
||||
def _get_diff_ips(self, orig, port):
|
||||
orig_ips = set([ip['ip_address'] for ip in orig['fixed_ips']])
|
||||
@ -72,19 +87,21 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
context, orig, context.original_host)
|
||||
if not port_infos:
|
||||
return
|
||||
agent, agent_ip, segment, port_fdb_entries = port_infos
|
||||
agent, agent_host, agent_ip, segment, port_fdb_entries = port_infos
|
||||
|
||||
orig_mac_ip = [[port['mac_address'], ip] for ip in orig_ips]
|
||||
port_mac_ip = [[port['mac_address'], ip] for ip in port_ips]
|
||||
orig_mac_devowner_ip = [[port['mac_address'], port['device_owner'], ip]
|
||||
for ip in orig_ips]
|
||||
port_mac_devowner_ip = [[port['mac_address'], port['device_owner'], ip]
|
||||
for ip in port_ips]
|
||||
|
||||
upd_fdb_entries = {port['network_id']: {agent_ip: {}}}
|
||||
|
||||
ports = upd_fdb_entries[port['network_id']][agent_ip]
|
||||
if orig_mac_ip:
|
||||
ports['before'] = orig_mac_ip
|
||||
if orig_mac_devowner_ip:
|
||||
ports['before'] = orig_mac_devowner_ip
|
||||
|
||||
if port_mac_ip:
|
||||
ports['after'] = port_mac_ip
|
||||
if port_mac_devowner_ip:
|
||||
ports['after'] = port_mac_devowner_ip
|
||||
|
||||
self.L2populationAgentNotify.update_fdb_entries(
|
||||
self.rpc_ctx, {'chg_ip': upd_fdb_entries})
|
||||
@ -98,7 +115,16 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
diff_ips = self._get_diff_ips(orig, port)
|
||||
if diff_ips:
|
||||
self._fixed_ips_changed(context, orig, port, diff_ips)
|
||||
if (context.host != context.original_host
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
if context.status == const.PORT_STATUS_ACTIVE:
|
||||
self._update_port_up(context)
|
||||
if context.status == const.PORT_STATUS_DOWN:
|
||||
agent_host = context.host
|
||||
fdb_entries = self._update_port_down(
|
||||
context, port, agent_host)
|
||||
self.L2populationAgentNotify.remove_fdb_entries(
|
||||
self.rpc_ctx, fdb_entries)
|
||||
elif (context.host != context.original_host
|
||||
and context.status == const.PORT_STATUS_ACTIVE
|
||||
and not self.migrated_ports.get(orig['id'])):
|
||||
# The port has been migrated. We have to store the original
|
||||
@ -153,7 +179,7 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
|
||||
fdb_entries = self._get_port_fdb_entries(port)
|
||||
|
||||
return agent, agent_ip, segment, fdb_entries
|
||||
return agent, agent_host, agent_ip, segment, fdb_entries
|
||||
|
||||
def _update_port_up(self, context):
|
||||
port = context.current
|
||||
@ -161,7 +187,7 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
port_infos = self._get_port_infos(context, port, agent_host)
|
||||
if not port_infos:
|
||||
return
|
||||
agent, agent_ip, segment, port_fdb_entries = port_infos
|
||||
agent, agent_host, agent_ip, segment, port_fdb_entries = port_infos
|
||||
|
||||
network_id = port['network_id']
|
||||
|
||||
@ -184,8 +210,9 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
'ports': {}}}
|
||||
ports = agent_fdb_entries[network_id]['ports']
|
||||
|
||||
network_ports = self.get_network_ports(session, network_id)
|
||||
for network_port in network_ports:
|
||||
nondvr_network_ports = self.get_nondvr_network_ports(session,
|
||||
network_id)
|
||||
for network_port in nondvr_network_ports:
|
||||
binding, agent = network_port
|
||||
if agent.host == agent_host:
|
||||
continue
|
||||
@ -201,6 +228,22 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
agent_ports += self._get_port_fdb_entries(binding.port)
|
||||
ports[ip] = agent_ports
|
||||
|
||||
dvr_network_ports = self.get_dvr_network_ports(session, network_id)
|
||||
for network_port in dvr_network_ports:
|
||||
binding, agent = network_port
|
||||
if agent.host == agent_host:
|
||||
continue
|
||||
|
||||
ip = self.get_agent_ip(agent)
|
||||
if not ip:
|
||||
LOG.debug("Unable to retrieve the agent ip, check "
|
||||
"the agent %(agent_host)s configuration.",
|
||||
{'agent_host': agent.host})
|
||||
continue
|
||||
|
||||
agent_ports = ports.get(ip, [const.FLOODING_ENTRY])
|
||||
ports[ip] = agent_ports
|
||||
|
||||
# And notify other agents to add flooding entry
|
||||
other_fdb_entries[network_id]['ports'][agent_ip].append(
|
||||
const.FLOODING_ENTRY)
|
||||
@ -210,16 +253,19 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
self.rpc_ctx, agent_fdb_entries, agent_host)
|
||||
|
||||
# Notify other agents to add fdb rule for current port
|
||||
other_fdb_entries[network_id]['ports'][agent_ip] += port_fdb_entries
|
||||
if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
other_fdb_entries[network_id]['ports'][agent_ip] += (
|
||||
port_fdb_entries)
|
||||
|
||||
self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx,
|
||||
other_fdb_entries)
|
||||
|
||||
def _update_port_down(self, context, port, agent_host):
|
||||
def _update_port_down(self, context, port, agent_host,
|
||||
agent_active_ports_count_for_flooding=0):
|
||||
port_infos = self._get_port_infos(context, port, agent_host)
|
||||
if not port_infos:
|
||||
return
|
||||
agent, agent_ip, segment, port_fdb_entries = port_infos
|
||||
agent, agent_host, agent_ip, segment, port_fdb_entries = port_infos
|
||||
|
||||
network_id = port['network_id']
|
||||
|
||||
@ -231,12 +277,14 @@ class L2populationMechanismDriver(api.MechanismDriver,
|
||||
{'segment_id': segment['segmentation_id'],
|
||||
'network_type': segment['network_type'],
|
||||
'ports': {agent_ip: []}}}
|
||||
if not agent_active_ports:
|
||||
if agent_active_ports == agent_active_ports_count_for_flooding:
|
||||
# Agent is removing its last activated port in this network,
|
||||
# other agents needs to be notified to delete their flooding entry.
|
||||
other_fdb_entries[network_id]['ports'][agent_ip].append(
|
||||
const.FLOODING_ENTRY)
|
||||
# Notify other agents to remove fdb rules for current port
|
||||
other_fdb_entries[network_id]['ports'][agent_ip] += port_fdb_entries
|
||||
if port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
fdb_entries = port_fdb_entries
|
||||
other_fdb_entries[network_id]['ports'][agent_ip] += fdb_entries
|
||||
|
||||
return other_fdb_entries
|
||||
|
@ -32,6 +32,7 @@ from neutron.db import agents_db
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import dvr_mac_db
|
||||
from neutron.db import external_net_db
|
||||
from neutron.db import extradhcpopt_db
|
||||
from neutron.db import models_v2
|
||||
@ -68,6 +69,7 @@ TYPE_MULTI_SEGMENT = 'multi-segment'
|
||||
|
||||
|
||||
class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
dvr_mac_db.DVRDbMixin,
|
||||
external_net_db.External_net_db_mixin,
|
||||
sg_db_rpc.SecurityGroupServerRpcMixin,
|
||||
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
||||
@ -246,6 +248,13 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
binding.driver = None
|
||||
binding.segment = None
|
||||
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
binding.vif_type = portbindings.VIF_TYPE_DISTRIBUTED
|
||||
binding.vif_details = ''
|
||||
binding.driver = None
|
||||
binding.segment = None
|
||||
binding.host = ''
|
||||
|
||||
self._update_port_dict_binding(port, binding)
|
||||
return changes
|
||||
|
||||
@ -870,6 +879,84 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
need_notify=need_port_update_notify)
|
||||
return bound_port._port
|
||||
|
||||
def _process_dvr_port_binding(self, mech_context, context, attrs):
|
||||
binding = mech_context._binding
|
||||
port = mech_context.current
|
||||
|
||||
if binding.vif_type != portbindings.VIF_TYPE_UNBOUND:
|
||||
binding.vif_details = ''
|
||||
binding.vif_type = portbindings.VIF_TYPE_UNBOUND
|
||||
binding.driver = None
|
||||
binding.segment = None
|
||||
binding.host = ''
|
||||
|
||||
self._update_port_dict_binding(port, binding)
|
||||
binding.host = attrs and attrs.get(portbindings.HOST_ID)
|
||||
|
||||
def update_dvr_port_binding(self, context, id, port):
|
||||
attrs = port['port']
|
||||
|
||||
host = attrs and attrs.get(portbindings.HOST_ID)
|
||||
host_set = attributes.is_attr_set(host)
|
||||
|
||||
if not host_set:
|
||||
LOG.error(_("No Host supplied to bind DVR Port %s"), id)
|
||||
return
|
||||
|
||||
session = context.session
|
||||
binding = db.get_dvr_port_binding_by_host(session, id, host)
|
||||
if (not binding or
|
||||
binding.vif_type == portbindings.VIF_TYPE_BINDING_FAILED):
|
||||
with session.begin(subtransactions=True):
|
||||
if not binding:
|
||||
binding = db.ensure_dvr_port_binding(
|
||||
session, id, host, router_id=attrs['device_id'])
|
||||
orig_port = super(Ml2Plugin, self).get_port(context, id)
|
||||
network = self.get_network(context, orig_port['network_id'])
|
||||
mech_context = driver_context.DvrPortContext(self,
|
||||
context, orig_port, network,
|
||||
binding, original_port=orig_port)
|
||||
self._process_dvr_port_binding(mech_context, context, attrs)
|
||||
self.mechanism_manager.bind_port(mech_context)
|
||||
# Now try to commit result of attempting to bind the port.
|
||||
self._commit_dvr_port_binding(mech_context._plugin_context,
|
||||
orig_port['id'],
|
||||
host,
|
||||
mech_context)
|
||||
|
||||
def _commit_dvr_port_binding(self, plugin_context,
|
||||
port_id, host,
|
||||
mech_context):
|
||||
session = plugin_context.session
|
||||
new_binding = mech_context._binding
|
||||
with contextlib.nested(lockutils.lock('db-access'),
|
||||
session.begin(subtransactions=True)):
|
||||
# Get the current port state and build a new PortContext
|
||||
# reflecting this state as original state for subsequent
|
||||
# mechanism driver update_port_*commit() calls.
|
||||
cur_binding = db.get_dvr_port_binding_by_host(session,
|
||||
port_id,
|
||||
host)
|
||||
# Commit our binding results only if port has not been
|
||||
# successfully bound concurrently by another thread or
|
||||
# process and no binding inputs have been changed.
|
||||
commit = ((cur_binding.vif_type in
|
||||
[portbindings.VIF_TYPE_UNBOUND,
|
||||
portbindings.VIF_TYPE_BINDING_FAILED]) and
|
||||
new_binding.host == cur_binding.host and
|
||||
new_binding.vnic_type == cur_binding.vnic_type and
|
||||
new_binding.profile == cur_binding.profile)
|
||||
|
||||
if commit:
|
||||
# Update the port's binding state with our binding
|
||||
# results.
|
||||
cur_binding.vif_type = new_binding.vif_type
|
||||
cur_binding.vif_details = new_binding.vif_details
|
||||
cur_binding.driver = new_binding.driver
|
||||
cur_binding.segment = new_binding.segment
|
||||
if cur_binding.profile != new_binding.profile:
|
||||
cur_binding.profile = new_binding.profile
|
||||
|
||||
def delete_port(self, context, id, l3_port_check=True):
|
||||
LOG.debug(_("Deleting port %s"), id)
|
||||
l3plugin = manager.NeutronManager.get_service_plugins().get(
|
||||
@ -893,15 +980,24 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
port = self._make_port_dict(port_db)
|
||||
|
||||
network = self.get_network(context, port['network_id'])
|
||||
mech_context = driver_context.PortContext(self, context, port,
|
||||
network, binding)
|
||||
self.mechanism_manager.delete_port_precommit(mech_context)
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
LOG.debug(_("Calling base delete_port"))
|
||||
mech_context = None
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
bindings = db.get_dvr_port_bindings(context.session, id)
|
||||
for bind in bindings:
|
||||
mech_context = driver_context.DvrPortContext(
|
||||
self, context, port, network, bind)
|
||||
self.mechanism_manager.delete_port_precommit(mech_context)
|
||||
else:
|
||||
mech_context = driver_context.PortContext(self, context, port,
|
||||
network, binding)
|
||||
self.mechanism_manager.delete_port_precommit(mech_context)
|
||||
self._delete_port_security_group_bindings(context, id)
|
||||
if l3plugin:
|
||||
router_ids = l3plugin.disassociate_floatingips(
|
||||
context, id, do_notify=False)
|
||||
|
||||
LOG.debug("Calling delete_port for %(port_id)s owned by %(owner)s"
|
||||
% {"port_id": id, "owner": port['device_owner']})
|
||||
super(Ml2Plugin, self).delete_port(context, id)
|
||||
|
||||
# now that we've left db transaction, we are safe to notify
|
||||
@ -909,15 +1005,22 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
l3plugin.notify_routers_updated(context, router_ids)
|
||||
|
||||
try:
|
||||
self.mechanism_manager.delete_port_postcommit(mech_context)
|
||||
# for both normal and DVR Interface ports, only one invocation of
|
||||
# delete_port_postcommit. We use gather/scatter technique for DVR
|
||||
# interface ports, where the bindings are gathered in
|
||||
# delete_port_precommit() call earlier and scattered as l2pop
|
||||
# rules to cloud nodes in delete_port_postcommit() here
|
||||
if mech_context:
|
||||
self.mechanism_manager.delete_port_postcommit(mech_context)
|
||||
except ml2_exc.MechanismDriverError:
|
||||
# TODO(apech) - One or more mechanism driver failed to
|
||||
# delete the port. Ideally we'd notify the caller of the
|
||||
# fact that an error occurred.
|
||||
LOG.error(_("mechanism_manager.delete_port_postcommit failed"))
|
||||
LOG.error(_("mechanism_manager.delete_port_postcommit failed for "
|
||||
"port %s"), id)
|
||||
self.notify_security_groups_member_updated(context, port)
|
||||
|
||||
def get_bound_port_context(self, plugin_context, port_id):
|
||||
def get_bound_port_context(self, plugin_context, port_id, host=None):
|
||||
session = plugin_context.session
|
||||
with session.begin(subtransactions=True):
|
||||
try:
|
||||
@ -933,8 +1036,18 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
return
|
||||
port = self._make_port_dict(port_db)
|
||||
network = self.get_network(plugin_context, port['network_id'])
|
||||
port_context = driver_context.PortContext(
|
||||
self, plugin_context, port, network, port_db.port_binding)
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
binding = db.get_dvr_port_binding_by_host(
|
||||
session, port['id'], host)
|
||||
if not binding:
|
||||
LOG.error(_("Binding info for DVR port %s not found"),
|
||||
port_id)
|
||||
return None
|
||||
port_context = driver_context.DvrPortContext(
|
||||
self, plugin_context, port, network, binding)
|
||||
else:
|
||||
port_context = driver_context.PortContext(
|
||||
self, plugin_context, port, network, port_db.port_binding)
|
||||
|
||||
return self._bind_port_if_needed(port_context)
|
||||
|
||||
@ -956,7 +1069,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
LOG.warning(_("Port %(port)s updated up by agent not found"),
|
||||
{'port': port_id})
|
||||
return None
|
||||
if port.status != status:
|
||||
if (port.status != status and
|
||||
port['device_owner'] != const.DEVICE_OWNER_DVR_INTERFACE):
|
||||
original_port = self._make_port_dict(port)
|
||||
port.status = status
|
||||
updated_port = self._make_port_dict(port)
|
||||
@ -967,12 +1081,53 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
original_port=original_port)
|
||||
self.mechanism_manager.update_port_precommit(mech_context)
|
||||
updated = True
|
||||
elif port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
binding = db.get_dvr_port_binding_by_host(
|
||||
session, port['id'], host)
|
||||
if not binding:
|
||||
return
|
||||
binding['status'] = status
|
||||
binding.update(binding)
|
||||
updated = True
|
||||
|
||||
if (updated and
|
||||
port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE):
|
||||
with contextlib.nested(lockutils.lock('db-access'),
|
||||
session.begin(subtransactions=True)):
|
||||
port = db.get_port(session, port_id)
|
||||
if not port:
|
||||
LOG.warning(_("Port %s not found during update"), port_id)
|
||||
return
|
||||
original_port = self._make_port_dict(port)
|
||||
network = self.get_network(context,
|
||||
original_port['network_id'])
|
||||
port.status = db.generate_dvr_port_status(session, port['id'])
|
||||
updated_port = self._make_port_dict(port)
|
||||
mech_context = (driver_context.DvrPortContext(
|
||||
self, context, updated_port, network,
|
||||
binding, original_port=original_port))
|
||||
self.mechanism_manager.update_port_precommit(mech_context)
|
||||
|
||||
if updated:
|
||||
self.mechanism_manager.update_port_postcommit(mech_context)
|
||||
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
db.delete_dvr_port_binding_if_stale(session, binding)
|
||||
|
||||
return port['id']
|
||||
|
||||
def port_bound_to_host(self, context, port_id, host):
|
||||
port_host = db.get_port_binding_host(port_id)
|
||||
return (port_host == host)
|
||||
port = db.get_port(context.session, port_id)
|
||||
if not port:
|
||||
LOG.debug("No Port match for: %s", port_id)
|
||||
return False
|
||||
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||
bindings = db.get_dvr_port_bindings(context.session, port_id)
|
||||
for b in bindings:
|
||||
if b.host == host:
|
||||
return True
|
||||
LOG.debug("No binding found for DVR port %s", port['id'])
|
||||
return False
|
||||
else:
|
||||
port_host = db.get_port_binding_host(port_id)
|
||||
return (port_host == host)
|
||||
|
@ -89,7 +89,9 @@ class RpcCallbacks(n_rpc.RpcCallback,
|
||||
port_id = self._device_to_port_id(device)
|
||||
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
port_context = plugin.get_bound_port_context(rpc_context, port_id)
|
||||
port_context = plugin.get_bound_port_context(rpc_context,
|
||||
port_id,
|
||||
host)
|
||||
if not port_context:
|
||||
LOG.warning(_("Device %(device)s requested by agent "
|
||||
"%(agent_id)s not found in database"),
|
||||
|
@ -107,6 +107,18 @@ class Ml2DBTestCase(base.BaseTestCase):
|
||||
ml2_db.delete_dvr_port_binding(
|
||||
self.ctx.session, 'foo_port_id', 'foo_host')
|
||||
|
||||
def test_delete_dvr_port_binding_if_stale(self):
|
||||
network_id = 'foo_network_id'
|
||||
port_id = 'foo_port_id'
|
||||
self._setup_neutron_network(network_id, [port_id])
|
||||
binding = self._setup_dvr_binding(
|
||||
network_id, port_id, None, 'foo_host_id')
|
||||
|
||||
ml2_db.delete_dvr_port_binding_if_stale(self.ctx.session, binding)
|
||||
count = (self.ctx.session.query(ml2_models.DVRPortBinding).
|
||||
filter_by(port_id=binding.port_id).count())
|
||||
self.assertFalse(count)
|
||||
|
||||
def test_get_dvr_port_binding_by_host_not_found(self):
|
||||
port = ml2_db.get_dvr_port_binding_by_host(
|
||||
self.ctx.session, 'foo_port_id', 'foo_host_id')
|
||||
|
@ -81,6 +81,7 @@ L2_AGENT_4 = {
|
||||
|
||||
PLUGIN_NAME = 'neutron.plugins.ml2.plugin.Ml2Plugin'
|
||||
NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi'
|
||||
DEVICE_OWNER_COMPUTE = 'compute:None'
|
||||
|
||||
|
||||
class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
@ -157,6 +158,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
with self.port(subnet=subnet,
|
||||
@ -178,6 +180,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -216,11 +219,13 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
host_arg = {portbindings.HOST_ID: HOST,
|
||||
'admin_state_up': True}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID, 'admin_state_up',),
|
||||
**host_arg) as port1:
|
||||
host_arg = {portbindings.HOST_ID: HOST + '_2',
|
||||
'admin_state_up': True}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,
|
||||
'admin_state_up',),
|
||||
**host_arg) as port2:
|
||||
@ -244,6 +249,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.2': [constants.FLOODING_ENTRY,
|
||||
[p2['mac_address'],
|
||||
p2['device_owner'],
|
||||
p2_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -265,6 +271,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -280,14 +287,17 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST + '_2'}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
with self.subnet(cidr='10.1.0.0/24') as subnet2:
|
||||
with self.port(subnet=subnet2,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg):
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port3:
|
||||
p1 = port1['port']
|
||||
@ -310,6 +320,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'20.0.0.2':
|
||||
[constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -334,6 +345,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'20.0.0.1':
|
||||
[constants.FLOODING_ENTRY,
|
||||
[p3['mac_address'],
|
||||
p3['device_owner'],
|
||||
p3_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -350,9 +362,11 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port2:
|
||||
p2 = port2['port']
|
||||
@ -380,6 +394,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{p2['network_id']:
|
||||
{'ports':
|
||||
{'20.0.0.1': [[p2['mac_address'],
|
||||
p2['device_owner'],
|
||||
p2_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -395,9 +410,11 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg):
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port2:
|
||||
p2 = port2['port']
|
||||
@ -419,6 +436,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p2['mac_address'],
|
||||
p2['device_owner'],
|
||||
p2_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -434,6 +452,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port:
|
||||
p1 = port['port']
|
||||
@ -445,6 +464,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
device=device)
|
||||
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port2:
|
||||
p2 = port2['port']
|
||||
@ -461,6 +481,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{p2['network_id']:
|
||||
{'ports':
|
||||
{'20.0.0.1': [[p2['mac_address'],
|
||||
p2['device_owner'],
|
||||
p2_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -476,9 +497,11 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg):
|
||||
with self.port(subnet=subnet,
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port:
|
||||
p1 = port['port']
|
||||
@ -496,6 +519,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -511,6 +535,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
p1 = port1['port']
|
||||
@ -536,6 +561,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{p1['network_id']:
|
||||
{'20.0.0.1':
|
||||
{'after': [[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
'10.0.0.10']]}}}}},
|
||||
'namespace': None,
|
||||
'method': 'update_fdb_entries'}
|
||||
@ -558,8 +584,10 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{p1['network_id']:
|
||||
{'20.0.0.1':
|
||||
{'before': [[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
'10.0.0.10']],
|
||||
'after': [[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
'10.0.0.16']]}}}}},
|
||||
'namespace': None,
|
||||
'method': 'update_fdb_entries'}
|
||||
@ -581,6 +609,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{p1['network_id']:
|
||||
{'20.0.0.1':
|
||||
{'before': [[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
'10.0.0.2']]}}}}},
|
||||
'namespace': None,
|
||||
'method': 'update_fdb_entries'}
|
||||
@ -594,6 +623,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
with self.subnet(network=self._network) as subnet:
|
||||
host_arg = {portbindings.HOST_ID: HOST}
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
p1 = port1['port']
|
||||
@ -623,9 +653,11 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
host_arg = {portbindings.HOST_ID: L2_AGENT['host']}
|
||||
host2_arg = {portbindings.HOST_ID: L2_AGENT_2['host']}
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host2_arg) as port2:
|
||||
p1 = port1['port']
|
||||
@ -658,6 +690,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
@ -673,9 +706,11 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
host_arg = {portbindings.HOST_ID: L2_AGENT['host']}
|
||||
host2_arg = {portbindings.HOST_ID: L2_AGENT_2['host']}
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host_arg) as port1:
|
||||
with self.port(subnet=subnet, cidr='10.0.0.0/24',
|
||||
device_owner=DEVICE_OWNER_COMPUTE,
|
||||
arg_list=(portbindings.HOST_ID,),
|
||||
**host2_arg) as port2:
|
||||
p1 = port1['port']
|
||||
@ -714,6 +749,7 @@ class TestL2PopulationRpcTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ports':
|
||||
{'20.0.0.1': [constants.FLOODING_ENTRY,
|
||||
[p1['mac_address'],
|
||||
p1['device_owner'],
|
||||
p1_ips[0]]]},
|
||||
'network_type': 'vxlan',
|
||||
'segment_id': 1}}},
|
||||
|
Loading…
Reference in New Issue
Block a user