diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py index c872184d6f..ef3a029db7 100644 --- a/tempest/api/compute/admin/test_servers.py +++ b/tempest/api/compute/admin/test_servers.py @@ -16,6 +16,7 @@ from tempest_lib.common.utils import data_utils from tempest_lib import decorators from tempest.api.compute import base +from tempest.common import fixed_network from tempest import test @@ -112,7 +113,10 @@ class ServersAdminTestJSON(base.BaseV2ComputeAdminTest): name = data_utils.rand_name('server') flavor = self.flavor_ref image_id = self.image_ref - test_server = self.client.create_server(name, image_id, flavor) + network = self.get_tenant_network() + network_kwargs = fixed_network.set_networks_kwarg(network) + test_server = self.client.create_server(name, image_id, flavor, + **network_kwargs) self.addCleanup(self.client.delete_server, test_server['id']) self.client.wait_for_server_status(test_server['id'], 'ACTIVE') server = self.client.get_server(test_server['id']) diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py index ddfe6de4c4..b7941016d7 100644 --- a/tempest/api/compute/base.py +++ b/tempest/api/compute/base.py @@ -22,6 +22,7 @@ from tempest_lib import exceptions as lib_exc from tempest import clients from tempest.common import credentials +from tempest.common import fixed_network from tempest import config from tempest import exceptions import tempest.test @@ -212,6 +213,8 @@ class BaseComputeTest(tempest.test.BaseTestCase): flavor = kwargs.get('flavor', cls.flavor_ref) image_id = kwargs.get('image_id', cls.image_ref) + kwargs = fixed_network.set_networks_kwarg( + cls.get_tenant_network(), kwargs) or {} body = cls.servers_client.create_server( name, image_id, flavor, **kwargs) diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py index 0702f3f2ba..42a61daa21 100644 --- a/tempest/api/compute/servers/test_attach_interfaces.py +++ b/tempest/api/compute/servers/test_attach_interfaces.py @@ -13,13 +13,15 @@ # License for the specific language governing permissions and limitations # under the License. +import time + +from tempest_lib import exceptions as lib_exc + from tempest.api.compute import base from tempest import config from tempest import exceptions from tempest import test -import time - CONF = config.CONF @@ -125,8 +127,15 @@ class AttachInterfacesTestJSON(base.BaseV2ComputeTest): self.assertTrue(interface_count > 0) self._check_interface(ifs[0]) - iface = self._test_create_interface(server) - ifs.append(iface) + try: + iface = self._test_create_interface(server) + except lib_exc.BadRequest as e: + msg = ('Multiple possible networks found, use a Network ID to be ' + 'more specific.') + if not CONF.compute.fixed_network_name and e.message == msg: + raise + else: + ifs.append(iface) iface = self._test_create_interface_by_network_id(server, ifs) ifs.append(iface) diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py index 5c10f30185..70e4dffdc6 100644 --- a/tempest/api/compute/servers/test_list_server_filters.py +++ b/tempest/api/compute/servers/test_list_server_filters.py @@ -19,6 +19,7 @@ from tempest_lib import exceptions as lib_exc from tempest.api.compute import base from tempest.api import utils +from tempest.common import fixed_network from tempest import config from tempest import test @@ -66,9 +67,13 @@ class ListServerFiltersTestJSON(base.BaseV2ComputeTest): raise RuntimeError("Image %s (image_ref_alt) was not found!" % cls.image_ref_alt) + network = cls.get_tenant_network() + cls.fixed_network_name = network['name'] + network_kwargs = fixed_network.set_networks_kwarg(network) cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance') cls.s1 = cls.create_test_server(name=cls.s1_name, - wait_until='ACTIVE') + wait_until='ACTIVE', + **network_kwargs) cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance') cls.s2 = cls.create_test_server(name=cls.s2_name, @@ -80,12 +85,6 @@ class ListServerFiltersTestJSON(base.BaseV2ComputeTest): flavor=cls.flavor_ref_alt, wait_until='ACTIVE') - cls.fixed_network_name = CONF.compute.fixed_network_name - if CONF.service_available.neutron: - if hasattr(cls.isolated_creds, 'get_primary_network'): - network = cls.isolated_creds.get_primary_network() - cls.fixed_network_name = network['name'] - @test.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba') @utils.skip_unless_attr('multiple_images', 'Only one image found') @test.attr(type='gate') diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py index 157bd4488b..1f76b1c131 100644 --- a/tempest/api/volume/base.py +++ b/tempest/api/volume/base.py @@ -18,6 +18,7 @@ from tempest_lib.common.utils import data_utils from tempest_lib import exceptions as lib_exc from tempest import clients +from tempest.common import fixed_network from tempest import config from tempest import exceptions import tempest.test @@ -62,6 +63,7 @@ class BaseVolumeTest(tempest.test.BaseTestCase): super(BaseVolumeTest, cls).setup_clients() cls.servers_client = cls.os.servers_client + cls.networks_client = cls.os.networks_client if cls._api_version == 1: cls.snapshots_client = cls.os.snapshots_client @@ -159,6 +161,15 @@ class BaseVolumeTest(tempest.test.BaseTestCase): except Exception: pass + @classmethod + def create_server(cls, name, **kwargs): + network = cls.get_tenant_network() + network_kwargs = fixed_network.set_networks_kwarg(network, kwargs) + return cls.servers_client.create_server(name, + cls.image_ref, + cls.flavor_ref, + **network_kwargs) + class BaseVolumeAdminTest(BaseVolumeTest): """Base test case class for all Volume Admin API tests.""" diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py index 3de5021b3f..1872ec7c3b 100644 --- a/tempest/api/volume/test_volumes_actions.py +++ b/tempest/api/volume/test_volumes_actions.py @@ -36,9 +36,7 @@ class VolumesV2ActionsTest(base.BaseVolumeTest): # Create a test shared instance srv_name = data_utils.rand_name(cls.__name__ + '-Instance') - cls.server = cls.servers_client.create_server(srv_name, - cls.image_ref, - cls.flavor_ref) + cls.server = cls.create_server(srv_name) cls.servers_client.wait_for_server_status(cls.server['id'], 'ACTIVE') # Create a test shared volume for attach/detach tests diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py index af6e24e1b7..a47e964683 100644 --- a/tempest/api/volume/test_volumes_negative.py +++ b/tempest/api/volume/test_volumes_negative.py @@ -179,9 +179,7 @@ class VolumesV2NegativeTest(base.BaseVolumeTest): @test.services('compute') def test_attach_volumes_with_nonexistent_volume_id(self): srv_name = data_utils.rand_name('Instance') - server = self.servers_client.create_server(srv_name, - self.image_ref, - self.flavor_ref) + server = self.create_server(srv_name) self.addCleanup(self.servers_client.delete_server, server['id']) self.servers_client.wait_for_server_status(server['id'], 'ACTIVE') self.assertRaises(lib_exc.NotFound, diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py index 2b6339ac96..b277390fd0 100644 --- a/tempest/api/volume/test_volumes_snapshots.py +++ b/tempest/api/volume/test_volumes_snapshots.py @@ -69,9 +69,7 @@ class VolumesV2SnapshotTestJSON(base.BaseVolumeTest): # Create a snapshot when volume status is in-use # Create a test instance server_name = data_utils.rand_name('instance') - server = self.servers_client.create_server(server_name, - self.image_ref, - self.flavor_ref) + server = self.create_server(server_name) self.addCleanup(self.servers_client.delete_server, server['id']) self.servers_client.wait_for_server_status(server['id'], 'ACTIVE') mountpoint = '/dev/%s' % CONF.compute.volume_device_name diff --git a/tempest/common/fixed_network.py b/tempest/common/fixed_network.py new file mode 100644 index 0000000000..83822c26d1 --- /dev/null +++ b/tempest/common/fixed_network.py @@ -0,0 +1,83 @@ +# 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 copy +from oslo_log import log as logging + +from tempest_lib import exceptions as lib_exc + +from tempest import config + +CONF = config.CONF + +LOG = logging.getLogger(__name__) + + +def get_tenant_network(creds_provider, compute_networks_client): + """Get a network usable by the primary tenant + + :param creds_provider: instance of credential provider + :param compute_networks_client: compute network client. We want to have the + compute network client so we can have use a common approach for both + neutron and nova-network cases. If this is not an admin network + client, set_network_kwargs might fail in case fixed_network_name + is the network to be used, and it's not visible to the tenant + :return a dict with 'id' and 'name' of the network + """ + fixed_network_name = CONF.compute.fixed_network_name + # NOTE(andreaf) get_primary_network will always be available once + # bp test-accounts-continued is implemented + if (CONF.auth.allow_tenant_isolation and + (CONF.service_available.neutron and + not CONF.service_available.ironic)): + network = creds_provider.get_primary_network() + else: + if fixed_network_name: + try: + resp = compute_networks_client.list_networks( + name=fixed_network_name) + if isinstance(resp, list): + networks = resp + elif isinstance(resp, dict): + networks = resp['networks'] + else: + raise lib_exc.NotFound() + network = networks[0] + # To be consistent with network isolation, add name is only + # label is available + network['name'] = network.get('name', network.get('label')) + except lib_exc.NotFound: + # In case of nova network, if the fixed_network_name is not + # owned by the tenant, and the network client is not an admin + # one, list_networks will not find it + LOG.info('Unable to find network %s. ' + 'Starting instance without specifying a network.' % + fixed_network_name) + network = {'name': fixed_network_name} + LOG.info('Found network %s available for tenant' % network) + return network + + +def set_networks_kwarg(network, kwargs=None): + """Set 'networks' kwargs for a server create if missing + + :param network: dict of network to be used with 'id' and 'name' + :param kwargs: server create kwargs to be enhanced + :return: new dict of kwargs updated to include networks + """ + params = copy.copy(kwargs) or {} + if kwargs and 'networks' in kwargs: + return params + + if network: + params.update({"networks": [{'uuid': network['id']}]}) + return params diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py index f8cc17cde5..bae8296774 100644 --- a/tempest/scenario/manager.py +++ b/tempest/scenario/manager.py @@ -24,6 +24,7 @@ from tempest_lib import exceptions as lib_exc from tempest import clients from tempest.common import credentials +from tempest.common import fixed_network from tempest.common.utils.linux import remote_client from tempest import config from tempest import exceptions @@ -184,6 +185,8 @@ class ScenarioTest(tempest.test.BaseTestCase): flavor = CONF.compute.flavor_ref if create_kwargs is None: create_kwargs = {} + network = self.get_tenant_network() + fixed_network.set_networks_kwarg(network, create_kwargs) LOG.debug("Creating a server (name: %s, image: %s, flavor: %s)", name, image, flavor) diff --git a/tempest/services/compute/json/networks_client.py b/tempest/services/compute/json/networks_client.py index ef1c0583db..0ae0920ad9 100644 --- a/tempest/services/compute/json/networks_client.py +++ b/tempest/services/compute/json/networks_client.py @@ -20,11 +20,15 @@ from tempest.common import service_client class NetworksClientJSON(service_client.ServiceClient): - def list_networks(self): + def list_networks(self, name=None): resp, body = self.get("os-networks") body = json.loads(body) self.expected_success(200, resp.status) - return service_client.ResponseBodyList(resp, body['networks']) + if name: + networks = [n for n in body['networks'] if n['label'] == name] + else: + networks = body['networks'] + return service_client.ResponseBodyList(resp, networks) def get_network(self, network_id): resp, body = self.get("os-networks/%s" % str(network_id)) diff --git a/tempest/test.py b/tempest/test.py index 19bae7418a..da936b4eff 100644 --- a/tempest/test.py +++ b/tempest/test.py @@ -32,6 +32,7 @@ import testtools from tempest import clients from tempest.common import credentials +from tempest.common import fixed_network import tempest.common.generator.valid_generator as valid from tempest import config from tempest import exceptions @@ -435,6 +436,21 @@ class BaseTestCase(testtools.testcase.WithAttributes, 'subnet': subnet, 'dhcp': dhcp} + @classmethod + def get_tenant_network(cls): + """Get the network to be used in testing + + :return: network dict including 'id' and 'name' + """ + # Make sure isolated_creds exists and get a network client + networks_client = cls.get_client_manager().networks_client + isolated_creds = getattr(cls, 'isolated_creds', None) + if credentials.is_admin_available(): + admin_creds = isolated_creds.get_admin_creds() + networks_client = clients.Manager(admin_creds).networks_client + return fixed_network.get_tenant_network(isolated_creds, + networks_client) + def assertEmpty(self, list, msg=None): self.assertTrue(len(list) == 0, msg)