Merge "Add release note for blueprint vlan-aware-vms"
This commit is contained in:
commit
1a51051836
@ -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
|
||||
++++++++++++++++++++
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user