From 9ce97dfa353e3659aa0ec0a7f62ed3a7e54c4bdf Mon Sep 17 00:00:00 2001 From: Rohan Kanade Date: Tue, 10 Dec 2013 18:59:35 +0530 Subject: [PATCH] Start instances using fixed network when possible Uses the fixed_network_name option to select which network to boot test instances on. This allows Tempest to be run in environments where there are multiple networks available. This problem does not apply to nova-network environments because it will happily start instances with multiple networks attached, so the behavior is left as before in that case. Co-Authored-By: Ben Nemec Co-Authored-By: Vincent Untz Co-Authored-By: git-harry Change-Id: Ib5b84b59e3d182d8b9cc83954537c32f3eb9e388 Closes-Bug: #1250866 --- tempest/api/compute/admin/test_servers.py | 6 +- tempest/api/compute/base.py | 3 + .../compute/servers/test_attach_interfaces.py | 17 +++- .../servers/test_list_server_filters.py | 13 ++- tempest/api/volume/base.py | 11 +++ tempest/api/volume/test_volumes_actions.py | 4 +- tempest/api/volume/test_volumes_negative.py | 4 +- tempest/api/volume/test_volumes_snapshots.py | 4 +- tempest/common/fixed_network.py | 83 +++++++++++++++++++ tempest/scenario/manager.py | 3 + .../services/compute/json/networks_client.py | 8 +- tempest/test.py | 16 ++++ 12 files changed, 149 insertions(+), 23 deletions(-) create mode 100644 tempest/common/fixed_network.py 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)