From 6a0a18a172002eb90a16dbeb438bf60ccb3fb04f Mon Sep 17 00:00:00 2001 From: Roee Agiman Date: Thu, 16 Nov 2017 11:51:56 +0200 Subject: [PATCH] Added test suite and case to cover 'availability zone' This was required to test advanced scenarios. * Added a new 'admin' folder in 'scenario' folder to test advanced operations. * Added a new file to the 'admin' folder that contains test cases for floating ip with admin privileges. * Added a scenario test that verify valid connection between two instances on a single compute node using availability-zone. * Changed 'base.py' to enable booting an instance with admin privileges. Change-Id: I711a10eef321622c335e35b84e386b01d73b938a --- neutron_tempest_plugin/api/clients.py | 6 + .../scenario/admin/__init__.py | 0 .../scenario/admin/test_floatingip.py | 109 ++++++++++++++++++ neutron_tempest_plugin/scenario/base.py | 83 ++++++++----- 4 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 neutron_tempest_plugin/scenario/admin/__init__.py create mode 100644 neutron_tempest_plugin/scenario/admin/test_floatingip.py diff --git a/neutron_tempest_plugin/api/clients.py b/neutron_tempest_plugin/api/clients.py index 272f5bec..875992ee 100644 --- a/neutron_tempest_plugin/api/clients.py +++ b/neutron_tempest_plugin/api/clients.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from tempest.lib.services.compute import availability_zone_client +from tempest.lib.services.compute import hypervisor_client from tempest.lib.services.compute import keypairs_client from tempest.lib.services.compute import servers_client from tempest.lib.services.identity.v2 import tenants_client @@ -74,6 +76,10 @@ class Manager(manager.Manager): **params) self.keypairs_client = keypairs_client.KeyPairsClient( self.auth_provider, **params) + self.hv_client = hypervisor_client.HypervisorClient( + self.auth_provider, **params) + self.az_client = availability_zone_client.AvailabilityZoneClient( + self.auth_provider, **params) def _set_identity_clients(self): params = { diff --git a/neutron_tempest_plugin/scenario/admin/__init__.py b/neutron_tempest_plugin/scenario/admin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/neutron_tempest_plugin/scenario/admin/test_floatingip.py b/neutron_tempest_plugin/scenario/admin/test_floatingip.py new file mode 100644 index 00000000..e58be836 --- /dev/null +++ b/neutron_tempest_plugin/scenario/admin/test_floatingip.py @@ -0,0 +1,109 @@ +# Copyright 2017 Red Hat, Inc. +# All Rights Reserved. +# +# 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. +from tempest.common import utils +from tempest.common import waiters +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +from neutron_tempest_plugin.common import ssh +from neutron_tempest_plugin import config +from neutron_tempest_plugin.scenario import base +from neutron_tempest_plugin.scenario import constants as const + +CONF = config.CONF + + +class FloatingIpTestCasesAdmin(base.BaseTempestTestCase): + credentials = ['primary', 'admin'] + + @classmethod + @utils.requires_ext(extension="router", service="network") + def resource_setup(cls): + super(FloatingIpTestCasesAdmin, cls).resource_setup() + cls.network = cls.create_network() + cls.create_subnet(cls.network) + router = cls.create_router_by_client() + cls.create_router_interface(router['id'], cls.subnets[0]['id']) + # Create keypair with admin privileges + cls.keypair = cls.create_keypair(client=cls.os_admin.keypairs_client) + # Create security group with admin privileges + cls.secgroup = cls.os_admin.network_client.create_security_group( + name=data_utils.rand_name('secgroup'))['security_group'] + # Execute funcs to achieve ssh and ICMP capabilities + funcs = [cls.create_loginable_secgroup_rule, + cls.create_pingable_secgroup_rule] + for func in funcs: + func(secgroup_id=cls.secgroup['id'], + client=cls.os_admin.network_client) + + @classmethod + def resource_cleanup(cls): + # Cleanup for security group + cls.os_admin.network_client.delete_security_group( + security_group_id=cls.secgroup['id']) + super(FloatingIpTestCasesAdmin, cls).resource_cleanup() + + def _list_hypervisors(self): + # List of hypervisors + return self.os_admin.hv_client.list_hypervisors()['hypervisors'] + + def _list_availability_zones(self): + # List of availability zones + func = self.os_admin.az_client.list_availability_zones + return func()['availabilityZoneInfo'] + + def _create_vms(self, hyper, avail_zone, num_servers=2): + servers, fips, server_ssh_clients = ([], [], []) + # Create the availability zone with default zone and + # a specific mentioned hypervisor. + az = avail_zone + ':' + hyper + for i in range(num_servers): + servers.append(self.create_server( + flavor_ref=CONF.compute.flavor_ref, + image_ref=CONF.compute.image_ref, + key_name=self.keypair['name'], + networks=[{'uuid': self.network['id']}], + security_groups=[{'name': self.secgroup['name']}], + availability_zone=az)) + for i, server in enumerate(servers): + waiters.wait_for_server_status( + self.os_admin.servers_client, server['server']['id'], + const.SERVER_STATUS_ACTIVE) + port = self.client.list_ports( + network_id=self.network['id'], + device_id=server['server']['id'] + )['ports'][0] + fips.append(self.create_and_associate_floatingip( + port['id'], client=self.os_admin.network_client)) + server_ssh_clients.append(ssh.Client( + fips[i]['floating_ip_address'], CONF.validation.image_ssh_user, + pkey=self.keypair['private_key'])) + self.addCleanup(self.os_admin.network_client.delete_floatingip, + fips[i]['id']) + return server_ssh_clients, fips + + @decorators.idempotent_id('6bba729b-3fb6-494b-9e1e-82bbd89a1045') + def test_two_vms_fips(self): + """This test verifies the ability of two instances + that were created in the same compute node and same availability zone + to reach each other. + """ + # Get hypervisor list to pass it for vm creation + hyper = self._list_hypervisors()[0]['hypervisor_hostname'] + # Get availability zone list to pass it for vm creation + avail_zone = self._list_availability_zones()[0]['zoneName'] + server_ssh_clients, fips = self._create_vms(hyper, avail_zone) + self.check_remote_connectivity( + server_ssh_clients[0], fips[1]['floating_ip_address']) diff --git a/neutron_tempest_plugin/scenario/base.py b/neutron_tempest_plugin/scenario/base.py index 5cc085fe..9fe6a946 100644 --- a/neutron_tempest_plugin/scenario/base.py +++ b/neutron_tempest_plugin/scenario/base.py @@ -42,15 +42,17 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): @classmethod def resource_cleanup(cls): for keypair in cls.keypairs: - cls.os_primary.keypairs_client.delete_keypair( - keypair_name=keypair['name']) + client = keypair['client'] + client.delete_keypair( + keypair_name=keypair['keypair']['name']) super(BaseTempestTestCase, cls).resource_cleanup() def create_server(self, flavor_ref, image_ref, key_name, networks, - name=None, security_groups=None): + **kwargs): """Create a server using tempest lib All the parameters are the ones used in Compute API + * - Kwargs that require admin privileges Args: flavor_ref(str): The flavor of the server to be provisioned. @@ -61,30 +63,47 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): an interface to be attached to the server. For network it should be {'uuid': network_uuid} and for port it should be {'port': port_uuid} + kwargs: name(str): Name of the server to be provisioned. security_groups(list): List of dictionaries where the keys is 'name' and the value is the name of the security group. If it's not passed the default security group will be used. + availability_zone(str)*: The availability zone that + the instance will be in. + You can request a specific az without actually creating one, + Just pass 'X:Y' where X is the default availability + zone, and Y is the compute host name. """ - name = name or data_utils.rand_name('server-test') - if not security_groups: - security_groups = [{'name': 'default'}] + name = kwargs.get('name', data_utils.rand_name('server-test')) + security_groups = kwargs.get( + 'security_groups', [{'name': 'default'}]) + availability_zone = kwargs.get('availability_zone') - server = self.os_primary.servers_client.create_server( - name=name, - flavorRef=flavor_ref, - imageRef=image_ref, - key_name=key_name, - networks=networks, - security_groups=security_groups) + server_args = { + 'name': name, + 'flavorRef': flavor_ref, + 'imageRef': image_ref, + 'key_name': key_name, + 'networks': networks, + 'security_groups': security_groups + } + + if availability_zone: + server_args['availability_zone'] = availability_zone + client = self.os_admin.servers_client + else: + client = self.os_primary.servers_client + + server = client.create_server(**server_args) self.addCleanup(test_utils.call_and_ignore_notfound_exc, - waiters.wait_for_server_termination, - self.os_primary.servers_client, server['server']['id']) + waiters.wait_for_server_termination, + client, + server['server']['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, - self.os_primary.servers_client.delete_server, + client.delete_server, server['server']['id']) return server @@ -93,12 +112,16 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): client = client or cls.os_primary.keypairs_client name = data_utils.rand_name('keypair-test') body = client.create_keypair(name=name) - cls.keypairs.append(body['keypair']) + body.update(client=client) + if client is cls.os_primary.keypairs_client: + cls.keypairs.append(body) + return body['keypair'] @classmethod - def create_secgroup_rules(cls, rule_list, secgroup_id=None): - client = cls.os_primary.network_client + def create_secgroup_rules(cls, rule_list, client=None, + secgroup_id=None): + client = client or cls.os_primary.network_client if not secgroup_id: sgs = client.list_security_groups()['security_groups'] for sg in sgs: @@ -114,7 +137,8 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): **rule) @classmethod - def create_loginable_secgroup_rule(cls, secgroup_id=None): + def create_loginable_secgroup_rule(cls, secgroup_id=None, + client=None): """This rule is intended to permit inbound ssh Allowing ssh traffic traffic from all sources, so no group_id is @@ -128,10 +152,13 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): 'port_range_min': 22, 'port_range_max': 22, 'remote_ip_prefix': '0.0.0.0/0'}] - cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id) + client = client or cls.os_primary.network_client + cls.create_secgroup_rules(rule_list, client, + secgroup_id=secgroup_id) @classmethod - def create_pingable_secgroup_rule(cls, secgroup_id=None): + def create_pingable_secgroup_rule(cls, secgroup_id=None, + client=None): """This rule is intended to permit inbound ping """ @@ -140,7 +167,9 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): 'port_range_min': 8, # type 'port_range_max': 0, # code 'remote_ip_prefix': '0.0.0.0/0'}] - cls.create_secgroup_rules(rule_list, secgroup_id=secgroup_id) + client = client or cls.os_primary.network_client + cls.create_secgroup_rules(rule_list, client, + secgroup_id=secgroup_id) @classmethod def create_router_by_client(cls, is_admin=False, **kwargs): @@ -155,11 +184,13 @@ class BaseTempestTestCase(base_api.BaseNetworkTest): cls.routers.append(router) return router - def create_and_associate_floatingip(self, port_id): - fip = self.os_primary.network_client.create_floatingip( + def create_and_associate_floatingip(self, port_id, client=None): + client = client or self.os_primary.network_client + fip = client.create_floatingip( CONF.network.public_network_id, port_id=port_id)['floatingip'] - self.floating_ips.append(fip) + if client is self.os_primary.network_client: + self.floating_ips.append(fip) return fip def setup_network_and_server(self, router=None, **kwargs):