Commit 086a8d8c authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis
Browse files

burnin: Implement NetworkTestSuite

parent 3e7beabb
......@@ -46,6 +46,7 @@ from synnefo_tools.burnin.images_tests import \
FlavorsTestSuite, ImagesTestSuite
from synnefo_tools.burnin.pithos_tests import PithosTestSuite
from synnefo_tools.burnin.server_tests import ServerTestSuite
from synnefo_tools.burnin.network_tests import NetworkTestSuite
from synnefo_tools.burnin.stale_tests import \
StaleServersTestSuite, StaleNetworksTestSuite
......@@ -58,6 +59,7 @@ TESTSUITES = [
ImagesTestSuite,
PithosTestSuite,
ServerTestSuite,
NetworkTestSuite,
]
TSUITES_NAMES = [tsuite.__name__ for tsuite in TESTSUITES]
......@@ -253,12 +255,13 @@ def main():
(opts, _) = parse_arguments(sys.argv[1:])
# Initialize burnin
testsuites = common.initialize(opts, TSUITES_NAMES, STALE_TSUITES_NAMES)
(testsuites, failfast) = \
common.initialize(opts, TSUITES_NAMES, STALE_TSUITES_NAMES)
testsuites = string_to_class(testsuites)
# Run burnin
# The return value denotes the success status
return common.run_burnin(testsuites, failfast=opts.failfast,
return common.run_burnin(testsuites, failfast=failfast,
final_report=opts.final_report)
......
......@@ -475,7 +475,7 @@ def initialize(opts, testsuites, stale_testsuites):
# Choose tests to run
if opts.show_stale:
# We will run the stale_testsuites
return stale_testsuites
return (stale_testsuites, True)
if opts.tests != "all":
testsuites = opts.tests
......@@ -483,7 +483,7 @@ def initialize(opts, testsuites, stale_testsuites):
testsuites = [tsuite for tsuite in testsuites
if tsuite not in opts.exclude_tests]
return testsuites
return (testsuites, opts.failfast)
# --------------------------------------------------------------------
......
......@@ -52,7 +52,7 @@ from synnefo_tools.burnin.common import BurninTests
# Too many public methods. pylint: disable-msg=R0904
class CycladesTests(BurninTests):
"""Extends the BurninTests class for Cyclades"""
def _ry_until_timeout_expires(self, opmsg, check_fun):
def _try_until_timeout_expires(self, opmsg, check_fun):
"""Try to perform an action until timeout expires"""
assert callable(check_fun), "Not a function"
......@@ -95,24 +95,34 @@ class CycladesTests(BurninTests):
self.info("Getting simple list of servers")
return self.clients.cyclades.list_servers(detail=detail)
def _get_server_details(self, server):
def _get_list_of_networks(self, detail=False):
"""Get (detailed) list of networks"""
if detail:
self.info("Getting detailed list of networks")
else:
self.info("Getting simple list of networks")
return self.clients.cyclades.list_networks(detail=detail)
def _get_server_details(self, server, quiet=False):
"""Get details for a server"""
self.info("Getting details for server %s with id %s",
server['name'], server['id'])
if not quiet:
self.info("Getting details for server %s with id %s",
server['name'], server['id'])
return self.clients.cyclades.get_server_details(server['id'])
def _create_server(self, name, image, flavor, personality):
def _create_server(self, image, flavor, personality=None):
"""Create a new server"""
self.info("Creating a server with name %s", name)
servername = "%s for %s" % (self.run_id, image['name'])
self.info("Creating a server with name %s", servername)
self.info("Using image %s with id %s", image['name'], image['id'])
self.info("Using flavor %s with id %s", flavor['name'], flavor['id'])
server = self.clients.cyclades.create_server(
name, flavor['id'], image['id'], personality=personality)
servername, flavor['id'], image['id'], personality=personality)
self.info("Server id: %s", server['id'])
self.info("Server password: %s", server['adminPass'])
self.assertEqual(server['name'], name)
self.assertEqual(server['name'], servername)
self.assertEqual(server['flavor']['id'], flavor['id'])
self.assertEqual(server['image']['id'], image['id'])
self.assertEqual(server['status'], "BUILD")
......@@ -144,22 +154,61 @@ class CycladesTests(BurninTests):
self.info("User's login name: %s", ret_user)
return ret_user
def _insist_on_server_transition(self, server, curr_status, new_status):
"""Insist on server transiting from curr_status to new_status"""
def _insist_on_server_transition(self, server, curr_statuses, new_status):
"""Insist on server transiting from curr_statuses to new_status"""
def check_fun():
"""Check server status"""
srv = self.clients.cyclades.get_server_details(server['id'])
if srv['status'] == curr_status:
srv = self._get_server_details(server, quiet=True)
if srv['status'] in curr_statuses:
raise Retry()
elif srv['status'] == new_status:
return
else:
msg = "Server %s went to unexpected status %s"
self.error(msg, server['name'], srv['status'])
self.fail(msg % (server['name'], srv['status']))
opmsg = "Waiting for server %s to transit from %s to %s"
self.info(opmsg, server['name'], curr_status, new_status)
opmsg = opmsg % (server['name'], curr_status, new_status)
msg = "Server \"%s\" with id %s went to unexpected status %s"
self.error(msg, server['name'], server['id'], srv['status'])
self.fail(msg % (server['name'], server['id'], srv['status']))
opmsg = "Waiting for server \"%s\" to become %s"
self.info(opmsg, server['name'], new_status)
opmsg = opmsg % (server['name'], new_status)
self._try_until_timeout_expires(opmsg, check_fun)
def _insist_on_network_transition(self, network,
curr_statuses, new_status):
"""Insist on network transiting from curr_statuses to new_status"""
def check_fun():
"""Check network status"""
ntw = self.clients.cyclades.get_network_details(network['id'])
if ntw['status'] in curr_statuses:
raise Retry()
elif ntw['status'] == new_status:
return
else:
msg = "Network %s with id %s went to unexpected status %s"
self.error(msg, network['name'], network['id'], ntw['status'])
self.fail(msg %
(network['name'], network['id'], ntw['status']))
opmsg = "Waiting for network \"%s\" to become %s"
self.info(opmsg, network['name'], new_status)
opmsg = opmsg % (network['name'], new_status)
self._try_until_timeout_expires(opmsg, check_fun)
def _insist_on_network_connection(self, server, network, disconnect=False):
"""Insist that the server has connected to the network"""
def check_fun():
"""Check network connection"""
dsrv = self._get_server_details(server, quiet=True)
nets = [s['network_id'] for s in dsrv['attachments']]
if not disconnect and network['id'] not in nets:
raise Retry()
if disconnect and network['id'] in nets:
raise Retry()
if disconnect:
opmsg = \
"Waiting for server \"%s\" to disconnect from network \"%s\""
else:
opmsg = "Waiting for server \"%s\" to connect to network \"%s\""
self.info(opmsg, server['name'], network['name'])
opmsg = opmsg % (server['name'], network['name'])
self._try_until_timeout_expires(opmsg, check_fun)
def _insist_on_tcp_connection(self, family, host, port):
......@@ -192,21 +241,36 @@ class CycladesTests(BurninTests):
opmsg = opmsg % (familystr.get(family, "Unknown"), host, port)
return self._try_until_timeout_expires(opmsg, check_fun)
def _get_ip(self, server, version=4):
"""Get the public IP of a server from the detailed server info"""
def _get_ip(self, server, version=4, network=None):
"""Get the IP of a server from the detailed server info
If network not given then get the public IP. Else the ip
attached to that network
"""
assert version in (4, 6)
nics = server['attachments']
public_addrs = None
addrs = None
for nic in nics:
net_id = nic['network_id']
if self.clients.cyclades.get_network_details(net_id)['public']:
public_addrs = nic['ipv' + str(version)]
if network is None:
if self.clients.cyclades.get_network_details(net_id)['public']:
addrs = nic['ipv' + str(version)]
break
else:
if net_id == network['id']:
addrs = nic['ipv%s' % version]
break
self.assertIsNotNone(public_addrs)
msg = "Server's public IPv%s is %s"
self.info(msg, version, public_addrs)
return public_addrs
self.assertIsNotNone(addrs, "Can not get IP from server attachments")
if network is None:
msg = "Server's public IPv%s is %s"
self.info(msg, version, addrs)
else:
msg = "Server's IPv%s attached to network \"%s\" is %s"
self.info(msg, version, network['id'], addrs)
return addrs
def _insist_on_ping(self, ip_addr, version=4):
"""Test server responds to a single IPv4 of IPv6 ping"""
......@@ -284,6 +348,16 @@ class CycladesTests(BurninTests):
remote_content = base64.b64encode(ftmp.read())
self.assertEqual(content, remote_content)
def _disconnect_from_network(self, server, network):
"""Disconnect server from network"""
nid = None
for nic in server['attachments']:
if nic['network_id'] == network['id']:
nid = nic['id']
break
self.assertIsNotNone(nid, "Could not find network card")
self.clients.cyclades.disconnect_server(server['id'], nid)
class Retry(Exception):
"""Retry the action
......
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
"""
This is the burnin class that tests the Networks' functionality
"""
import random
from synnefo_tools.burnin.common import Proper
from synnefo_tools.burnin.cyclades_common import CycladesTests
# Too many public methods. pylint: disable-msg=R0904
class NetworkTestSuite(CycladesTests):
"""Test Networking in Cyclades"""
avail_images = Proper(value=None)
avail_flavors = Proper(value=None)
server_a = Proper(value=None)
server_b = Proper(value=None)
network = Proper(value=None)
def test_001_images_to_use(self):
"""Find images to be used to create our machines"""
if self.images is None:
self.info("No --images given. Will use the default %s",
"^Debian Base$")
filters = ["name:^Debian Base$"]
else:
filters = self.images
self.avail_images = self._find_images(filters)
self.info("Found %s images to choose from", len(self.avail_images))
def test_002_flavors_to_use(self):
"""Find flavors to be used to create our machines"""
flavors = self._get_list_of_flavors(detail=True)
if self.flavors is None:
self.info("No --flavors given. Will use all of them")
self.avail_flavors = flavors
else:
self.avail_flavors = self._find_flavors(
self.flavors, flavors=flavors)
self.info("Found %s flavors to choose from", len(self.avail_flavors))
def test_003_submit_create_server_a(self):
"""Submit create server request for server A"""
use_image = random.choice(self.avail_images)
use_flavor = random.choice(self.avail_flavors)
server = self._create_server(use_image, use_flavor)
self.server_a = {}
self.server_a['server'] = server
self.server_a['image'] = use_image
self.server_a['flavor'] = use_flavor
self.server_a['username'] = self._get_connection_username(server)
self.server_a['password'] = server['adminPass']
def test_004_submit_create_server_b(self):
"""Submit create server request for server B"""
use_image = random.choice(self.avail_images)
use_flavor = random.choice(self.avail_flavors)
server = self._create_server(use_image, use_flavor)
self.server_b = {}
self.server_b['server'] = server
self.server_b['image'] = use_image
self.server_b['flavor'] = use_flavor
self.server_b['username'] = self._get_connection_username(server)
self.server_b['password'] = server['adminPass']
def test_005_server_a_active(self):
"""Test that server A becomes ACTIVE"""
self._insist_on_server_transition(
self.server_a['server'], ["BUILD"], "ACTIVE")
def test_005_server_b_active(self):
"""Test that server B becomes ACTIVE"""
self._insist_on_server_transition(
self.server_b['server'], ["BUILD"], "ACTIVE")
def test_006_create_network(self):
"""Submit a create network request"""
name = self.run_id
self.network = self.clients.cyclades.create_network(
name, cidr="10.0.1.0/28", dhcp=True)
self.info("Network with id %s created", self.network['id'])
#Test if right the name is assigned
self.assertEqual(self.network['name'], name)
self._insist_on_network_transition(
self.network, ["BUILD"], "ACTIVE")
def test_007_connect_to_network(self):
"""Connect the two VMs to the newly created network"""
self.clients.cyclades.connect_server(
self.server_a['server']['id'], self.network['id'])
self.clients.cyclades.connect_server(
self.server_b['server']['id'], self.network['id'])
self._insist_on_network_connection(
self.server_a['server'], self.network)
self._insist_on_network_connection(
self.server_b['server'], self.network)
# Update servers
self.server_a['server'] = self._get_server_details(
self.server_a['server'])
self.server_b['server'] = self._get_server_details(
self.server_b['server'])
# Check that servers got private IPs
self.server_a['pr_ipv4'] = self._get_ip(
self.server_a['server'], network=self.network)
self.server_b['pr_ipv4'] = self._get_ip(
self.server_b['server'], network=self.network)
def test_008_reboot_server_a(self):
"""Rebooting server A"""
self.clients.cyclades.shutdown_server(self.server_a['server']['id'])
self._insist_on_server_transition(
self.server_a['server'], ["ACTIVE"], "STOPPED")
self.clients.cyclades.start_server(self.server_a['server']['id'])
self._insist_on_server_transition(
self.server_a['server'], ["STOPPED"], "ACTIVE")
def test_009_ping_server_a(self):
"""Test if server A responds to IPv4 pings"""
self._insist_on_ping(self._get_ip(self.server_a['server']))
def test_010_reboot_server_b(self):
"""Rebooting server B"""
self.clients.cyclades.shutdown_server(self.server_b['server']['id'])
self._insist_on_server_transition(
self.server_b['server'], ["ACTIVE"], "STOPPED")
self.clients.cyclades.start_server(self.server_b['server']['id'])
self._insist_on_server_transition(
self.server_b['server'], ["STOPPED"], "ACTIVE")
def test_011_ping_server_b(self):
"""Test that server B responds to IPv4 pings"""
self._insist_on_ping(self._get_ip(self.server_b['server']))
def test_012_test_connection_exists(self):
"""Ping server B from server A to test if connection exists"""
self._skip_if(not self._image_is(self.server_a['image'], "linux"),
"only valid for Linux servers")
self._skip_if(not self._image_is(self.server_b['image'], "linux"),
"only valid for Linux servers")
server_a_public_ip = self._get_ip(self.server_a['server'])
server_b_private_ip = self._get_ip(
self.server_b['server'], network=self.network)
msg = "Will try to connect to server A (%s) and ping to server B (%s)"
self.info(msg, server_a_public_ip, server_b_private_ip)
cmd = "for i in {1..7}; do if ping -c 3 -w 20 %s > /dev/null; " \
"then echo 'True'; break; fi; done" % server_b_private_ip
lines, status = self._ssh_execute(
server_a_public_ip, self.server_a['username'],
self.server_a['password'], cmd)
self.assertEqual(status, 0)
self.assertEqual(lines, ['True\n'])
def test_013_disconnect_network(self):
"""Disconnecting servers A and B from network"""
self._disconnect_from_network(self.server_a['server'], self.network)
self._disconnect_from_network(self.server_b['server'], self.network)
self._insist_on_network_connection(
self.server_a['server'], self.network, disconnect=True)
self._insist_on_network_connection(
self.server_b['server'], self.network, disconnect=True)
def test_014_destroy_network(self):
"""Submit delete network request"""
self.clients.cyclades.delete_network(self.network['id'])
self._insist_on_network_transition(
self.network, ["ACTIVE"], "DELETED")
networks = [net['id'] for net in self._get_list_of_networks()]
self.assertNotIn(self.network['id'], networks)
def test_015_cleanup_servers(self):
"""Cleanup servers created for this test"""
self.clients.cyclades.delete_server(self.server_a['server']['id'])
self.clients.cyclades.delete_server(self.server_b['server']['id'])
self._insist_on_server_transition(
self.server_a['server'], ["ACTIVE"], "DELETED")
self._insist_on_server_transition(
self.server_b['server'], ["ACTIVE"], "DELETED")
......@@ -75,11 +75,10 @@ class GeneratedServerTestSuite(CycladesTests):
'mode': stat.S_IRUSR | stat.S_IWUSR,
'contents': base64.b64encode("This is a personality file")
}]
servername = "%s for %s" % (self.run_id, self.use_image['name'])
self.use_flavor = random.choice(self.avail_flavors)
self.server = self._create_server(
servername, self.use_image, self.use_flavor, self.personality)
self.use_image, self.use_flavor, self.personality)
self.username = self._get_connection_username(self.server)
self.password = self.server['adminPass']
......@@ -118,7 +117,7 @@ class GeneratedServerTestSuite(CycladesTests):
def test_005_server_becomes_active(self):
"""Test server becomes ACTIVE"""
self._insist_on_server_transition(self.server, "BUILD", "ACTIVE")
self._insist_on_server_transition(self.server, ["BUILD"], "ACTIVE")
def test_006_get_server_oob_console(self):
"""Test getting OOB server console over VNC
......@@ -189,7 +188,7 @@ class GeneratedServerTestSuite(CycladesTests):
def test_012_server_becomes_stopped(self):
"""Test server becomes STOPPED"""
self._insist_on_server_transition(self.server, "ACTIVE", "STOPPED")
self._insist_on_server_transition(self.server, ["ACTIVE"], "STOPPED")
def test_013_submit_start(self):
"""Test submit start server request"""
......@@ -197,7 +196,7 @@ class GeneratedServerTestSuite(CycladesTests):
def test_014_server_becomes_active(self):
"""Test server becomes ACTIVE again"""
self._insist_on_server_transition(self.server, "STOPPED", "ACTIVE")
self._insist_on_server_transition(self.server, ["STOPPED"], "ACTIVE")
def test_015_server_ping_ipv4(self):
"""Test server OS is actually up and running again"""
......@@ -262,7 +261,7 @@ class GeneratedServerTestSuite(CycladesTests):
def test_022_server_becomes_deleted(self):
"""Test server becomes DELETED"""
self._insist_on_server_transition(self.server, "ACTIVE", "DELETED")
self._insist_on_server_transition(self.server, ["ACTIVE"], "DELETED")
def test_023_server_no_longer(self):
"""Test server is no longer in server list"""
......@@ -309,7 +308,7 @@ class ServerTestSuite(BurninTests):
self.avail_flavors = flavors
else:
self.avail_flavors = self._find_flavors(
patterns=self.flavors, flavors=flavors)
self.flavors, flavors=flavors)
self.info("Found %s flavors to choose from", len(self.avail_flavors))
def test_003_create_testsuites(self):
......
......@@ -77,12 +77,46 @@ class StaleServersTestSuite(CycladesTests):
self.clients.cyclades.delete_server(stl['id'])
for stl in self.stale_servers:
self._insist_on_server_transition(stl, "ACTIVE", "DELETED")
self._insist_on_server_transition(
stl, ["ACTIVE", "ERROR"], "DELETED")
# Too many public methods. pylint: disable-msg=R0904
class StaleNetworksTestSuite(CycladesTests):
"""Handle stale Networks"""
stale_networks = Proper(value=None)
def test_001_show_stale_networks(self):
"""Show staled networks (networks left from previous runs)"""
return
networks = self._get_list_of_networks()
self.stale_networks = [n for n in networks
if n['name'].startswith(SNF_TEST_PREFIX)]
len_stale = len(self.stale_networks)
if len_stale == 0:
self.info("No stale networks found")
return
self.info("Found %s stale networks:", len_stale)
for stl in self.stale_networks:
self.info(" Network \"%s\" with id %s", stl['name'], stl['id'])
def test_002_delete_stale_networks(self):
"""Delete staled networks (networks left from previous runs)"""
len_stale = len(self.stale_networks)
if not self.delete_stale and len_stale != 0:
msg = "Use --delete-stale flag to delete stale networks"
self.error(msg)
self.fail(msg)
elif len_stale == 0:
self.info("No stale networks found")
else:
self.info("Deleting %s stale networks:", len_stale)
for stl in self.stale_networks:
self.info(" Deleting network \"%s\" with id %s",
stl['name'], stl['id'])
self.clients.cyclades.delete_network(stl['id'])
for stl in self.stale_networks:
self._insist_on_network_transition(
stl, ["ACTIVE", "ERROR"], "DELETED")
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment