Merge "Add container network connectivity test"
This commit is contained in:
commit
f88ab90318
|
@ -17,8 +17,11 @@ from tempest import config
|
||||||
from tempest.lib.common import api_version_utils
|
from tempest.lib.common import api_version_utils
|
||||||
from tempest.lib.common import rest_client
|
from tempest.lib.common import rest_client
|
||||||
from tempest.lib.services.image.v2 import images_client
|
from tempest.lib.services.image.v2 import images_client
|
||||||
|
from tempest.lib.services.network import floating_ips_client
|
||||||
from tempest.lib.services.network import networks_client
|
from tempest.lib.services.network import networks_client
|
||||||
from tempest.lib.services.network import ports_client
|
from tempest.lib.services.network import ports_client
|
||||||
|
from tempest.lib.services.network import routers_client
|
||||||
|
from tempest.lib.services.network import security_group_rules_client
|
||||||
from tempest.lib.services.network import security_groups_client
|
from tempest.lib.services.network import security_groups_client
|
||||||
from tempest.lib.services.network import subnetpools_client
|
from tempest.lib.services.network import subnetpools_client
|
||||||
from tempest.lib.services.network import subnets_client
|
from tempest.lib.services.network import subnets_client
|
||||||
|
@ -80,6 +83,10 @@ class Manager(manager.Manager):
|
||||||
self.sgs_client = security_groups_client.SecurityGroupsClient(
|
self.sgs_client = security_groups_client.SecurityGroupsClient(
|
||||||
self.auth_provider, 'network', CONF.identity.region,
|
self.auth_provider, 'network', CONF.identity.region,
|
||||||
disable_ssl_certificate_validation=True)
|
disable_ssl_certificate_validation=True)
|
||||||
|
self.sg_rules_client = \
|
||||||
|
security_group_rules_client.SecurityGroupRulesClient(
|
||||||
|
self.auth_provider, 'network', CONF.identity.region,
|
||||||
|
disable_ssl_certificate_validation=True)
|
||||||
self.vol_client = volumes_client.VolumesClient(
|
self.vol_client = volumes_client.VolumesClient(
|
||||||
self.auth_provider, 'volumev3', CONF.identity.region,
|
self.auth_provider, 'volumev3', CONF.identity.region,
|
||||||
disable_ssl_certificate_validation=True)
|
disable_ssl_certificate_validation=True)
|
||||||
|
@ -93,6 +100,12 @@ class Manager(manager.Manager):
|
||||||
self.subnetpools_client = subnetpools_client.SubnetpoolsClient(
|
self.subnetpools_client = subnetpools_client.SubnetpoolsClient(
|
||||||
self.auth_provider, 'network', CONF.identity.region,
|
self.auth_provider, 'network', CONF.identity.region,
|
||||||
disable_ssl_certificate_validation=True)
|
disable_ssl_certificate_validation=True)
|
||||||
|
self.fip_client = floating_ips_client.FloatingIPsClient(
|
||||||
|
self.auth_provider, 'network', CONF.identity.region,
|
||||||
|
disable_ssl_certificate_validation=True)
|
||||||
|
self.routers_client = routers_client.RoutersClient(
|
||||||
|
self.auth_provider, 'network', CONF.identity.region,
|
||||||
|
disable_ssl_certificate_validation=True)
|
||||||
|
|
||||||
|
|
||||||
class ZunClient(rest_client.RestClient):
|
class ZunClient(rest_client.RestClient):
|
||||||
|
@ -274,9 +287,11 @@ class ZunClient(rest_client.RestClient):
|
||||||
self.container_uri(container_id, action='rebuild'), None, **kwargs)
|
self.container_uri(container_id, action='rebuild'), None, **kwargs)
|
||||||
|
|
||||||
def exec_container(self, container_id, command, **kwargs):
|
def exec_container(self, container_id, command, **kwargs):
|
||||||
return self.post(
|
resp, body = self.post(
|
||||||
self.container_uri(container_id, action='execute'),
|
self.container_uri(container_id, action='execute'),
|
||||||
'{"command": "%s"}' % command, **kwargs)
|
'{"command": "%s"}' % command, **kwargs)
|
||||||
|
return self.deserialize(
|
||||||
|
resp, body, container_model.ContainerExecEntity)
|
||||||
|
|
||||||
def logs_container(self, container_id, **kwargs):
|
def logs_container(self, container_id, **kwargs):
|
||||||
return self.get(
|
return self.get(
|
||||||
|
|
|
@ -50,3 +50,14 @@ class ContainerActionEntity(base_model.EntityModel):
|
||||||
"""Entity Model that represents a single instance of ContainerActionData"""
|
"""Entity Model that represents a single instance of ContainerActionData"""
|
||||||
ENTITY_NAME = 'containeraction'
|
ENTITY_NAME = 'containeraction'
|
||||||
MODEL_TYPE = ContainerActionData
|
MODEL_TYPE = ContainerActionData
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerExecData(base_model.BaseModel):
|
||||||
|
"""Data that encapsulates container exec attributes"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerExecEntity(base_model.EntityModel):
|
||||||
|
"""Entity Model that represents a single instance of ContainerExecData"""
|
||||||
|
ENTITY_NAME = 'containerexec'
|
||||||
|
MODEL_TYPE = ContainerExecData
|
||||||
|
|
|
@ -12,15 +12,19 @@
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import random
|
import random
|
||||||
|
import subprocess
|
||||||
import tarfile
|
import tarfile
|
||||||
import testtools
|
import testtools
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils as json
|
from oslo_serialization import jsonutils as json
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
|
from tempest.common.utils import net_utils
|
||||||
from tempest import config
|
from tempest import config
|
||||||
from tempest.lib.common.utils import data_utils
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib.common.utils import test_utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
from tempest.lib import exceptions as lib_exc
|
from tempest.lib import exceptions as lib_exc
|
||||||
|
|
||||||
|
@ -31,6 +35,7 @@ from zun_tempest_plugin.tests.tempest import utils
|
||||||
|
|
||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TestContainer(base.BaseZunTest):
|
class TestContainer(base.BaseZunTest):
|
||||||
|
@ -53,6 +58,7 @@ class TestContainer(base.BaseZunTest):
|
||||||
super(TestContainer, cls).setup_clients()
|
super(TestContainer, cls).setup_clients()
|
||||||
cls.images_client = cls.os_primary.images_client
|
cls.images_client = cls.os_primary.images_client
|
||||||
cls.sgs_client = cls.os_primary.sgs_client
|
cls.sgs_client = cls.os_primary.sgs_client
|
||||||
|
cls.sg_rules_client = cls.os_primary.sg_rules_client
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resource_setup(cls):
|
def resource_setup(cls):
|
||||||
|
@ -416,7 +422,7 @@ class TestContainer(base.BaseZunTest):
|
||||||
resp, body = self.container_client.exec_container(
|
resp, body = self.container_client.exec_container(
|
||||||
model.uuid, command='cat %s' % container_file)
|
model.uuid, command='cat %s' % container_file)
|
||||||
self.assertEqual(200, resp.status)
|
self.assertEqual(200, resp.status)
|
||||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
self.assertTrue('hello' in body.output)
|
||||||
|
|
||||||
@decorators.idempotent_id('df7b2518-f779-43f6-b188-28cf3595e251')
|
@decorators.idempotent_id('df7b2518-f779-43f6-b188-28cf3595e251')
|
||||||
@utils.requires_microversion('1.24')
|
@utils.requires_microversion('1.24')
|
||||||
|
@ -501,7 +507,7 @@ class TestContainer(base.BaseZunTest):
|
||||||
resp, body = self.container_client.exec_container(
|
resp, body = self.container_client.exec_container(
|
||||||
model.uuid, command='cat %s' % container_file)
|
model.uuid, command='cat %s' % container_file)
|
||||||
self.assertEqual(200, resp.status)
|
self.assertEqual(200, resp.status)
|
||||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
self.assertTrue('hello' in body.output)
|
||||||
# delete the container and assert the volume is removed.
|
# delete the container and assert the volume is removed.
|
||||||
self.container_client.delete_container(
|
self.container_client.delete_container(
|
||||||
model.uuid, params={'stop': True})
|
model.uuid, params={'stop': True})
|
||||||
|
@ -524,7 +530,7 @@ class TestContainer(base.BaseZunTest):
|
||||||
resp, body = self.container_client.exec_container(
|
resp, body = self.container_client.exec_container(
|
||||||
model.uuid, command='cat %s' % container_file)
|
model.uuid, command='cat %s' % container_file)
|
||||||
self.assertEqual(200, resp.status)
|
self.assertEqual(200, resp.status)
|
||||||
self.assertTrue(file_content in encodeutils.safe_decode(body))
|
self.assertTrue(file_content in body.output)
|
||||||
|
|
||||||
@decorators.idempotent_id('0c8afb23-312d-4647-897d-b3c8591b26eb')
|
@decorators.idempotent_id('0c8afb23-312d-4647-897d-b3c8591b26eb')
|
||||||
@utils.requires_microversion('1.39')
|
@utils.requires_microversion('1.39')
|
||||||
|
@ -666,7 +672,7 @@ class TestContainer(base.BaseZunTest):
|
||||||
resp, body = self.container_client.exec_container(model.uuid,
|
resp, body = self.container_client.exec_container(model.uuid,
|
||||||
command='echo hello')
|
command='echo hello')
|
||||||
self.assertEqual(200, resp.status)
|
self.assertEqual(200, resp.status)
|
||||||
self.assertTrue('hello' in encodeutils.safe_decode(body))
|
self.assertTrue('hello' in body.output)
|
||||||
|
|
||||||
@decorators.idempotent_id('a912ca23-14e7-442f-ab15-e05aaa315204')
|
@decorators.idempotent_id('a912ca23-14e7-442f-ab15-e05aaa315204')
|
||||||
def test_logs_container(self):
|
def test_logs_container(self):
|
||||||
|
@ -890,6 +896,291 @@ class TestContainer(base.BaseZunTest):
|
||||||
self.assertEqual(body['stat']['name'], tarinfo.name)
|
self.assertEqual(body['stat']['name'], tarinfo.name)
|
||||||
self.assertEqual(body['stat']['size'], tarinfo.size)
|
self.assertEqual(body['stat']['size'], tarinfo.size)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('91d8bf98-9dbf-4c38-91c3-6dc8cc47132f')
|
||||||
|
def test_container_network(self):
|
||||||
|
"""Basic network operation test
|
||||||
|
|
||||||
|
For a freshly-booted container with an IP address ("port") on a given
|
||||||
|
network:
|
||||||
|
- the Tempest host can ping the IP address. This implies, but
|
||||||
|
does not guarantee, that the
|
||||||
|
container has been assigned the correct IP address and has
|
||||||
|
connectivity to the Tempest host.
|
||||||
|
- the Tempest host can enter the container and
|
||||||
|
successfully execute the following:
|
||||||
|
- ping an external IP address, implying external connectivity.
|
||||||
|
- ping an internal IP address, implying connectivity to another
|
||||||
|
container on the same network.
|
||||||
|
- detach the floating-ip from the container and verify that it becomes
|
||||||
|
unreachable
|
||||||
|
- associate detached floating ip to a new container and verify
|
||||||
|
connectivity.
|
||||||
|
Verifies that floating IP status is updated correctly after each change
|
||||||
|
"""
|
||||||
|
if not CONF.network.public_network_id:
|
||||||
|
msg = 'public network not defined.'
|
||||||
|
raise self.skipException(msg)
|
||||||
|
|
||||||
|
container, floating_ip, network = self._setup_network_and_containers()
|
||||||
|
self._check_public_network_connectivity(floating_ip,
|
||||||
|
should_connect=True)
|
||||||
|
self._check_network_internal_connectivity(container, network)
|
||||||
|
self._check_network_external_connectivity(container)
|
||||||
|
self._disassociate_floating_ips(floating_ip)
|
||||||
|
self._check_public_network_connectivity(
|
||||||
|
floating_ip, should_connect=False,
|
||||||
|
msg="after disassociate floating ip")
|
||||||
|
self._reassociate_floating_ips(floating_ip, network)
|
||||||
|
self._check_public_network_connectivity(
|
||||||
|
floating_ip, should_connect=True,
|
||||||
|
msg="after re-associate floating ip")
|
||||||
|
|
||||||
|
def _setup_network_and_containers(self, **kwargs):
|
||||||
|
network = self.create_network()
|
||||||
|
router = self.create_router()
|
||||||
|
subnet = self.create_subnet(network, allocate_cidr=True)
|
||||||
|
self.routers_client.add_router_interface(router['id'],
|
||||||
|
subnet_id=subnet['id'])
|
||||||
|
self.addCleanup(self.routers_client.remove_router_interface,
|
||||||
|
router['id'], subnet_id=subnet['id'])
|
||||||
|
|
||||||
|
tenant_network_id = network['id']
|
||||||
|
security_group = self._create_security_group()
|
||||||
|
_, model = self._run_container(
|
||||||
|
nets=[{'network': tenant_network_id}],
|
||||||
|
security_groups=[security_group['name']])
|
||||||
|
self.assertEqual(1, len(model.addresses))
|
||||||
|
self.assertEqual(1, len(model.addresses[tenant_network_id]))
|
||||||
|
port_id = model.addresses[tenant_network_id][0]['port']
|
||||||
|
fixed_ip_address = model.addresses[tenant_network_id][0]['addr']
|
||||||
|
|
||||||
|
floating_ip = self.fip_client.create_floatingip(
|
||||||
|
floating_network_id=CONF.network.public_network_id,
|
||||||
|
port_id=port_id,
|
||||||
|
fixed_ip_address=fixed_ip_address)['floatingip']
|
||||||
|
return model, floating_ip, network
|
||||||
|
|
||||||
|
def _create_security_group(self):
|
||||||
|
# Create security group
|
||||||
|
sg_name = data_utils.rand_name(self.__class__.__name__)
|
||||||
|
sg_desc = sg_name + " description"
|
||||||
|
secgroup = self.sgs_client.create_security_group(
|
||||||
|
name=sg_name, description=sg_desc)['security_group']
|
||||||
|
self.assertEqual(secgroup['name'], sg_name)
|
||||||
|
self.assertEqual(secgroup['description'], sg_desc)
|
||||||
|
self.addCleanup(
|
||||||
|
self.sgs_client.delete_security_group,
|
||||||
|
secgroup['id'])
|
||||||
|
|
||||||
|
# Add rules to the security group
|
||||||
|
self._create_pingable_secgroup_rule(secgroup)
|
||||||
|
|
||||||
|
return secgroup
|
||||||
|
|
||||||
|
def _create_pingable_secgroup_rule(self, secgroup, sg_rules_client=None):
|
||||||
|
if sg_rules_client is None:
|
||||||
|
sg_rules_client = self.sg_rules_client
|
||||||
|
rulesets = [
|
||||||
|
dict(
|
||||||
|
# ping
|
||||||
|
protocol='icmp',
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
# ipv6-icmp for ping6
|
||||||
|
protocol='icmp',
|
||||||
|
ethertype='IPv6',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for ruleset in rulesets:
|
||||||
|
for r_direction in ['ingress', 'egress']:
|
||||||
|
ruleset['direction'] = r_direction
|
||||||
|
try:
|
||||||
|
sg_rules_client.create_security_group_rule(
|
||||||
|
security_group_id=secgroup['id'],
|
||||||
|
project_id=secgroup['project_id'],
|
||||||
|
**ruleset)
|
||||||
|
except lib_exc.Conflict as ex:
|
||||||
|
# if rule already exist - skip rule and continue
|
||||||
|
msg = 'Security group rule already exists'
|
||||||
|
if msg not in ex._error_string:
|
||||||
|
raise ex
|
||||||
|
|
||||||
|
def _disassociate_floating_ips(self, floating_ip):
|
||||||
|
floating_ip = self.fip_client.update_floatingip(
|
||||||
|
floating_ip['id'], port_id=None)['floatingip']
|
||||||
|
self.assertIsNone(floating_ip['port_id'])
|
||||||
|
|
||||||
|
def _reassociate_floating_ips(self, floating_ip, network):
|
||||||
|
# create a new container for the floating ip
|
||||||
|
tenant_network_id = network['id']
|
||||||
|
security_group = self._create_security_group()
|
||||||
|
_, container = self._run_container(
|
||||||
|
nets=[{'network': tenant_network_id}],
|
||||||
|
security_groups=[security_group['name']])
|
||||||
|
self.assertEqual(1, len(container.addresses))
|
||||||
|
self.assertEqual(1, len(container.addresses[tenant_network_id]))
|
||||||
|
port_id = container.addresses[tenant_network_id][0]['port']
|
||||||
|
floating_ip = self.fip_client.update_floatingip(
|
||||||
|
floating_ip['id'], port_id=port_id)['floatingip']
|
||||||
|
self.assertEqual(port_id, floating_ip['port_id'])
|
||||||
|
|
||||||
|
def _check_public_network_connectivity(
|
||||||
|
self, floating_ip, should_connect=True, msg=None,
|
||||||
|
should_check_floating_ip_status=True, mtu=None):
|
||||||
|
ip_address = floating_ip['floating_ip_address']
|
||||||
|
floatingip_status = 'DOWN'
|
||||||
|
if should_connect:
|
||||||
|
floatingip_status = 'ACTIVE'
|
||||||
|
|
||||||
|
# Check FloatingIP Status before initiating a connection
|
||||||
|
if should_check_floating_ip_status:
|
||||||
|
self._check_floating_ip_status(floating_ip, floatingip_status)
|
||||||
|
|
||||||
|
message = 'Public network connectivity check failed'
|
||||||
|
if msg:
|
||||||
|
message += '. Reason: %s' % msg
|
||||||
|
|
||||||
|
self._check_ip_connectivity(ip_address, should_connect, message,
|
||||||
|
mtu=mtu)
|
||||||
|
|
||||||
|
def _check_ip_connectivity(self, ip_address, should_connect=True,
|
||||||
|
extra_msg="", mtu=None):
|
||||||
|
LOG.debug('checking network connections to IP: %s', ip_address)
|
||||||
|
if should_connect:
|
||||||
|
msg = "Timed out waiting for %s to become reachable" % ip_address
|
||||||
|
else:
|
||||||
|
msg = "ip address %s is reachable" % ip_address
|
||||||
|
if extra_msg:
|
||||||
|
msg = "%s\n%s" % (extra_msg, msg)
|
||||||
|
self.assertTrue(self._ping_ip_address(ip_address,
|
||||||
|
should_succeed=should_connect,
|
||||||
|
mtu=mtu),
|
||||||
|
msg=msg)
|
||||||
|
|
||||||
|
def _check_network_internal_connectivity(self, container, network,
|
||||||
|
should_connect=True):
|
||||||
|
"""check container internal connectivity:
|
||||||
|
|
||||||
|
- ping internal gateway and DHCP port, implying in-tenant connectivity
|
||||||
|
pinging both, because L3 and DHCP agents might be on different nodes
|
||||||
|
"""
|
||||||
|
# get internal ports' ips:
|
||||||
|
# get all network and compute ports in the new network
|
||||||
|
internal_ips = (
|
||||||
|
p['fixed_ips'][0]['ip_address'] for p in
|
||||||
|
self.os_admin.ports_client.list_ports(
|
||||||
|
project_id=container.project_id,
|
||||||
|
network_id=network['id'])['ports']
|
||||||
|
if p['device_owner'].startswith('network')
|
||||||
|
)
|
||||||
|
|
||||||
|
for internal_ip in internal_ips:
|
||||||
|
self._check_remote_connectivity(container, internal_ip,
|
||||||
|
should_connect)
|
||||||
|
|
||||||
|
def _check_network_external_connectivity(self, container):
|
||||||
|
# We ping the external IP from the container using its floating IP
|
||||||
|
# which is always IPv4, so we must only test connectivity to
|
||||||
|
# external IPv4 IPs if the external network is dualstack.
|
||||||
|
v4_subnets = [
|
||||||
|
s for s in self.os_admin.subnets_client.list_subnets(
|
||||||
|
network_id=CONF.network.public_network_id)['subnets']
|
||||||
|
if s['ip_version'] == 4
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(v4_subnets) > 1:
|
||||||
|
self.assertTrue(
|
||||||
|
CONF.network.subnet_id,
|
||||||
|
"Found %d subnets. Specify subnet using configuration "
|
||||||
|
"option [network].subnet_id."
|
||||||
|
% len(v4_subnets))
|
||||||
|
subnet = self.os_admin.subnets_client.show_subnet(
|
||||||
|
CONF.network.subnet_id)['subnet']
|
||||||
|
external_ip = subnet['gateway_ip']
|
||||||
|
else:
|
||||||
|
external_ip = v4_subnets[0]['gateway_ip']
|
||||||
|
|
||||||
|
self._check_remote_connectivity(container, external_ip)
|
||||||
|
|
||||||
|
def _check_remote_connectivity(self, container, dest, should_succeed=True):
|
||||||
|
def connect_remote():
|
||||||
|
resp, body = self.container_client.exec_container(
|
||||||
|
container.uuid,
|
||||||
|
command="ping -c1 -w1 %s" % dest)
|
||||||
|
self.assertEqual(200, resp.status)
|
||||||
|
return (body.exit_code == 0) == should_succeed
|
||||||
|
|
||||||
|
result = test_utils.call_until_true(connect_remote,
|
||||||
|
CONF.validation.ping_timeout, 1)
|
||||||
|
if result:
|
||||||
|
return
|
||||||
|
|
||||||
|
if should_succeed:
|
||||||
|
msg = "Timed out waiting for %s to become reachable" % (
|
||||||
|
dest)
|
||||||
|
else:
|
||||||
|
msg = "%s is reachable from container" % (dest)
|
||||||
|
self.fail(msg)
|
||||||
|
|
||||||
|
def _ping_ip_address(self, ip_address, should_succeed=True,
|
||||||
|
ping_timeout=None, mtu=None, server=None):
|
||||||
|
timeout = ping_timeout or CONF.validation.ping_timeout
|
||||||
|
cmd = ['ping', '-c1', '-w1']
|
||||||
|
|
||||||
|
if mtu:
|
||||||
|
cmd += [
|
||||||
|
# don't fragment
|
||||||
|
'-M', 'do',
|
||||||
|
# ping receives just the size of ICMP payload
|
||||||
|
'-s', str(net_utils.get_ping_payload_size(mtu, 4))
|
||||||
|
]
|
||||||
|
cmd.append(ip_address)
|
||||||
|
|
||||||
|
def ping():
|
||||||
|
proc = subprocess.Popen(cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
proc.communicate()
|
||||||
|
|
||||||
|
return (proc.returncode == 0) == should_succeed
|
||||||
|
|
||||||
|
caller = test_utils.find_test_caller()
|
||||||
|
LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
|
||||||
|
' expected result is %(should_succeed)s', {
|
||||||
|
'caller': caller, 'ip': ip_address, 'timeout': timeout,
|
||||||
|
'should_succeed':
|
||||||
|
'reachable' if should_succeed else 'unreachable'
|
||||||
|
})
|
||||||
|
result = test_utils.call_until_true(ping, timeout, 1)
|
||||||
|
LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
|
||||||
|
'ping result is %(result)s', {
|
||||||
|
'caller': caller, 'ip': ip_address, 'timeout': timeout,
|
||||||
|
'result': 'expected' if result else 'unexpected'
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _check_floating_ip_status(self, floating_ip, status):
|
||||||
|
floatingip_id = floating_ip['id']
|
||||||
|
|
||||||
|
def refresh():
|
||||||
|
floating_ip = (self.fip_client.
|
||||||
|
show_floatingip(floatingip_id)['floatingip'])
|
||||||
|
if status == floating_ip['status']:
|
||||||
|
LOG.info("FloatingIP: {fp} is at status: {st}"
|
||||||
|
.format(fp=floating_ip, st=status))
|
||||||
|
return status == floating_ip['status']
|
||||||
|
|
||||||
|
if not test_utils.call_until_true(refresh,
|
||||||
|
CONF.network.build_timeout,
|
||||||
|
CONF.network.build_interval):
|
||||||
|
floating_ip = self.fip_client.show_floatingip(
|
||||||
|
floatingip_id)['floatingip']
|
||||||
|
self.assertEqual(status, floating_ip['status'],
|
||||||
|
message="FloatingIP: {fp} is at status: {cst}. "
|
||||||
|
"failed to reach status: {st}"
|
||||||
|
.format(fp=floating_ip, cst=floating_ip['status'],
|
||||||
|
st=status))
|
||||||
|
|
||||||
def _ensure_network_detached(self, container, network):
|
def _ensure_network_detached(self, container, network):
|
||||||
def is_network_detached():
|
def is_network_detached():
|
||||||
_, model = self.container_client.get_container(container.uuid)
|
_, model = self.container_client.get_container(container.uuid)
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from tempest import config
|
from tempest import config
|
||||||
from tempest.lib.common import api_version_request
|
from tempest.lib.common import api_version_request
|
||||||
|
@ -56,6 +58,8 @@ class BaseZunTest(api_version_utils.BaseMicroversionTest,
|
||||||
cls.ports_client = cls.os_primary.ports_client
|
cls.ports_client = cls.os_primary.ports_client
|
||||||
cls.subnetpools_client = cls.os_primary.subnetpools_client
|
cls.subnetpools_client = cls.os_primary.subnetpools_client
|
||||||
cls.vol_client = cls.os_primary.vol_client
|
cls.vol_client = cls.os_primary.vol_client
|
||||||
|
cls.routers_client = cls.os_primary.routers_client
|
||||||
|
cls.fip_client = cls.os_primary.fip_client
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_credentials(cls):
|
def setup_credentials(cls):
|
||||||
|
@ -117,6 +121,22 @@ class BaseZunTest(api_version_utils.BaseMicroversionTest,
|
||||||
self.request_microversion
|
self.request_microversion
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def create_router(self, client=None, **values):
|
||||||
|
client = client or self.routers_client
|
||||||
|
kwargs = {
|
||||||
|
'name': data_utils.rand_name('test-router'),
|
||||||
|
'admin_state_up': True,
|
||||||
|
'project_id': getattr(client, 'project_id', client.tenant_id),
|
||||||
|
'external_gateway_info': {
|
||||||
|
'network_id': CONF.network.public_network_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if values:
|
||||||
|
kwargs.update(values)
|
||||||
|
router = client.create_router(**kwargs)['router']
|
||||||
|
self.addCleanup(client.delete_router, router['id'])
|
||||||
|
return router
|
||||||
|
|
||||||
def create_network(self, client=None, **values):
|
def create_network(self, client=None, **values):
|
||||||
kwargs = {'name': data_utils.rand_name('test-network')}
|
kwargs = {'name': data_utils.rand_name('test-network')}
|
||||||
if values:
|
if values:
|
||||||
|
@ -126,16 +146,57 @@ class BaseZunTest(api_version_utils.BaseMicroversionTest,
|
||||||
self.addCleanup(client.delete_network, network['id'])
|
self.addCleanup(client.delete_network, network['id'])
|
||||||
return network
|
return network
|
||||||
|
|
||||||
def create_subnet(self, network, client=None, **values):
|
def create_subnet(self, network, client=None, allocate_cidr=False,
|
||||||
|
**values):
|
||||||
kwargs = {'name': data_utils.rand_name('test-subnet'),
|
kwargs = {'name': data_utils.rand_name('test-subnet'),
|
||||||
'network_id': network['id'],
|
'network_id': network['id'],
|
||||||
|
'project_id': network['project_id'],
|
||||||
'ip_version': 4}
|
'ip_version': 4}
|
||||||
if values:
|
if values:
|
||||||
kwargs.update(values)
|
kwargs.update(values)
|
||||||
client = client or self.subnets_client
|
client = client or self.subnets_client
|
||||||
subnet = client.create_subnet(**kwargs)['subnet']
|
|
||||||
self.addCleanup(client.delete_subnet, subnet['id'])
|
def cidr_in_use(cidr, project_id):
|
||||||
return subnet
|
"""Check cidr existence
|
||||||
|
|
||||||
|
:returns: True if subnet with cidr already exist in tenant
|
||||||
|
False else
|
||||||
|
"""
|
||||||
|
cidr_in_use = self.os_admin.subnets_client.list_subnets(
|
||||||
|
project_id=project_id, cidr=cidr)['subnets']
|
||||||
|
return len(cidr_in_use) != 0
|
||||||
|
|
||||||
|
if allocate_cidr:
|
||||||
|
tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
|
||||||
|
num_bits = CONF.network.project_network_mask_bits
|
||||||
|
|
||||||
|
subnet = None
|
||||||
|
str_cidr = None
|
||||||
|
# Repeatedly attempt subnet creation with sequential cidr
|
||||||
|
# blocks until an unallocated block is found.
|
||||||
|
for subnet_cidr in tenant_cidr.subnet(num_bits):
|
||||||
|
str_cidr = str(subnet_cidr)
|
||||||
|
if cidr_in_use(str_cidr, project_id=network['project_id']):
|
||||||
|
continue
|
||||||
|
|
||||||
|
kwargs['cidr'] = str_cidr
|
||||||
|
try:
|
||||||
|
subnet = client.create_subnet(**kwargs)['subnet']
|
||||||
|
self.addCleanup(client.delete_subnet, subnet['id'])
|
||||||
|
break
|
||||||
|
except lib_exc.Conflict as e:
|
||||||
|
is_overlapping_cidr = \
|
||||||
|
'overlaps with another subnet' in str(e)
|
||||||
|
if not is_overlapping_cidr:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.assertIsNotNone(subnet, 'Unable to allocate tenant network')
|
||||||
|
self.assertEqual(subnet['cidr'], str_cidr)
|
||||||
|
return subnet
|
||||||
|
else:
|
||||||
|
subnet = client.create_subnet(**kwargs)['subnet']
|
||||||
|
self.addCleanup(client.delete_subnet, subnet['id'])
|
||||||
|
return subnet
|
||||||
|
|
||||||
def create_port(self, network, client=None, **values):
|
def create_port(self, network, client=None, **values):
|
||||||
kwargs = {'name': data_utils.rand_name('test-port'),
|
kwargs = {'name': data_utils.rand_name('test-port'),
|
||||||
|
|
Loading…
Reference in New Issue