Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / tools / burnin.py @ 65462ca9

History | View | Annotate | Download (43.3 kB)

1 65462ca9 John Giannelos
#!/usr/bin/env python
2 5a140b23 Vangelis Koukis
3 5a140b23 Vangelis Koukis
# Copyright 2011 GRNET S.A. All rights reserved.
4 5a140b23 Vangelis Koukis
#
5 5a140b23 Vangelis Koukis
# Redistribution and use in source and binary forms, with or
6 5a140b23 Vangelis Koukis
# without modification, are permitted provided that the following
7 5a140b23 Vangelis Koukis
# conditions are met:
8 5a140b23 Vangelis Koukis
#
9 5a140b23 Vangelis Koukis
#   1. Redistributions of source code must retain the above
10 5a140b23 Vangelis Koukis
#      copyright notice, this list of conditions and the following
11 5a140b23 Vangelis Koukis
#      disclaimer.
12 5a140b23 Vangelis Koukis
#
13 5a140b23 Vangelis Koukis
#   2. Redistributions in binary form must reproduce the above
14 5a140b23 Vangelis Koukis
#      copyright notice, this list of conditions and the following
15 5a140b23 Vangelis Koukis
#      disclaimer in the documentation and/or other materials
16 5a140b23 Vangelis Koukis
#      provided with the distribution.
17 5a140b23 Vangelis Koukis
#
18 5a140b23 Vangelis Koukis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 5a140b23 Vangelis Koukis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 5a140b23 Vangelis Koukis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 5a140b23 Vangelis Koukis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 5a140b23 Vangelis Koukis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 5a140b23 Vangelis Koukis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 5a140b23 Vangelis Koukis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 5a140b23 Vangelis Koukis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 5a140b23 Vangelis Koukis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 5a140b23 Vangelis Koukis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 5a140b23 Vangelis Koukis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 5a140b23 Vangelis Koukis
# POSSIBILITY OF SUCH DAMAGE.
30 5a140b23 Vangelis Koukis
#
31 5a140b23 Vangelis Koukis
# The views and conclusions contained in the software and
32 5a140b23 Vangelis Koukis
# documentation are those of the authors and should not be
33 5a140b23 Vangelis Koukis
# interpreted as representing official policies, either expressed
34 5a140b23 Vangelis Koukis
# or implied, of GRNET S.A.
35 5a140b23 Vangelis Koukis
36 5a140b23 Vangelis Koukis
"""Perform integration testing on a running Synnefo deployment"""
37 5a140b23 Vangelis Koukis
38 21bbbc9b Vangelis Koukis
import __main__
39 5a140b23 Vangelis Koukis
import datetime
40 5a140b23 Vangelis Koukis
import inspect
41 5a140b23 Vangelis Koukis
import logging
42 5a140b23 Vangelis Koukis
import os
43 5a140b23 Vangelis Koukis
import paramiko
44 21bbbc9b Vangelis Koukis
import prctl
45 5a140b23 Vangelis Koukis
import subprocess
46 21bbbc9b Vangelis Koukis
import signal
47 5a140b23 Vangelis Koukis
import socket
48 bc14ba88 Vangelis Koukis
import struct
49 5a140b23 Vangelis Koukis
import sys
50 5a140b23 Vangelis Koukis
import time
51 77054bf5 John Giannelos
import hashlib
52 9659e075 John Giannelos
from base64 import b64encode
53 9659e075 John Giannelos
from pwd import getpwuid
54 9659e075 John Giannelos
from grp import getgrgid
55 5a140b23 Vangelis Koukis
from IPy import IP
56 21bbbc9b Vangelis Koukis
from multiprocessing import Process, Queue
57 bc14ba88 Vangelis Koukis
from random import choice
58 21bbbc9b Vangelis Koukis
59 1c636ad6 John Giannelos
from kamaki.clients import ClientError, ComputeClient, CycladesClient
60 1c636ad6 John Giannelos
from kamaki.config import Config
61 1c636ad6 John Giannelos
62 bc14ba88 Vangelis Koukis
from vncauthproxy.d3des import generate_response as d3des_generate_response
63 5a140b23 Vangelis Koukis
64 5a140b23 Vangelis Koukis
# Use backported unittest functionality if Python < 2.7
65 5a140b23 Vangelis Koukis
try:
66 5a140b23 Vangelis Koukis
    import unittest2 as unittest
67 5a140b23 Vangelis Koukis
except ImportError:
68 bc14ba88 Vangelis Koukis
    if sys.version_info < (2, 7):
69 bc14ba88 Vangelis Koukis
        raise Exception("The unittest2 package is required for Python < 2.7")
70 5a140b23 Vangelis Koukis
    import unittest
71 5a140b23 Vangelis Koukis
72 5a140b23 Vangelis Koukis
73 21bbbc9b Vangelis Koukis
API = None
74 21bbbc9b Vangelis Koukis
TOKEN = None
75 38d247df Kostas Papadimitriou
DEFAULT_API = "http://127.0.0.1:8000/api/v1.1"
76 38d247df Kostas Papadimitriou
77 5a140b23 Vangelis Koukis
# A unique id identifying this test run
78 21bbbc9b Vangelis Koukis
TEST_RUN_ID = datetime.datetime.strftime(datetime.datetime.now(),
79 21bbbc9b Vangelis Koukis
                                         "%Y%m%d%H%M%S")
80 21bbbc9b Vangelis Koukis
SNF_TEST_PREFIX = "snf-test-"
81 5a140b23 Vangelis Koukis
82 5a140b23 Vangelis Koukis
# Setup logging (FIXME - verigak)
83 5a140b23 Vangelis Koukis
logging.basicConfig(format="%(message)s")
84 00f87624 Vangelis Koukis
log = logging.getLogger("burnin")
85 5a140b23 Vangelis Koukis
log.setLevel(logging.INFO)
86 5a140b23 Vangelis Koukis
87 5a140b23 Vangelis Koukis
class UnauthorizedTestCase(unittest.TestCase):
88 5a140b23 Vangelis Koukis
    def test_unauthorized_access(self):
89 5a140b23 Vangelis Koukis
        """Test access without a valid token fails"""
90 1c636ad6 John Giannelos
        falseToken = '12345'
91 1c636ad6 John Giannelos
        conf = Config()
92 1c636ad6 John Giannelos
        conf.set('compute_token', falseToken)
93 99d41650 John Giannelos
        c=ComputeClient(conf)
94 1c636ad6 John Giannelos
95 5a140b23 Vangelis Koukis
        with self.assertRaises(ClientError) as cm:
96 5a140b23 Vangelis Koukis
            c.list_servers()
97 5a140b23 Vangelis Koukis
        self.assertEqual(cm.exception.status, 401)
98 5a140b23 Vangelis Koukis
99 5a140b23 Vangelis Koukis
100 5a140b23 Vangelis Koukis
class ImagesTestCase(unittest.TestCase):
101 5a140b23 Vangelis Koukis
    """Test image lists for consistency"""
102 5a140b23 Vangelis Koukis
    @classmethod
103 5a140b23 Vangelis Koukis
    def setUpClass(cls):
104 5a140b23 Vangelis Koukis
        """Initialize kamaki, get (detailed) list of images"""
105 5a140b23 Vangelis Koukis
        log.info("Getting simple and detailed list of images")
106 1c636ad6 John Giannelos
107 1c636ad6 John Giannelos
        conf = Config()
108 1c636ad6 John Giannelos
        conf.set('compute_token', TOKEN)
109 1c636ad6 John Giannelos
        cls.client = ComputeClient(conf)
110 5a140b23 Vangelis Koukis
        cls.images = cls.client.list_images()
111 5a140b23 Vangelis Koukis
        cls.dimages = cls.client.list_images(detail=True)
112 5a140b23 Vangelis Koukis
113 5a140b23 Vangelis Koukis
    def test_001_list_images(self):
114 5a140b23 Vangelis Koukis
        """Test image list actually returns images"""
115 5a140b23 Vangelis Koukis
        self.assertGreater(len(self.images), 0)
116 5a140b23 Vangelis Koukis
117 5a140b23 Vangelis Koukis
    def test_002_list_images_detailed(self):
118 5a140b23 Vangelis Koukis
        """Test detailed image list is the same length as list"""
119 5a140b23 Vangelis Koukis
        self.assertEqual(len(self.dimages), len(self.images))
120 5a140b23 Vangelis Koukis
121 5a140b23 Vangelis Koukis
    def test_003_same_image_names(self):
122 5a140b23 Vangelis Koukis
        """Test detailed and simple image list contain same names"""
123 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.images))
124 5a140b23 Vangelis Koukis
        dnames = sorted(map(lambda x: x["name"], self.dimages))
125 5a140b23 Vangelis Koukis
        self.assertEqual(names, dnames)
126 5a140b23 Vangelis Koukis
127 5a140b23 Vangelis Koukis
    def test_004_unique_image_names(self):
128 5a140b23 Vangelis Koukis
        """Test images have unique names"""
129 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.images))
130 5a140b23 Vangelis Koukis
        self.assertEqual(sorted(list(set(names))), names)
131 5a140b23 Vangelis Koukis
132 5a140b23 Vangelis Koukis
    def test_005_image_metadata(self):
133 5a140b23 Vangelis Koukis
        """Test every image has specific metadata defined"""
134 1c636ad6 John Giannelos
        keys = frozenset(["os", "description", "size"])
135 5a140b23 Vangelis Koukis
        for i in self.dimages:
136 5a140b23 Vangelis Koukis
            self.assertTrue(keys.issubset(i["metadata"]["values"].keys()))
137 5a140b23 Vangelis Koukis
138 5a140b23 Vangelis Koukis
139 5a140b23 Vangelis Koukis
class FlavorsTestCase(unittest.TestCase):
140 5a140b23 Vangelis Koukis
    """Test flavor lists for consistency"""
141 5a140b23 Vangelis Koukis
    @classmethod
142 5a140b23 Vangelis Koukis
    def setUpClass(cls):
143 5a140b23 Vangelis Koukis
        """Initialize kamaki, get (detailed) list of flavors"""
144 5a140b23 Vangelis Koukis
        log.info("Getting simple and detailed list of flavors")
145 1c636ad6 John Giannelos
146 1c636ad6 John Giannelos
        conf = Config()
147 1c636ad6 John Giannelos
        conf.set('compute_token', TOKEN)
148 1c636ad6 John Giannelos
        cls.client = ComputeClient(conf)
149 5a140b23 Vangelis Koukis
        cls.flavors = cls.client.list_flavors()
150 5a140b23 Vangelis Koukis
        cls.dflavors = cls.client.list_flavors(detail=True)
151 5a140b23 Vangelis Koukis
152 5a140b23 Vangelis Koukis
    def test_001_list_flavors(self):
153 5a140b23 Vangelis Koukis
        """Test flavor list actually returns flavors"""
154 5a140b23 Vangelis Koukis
        self.assertGreater(len(self.flavors), 0)
155 5a140b23 Vangelis Koukis
156 5a140b23 Vangelis Koukis
    def test_002_list_flavors_detailed(self):
157 5a140b23 Vangelis Koukis
        """Test detailed flavor list is the same length as list"""
158 5a140b23 Vangelis Koukis
        self.assertEquals(len(self.dflavors), len(self.flavors))
159 5a140b23 Vangelis Koukis
160 5a140b23 Vangelis Koukis
    def test_003_same_flavor_names(self):
161 5a140b23 Vangelis Koukis
        """Test detailed and simple flavor list contain same names"""
162 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.flavors))
163 5a140b23 Vangelis Koukis
        dnames = sorted(map(lambda x: x["name"], self.dflavors))
164 5a140b23 Vangelis Koukis
        self.assertEqual(names, dnames)
165 5a140b23 Vangelis Koukis
166 5a140b23 Vangelis Koukis
    def test_004_unique_flavor_names(self):
167 5a140b23 Vangelis Koukis
        """Test flavors have unique names"""
168 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.flavors))
169 5a140b23 Vangelis Koukis
        self.assertEqual(sorted(list(set(names))), names)
170 5a140b23 Vangelis Koukis
171 5a140b23 Vangelis Koukis
    def test_005_well_formed_flavor_names(self):
172 5a140b23 Vangelis Koukis
        """Test flavors have names of the form CxxRyyDzz
173 5a140b23 Vangelis Koukis

174 5a140b23 Vangelis Koukis
        Where xx is vCPU count, yy is RAM in MiB, zz is Disk in GiB
175 5a140b23 Vangelis Koukis

176 5a140b23 Vangelis Koukis
        """
177 5a140b23 Vangelis Koukis
        for f in self.dflavors:
178 5a140b23 Vangelis Koukis
            self.assertEqual("C%dR%dD%d" % (f["cpu"], f["ram"], f["disk"]),
179 5a140b23 Vangelis Koukis
                             f["name"],
180 5a140b23 Vangelis Koukis
                             "Flavor %s does not match its specs." % f["name"])
181 5a140b23 Vangelis Koukis
182 5a140b23 Vangelis Koukis
183 5a140b23 Vangelis Koukis
class ServersTestCase(unittest.TestCase):
184 5a140b23 Vangelis Koukis
    """Test server lists for consistency"""
185 5a140b23 Vangelis Koukis
    @classmethod
186 5a140b23 Vangelis Koukis
    def setUpClass(cls):
187 5a140b23 Vangelis Koukis
        """Initialize kamaki, get (detailed) list of servers"""
188 5a140b23 Vangelis Koukis
        log.info("Getting simple and detailed list of servers")
189 1c636ad6 John Giannelos
190 1c636ad6 John Giannelos
        conf = Config()
191 1c636ad6 John Giannelos
        conf.set('compute_token', TOKEN)
192 1c636ad6 John Giannelos
        cls.client = ComputeClient(conf)
193 5a140b23 Vangelis Koukis
        cls.servers = cls.client.list_servers()
194 5a140b23 Vangelis Koukis
        cls.dservers = cls.client.list_servers(detail=True)
195 5a140b23 Vangelis Koukis
196 5a140b23 Vangelis Koukis
    def test_001_list_servers(self):
197 5a140b23 Vangelis Koukis
        """Test server list actually returns servers"""
198 5a140b23 Vangelis Koukis
        self.assertGreater(len(self.servers), 0)
199 5a140b23 Vangelis Koukis
200 5a140b23 Vangelis Koukis
    def test_002_list_servers_detailed(self):
201 5a140b23 Vangelis Koukis
        """Test detailed server list is the same length as list"""
202 5a140b23 Vangelis Koukis
        self.assertEqual(len(self.dservers), len(self.servers))
203 5a140b23 Vangelis Koukis
204 5a140b23 Vangelis Koukis
    def test_003_same_server_names(self):
205 5a140b23 Vangelis Koukis
        """Test detailed and simple flavor list contain same names"""
206 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.servers))
207 5a140b23 Vangelis Koukis
        dnames = sorted(map(lambda x: x["name"], self.dservers))
208 5a140b23 Vangelis Koukis
        self.assertEqual(names, dnames)
209 5a140b23 Vangelis Koukis
210 5a140b23 Vangelis Koukis
211 5a140b23 Vangelis Koukis
# This class gets replicated into actual TestCases dynamically
212 5a140b23 Vangelis Koukis
class SpawnServerTestCase(unittest.TestCase):
213 5a140b23 Vangelis Koukis
    """Test scenario for server of the specified image"""
214 5a140b23 Vangelis Koukis
215 5a140b23 Vangelis Koukis
    @classmethod
216 5a140b23 Vangelis Koukis
    def setUpClass(cls):
217 5a140b23 Vangelis Koukis
        """Initialize a kamaki instance"""
218 bc14ba88 Vangelis Koukis
        log.info("Spawning server for image `%s'", cls.imagename)
219 1c636ad6 John Giannelos
220 1c636ad6 John Giannelos
        conf = Config()
221 1c636ad6 John Giannelos
        conf.set('compute_token', TOKEN)
222 1c636ad6 John Giannelos
        cls.client = ComputeClient(conf)
223 74193008 John Giannelos
        cls.cyclades = CycladesClient(conf)
224 5a140b23 Vangelis Koukis
225 5a140b23 Vangelis Koukis
    def _get_ipv4(self, server):
226 bc14ba88 Vangelis Koukis
        """Get the public IPv4 of a server from the detailed server info"""
227 1c636ad6 John Giannelos
228 5a140b23 Vangelis Koukis
        public_addrs = filter(lambda x: x["id"] == "public",
229 5a140b23 Vangelis Koukis
                              server["addresses"]["values"])
230 5a140b23 Vangelis Koukis
        self.assertEqual(len(public_addrs), 1)
231 5a140b23 Vangelis Koukis
        ipv4_addrs = filter(lambda x: x["version"] == 4,
232 5a140b23 Vangelis Koukis
                            public_addrs[0]["values"])
233 5a140b23 Vangelis Koukis
        self.assertEqual(len(ipv4_addrs), 1)
234 5a140b23 Vangelis Koukis
        return ipv4_addrs[0]["addr"]
235 5a140b23 Vangelis Koukis
236 5a140b23 Vangelis Koukis
    def _get_ipv6(self, server):
237 bc14ba88 Vangelis Koukis
        """Get the public IPv6 of a server from the detailed server info"""
238 5a140b23 Vangelis Koukis
        public_addrs = filter(lambda x: x["id"] == "public",
239 5a140b23 Vangelis Koukis
                              server["addresses"]["values"])
240 5a140b23 Vangelis Koukis
        self.assertEqual(len(public_addrs), 1)
241 5a140b23 Vangelis Koukis
        ipv6_addrs = filter(lambda x: x["version"] == 6,
242 5a140b23 Vangelis Koukis
                            public_addrs[0]["values"])
243 5a140b23 Vangelis Koukis
        self.assertEqual(len(ipv6_addrs), 1)
244 5a140b23 Vangelis Koukis
        return ipv6_addrs[0]["addr"]
245 5a140b23 Vangelis Koukis
246 bc14ba88 Vangelis Koukis
    def _connect_loginname(self, os):
247 bc14ba88 Vangelis Koukis
        """Return the login name for connections based on the server OS"""
248 1c636ad6 John Giannelos
        if os in ("Ubuntu", "Kubuntu", "Fedora"):
249 21bbbc9b Vangelis Koukis
            return "user"
250 1c636ad6 John Giannelos
        elif os in ("windows", "windows_alpha1"):
251 21bbbc9b Vangelis Koukis
            return "Administrator"
252 bc14ba88 Vangelis Koukis
        else:
253 21bbbc9b Vangelis Koukis
            return "root"
254 bc14ba88 Vangelis Koukis
255 bc14ba88 Vangelis Koukis
    def _verify_server_status(self, current_status, new_status):
256 bc14ba88 Vangelis Koukis
        """Verify a server has switched to a specified status"""
257 bc14ba88 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
258 21bbbc9b Vangelis Koukis
        if server["status"] not in (current_status, new_status):
259 21bbbc9b Vangelis Koukis
            return None  # Do not raise exception, return so the test fails
260 bc14ba88 Vangelis Koukis
        self.assertEquals(server["status"], new_status)
261 bc14ba88 Vangelis Koukis
262 bc14ba88 Vangelis Koukis
    def _get_connected_tcp_socket(self, family, host, port):
263 bc14ba88 Vangelis Koukis
        """Get a connected socket from the specified family to host:port"""
264 bc14ba88 Vangelis Koukis
        sock = None
265 bc14ba88 Vangelis Koukis
        for res in \
266 bc14ba88 Vangelis Koukis
            socket.getaddrinfo(host, port, family, socket.SOCK_STREAM, 0,
267 bc14ba88 Vangelis Koukis
                               socket.AI_PASSIVE):
268 bc14ba88 Vangelis Koukis
            af, socktype, proto, canonname, sa = res
269 bc14ba88 Vangelis Koukis
            try:
270 bc14ba88 Vangelis Koukis
                sock = socket.socket(af, socktype, proto)
271 bc14ba88 Vangelis Koukis
            except socket.error as msg:
272 bc14ba88 Vangelis Koukis
                sock = None
273 bc14ba88 Vangelis Koukis
                continue
274 bc14ba88 Vangelis Koukis
            try:
275 bc14ba88 Vangelis Koukis
                sock.connect(sa)
276 bc14ba88 Vangelis Koukis
            except socket.error as msg:
277 bc14ba88 Vangelis Koukis
                sock.close()
278 bc14ba88 Vangelis Koukis
                sock = None
279 bc14ba88 Vangelis Koukis
                continue
280 bc14ba88 Vangelis Koukis
        self.assertIsNotNone(sock)
281 bc14ba88 Vangelis Koukis
        return sock
282 bc14ba88 Vangelis Koukis
283 bc14ba88 Vangelis Koukis
    def _ping_once(self, ipv6, ip):
284 bc14ba88 Vangelis Koukis
        """Test server responds to a single IPv4 or IPv6 ping"""
285 bc14ba88 Vangelis Koukis
        cmd = "ping%s -c 2 -w 3 %s" % ("6" if ipv6 else "", ip)
286 bc14ba88 Vangelis Koukis
        ping = subprocess.Popen(cmd, shell=True,
287 bc14ba88 Vangelis Koukis
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
288 bc14ba88 Vangelis Koukis
        (stdout, stderr) = ping.communicate()
289 bc14ba88 Vangelis Koukis
        ret = ping.wait()
290 bc14ba88 Vangelis Koukis
        self.assertEquals(ret, 0)
291 5a140b23 Vangelis Koukis
292 bc14ba88 Vangelis Koukis
    def _get_hostname_over_ssh(self, hostip, username, password):
293 bc14ba88 Vangelis Koukis
        ssh = paramiko.SSHClient()
294 bc14ba88 Vangelis Koukis
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
295 bc14ba88 Vangelis Koukis
        try:
296 bc14ba88 Vangelis Koukis
            ssh.connect(hostip, username=username, password=password)
297 bc14ba88 Vangelis Koukis
        except socket.error:
298 bc14ba88 Vangelis Koukis
            raise AssertionError
299 bc14ba88 Vangelis Koukis
        stdin, stdout, stderr = ssh.exec_command("hostname")
300 bc14ba88 Vangelis Koukis
        lines = stdout.readlines()
301 bc14ba88 Vangelis Koukis
        self.assertEqual(len(lines), 1)
302 4fdd25ab Vangelis Koukis
        return lines[0]
303 bc14ba88 Vangelis Koukis
304 bc14ba88 Vangelis Koukis
    def _try_until_timeout_expires(self, warn_timeout, fail_timeout,
305 bc14ba88 Vangelis Koukis
                                   opmsg, callable, *args, **kwargs):
306 bc14ba88 Vangelis Koukis
        if warn_timeout == fail_timeout:
307 5a140b23 Vangelis Koukis
            warn_timeout = fail_timeout + 1
308 5a140b23 Vangelis Koukis
        warn_tmout = time.time() + warn_timeout
309 5a140b23 Vangelis Koukis
        fail_tmout = time.time() + fail_timeout
310 5a140b23 Vangelis Koukis
        while True:
311 4fdd25ab Vangelis Koukis
            self.assertLess(time.time(), fail_tmout,
312 21bbbc9b Vangelis Koukis
                            "operation `%s' timed out" % opmsg)
313 5a140b23 Vangelis Koukis
            if time.time() > warn_tmout:
314 4fdd25ab Vangelis Koukis
                log.warning("Server %d: `%s' operation `%s' not done yet",
315 4fdd25ab Vangelis Koukis
                            self.serverid, self.servername, opmsg)
316 bc14ba88 Vangelis Koukis
            try:
317 4fdd25ab Vangelis Koukis
                log.info("%s... " % opmsg)
318 bc14ba88 Vangelis Koukis
                return callable(*args, **kwargs)
319 bc14ba88 Vangelis Koukis
            except AssertionError:
320 bc14ba88 Vangelis Koukis
                pass
321 5a140b23 Vangelis Koukis
            time.sleep(self.query_interval)
322 5a140b23 Vangelis Koukis
323 bc14ba88 Vangelis Koukis
    def _insist_on_tcp_connection(self, family, host, port):
324 21bbbc9b Vangelis Koukis
        familystr = {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6",
325 21bbbc9b Vangelis Koukis
                     socket.AF_UNSPEC: "Unspecified-IPv4/6"}
326 bc14ba88 Vangelis Koukis
        msg = "connect over %s to %s:%s" % \
327 bc14ba88 Vangelis Koukis
              (familystr.get(family, "Unknown"), host, port)
328 bc14ba88 Vangelis Koukis
        sock = self._try_until_timeout_expires(
329 bc14ba88 Vangelis Koukis
                self.action_timeout, self.action_timeout,
330 bc14ba88 Vangelis Koukis
                msg, self._get_connected_tcp_socket,
331 bc14ba88 Vangelis Koukis
                family, host, port)
332 bc14ba88 Vangelis Koukis
        return sock
333 bc14ba88 Vangelis Koukis
334 bc14ba88 Vangelis Koukis
    def _insist_on_status_transition(self, current_status, new_status,
335 bc14ba88 Vangelis Koukis
                                    fail_timeout, warn_timeout=None):
336 4fdd25ab Vangelis Koukis
        msg = "Server %d: `%s', waiting for %s -> %s" % \
337 4fdd25ab Vangelis Koukis
              (self.serverid, self.servername, current_status, new_status)
338 bc14ba88 Vangelis Koukis
        if warn_timeout is None:
339 bc14ba88 Vangelis Koukis
            warn_timeout = fail_timeout
340 bc14ba88 Vangelis Koukis
        self._try_until_timeout_expires(warn_timeout, fail_timeout,
341 bc14ba88 Vangelis Koukis
                                        msg, self._verify_server_status,
342 bc14ba88 Vangelis Koukis
                                        current_status, new_status)
343 21bbbc9b Vangelis Koukis
        # Ensure the status is actually the expected one
344 21bbbc9b Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
345 21bbbc9b Vangelis Koukis
        self.assertEquals(server["status"], new_status)
346 bc14ba88 Vangelis Koukis
347 bc14ba88 Vangelis Koukis
    def _insist_on_ssh_hostname(self, hostip, username, password):
348 4fdd25ab Vangelis Koukis
        msg = "SSH to %s, as %s/%s" % (hostip, username, password)
349 bc14ba88 Vangelis Koukis
        hostname = self._try_until_timeout_expires(
350 bc14ba88 Vangelis Koukis
                self.action_timeout, self.action_timeout,
351 bc14ba88 Vangelis Koukis
                msg, self._get_hostname_over_ssh,
352 bc14ba88 Vangelis Koukis
                hostip, username, password)
353 bc14ba88 Vangelis Koukis
354 bc14ba88 Vangelis Koukis
        # The hostname must be of the form 'prefix-id'
355 bc14ba88 Vangelis Koukis
        self.assertTrue(hostname.endswith("-%d\n" % self.serverid))
356 5a140b23 Vangelis Koukis
357 9659e075 John Giannelos
    def _check_file_through_ssh(self, hostip, username, password, remotepath, content):
358 9659e075 John Giannelos
        msg = "Trying file injection through SSH to %s, as %s/%s" % (hostip, username, password)
359 f97dce4d John Giannelos
        log.info(msg)
360 77054bf5 John Giannelos
        try:
361 77054bf5 John Giannelos
            ssh = paramiko.SSHClient()
362 77054bf5 John Giannelos
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
363 77054bf5 John Giannelos
            ssh.connect(hostip, username=username, password=password)
364 77054bf5 John Giannelos
        except socket.error:
365 77054bf5 John Giannelos
            raise AssertionError
366 77054bf5 John Giannelos
        
367 9659e075 John Giannelos
        transport = paramiko.Transport((hostip,22))
368 77054bf5 John Giannelos
        transport.connect(username = username, password = password)
369 9659e075 John Giannelos
370 77054bf5 John Giannelos
        localpath = '/tmp/'+SNF_TEST_PREFIX+'injection'
371 77054bf5 John Giannelos
        sftp = paramiko.SFTPClient.from_transport(transport)
372 77054bf5 John Giannelos
        sftp.get(remotepath, localpath)
373 77054bf5 John Giannelos
        
374 77054bf5 John Giannelos
        sftp.close()
375 77054bf5 John Giannelos
        transport.close()
376 77054bf5 John Giannelos
377 9659e075 John Giannelos
        f = open(localpath)
378 9659e075 John Giannelos
        remote_content = b64encode(f.read())
379 9659e075 John Giannelos
380 77054bf5 John Giannelos
        # Check if files are the same
381 65462ca9 John Giannelos
        return (remote_content == content)
382 77054bf5 John Giannelos
383 5a140b23 Vangelis Koukis
    def _skipIf(self, condition, msg):
384 5a140b23 Vangelis Koukis
        if condition:
385 5a140b23 Vangelis Koukis
            self.skipTest(msg)
386 5a140b23 Vangelis Koukis
387 5a140b23 Vangelis Koukis
    def test_001_submit_create_server(self):
388 5a140b23 Vangelis Koukis
        """Test submit create server request"""
389 5a140b23 Vangelis Koukis
        server = self.client.create_server(self.servername, self.flavorid,
390 5a140b23 Vangelis Koukis
                                           self.imageid, self.personality)
391 9659e075 John Giannelos
392 5a140b23 Vangelis Koukis
        self.assertEqual(server["name"], self.servername)
393 5a140b23 Vangelis Koukis
        self.assertEqual(server["flavorRef"], self.flavorid)
394 5a140b23 Vangelis Koukis
        self.assertEqual(server["imageRef"], self.imageid)
395 5a140b23 Vangelis Koukis
        self.assertEqual(server["status"], "BUILD")
396 5a140b23 Vangelis Koukis
397 5a140b23 Vangelis Koukis
        # Update class attributes to reflect data on building server
398 5a140b23 Vangelis Koukis
        cls = type(self)
399 5a140b23 Vangelis Koukis
        cls.serverid = server["id"]
400 bc14ba88 Vangelis Koukis
        cls.username = None
401 5a140b23 Vangelis Koukis
        cls.passwd = server["adminPass"]
402 5a140b23 Vangelis Koukis
403 5a140b23 Vangelis Koukis
    def test_002a_server_is_building_in_list(self):
404 5a140b23 Vangelis Koukis
        """Test server is in BUILD state, in server list"""
405 5a140b23 Vangelis Koukis
        servers = self.client.list_servers(detail=True)
406 5a140b23 Vangelis Koukis
        servers = filter(lambda x: x["name"] == self.servername, servers)
407 5a140b23 Vangelis Koukis
        self.assertEqual(len(servers), 1)
408 5a140b23 Vangelis Koukis
        server = servers[0]
409 5a140b23 Vangelis Koukis
        self.assertEqual(server["name"], self.servername)
410 5a140b23 Vangelis Koukis
        self.assertEqual(server["flavorRef"], self.flavorid)
411 5a140b23 Vangelis Koukis
        self.assertEqual(server["imageRef"], self.imageid)
412 5a140b23 Vangelis Koukis
        self.assertEqual(server["status"], "BUILD")
413 5a140b23 Vangelis Koukis
414 5a140b23 Vangelis Koukis
    def test_002b_server_is_building_in_details(self):
415 5a140b23 Vangelis Koukis
        """Test server is in BUILD state, in details"""
416 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
417 5a140b23 Vangelis Koukis
        self.assertEqual(server["name"], self.servername)
418 5a140b23 Vangelis Koukis
        self.assertEqual(server["flavorRef"], self.flavorid)
419 5a140b23 Vangelis Koukis
        self.assertEqual(server["imageRef"], self.imageid)
420 5a140b23 Vangelis Koukis
        self.assertEqual(server["status"], "BUILD")
421 5a140b23 Vangelis Koukis
422 5a140b23 Vangelis Koukis
    def test_002c_set_server_metadata(self):
423 5a140b23 Vangelis Koukis
        image = self.client.get_image_details(self.imageid)
424 74193008 John Giannelos
        os = image["metadata"]["values"]["os"]
425 74193008 John Giannelos
        loginname = image["metadata"]["values"].get("users", None)
426 c1d11f96 John Giannelos
        self.client.update_server_metadata(self.serverid, OS=os)
427 bc14ba88 Vangelis Koukis
428 bc14ba88 Vangelis Koukis
        # Determine the username to use for future connections
429 bc14ba88 Vangelis Koukis
        # to this host
430 bc14ba88 Vangelis Koukis
        cls = type(self)
431 bc14ba88 Vangelis Koukis
        cls.username = loginname
432 bc14ba88 Vangelis Koukis
        if not cls.username:
433 bc14ba88 Vangelis Koukis
            cls.username = self._connect_loginname(os)
434 bc14ba88 Vangelis Koukis
        self.assertIsNotNone(cls.username)
435 5a140b23 Vangelis Koukis
436 5a140b23 Vangelis Koukis
    def test_002d_verify_server_metadata(self):
437 5a140b23 Vangelis Koukis
        """Test server metadata keys are set based on image metadata"""
438 5a140b23 Vangelis Koukis
        servermeta = self.client.get_server_metadata(self.serverid)
439 5a140b23 Vangelis Koukis
        imagemeta = self.client.get_image_metadata(self.imageid)
440 99d41650 John Giannelos
        self.assertEqual(servermeta["OS"], imagemeta["os"])
441 5a140b23 Vangelis Koukis
442 5a140b23 Vangelis Koukis
    def test_003_server_becomes_active(self):
443 5a140b23 Vangelis Koukis
        """Test server becomes ACTIVE"""
444 bc14ba88 Vangelis Koukis
        self._insist_on_status_transition("BUILD", "ACTIVE",
445 5a140b23 Vangelis Koukis
                                         self.build_fail, self.build_warning)
446 5a140b23 Vangelis Koukis
447 65462ca9 John Giannelos
    def test_003a_get_server_oob_console(self):
448 65462ca9 John Giannelos
        """Test getting OOB server console over VNC
449 bc14ba88 Vangelis Koukis

450 65462ca9 John Giannelos
        Implementation of RFB protocol follows
451 65462ca9 John Giannelos
        http://www.realvnc.com/docs/rfbproto.pdf.
452 bc14ba88 Vangelis Koukis

453 65462ca9 John Giannelos
        """
454 9659e075 John Giannelos
        
455 65462ca9 John Giannelos
        console = self.cyclades.get_server_console(self.serverid)
456 65462ca9 John Giannelos
        self.assertEquals(console['type'], "vnc")
457 65462ca9 John Giannelos
        sock = self._insist_on_tcp_connection(socket.AF_UNSPEC,
458 65462ca9 John Giannelos
                                        console["host"], console["port"])
459 65462ca9 John Giannelos
460 65462ca9 John Giannelos
        # Step 1. ProtocolVersion message (par. 6.1.1)
461 65462ca9 John Giannelos
        version = sock.recv(1024)
462 65462ca9 John Giannelos
        self.assertEquals(version, 'RFB 003.008\n')
463 65462ca9 John Giannelos
        sock.send(version)
464 65462ca9 John Giannelos
465 65462ca9 John Giannelos
        # Step 2. Security (par 6.1.2): Only VNC Authentication supported
466 65462ca9 John Giannelos
        sec = sock.recv(1024)
467 65462ca9 John Giannelos
        self.assertEquals(list(sec), ['\x01', '\x02'])
468 65462ca9 John Giannelos
469 65462ca9 John Giannelos
        # Step 3. Request VNC Authentication (par 6.1.2)
470 65462ca9 John Giannelos
        sock.send('\x02')
471 65462ca9 John Giannelos
472 65462ca9 John Giannelos
        # Step 4. Receive Challenge (par 6.2.2)
473 65462ca9 John Giannelos
        challenge = sock.recv(1024)
474 65462ca9 John Giannelos
        self.assertEquals(len(challenge), 16)
475 65462ca9 John Giannelos
476 65462ca9 John Giannelos
        # Step 5. DES-Encrypt challenge, use password as key (par 6.2.2)
477 65462ca9 John Giannelos
        response = d3des_generate_response(
478 65462ca9 John Giannelos
            (console["password"] + '\0' * 8)[:8], challenge)
479 65462ca9 John Giannelos
        sock.send(response)
480 65462ca9 John Giannelos
481 65462ca9 John Giannelos
        # Step 6. SecurityResult (par 6.1.3)
482 65462ca9 John Giannelos
        result = sock.recv(4)
483 65462ca9 John Giannelos
        self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
484 65462ca9 John Giannelos
        sock.close()
485 74193008 John Giannelos
        
486 5a140b23 Vangelis Koukis
    def test_004_server_has_ipv4(self):
487 5a140b23 Vangelis Koukis
        """Test active server has a valid IPv4 address"""
488 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
489 5a140b23 Vangelis Koukis
        ipv4 = self._get_ipv4(server)
490 5a140b23 Vangelis Koukis
        self.assertEquals(IP(ipv4).version(), 4)
491 5a140b23 Vangelis Koukis
492 65462ca9 John Giannelos
    def test_005_server_has_ipv6(self):
493 65462ca9 John Giannelos
        """Test active server has a valid IPv6 address"""
494 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
495 65462ca9 John Giannelos
        ipv6 = self._get_ipv6(server)
496 65462ca9 John Giannelos
        self.assertEquals(IP(ipv6).version(), 6)
497 5a140b23 Vangelis Koukis
498 5a140b23 Vangelis Koukis
    def test_006_server_responds_to_ping_IPv4(self):
499 5a140b23 Vangelis Koukis
        """Test server responds to ping on IPv4 address"""
500 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
501 bc14ba88 Vangelis Koukis
        ip = self._get_ipv4(server)
502 bc14ba88 Vangelis Koukis
        self._try_until_timeout_expires(self.action_timeout,
503 bc14ba88 Vangelis Koukis
                                        self.action_timeout,
504 4fdd25ab Vangelis Koukis
                                        "PING IPv4 to %s" % ip,
505 4fdd25ab Vangelis Koukis
                                        self._ping_once,
506 bc14ba88 Vangelis Koukis
                                        False, ip)
507 5a140b23 Vangelis Koukis
508 65462ca9 John Giannelos
    def test_007_server_responds_to_ping_IPv6(self):
509 65462ca9 John Giannelos
        """Test server responds to ping on IPv6 address"""
510 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
511 65462ca9 John Giannelos
        ip = self._get_ipv6(server)
512 65462ca9 John Giannelos
        self._try_until_timeout_expires(self.action_timeout,
513 65462ca9 John Giannelos
                                        self.action_timeout,
514 65462ca9 John Giannelos
                                        "PING IPv6 to %s" % ip,
515 65462ca9 John Giannelos
                                        self._ping_once,
516 65462ca9 John Giannelos
                                        True, ip)
517 5a140b23 Vangelis Koukis
518 5a140b23 Vangelis Koukis
    def test_008_submit_shutdown_request(self):
519 5a140b23 Vangelis Koukis
        """Test submit request to shutdown server"""
520 74193008 John Giannelos
        self.cyclades.shutdown_server(self.serverid)
521 5a140b23 Vangelis Koukis
522 5a140b23 Vangelis Koukis
    def test_009_server_becomes_stopped(self):
523 5a140b23 Vangelis Koukis
        """Test server becomes STOPPED"""
524 bc14ba88 Vangelis Koukis
        self._insist_on_status_transition("ACTIVE", "STOPPED",
525 bc14ba88 Vangelis Koukis
                                         self.action_timeout,
526 5a140b23 Vangelis Koukis
                                         self.action_timeout)
527 5a140b23 Vangelis Koukis
528 5a140b23 Vangelis Koukis
    def test_010_submit_start_request(self):
529 5a140b23 Vangelis Koukis
        """Test submit start server request"""
530 74193008 John Giannelos
        self.cyclades.start_server(self.serverid)
531 5a140b23 Vangelis Koukis
532 5a140b23 Vangelis Koukis
    def test_011_server_becomes_active(self):
533 5a140b23 Vangelis Koukis
        """Test server becomes ACTIVE again"""
534 bc14ba88 Vangelis Koukis
        self._insist_on_status_transition("STOPPED", "ACTIVE",
535 bc14ba88 Vangelis Koukis
                                         self.action_timeout,
536 5a140b23 Vangelis Koukis
                                         self.action_timeout)
537 5a140b23 Vangelis Koukis
538 5a140b23 Vangelis Koukis
    def test_011a_server_responds_to_ping_IPv4(self):
539 5a140b23 Vangelis Koukis
        """Test server OS is actually up and running again"""
540 5a140b23 Vangelis Koukis
        self.test_006_server_responds_to_ping_IPv4()
541 5a140b23 Vangelis Koukis
542 5a140b23 Vangelis Koukis
    def test_012_ssh_to_server_IPv4(self):
543 5a140b23 Vangelis Koukis
        """Test SSH to server public IPv4 works, verify hostname"""
544 bc14ba88 Vangelis Koukis
        self._skipIf(self.is_windows, "only valid for Linux servers")
545 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
546 bc14ba88 Vangelis Koukis
        self._insist_on_ssh_hostname(self._get_ipv4(server),
547 bc14ba88 Vangelis Koukis
                                     self.username, self.passwd)
548 5a140b23 Vangelis Koukis
549 65462ca9 John Giannelos
    def test_013_ssh_to_server_IPv6(self):
550 65462ca9 John Giannelos
        """Test SSH to server public IPv6 works, verify hostname"""
551 65462ca9 John Giannelos
        self._skipIf(self.is_windows, "only valid for Linux servers")
552 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
553 65462ca9 John Giannelos
        self._insist_on_ssh_hostname(self._get_ipv6(server),
554 65462ca9 John Giannelos
                                     self.username, self.passwd)
555 5a140b23 Vangelis Koukis
556 5a140b23 Vangelis Koukis
    def test_014_rdp_to_server_IPv4(self):
557 5a140b23 Vangelis Koukis
        "Test RDP connection to server public IPv4 works"""
558 bc14ba88 Vangelis Koukis
        self._skipIf(not self.is_windows, "only valid for Windows servers")
559 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
560 5a140b23 Vangelis Koukis
        ipv4 = self._get_ipv4(server)
561 bc14ba88 Vangelis Koukis
        sock = _insist_on_tcp_connection(socket.AF_INET, ipv4, 3389)
562 5a140b23 Vangelis Koukis
563 5a140b23 Vangelis Koukis
        # No actual RDP processing done. We assume the RDP server is there
564 5a140b23 Vangelis Koukis
        # if the connection to the RDP port is successful.
565 cb1fa17c Vangelis Koukis
        # FIXME: Use rdesktop, analyze exit code? see manpage [costasd]
566 5a140b23 Vangelis Koukis
        sock.close()
567 5a140b23 Vangelis Koukis
568 65462ca9 John Giannelos
    def test_015_rdp_to_server_IPv6(self):
569 65462ca9 John Giannelos
        "Test RDP connection to server public IPv6 works"""
570 65462ca9 John Giannelos
        self._skipIf(not self.is_windows, "only valid for Windows servers")
571 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
572 65462ca9 John Giannelos
        ipv6 = self._get_ipv6(server)
573 65462ca9 John Giannelos
        sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
574 5a140b23 Vangelis Koukis
575 65462ca9 John Giannelos
        # No actual RDP processing done. We assume the RDP server is there
576 65462ca9 John Giannelos
        # if the connection to the RDP port is successful.
577 65462ca9 John Giannelos
        sock.close()
578 5a140b23 Vangelis Koukis
579 5a140b23 Vangelis Koukis
    def test_016_personality_is_enforced(self):
580 5a140b23 Vangelis Koukis
        """Test file injection for personality enforcement"""
581 bc14ba88 Vangelis Koukis
        self._skipIf(self.is_windows, "only implemented for Linux servers")
582 9659e075 John Giannelos
        self._skipIf(self.personality == None, "No personality file selected")
583 77054bf5 John Giannelos
584 77054bf5 John Giannelos
        server = self.client.get_server_details(self.serverid)
585 77054bf5 John Giannelos
586 9659e075 John Giannelos
        for inj_file in self.personality:
587 9659e075 John Giannelos
            equal_files = self._check_file_through_ssh(self._get_ipv4(server), inj_file['owner'], 
588 9659e075 John Giannelos
                                                       self.passwd, inj_file['path'], inj_file['contents'])
589 9659e075 John Giannelos
            self.assertTrue(equal_files)
590 77054bf5 John Giannelos
        
591 5a140b23 Vangelis Koukis
592 4fdd25ab Vangelis Koukis
    def test_017_submit_delete_request(self):
593 4fdd25ab Vangelis Koukis
        """Test submit request to delete server"""
594 4fdd25ab Vangelis Koukis
        self.client.delete_server(self.serverid)
595 4fdd25ab Vangelis Koukis
596 4fdd25ab Vangelis Koukis
    def test_018_server_becomes_deleted(self):
597 4fdd25ab Vangelis Koukis
        """Test server becomes DELETED"""
598 4fdd25ab Vangelis Koukis
        self._insist_on_status_transition("ACTIVE", "DELETED",
599 4fdd25ab Vangelis Koukis
                                         self.action_timeout,
600 4fdd25ab Vangelis Koukis
                                         self.action_timeout)
601 4fdd25ab Vangelis Koukis
602 4fdd25ab Vangelis Koukis
    def test_019_server_no_longer_in_server_list(self):
603 4fdd25ab Vangelis Koukis
        """Test server is no longer in server list"""
604 4fdd25ab Vangelis Koukis
        servers = self.client.list_servers()
605 21bbbc9b Vangelis Koukis
        self.assertNotIn(self.serverid, [s["id"] for s in servers])
606 21bbbc9b Vangelis Koukis
607 21bbbc9b Vangelis Koukis
608 9e4682b5 John Giannelos
class NetworkTestCase(unittest.TestCase):
609 e94a9d8c John Giannelos
    """ Testing networking in cyclades """
610 65462ca9 John Giannelos
  
611 9e4682b5 John Giannelos
    @classmethod
612 e94a9d8c John Giannelos
    def setUpClass(cls):
613 e94a9d8c John Giannelos
        "Initialize kamaki, get list of current networks"
614 e94a9d8c John Giannelos
        conf = Config()
615 e94a9d8c John Giannelos
        conf.set('compute_token', TOKEN)
616 e94a9d8c John Giannelos
        cls.client = CycladesClient(conf)
617 9e4682b5 John Giannelos
        cls.compute = ComputeClient(conf)
618 9e4682b5 John Giannelos
619 9e4682b5 John Giannelos
        images = cls.compute.list_images(detail = True)
620 9e4682b5 John Giannelos
        flavors = cls.compute.list_flavors(detail = True)
621 9e4682b5 John Giannelos
        imageid = choice([im['id'] for im in images])
622 9e4682b5 John Giannelos
        flavorid = choice([f["id"] for f in flavors if f["disk"] >= 20])
623 9e4682b5 John Giannelos
624 9e4682b5 John Giannelos
        for image in images:
625 9e4682b5 John Giannelos
            if image['id'] == imageid:
626 9e4682b5 John Giannelos
                imagename = image['name']
627 e94a9d8c John Giannelos
628 9e4682b5 John Giannelos
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
629 9e4682b5 John Giannelos
        is_windows = imagename.lower().find("windows") >= 0
630 65462ca9 John Giannelos
631 65462ca9 John Giannelos
        #Run testcases for server spawning in order to ensure it is done right
632 9e4682b5 John Giannelos
        setupCase =  _spawn_server_test_case(imageid=str(imageid), flavorid=flavorid,
633 9e4682b5 John Giannelos
                                             imagename=imagename,
634 9e4682b5 John Giannelos
                                             personality=None,
635 9e4682b5 John Giannelos
                                             servername=servername,
636 9e4682b5 John Giannelos
                                             is_windows=is_windows,
637 9e4682b5 John Giannelos
                                             action_timeout=200,
638 9e4682b5 John Giannelos
                                             build_warning=1200,
639 9e4682b5 John Giannelos
                                             build_fail=500,
640 9e4682b5 John Giannelos
                                             query_interval=3)
641 9e4682b5 John Giannelos
642 9e4682b5 John Giannelos
        #Using already implemented tests for serverlist population
643 9e4682b5 John Giannelos
        suite = unittest.TestSuite()
644 9e4682b5 John Giannelos
        suite.addTest(setupCase('test_001_submit_create_server'))
645 9e4682b5 John Giannelos
        suite.addTest(setupCase('test_002a_server_is_building_in_list'))
646 9e4682b5 John Giannelos
        suite.addTest(setupCase('test_002b_server_is_building_in_details'))        
647 9e4682b5 John Giannelos
        suite.addTest(setupCase('test_003_server_becomes_active'))
648 9e4682b5 John Giannelos
        unittest.TextTestRunner(verbosity=2).run(suite)
649 e94a9d8c John Giannelos
650 9e4682b5 John Giannelos
    def test_001_create_network(self):
651 e94a9d8c John Giannelos
        """Test submit create network request"""
652 e94a9d8c John Giannelos
        name = SNF_TEST_PREFIX+TEST_RUN_ID
653 9e4682b5 John Giannelos
        previous_num = len(self.client.list_networks())
654 f97dce4d John Giannelos
        network =  self.client.create_network(name)        
655 f97dce4d John Giannelos
       
656 9e4682b5 John Giannelos
        #Test if right name is assigned
657 e94a9d8c John Giannelos
        self.assertEqual(network['name'], name)
658 e94a9d8c John Giannelos
        
659 9e4682b5 John Giannelos
        # Update class attributes
660 e94a9d8c John Giannelos
        cls = type(self)
661 e94a9d8c John Giannelos
        cls.networkid = network['id']
662 9e4682b5 John Giannelos
        networks = self.client.list_networks()
663 9e4682b5 John Giannelos
664 9e4682b5 John Giannelos
        #Test if new network is created
665 9e4682b5 John Giannelos
        self.assertTrue(len(networks) > previous_num)
666 9e4682b5 John Giannelos
        
667 e94a9d8c John Giannelos
    
668 9e4682b5 John Giannelos
    def test_002_connect_to_network(self):
669 f97dce4d John Giannelos
        """Test connect VM to network"""
670 9e4682b5 John Giannelos
        servers = self.compute.list_servers()
671 65462ca9 John Giannelos
672 65462ca9 John Giannelos
        #Pick a server created only for the test
673 65462ca9 John Giannelos
        server = choice([s for s in servers if s['name'].startswith(SNF_TEST_PREFIX)])
674 9e4682b5 John Giannelos
        self.client.connect_server(server['id'], self.networkid)
675 9e4682b5 John Giannelos
        
676 9e4682b5 John Giannelos
        #Update class attributes
677 9e4682b5 John Giannelos
        cls = type(self)
678 9e4682b5 John Giannelos
        cls.serverid = server['id']
679 9e4682b5 John Giannelos
680 65462ca9 John Giannelos
        #Insist on connecting until action timeout
681 9e4682b5 John Giannelos
        connected = (self.client.get_network_details(self.networkid))
682 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
683 9e4682b5 John Giannelos
684 65462ca9 John Giannelos
        while True:
685 65462ca9 John Giannelos
            connections = connected['servers']['values']
686 65462ca9 John Giannelos
            if (self.serverid in connections):
687 65462ca9 John Giannelos
                conn_exists = True
688 65462ca9 John Giannelos
            if time.time() > fail_tmout:
689 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
690 65462ca9 John Giannelos
            else:
691 65462ca9 John Giannelos
                time.sleep(self.query_interval)
692 65462ca9 John Giannelos
693 65462ca9 John Giannelos
        self.assertTrue(conn_exists)
694 65462ca9 John Giannelos
            
695 9e4682b5 John Giannelos
696 9e4682b5 John Giannelos
    def test_003_disconnect_from_network(self):
697 f97dce4d John Giannelos
        prev_state = self.client.get_network_details(self.networkid)
698 9e4682b5 John Giannelos
        prev_conn = len(prev_state['servers']['values'])
699 e94a9d8c John Giannelos
700 e94a9d8c John Giannelos
        self.client.disconnect_server(self.serverid, self.networkid)
701 f97dce4d John Giannelos
        time.sleep(15)
702 f97dce4d John Giannelos
703 65462ca9 John Giannelos
        #Insist on deleting until action timeout
704 9e4682b5 John Giannelos
        connected = (self.client.get_network_details(self.networkid))
705 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
706 9e4682b5 John Giannelos
707 65462ca9 John Giannelos
        while True:
708 65462ca9 John Giannelos
            connections = connected['servers']['values']
709 65462ca9 John Giannelos
            if (self.serverid not in connections):
710 65462ca9 John Giannelos
                conn_exists = False
711 65462ca9 John Giannelos
            if time.time() > fail_tmout:
712 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
713 65462ca9 John Giannelos
            else:
714 65462ca9 John Giannelos
                time.sleep(self.query_interval)
715 65462ca9 John Giannelos
716 65462ca9 John Giannelos
        self.assertFalse(conn_exists)
717 9e4682b5 John Giannelos
718 9e4682b5 John Giannelos
    def test_004_destroy_network(self):
719 e94a9d8c John Giannelos
        """Test submit delete network request"""
720 65462ca9 John Giannelos
        self.client.delete_network(self.networkid)        
721 9e4682b5 John Giannelos
        networks = self.client.list_networks()
722 e94a9d8c John Giannelos
723 65462ca9 John Giannelos
        curr_net = []
724 65462ca9 John Giannelos
        for net in networks:
725 65462ca9 John Giannelos
            curr_net.appent(net['id'])
726 65462ca9 John Giannelos
727 65462ca9 John Giannelos
        self.assertTrue(self.networkid not in curr_net)
728 65462ca9 John Giannelos
        
729 65462ca9 John Giannelos
    def test_005_cleanup_servers(self):
730 65462ca9 John Giannelos
        """Cleanup servers created for this test"""
731 65462ca9 John Giannelos
        self.compute.delete_server(self.server_id)
732 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
733 65462ca9 John Giannelos
734 65462ca9 John Giannelos
        #Ensure server gets deleted
735 65462ca9 John Giannelos
        while True:
736 65462ca9 John Giannelos
            status = self.compute.get_server_details(self.serverid)
737 65462ca9 John Giannelos
            if status == 'DELETED':
738 65462ca9 John Giannelos
                deleted = True
739 65462ca9 John Giannelos
            elif time.time() > fail_tmout: 
740 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
741 65462ca9 John Giannelos
            else:
742 65462ca9 John Giannelos
                time.sleep(self.query_interval)
743 65462ca9 John Giannelos
744 65462ca9 John Giannelos
        self.assertTrue(deleted)
745 e94a9d8c John Giannelos
746 21bbbc9b Vangelis Koukis
class TestRunnerProcess(Process):
747 21bbbc9b Vangelis Koukis
    """A distinct process used to execute part of the tests in parallel"""
748 21bbbc9b Vangelis Koukis
    def __init__(self, **kw):
749 21bbbc9b Vangelis Koukis
        Process.__init__(self, **kw)
750 21bbbc9b Vangelis Koukis
        kwargs = kw["kwargs"]
751 21bbbc9b Vangelis Koukis
        self.testq = kwargs["testq"]
752 21bbbc9b Vangelis Koukis
        self.runner = kwargs["runner"]
753 21bbbc9b Vangelis Koukis
754 21bbbc9b Vangelis Koukis
    def run(self):
755 21bbbc9b Vangelis Koukis
        # Make sure this test runner process dies with the parent
756 21bbbc9b Vangelis Koukis
        # and is not left behind.
757 21bbbc9b Vangelis Koukis
        #
758 21bbbc9b Vangelis Koukis
        # WARNING: This uses the prctl(2) call and is
759 21bbbc9b Vangelis Koukis
        # Linux-specific.
760 21bbbc9b Vangelis Koukis
        prctl.set_pdeathsig(signal.SIGHUP)
761 21bbbc9b Vangelis Koukis
762 21bbbc9b Vangelis Koukis
        while True:
763 21bbbc9b Vangelis Koukis
            log.debug("I am process %d, GETting from queue is %s",
764 21bbbc9b Vangelis Koukis
                     os.getpid(), self.testq)
765 21bbbc9b Vangelis Koukis
            msg = self.testq.get()
766 21bbbc9b Vangelis Koukis
            log.debug("Dequeued msg: %s", msg)
767 21bbbc9b Vangelis Koukis
768 21bbbc9b Vangelis Koukis
            if msg == "TEST_RUNNER_TERMINATE":
769 21bbbc9b Vangelis Koukis
                raise SystemExit
770 21bbbc9b Vangelis Koukis
            elif issubclass(msg, unittest.TestCase):
771 21bbbc9b Vangelis Koukis
                # Assemble a TestSuite, and run it
772 21bbbc9b Vangelis Koukis
                suite = unittest.TestLoader().loadTestsFromTestCase(msg)
773 21bbbc9b Vangelis Koukis
                self.runner.run(suite)
774 21bbbc9b Vangelis Koukis
            else:
775 21bbbc9b Vangelis Koukis
                raise Exception("Cannot handle msg: %s" % msg)
776 21bbbc9b Vangelis Koukis
777 21bbbc9b Vangelis Koukis
778 99d41650 John Giannelos
779 21bbbc9b Vangelis Koukis
def _run_cases_in_parallel(cases, fanout=1, runner=None):
780 21bbbc9b Vangelis Koukis
    """Run instances of TestCase in parallel, in a number of distinct processes
781 21bbbc9b Vangelis Koukis

782 21bbbc9b Vangelis Koukis
    The cases iterable specifies the TestCases to be executed in parallel,
783 21bbbc9b Vangelis Koukis
    by test runners running in distinct processes.
784 21bbbc9b Vangelis Koukis
    The fanout parameter specifies the number of processes to spawn,
785 21bbbc9b Vangelis Koukis
    and defaults to 1.
786 21bbbc9b Vangelis Koukis
    The runner argument specifies the test runner class to use inside each
787 21bbbc9b Vangelis Koukis
    runner process.
788 21bbbc9b Vangelis Koukis

789 21bbbc9b Vangelis Koukis
    """
790 21bbbc9b Vangelis Koukis
    if runner is None:
791 00f87624 Vangelis Koukis
        runner = unittest.TextTestRunner(verbosity=2, failfast=True)
792 21bbbc9b Vangelis Koukis
793 21bbbc9b Vangelis Koukis
    # testq: The master process enqueues TestCase objects into this queue,
794 21bbbc9b Vangelis Koukis
    #        test runner processes pick them up for execution, in parallel.
795 21bbbc9b Vangelis Koukis
    testq = Queue()
796 21bbbc9b Vangelis Koukis
    runners = []
797 21bbbc9b Vangelis Koukis
    for i in xrange(0, fanout):
798 21bbbc9b Vangelis Koukis
        kwargs = dict(testq=testq, runner=runner)
799 21bbbc9b Vangelis Koukis
        runners.append(TestRunnerProcess(kwargs=kwargs))
800 21bbbc9b Vangelis Koukis
801 21bbbc9b Vangelis Koukis
    log.info("Spawning %d test runner processes", len(runners))
802 21bbbc9b Vangelis Koukis
    for p in runners:
803 21bbbc9b Vangelis Koukis
        p.start()
804 21bbbc9b Vangelis Koukis
    log.debug("Spawned %d test runners, PIDs are %s",
805 21bbbc9b Vangelis Koukis
              len(runners), [p.pid for p in runners])
806 21bbbc9b Vangelis Koukis
807 21bbbc9b Vangelis Koukis
    # Enqueue test cases
808 21bbbc9b Vangelis Koukis
    map(testq.put, cases)
809 21bbbc9b Vangelis Koukis
    map(testq.put, ["TEST_RUNNER_TERMINATE"] * len(runners))
810 21bbbc9b Vangelis Koukis
811 21bbbc9b Vangelis Koukis
    log.debug("Joining %d processes", len(runners))
812 21bbbc9b Vangelis Koukis
    for p in runners:
813 21bbbc9b Vangelis Koukis
        p.join()
814 21bbbc9b Vangelis Koukis
    log.debug("Done joining %d processes", len(runners))
815 4fdd25ab Vangelis Koukis
816 5a140b23 Vangelis Koukis
817 5a140b23 Vangelis Koukis
def _spawn_server_test_case(**kwargs):
818 5a140b23 Vangelis Koukis
    """Construct a new unit test case class from SpawnServerTestCase"""
819 5a140b23 Vangelis Koukis
820 c1d11f96 John Giannelos
    name = "SpawnServerTestCase_%s" % kwargs["imageid"]
821 5a140b23 Vangelis Koukis
    cls = type(name, (SpawnServerTestCase,), kwargs)
822 5a140b23 Vangelis Koukis
823 5a140b23 Vangelis Koukis
    # Patch extra parameters into test names by manipulating method docstrings
824 5a140b23 Vangelis Koukis
    for (mname, m) in \
825 5a140b23 Vangelis Koukis
        inspect.getmembers(cls, lambda x: inspect.ismethod(x)):
826 5a140b23 Vangelis Koukis
            if hasattr(m, __doc__):
827 5a140b23 Vangelis Koukis
                m.__func__.__doc__ = "[%s] %s" % (imagename, m.__doc__)
828 e72bcf60 Vangelis Koukis
829 e72bcf60 Vangelis Koukis
    # Make sure the class can be pickled, by listing it among
830 e72bcf60 Vangelis Koukis
    # the attributes of __main__. A PicklingError is raised otherwise.
831 e72bcf60 Vangelis Koukis
    setattr(__main__, name, cls)
832 65462ca9 John Giannelos
    return cls 
833 65462ca9 John Giannelos
834 65462ca9 John Giannelos
def _spawn_network_test_case(**kwargs):
835 65462ca9 John Giannelos
    """Construct a new unit test case class from NetworkTestCase"""
836 65462ca9 John Giannelos
837 65462ca9 John Giannelos
    name = "NetworkTestCase"+TEST_RUN_ID
838 65462ca9 John Giannelos
    cls = type(name, (NetworkTestCase,), kwargs)
839 65462ca9 John Giannelos
840 65462ca9 John Giannelos
    # Make sure the class can be pickled, by listing it among
841 65462ca9 John Giannelos
    # the attributes of __main__. A PicklingError is raised otherwise.
842 65462ca9 John Giannelos
    setattr(__main__, name, cls)
843 65462ca9 John Giannelos
    return cls 
844 5a140b23 Vangelis Koukis
845 5a140b23 Vangelis Koukis
846 5a140b23 Vangelis Koukis
def cleanup_servers(delete_stale=False):
847 74193008 John Giannelos
848 74193008 John Giannelos
    conf = Config()
849 74193008 John Giannelos
    conf.set('compute_token', TOKEN)
850 74193008 John Giannelos
    c = ComputeClient(conf)
851 74193008 John Giannelos
852 5a140b23 Vangelis Koukis
    servers = c.list_servers()
853 5a140b23 Vangelis Koukis
    stale = [s for s in servers if s["name"].startswith(SNF_TEST_PREFIX)]
854 5a140b23 Vangelis Koukis
855 4fdd25ab Vangelis Koukis
    if len(stale) == 0:
856 4fdd25ab Vangelis Koukis
        return
857 4fdd25ab Vangelis Koukis
858 5a140b23 Vangelis Koukis
    print >> sys.stderr, "Found these stale servers from previous runs:"
859 5a140b23 Vangelis Koukis
    print "    " + \
860 21bbbc9b Vangelis Koukis
          "\n    ".join(["%d: %s" % (s["id"], s["name"]) for s in stale])
861 5a140b23 Vangelis Koukis
862 5a140b23 Vangelis Koukis
    if delete_stale:
863 5a140b23 Vangelis Koukis
        print >> sys.stderr, "Deleting %d stale servers:" % len(stale)
864 5a140b23 Vangelis Koukis
        for server in stale:
865 21bbbc9b Vangelis Koukis
            c.delete_server(server["id"])
866 5a140b23 Vangelis Koukis
        print >> sys.stderr, "    ...done"
867 5a140b23 Vangelis Koukis
    else:
868 5a140b23 Vangelis Koukis
        print >> sys.stderr, "Use --delete-stale to delete them."
869 5a140b23 Vangelis Koukis
870 5a140b23 Vangelis Koukis
871 5a140b23 Vangelis Koukis
def parse_arguments(args):
872 5a140b23 Vangelis Koukis
    from optparse import OptionParser
873 5a140b23 Vangelis Koukis
874 5a140b23 Vangelis Koukis
    kw = {}
875 5a140b23 Vangelis Koukis
    kw["usage"] = "%prog [options]"
876 5a140b23 Vangelis Koukis
    kw["description"] = \
877 5a140b23 Vangelis Koukis
        "%prog runs a number of test scenarios on a " \
878 5a140b23 Vangelis Koukis
        "Synnefo deployment."
879 5a140b23 Vangelis Koukis
880 5a140b23 Vangelis Koukis
    parser = OptionParser(**kw)
881 5a140b23 Vangelis Koukis
    parser.disable_interspersed_args()
882 21bbbc9b Vangelis Koukis
    parser.add_option("--api",
883 21bbbc9b Vangelis Koukis
                      action="store", type="string", dest="api",
884 21bbbc9b Vangelis Koukis
                      help="The API URI to use to reach the Synnefo API",
885 21bbbc9b Vangelis Koukis
                      default=DEFAULT_API)
886 21bbbc9b Vangelis Koukis
    parser.add_option("--token",
887 21bbbc9b Vangelis Koukis
                      action="store", type="string", dest="token",
888 38d247df Kostas Papadimitriou
                      help="The token to use for authentication to the API")
889 00f87624 Vangelis Koukis
    parser.add_option("--nofailfast",
890 00f87624 Vangelis Koukis
                      action="store_true", dest="nofailfast",
891 00f87624 Vangelis Koukis
                      help="Do not fail immediately if one of the tests " \
892 00f87624 Vangelis Koukis
                           "fails (EXPERIMENTAL)",
893 bc14ba88 Vangelis Koukis
                      default=False)
894 5a140b23 Vangelis Koukis
    parser.add_option("--action-timeout",
895 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="action_timeout",
896 5a140b23 Vangelis Koukis
                      metavar="TIMEOUT",
897 5a140b23 Vangelis Koukis
                      help="Wait SECONDS seconds for a server action to " \
898 5a140b23 Vangelis Koukis
                           "complete, then the test is considered failed",
899 9e4682b5 John Giannelos
                      default=100)
900 5a140b23 Vangelis Koukis
    parser.add_option("--build-warning",
901 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="build_warning",
902 5a140b23 Vangelis Koukis
                      metavar="TIMEOUT",
903 5a140b23 Vangelis Koukis
                      help="Warn if TIMEOUT seconds have passed and a " \
904 5a140b23 Vangelis Koukis
                           "build operation is still pending",
905 5a140b23 Vangelis Koukis
                      default=600)
906 5a140b23 Vangelis Koukis
    parser.add_option("--build-fail",
907 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="build_fail",
908 5a140b23 Vangelis Koukis
                      metavar="BUILD_TIMEOUT",
909 5a140b23 Vangelis Koukis
                      help="Fail the test if TIMEOUT seconds have passed " \
910 5a140b23 Vangelis Koukis
                           "and a build operation is still incomplete",
911 5a140b23 Vangelis Koukis
                      default=900)
912 5a140b23 Vangelis Koukis
    parser.add_option("--query-interval",
913 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="query_interval",
914 5a140b23 Vangelis Koukis
                      metavar="INTERVAL",
915 5a140b23 Vangelis Koukis
                      help="Query server status when requests are pending " \
916 5a140b23 Vangelis Koukis
                           "every INTERVAL seconds",
917 5a140b23 Vangelis Koukis
                      default=3)
918 21bbbc9b Vangelis Koukis
    parser.add_option("--fanout",
919 21bbbc9b Vangelis Koukis
                      action="store", type="int", dest="fanout",
920 5a140b23 Vangelis Koukis
                      metavar="COUNT",
921 21bbbc9b Vangelis Koukis
                      help="Spawn up to COUNT child processes to execute " \
922 21bbbc9b Vangelis Koukis
                           "in parallel, essentially have up to COUNT " \
923 00f87624 Vangelis Koukis
                           "server build requests outstanding (EXPERIMENTAL)",
924 5a140b23 Vangelis Koukis
                      default=1)
925 5a140b23 Vangelis Koukis
    parser.add_option("--force-flavor",
926 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="force_flavorid",
927 5a140b23 Vangelis Koukis
                      metavar="FLAVOR ID",
928 5a140b23 Vangelis Koukis
                      help="Force all server creations to use the specified "\
929 5a140b23 Vangelis Koukis
                           "FLAVOR ID instead of a randomly chosen one, " \
930 5a140b23 Vangelis Koukis
                           "useful if disk space is scarce",
931 00f87624 Vangelis Koukis
                      default=None)
932 7f62a0b5 Vangelis Koukis
    parser.add_option("--image-id",
933 7f62a0b5 Vangelis Koukis
                      action="store", type="string", dest="force_imageid",
934 00f87624 Vangelis Koukis
                      metavar="IMAGE ID",
935 7f62a0b5 Vangelis Koukis
                      help="Test the specified image id, use 'all' to test " \
936 7f62a0b5 Vangelis Koukis
                           "all available images (mandatory argument)",
937 00f87624 Vangelis Koukis
                      default=None)
938 5a140b23 Vangelis Koukis
    parser.add_option("--show-stale",
939 5a140b23 Vangelis Koukis
                      action="store_true", dest="show_stale",
940 5a140b23 Vangelis Koukis
                      help="Show stale servers from previous runs, whose "\
941 21bbbc9b Vangelis Koukis
                           "name starts with `%s'" % SNF_TEST_PREFIX,
942 5a140b23 Vangelis Koukis
                      default=False)
943 5a140b23 Vangelis Koukis
    parser.add_option("--delete-stale",
944 5a140b23 Vangelis Koukis
                      action="store_true", dest="delete_stale",
945 5a140b23 Vangelis Koukis
                      help="Delete stale servers from previous runs, whose "\
946 21bbbc9b Vangelis Koukis
                           "name starts with `%s'" % SNF_TEST_PREFIX,
947 5a140b23 Vangelis Koukis
                      default=False)
948 9659e075 John Giannelos
    parser.add_option("--force-personality",
949 65462ca9 John Giannelos
                      action="store", type="string", dest="personality_path",
950 9659e075 John Giannelos
                      help="Force a personality file injection. File path required. ",
951 9659e075 John Giannelos
                      default=None)
952 9659e075 John Giannelos
    
953 5a140b23 Vangelis Koukis
954 5a140b23 Vangelis Koukis
    # FIXME: Change the default for build-fanout to 10
955 5a140b23 Vangelis Koukis
    # FIXME: Allow the user to specify a specific set of Images to test
956 5a140b23 Vangelis Koukis
957 5a140b23 Vangelis Koukis
    (opts, args) = parser.parse_args(args)
958 5a140b23 Vangelis Koukis
959 5a140b23 Vangelis Koukis
    # Verify arguments
960 5a140b23 Vangelis Koukis
    if opts.delete_stale:
961 5a140b23 Vangelis Koukis
        opts.show_stale = True
962 5a140b23 Vangelis Koukis
963 7f62a0b5 Vangelis Koukis
    if not opts.show_stale:
964 7f62a0b5 Vangelis Koukis
        if not opts.force_imageid:
965 7f62a0b5 Vangelis Koukis
            print >>sys.stderr, "The --image-id argument is mandatory."
966 7f62a0b5 Vangelis Koukis
            parser.print_help()
967 7f62a0b5 Vangelis Koukis
            sys.exit(1)
968 7f62a0b5 Vangelis Koukis
969 7f62a0b5 Vangelis Koukis
        if opts.force_imageid != 'all':
970 7f62a0b5 Vangelis Koukis
            try:
971 99d41650 John Giannelos
                opts.force_imageid = str(opts.force_imageid)
972 7f62a0b5 Vangelis Koukis
            except ValueError:
973 7f62a0b5 Vangelis Koukis
                print >>sys.stderr, "Invalid value specified for --image-id." \
974 e94a9d8c John Giannelos
                                    "Use a valid id, or `all'."
975 7f62a0b5 Vangelis Koukis
                sys.exit(1)
976 7f62a0b5 Vangelis Koukis
977 5a140b23 Vangelis Koukis
    return (opts, args)
978 5a140b23 Vangelis Koukis
979 5a140b23 Vangelis Koukis
980 5a140b23 Vangelis Koukis
def main():
981 5a140b23 Vangelis Koukis
    """Assemble test cases into a test suite, and run it
982 5a140b23 Vangelis Koukis

983 5a140b23 Vangelis Koukis
    IMPORTANT: Tests have dependencies and have to be run in the specified
984 5a140b23 Vangelis Koukis
    order inside a single test case. They communicate through attributes of the
985 21bbbc9b Vangelis Koukis
    corresponding TestCase class (shared fixtures). Distinct subclasses of
986 21bbbc9b Vangelis Koukis
    TestCase MAY SHARE NO DATA, since they are run in parallel, in distinct
987 21bbbc9b Vangelis Koukis
    test runner processes.
988 5a140b23 Vangelis Koukis

989 5a140b23 Vangelis Koukis
    """
990 5a140b23 Vangelis Koukis
    (opts, args) = parse_arguments(sys.argv[1:])
991 5a140b23 Vangelis Koukis
992 21bbbc9b Vangelis Koukis
    global API, TOKEN
993 21bbbc9b Vangelis Koukis
    API = opts.api
994 21bbbc9b Vangelis Koukis
    TOKEN = opts.token
995 21bbbc9b Vangelis Koukis
996 5a140b23 Vangelis Koukis
    # Cleanup stale servers from previous runs
997 5a140b23 Vangelis Koukis
    if opts.show_stale:
998 5a140b23 Vangelis Koukis
        cleanup_servers(delete_stale=opts.delete_stale)
999 5a140b23 Vangelis Koukis
        return 0
1000 5a140b23 Vangelis Koukis
1001 5a140b23 Vangelis Koukis
    # Initialize a kamaki instance, get flavors, images
1002 74193008 John Giannelos
1003 74193008 John Giannelos
    conf = Config()
1004 74193008 John Giannelos
    conf.set('compute_token', TOKEN)
1005 74193008 John Giannelos
    c = ComputeClient(conf)
1006 74193008 John Giannelos
1007 5a140b23 Vangelis Koukis
    DIMAGES = c.list_images(detail=True)
1008 5a140b23 Vangelis Koukis
    DFLAVORS = c.list_flavors(detail=True)
1009 5a140b23 Vangelis Koukis
1010 21bbbc9b Vangelis Koukis
    # FIXME: logging, log, LOG PID, TEST_RUN_ID, arguments
1011 5a140b23 Vangelis Koukis
    # Run them: FIXME: In parallel, FAILEARLY, catchbreak?
1012 5a140b23 Vangelis Koukis
    #unittest.main(verbosity=2, catchbreak=True)
1013 5a140b23 Vangelis Koukis
1014 e94a9d8c John Giannelos
    if opts.force_imageid == 'all':
1015 e94a9d8c John Giannelos
        test_images = DIMAGES
1016 e94a9d8c John Giannelos
    else:
1017 e94a9d8c John Giannelos
        test_images = filter(lambda x: x["id"] == opts.force_imageid, DIMAGES)
1018 e94a9d8c John Giannelos
1019 00f87624 Vangelis Koukis
    for image in test_images:
1020 99d41650 John Giannelos
        imageid = str(image["id"])
1021 99d41650 John Giannelos
        flavorid = choice([f["id"] for f in DFLAVORS if f["disk"] >= 20])
1022 21bbbc9b Vangelis Koukis
        imagename = image["name"]
1023 9659e075 John Giannelos
        
1024 9659e075 John Giannelos
        
1025 9659e075 John Giannelos
        if opts.personality_path != None:
1026 9659e075 John Giannelos
            f = open(opts.personality_path)
1027 9659e075 John Giannelos
            content = b64encode(f.read())
1028 9659e075 John Giannelos
            personality = []
1029 9659e075 John Giannelos
            st = os.stat(opts.personality_path)
1030 9659e075 John Giannelos
            personality.append({
1031 9659e075 John Giannelos
                    'path': '/root/test_inj_file',
1032 9659e075 John Giannelos
                    'owner': 'root',
1033 9659e075 John Giannelos
                    'group': 'root',
1034 9659e075 John Giannelos
                    'mode': 0x7777 & st.st_mode,
1035 9659e075 John Giannelos
                    'contents': content
1036 9659e075 John Giannelos
                    })
1037 9659e075 John Giannelos
        else:
1038 9659e075 John Giannelos
            personality = None
1039 9659e075 John Giannelos
1040 21bbbc9b Vangelis Koukis
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
1041 21bbbc9b Vangelis Koukis
        is_windows = imagename.lower().find("windows") >= 0
1042 9e4682b5 John Giannelos
        
1043 65462ca9 John Giannelos
    ServerTestCase = _spawn_server_test_case(imageid=imageid, flavorid=flavorid,
1044 65462ca9 John Giannelos
                                             imagename=imagename,
1045 65462ca9 John Giannelos
                                             personality=personality,
1046 65462ca9 John Giannelos
                                             servername=servername,
1047 65462ca9 John Giannelos
                                             is_windows=is_windows,
1048 65462ca9 John Giannelos
                                             action_timeout=opts.action_timeout,
1049 65462ca9 John Giannelos
                                             build_warning=opts.build_warning,
1050 65462ca9 John Giannelos
                                             build_fail=opts.build_fail,
1051 65462ca9 John Giannelos
                                             query_interval=opts.query_interval,
1052 65462ca9 John Giannelos
                                             )
1053 21bbbc9b Vangelis Koukis
1054 99d41650 John Giannelos
1055 e94a9d8c John Giannelos
    #Running all the testcases sequentially
1056 9e4682b5 John Giannelos
    #seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase, ServerTestCase, NetworkTestCase]
1057 65462ca9 John Giannelos
    
1058 65462ca9 John Giannelos
    newNetworkTestCase = _spawn_network_test_case(action_timeout = opts.action_timeout,
1059 65462ca9 John Giannelos
                                                  query_interval = opts.query_interval)
1060 65462ca9 John Giannelos
    
1061 65462ca9 John Giannelos
    seq_cases = [newNetworkTestCase]
1062 99d41650 John Giannelos
    for case in seq_cases:
1063 99d41650 John Giannelos
        suite = unittest.TestLoader().loadTestsFromTestCase(case)
1064 99d41650 John Giannelos
        unittest.TextTestRunner(verbosity=2).run(suite)
1065 e94a9d8c John Giannelos
        
1066 99d41650 John Giannelos
    
1067 99d41650 John Giannelos
1068 99d41650 John Giannelos
    # # The Following cases run sequentially
1069 99d41650 John Giannelos
    # seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase]
1070 99d41650 John Giannelos
    # _run_cases_in_parallel(seq_cases, fanout=3, runner=runner)
1071 99d41650 John Giannelos
1072 99d41650 John Giannelos
    # # The following cases run in parallel
1073 99d41650 John Giannelos
    # par_cases = []
1074 99d41650 John Giannelos
1075 99d41650 John Giannelos
    # if opts.force_imageid == 'all':
1076 99d41650 John Giannelos
    #     test_images = DIMAGES
1077 99d41650 John Giannelos
    # else:
1078 99d41650 John Giannelos
    #     test_images = filter(lambda x: x["id"] == opts.force_imageid, DIMAGES)
1079 99d41650 John Giannelos
1080 99d41650 John Giannelos
    # for image in test_images:
1081 99d41650 John Giannelos
    #     imageid = image["id"]
1082 99d41650 John Giannelos
    #     imagename = image["name"]
1083 99d41650 John Giannelos
    #     if opts.force_flavorid:
1084 99d41650 John Giannelos
    #         flavorid = opts.force_flavorid
1085 99d41650 John Giannelos
    #     else:
1086 99d41650 John Giannelos
    #         flavorid = choice([f["id"] for f in DFLAVORS if f["disk"] >= 20])
1087 99d41650 John Giannelos
    #     personality = None   # FIXME
1088 99d41650 John Giannelos
    #     servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
1089 99d41650 John Giannelos
    #     is_windows = imagename.lower().find("windows") >= 0
1090 99d41650 John Giannelos
    #     case = _spawn_server_test_case(imageid=str(imageid), flavorid=flavorid,
1091 99d41650 John Giannelos
    #                                    imagename=imagename,
1092 99d41650 John Giannelos
    #                                    personality=personality,
1093 99d41650 John Giannelos
    #                                    servername=servername,
1094 99d41650 John Giannelos
    #                                    is_windows=is_windows,
1095 99d41650 John Giannelos
    #                                    action_timeout=opts.action_timeout,
1096 99d41650 John Giannelos
    #                                    build_warning=opts.build_warning,
1097 99d41650 John Giannelos
    #                                    build_fail=opts.build_fail,
1098 99d41650 John Giannelos
    #                                    query_interval=opts.query_interval)
1099 99d41650 John Giannelos
    #     par_cases.append(case)
1100 99d41650 John Giannelos
1101 99d41650 John Giannelos
    # _run_cases_in_parallel(par_cases, fanout=opts.fanout, runner=runner)
1102 5a140b23 Vangelis Koukis
1103 5a140b23 Vangelis Koukis
if __name__ == "__main__":
1104 5a140b23 Vangelis Koukis
    sys.exit(main())