From e1b2ac1e9ebb48e127c3893ef870c1d5c9a22266 Mon Sep 17 00:00:00 2001 From: Armando Migliaccio Date: Wed, 24 Aug 2016 13:45:15 -0700 Subject: [PATCH] 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 --- doc/source/devref/openvswitch_agent.rst | 59 ++++++------- .../openvswitch/agent/trunk_manager.py | 86 +++++++++++-------- ...-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml | 28 ++++++ 3 files changed, 107 insertions(+), 66 deletions(-) create mode 100644 releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml diff --git a/doc/source/devref/openvswitch_agent.rst b/doc/source/devref/openvswitch_agent.rst index 2d709a18ff1..9c85ed569b3 100644 --- a/doc/source/devref/openvswitch_agent.rst +++ b/doc/source/devref/openvswitch_agent.rst @@ -467,7 +467,7 @@ Implementation Trunk Bridge (Option C) This implementation is based on this `etherpad `_. 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 ++++++++++++++++++++ diff --git a/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py b/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py index a7dc779bb0f..11d36517387 100644 --- a/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py +++ b/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py @@ -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- for + the patch port that goes into the integration bridge, and tpt- + 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- self.patch_port_int_name = get_br_int_port_name( self.DEV_PREFIX, port_id) - # The name has form of tpt- 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- or spt- 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- for + the patch port that goes into the integration bridge, and spt- + 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) diff --git a/releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml b/releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml new file mode 100644 index 00000000000..5f3ab6c6adb --- /dev/null +++ b/releasenotes/notes/vlan-aware-vms-aka-trunk-3341cc75ba1bf5b4.yaml @@ -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 `_ + 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.