Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (48.2 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 96da37c8 John Giannelos
from kamaki.clients.compute import ComputeClient
60 96da37c8 John Giannelos
from kamaki.clients.cyclades import CycladesClient
61 96da37c8 John Giannelos
62 1c636ad6 John Giannelos
63 bc14ba88 Vangelis Koukis
from vncauthproxy.d3des import generate_response as d3des_generate_response
64 5a140b23 Vangelis Koukis
65 5a140b23 Vangelis Koukis
# Use backported unittest functionality if Python < 2.7
66 5a140b23 Vangelis Koukis
try:
67 5a140b23 Vangelis Koukis
    import unittest2 as unittest
68 5a140b23 Vangelis Koukis
except ImportError:
69 bc14ba88 Vangelis Koukis
    if sys.version_info < (2, 7):
70 bc14ba88 Vangelis Koukis
        raise Exception("The unittest2 package is required for Python < 2.7")
71 5a140b23 Vangelis Koukis
    import unittest
72 5a140b23 Vangelis Koukis
73 5a140b23 Vangelis Koukis
74 21bbbc9b Vangelis Koukis
API = None
75 21bbbc9b Vangelis Koukis
TOKEN = None
76 567ffb85 John Giannelos
DEFAULT_API = "https://cyclades.okeanos.grnet.gr/api/v1.1"
77 38d247df Kostas Papadimitriou
78 5a140b23 Vangelis Koukis
# A unique id identifying this test run
79 21bbbc9b Vangelis Koukis
TEST_RUN_ID = datetime.datetime.strftime(datetime.datetime.now(),
80 21bbbc9b Vangelis Koukis
                                         "%Y%m%d%H%M%S")
81 21bbbc9b Vangelis Koukis
SNF_TEST_PREFIX = "snf-test-"
82 5a140b23 Vangelis Koukis
83 5a140b23 Vangelis Koukis
# Setup logging (FIXME - verigak)
84 5a140b23 Vangelis Koukis
logging.basicConfig(format="%(message)s")
85 00f87624 Vangelis Koukis
log = logging.getLogger("burnin")
86 5a140b23 Vangelis Koukis
log.setLevel(logging.INFO)
87 5a140b23 Vangelis Koukis
88 5a140b23 Vangelis Koukis
class UnauthorizedTestCase(unittest.TestCase):
89 5a140b23 Vangelis Koukis
    def test_unauthorized_access(self):
90 5a140b23 Vangelis Koukis
        """Test access without a valid token fails"""
91 1c636ad6 John Giannelos
        falseToken = '12345'
92 567ffb85 John Giannelos
        c=ComputeClient(API, falseToken)
93 1c636ad6 John Giannelos
94 5a140b23 Vangelis Koukis
        with self.assertRaises(ClientError) as cm:
95 5a140b23 Vangelis Koukis
            c.list_servers()
96 5a140b23 Vangelis Koukis
        self.assertEqual(cm.exception.status, 401)
97 5a140b23 Vangelis Koukis
98 5a140b23 Vangelis Koukis
99 5a140b23 Vangelis Koukis
class ImagesTestCase(unittest.TestCase):
100 5a140b23 Vangelis Koukis
    """Test image lists for consistency"""
101 5a140b23 Vangelis Koukis
    @classmethod
102 5a140b23 Vangelis Koukis
    def setUpClass(cls):
103 5a140b23 Vangelis Koukis
        """Initialize kamaki, get (detailed) list of images"""
104 5a140b23 Vangelis Koukis
        log.info("Getting simple and detailed list of images")
105 1c636ad6 John Giannelos
106 567ffb85 John Giannelos
        cls.client = ComputeClient(API, TOKEN)
107 5a140b23 Vangelis Koukis
        cls.images = cls.client.list_images()
108 5a140b23 Vangelis Koukis
        cls.dimages = cls.client.list_images(detail=True)
109 5a140b23 Vangelis Koukis
110 5a140b23 Vangelis Koukis
    def test_001_list_images(self):
111 5a140b23 Vangelis Koukis
        """Test image list actually returns images"""
112 5a140b23 Vangelis Koukis
        self.assertGreater(len(self.images), 0)
113 5a140b23 Vangelis Koukis
114 5a140b23 Vangelis Koukis
    def test_002_list_images_detailed(self):
115 5a140b23 Vangelis Koukis
        """Test detailed image list is the same length as list"""
116 5a140b23 Vangelis Koukis
        self.assertEqual(len(self.dimages), len(self.images))
117 5a140b23 Vangelis Koukis
118 5a140b23 Vangelis Koukis
    def test_003_same_image_names(self):
119 5a140b23 Vangelis Koukis
        """Test detailed and simple image list contain same names"""
120 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.images))
121 5a140b23 Vangelis Koukis
        dnames = sorted(map(lambda x: x["name"], self.dimages))
122 5a140b23 Vangelis Koukis
        self.assertEqual(names, dnames)
123 5a140b23 Vangelis Koukis
124 5a140b23 Vangelis Koukis
    def test_004_unique_image_names(self):
125 5a140b23 Vangelis Koukis
        """Test images have unique names"""
126 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.images))
127 5a140b23 Vangelis Koukis
        self.assertEqual(sorted(list(set(names))), names)
128 5a140b23 Vangelis Koukis
129 5a140b23 Vangelis Koukis
    def test_005_image_metadata(self):
130 5a140b23 Vangelis Koukis
        """Test every image has specific metadata defined"""
131 1c636ad6 John Giannelos
        keys = frozenset(["os", "description", "size"])
132 5a140b23 Vangelis Koukis
        for i in self.dimages:
133 5a140b23 Vangelis Koukis
            self.assertTrue(keys.issubset(i["metadata"]["values"].keys()))
134 5a140b23 Vangelis Koukis
135 5a140b23 Vangelis Koukis
136 5a140b23 Vangelis Koukis
class FlavorsTestCase(unittest.TestCase):
137 5a140b23 Vangelis Koukis
    """Test flavor lists for consistency"""
138 5a140b23 Vangelis Koukis
    @classmethod
139 5a140b23 Vangelis Koukis
    def setUpClass(cls):
140 5a140b23 Vangelis Koukis
        """Initialize kamaki, get (detailed) list of flavors"""
141 5a140b23 Vangelis Koukis
        log.info("Getting simple and detailed list of flavors")
142 1c636ad6 John Giannelos
143 567ffb85 John Giannelos
        cls.client = ComputeClient(API, TOKEN)
144 5a140b23 Vangelis Koukis
        cls.flavors = cls.client.list_flavors()
145 5a140b23 Vangelis Koukis
        cls.dflavors = cls.client.list_flavors(detail=True)
146 5a140b23 Vangelis Koukis
147 5a140b23 Vangelis Koukis
    def test_001_list_flavors(self):
148 5a140b23 Vangelis Koukis
        """Test flavor list actually returns flavors"""
149 5a140b23 Vangelis Koukis
        self.assertGreater(len(self.flavors), 0)
150 5a140b23 Vangelis Koukis
151 5a140b23 Vangelis Koukis
    def test_002_list_flavors_detailed(self):
152 5a140b23 Vangelis Koukis
        """Test detailed flavor list is the same length as list"""
153 5a140b23 Vangelis Koukis
        self.assertEquals(len(self.dflavors), len(self.flavors))
154 5a140b23 Vangelis Koukis
155 5a140b23 Vangelis Koukis
    def test_003_same_flavor_names(self):
156 5a140b23 Vangelis Koukis
        """Test detailed and simple flavor list contain same names"""
157 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.flavors))
158 5a140b23 Vangelis Koukis
        dnames = sorted(map(lambda x: x["name"], self.dflavors))
159 5a140b23 Vangelis Koukis
        self.assertEqual(names, dnames)
160 5a140b23 Vangelis Koukis
161 5a140b23 Vangelis Koukis
    def test_004_unique_flavor_names(self):
162 5a140b23 Vangelis Koukis
        """Test flavors have unique names"""
163 5a140b23 Vangelis Koukis
        names = sorted(map(lambda x: x["name"], self.flavors))
164 5a140b23 Vangelis Koukis
        self.assertEqual(sorted(list(set(names))), names)
165 5a140b23 Vangelis Koukis
166 5a140b23 Vangelis Koukis
    def test_005_well_formed_flavor_names(self):
167 5a140b23 Vangelis Koukis
        """Test flavors have names of the form CxxRyyDzz
168 5a140b23 Vangelis Koukis

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

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

441 65462ca9 John Giannelos
        Implementation of RFB protocol follows
442 65462ca9 John Giannelos
        http://www.realvnc.com/docs/rfbproto.pdf.
443 bc14ba88 Vangelis Koukis

444 65462ca9 John Giannelos
        """
445 9659e075 John Giannelos
        
446 65462ca9 John Giannelos
        console = self.cyclades.get_server_console(self.serverid)
447 65462ca9 John Giannelos
        self.assertEquals(console['type'], "vnc")
448 65462ca9 John Giannelos
        sock = self._insist_on_tcp_connection(socket.AF_UNSPEC,
449 65462ca9 John Giannelos
                                        console["host"], console["port"])
450 65462ca9 John Giannelos
451 65462ca9 John Giannelos
        # Step 1. ProtocolVersion message (par. 6.1.1)
452 65462ca9 John Giannelos
        version = sock.recv(1024)
453 65462ca9 John Giannelos
        self.assertEquals(version, 'RFB 003.008\n')
454 65462ca9 John Giannelos
        sock.send(version)
455 65462ca9 John Giannelos
456 65462ca9 John Giannelos
        # Step 2. Security (par 6.1.2): Only VNC Authentication supported
457 65462ca9 John Giannelos
        sec = sock.recv(1024)
458 65462ca9 John Giannelos
        self.assertEquals(list(sec), ['\x01', '\x02'])
459 65462ca9 John Giannelos
460 65462ca9 John Giannelos
        # Step 3. Request VNC Authentication (par 6.1.2)
461 65462ca9 John Giannelos
        sock.send('\x02')
462 65462ca9 John Giannelos
463 65462ca9 John Giannelos
        # Step 4. Receive Challenge (par 6.2.2)
464 65462ca9 John Giannelos
        challenge = sock.recv(1024)
465 65462ca9 John Giannelos
        self.assertEquals(len(challenge), 16)
466 65462ca9 John Giannelos
467 65462ca9 John Giannelos
        # Step 5. DES-Encrypt challenge, use password as key (par 6.2.2)
468 65462ca9 John Giannelos
        response = d3des_generate_response(
469 65462ca9 John Giannelos
            (console["password"] + '\0' * 8)[:8], challenge)
470 65462ca9 John Giannelos
        sock.send(response)
471 65462ca9 John Giannelos
472 65462ca9 John Giannelos
        # Step 6. SecurityResult (par 6.1.3)
473 65462ca9 John Giannelos
        result = sock.recv(4)
474 65462ca9 John Giannelos
        self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
475 65462ca9 John Giannelos
        sock.close()
476 74193008 John Giannelos
        
477 5a140b23 Vangelis Koukis
    def test_004_server_has_ipv4(self):
478 5a140b23 Vangelis Koukis
        """Test active server has a valid IPv4 address"""
479 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
480 5a140b23 Vangelis Koukis
        ipv4 = self._get_ipv4(server)
481 5a140b23 Vangelis Koukis
        self.assertEquals(IP(ipv4).version(), 4)
482 5a140b23 Vangelis Koukis
483 65462ca9 John Giannelos
    def test_005_server_has_ipv6(self):
484 65462ca9 John Giannelos
        """Test active server has a valid IPv6 address"""
485 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
486 65462ca9 John Giannelos
        ipv6 = self._get_ipv6(server)
487 65462ca9 John Giannelos
        self.assertEquals(IP(ipv6).version(), 6)
488 5a140b23 Vangelis Koukis
489 5a140b23 Vangelis Koukis
    def test_006_server_responds_to_ping_IPv4(self):
490 5a140b23 Vangelis Koukis
        """Test server responds to ping on IPv4 address"""
491 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
492 bc14ba88 Vangelis Koukis
        ip = self._get_ipv4(server)
493 bc14ba88 Vangelis Koukis
        self._try_until_timeout_expires(self.action_timeout,
494 bc14ba88 Vangelis Koukis
                                        self.action_timeout,
495 4fdd25ab Vangelis Koukis
                                        "PING IPv4 to %s" % ip,
496 4fdd25ab Vangelis Koukis
                                        self._ping_once,
497 bc14ba88 Vangelis Koukis
                                        False, ip)
498 5a140b23 Vangelis Koukis
499 65462ca9 John Giannelos
    def test_007_server_responds_to_ping_IPv6(self):
500 65462ca9 John Giannelos
        """Test server responds to ping on IPv6 address"""
501 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
502 65462ca9 John Giannelos
        ip = self._get_ipv6(server)
503 65462ca9 John Giannelos
        self._try_until_timeout_expires(self.action_timeout,
504 65462ca9 John Giannelos
                                        self.action_timeout,
505 65462ca9 John Giannelos
                                        "PING IPv6 to %s" % ip,
506 65462ca9 John Giannelos
                                        self._ping_once,
507 65462ca9 John Giannelos
                                        True, ip)
508 5a140b23 Vangelis Koukis
509 5a140b23 Vangelis Koukis
    def test_008_submit_shutdown_request(self):
510 5a140b23 Vangelis Koukis
        """Test submit request to shutdown server"""
511 74193008 John Giannelos
        self.cyclades.shutdown_server(self.serverid)
512 5a140b23 Vangelis Koukis
513 5a140b23 Vangelis Koukis
    def test_009_server_becomes_stopped(self):
514 5a140b23 Vangelis Koukis
        """Test server becomes STOPPED"""
515 bc14ba88 Vangelis Koukis
        self._insist_on_status_transition("ACTIVE", "STOPPED",
516 bc14ba88 Vangelis Koukis
                                         self.action_timeout,
517 5a140b23 Vangelis Koukis
                                         self.action_timeout)
518 5a140b23 Vangelis Koukis
519 5a140b23 Vangelis Koukis
    def test_010_submit_start_request(self):
520 5a140b23 Vangelis Koukis
        """Test submit start server request"""
521 74193008 John Giannelos
        self.cyclades.start_server(self.serverid)
522 5a140b23 Vangelis Koukis
523 5a140b23 Vangelis Koukis
    def test_011_server_becomes_active(self):
524 5a140b23 Vangelis Koukis
        """Test server becomes ACTIVE again"""
525 bc14ba88 Vangelis Koukis
        self._insist_on_status_transition("STOPPED", "ACTIVE",
526 bc14ba88 Vangelis Koukis
                                         self.action_timeout,
527 5a140b23 Vangelis Koukis
                                         self.action_timeout)
528 5a140b23 Vangelis Koukis
529 5a140b23 Vangelis Koukis
    def test_011a_server_responds_to_ping_IPv4(self):
530 5a140b23 Vangelis Koukis
        """Test server OS is actually up and running again"""
531 5a140b23 Vangelis Koukis
        self.test_006_server_responds_to_ping_IPv4()
532 5a140b23 Vangelis Koukis
533 5a140b23 Vangelis Koukis
    def test_012_ssh_to_server_IPv4(self):
534 5a140b23 Vangelis Koukis
        """Test SSH to server public IPv4 works, verify hostname"""
535 bc14ba88 Vangelis Koukis
        self._skipIf(self.is_windows, "only valid for Linux servers")
536 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
537 bc14ba88 Vangelis Koukis
        self._insist_on_ssh_hostname(self._get_ipv4(server),
538 bc14ba88 Vangelis Koukis
                                     self.username, self.passwd)
539 5a140b23 Vangelis Koukis
540 65462ca9 John Giannelos
    def test_013_ssh_to_server_IPv6(self):
541 65462ca9 John Giannelos
        """Test SSH to server public IPv6 works, verify hostname"""
542 65462ca9 John Giannelos
        self._skipIf(self.is_windows, "only valid for Linux servers")
543 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
544 65462ca9 John Giannelos
        self._insist_on_ssh_hostname(self._get_ipv6(server),
545 65462ca9 John Giannelos
                                     self.username, self.passwd)
546 5a140b23 Vangelis Koukis
547 5a140b23 Vangelis Koukis
    def test_014_rdp_to_server_IPv4(self):
548 5a140b23 Vangelis Koukis
        "Test RDP connection to server public IPv4 works"""
549 bc14ba88 Vangelis Koukis
        self._skipIf(not self.is_windows, "only valid for Windows servers")
550 5a140b23 Vangelis Koukis
        server = self.client.get_server_details(self.serverid)
551 5a140b23 Vangelis Koukis
        ipv4 = self._get_ipv4(server)
552 bc14ba88 Vangelis Koukis
        sock = _insist_on_tcp_connection(socket.AF_INET, ipv4, 3389)
553 5a140b23 Vangelis Koukis
554 5a140b23 Vangelis Koukis
        # No actual RDP processing done. We assume the RDP server is there
555 5a140b23 Vangelis Koukis
        # if the connection to the RDP port is successful.
556 cb1fa17c Vangelis Koukis
        # FIXME: Use rdesktop, analyze exit code? see manpage [costasd]
557 5a140b23 Vangelis Koukis
        sock.close()
558 5a140b23 Vangelis Koukis
559 65462ca9 John Giannelos
    def test_015_rdp_to_server_IPv6(self):
560 65462ca9 John Giannelos
        "Test RDP connection to server public IPv6 works"""
561 65462ca9 John Giannelos
        self._skipIf(not self.is_windows, "only valid for Windows servers")
562 65462ca9 John Giannelos
        server = self.client.get_server_details(self.serverid)
563 65462ca9 John Giannelos
        ipv6 = self._get_ipv6(server)
564 65462ca9 John Giannelos
        sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
565 5a140b23 Vangelis Koukis
566 65462ca9 John Giannelos
        # No actual RDP processing done. We assume the RDP server is there
567 65462ca9 John Giannelos
        # if the connection to the RDP port is successful.
568 65462ca9 John Giannelos
        sock.close()
569 5a140b23 Vangelis Koukis
570 5a140b23 Vangelis Koukis
    def test_016_personality_is_enforced(self):
571 5a140b23 Vangelis Koukis
        """Test file injection for personality enforcement"""
572 bc14ba88 Vangelis Koukis
        self._skipIf(self.is_windows, "only implemented for Linux servers")
573 9659e075 John Giannelos
        self._skipIf(self.personality == None, "No personality file selected")
574 77054bf5 John Giannelos
575 77054bf5 John Giannelos
        server = self.client.get_server_details(self.serverid)
576 77054bf5 John Giannelos
577 9659e075 John Giannelos
        for inj_file in self.personality:
578 9659e075 John Giannelos
            equal_files = self._check_file_through_ssh(self._get_ipv4(server), inj_file['owner'], 
579 9659e075 John Giannelos
                                                       self.passwd, inj_file['path'], inj_file['contents'])
580 9659e075 John Giannelos
            self.assertTrue(equal_files)
581 77054bf5 John Giannelos
        
582 5a140b23 Vangelis Koukis
583 4fdd25ab Vangelis Koukis
    def test_017_submit_delete_request(self):
584 4fdd25ab Vangelis Koukis
        """Test submit request to delete server"""
585 4fdd25ab Vangelis Koukis
        self.client.delete_server(self.serverid)
586 4fdd25ab Vangelis Koukis
587 4fdd25ab Vangelis Koukis
    def test_018_server_becomes_deleted(self):
588 4fdd25ab Vangelis Koukis
        """Test server becomes DELETED"""
589 4fdd25ab Vangelis Koukis
        self._insist_on_status_transition("ACTIVE", "DELETED",
590 4fdd25ab Vangelis Koukis
                                         self.action_timeout,
591 4fdd25ab Vangelis Koukis
                                         self.action_timeout)
592 4fdd25ab Vangelis Koukis
593 4fdd25ab Vangelis Koukis
    def test_019_server_no_longer_in_server_list(self):
594 4fdd25ab Vangelis Koukis
        """Test server is no longer in server list"""
595 4fdd25ab Vangelis Koukis
        servers = self.client.list_servers()
596 21bbbc9b Vangelis Koukis
        self.assertNotIn(self.serverid, [s["id"] for s in servers])
597 21bbbc9b Vangelis Koukis
598 21bbbc9b Vangelis Koukis
599 9e4682b5 John Giannelos
class NetworkTestCase(unittest.TestCase):
600 e94a9d8c John Giannelos
    """ Testing networking in cyclades """
601 65462ca9 John Giannelos
  
602 9e4682b5 John Giannelos
    @classmethod
603 e94a9d8c John Giannelos
    def setUpClass(cls):
604 e94a9d8c John Giannelos
        "Initialize kamaki, get list of current networks"
605 567ffb85 John Giannelos
606 567ffb85 John Giannelos
        cls.client = CycladesClient(API, TOKEN)
607 567ffb85 John Giannelos
        cls.compute = ComputeClient(API, TOKEN)
608 9e4682b5 John Giannelos
609 9e4682b5 John Giannelos
        images = cls.compute.list_images(detail = True)
610 9e4682b5 John Giannelos
        flavors = cls.compute.list_flavors(detail = True)
611 91de9b55 John Giannelos
612 96da37c8 John Giannelos
        cls.imageid = choice([im['id'] for im in images if not im['name'].lower().find("windows") >= 0])
613 91de9b55 John Giannelos
        cls.flavorid = choice([f['id'] for f in flavors if f['disk'] >= 20])
614 9e4682b5 John Giannelos
615 9e4682b5 John Giannelos
        for image in images:
616 96da37c8 John Giannelos
            if image['id'] == cls.imageid:
617 9e4682b5 John Giannelos
                imagename = image['name']
618 e94a9d8c John Giannelos
619 91de9b55 John Giannelos
        cls.servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
620 91de9b55 John Giannelos
621 91de9b55 John Giannelos
        #Dictionary initialization for the vms credentials
622 08748d73 John Giannelos
623 91de9b55 John Giannelos
        cls.serverid = dict()
624 91de9b55 John Giannelos
        cls.username = dict()
625 91de9b55 John Giannelos
        cls.password = dict()
626 91de9b55 John Giannelos
627 1b40b5e3 John Giannelos
    def _get_ipv4(self, server):
628 1b40b5e3 John Giannelos
    
629 1b40b5e3 John Giannelos
        """Get the public IPv4 of a server from the detailed server info"""
630 1b40b5e3 John Giannelos
631 1b40b5e3 John Giannelos
        public_addrs = filter(lambda x: x["id"] == "public",
632 1b40b5e3 John Giannelos
                              server["addresses"]["values"])
633 1b40b5e3 John Giannelos
        self.assertEqual(len(public_addrs), 1)
634 1b40b5e3 John Giannelos
        ipv4_addrs = filter(lambda x: x["version"] == 4,
635 1b40b5e3 John Giannelos
                            public_addrs[0]["values"])
636 1b40b5e3 John Giannelos
        self.assertEqual(len(ipv4_addrs), 1)
637 1b40b5e3 John Giannelos
        return ipv4_addrs[0]["addr"]
638 1b40b5e3 John Giannelos
639 1b40b5e3 John Giannelos
640 1b40b5e3 John Giannelos
    def _connect_loginname(self, os):
641 1b40b5e3 John Giannelos
        """Return the login name for connections based on the server OS"""
642 1b40b5e3 John Giannelos
        if os in ("Ubuntu", "Kubuntu", "Fedora"):
643 1b40b5e3 John Giannelos
            return "user"
644 1b40b5e3 John Giannelos
        elif os in ("windows", "windows_alpha1"):
645 1b40b5e3 John Giannelos
            return "Administrator"
646 1b40b5e3 John Giannelos
        else:
647 1b40b5e3 John Giannelos
            return "root"
648 1b40b5e3 John Giannelos
649 1b40b5e3 John Giannelos
650 08748d73 John Giannelos
    def test_00001_submit_create_server_A(self):
651 91de9b55 John Giannelos
        """Test submit create server request"""
652 91de9b55 John Giannelos
        serverA = self.client.create_server(self.servername, self.flavorid,
653 91de9b55 John Giannelos
                                           self.imageid, personality=None)
654 91de9b55 John Giannelos
655 c54e3d4c John Giannelos
        self.assertEqual(serverA["name"], self.servername)
656 c54e3d4c John Giannelos
        self.assertEqual(serverA["flavorRef"], self.flavorid)
657 c54e3d4c John Giannelos
        self.assertEqual(serverA["imageRef"], self.imageid)
658 c54e3d4c John Giannelos
        self.assertEqual(serverA["status"], "BUILD")
659 91de9b55 John Giannelos
660 91de9b55 John Giannelos
        # Update class attributes to reflect data on building server
661 08748d73 John Giannelos
        self.serverid['A'] = serverA["id"]
662 08748d73 John Giannelos
        self.username['A'] = None
663 08748d73 John Giannelos
        self.password['A'] = serverA["adminPass"]
664 91de9b55 John Giannelos
665 65462ca9 John Giannelos
666 fade8dab John Giannelos
    def test_00001_serverA_becomes_active(self):
667 91de9b55 John Giannelos
        """Test server becomes ACTIVE"""
668 91de9b55 John Giannelos
669 91de9b55 John Giannelos
        fail_tmout = time.time()+self.action_timeout
670 91de9b55 John Giannelos
        while True:
671 91de9b55 John Giannelos
            d = self.client.get_server_details(self.serverid['A'])
672 91de9b55 John Giannelos
            status = d['status']
673 91de9b55 John Giannelos
            if status == 'ACTIVE':
674 91de9b55 John Giannelos
                active = True
675 91de9b55 John Giannelos
                break
676 91de9b55 John Giannelos
            elif time.time() > fail_tmout:
677 91de9b55 John Giannelos
                self.assertLess(time.time(), fail_tmout)
678 91de9b55 John Giannelos
            else:
679 91de9b55 John Giannelos
                time.sleep(self.query_interval)
680 91de9b55 John Giannelos
681 91de9b55 John Giannelos
        self.assertTrue(active)
682 91de9b55 John Giannelos
683 fade8dab John Giannelos
        
684 fade8dab John Giannelos
    
685 fade8dab John Giannelos
    def test_00002_submit_create_server_B(self):
686 fade8dab John Giannelos
        """Test submit create server request"""
687 fade8dab John Giannelos
        serverB = self.client.create_server(self.servername, self.flavorid,
688 fade8dab John Giannelos
                                           self.imageid, personality=None)
689 fade8dab John Giannelos
690 fade8dab John Giannelos
        self.assertEqual(serverB["name"], self.servername)
691 fade8dab John Giannelos
        self.assertEqual(serverB["flavorRef"], self.flavorid)
692 fade8dab John Giannelos
        self.assertEqual(serverB["imageRef"], self.imageid)
693 fade8dab John Giannelos
        self.assertEqual(serverB["status"], "BUILD")
694 fade8dab John Giannelos
695 fade8dab John Giannelos
        # Update class attributes to reflect data on building server
696 fade8dab John Giannelos
        self.serverid['B'] = serverB["id"]
697 fade8dab John Giannelos
        self.username['B'] = None
698 fade8dab John Giannelos
        self.password['B'] = serverB["adminPass"]
699 fade8dab John Giannelos
700 fade8dab John Giannelos
701 fade8dab John Giannelos
        def test_00002_serverB_becomes_active(self):
702 91de9b55 John Giannelos
        """Test server becomes ACTIVE"""
703 91de9b55 John Giannelos
704 91de9b55 John Giannelos
        fail_tmout = time.time()+self.action_timeout
705 91de9b55 John Giannelos
        while True:
706 91de9b55 John Giannelos
            d = self.client.get_server_details(self.serverid['B'])
707 91de9b55 John Giannelos
            status = d['status']
708 91de9b55 John Giannelos
            if status == 'ACTIVE':
709 91de9b55 John Giannelos
                active = True
710 91de9b55 John Giannelos
                break
711 91de9b55 John Giannelos
            elif time.time() > fail_tmout:
712 91de9b55 John Giannelos
                self.assertLess(time.time(), fail_tmout)
713 91de9b55 John Giannelos
            else:
714 91de9b55 John Giannelos
                time.sleep(self.query_interval)
715 91de9b55 John Giannelos
716 91de9b55 John Giannelos
        self.assertTrue(active)
717 567ffb85 John Giannelos
718 e94a9d8c John Giannelos
719 9e4682b5 John Giannelos
    def test_001_create_network(self):
720 e94a9d8c John Giannelos
        """Test submit create network request"""
721 e94a9d8c John Giannelos
        name = SNF_TEST_PREFIX+TEST_RUN_ID
722 9e4682b5 John Giannelos
        previous_num = len(self.client.list_networks())
723 f97dce4d John Giannelos
        network =  self.client.create_network(name)        
724 f97dce4d John Giannelos
       
725 9e4682b5 John Giannelos
        #Test if right name is assigned
726 e94a9d8c John Giannelos
        self.assertEqual(network['name'], name)
727 e94a9d8c John Giannelos
        
728 9e4682b5 John Giannelos
        # Update class attributes
729 e94a9d8c John Giannelos
        cls = type(self)
730 e94a9d8c John Giannelos
        cls.networkid = network['id']
731 9e4682b5 John Giannelos
        networks = self.client.list_networks()
732 9e4682b5 John Giannelos
733 9e4682b5 John Giannelos
        #Test if new network is created
734 9e4682b5 John Giannelos
        self.assertTrue(len(networks) > previous_num)
735 9e4682b5 John Giannelos
        
736 e94a9d8c John Giannelos
    
737 9e4682b5 John Giannelos
    def test_002_connect_to_network(self):
738 f97dce4d John Giannelos
        """Test connect VM to network"""
739 9e4682b5 John Giannelos
740 91de9b55 John Giannelos
        self.client.connect_server(self.serverid['A'], self.networkid)
741 91de9b55 John Giannelos
        self.client.connect_server(self.serverid['B'], self.networkid)
742 91de9b55 John Giannelos
                
743 65462ca9 John Giannelos
        #Insist on connecting until action timeout
744 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
745 9e4682b5 John Giannelos
746 65462ca9 John Giannelos
        while True:
747 de2461ec John Giannelos
            connected = (self.client.get_network_details(self.networkid))
748 65462ca9 John Giannelos
            connections = connected['servers']['values']
749 91de9b55 John Giannelos
            if (self.serverid['A'] in connections) and (self.serverid['B'] in connections):
750 65462ca9 John Giannelos
                conn_exists = True
751 de2461ec John Giannelos
                break
752 de2461ec John Giannelos
            elif time.time() > fail_tmout:
753 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
754 65462ca9 John Giannelos
            else:
755 65462ca9 John Giannelos
                time.sleep(self.query_interval)
756 65462ca9 John Giannelos
757 65462ca9 John Giannelos
        self.assertTrue(conn_exists)
758 65462ca9 John Giannelos
            
759 9e4682b5 John Giannelos
760 1b40b5e3 John Giannelos
    def test_002a_setup_interface_A(self):
761 1b40b5e3 John Giannelos
762 1b40b5e3 John Giannelos
        server = self.client.get_server_details(self.serverid['A'])
763 08748d73 John Giannelos
        image = self.client.get_image_details(self.imageid)
764 1b40b5e3 John Giannelos
        os = image['metadata']['values']['os']
765 1b40b5e3 John Giannelos
        loginname = image["metadata"]["values"].get("users", None)
766 1b40b5e3 John Giannelos
        hostip = self._get_ipv4(server) 
767 1b40b5e3 John Giannelos
        
768 1b40b5e3 John Giannelos
        if not loginname:
769 1b40b5e3 John Giannelos
            loginname = self._connect_loginname(os)
770 1b40b5e3 John Giannelos
771 1b40b5e3 John Giannelos
        try:
772 1b40b5e3 John Giannelos
            ssh = paramiko.SSHClient()
773 1b40b5e3 John Giannelos
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
774 1b40b5e3 John Giannelos
            ssh.connect(hostip, username = loginname, password = self.password['A'])
775 1b40b5e3 John Giannelos
        except socket.error:
776 1b40b5e3 John Giannelos
            raise AssertionError
777 1b40b5e3 John Giannelos
778 1b40b5e3 John Giannelos
        stdin, stdout, stderr = ssh.exec_command("ifconfig eth1 %s up"%("192.168.0.42"))
779 1b40b5e3 John Giannelos
        lines = stdout.readlines()
780 1b40b5e3 John Giannelos
781 1b40b5e3 John Giannelos
        self.assertEqual(len(lines), 0)
782 1b40b5e3 John Giannelos
        
783 1b40b5e3 John Giannelos
784 fade8dab John Giannelos
    def test_002a_setup_interface_B(self):
785 1b40b5e3 John Giannelos
786 1b40b5e3 John Giannelos
        server = self.client.get_server_details(self.serverid['B'])
787 08748d73 John Giannelos
        image = self.client.get_image_details(self.imageid)
788 1b40b5e3 John Giannelos
        os = image['metadata']['values']['os']
789 1b40b5e3 John Giannelos
        loginname = image["metadata"]["values"].get("users", None)
790 1b40b5e3 John Giannelos
        hostip = self._get_ipv4(server) 
791 1b40b5e3 John Giannelos
        
792 1b40b5e3 John Giannelos
        if not loginname:
793 1b40b5e3 John Giannelos
            loginname = self._connect_loginname(os)
794 1b40b5e3 John Giannelos
795 1b40b5e3 John Giannelos
        try:
796 1b40b5e3 John Giannelos
            ssh = paramiko.SSHClient()
797 1b40b5e3 John Giannelos
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
798 1b40b5e3 John Giannelos
            ssh.connect(hostip, username = loginname, password = self.password['B'])
799 1b40b5e3 John Giannelos
        except socket.error:
800 1b40b5e3 John Giannelos
            raise AssertionError
801 1b40b5e3 John Giannelos
802 1b40b5e3 John Giannelos
        stdin, stdout, stderr = ssh.exec_command("ifconfig eth1 %s up"%("192.168.0.43"))
803 1b40b5e3 John Giannelos
        lines = stdout.readlines()
804 1b40b5e3 John Giannelos
805 1b40b5e3 John Giannelos
        self.assertEqual(len(lines), 0)
806 1b40b5e3 John Giannelos
807 1b40b5e3 John Giannelos
808 1b40b5e3 John Giannelos
809 fade8dab John Giannelos
    def test_002b_test_connection_exists(self):
810 1b40b5e3 John Giannelos
        """Ping serverB from serverA to test if connection exists"""
811 1b40b5e3 John Giannelos
812 1b40b5e3 John Giannelos
        server = self.client.get_server_details(self.serverid['A'])
813 08748d73 John Giannelos
        image = self.client.get_image_details(self.imageid)
814 1b40b5e3 John Giannelos
        os = image['metadata']['values']['os']
815 1b40b5e3 John Giannelos
        loginname = image["metadata"]["values"].get("users", None)
816 1b40b5e3 John Giannelos
        
817 1b40b5e3 John Giannelos
        hostip = self._get_ipv4(server)
818 1b40b5e3 John Giannelos
        
819 1b40b5e3 John Giannelos
        if not loginname:
820 1b40b5e3 John Giannelos
            loginname = self._connect_loginname(os)
821 1b40b5e3 John Giannelos
822 1b40b5e3 John Giannelos
        try:
823 1b40b5e3 John Giannelos
            ssh = paramiko.SSHClient()
824 1b40b5e3 John Giannelos
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
825 1b40b5e3 John Giannelos
            ssh.connect(hostip, username = loginname, password = self.password['A'])
826 1b40b5e3 John Giannelos
        except socket.error:
827 1b40b5e3 John Giannelos
            raise AssertionError
828 1b40b5e3 John Giannelos
829 1b40b5e3 John Giannelos
        cmd = "if ping -c 2 -w 3 %s >/dev/null; then echo \"True\"; fi;" % ("192.168.0.43")
830 1b40b5e3 John Giannelos
        stdin, stdout, stderr = ssh.exec_command(cmd)
831 1b40b5e3 John Giannelos
        lines = stdout.readlines()
832 1b40b5e3 John Giannelos
833 1b40b5e3 John Giannelos
        for i in lines:
834 1b40b5e3 John Giannelos
            if i=='True\n':
835 1b40b5e3 John Giannelos
                exists = True
836 1b40b5e3 John Giannelos
837 1b40b5e3 John Giannelos
        self.assertTrue(exists)
838 1b40b5e3 John Giannelos
839 1b40b5e3 John Giannelos
840 1b40b5e3 John Giannelos
841 9e4682b5 John Giannelos
    def test_003_disconnect_from_network(self):
842 f97dce4d John Giannelos
        prev_state = self.client.get_network_details(self.networkid)
843 9e4682b5 John Giannelos
        prev_conn = len(prev_state['servers']['values'])
844 e94a9d8c John Giannelos
845 91de9b55 John Giannelos
        self.client.disconnect_server(self.serverid['A'], self.networkid)
846 91de9b55 John Giannelos
        self.client.disconnect_server(self.serverid['B'], self.networkid)
847 f97dce4d John Giannelos
848 65462ca9 John Giannelos
        #Insist on deleting until action timeout
849 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
850 9e4682b5 John Giannelos
851 65462ca9 John Giannelos
        while True:
852 de2461ec John Giannelos
            connected = (self.client.get_network_details(self.networkid))
853 65462ca9 John Giannelos
            connections = connected['servers']['values']
854 91de9b55 John Giannelos
            if (self.serverid['A'] not in connections) and (self.serverid['B'] not in connections):
855 65462ca9 John Giannelos
                conn_exists = False
856 de2461ec John Giannelos
                break
857 de2461ec John Giannelos
            elif time.time() > fail_tmout:
858 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
859 65462ca9 John Giannelos
            else:
860 65462ca9 John Giannelos
                time.sleep(self.query_interval)
861 65462ca9 John Giannelos
862 65462ca9 John Giannelos
        self.assertFalse(conn_exists)
863 9e4682b5 John Giannelos
864 9e4682b5 John Giannelos
    def test_004_destroy_network(self):
865 e94a9d8c John Giannelos
        """Test submit delete network request"""
866 65462ca9 John Giannelos
        self.client.delete_network(self.networkid)        
867 9e4682b5 John Giannelos
        networks = self.client.list_networks()
868 e94a9d8c John Giannelos
869 65462ca9 John Giannelos
        curr_net = []
870 65462ca9 John Giannelos
        for net in networks:
871 de2461ec John Giannelos
            curr_net.append(net['id'])
872 65462ca9 John Giannelos
873 65462ca9 John Giannelos
        self.assertTrue(self.networkid not in curr_net)
874 65462ca9 John Giannelos
        
875 65462ca9 John Giannelos
    def test_005_cleanup_servers(self):
876 65462ca9 John Giannelos
        """Cleanup servers created for this test"""
877 91de9b55 John Giannelos
        self.compute.delete_server(self.serverid['A'])
878 91de9b55 John Giannelos
        self.compute.delete_server(self.serverid['B'])
879 91de9b55 John Giannelos
880 65462ca9 John Giannelos
        fail_tmout = time.time()+self.action_timeout
881 65462ca9 John Giannelos
882 65462ca9 John Giannelos
        #Ensure server gets deleted
883 91de9b55 John Giannelos
        status = dict() 
884 91de9b55 John Giannelos
885 65462ca9 John Giannelos
        while True:
886 91de9b55 John Giannelos
            details = self.compute.get_server_details(self.serverid['A'])
887 91de9b55 John Giannelos
            status['A'] = details['status']
888 91de9b55 John Giannelos
            details = self.compute.get_server_details(self.serverid['B'])
889 91de9b55 John Giannelos
            status['B'] = details['status']
890 91de9b55 John Giannelos
            if (status['A'] == 'DELETED') and (status['B'] == 'DELETED'):
891 65462ca9 John Giannelos
                deleted = True
892 de2461ec John Giannelos
                break
893 65462ca9 John Giannelos
            elif time.time() > fail_tmout: 
894 65462ca9 John Giannelos
                self.assertLess(time.time(), fail_tmout)
895 65462ca9 John Giannelos
            else:
896 65462ca9 John Giannelos
                time.sleep(self.query_interval)
897 65462ca9 John Giannelos
898 65462ca9 John Giannelos
        self.assertTrue(deleted)
899 e94a9d8c John Giannelos
900 21bbbc9b Vangelis Koukis
class TestRunnerProcess(Process):
901 21bbbc9b Vangelis Koukis
    """A distinct process used to execute part of the tests in parallel"""
902 21bbbc9b Vangelis Koukis
    def __init__(self, **kw):
903 21bbbc9b Vangelis Koukis
        Process.__init__(self, **kw)
904 96da37c8 John Giannelos
        kwargs = kw["kwargs"]
905 21bbbc9b Vangelis Koukis
        self.testq = kwargs["testq"]
906 21bbbc9b Vangelis Koukis
        self.runner = kwargs["runner"]
907 21bbbc9b Vangelis Koukis
908 21bbbc9b Vangelis Koukis
    def run(self):
909 21bbbc9b Vangelis Koukis
        # Make sure this test runner process dies with the parent
910 21bbbc9b Vangelis Koukis
        # and is not left behind.
911 21bbbc9b Vangelis Koukis
        #
912 21bbbc9b Vangelis Koukis
        # WARNING: This uses the prctl(2) call and is
913 21bbbc9b Vangelis Koukis
        # Linux-specific.
914 21bbbc9b Vangelis Koukis
        prctl.set_pdeathsig(signal.SIGHUP)
915 21bbbc9b Vangelis Koukis
916 21bbbc9b Vangelis Koukis
        while True:
917 21bbbc9b Vangelis Koukis
            log.debug("I am process %d, GETting from queue is %s",
918 21bbbc9b Vangelis Koukis
                     os.getpid(), self.testq)
919 21bbbc9b Vangelis Koukis
            msg = self.testq.get()
920 21bbbc9b Vangelis Koukis
            log.debug("Dequeued msg: %s", msg)
921 21bbbc9b Vangelis Koukis
922 21bbbc9b Vangelis Koukis
            if msg == "TEST_RUNNER_TERMINATE":
923 21bbbc9b Vangelis Koukis
                raise SystemExit
924 21bbbc9b Vangelis Koukis
            elif issubclass(msg, unittest.TestCase):
925 21bbbc9b Vangelis Koukis
                # Assemble a TestSuite, and run it
926 21bbbc9b Vangelis Koukis
                suite = unittest.TestLoader().loadTestsFromTestCase(msg)
927 21bbbc9b Vangelis Koukis
                self.runner.run(suite)
928 21bbbc9b Vangelis Koukis
            else:
929 21bbbc9b Vangelis Koukis
                raise Exception("Cannot handle msg: %s" % msg)
930 21bbbc9b Vangelis Koukis
931 21bbbc9b Vangelis Koukis
932 99d41650 John Giannelos
933 21bbbc9b Vangelis Koukis
def _run_cases_in_parallel(cases, fanout=1, runner=None):
934 21bbbc9b Vangelis Koukis
    """Run instances of TestCase in parallel, in a number of distinct processes
935 21bbbc9b Vangelis Koukis

936 21bbbc9b Vangelis Koukis
    The cases iterable specifies the TestCases to be executed in parallel,
937 21bbbc9b Vangelis Koukis
    by test runners running in distinct processes.
938 21bbbc9b Vangelis Koukis
    The fanout parameter specifies the number of processes to spawn,
939 21bbbc9b Vangelis Koukis
    and defaults to 1.
940 21bbbc9b Vangelis Koukis
    The runner argument specifies the test runner class to use inside each
941 21bbbc9b Vangelis Koukis
    runner process.
942 21bbbc9b Vangelis Koukis

943 21bbbc9b Vangelis Koukis
    """
944 21bbbc9b Vangelis Koukis
    if runner is None:
945 00f87624 Vangelis Koukis
        runner = unittest.TextTestRunner(verbosity=2, failfast=True)
946 21bbbc9b Vangelis Koukis
947 21bbbc9b Vangelis Koukis
    # testq: The master process enqueues TestCase objects into this queue,
948 21bbbc9b Vangelis Koukis
    #        test runner processes pick them up for execution, in parallel.
949 21bbbc9b Vangelis Koukis
    testq = Queue()
950 21bbbc9b Vangelis Koukis
    runners = []
951 21bbbc9b Vangelis Koukis
    for i in xrange(0, fanout):
952 21bbbc9b Vangelis Koukis
        kwargs = dict(testq=testq, runner=runner)
953 21bbbc9b Vangelis Koukis
        runners.append(TestRunnerProcess(kwargs=kwargs))
954 21bbbc9b Vangelis Koukis
955 21bbbc9b Vangelis Koukis
    log.info("Spawning %d test runner processes", len(runners))
956 21bbbc9b Vangelis Koukis
    for p in runners:
957 21bbbc9b Vangelis Koukis
        p.start()
958 21bbbc9b Vangelis Koukis
    log.debug("Spawned %d test runners, PIDs are %s",
959 21bbbc9b Vangelis Koukis
              len(runners), [p.pid for p in runners])
960 21bbbc9b Vangelis Koukis
961 21bbbc9b Vangelis Koukis
    # Enqueue test cases
962 21bbbc9b Vangelis Koukis
    map(testq.put, cases)
963 21bbbc9b Vangelis Koukis
    map(testq.put, ["TEST_RUNNER_TERMINATE"] * len(runners))
964 21bbbc9b Vangelis Koukis
965 21bbbc9b Vangelis Koukis
    log.debug("Joining %d processes", len(runners))
966 21bbbc9b Vangelis Koukis
    for p in runners:
967 21bbbc9b Vangelis Koukis
        p.join()
968 21bbbc9b Vangelis Koukis
    log.debug("Done joining %d processes", len(runners))
969 4fdd25ab Vangelis Koukis
970 5a140b23 Vangelis Koukis
971 5a140b23 Vangelis Koukis
def _spawn_server_test_case(**kwargs):
972 5a140b23 Vangelis Koukis
    """Construct a new unit test case class from SpawnServerTestCase"""
973 5a140b23 Vangelis Koukis
974 c1d11f96 John Giannelos
    name = "SpawnServerTestCase_%s" % kwargs["imageid"]
975 5a140b23 Vangelis Koukis
    cls = type(name, (SpawnServerTestCase,), kwargs)
976 5a140b23 Vangelis Koukis
977 5a140b23 Vangelis Koukis
    # Patch extra parameters into test names by manipulating method docstrings
978 5a140b23 Vangelis Koukis
    for (mname, m) in \
979 5a140b23 Vangelis Koukis
        inspect.getmembers(cls, lambda x: inspect.ismethod(x)):
980 5a140b23 Vangelis Koukis
            if hasattr(m, __doc__):
981 5a140b23 Vangelis Koukis
                m.__func__.__doc__ = "[%s] %s" % (imagename, m.__doc__)
982 e72bcf60 Vangelis Koukis
983 e72bcf60 Vangelis Koukis
    # Make sure the class can be pickled, by listing it among
984 e72bcf60 Vangelis Koukis
    # the attributes of __main__. A PicklingError is raised otherwise.
985 e72bcf60 Vangelis Koukis
    setattr(__main__, name, cls)
986 65462ca9 John Giannelos
    return cls 
987 65462ca9 John Giannelos
988 65462ca9 John Giannelos
def _spawn_network_test_case(**kwargs):
989 65462ca9 John Giannelos
    """Construct a new unit test case class from NetworkTestCase"""
990 65462ca9 John Giannelos
991 65462ca9 John Giannelos
    name = "NetworkTestCase"+TEST_RUN_ID
992 65462ca9 John Giannelos
    cls = type(name, (NetworkTestCase,), kwargs)
993 65462ca9 John Giannelos
994 65462ca9 John Giannelos
    # Make sure the class can be pickled, by listing it among
995 65462ca9 John Giannelos
    # the attributes of __main__. A PicklingError is raised otherwise.
996 65462ca9 John Giannelos
    setattr(__main__, name, cls)
997 65462ca9 John Giannelos
    return cls 
998 5a140b23 Vangelis Koukis
999 5a140b23 Vangelis Koukis
1000 5a140b23 Vangelis Koukis
def cleanup_servers(delete_stale=False):
1001 74193008 John Giannelos
1002 567ffb85 John Giannelos
    c = ComputeClient(API, TOKEN)
1003 74193008 John Giannelos
1004 5a140b23 Vangelis Koukis
    servers = c.list_servers()
1005 5a140b23 Vangelis Koukis
    stale = [s for s in servers if s["name"].startswith(SNF_TEST_PREFIX)]
1006 5a140b23 Vangelis Koukis
1007 4fdd25ab Vangelis Koukis
    if len(stale) == 0:
1008 4fdd25ab Vangelis Koukis
        return
1009 4fdd25ab Vangelis Koukis
1010 5a140b23 Vangelis Koukis
    print >> sys.stderr, "Found these stale servers from previous runs:"
1011 5a140b23 Vangelis Koukis
    print "    " + \
1012 21bbbc9b Vangelis Koukis
          "\n    ".join(["%d: %s" % (s["id"], s["name"]) for s in stale])
1013 5a140b23 Vangelis Koukis
1014 5a140b23 Vangelis Koukis
    if delete_stale:
1015 5a140b23 Vangelis Koukis
        print >> sys.stderr, "Deleting %d stale servers:" % len(stale)
1016 5a140b23 Vangelis Koukis
        for server in stale:
1017 21bbbc9b Vangelis Koukis
            c.delete_server(server["id"])
1018 5a140b23 Vangelis Koukis
        print >> sys.stderr, "    ...done"
1019 5a140b23 Vangelis Koukis
    else:
1020 5a140b23 Vangelis Koukis
        print >> sys.stderr, "Use --delete-stale to delete them."
1021 5a140b23 Vangelis Koukis
1022 5a140b23 Vangelis Koukis
1023 5a140b23 Vangelis Koukis
def parse_arguments(args):
1024 5a140b23 Vangelis Koukis
    from optparse import OptionParser
1025 5a140b23 Vangelis Koukis
1026 5a140b23 Vangelis Koukis
    kw = {}
1027 5a140b23 Vangelis Koukis
    kw["usage"] = "%prog [options]"
1028 5a140b23 Vangelis Koukis
    kw["description"] = \
1029 5a140b23 Vangelis Koukis
        "%prog runs a number of test scenarios on a " \
1030 5a140b23 Vangelis Koukis
        "Synnefo deployment."
1031 5a140b23 Vangelis Koukis
1032 5a140b23 Vangelis Koukis
    parser = OptionParser(**kw)
1033 5a140b23 Vangelis Koukis
    parser.disable_interspersed_args()
1034 21bbbc9b Vangelis Koukis
    parser.add_option("--api",
1035 21bbbc9b Vangelis Koukis
                      action="store", type="string", dest="api",
1036 21bbbc9b Vangelis Koukis
                      help="The API URI to use to reach the Synnefo API",
1037 21bbbc9b Vangelis Koukis
                      default=DEFAULT_API)
1038 21bbbc9b Vangelis Koukis
    parser.add_option("--token",
1039 21bbbc9b Vangelis Koukis
                      action="store", type="string", dest="token",
1040 38d247df Kostas Papadimitriou
                      help="The token to use for authentication to the API")
1041 00f87624 Vangelis Koukis
    parser.add_option("--nofailfast",
1042 00f87624 Vangelis Koukis
                      action="store_true", dest="nofailfast",
1043 00f87624 Vangelis Koukis
                      help="Do not fail immediately if one of the tests " \
1044 00f87624 Vangelis Koukis
                           "fails (EXPERIMENTAL)",
1045 bc14ba88 Vangelis Koukis
                      default=False)
1046 5a140b23 Vangelis Koukis
    parser.add_option("--action-timeout",
1047 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="action_timeout",
1048 5a140b23 Vangelis Koukis
                      metavar="TIMEOUT",
1049 5a140b23 Vangelis Koukis
                      help="Wait SECONDS seconds for a server action to " \
1050 5a140b23 Vangelis Koukis
                           "complete, then the test is considered failed",
1051 9e4682b5 John Giannelos
                      default=100)
1052 5a140b23 Vangelis Koukis
    parser.add_option("--build-warning",
1053 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="build_warning",
1054 5a140b23 Vangelis Koukis
                      metavar="TIMEOUT",
1055 5a140b23 Vangelis Koukis
                      help="Warn if TIMEOUT seconds have passed and a " \
1056 5a140b23 Vangelis Koukis
                           "build operation is still pending",
1057 5a140b23 Vangelis Koukis
                      default=600)
1058 5a140b23 Vangelis Koukis
    parser.add_option("--build-fail",
1059 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="build_fail",
1060 5a140b23 Vangelis Koukis
                      metavar="BUILD_TIMEOUT",
1061 5a140b23 Vangelis Koukis
                      help="Fail the test if TIMEOUT seconds have passed " \
1062 5a140b23 Vangelis Koukis
                           "and a build operation is still incomplete",
1063 5a140b23 Vangelis Koukis
                      default=900)
1064 5a140b23 Vangelis Koukis
    parser.add_option("--query-interval",
1065 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="query_interval",
1066 5a140b23 Vangelis Koukis
                      metavar="INTERVAL",
1067 5a140b23 Vangelis Koukis
                      help="Query server status when requests are pending " \
1068 5a140b23 Vangelis Koukis
                           "every INTERVAL seconds",
1069 5a140b23 Vangelis Koukis
                      default=3)
1070 21bbbc9b Vangelis Koukis
    parser.add_option("--fanout",
1071 21bbbc9b Vangelis Koukis
                      action="store", type="int", dest="fanout",
1072 5a140b23 Vangelis Koukis
                      metavar="COUNT",
1073 21bbbc9b Vangelis Koukis
                      help="Spawn up to COUNT child processes to execute " \
1074 21bbbc9b Vangelis Koukis
                           "in parallel, essentially have up to COUNT " \
1075 00f87624 Vangelis Koukis
                           "server build requests outstanding (EXPERIMENTAL)",
1076 5a140b23 Vangelis Koukis
                      default=1)
1077 5a140b23 Vangelis Koukis
    parser.add_option("--force-flavor",
1078 5a140b23 Vangelis Koukis
                      action="store", type="int", dest="force_flavorid",
1079 5a140b23 Vangelis Koukis
                      metavar="FLAVOR ID",
1080 5a140b23 Vangelis Koukis
                      help="Force all server creations to use the specified "\
1081 5a140b23 Vangelis Koukis
                           "FLAVOR ID instead of a randomly chosen one, " \
1082 5a140b23 Vangelis Koukis
                           "useful if disk space is scarce",
1083 00f87624 Vangelis Koukis
                      default=None)
1084 7f62a0b5 Vangelis Koukis
    parser.add_option("--image-id",
1085 7f62a0b5 Vangelis Koukis
                      action="store", type="string", dest="force_imageid",
1086 00f87624 Vangelis Koukis
                      metavar="IMAGE ID",
1087 7f62a0b5 Vangelis Koukis
                      help="Test the specified image id, use 'all' to test " \
1088 7f62a0b5 Vangelis Koukis
                           "all available images (mandatory argument)",
1089 00f87624 Vangelis Koukis
                      default=None)
1090 5a140b23 Vangelis Koukis
    parser.add_option("--show-stale",
1091 5a140b23 Vangelis Koukis
                      action="store_true", dest="show_stale",
1092 5a140b23 Vangelis Koukis
                      help="Show stale servers from previous runs, whose "\
1093 21bbbc9b Vangelis Koukis
                           "name starts with `%s'" % SNF_TEST_PREFIX,
1094 5a140b23 Vangelis Koukis
                      default=False)
1095 5a140b23 Vangelis Koukis
    parser.add_option("--delete-stale",
1096 5a140b23 Vangelis Koukis
                      action="store_true", dest="delete_stale",
1097 5a140b23 Vangelis Koukis
                      help="Delete stale servers from previous runs, whose "\
1098 21bbbc9b Vangelis Koukis
                           "name starts with `%s'" % SNF_TEST_PREFIX,
1099 5a140b23 Vangelis Koukis
                      default=False)
1100 9659e075 John Giannelos
    parser.add_option("--force-personality",
1101 65462ca9 John Giannelos
                      action="store", type="string", dest="personality_path",
1102 9659e075 John Giannelos
                      help="Force a personality file injection. File path required. ",
1103 9659e075 John Giannelos
                      default=None)
1104 9659e075 John Giannelos
    
1105 5a140b23 Vangelis Koukis
1106 5a140b23 Vangelis Koukis
    # FIXME: Change the default for build-fanout to 10
1107 5a140b23 Vangelis Koukis
    # FIXME: Allow the user to specify a specific set of Images to test
1108 5a140b23 Vangelis Koukis
1109 5a140b23 Vangelis Koukis
    (opts, args) = parser.parse_args(args)
1110 5a140b23 Vangelis Koukis
1111 5a140b23 Vangelis Koukis
    # Verify arguments
1112 5a140b23 Vangelis Koukis
    if opts.delete_stale:
1113 5a140b23 Vangelis Koukis
        opts.show_stale = True
1114 5a140b23 Vangelis Koukis
1115 7f62a0b5 Vangelis Koukis
    if not opts.show_stale:
1116 7f62a0b5 Vangelis Koukis
        if not opts.force_imageid:
1117 7f62a0b5 Vangelis Koukis
            print >>sys.stderr, "The --image-id argument is mandatory."
1118 7f62a0b5 Vangelis Koukis
            parser.print_help()
1119 7f62a0b5 Vangelis Koukis
            sys.exit(1)
1120 7f62a0b5 Vangelis Koukis
1121 7f62a0b5 Vangelis Koukis
        if opts.force_imageid != 'all':
1122 7f62a0b5 Vangelis Koukis
            try:
1123 99d41650 John Giannelos
                opts.force_imageid = str(opts.force_imageid)
1124 7f62a0b5 Vangelis Koukis
            except ValueError:
1125 7f62a0b5 Vangelis Koukis
                print >>sys.stderr, "Invalid value specified for --image-id." \
1126 e94a9d8c John Giannelos
                                    "Use a valid id, or `all'."
1127 7f62a0b5 Vangelis Koukis
                sys.exit(1)
1128 7f62a0b5 Vangelis Koukis
1129 5a140b23 Vangelis Koukis
    return (opts, args)
1130 5a140b23 Vangelis Koukis
1131 5a140b23 Vangelis Koukis
1132 5a140b23 Vangelis Koukis
def main():
1133 5a140b23 Vangelis Koukis
    """Assemble test cases into a test suite, and run it
1134 5a140b23 Vangelis Koukis

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

1141 5a140b23 Vangelis Koukis
    """
1142 5a140b23 Vangelis Koukis
    (opts, args) = parse_arguments(sys.argv[1:])
1143 5a140b23 Vangelis Koukis
1144 21bbbc9b Vangelis Koukis
    global API, TOKEN
1145 21bbbc9b Vangelis Koukis
    API = opts.api
1146 21bbbc9b Vangelis Koukis
    TOKEN = opts.token
1147 21bbbc9b Vangelis Koukis
1148 5a140b23 Vangelis Koukis
    # Cleanup stale servers from previous runs
1149 5a140b23 Vangelis Koukis
    if opts.show_stale:
1150 5a140b23 Vangelis Koukis
        cleanup_servers(delete_stale=opts.delete_stale)
1151 5a140b23 Vangelis Koukis
        return 0
1152 5a140b23 Vangelis Koukis
1153 5a140b23 Vangelis Koukis
    # Initialize a kamaki instance, get flavors, images
1154 74193008 John Giannelos
1155 96da37c8 John Giannelos
    c = ComputeClient(API, TOKEN)
1156 74193008 John Giannelos
1157 5a140b23 Vangelis Koukis
    DIMAGES = c.list_images(detail=True)
1158 5a140b23 Vangelis Koukis
    DFLAVORS = c.list_flavors(detail=True)
1159 5a140b23 Vangelis Koukis
1160 21bbbc9b Vangelis Koukis
    # FIXME: logging, log, LOG PID, TEST_RUN_ID, arguments
1161 5a140b23 Vangelis Koukis
    # Run them: FIXME: In parallel, FAILEARLY, catchbreak?
1162 5a140b23 Vangelis Koukis
    #unittest.main(verbosity=2, catchbreak=True)
1163 5a140b23 Vangelis Koukis
1164 e94a9d8c John Giannelos
    if opts.force_imageid == 'all':
1165 e94a9d8c John Giannelos
        test_images = DIMAGES
1166 e94a9d8c John Giannelos
    else:
1167 e94a9d8c John Giannelos
        test_images = filter(lambda x: x["id"] == opts.force_imageid, DIMAGES)
1168 e94a9d8c John Giannelos
1169 00f87624 Vangelis Koukis
    for image in test_images:
1170 99d41650 John Giannelos
        imageid = str(image["id"])
1171 99d41650 John Giannelos
        flavorid = choice([f["id"] for f in DFLAVORS if f["disk"] >= 20])
1172 21bbbc9b Vangelis Koukis
        imagename = image["name"]
1173 9659e075 John Giannelos
        
1174 567ffb85 John Giannelos
        #Personality dictionary for file injection test
1175 9659e075 John Giannelos
        if opts.personality_path != None:
1176 9659e075 John Giannelos
            f = open(opts.personality_path)
1177 9659e075 John Giannelos
            content = b64encode(f.read())
1178 9659e075 John Giannelos
            personality = []
1179 9659e075 John Giannelos
            st = os.stat(opts.personality_path)
1180 9659e075 John Giannelos
            personality.append({
1181 9659e075 John Giannelos
                    'path': '/root/test_inj_file',
1182 9659e075 John Giannelos
                    'owner': 'root',
1183 9659e075 John Giannelos
                    'group': 'root',
1184 9659e075 John Giannelos
                    'mode': 0x7777 & st.st_mode,
1185 9659e075 John Giannelos
                    'contents': content
1186 9659e075 John Giannelos
                    })
1187 9659e075 John Giannelos
        else:
1188 9659e075 John Giannelos
            personality = None
1189 9659e075 John Giannelos
1190 21bbbc9b Vangelis Koukis
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
1191 21bbbc9b Vangelis Koukis
        is_windows = imagename.lower().find("windows") >= 0
1192 9e4682b5 John Giannelos
        
1193 65462ca9 John Giannelos
    ServerTestCase = _spawn_server_test_case(imageid=imageid, flavorid=flavorid,
1194 65462ca9 John Giannelos
                                             imagename=imagename,
1195 65462ca9 John Giannelos
                                             personality=personality,
1196 65462ca9 John Giannelos
                                             servername=servername,
1197 65462ca9 John Giannelos
                                             is_windows=is_windows,
1198 65462ca9 John Giannelos
                                             action_timeout=opts.action_timeout,
1199 65462ca9 John Giannelos
                                             build_warning=opts.build_warning,
1200 65462ca9 John Giannelos
                                             build_fail=opts.build_fail,
1201 65462ca9 John Giannelos
                                             query_interval=opts.query_interval,
1202 65462ca9 John Giannelos
                                             )
1203 21bbbc9b Vangelis Koukis
1204 99d41650 John Giannelos
1205 e94a9d8c John Giannelos
    #Running all the testcases sequentially
1206 65462ca9 John Giannelos
    
1207 567ffb85 John Giannelos
    #To run all cases
1208 567ffb85 John Giannelos
    #seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase, ServerTestCase, NetworkTestCase]
1209 65462ca9 John Giannelos
    
1210 96da37c8 John Giannelos
    newNetworkTestCase = _spawn_network_test_case(action_timeout = opts.action_timeout,
1211 96da37c8 John Giannelos
                                                  query_interval = opts.query_interval)    
1212 65462ca9 John Giannelos
    seq_cases = [newNetworkTestCase]
1213 567ffb85 John Giannelos
1214 99d41650 John Giannelos
    for case in seq_cases:
1215 99d41650 John Giannelos
        suite = unittest.TestLoader().loadTestsFromTestCase(case)
1216 99d41650 John Giannelos
        unittest.TextTestRunner(verbosity=2).run(suite)
1217 e94a9d8c John Giannelos
        
1218 99d41650 John Giannelos
    
1219 99d41650 John Giannelos
1220 99d41650 John Giannelos
    # # The Following cases run sequentially
1221 99d41650 John Giannelos
    # seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase]
1222 99d41650 John Giannelos
    # _run_cases_in_parallel(seq_cases, fanout=3, runner=runner)
1223 99d41650 John Giannelos
1224 99d41650 John Giannelos
    # # The following cases run in parallel
1225 99d41650 John Giannelos
    # par_cases = []
1226 99d41650 John Giannelos
1227 99d41650 John Giannelos
    # if opts.force_imageid == 'all':
1228 99d41650 John Giannelos
    #     test_images = DIMAGES
1229 99d41650 John Giannelos
    # else:
1230 99d41650 John Giannelos
    #     test_images = filter(lambda x: x["id"] == opts.force_imageid, DIMAGES)
1231 99d41650 John Giannelos
1232 99d41650 John Giannelos
    # for image in test_images:
1233 99d41650 John Giannelos
    #     imageid = image["id"]
1234 99d41650 John Giannelos
    #     imagename = image["name"]
1235 99d41650 John Giannelos
    #     if opts.force_flavorid:
1236 99d41650 John Giannelos
    #         flavorid = opts.force_flavorid
1237 99d41650 John Giannelos
    #     else:
1238 99d41650 John Giannelos
    #         flavorid = choice([f["id"] for f in DFLAVORS if f["disk"] >= 20])
1239 99d41650 John Giannelos
    #     personality = None   # FIXME
1240 99d41650 John Giannelos
    #     servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
1241 99d41650 John Giannelos
    #     is_windows = imagename.lower().find("windows") >= 0
1242 99d41650 John Giannelos
    #     case = _spawn_server_test_case(imageid=str(imageid), flavorid=flavorid,
1243 99d41650 John Giannelos
    #                                    imagename=imagename,
1244 99d41650 John Giannelos
    #                                    personality=personality,
1245 99d41650 John Giannelos
    #                                    servername=servername,
1246 99d41650 John Giannelos
    #                                    is_windows=is_windows,
1247 99d41650 John Giannelos
    #                                    action_timeout=opts.action_timeout,
1248 99d41650 John Giannelos
    #                                    build_warning=opts.build_warning,
1249 99d41650 John Giannelos
    #                                    build_fail=opts.build_fail,
1250 99d41650 John Giannelos
    #                                    query_interval=opts.query_interval)
1251 99d41650 John Giannelos
    #     par_cases.append(case)
1252 99d41650 John Giannelos
1253 99d41650 John Giannelos
    # _run_cases_in_parallel(par_cases, fanout=opts.fanout, runner=runner)
1254 5a140b23 Vangelis Koukis
1255 5a140b23 Vangelis Koukis
if __name__ == "__main__":
1256 5a140b23 Vangelis Koukis
    sys.exit(main())