@abc.abstractproperty
def host(self):
- """Return the host associated with the 'current' port."""
+ """Return the host with which the port is associated.
+
+ In the context of a host-specific operation on a distributed
+ port, the host property indicates the host for which the port
+ operation is being performed. Otherwise, it is the same value
+ as current['binding:host_id'].
+ """
pass
@abc.abstractproperty
def original_host(self):
- """Return the host associated with the 'original' port.
+ """Return the original host with which the port was associated.
+
+ In the context of a host-specific operation on a distributed
+ port, the original_host property indicates the host for which
+ the port operation is being performed. Otherwise, it is the
+ same value as original['binding:host_id'].
+
+ This property is only valid within calls to
+ update_port_precommit and update_port_postcommit. It returns
+ None otherwise.
+ """
+ pass
+
+ @abc.abstractproperty
+ def vif_type(self):
+ """Return the vif_type indicating the binding state of the port.
+
+ In the context of a host-specific operation on a distributed
+ port, the vif_type property indicates the binding state for
+ the host for which the port operation is being
+ performed. Otherwise, it is the same value as
+ current['binding:vif_type'].
+ """
+ pass
+
+ @abc.abstractproperty
+ def original_vif_type(self):
+ """Return the original vif_type of the port.
+
+ In the context of a host-specific operation on a distributed
+ port, the original_vif_type property indicates original
+ binding state for the host for which the port operation is
+ being performed. Otherwise, it is the same value as
+ original['binding:vif_type'].
- Method is only valid within calls to update_port_precommit
- and update_port_postcommit.
+ This property is only valid within calls to
+ update_port_precommit and update_port_postcommit. It returns
+ None otherwise.
+ """
+ pass
+
+ @abc.abstractproperty
+ def vif_details(self):
+ """Return the vif_details describing the binding of the port.
+
+ In the context of a host-specific operation on a distributed
+ port, the vif_details property describes the binding for the
+ host for which the port operation is being
+ performed. Otherwise, it is the same value as
+ current['binding:vif_details'].
+ """
+ pass
+
+ @abc.abstractproperty
+ def original_vif_details(self):
+ """Return the original vif_details of the port.
+
+ In the context of a host-specific operation on a distributed
+ port, the original_vif_details property describes the original
+ binding for the host for which the port operation is being
+ performed. Otherwise, it is the same value as
+ original['binding:vif_details'].
+
+ This property is only valid within calls to
+ update_port_precommit and update_port_postcommit. It returns
+ None otherwise.
"""
pass
self._new_bound_segment = None
self._next_segments_to_bind = None
if original_port:
+ self._original_vif_type = binding.vif_type
+ self._original_vif_details = self._plugin._get_vif_details(binding)
self._original_binding_levels = self._binding_levels
else:
+ self._original_vif_type = None
+ self._original_vif_details = None
self._original_binding_levels = None
self._new_port_status = None
@property
def original_status(self):
- return self._original_port['status']
+ # REVISIT(rkukura): Should return host-specific status for DVR
+ # ports. Fix as part of resolving bug 1367391.
+ if self._original_port:
+ return self._original_port['status']
@property
def network(self):
@property
def original_host(self):
- return self._original_port.get(portbindings.HOST_ID)
+ # REVISIT(rkukura): Eliminate special DVR case as part of
+ # resolving bug 1367391?
+ if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
+ return self._original_port and self._binding.host
+ else:
+ return (self._original_port and
+ self._original_port.get(portbindings.HOST_ID))
+
+ @property
+ def vif_type(self):
+ return self._binding.vif_type
+
+ @property
+ def original_vif_type(self):
+ return self._original_vif_type
+
+ @property
+ def vif_details(self):
+ return self._plugin._get_vif_details(self._binding)
+
+ @property
+ def original_vif_details(self):
+ return self._original_vif_details
@property
def segments_to_bind(self):
mech_context._clear_binding_levels()
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
- binding.vif_type = portbindings.VIF_TYPE_DISTRIBUTED
+ binding.vif_type = portbindings.VIF_TYPE_UNBOUND
binding.vif_details = ''
db.clear_binding_levels(session, port_id, original_host)
mech_context._clear_binding_levels()
return (cur_context, commit)
def _update_port_dict_binding(self, port, binding):
- port[portbindings.HOST_ID] = binding.host
port[portbindings.VNIC_TYPE] = binding.vnic_type
port[portbindings.PROFILE] = self._get_profile(binding)
- port[portbindings.VIF_TYPE] = binding.vif_type
- port[portbindings.VIF_DETAILS] = self._get_vif_details(binding)
+ if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
+ port[portbindings.HOST_ID] = ''
+ port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_DISTRIBUTED
+ port[portbindings.VIF_DETAILS] = {}
+ else:
+ port[portbindings.HOST_ID] = binding.host
+ port[portbindings.VIF_TYPE] = binding.vif_type
+ port[portbindings.VIF_DETAILS] = self._get_vif_details(binding)
def _get_vif_details(self, binding):
if binding.vif_details:
{'device': device,
'agent_id': agent_id,
'network_id': port['network_id'],
- 'vif_type': port[portbindings.VIF_TYPE]})
+ 'vif_type': port_context.vif_type})
return {'device': device}
if (not host or host == port_context.host):
def original_host(self):
return None
+ @property
+ def vif_type(self):
+ return portbindings.UNBOUND
+
+ @property
+ def original_vif_type(self):
+ return portbindings.UNBOUND
+
+ @property
+ def vif_details(self):
+ return None
+
+ @property
+ def original_vif_details(self):
+ return None
+
@property
def segments_to_bind(self):
return self._network_context.network_segments
network_context = context.network
LOG.info(_("%(method)s called with port settings %(current)s "
"(original settings %(original)s) "
+ "host %(host)s "
+ "(original host %(original_host)s) "
+ "vif type %(vif_type)s "
+ "(original vif type %(original_vif_type)s) "
+ "vif details %(vif_details)s "
+ "(original vif details %(original_vif_details)s) "
"binding levels %(levels)s "
"(original binding levels %(original_levels)s) "
"on network %(network)s "
{'method': method_name,
'current': context.current,
'original': context.original,
+ 'host': context.host,
+ 'original_host': context.original_host,
+ 'vif_type': context.vif_type,
+ 'original_vif_type': context.original_vif_type,
+ 'vif_details': context.vif_details,
+ 'original_vif_details': context.original_vif_details,
'levels': context.binding_levels,
'original_levels': context.original_binding_levels,
'network': network_context.current,
def _check_port_context(self, context, original_expected):
assert(isinstance(context, api.PortContext))
- assert(isinstance(context.current, dict))
- assert(context.current['id'] is not None)
- vif_type = context.current.get(portbindings.VIF_TYPE)
- assert(vif_type is not None)
+ self._check_port_info(context.current, context.host,
+ context.vif_type, context.vif_details)
- if vif_type in (portbindings.VIF_TYPE_UNBOUND,
- portbindings.VIF_TYPE_BINDING_FAILED):
+ if context.vif_type in (portbindings.VIF_TYPE_UNBOUND,
+ portbindings.VIF_TYPE_BINDING_FAILED):
if (context.segments_to_bind and
context.segments_to_bind[0][api.NETWORK_TYPE] == 'vlan'):
# Partially bound.
self._check_unbound(context.binding_levels,
context.top_bound_segment,
context.bottom_bound_segment)
- assert(context.current['id'] not in self.bound_ports)
+ assert((context.current['id'], context.host)
+ not in self.bound_ports)
else:
self._check_bound(context.binding_levels,
context.top_bound_segment,
context.bottom_bound_segment)
- assert(context.current['id'] in self.bound_ports)
+ assert((context.current['id'], context.host) in self.bound_ports)
if original_expected:
- assert(isinstance(context.original, dict))
+ self._check_port_info(context.original, context.original_host,
+ context.original_vif_type,
+ context.original_vif_details)
+
assert(context.current['id'] == context.original['id'])
- vif_type = context.original.get(portbindings.VIF_TYPE)
- assert(vif_type is not None)
- if vif_type in (portbindings.VIF_TYPE_UNBOUND,
- portbindings.VIF_TYPE_BINDING_FAILED):
+
+ if (context.original_vif_type in
+ (portbindings.VIF_TYPE_UNBOUND,
+ portbindings.VIF_TYPE_BINDING_FAILED)):
self._check_unbound(context.original_binding_levels,
context.original_top_bound_segment,
context.original_bottom_bound_segment)
context.original_bottom_bound_segment)
else:
assert(context.original is None)
+ assert(context.original_host is None)
+ assert(context.original_vif_type is None)
+ assert(context.original_vif_details is None)
+ assert(context.original_status is None)
self._check_unbound(context.original_binding_levels,
context.original_top_bound_segment,
context.original_bottom_bound_segment)
assert(isinstance(network_context, api.NetworkContext))
self._check_network_context(network_context, False)
+ def _check_port_info(self, port, host, vif_type, vif_details):
+ assert(isinstance(port, dict))
+ assert(port['id'] is not None)
+ assert(vif_type in (portbindings.VIF_TYPE_UNBOUND,
+ portbindings.VIF_TYPE_BINDING_FAILED,
+ portbindings.VIF_TYPE_DISTRIBUTED,
+ portbindings.VIF_TYPE_OVS,
+ portbindings.VIF_TYPE_BRIDGE))
+ if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
+ assert(port[portbindings.HOST_ID] == '')
+ assert(port[portbindings.VIF_TYPE] ==
+ portbindings.VIF_TYPE_DISTRIBUTED)
+ assert(port[portbindings.VIF_DETAILS] == {})
+ else:
+ assert(port[portbindings.HOST_ID] == host)
+ assert(port[portbindings.VIF_TYPE] !=
+ portbindings.VIF_TYPE_DISTRIBUTED)
+ assert(port[portbindings.VIF_TYPE] == vif_type)
+ assert(isinstance(vif_details, dict))
+ assert(port[portbindings.VIF_DETAILS] == vif_details)
+
def _check_unbound(self, levels, top_segment, bottom_segment):
assert(levels is None)
assert(top_segment is None)
def update_port_precommit(self, context):
if (context.original_top_bound_segment and
not context.top_bound_segment):
- self.bound_ports.remove(context.original['id'])
+ self.bound_ports.remove((context.original['id'],
+ context.original_host))
self._check_port_context(context, True)
def update_port_postcommit(self, context):
if host == "host-ovs-no_filter":
context.set_binding(segment_id, portbindings.VIF_TYPE_OVS,
{portbindings.CAP_PORT_FILTER: False})
- self.bound_ports.add(context.current['id'])
+ self.bound_ports.add((context.current['id'], host))
elif host == "host-bridge-filter":
context.set_binding(segment_id, portbindings.VIF_TYPE_BRIDGE,
{portbindings.CAP_PORT_FILTER: True})
- self.bound_ports.add(context.current['id'])
+ self.bound_ports.add((context.current['id'], host))
elif host == "host-ovs-filter-active":
context.set_binding(segment_id, portbindings.VIF_TYPE_OVS,
{portbindings.CAP_PORT_FILTER: True},
status=const.PORT_STATUS_ACTIVE)
- self.bound_ports.add(context.current['id'])
+ self.bound_ports.add((context.current['id'], host))
elif host == "host-hierarchical":
segment_type = segment[api.NETWORK_TYPE]
if segment_type == 'local':
context.set_binding(segment_id,
portbindings.VIF_TYPE_OVS,
{portbindings.CAP_PORT_FILTER: False})
- self.bound_ports.add(context.current['id'])
+ self.bound_ports.add((context.current['id'], host))
import mock
+from neutron.common import constants as const
from neutron import context
from neutron.extensions import portbindings
from neutron import manager
def test_update_from_host_to_empty_binding_notifies_agent(self):
self._test_update_port_binding('host-ovs-no_filter', '')
+
+ def test_dvr_binding(self):
+ ctx = context.get_admin_context()
+ with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
+ port_id = port['port']['id']
+
+ # Verify port's VIF type and status.
+ self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
+ port['port'][portbindings.VIF_TYPE])
+ self.assertEqual('DOWN', port['port']['status'])
+
+ # Update port to bind for a host.
+ self.plugin.update_dvr_port_binding(ctx, port_id, {'port': {
+ portbindings.HOST_ID: 'host-ovs-no_filter',
+ 'device_id': 'router1'}})
+
+ # Get port and verify VIF type and status unchanged.
+ port = self._show('ports', port_id)
+ self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
+ port['port'][portbindings.VIF_TYPE])
+ self.assertEqual('DOWN', port['port']['status'])
+
+ # Get and verify binding details for host
+ details = self.plugin.endpoints[0].get_device_details(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+ self.assertEqual('local', details['network_type'])
+
+ # Get port and verify VIF type and changed status.
+ port = self._show('ports', port_id)
+ self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
+ port['port'][portbindings.VIF_TYPE])
+ self.assertEqual('BUILD', port['port']['status'])
+
+ # Mark device up.
+ self.plugin.endpoints[0].update_device_up(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+
+ # Get port and verify VIF type and changed status.
+ port = self._show('ports', port_id)
+ self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
+ port['port'][portbindings.VIF_TYPE])
+ self.assertEqual('ACTIVE', port['port']['status'])
+
+ # Mark device down.
+ self.plugin.endpoints[0].update_device_down(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+
+ # Get port and verify VIF type and changed status.
+ port = self._show('ports', port_id)
+ self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
+ port['port'][portbindings.VIF_TYPE])
+ self.assertEqual('DOWN', port['port']['status'])
+
+ def test_dvr_binding_multi_host_status(self):
+ ctx = context.get_admin_context()
+ with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
+ port_id = port['port']['id']
+
+ # Update port to bind for 1st host.
+ self.plugin.update_dvr_port_binding(ctx, port_id, {'port': {
+ portbindings.HOST_ID: 'host-ovs-no_filter',
+ 'device_id': 'router1'}})
+
+ # Mark 1st device up.
+ self.plugin.endpoints[0].update_device_up(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+
+ # Get port and verify status is ACTIVE.
+ port = self._show('ports', port_id)
+ self.assertEqual('ACTIVE', port['port']['status'])
+
+ # Update port to bind for a 2nd host.
+ self.plugin.update_dvr_port_binding(ctx, port_id, {'port': {
+ portbindings.HOST_ID: 'host-bridge-filter',
+ 'device_id': 'router1'}})
+
+ # Mark 2nd device up.
+ self.plugin.endpoints[0].update_device_up(
+ ctx, agent_id="the2ndAgentId", device=port_id,
+ host='host-bridge-filter')
+
+ # Get port and verify status unchanged.
+ port = self._show('ports', port_id)
+ self.assertEqual('ACTIVE', port['port']['status'])
+
+ # Mark 1st device down.
+ self.plugin.endpoints[0].update_device_down(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+
+ # Get port and verify status unchanged.
+ port = self._show('ports', port_id)
+ self.assertEqual('ACTIVE', port['port']['status'])
+
+ # Mark 2nd device down.
+ self.plugin.endpoints[0].update_device_down(
+ ctx, agent_id="the2ndAgentId", device=port_id,
+ host='host-bridge-filter')
+
+ # Get port and verify status is DOWN.
+ port = self._show('ports', port_id)
+ self.assertEqual('DOWN', port['port']['status'])
+
+ def test_dvr_binding_update_unbound_host(self):
+ ctx = context.get_admin_context()
+ with self.port(device_owner=const.DEVICE_OWNER_DVR_INTERFACE) as port:
+ port_id = port['port']['id']
+
+ # Mark device up without first binding on host.
+ self.plugin.endpoints[0].update_device_up(
+ ctx, agent_id="theAgentId", device=port_id,
+ host='host-ovs-no_filter')
+
+ # Get port and verify status is still DOWN.
+ port = self._show('ports', port_id)
+ self.assertEqual('DOWN', port['port']['status'])