Add release note for blueprint vlan-aware-vms

Now that the bulk of the code is in place, and we are about
to see the finish line, it is time to prepare the release
notes for this feature.

This patch refines docstrings that recently went in and
makes sure the code is in sync with the agent's devref.

Implements: blueprint vlan-aware-vms

Change-Id: Idb9379046aeb07354a170a361da777140a2a028e
This commit is contained in:
Armando Migliaccio 2016-08-24 13:45:15 -07:00
parent eec6ebd425
commit e1b2ac1e9e
3 changed files with 107 additions and 66 deletions

View File

@ -467,7 +467,7 @@ Implementation Trunk Bridge (Option C)
This implementation is based on this `etherpad <https://etherpad.openstack.org/p/trunk-bridge-tagged-patch-experiment>`_.
Credits to Bence Romsics.
The option use_veth_interconnection=true won't be supported, it will probably be deprecated soon,
see [1].
see [1]. The IDs used for bridge and port names are truncated.
::
@ -479,24 +479,25 @@ see [1].
|
+-----+--------------------------+
| tap1 |
| br-trunk-1 |
| tbr-trunk-id |
| |
| tp-patch-trunk sp-patch-trunk |
| tpt-parent-id spt-subport-id |
| (tag 100) |
+-----+-----------------+--------+
| |
| |
| |
+-----+-----------------+---------+
| tp-patch-int sp-patch-int |
| tpi-parent-id spi-subport-id |
| (tag 3) (tag 5) |
| |
| br-int |
+---------------------------------+
tp-patch-trunk: trunk bridge side of the patch port that implements a trunk
tp-patch-int: int bridge side of the patch port that implements a trunk
sp-patch-trunk: trunk bridge side of the patch port that implements a subport
sp-patch-int: int bridge side of the patch port that implements a subport
tpt-parent-id: trunk bridge side of the patch port that implements a trunk.
tpi-parent-id: int bridge side of the patch port that implements a trunk.
spt-subport-id: trunk bridge side of the patch port that implements a subport.
spi-subport-id: int bridge side of the patch port that implements a subport.
[1] https://bugs.launchpad.net/neutron/+bug/1587296
@ -505,23 +506,23 @@ Trunk creation
A VM is spawned passing to Nova the port-id of a parent port associated with
a trunk. Neutron will pass to Nova the bridge where to plug the vif as part of the vif details.
The os-vif driver creates the trunk bridge br-trunk-1 if it does not exist in plug().
It will create the tap interface tap1 and plug it into br-trunk-1 setting the parent port ID in the external-ids.
The os-vif driver creates the trunk bridge tbr-trunk-id if it does not exist in plug().
It will create the tap interface tap1 and plug it into tbr-trunk-id setting the parent port ID in the external-ids.
The OVS agent will be monitoring the creation of ports on the trunk bridges. When it detects
that a new port has been created on the trunk bridge, it will do the following:
::
ovs-vsctl add-port br-trunk-1 tp-patch-trunk -- set Interface tp-patch-trunk type=patch options:peer=tp-patch-int
ovs-vsctl add-port br-int tp-patch-int tag=3 -- set Interface tp-patch-int type=patch options:peer=tp-patch-trunk
ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id
ovs-vsctl add-port br-int tpi-parent-id tag=3 -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id
A patch port is created to connect the trunk bridge to the integration bridge.
tp-patch-trunk, the trunk bridge side of the patch is not associated to any
tpt-parent-id, the trunk bridge side of the patch is not associated to any
tag. It will carry untagged traffic.
tp-patch-int, the br-int side the patch port is tagged with VLAN 3. We assume that the
tpi-parent-id, the br-int side the patch port is tagged with VLAN 3. We assume that the
trunk is on network1 that on this host is associated with VLAN 3.
The OVS agent will set the trunk ID in the external-ids of tp-patch-trunk and tp-patch-int.
The OVS agent will set the trunk ID in the external-ids of tpt-parent-id and tpi-parent-id.
If the parent port is associated with one or more subports the agent will process them as
described in the next paragraph.
@ -536,32 +537,32 @@ create a new patch port:
::
ovs-vsctl add-port br-trunk-1 sp-patch-trunk tag=100 -- set Interface sp-patch-trunk type=patch options:peer=sp-patch-int
ovs-vsctl add-port br-int sp-patch-int tag=5 -- set Interface sp-patch-int type=patch options:peer=sp-patch-trunk
ovs-vsctl add-port tbr-trunk-id spt-subport-id tag=100 -- set Interface spt-subport-id type=patch options:peer=spi-subport-id
ovs-vsctl add-port br-int spi-subport-id tag=5 -- set Interface spi-subport-id type=patch options:peer=spt-subport-id
This patch port connects the trunk bridge to the integration bridge.
sp-patch-trunk, the trunk bridge side of the patch is tagged using VLAN 100.
spt-subport-id, the trunk bridge side of the patch is tagged using VLAN 100.
We assume that the segmentation ID of the subport is 100.
sp-patch-int, the br-int side of the patch port is tagged with VLAN 5. We
spi-subport-id, the br-int side of the patch port is tagged with VLAN 5. We
assume that the subport is on network2 that on this host uses VLAN 5.
The OVS agent will set the subport ID in the external-ids of sp-patch-trunk and sp-patch-int.
The OVS agent will set the subport ID in the external-ids of spt-subport-id and spi-subport-id.
*Inbound traffic from the VM point of view*
The traffic coming out of tp-patch-int will be stripped by br-int of VLAN 3.
It will reach tp-patch-trunk untagged and from there tap1.
The traffic coming out of sp-patch-int will be stripped by br-int of VLAN 5.
It will reach sp-patch-trunk where it will be tagged with VLAN 100 and it will
The traffic coming out of tpi-parent-id will be stripped by br-int of VLAN 3.
It will reach tpt-parent-id untagged and from there tap1.
The traffic coming out of spi-subport-id will be stripped by br-int of VLAN 5.
It will reach spt-subport-id where it will be tagged with VLAN 100 and it will
then get to tap1 tagged.
*Outbound traffic from the VM point of view*
The untagged traffic coming from tap1 will reach tp-patch-trunk and from there
tp-patch-int where it will be tagged using VLAN 3.
The traffic tagged with VLAN 100 from tap1 will reach sp-patch-trunk.
VLAN 100 will be stripped since sp-patch-trunk is a tagged port and the packet
will reach sp-patch-int, where it's tagged using VLAN 5.
The untagged traffic coming from tap1 will reach tpt-parent-id and from there
tpi-parent-id where it will be tagged using VLAN 3.
The traffic tagged with VLAN 100 from tap1 will reach spt-subport-id.
VLAN 100 will be stripped since spt-subport-id is a tagged port and the packet
will reach spi-subport-id, where it's tagged using VLAN 5.
Parent port deletion
++++++++++++++++++++

View File

@ -59,7 +59,10 @@ def get_patch_peer_attrs(peer_name, port_mac=None, port_id=None):
class TrunkBridge(ovs_lib.OVSBridge):
"""An OVS trunk bridge.
A trunk bridge has a name that follows a specific naming convention.
"""
def __init__(self, trunk_id):
name = utils.gen_trunk_br_name(trunk_id)
super(TrunkBridge, self).__init__(name)
@ -69,6 +72,14 @@ class TrunkBridge(ovs_lib.OVSBridge):
class TrunkParentPort(object):
"""An OVS trunk parent port.
A trunk parent port is represented in OVS with two patch ports that
connect a trunk bridge and the integration bridge respectively.
These patch ports follow strict naming conventions: tpi-<hash> for
the patch port that goes into the integration bridge, and tpt-<hash>
for the patch port that goes into the trunk bridge.
"""
DEV_PREFIX = 'tp'
def __init__(self, trunk_id, port_id, port_mac=None):
@ -76,10 +87,8 @@ class TrunkParentPort(object):
self.port_id = port_id
self.port_mac = port_mac
self.bridge = TrunkBridge(self.trunk_id)
# The name has form of tpi-<hash>
self.patch_port_int_name = get_br_int_port_name(
self.DEV_PREFIX, port_id)
# The name has form of tpt-<hash>
self.patch_port_trunk_name = get_br_trunk_port_name(
self.DEV_PREFIX, port_id)
self._transaction = None
@ -104,20 +113,24 @@ class TrunkParentPort(object):
self._transaction = None
def plug(self, br_int):
"""Create patch ports between trunk bridge and given bridge.
"""Plug patch ports between trunk bridge and given bridge.
The method creates one patch port on the given bridge side using
port mac and id as external ids. The other endpoint of patch port is
The method plugs one patch port on the given bridge side using
port MAC and ID as external IDs. The other endpoint of patch port is
attached to the trunk bridge. Everything is done in a single
ovsdb transaction so either all operations succeed or fail.
OVSDB transaction so either all operations succeed or fail.
:param br_int: An integration bridge where peer endpoint of patch port
:param br_int: an integration bridge where peer endpoint of patch port
will be created.
"""
# NOTE(jlibosva): osvdb is an api so it doesn't matter whether we
# NOTE(jlibosva): OVSDB is an api so it doesn't matter whether we
# use self.bridge or br_int
ovsdb = self.bridge.ovsdb
# Once the bridges are connected with the following patch ports,
# the ovs agent will recognize the ports for processing and it will
# take over the wiring process and everything that entails.
# REVISIT(rossella_s): revisit this integration part, should tighter
# control over the wiring logic for trunk ports be required.
patch_int_attrs = get_patch_peer_attrs(
self.patch_port_trunk_name, self.port_mac, self.port_id)
patch_trunk_attrs = get_patch_peer_attrs(self.patch_port_int_name)
@ -135,10 +148,10 @@ class TrunkParentPort(object):
def unplug(self, bridge):
"""Unplug the trunk from bridge.
Method deletes in single ovsdb transaction the trunk bridge and patch
Method unplugs in single OVSDB transaction the trunk bridge and patch
port on provided bridge.
:param bridge: Bridge that has peer side of patch port for this
:param bridge: bridge that has peer side of patch port for this
subport.
"""
ovsdb = self.bridge.ovsdb
@ -149,7 +162,14 @@ class TrunkParentPort(object):
class SubPort(TrunkParentPort):
# Patch port names have form of spi-<hash> or spt-<hash> respectively.
"""An OVS trunk subport.
A subport is represented in OVS with two patch ports that
connect a trunk bridge and the integration bridge respectively.
These patch ports follow strict naming conventions: spi-<hash> for
the patch port that goes into the integration bridge, and spt-<hash>
for the patch port that goes into the trunk bridge.
"""
DEV_PREFIX = 'sp'
def __init__(self, trunk_id, port_id, port_mac=None, segmentation_id=None):
@ -157,17 +177,16 @@ class SubPort(TrunkParentPort):
self.segmentation_id = segmentation_id
def plug(self, br_int):
"""Create patch ports between trunk bridge and given bridge.
"""Unplug patch ports between trunk bridge and given bridge.
The method creates one patch port on the given bridge side using
port mac and id as external ids. The other endpoint of patch port is
The method unplugs one patch port on the given bridge side using
port MAC and ID as external IDs. The other endpoint of patch port is
attached to the trunk bridge. Then it sets vlan tag represented by
segmentation_id. Everything is done in a single ovsdb transaction so
segmentation_id. Everything is done in a single OVSDB transaction so
either all operations succeed or fail.
:param br_int: An integration bridge where peer endpoint of patch port
:param br_int: an integration bridge where peer endpoint of patch port
will be created.
"""
ovsdb = self.bridge.ovsdb
with self.ovsdb_transaction() as txn:
@ -179,10 +198,10 @@ class SubPort(TrunkParentPort):
def unplug(self, bridge):
"""Unplug the sub port from the bridge.
Method deletes in single ovsdb transaction both endpoints of patch
Method unplugs in single OVSDB transaction both endpoints of patch
ports that represents the subport.
:param bridge: Bridge that has peer side of patch port for this
:param bridge: bridge that has peer side of patch port for this
subport.
"""
ovsdb = self.bridge.ovsdb
@ -211,19 +230,13 @@ class TrunkManager(object):
:param trunk_id: ID of the trunk.
:param port_id: ID of the parent port.
:param port_mac: the MAC address of the parent port.
:raises: TrunkBridgeNotFound -- In case trunk bridge doesn't exist.
:raises:
TrunkBridgeNotFound: in case trunk bridge does not exist.
"""
trunk = TrunkParentPort(trunk_id, port_id, port_mac)
try:
if not trunk.bridge.exists():
raise exc.TrunkBridgeNotFound(bridge=trunk.bridge.br_name)
# Once the bridges are connected with the following patch ports,
# the ovs agent will recognize the ports for processing and it will
# take over the wiring process and everything that entails.
# REVISIT(rossella_s): revisit this integration part, should
# tighter control over the wiring logic for trunk ports be
# required.
trunk.plug(self.br_int)
except RuntimeError as e:
raise TrunkManagerError(error=e)
@ -235,18 +248,17 @@ class TrunkManager(object):
if trunk.bridge.exists():
trunk.unplug(self.br_int)
else:
LOG.debug("Trunk bridge with ID %s doesn't exist.", trunk_id)
LOG.debug("Trunk bridge with ID %s does not exist.", trunk_id)
except RuntimeError as e:
raise TrunkManagerError(error=e)
def add_sub_port(self, trunk_id, port_id, port_mac, segmentation_id):
"""Create a sub_port.
:param trunk_id: ID of the trunk
:param port_id: ID of the child port
:param segmentation_id: segmentation ID associated with this sub-port
:param port_mac: MAC address of the child port
:param trunk_id: ID of the trunk.
:param port_id: ID of the subport.
:param segmentation_id: segmentation ID associated with this subport.
:param port_mac: MAC address of the subport.
"""
sub_port = SubPort(trunk_id, port_id, port_mac, segmentation_id)
# If creating of parent trunk bridge takes longer than API call for
@ -261,8 +273,8 @@ class TrunkManager(object):
def remove_sub_port(self, trunk_id, port_id):
"""Remove a sub_port.
:param trunk_id: ID of the trunk
:param port_id: ID of the child port
:param trunk_id: ID of the trunk.
:param port_id: ID of the subport.
"""
sub_port = SubPort(trunk_id, port_id)
@ -272,7 +284,7 @@ class TrunkManager(object):
if sub_port.bridge.exists():
sub_port.unplug(self.br_int)
else:
LOG.debug("Trunk bridge with ID %s doesn't exist.", trunk_id)
LOG.debug("Trunk bridge with ID %s does not exist.", trunk_id)
except RuntimeError as e:
raise TrunkManagerError(error=e)

View File

@ -0,0 +1,28 @@
---
prelude: >
The "vlan-aware-vms" feature allows Nova users to launch VMs on a single
port (trunk parent port) that connects multiple Neutron logical networks
together.
features:
- The feature "vlan-aware-vms" is available. To enable it, a service plugin
named 'trunk' must be added to the option ``service_plugins`` in your
neutron.conf. The plugin exposes two new extensions ``trunk`` and
``trunk_details``. The plugin can work with multiple backends and in
particular Neutron has support for `ML2/openvswitch <http://docs.openstack.org/developer/neutron/devref/openvswitch_agent.html#tackling-the-network-trunking-use-case>`_
and ML2/linuxbridge.
Even though Neutron API compatibility should be preserved for ports
associated to trunks, since this is the first release where the feature
is available, it is reasonable to expect possible functionality gaps for
one or both drivers. These will be filled over time as being reported.
The CLI is available via openstackclient, and python-neutronclient 5.1.0
or above. For more details, please check the networking guide.
security:
- |
When working with the ML2/openvswitch driver, the "vlan-aware-vms" feature
has the following limitations:
* security groups do not work in conjunction with the iptables-based
firewall driver.
* if security groups are desired, the use of the stateful OVS firewall is
required, however that prevents the use of the DPDK datapath for OVS
versions 2.5 or lower.