Merge "Add scenario test for trunk E2E flow"
This commit is contained in:
commit
d3d09d2600
17
.zuul.yaml
17
.zuul.yaml
@ -167,6 +167,7 @@
|
||||
$TEMPEST_CONFIG:
|
||||
neutron_plugin_options:
|
||||
available_type_drivers: flat,vlan,local,vxlan
|
||||
q_agent: linuxbridge
|
||||
|
||||
- job:
|
||||
name: neutron-tempest-plugin-scenario-linuxbridge-queens
|
||||
@ -177,6 +178,14 @@
|
||||
devstack_localrc:
|
||||
USE_PYTHON3: false
|
||||
NETWORK_API_EXTENSIONS: address-scope,agent,allowed-address-pairs,auto-allocated-topology,availability_zone,binding,default-subnetpools,dhcp_agent_scheduler,dns-integration,ext-gw-mode,external-net,extra_dhcp_opt,extraroute,flavors,ip-substring-filtering,l3-flavors,l3-ha,l3_agent_scheduler,logging,metering,multi-provider,net-mtu,net-mtu-writable,network-ip-availability,network_availability_zone,pagination,port-security,project-id,provider,qos,qos-fip,quotas,quota_details,rbac-policies,router,router_availability_zone,security-group,port-security-groups-filtering,segment,service-type,sorting,standard-attr-description,standard-attr-revisions,standard-attr-timestamp,standard-attr-tag,subnet_allocation,tag,tag-ext,trunk,trunk-details
|
||||
devstack_local_conf:
|
||||
test-config:
|
||||
# NOTE: ignores linux bridge's trunk delete on bound port test
|
||||
# for queens branch (as https://review.openstack.org/#/c/605589/
|
||||
# fix will not apply for queens branch)
|
||||
$TEMPEST_CONFIG:
|
||||
neutron_plugin_options:
|
||||
q_agent: None
|
||||
|
||||
- job:
|
||||
name: neutron-tempest-plugin-scenario-linuxbridge-rocky
|
||||
@ -187,6 +196,14 @@
|
||||
devstack_localrc:
|
||||
USE_PYTHON3: false
|
||||
NETWORK_API_EXTENSIONS: address-scope,agent,allowed-address-pairs,auto-allocated-topology,availability_zone,binding,default-subnetpools,dhcp_agent_scheduler,dns-domain-ports,dns-integration,ext-gw-mode,external-net,extra_dhcp_opt,extraroute,fip-port-details,flavors,ip-substring-filtering,l3-flavors,l3-ha,l3_agent_scheduler,logging,metering,multi-provider,net-mtu,net-mtu-writable,network-ip-availability,network_availability_zone,pagination,port-security,project-id,provider,qos,qos-fip,quotas,quota_details,rbac-policies,router,router_availability_zone,security-group,port-security-groups-filtering,segment,service-type,sorting,standard-attr-description,standard-attr-revisions,standard-attr-timestamp,standard-attr-tag,subnet_allocation,tag,tag-ext,trunk,trunk-details
|
||||
devstack_local_conf:
|
||||
test-config:
|
||||
# NOTE: ignores linux bridge's trunk delete on bound port test
|
||||
# for rocky branch (as https://review.openstack.org/#/c/605589/
|
||||
# fix will not apply for rocky branch)
|
||||
$TEMPEST_CONFIG:
|
||||
neutron_plugin_options:
|
||||
q_agent: None
|
||||
|
||||
- job:
|
||||
name: neutron-tempest-plugin-dvr-multinode-scenario
|
||||
|
@ -830,7 +830,7 @@ class BaseNetworkTest(test.BaseTestCase):
|
||||
return trunk
|
||||
|
||||
@classmethod
|
||||
def delete_trunk(cls, trunk, client=None):
|
||||
def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
|
||||
"""Delete network trunk
|
||||
|
||||
:param trunk: dictionary containing trunk ID (trunk['id'])
|
||||
@ -856,7 +856,7 @@ class BaseNetworkTest(test.BaseTestCase):
|
||||
parent_port.update(client.show_port(parent_port['id'])['port'])
|
||||
return not parent_port['device_id']
|
||||
|
||||
if not is_parent_port_detached():
|
||||
if detach_parent_port and not is_parent_port_detached():
|
||||
# this could probably happen when trunk is deleted and parent port
|
||||
# has been assigned to a VM that is still running. Here we are
|
||||
# assuming that device_id points to such VM.
|
||||
|
@ -53,6 +53,10 @@ NeutronPluginOptions = [
|
||||
'"mtu":<MTU> - integer '
|
||||
'"cidr"<SUBNET/MASK> - string '
|
||||
'"provider:segmentation_id":<VLAN_ID> - integer'),
|
||||
cfg.StrOpt('q_agent',
|
||||
default=None,
|
||||
choices=['None', 'linuxbridge', 'ovs', 'sriov'],
|
||||
help='Agent used for devstack@q-agt.service'),
|
||||
|
||||
# Option for feature to connect via SSH to VMs using an intermediate SSH
|
||||
# server
|
||||
|
@ -47,8 +47,8 @@ class TrunkTest(base.BaseTempestTestCase):
|
||||
# setup basic topology for servers we can log into
|
||||
cls.network = cls.create_network()
|
||||
cls.subnet = cls.create_subnet(cls.network)
|
||||
router = cls.create_router_by_client()
|
||||
cls.create_router_interface(router['id'], cls.subnet['id'])
|
||||
cls.router = cls.create_router_by_client()
|
||||
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
|
||||
cls.keypair = cls.create_keypair()
|
||||
cls.secgroup = cls.os_primary.network_client.create_security_group(
|
||||
name=data_utils.rand_name('secgroup'))
|
||||
@ -95,6 +95,27 @@ class TrunkTest(base.BaseTempestTestCase):
|
||||
t = self.client.show_trunk(trunk_id)['trunk']
|
||||
return t['status'] == 'ACTIVE'
|
||||
|
||||
def _create_server_with_network(self, network, use_advanced_image=False):
|
||||
port = self.create_port(network, security_groups=[
|
||||
self.secgroup['security_group']['id']])
|
||||
server, fip = self._create_server_with_fip(
|
||||
port['id'], use_advanced_image=use_advanced_image)
|
||||
ssh_user = CONF.validation.image_ssh_user
|
||||
if use_advanced_image:
|
||||
ssh_user = CONF.neutron_plugin_options.advanced_image_ssh_user
|
||||
|
||||
server_ssh_client = ssh.Client(
|
||||
fip['floating_ip_address'],
|
||||
ssh_user,
|
||||
pkey=self.keypair['private_key'])
|
||||
|
||||
return {
|
||||
'server': server,
|
||||
'fip': fip,
|
||||
'ssh_client': server_ssh_client,
|
||||
'port': port,
|
||||
}
|
||||
|
||||
def _create_server_with_port_and_subport(self, vlan_network, vlan_tag,
|
||||
use_advanced_image=False):
|
||||
parent_port = self.create_port(self.network, security_groups=[
|
||||
@ -107,7 +128,7 @@ class TrunkTest(base.BaseTempestTestCase):
|
||||
'port_id': port_for_subport['id'],
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': vlan_tag}
|
||||
self.create_trunk(parent_port, [subport])
|
||||
trunk = self.create_trunk(parent_port, [subport])
|
||||
|
||||
server, fip = self._create_server_with_fip(
|
||||
parent_port['id'], use_advanced_image=use_advanced_image)
|
||||
@ -126,6 +147,8 @@ class TrunkTest(base.BaseTempestTestCase):
|
||||
'fip': fip,
|
||||
'ssh_client': server_ssh_client,
|
||||
'subport': port_for_subport,
|
||||
'parentport': parent_port,
|
||||
'trunk': trunk,
|
||||
}
|
||||
|
||||
def _wait_for_server(self, server, advanced_image=False):
|
||||
@ -260,3 +283,78 @@ class TrunkTest(base.BaseTempestTestCase):
|
||||
servers[1]['subport']['fixed_ips'][0]['ip_address'],
|
||||
should_succeed=True
|
||||
)
|
||||
|
||||
@testtools.skipUnless(
|
||||
CONF.neutron_plugin_options.advanced_image_ref,
|
||||
"Advanced image is required to run this test.")
|
||||
@testtools.skipUnless(
|
||||
CONF.neutron_plugin_options.q_agent == "linuxbridge",
|
||||
"Linux bridge agent is required to run this test.")
|
||||
@decorators.idempotent_id('d61cbdf6-1896-491c-b4b4-871caf7fbffe')
|
||||
def test_parent_port_connectivity_after_trunk_deleted_lb(self):
|
||||
vlan_tag = 10
|
||||
|
||||
vlan_network = self.create_network()
|
||||
vlan_subnet = self.create_subnet(vlan_network)
|
||||
self.create_router_interface(self.router['id'], vlan_subnet['id'])
|
||||
|
||||
trunk_network_server = self._create_server_with_port_and_subport(
|
||||
vlan_network, vlan_tag, use_advanced_image=True)
|
||||
normal_network_server = self._create_server_with_network(self.network)
|
||||
vlan_network_server = self._create_server_with_network(vlan_network)
|
||||
|
||||
self._wait_for_server(trunk_network_server, advanced_image=True)
|
||||
# Configure VLAN interfaces on server
|
||||
command = CONFIGURE_VLAN_INTERFACE_COMMANDS % {'tag': vlan_tag}
|
||||
trunk_network_server['ssh_client'].exec_command(command)
|
||||
out = trunk_network_server['ssh_client'].exec_command(
|
||||
'PATH=$PATH:/usr/sbin;ip addr list')
|
||||
LOG.debug("Interfaces on server %s: %s", trunk_network_server, out)
|
||||
|
||||
self._wait_for_server(normal_network_server)
|
||||
self._wait_for_server(vlan_network_server)
|
||||
|
||||
# allow intra-securitygroup traffic
|
||||
rule = self.client.create_security_group_rule(
|
||||
security_group_id=self.secgroup['security_group']['id'],
|
||||
direction='ingress', ethertype='IPv4', protocol='icmp',
|
||||
remote_group_id=self.secgroup['security_group']['id'])
|
||||
self.addCleanup(self.client.delete_security_group_rule,
|
||||
rule['security_group_rule']['id'])
|
||||
|
||||
# Ping from trunk_network_server to normal_network_server
|
||||
# via parent port
|
||||
self.check_remote_connectivity(
|
||||
trunk_network_server['ssh_client'],
|
||||
normal_network_server['port']['fixed_ips'][0]['ip_address'],
|
||||
should_succeed=True
|
||||
)
|
||||
|
||||
# Ping from trunk_network_server to vlan_network_server via VLAN
|
||||
# interface should success
|
||||
self.check_remote_connectivity(
|
||||
trunk_network_server['ssh_client'],
|
||||
vlan_network_server['port']['fixed_ips'][0]['ip_address'],
|
||||
should_succeed=True
|
||||
)
|
||||
|
||||
# Delete the trunk
|
||||
self.delete_trunk(trunk_network_server['trunk'],
|
||||
detach_parent_port=False)
|
||||
LOG.debug("Trunk %s is deleted.", trunk_network_server['trunk']['id'])
|
||||
|
||||
# Ping from trunk_network_server to normal_network_server
|
||||
# via parent port success after trunk deleted
|
||||
self.check_remote_connectivity(
|
||||
trunk_network_server['ssh_client'],
|
||||
normal_network_server['port']['fixed_ips'][0]['ip_address'],
|
||||
should_succeed=True
|
||||
)
|
||||
|
||||
# Ping from trunk_network_server to vlan_network_server via VLAN
|
||||
# interface should fail after trunk deleted
|
||||
self.check_remote_connectivity(
|
||||
trunk_network_server['ssh_client'],
|
||||
vlan_network_server['port']['fixed_ips'][0]['ip_address'],
|
||||
should_succeed=False
|
||||
)
|
||||
|
@ -876,6 +876,13 @@ class NetworkClientJSON(service_client.RestClient):
|
||||
body = jsonutils.loads(body)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_security_group_rule(self, security_group_rule_id):
|
||||
uri = '%s/security-group-rules/%s' % (self.uri_prefix,
|
||||
security_group_rule_id)
|
||||
resp, body = self.delete(uri)
|
||||
self.expected_success(204, resp.status)
|
||||
return service_client.ResponseBody(resp, body)
|
||||
|
||||
def list_security_groups(self, **kwargs):
|
||||
post_body = {'security_groups': kwargs}
|
||||
body = jsonutils.dumps(post_body)
|
||||
|
Loading…
Reference in New Issue
Block a user