From d8850af7f8cec2418dc279fcdd6dbf98990cfc08 Mon Sep 17 00:00:00 2001 From: Georg Kunz Date: Sat, 18 Feb 2017 00:59:39 +0100 Subject: Finalizing Gluon vping test case Finalizing Gluon vping test case by implementing instance creation and userdata-based ping between instances. Note: DHCP is not supported. This requires static configuration of IPs and routes in the instances by means of user data. Moreover the test case takes slightly longer because of a DHCP timeout during boot up. Change-Id: I9c9ef658005ba14f30e28b44dba82aa1b9a6daad Signed-off-by: Georg Kunz --- test/functest/gluon-test-suite.py | 221 ++++++++++++++++++++++++++++++++++---- 1 file changed, 202 insertions(+), 19 deletions(-) diff --git a/test/functest/gluon-test-suite.py b/test/functest/gluon-test-suite.py index b26d806..2214be6 100755 --- a/test/functest/gluon-test-suite.py +++ b/test/functest/gluon-test-suite.py @@ -3,6 +3,7 @@ from opnfv.deployment import factory import functest.utils.openstack_utils as osutils import functest.utils.functest_logger as ft_logger +from functest.utils.constants import CONST import json import logging @@ -10,7 +11,7 @@ import os import sys import time -logger = ft_logger.Logger("netready-gluon-vping").getLogger() +logger = ft_logger.Logger('netready-gluon-vping').getLogger() logger.setLevel(logging.DEBUG) @@ -21,6 +22,13 @@ class GluonVPing: self.port_ids = [] self.vpn_id = None self.vpnbinding_ids = [] + self.novaclient = osutils.get_nova_client() + self.neutronclient = osutils.get_neutron_client() + self.image_id = None + self.flavor_id = None + self.instance_1 = None + self.instance_2 = None + self.vm_boot_timeout = 180 def prepare_test(self): logger.info("Preparing Gluon vPing testcase.") @@ -42,12 +50,12 @@ class GluonVPing: raise Exception('No controller node found') def run(self): + self._prepare_image() + self._prepare_flavor() self._create_ports() self._create_vpn() self._create_vpnbinding() - # - # TODO boot VMs and ping - # + self._boot_vms() def _run_protonclient(self, options): cmd = "protonclient --api net-l3vpn " + options @@ -73,13 +81,12 @@ class GluonVPing: ' --tenant_id ' + tenant_id tenant_id = osutils.get_tenant_id( - osutils.get_keystone_client(), - 'admin') + osutils.get_keystone_client(), + 'admin') for i in [1, 2]: - options = _build_create_port_options( - 'gluonPort' + str(i), - 'c0:2a:14:04:43:0' + str(i), - tenant_id) + options = _build_create_port_options('gluonPort' + str(i), + 'c0:2a:14:04:43:0' + str(i), + tenant_id) output = self._run_protonclient(options) port_data = json.loads(output) @@ -89,10 +96,10 @@ class GluonVPing: def _create_vpn(self): vpn_options = 'vpn-create '\ - '--name "GluonVPN" ' \ - '--ipv4_family 1000:1000 ' \ - '--ipv6_family 1000:1000 ' \ - '--route_distinguishers 1000:1000' + '--name "GluonVPN" ' \ + '--ipv4_family 1000:1000 ' \ + '--ipv6_family 1000:1000 ' \ + '--route_distinguishers 1000:1000' output = self._run_protonclient(vpn_options) vpn_data = json.loads(output) @@ -103,11 +110,11 @@ class GluonVPing: def _create_vpnbinding(self): def _build_create_vpnbinding(port_id, counter): return 'vpnbinding-create' \ - ' --interface_id ' + port_id + \ - ' --service_id ' + self.vpn_id + \ - ' --ipaddress 10.10.' + str(counter) + '.2' \ - ' --subnet_prefix 24' \ - ' --gateway 10.10.' + str(counter) + '.1' + ' --interface_id ' + port_id + \ + ' --service_id ' + self.vpn_id + \ + ' --ipaddress 10.10.' + str(counter) + '.2' \ + ' --subnet_prefix 24' \ + ' --gateway 10.10.' + str(counter) + '.1' counter = 0 for port_id in self.port_ids: @@ -137,9 +144,185 @@ class GluonVPing: def cleanup(self): logger.info("Cleaning up Gluon vPing testcase...") + self._cleanup_instances() self._cleanup_vpnbindings() self._cleanup_vpn() self._cleanup_ports() + self._cleanup_image() + self._cleanup_flavor() + + def _prepare_image(self): + image_name = 'gluon-test-image' + self.image_id = osutils.get_image_id(osutils.get_glance_client(), + image_name) + if self.image_id == '': + image_filename = CONST.openstack_image_file_name + image_format = CONST.openstack_image_disk_format + image_path = '{0}/{1}'.format(CONST.dir_functest_data, + image_filename) + + logger.info('Creating Glance image.') + self.image_id = \ + osutils.create_glance_image(osutils.get_glance_client(), + image_name, + image_path, + image_format, + container='bare', + public='public') + if self.image_id is None: + raise Exception('Glance image creation failed') + else: + logger.info('Test image %s already exists' % image_name) + + def _cleanup_image(self): + logger.info('Deleting Glance image.') + if not osutils.delete_glance_image(self.novaclient, self.image_id): + raise Exception('Image deletion failed.') + + def _prepare_flavor(self): + logger.info('Creating Nova flavor.') + self.flavor_id = osutils.get_flavor_id(self.novaclient, 'gluon-flavor') + if self.flavor_id == '': + self.flavor_id = osutils.create_flavor(self.novaclient, + 'gluon-flavor', + 512, 1, 1) + if self.flavor_id is None: + raise Exception('Nova flavor creation failed') + + def _cleanup_flavor(self): + logger.info('Deleting flavor') + self.novaclient.flavors.delete(self.flavor_id) + + def _wait_for_instance(self, instance): + if not self._wait_for_instance_status_change(self.novaclient, + instance): + vm_status = osutils.get_instance_status(self.novaclient, instance) + logger.error("Instance '%s' cannot be booted. Status is '%s'" + % (instance, vm_status)) + return None + else: + logger.info("Instance '%s' is ACTIVE." % instance) + + def _wait_for_instance_status_change(self, nova, vm): + sleep_time = 3 + count = self.vm_boot_timeout / sleep_time + while True: + status = osutils.get_instance_status(nova, vm) + logger.debug("Status: %s" % status) + if status == "ACTIVE": + return True + if status == "ERROR" or status == "error": + return False + if count == 0: + logger.debug("Booting a VM timed out...") + return False + count -= 1 + time.sleep(sleep_time) + + def _build_user_data(self, instance_ip, gw_ip, test_ip): + return ("#!/bin/sh\n\n" + "killall -9 udhcpc\n" + "ifconfig eth0 %s netmask 255.255.255.0\n" + "route add default gw %s\n" + "while true; do\n" + " ping -c 1 %s 2>&1 >/dev/null\n" + " RES=$?\n" + " if [ \"Z$RES\" = \"Z0\" ] ; then\n" + " echo 'vPing OK'\n" + " break\n" + " else\n" + " echo 'vPing KO'\n" + " fi\n" + " sleep 1\n" + "done\n" % (instance_ip, gw_ip, test_ip)) + + def _boot_vms(self): + neutron_ports = self.neutronclient.list_ports() + neutron_port_ids = [p['id'] for p in neutron_ports['ports']] + logger.info("Neutron port IDs: %s" % neutron_port_ids) + + for port_id in self.port_ids: + if port_id not in neutron_port_ids: + raise Exception('Gluon port %s not found among existing ' + 'Neutron ports' % port_id) + + ip_instance_1 = '10.10.0.2' + gw_instance_1 = '10.10.0.1' + + ip_instance_2 = '10.10.1.2' + gw_instance_2 = '10.10.1.1' + + logger.info('Booting first instance.') + user_data_1 = self._build_user_data(ip_instance_1, + gw_instance_1, + ip_instance_2) + self.instance_1 = \ + self.novaclient.servers.create(name='Gluon VM 1', + flavor=self.flavor_id, + image=self.image_id, + nics= + [{"port-id": self.port_ids[0]}], + config_drive=True, + userdata=user_data_1) + self._wait_for_instance(self.instance_1) + + logger.info('Booting second instance.') + user_data_2 = self._build_user_data(ip_instance_2, + gw_instance_2, + ip_instance_1) + self.instance_2 = \ + self.novaclient.servers.create(name='Gluon VM 2', + flavor=self.flavor_id, + image=self.image_id, + nics= + [{"port-id": self.port_ids[1]}], + config_drive=True, + userdata=user_data_2) + self._wait_for_instance(self.instance_2) + + error_code = self._check_ping(self.instance_1, ip_instance_2, 200) + if error_code != 0: + raise Exception('Ping of instance 1 to %s failed' % ip_instance_2) + + error_code = self._check_ping(self.instance_2, ip_instance_1, 200) + if error_code != 0: + raise Exception('Ping of instance 2 to %s failed' % ip_instance_1) + + def _check_ping(self, instance, test_ip, timeout): + logger.info("Waiting for ping...") + EXIT_CODE = -1 + sec = 0 + tries = 0 + + while True: + time.sleep(1) + p_console = instance.get_console_output() + if "vPing OK" in p_console: + logger.info("vPing detected!") + EXIT_CODE = 0 + break + elif "failed to read iid from metadata" in p_console or tries > 5: + EXIT_CODE = -2 + break + elif sec == timeout: + logger.info("Timeout reached.") + break + elif sec % 10 == 0: + if "request failed" in p_console: + logger.debug("It seems userdata is not supported " + "in nova boot. Waiting a bit...") + tries += 1 + else: + logger.debug("Pinging %s. Waiting for response..." + % test_ip) + sec += 1 + + return EXIT_CODE + + def _cleanup_instances(self): + logger.info('Deleting instances.') + self.novaclient.servers.delete(self.instance_1.id) + self.novaclient.servers.delete(self.instance_2.id) def main(): -- cgit 1.2.3-korg