Merge "fullstack: VLAN aware VMs test"
This commit is contained in:
commit
992c006285
|
@ -15,6 +15,7 @@
|
|||
import functools
|
||||
|
||||
import fixtures
|
||||
import netaddr
|
||||
from neutron_lib import constants
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
|
@ -462,13 +463,11 @@ class OVSTrunkConnectionTester(OVSBaseConnectionTester):
|
|||
|
||||
"""
|
||||
|
||||
ip_wrap = ip_lib.IPWrapper(self._vm.namespace)
|
||||
dev_name = self._vm.port.name + ".%d" % vlan
|
||||
ip_wrap.add_vlan(dev_name, self._vm.port.name, vlan)
|
||||
dev = ip_wrap.device(dev_name)
|
||||
dev.addr.add(ip_cidr)
|
||||
dev.link.set_up()
|
||||
self._ip_vlan = ip_cidr.partition('/')[0]
|
||||
network = netaddr.IPNetwork(ip_cidr)
|
||||
net_helpers.create_vlan_interface(
|
||||
self._vm.namespace, self._vm.port.name,
|
||||
self.vm_mac_address, network, vlan)
|
||||
self._ip_vlan = str(network.ip)
|
||||
ip_cidr = net_helpers.increment_ip_cidr(ip_cidr, 1)
|
||||
self._peer2 = self.useFixture(machine_fixtures.FakeMachine(
|
||||
self.bridge, ip_cidr))
|
||||
|
|
|
@ -70,6 +70,15 @@ class FakeMachineBase(fixtures.Fixture):
|
|||
"No ICMP reply obtained from IP address %s" % dst_ip)
|
||||
)
|
||||
|
||||
def block_until_no_ping(self, dst_ip):
|
||||
predicate = functools.partial(
|
||||
lambda ip: not self.ping_predicate(ip), dst_ip)
|
||||
utils.wait_until_true(
|
||||
predicate,
|
||||
exception=FakeMachineException(
|
||||
"ICMP packets still pass to %s IP address." % dst_ip)
|
||||
)
|
||||
|
||||
def assert_ping(self, dst_ip):
|
||||
net_helpers.assert_ping(self.namespace, dst_ip)
|
||||
|
||||
|
|
|
@ -235,6 +235,27 @@ def create_patch_ports(source, destination):
|
|||
destination.add_patch_port(destination_name, source_name)
|
||||
|
||||
|
||||
def create_vlan_interface(
|
||||
namespace, port_name, mac_address, ip_address, vlan_tag):
|
||||
"""Create a VLAN interface in namespace with IP address.
|
||||
|
||||
:param namespace: Namespace in which VLAN interface should be created.
|
||||
:param port_name: Name of the port to which VLAN should be added.
|
||||
:param ip_address: IPNetwork instance containing the VLAN interface IP
|
||||
address.
|
||||
:param vlan_tag: VLAN tag for VLAN interface.
|
||||
"""
|
||||
ip_wrap = ip_lib.IPWrapper(namespace)
|
||||
dev_name = "%s.%d" % (port_name, vlan_tag)
|
||||
ip_wrap.add_vlan(dev_name, port_name, vlan_tag)
|
||||
dev = ip_wrap.device(dev_name)
|
||||
dev.addr.add(str(ip_address))
|
||||
dev.link.set_address(mac_address)
|
||||
dev.link.set_up()
|
||||
|
||||
return dev
|
||||
|
||||
|
||||
class RootHelperProcess(subprocess.Popen):
|
||||
def __init__(self, cmd, *args, **kwargs):
|
||||
for arg in ('stdin', 'stdout', 'stderr'):
|
||||
|
|
|
@ -88,12 +88,15 @@ class ClientFixture(fixtures.Fixture):
|
|||
|
||||
return self._create_resource(resource_type, spec)
|
||||
|
||||
def create_port(self, tenant_id, network_id, hostname, qos_policy_id=None):
|
||||
def create_port(self, tenant_id, network_id, hostname=None,
|
||||
qos_policy_id=None, **kwargs):
|
||||
spec = {
|
||||
'network_id': network_id,
|
||||
'tenant_id': tenant_id,
|
||||
portbindings.HOST_ID: hostname,
|
||||
}
|
||||
spec.update(kwargs)
|
||||
if hostname is not None:
|
||||
spec[portbindings.HOST_ID] = hostname
|
||||
if qos_policy_id:
|
||||
spec['qos_policy_id'] = qos_policy_id
|
||||
return self._create_resource('port', spec)
|
||||
|
@ -170,3 +173,74 @@ class ClientFixture(fixtures.Fixture):
|
|||
qos_policy_id)
|
||||
|
||||
return rule['dscp_marking_rule']
|
||||
|
||||
def create_trunk(self, tenant_id, port_id, name=None,
|
||||
admin_state_up=None, sub_ports=None):
|
||||
"""Create a trunk via API.
|
||||
|
||||
:param tenant_id: ID of the tenant.
|
||||
:param port_id: Parent port of trunk.
|
||||
:param name: Name of the trunk.
|
||||
:param admin_state_up: Admin state of the trunk.
|
||||
:param sub_ports: List of subport dictionaries in format
|
||||
{'port_id': <ID of neutron port for subport>,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': <VLAN tag>}
|
||||
|
||||
:return: Dictionary with trunk's data returned from Neutron API.
|
||||
"""
|
||||
spec = {
|
||||
'port_id': port_id,
|
||||
'tenant_id': tenant_id,
|
||||
}
|
||||
if name is not None:
|
||||
spec['name'] = name
|
||||
if sub_ports is not None:
|
||||
spec['sub_ports'] = sub_ports
|
||||
if admin_state_up is not None:
|
||||
spec['admin_state_up'] = admin_state_up
|
||||
|
||||
trunk = self.client.create_trunk({'trunk': spec})['trunk']
|
||||
|
||||
if sub_ports:
|
||||
self.addCleanup(
|
||||
_safe_method(self.trunk_remove_subports),
|
||||
tenant_id, trunk['id'], trunk['sub_ports'])
|
||||
self.addCleanup(_safe_method(self.client.delete_trunk), trunk['id'])
|
||||
|
||||
return trunk
|
||||
|
||||
def trunk_add_subports(self, tenant_id, trunk_id, sub_ports):
|
||||
"""Add subports to the trunk.
|
||||
|
||||
:param tenant_id: ID of the tenant.
|
||||
:param trunk_id: ID of the trunk.
|
||||
:param sub_ports: List of subport dictionaries to be added in format
|
||||
{'port_id': <ID of neutron port for subport>,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': <VLAN tag>}
|
||||
"""
|
||||
spec = {
|
||||
'tenant_id': tenant_id,
|
||||
'sub_ports': sub_ports,
|
||||
}
|
||||
trunk = self.client.trunk_add_subports(trunk_id, spec)
|
||||
|
||||
sub_ports_to_remove = [
|
||||
sub_port for sub_port in trunk['sub_ports']
|
||||
if sub_port in sub_ports]
|
||||
self.addCleanup(
|
||||
_safe_method(self.trunk_remove_subports), tenant_id, trunk_id,
|
||||
sub_ports_to_remove)
|
||||
|
||||
def trunk_remove_subports(self, tenant_id, trunk_id, sub_ports):
|
||||
"""Remove subports from the trunk.
|
||||
|
||||
:param trunk_id: ID of the trunk.
|
||||
:param sub_ports: List of subport port IDs.
|
||||
"""
|
||||
spec = {
|
||||
'tenant_id': tenant_id,
|
||||
'sub_ports': sub_ports,
|
||||
}
|
||||
return self.client.trunk_remove_subports(trunk_id, spec)
|
||||
|
|
|
@ -54,7 +54,7 @@ class NeutronConfigFixture(ConfigFixture):
|
|||
super(NeutronConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir, base_filename='neutron.conf')
|
||||
|
||||
service_plugins = ['router']
|
||||
service_plugins = ['router', 'trunk']
|
||||
if env_desc.qos:
|
||||
service_plugins.append('qos')
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import netaddr
|
|||
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import utils
|
||||
from neutron.extensions import portbindings as pbs
|
||||
from neutron.tests.common import machine_fixtures
|
||||
|
@ -42,18 +43,19 @@ class FakeFullstackMachinesList(list):
|
|||
class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
||||
|
||||
def __init__(self, host, network_id, tenant_id, safe_client,
|
||||
neutron_port=None):
|
||||
neutron_port=None, bridge_name=None):
|
||||
super(FakeFullstackMachine, self).__init__()
|
||||
self.host = host
|
||||
self.tenant_id = tenant_id
|
||||
self.network_id = network_id
|
||||
self.safe_client = safe_client
|
||||
self.neutron_port = neutron_port
|
||||
self.bridge_name = bridge_name
|
||||
|
||||
def _setUp(self):
|
||||
super(FakeFullstackMachine, self)._setUp()
|
||||
|
||||
self.bridge = self.host.get_bridge(self.network_id)
|
||||
self.bridge = self._get_bridge()
|
||||
|
||||
if not self.neutron_port:
|
||||
self.neutron_port = self.safe_client.create_port(
|
||||
|
@ -64,6 +66,7 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
|||
hybrid_plug = self.neutron_port[pbs.VIF_DETAILS].get(
|
||||
pbs.OVS_HYBRID_PLUG, False)
|
||||
|
||||
self.bind_port_if_needed()
|
||||
self.port = self.useFixture(
|
||||
net_helpers.PortFixture.get(
|
||||
self.bridge, self.namespace, mac_address,
|
||||
|
@ -72,6 +75,28 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
|||
for fixed_ip in self.neutron_port['fixed_ips']:
|
||||
self._configure_ipaddress(fixed_ip)
|
||||
|
||||
def bind_port_if_needed(self):
|
||||
if self.neutron_port[pbs.VIF_TYPE] == pbs.VIF_TYPE_UNBOUND:
|
||||
self.safe_client.client.update_port(
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: self.host.hostname}})
|
||||
self.addCleanup(self.safe_client.client.update_port,
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: ''}})
|
||||
|
||||
def _get_bridge(self):
|
||||
if self.bridge_name is None:
|
||||
return self.host.get_bridge(self.network_id)
|
||||
agent_type = self.host.host_desc.l2_agent_type
|
||||
if agent_type == constants.AGENT_TYPE_OVS:
|
||||
new_bridge = self.useFixture(
|
||||
net_helpers.OVSTrunkBridgeFixture(self.bridge_name)).bridge
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Support for %s agent is not implemented." % agent_type)
|
||||
|
||||
return new_bridge
|
||||
|
||||
def _configure_ipaddress(self, fixed_ip):
|
||||
if (netaddr.IPAddress(fixed_ip['ip_address']).version ==
|
||||
constants.IP_VERSION_6):
|
||||
|
@ -108,3 +133,36 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
|||
lambda: (self.safe_client.client.show_port(self.neutron_port['id'])
|
||||
['port']['status'] == 'ACTIVE'),
|
||||
sleep=3)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy this fake machine.
|
||||
|
||||
This should simulate deletion of a vm. It doesn't call cleanUp().
|
||||
"""
|
||||
self.safe_client.client.update_port(
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: ''}}
|
||||
)
|
||||
# All associated vlan interfaces are deleted too
|
||||
self.bridge.delete_port(self.port.name)
|
||||
|
||||
ip_wrap = ip_lib.IPWrapper(self.namespace)
|
||||
ip_wrap.netns.delete(self.namespace)
|
||||
|
||||
|
||||
class FakeFullstackTrunkMachine(FakeFullstackMachine):
|
||||
def __init__(self, trunk, *args, **kwargs):
|
||||
super(FakeFullstackTrunkMachine, self).__init__(*args, **kwargs)
|
||||
self.trunk = trunk
|
||||
|
||||
def add_vlan_interface(self, mac_address, ip_address, segmentation_id):
|
||||
"""Add VLAN interface to VM's namespace.
|
||||
|
||||
:param mac_address: MAC address to be set on VLAN interface.
|
||||
:param ip_address: The IPNetwork instance containing IP address
|
||||
assigned to the interface.
|
||||
:param segmentation_id: VLAN tag added to the interface.
|
||||
"""
|
||||
net_helpers.create_vlan_interface(
|
||||
self.namespace, self.port.name, mac_address, ip_address,
|
||||
segmentation_id)
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
|
||||
import eventlet
|
||||
import netaddr
|
||||
from neutron_lib import constants
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.services.trunk.drivers.openvswitch.agent import ovsdb_handler
|
||||
from neutron.services.trunk.drivers.openvswitch.agent import trunk_manager
|
||||
from neutron.services.trunk.drivers.openvswitch import utils as trunk_ovs_utils
|
||||
from neutron.tests.fullstack import base
|
||||
from neutron.tests.fullstack.resources import environment
|
||||
from neutron.tests.fullstack.resources import machine
|
||||
|
||||
|
||||
def trunk_bridge_does_not_exist(trunk_id):
|
||||
"""Return true if trunk bridge for given ID does not exists."""
|
||||
bridge = trunk_manager.TrunkBridge(trunk_id)
|
||||
return not bridge.exists()
|
||||
|
||||
|
||||
def make_ip_network(port, network):
|
||||
"""Make an IPNetwork object from port and network.
|
||||
|
||||
Function returns IPNetwork object containing fixed IP address from port
|
||||
dictionary with prefixlen from network object.
|
||||
|
||||
:param port: Port dictionary returned by Neutron API
|
||||
:param network: IPNetwork object in which the port's IP will be assigned.
|
||||
"""
|
||||
ip_address = netaddr.IPAddress(
|
||||
port['fixed_ips'][0]['ip_address'])
|
||||
return netaddr.IPNetwork(
|
||||
(ip_address.value, network.prefixlen))
|
||||
|
||||
|
||||
class TrunkTestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Network(object):
|
||||
"""A helper class to keep persistent info about assigned addresses."""
|
||||
def __init__(self, prefix, network_cidr, tag=None):
|
||||
self.prefix = prefix
|
||||
self.network = netaddr.IPNetwork(network_cidr)
|
||||
self.neutron_network = None
|
||||
self.neutron_subnet = None
|
||||
self.tag = tag
|
||||
# Currently, only vlan is supported. Pass via __init__ once more are
|
||||
# supported.
|
||||
self.segmentation_type = 'vlan'
|
||||
|
||||
@property
|
||||
def cidr(self):
|
||||
return str(self.network.cidr)
|
||||
|
||||
@property
|
||||
def gateway(self):
|
||||
"""Return lowest possible IP in the given subnet."""
|
||||
return str(netaddr.IPAddress(self.network.first + 1))
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.neutron_network['id']
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return "%s-network" % self.prefix
|
||||
|
||||
@property
|
||||
def subnet_name(self):
|
||||
return "%s-subnet" % self.prefix
|
||||
|
||||
|
||||
class TestTrunkPlugin(base.BaseFullStackTestCase):
|
||||
def setUp(self):
|
||||
host_desc = [environment.HostDescription(
|
||||
l3_agent=False,
|
||||
l2_agent_type=constants.AGENT_TYPE_OVS)]
|
||||
env_desc = environment.EnvironmentDescription()
|
||||
env = environment.Environment(env_desc, host_desc)
|
||||
super(TestTrunkPlugin, self).setUp(env)
|
||||
|
||||
self.tenant_id = uuidutils.generate_uuid()
|
||||
self.trunk_network = Network('trunk', '10.0.0.0/24')
|
||||
self.vlan1_network = Network('vlan1', '192.168.0.0/24', tag=10)
|
||||
self.vlan2_network = Network('vlan2', '192.168.1.0/24', tag=20)
|
||||
|
||||
self.host = self.environment.hosts[0]
|
||||
|
||||
for network in (
|
||||
self.trunk_network, self.vlan1_network, self.vlan2_network):
|
||||
self.create_network_and_subnet(network)
|
||||
|
||||
def create_network_and_subnet(self, network):
|
||||
"""Create network and subnet resources in Neutron based on network
|
||||
object.
|
||||
|
||||
The resource names will be <prefix>-network and <prefix>-subnet, where
|
||||
prefix is taken from network object.
|
||||
|
||||
:param network: Network object from this module.
|
||||
"""
|
||||
network.neutron_network = self.safe_client.create_network(
|
||||
self.tenant_id, network.name)
|
||||
network.neutron_subnet = self.safe_client.create_subnet(
|
||||
self.tenant_id,
|
||||
network.id,
|
||||
cidr=network.cidr,
|
||||
gateway_ip=network.gateway,
|
||||
name=network.subnet_name,
|
||||
enable_dhcp=False)
|
||||
|
||||
def create_vlan_aware_vm(self, trunk_network, vlan_networks):
|
||||
"""Create a fake machine with one untagged port and subports
|
||||
according vlan_networks parameter.
|
||||
|
||||
:param trunk_network: Instance of Network where trunk port should be
|
||||
created.
|
||||
:param vlan_networks: List of Network instances where subports should
|
||||
be created.
|
||||
"""
|
||||
trunk_parent_port = self.safe_client.create_port(
|
||||
self.tenant_id, trunk_network.id)
|
||||
|
||||
vlan_subports = [
|
||||
self.safe_client.create_port(self.tenant_id, vlan_network.id,
|
||||
mac_address=trunk_parent_port['mac_address'])
|
||||
for vlan_network in vlan_networks]
|
||||
|
||||
trunk = self.safe_client.create_trunk(
|
||||
self.tenant_id,
|
||||
name='mytrunk',
|
||||
port_id=trunk_parent_port['id'],
|
||||
sub_ports=[
|
||||
{'port_id': vlan_subport['id'],
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': vlan_network.tag}
|
||||
for vlan_subport, vlan_network in zip(vlan_subports,
|
||||
vlan_networks)
|
||||
],
|
||||
)
|
||||
|
||||
vm = self.useFixture(
|
||||
machine.FakeFullstackTrunkMachine(
|
||||
trunk,
|
||||
self.host,
|
||||
trunk_network.id,
|
||||
self.tenant_id,
|
||||
self.safe_client,
|
||||
neutron_port=trunk_parent_port,
|
||||
bridge_name=trunk_ovs_utils.gen_trunk_br_name(trunk['id'])))
|
||||
|
||||
for port, vlan_network in zip(vlan_subports, vlan_networks):
|
||||
ip_network = make_ip_network(port, vlan_network.network)
|
||||
vm.add_vlan_interface(
|
||||
port['mac_address'], ip_network, vlan_network.tag)
|
||||
vm.block_until_boot()
|
||||
|
||||
return vm
|
||||
|
||||
def create_vm_in_network(self, network):
|
||||
"""Create a fake machine in given network."""
|
||||
return self.useFixture(
|
||||
machine.FakeFullstackMachine(
|
||||
self.host,
|
||||
network.id,
|
||||
self.tenant_id,
|
||||
self.safe_client
|
||||
)
|
||||
)
|
||||
|
||||
def add_subport_to_vm(self, vm, subport_network):
|
||||
"""Add subport from subport_network to given vm.
|
||||
|
||||
:param vm: FakeFullstackMachine instance to with subport should be
|
||||
added.
|
||||
:param subport_network: Network object representing network containing
|
||||
port for subport.
|
||||
"""
|
||||
subport = self.safe_client.create_port(
|
||||
self.tenant_id, subport_network.id,
|
||||
mac_address=vm.neutron_port['mac_address'])
|
||||
subport_spec = {
|
||||
'port_id': subport['id'],
|
||||
'segmentation_type': subport_network.segmentation_type,
|
||||
'segmentation_id': subport_network.tag
|
||||
}
|
||||
|
||||
self.safe_client.trunk_add_subports(
|
||||
self.tenant_id, vm.trunk['id'], [subport_spec])
|
||||
ip_network = make_ip_network(subport, subport_network.network)
|
||||
vm.add_vlan_interface(
|
||||
subport['mac_address'], ip_network, subport_network.tag)
|
||||
|
||||
def test_trunk_lifecycle(self):
|
||||
"""Test life-cycle of a fake VM with trunk port.
|
||||
|
||||
This test uses 4 fake machines:
|
||||
- vlan_aware_vm (A) that is at the beginning connected to a trunk
|
||||
network and a vlan1 network.
|
||||
- trunk_network_vm (B) that is connected to the trunk network.
|
||||
- vlan1_network_vm (C) that is connected to the vlan1 network.
|
||||
- vlan2_network_vm (D) that is connected to a vlan2 network.
|
||||
|
||||
Scenario steps:
|
||||
- all the vms from above are created
|
||||
- A can talk with B (over the trunk network)
|
||||
- A can talk with C (over the vlan1 network)
|
||||
- A can not talk with D (no leg on the vlan2 network)
|
||||
|
||||
- subport from the vlan2 network is added to A
|
||||
- A can now talk with D (over the vlan2 network)
|
||||
|
||||
- subport from the vlan1 network is removed from A
|
||||
- A can talk with B (over the trunk network)
|
||||
- A can not talk with C (no leg on the vlan1 network)
|
||||
- A can talk with D (over the vlan2 network)
|
||||
|
||||
- A is deleted which leads to removal of trunk bridge
|
||||
- no leftovers like patch ports to the trunk bridge should remain on
|
||||
an integration bridge
|
||||
"""
|
||||
|
||||
vlan_aware_vm = self.create_vlan_aware_vm(
|
||||
self.trunk_network,
|
||||
[self.vlan1_network]
|
||||
)
|
||||
trunk_id = vlan_aware_vm.trunk['id']
|
||||
|
||||
# Create helper vms with different networks
|
||||
trunk_network_vm = self.create_vm_in_network(self.trunk_network)
|
||||
vlan1_network_vm = self.create_vm_in_network(self.vlan1_network)
|
||||
vlan2_network_vm = self.create_vm_in_network(self.vlan2_network)
|
||||
|
||||
for vm in trunk_network_vm, vlan1_network_vm, vlan2_network_vm:
|
||||
vm.block_until_boot()
|
||||
|
||||
# Test connectivity to trunk and subport
|
||||
vlan_aware_vm.block_until_ping(trunk_network_vm.ip)
|
||||
vlan_aware_vm.block_until_ping(vlan1_network_vm.ip)
|
||||
|
||||
# Subport for vlan2 hasn't been added yet
|
||||
vlan_aware_vm.block_until_no_ping(vlan2_network_vm.ip)
|
||||
|
||||
# Add another subport and test
|
||||
self.add_subport_to_vm(vlan_aware_vm, self.vlan2_network)
|
||||
vlan_aware_vm.block_until_ping(vlan2_network_vm.ip)
|
||||
|
||||
# Remove the first subport
|
||||
self.safe_client.trunk_remove_subports(
|
||||
self.tenant_id,
|
||||
trunk_id,
|
||||
[vlan_aware_vm.trunk['sub_ports'][0]])
|
||||
|
||||
# vlan1_network_vm now shouldn't be able to talk to vlan_aware_vm
|
||||
vlan_aware_vm.block_until_no_ping(vlan1_network_vm.ip)
|
||||
|
||||
# but trunk and vlan2 should be able to ping
|
||||
vlan_aware_vm.block_until_ping(trunk_network_vm.ip)
|
||||
vlan_aware_vm.block_until_ping(vlan2_network_vm.ip)
|
||||
|
||||
# Delete vm and check that patch ports and trunk bridge are gone
|
||||
vlan_aware_vm.destroy()
|
||||
bridge_doesnt_exist_predicate = functools.partial(
|
||||
trunk_bridge_does_not_exist, trunk_id)
|
||||
utils.wait_until_true(
|
||||
bridge_doesnt_exist_predicate,
|
||||
exception=TrunkTestException(
|
||||
'Trunk bridge with ID %s has not been removed' %
|
||||
trunk_id)
|
||||
)
|
||||
|
||||
integration_bridge = self.host.get_bridge(None)
|
||||
no_patch_ports_predicate = functools.partial(
|
||||
lambda bridge: not ovsdb_handler.bridge_has_service_port(bridge),
|
||||
integration_bridge,
|
||||
)
|
||||
try:
|
||||
utils.wait_until_true(no_patch_ports_predicate)
|
||||
except eventlet.TimeoutError:
|
||||
raise TrunkTestException(
|
||||
"Integration bridge %s still has following ports while some of"
|
||||
" them are patch ports for trunk that were supposed to be "
|
||||
"removed: %s" % (
|
||||
integration_bridge.br_name,
|
||||
integration_bridge.get_iface_name_list()
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue