burnin.py 77.6 KB
Newer Older
1
#!/usr/bin/env python
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# Copyright 2011 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

"""Perform integration testing on a running Synnefo deployment"""

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
38
#import __main__
39
40
41
42
import datetime
import inspect
import logging
import os
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
43
import os.path
44
import paramiko
45
import prctl
46
import subprocess
47
import signal
48
49
50
import socket
import sys
import time
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
51
import tempfile
52
from base64 import b64encode
53
from IPy import IP
54
from multiprocessing import Process, Queue
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
55
from random import choice, randint
John Giannelos's avatar
John Giannelos committed
56
from optparse import OptionParser, OptionValueError
57

John Giannelos's avatar
John Giannelos committed
58
59
from kamaki.clients.compute import ComputeClient
from kamaki.clients.cyclades import CycladesClient
60
from kamaki.clients.image import ImageClient
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
61
from kamaki.clients.pithos import PithosClient
John Giannelos's avatar
John Giannelos committed
62
from kamaki.clients import ClientError
John Giannelos's avatar
John Giannelos committed
63

64
from vncauthproxy.d3des import generate_response as d3des_generate_response
65
66
67
68
69

# Use backported unittest functionality if Python < 2.7
try:
    import unittest2 as unittest
except ImportError:
70
71
    if sys.version_info < (2, 7):
        raise Exception("The unittest2 package is required for Python < 2.7")
72
73
    import unittest

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
74
75
76
77
78
79
# --------------------------------------------------------------------
# Global Variables
API = None
TOKEN = None
PLANKTON = None
PLANKTON_USER = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
80
81
PITHOS = None
PITHOS_USER = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
NO_IPV6 = None
DEFAULT_PLANKTON_USER = "images@okeanos.grnet.gr"
NOFAILFAST = None
VERBOSE = None

# A unique id identifying this test run
TEST_RUN_ID = datetime.datetime.strftime(datetime.datetime.now(),
                                         "%Y%m%d%H%M%S")
SNF_TEST_PREFIX = "snf-test-"

red = '\x1b[31m'
yellow = '\x1b[33m'
green = '\x1b[32m'
normal = '\x1b[0m'

97

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# --------------------------------------------------------------------
# Global functions
def _ssh_execute(hostip, username, password, command):
    """Execute a command via ssh"""
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(hostip, username=username, password=password)
    except socket.error:
        raise AssertionError
    try:
        stdin, stdout, stderr = ssh.exec_command(command)
    except paramiko.SSHException:
        raise AssertionError
    status = stdout.channel.recv_exit_status()
    output = stdout.readlines()
    ssh.close()
    return output, status


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
118
119
# --------------------------------------------------------------------
# BurninTestReulst class
120
121
122
123
class BurninTestResult(unittest.TextTestResult):
    def addSuccess(self, test):
        super(BurninTestResult, self).addSuccess(test)
        if self.showAll:
John Giannelos's avatar
John Giannelos committed
124
            if hasattr(test, 'result_dict'):
125
126
127
128
129
130
131
132
133
                run_details = test.result_dict

                self.stream.write("\n")
                for i in run_details:
                    self.stream.write("%s : %s \n" % (i, run_details[i]))
                self.stream.write("\n")

        elif self.dots:
            self.stream.write('.')
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
134
135
            self.stream.flush()

136
137
138
139
    def addError(self, test, err):
        super(BurninTestResult, self).addError(test, err)
        if self.showAll:
            self.stream.writeln("ERROR")
John Giannelos's avatar
John Giannelos committed
140
141
            if hasattr(test, 'result_dict'):
                run_details = test.result_dict
142

John Giannelos's avatar
John Giannelos committed
143
144
145
146
                self.stream.write("\n")
                for i in run_details:
                    self.stream.write("%s : %s \n" % (i, run_details[i]))
                self.stream.write("\n")
147
148
149
150
151
152
153
154
155

        elif self.dots:
            self.stream.write('E')
            self.stream.flush()

    def addFailure(self, test, err):
        super(BurninTestResult, self).addFailure(test, err)
        if self.showAll:
            self.stream.writeln("FAIL")
John Giannelos's avatar
John Giannelos committed
156
157
            if hasattr(test, 'result_dict'):
                run_details = test.result_dict
158

John Giannelos's avatar
John Giannelos committed
159
160
161
162
                self.stream.write("\n")
                for i in run_details:
                    self.stream.write("%s : %s \n" % (i, run_details[i]))
                self.stream.write("\n")
163
164
165
166
167
168

        elif self.dots:
            self.stream.write('F')
            self.stream.flush()


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
169
170
# --------------------------------------------------------------------
# Format Results
John Giannelos's avatar
John Giannelos committed
171
172
class burninFormatter(logging.Formatter):
    err_fmt = red + "ERROR: %(msg)s" + normal
John Giannelos's avatar
John Giannelos committed
173
    dbg_fmt = green + "* %(msg)s" + normal
John Giannelos's avatar
John Giannelos committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
    info_fmt = "%(msg)s"

    def __init__(self, fmt="%(levelno)s: %(msg)s"):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        format_orig = self._fmt
        # Replace the original format with one customized by logging level
        if record.levelno == 10:    # DEBUG
            self._fmt = burninFormatter.dbg_fmt
        elif record.levelno == 20:  # INFO
            self._fmt = burninFormatter.info_fmt
        elif record.levelno == 40:  # ERROR
            self._fmt = burninFormatter.err_fmt
        result = logging.Formatter.format(self, record)
        self._fmt = format_orig
        return result

192
log = logging.getLogger("burnin")
John Giannelos's avatar
John Giannelos committed
193
194
195
196
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(burninFormatter())
log.addHandler(handler)
197

198

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
199
200
# --------------------------------------------------------------------
# UnauthorizedTestCase class
201
class UnauthorizedTestCase(unittest.TestCase):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
202
    """Test unauthorized access"""
203
204
205
206
    @classmethod
    def setUpClass(cls):
        cls.result_dict = dict()

207
208
    def test_unauthorized_access(self):
        """Test access without a valid token fails"""
209
        log.info("Authentication test")
210
        falseToken = '12345'
211
        c = ComputeClient(API, falseToken)
212

213
214
        with self.assertRaises(ClientError) as cm:
            c.list_servers()
John Giannelos's avatar
John Giannelos committed
215
            self.assertEqual(cm.exception.status, 401)
216
217


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
218
# --------------------------------------------------------------------
219
# This class gest replicated into Images TestCases dynamically
220
221
222
223
224
225
class ImagesTestCase(unittest.TestCase):
    """Test image lists for consistency"""
    @classmethod
    def setUpClass(cls):
        """Initialize kamaki, get (detailed) list of images"""
        log.info("Getting simple and detailed list of images")
John Giannelos's avatar
John Giannelos committed
226
        cls.client = ComputeClient(API, TOKEN)
227
228
229
        cls.plankton = ImageClient(PLANKTON, TOKEN)
        cls.images = cls.plankton.list_public()
        cls.dimages = cls.plankton.list_public(detail=True)
230
        cls.result_dict = dict()
231
232
233
234
235
236
        # Create temp directory and store it inside our class
        # XXX: In my machine /tmp has not enough space
        #      so use current directory to be sure.
        cls.temp_dir = tempfile.mkdtemp(dir=os.getcwd())
        cls.temp_image_name = \
            SNF_TEST_PREFIX + cls.imageid + ".diskdump"
237

238
239
240
241
242
243
244
245
246
247
248
249
250
    @classmethod
    def tearDownClass(cls):
        """Remove local files"""
        try:
            temp_file = os.path.join(cls.temp_dir, cls.temp_image_name)
            os.unlink(temp_file)
        except:
            pass
        try:
            os.rmdir(cls.temp_dir)
        except:
            pass

251
252
    def test_001_list_images(self):
        """Test image list actually returns images"""
John Giannelos's avatar
John Giannelos committed
253
        self.assertGreater(len(self.images), 0)
254

255
256
    def test_002_list_images_detailed(self):
        """Test detailed image list is the same length as list"""
John Giannelos's avatar
John Giannelos committed
257
        self.assertEqual(len(self.dimages), len(self.images))
258

259
260
261
262
263
264
265
    def test_003_same_image_names(self):
        """Test detailed and simple image list contain same names"""
        names = sorted(map(lambda x: x["name"], self.images))
        dnames = sorted(map(lambda x: x["name"], self.dimages))
        self.assertEqual(names, dnames)

    def test_004_unique_image_names(self):
266
267
268
269
        """Test system images have unique names"""
        sys_images = filter(lambda x: x['owner'] == PLANKTON_USER,
                            self.dimages)
        names = sorted(map(lambda x: x["name"], sys_images))
270
271
272
273
        self.assertEqual(sorted(list(set(names))), names)

    def test_005_image_metadata(self):
        """Test every image has specific metadata defined"""
274
        keys = frozenset(["osfamily", "root_partition"])
John Giannelos's avatar
John Giannelos committed
275
276
        details = self.client.list_images(detail=True)
        for i in details:
277
278
            self.assertTrue(keys.issubset(i["metadata"]["values"].keys()))

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
    def test_006_download_image(self):
        """Download image from pithos+"""
        # Get image location
        image = filter(
            lambda x: x['id'] == self.imageid, self.dimages)[0]
        image_location = \
            image['location'].replace("://", " ").replace("/", " ").split()
        log.info("Download image, with owner %s\n\tcontainer %s, and name %s"
                 % (image_location[1], image_location[2], image_location[3]))
        pithos_client = PithosClient(PITHOS, TOKEN, image_location[1])
        pithos_client.container = image_location[2]
        temp_file = os.path.join(self.temp_dir, self.temp_image_name)
        with open(temp_file, "wb+") as f:
            pithos_client.download_object(image_location[3], f)

    def test_007_upload_image(self):
        """Upload and register image"""
        temp_file = os.path.join(self.temp_dir, self.temp_image_name)
        log.info("Upload image to pithos+")
        # Create container `images'
        pithos_client = PithosClient(PITHOS, TOKEN, PITHOS_USER)
        pithos_client.container = "images"
        pithos_client.container_put()
        with open(temp_file, "rb+") as f:
            pithos_client.upload_object(self.temp_image_name, f)
        log.info("Register image to plankton")
        location = "pithos://" + PITHOS_USER + \
            "/images/" + self.temp_image_name
        params = {'is_public': True}
        properties = {'OSFAMILY': "linux", 'ROOT_PARTITION': 1}
        self.plankton.register(self.temp_image_name, location,
                               params, properties)
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        # Get image id
        details = self.plankton.list_public(detail=True)
        detail = filter(
            lambda x: x['owner'] == PITHOS_USER and
            x['name'] == self.temp_image_name,
            details)
        self.assertEqual(len(detail), 1)
        cls = type(self)
        cls.temp_image_id = detail[0]['id']
        log.info("Image registered with id %s" % detail[0]['id'])

    def test_008_cleanup_image(self):
        """Cleanup image test"""
        log.info("Cleanup image test")
        # Remove image from pithos+
        pithos_client = PithosClient(PITHOS, TOKEN, PITHOS_USER)
        pithos_client.container = "images"
        pithos_client.del_object(self.temp_image_name)
329

330

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
331
332
# --------------------------------------------------------------------
# FlavorsTestCase class
333
334
335
336
337
338
class FlavorsTestCase(unittest.TestCase):
    """Test flavor lists for consistency"""
    @classmethod
    def setUpClass(cls):
        """Initialize kamaki, get (detailed) list of flavors"""
        log.info("Getting simple and detailed list of flavors")
John Giannelos's avatar
John Giannelos committed
339
        cls.client = ComputeClient(API, TOKEN)
340
341
        cls.flavors = cls.client.list_flavors()
        cls.dflavors = cls.client.list_flavors(detail=True)
342
        cls.result_dict = dict()
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372

    def test_001_list_flavors(self):
        """Test flavor list actually returns flavors"""
        self.assertGreater(len(self.flavors), 0)

    def test_002_list_flavors_detailed(self):
        """Test detailed flavor list is the same length as list"""
        self.assertEquals(len(self.dflavors), len(self.flavors))

    def test_003_same_flavor_names(self):
        """Test detailed and simple flavor list contain same names"""
        names = sorted(map(lambda x: x["name"], self.flavors))
        dnames = sorted(map(lambda x: x["name"], self.dflavors))
        self.assertEqual(names, dnames)

    def test_004_unique_flavor_names(self):
        """Test flavors have unique names"""
        names = sorted(map(lambda x: x["name"], self.flavors))
        self.assertEqual(sorted(list(set(names))), names)

    def test_005_well_formed_flavor_names(self):
        """Test flavors have names of the form CxxRyyDzz
        Where xx is vCPU count, yy is RAM in MiB, zz is Disk in GiB
        """
        for f in self.dflavors:
            self.assertEqual("C%dR%dD%d" % (f["cpu"], f["ram"], f["disk"]),
                             f["name"],
                             "Flavor %s does not match its specs." % f["name"])


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
373
374
# --------------------------------------------------------------------
# ServersTestCase class
375
376
377
378
379
380
class ServersTestCase(unittest.TestCase):
    """Test server lists for consistency"""
    @classmethod
    def setUpClass(cls):
        """Initialize kamaki, get (detailed) list of servers"""
        log.info("Getting simple and detailed list of servers")
381

John Giannelos's avatar
John Giannelos committed
382
        cls.client = ComputeClient(API, TOKEN)
383
384
        cls.servers = cls.client.list_servers()
        cls.dservers = cls.client.list_servers(detail=True)
385
        cls.result_dict = dict()
386

387
388
389
    # def test_001_list_servers(self):
    #     """Test server list actually returns servers"""
    #     self.assertGreater(len(self.servers), 0)
390
391
392
393
394
395
396
397
398
399
400
401

    def test_002_list_servers_detailed(self):
        """Test detailed server list is the same length as list"""
        self.assertEqual(len(self.dservers), len(self.servers))

    def test_003_same_server_names(self):
        """Test detailed and simple flavor list contain same names"""
        names = sorted(map(lambda x: x["name"], self.servers))
        dnames = sorted(map(lambda x: x["name"], self.dservers))
        self.assertEqual(names, dnames)


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
# --------------------------------------------------------------------
# Pithos Test Cases
class PithosTestCase(unittest.TestCase):
    """Test pithos functionality"""
    @classmethod
    def setUpClass(cls):
        """Initialize kamaki, get list of containers"""
        log.info("Getting list of containers")

        cls.client = PithosClient(PITHOS, TOKEN, PITHOS_USER)
        cls.containers = cls.client.list_containers()
        cls.result_dict = dict()

    def test_001_list_containers(self):
        """Test container list actually returns containers"""
        self.assertGreater(len(self.containers), 0)

    def test_002_unique_containers(self):
        """Test if containers have unique names"""
        names = [n['name'] for n in self.containers]
        names = sorted(names)
        self.assertEqual(sorted(list(set(names))), names)

    def test_003_create_container(self):
        """Test create a container"""
        rand_num = randint(1000, 9999)
        rand_name = "%s%s" % (SNF_TEST_PREFIX, rand_num)
        names = [n['name'] for n in self.containers]
        while rand_name in names:
            rand_num = randint(1000, 9999)
            rand_name = "%s%s" % (SNF_TEST_PREFIX, rand_num)
        # Create container
        self.client.container = rand_name
        self.client.container_put()
        # Get list of containers
        new_containers = self.client.list_containers()
        new_container_names = [n['name'] for n in new_containers]
        self.assertIn(rand_name, new_container_names)

    def test_004_upload(self):
        """Test uploading something to pithos+"""
        # Create a tmp file
        with tempfile.TemporaryFile() as f:
            f.write("This is a temp file")
            f.seek(0, 0)
            # Where to save file
            self.client.upload_object("test.txt", f)

    def test_005_download(self):
        """Test download something from pithos+"""
        # Create tmp directory to save file
        tmp_dir = tempfile.mkdtemp()
        tmp_file = os.path.join(tmp_dir, "test.txt")
        with open(tmp_file, "wb+") as f:
            self.client.download_object("test.txt", f)
            # Read file
            f.seek(0, 0)
            content = f.read()
        # Remove files
        os.unlink(tmp_file)
        os.rmdir(tmp_dir)
        # Compare results
        self.assertEqual(content, "This is a temp file")

    def test_006_remove(self):
        """Test removing files and containers"""
        cont_name = self.client.container
        self.client.del_object("test.txt")
        self.client.purge_container()
        # List containers
        containers = self.client.list_containers()
        cont_names = [n['name'] for n in containers]
        self.assertNotIn(cont_name, cont_names)


# --------------------------------------------------------------------
478
479
480
481
482
483
# This class gets replicated into actual TestCases dynamically
class SpawnServerTestCase(unittest.TestCase):
    """Test scenario for server of the specified image"""
    @classmethod
    def setUpClass(cls):
        """Initialize a kamaki instance"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
484
        log.info("Spawning server for image `%s'" % cls.imagename)
John Giannelos's avatar
John Giannelos committed
485
486
        cls.client = ComputeClient(API, TOKEN)
        cls.cyclades = CycladesClient(API, TOKEN)
487
        cls.result_dict = dict()
488
489

    def _get_ipv4(self, server):
490
        """Get the public IPv4 of a server from the detailed server info"""
491

492
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
493

494
495
        for nic in nics:
            net_id = nic["network_id"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
496
            if self.cyclades.get_network_details(net_id)["public"]:
497
                public_addrs = nic["ipv4"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
498
499

        self.assertTrue(public_addrs is not None)
John Giannelos's avatar
John Giannelos committed
500

501
        return public_addrs
502
503

    def _get_ipv6(self, server):
504
        """Get the public IPv6 of a server from the detailed server info"""
John Giannelos's avatar
John Giannelos committed
505

506
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
507

508
509
        for nic in nics:
            net_id = nic["network_id"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
510
            if self.cyclades.get_network_details(net_id)["public"]:
511
                public_addrs = nic["ipv6"]
John Giannelos's avatar
John Giannelos committed
512

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
513
        self.assertTrue(public_addrs is not None)
John Giannelos's avatar
John Giannelos committed
514

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
515
        return public_addrs
516

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
517
    def _connect_loginname(self, os_value):
518
        """Return the login name for connections based on the server OS"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
519
        if os_value in ("Ubuntu", "Kubuntu", "Fedora"):
520
            return "user"
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
521
        elif os_value in ("windows", "windows_alpha1"):
522
            return "Administrator"
523
        else:
524
            return "root"
525
526
527
528

    def _verify_server_status(self, current_status, new_status):
        """Verify a server has switched to a specified status"""
        server = self.client.get_server_details(self.serverid)
529
530
        if server["status"] not in (current_status, new_status):
            return None  # Do not raise exception, return so the test fails
531
532
533
534
535
536
537
538
539
540
541
        self.assertEquals(server["status"], new_status)

    def _get_connected_tcp_socket(self, family, host, port):
        """Get a connected socket from the specified family to host:port"""
        sock = None
        for res in \
            socket.getaddrinfo(host, port, family, socket.SOCK_STREAM, 0,
                               socket.AI_PASSIVE):
            af, socktype, proto, canonname, sa = res
            try:
                sock = socket.socket(af, socktype, proto)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
542
            except socket.error:
543
544
545
546
                sock = None
                continue
            try:
                sock.connect(sa)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
547
            except socket.error:
548
549
550
551
552
553
554
555
556
557
558
559
560
561
                sock.close()
                sock = None
                continue
        self.assertIsNotNone(sock)
        return sock

    def _ping_once(self, ipv6, ip):
        """Test server responds to a single IPv4 or IPv6 ping"""
        cmd = "ping%s -c 2 -w 3 %s" % ("6" if ipv6 else "", ip)
        ping = subprocess.Popen(cmd, shell=True,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (stdout, stderr) = ping.communicate()
        ret = ping.wait()
        self.assertEquals(ret, 0)
562

563
    def _get_hostname_over_ssh(self, hostip, username, password):
564
565
        lines, status = _ssh_execute(
            hostip, username, password, "hostname")
566
        self.assertEqual(len(lines), 1)
567
        return lines[0]
568
569
570
571

    def _try_until_timeout_expires(self, warn_timeout, fail_timeout,
                                   opmsg, callable, *args, **kwargs):
        if warn_timeout == fail_timeout:
572
573
574
575
            warn_timeout = fail_timeout + 1
        warn_tmout = time.time() + warn_timeout
        fail_tmout = time.time() + fail_timeout
        while True:
576
            self.assertLess(time.time(), fail_tmout,
577
                            "operation `%s' timed out" % opmsg)
578
            if time.time() > warn_tmout:
579
580
                log.warning("Server %d: `%s' operation `%s' not done yet",
                            self.serverid, self.servername, opmsg)
581
            try:
582
                log.info("%s... " % opmsg)
583
584
585
                return callable(*args, **kwargs)
            except AssertionError:
                pass
586
587
            time.sleep(self.query_interval)

588
    def _insist_on_tcp_connection(self, family, host, port):
589
590
        familystr = {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6",
                     socket.AF_UNSPEC: "Unspecified-IPv4/6"}
591
592
593
        msg = "connect over %s to %s:%s" % \
              (familystr.get(family, "Unknown"), host, port)
        sock = self._try_until_timeout_expires(
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
594
595
596
            self.action_timeout, self.action_timeout,
            msg, self._get_connected_tcp_socket,
            family, host, port)
597
598
599
        return sock

    def _insist_on_status_transition(self, current_status, new_status,
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
600
                                     fail_timeout, warn_timeout=None):
601
602
        msg = "Server %d: `%s', waiting for %s -> %s" % \
              (self.serverid, self.servername, current_status, new_status)
603
604
605
606
607
        if warn_timeout is None:
            warn_timeout = fail_timeout
        self._try_until_timeout_expires(warn_timeout, fail_timeout,
                                        msg, self._verify_server_status,
                                        current_status, new_status)
608
609
610
        # Ensure the status is actually the expected one
        server = self.client.get_server_details(self.serverid)
        self.assertEquals(server["status"], new_status)
611
612

    def _insist_on_ssh_hostname(self, hostip, username, password):
613
        msg = "SSH to %s, as %s/%s" % (hostip, username, password)
614
        hostname = self._try_until_timeout_expires(
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
615
616
617
            self.action_timeout, self.action_timeout,
            msg, self._get_hostname_over_ssh,
            hostip, username, password)
618
619
620

        # The hostname must be of the form 'prefix-id'
        self.assertTrue(hostname.endswith("-%d\n" % self.serverid))
621

622
623
624
625
    def _check_file_through_ssh(self, hostip, username, password,
                                remotepath, content):
        msg = "Trying file injection through SSH to %s, as %s/%s" % \
            (hostip, username, password)
626
        log.info(msg)
627
628
629
630
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostip, username=username, password=password)
631
            ssh.close()
632
633
        except socket.error:
            raise AssertionError
634

635
636
637
638
        transport = paramiko.Transport((hostip, 22))
        transport.connect(username=username, password=password)

        localpath = '/tmp/' + SNF_TEST_PREFIX + 'injection'
639
        sftp = paramiko.SFTPClient.from_transport(transport)
640
        sftp.get(remotepath, localpath)
641
642
643
        sftp.close()
        transport.close()

644
645
646
        f = open(localpath)
        remote_content = b64encode(f.read())

647
        # Check if files are the same
648
        return (remote_content == content)
649

650
651
652
653
654
655
    def _skipIf(self, condition, msg):
        if condition:
            self.skipTest(msg)

    def test_001_submit_create_server(self):
        """Test submit create server request"""
656
657

        log.info("Submit new server request")
658
659
        server = self.client.create_server(self.servername, self.flavorid,
                                           self.imageid, self.personality)
660

661
662
        log.info("Server id: " + str(server["id"]))
        log.info("Server password: " + server["adminPass"])
663
664
665
666
667
668
669
670
        self.assertEqual(server["name"], self.servername)
        self.assertEqual(server["flavorRef"], self.flavorid)
        self.assertEqual(server["imageRef"], self.imageid)
        self.assertEqual(server["status"], "BUILD")

        # Update class attributes to reflect data on building server
        cls = type(self)
        cls.serverid = server["id"]
671
        cls.username = None
672
673
        cls.passwd = server["adminPass"]

674
675
676
        self.result_dict["Server ID"] = str(server["id"])
        self.result_dict["Password"] = str(server["adminPass"])

677
678
    def test_002a_server_is_building_in_list(self):
        """Test server is in BUILD state, in server list"""
679
680
        log.info("Server in BUILD state in server list")

681
682
        self.result_dict.clear()

683
684
        servers = self.client.list_servers(detail=True)
        servers = filter(lambda x: x["name"] == self.servername, servers)
John Giannelos's avatar
John Giannelos committed
685

686
687
688
689
690
691
692
693
        server = servers[0]
        self.assertEqual(server["name"], self.servername)
        self.assertEqual(server["flavorRef"], self.flavorid)
        self.assertEqual(server["imageRef"], self.imageid)
        self.assertEqual(server["status"], "BUILD")

    def test_002b_server_is_building_in_details(self):
        """Test server is in BUILD state, in details"""
694
695
696

        log.info("Server in BUILD state in details")

697
698
699
700
701
702
703
        server = self.client.get_server_details(self.serverid)
        self.assertEqual(server["name"], self.servername)
        self.assertEqual(server["flavorRef"], self.flavorid)
        self.assertEqual(server["imageRef"], self.imageid)
        self.assertEqual(server["status"], "BUILD")

    def test_002c_set_server_metadata(self):
704
705
706

        log.info("Creating server metadata")

707
        image = self.client.get_image_details(self.imageid)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
708
        os_value = image["metadata"]["values"]["os"]
709
        users = image["metadata"]["values"].get("users", None)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
710
        self.client.update_server_metadata(self.serverid, OS=os_value)
John Giannelos's avatar
John Giannelos committed
711

712
        userlist = users.split()
713

714
715
716
        # Determine the username to use for future connections
        # to this host
        cls = type(self)
717
718
719

        if "root" in userlist:
            cls.username = "root"
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
720
721
        elif users is None:
            cls.username = self._connect_loginname(os_value)
722
723
724
        else:
            cls.username = choice(userlist)

725
        self.assertIsNotNone(cls.username)
726
727
728

    def test_002d_verify_server_metadata(self):
        """Test server metadata keys are set based on image metadata"""
729
730
731

        log.info("Verifying image metadata")

732
733
        servermeta = self.client.get_server_metadata(self.serverid)
        imagemeta = self.client.get_image_metadata(self.imageid)
734

John Giannelos's avatar
John Giannelos committed
735
        self.assertEqual(servermeta["OS"], imagemeta["os"])
736
737
738

    def test_003_server_becomes_active(self):
        """Test server becomes ACTIVE"""
739
740
741

        log.info("Waiting for server to become ACTIVE")

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
742
743
        self._insist_on_status_transition(
            "BUILD", "ACTIVE", self.build_fail, self.build_warning)
744

John Giannelos's avatar
John Giannelos committed
745
746
    def test_003a_get_server_oob_console(self):
        """Test getting OOB server console over VNC
747

John Giannelos's avatar
John Giannelos committed
748
749
        Implementation of RFB protocol follows
        http://www.realvnc.com/docs/rfbproto.pdf.
750

John Giannelos's avatar
John Giannelos committed
751
752
753
        """
        console = self.cyclades.get_server_console(self.serverid)
        self.assertEquals(console['type'], "vnc")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
754
755
        sock = self._insist_on_tcp_connection(
            socket.AF_INET, console["host"], console["port"])
John Giannelos's avatar
John Giannelos committed
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

        # Step 1. ProtocolVersion message (par. 6.1.1)
        version = sock.recv(1024)
        self.assertEquals(version, 'RFB 003.008\n')
        sock.send(version)

        # Step 2. Security (par 6.1.2): Only VNC Authentication supported
        sec = sock.recv(1024)
        self.assertEquals(list(sec), ['\x01', '\x02'])

        # Step 3. Request VNC Authentication (par 6.1.2)
        sock.send('\x02')

        # Step 4. Receive Challenge (par 6.2.2)
        challenge = sock.recv(1024)
        self.assertEquals(len(challenge), 16)

        # Step 5. DES-Encrypt challenge, use password as key (par 6.2.2)
        response = d3des_generate_response(
            (console["password"] + '\0' * 8)[:8], challenge)
        sock.send(response)

        # Step 6. SecurityResult (par 6.1.3)
        result = sock.recv(4)
        self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
        sock.close()
782

783
784
    def test_004_server_has_ipv4(self):
        """Test active server has a valid IPv4 address"""
785

786
        log.info("Validate server's IPv4")
787

788
789
        server = self.client.get_server_details(self.serverid)
        ipv4 = self._get_ipv4(server)
790
791
792
793

        self.result_dict.clear()
        self.result_dict["IPv4"] = str(ipv4)

794
795
        self.assertEquals(IP(ipv4).version(), 4)

796
797
    def test_005_server_has_ipv6(self):
        """Test active server has a valid IPv6 address"""
798
        self._skipIf(NO_IPV6, "--no-ipv6 flag enabled")
799

800
        log.info("Validate server's IPv6")
801

802
803
        server = self.client.get_server_details(self.serverid)
        ipv6 = self._get_ipv6(server)
804
805
806
807

        self.result_dict.clear()
        self.result_dict["IPv6"] = str(ipv6)

808
        self.assertEquals(IP(ipv6).version(), 6)
809

John Giannelos's avatar
John Giannelos committed
810
811
812
813
    def test_006_server_responds_to_ping_IPv4(self):
        """Test server responds to ping on IPv4 address"""

        log.info("Testing if server responds to pings in IPv4")
814
        self.result_dict.clear()
John Giannelos's avatar
John Giannelos committed
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835

        server = self.client.get_server_details(self.serverid)
        ip = self._get_ipv4(server)
        self._try_until_timeout_expires(self.action_timeout,
                                        self.action_timeout,
                                        "PING IPv4 to %s" % ip,
                                        self._ping_once,
                                        False, ip)

    def test_007_server_responds_to_ping_IPv6(self):
        """Test server responds to ping on IPv6 address"""
        self._skipIf(NO_IPV6, "--no-ipv6 flag enabled")
        log.info("Testing if server responds to pings in IPv6")

        server = self.client.get_server_details(self.serverid)
        ip = self._get_ipv6(server)
        self._try_until_timeout_expires(self.action_timeout,
                                        self.action_timeout,
                                        "PING IPv6 to %s" % ip,
                                        self._ping_once,
                                        True, ip)
836
837
838

    def test_008_submit_shutdown_request(self):
        """Test submit request to shutdown server"""
839
840
841

        log.info("Shutting down server")

842
        self.cyclades.shutdown_server(self.serverid)
843
844
845

    def test_009_server_becomes_stopped(self):
        """Test server becomes STOPPED"""
846
847

        log.info("Waiting until server becomes STOPPED")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
848
849
        self._insist_on_status_transition(
            "ACTIVE", "STOPPED", self.action_timeout, self.action_timeout)
850
851
852

    def test_010_submit_start_request(self):
        """Test submit start server request"""
853
854
855

        log.info("Starting server")

856
        self.cyclades.start_server(self.serverid)
857
858
859

    def test_011_server_becomes_active(self):
        """Test server becomes ACTIVE again"""
860
861

        log.info("Waiting until server becomes ACTIVE")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
862
863
        self._insist_on_status_transition(
            "STOPPED", "ACTIVE", self.action_timeout, self.action_timeout)
864

John Giannelos's avatar
John Giannelos committed
865
866
    def test_011a_server_responds_to_ping_IPv4(self):
        """Test server OS is actually up and running again"""
867

John Giannelos's avatar
John Giannelos committed
868
        log.info("Testing if server is actually up and running")
869

John Giannelos's avatar
John Giannelos committed
870
        self.test_006_server_responds_to_ping_IPv4()
871

John Giannelos's avatar
John Giannelos committed
872
873
    def test_012_ssh_to_server_IPv4(self):
        """Test SSH to server public IPv4 works, verify hostname"""
874

John Giannelos's avatar
John Giannelos committed
875
876
877
878
        self._skipIf(self.is_windows, "only valid for Linux servers")
        server = self.client.get_server_details(self.serverid)
        self._insist_on_ssh_hostname(self._get_ipv4(server),
                                     self.username, self.passwd)
879

John Giannelos's avatar
John Giannelos committed
880
881
882
883
    def test_013_ssh_to_server_IPv6(self):
        """Test SSH to server public IPv6 works, verify hostname"""
        self._skipIf(self.is_windows, "only valid for Linux servers")
        self._skipIf(NO_IPV6, "--no-ipv6 flag enabled")
884

John Giannelos's avatar
John Giannelos committed
885
886
887
        server = self.client.get_server_details(self.serverid)
        self._insist_on_ssh_hostname(self._get_ipv6(server),
                                     self.username, self.passwd)
888

John Giannelos's avatar
John Giannelos committed
889
890
891
892
893
    def test_014_rdp_to_server_IPv4(self):
        "Test RDP connection to server public IPv4 works"""
        self._skipIf(not self.is_windows, "only valid for Windows servers")
        server = self.client.get_server_details(self.serverid)
        ipv4 = self._get_ipv4(server)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
894
        sock = self._insist_on_tcp_connection(socket.AF_INET, ipv4, 3389)
895

John Giannelos's avatar
John Giannelos committed
896
897
898
899
        # No actual RDP processing done. We assume the RDP server is there
        # if the connection to the RDP port is successful.
        # FIXME: Use rdesktop, analyze exit code? see manpage [costasd]
        sock.close()
900

John Giannelos's avatar
John Giannelos committed
901
902
903
904
    def test_015_rdp_to_server_IPv6(self):
        "Test RDP connection to server public IPv6 works"""
        self._skipIf(not self.is_windows, "only valid for Windows servers")
        self._skipIf(NO_IPV6, "--no-ipv6 flag enabled")
905

John Giannelos's avatar
John Giannelos committed
906
907
        server = self.client.get_server_details(self.serverid)
        ipv6 = self._get_ipv6(server)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
908
        sock = self._get_tcp_connection(socket.AF_INET6, ipv6, 3389)
909

John Giannelos's avatar
John Giannelos committed
910
911
912
        # No actual RDP processing done. We assume the RDP server is there
        # if the connection to the RDP port is successful.
        sock.close()
913

John Giannelos's avatar
John Giannelos committed
914
915
916
    def test_016_personality_is_enforced(self):
        """Test file injection for personality enforcement"""
        self._skipIf(self.is_windows, "only implemented for Linux servers")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
917
        self._skipIf(self.personality is None, "No personality file selected")
918

John Giannelos's avatar
John Giannelos committed
919
        log.info("Trying to inject file for personality enforcement")
920

John Giannelos's avatar
John Giannelos committed
921
        server = self.client.get_server_details(self.serverid)
922

John Giannelos's avatar
John Giannelos committed
923
924
925
926
927
928
929
        for inj_file in self.personality:
            equal_files = self._check_file_through_ssh(self._get_ipv4(server),
                                                       inj_file['owner'],
                                                       self.passwd,
                                                       inj_file['path'],
                                                       inj_file['contents'])
            self.assertTrue(equal_files)
930

931
932
    def test_017_submit_delete_request(self):
        """Test submit request to delete server"""
933
934
935

        log.info("Deleting server")

936
937
938
939
        self.client.delete_server(self.serverid)

    def test_018_server_becomes_deleted(self):
        """Test server becomes DELETED"""
940
941
942

        log.info("Testing if server becomes DELETED")

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
943
944
        self._insist_on_status_transition(
            "ACTIVE", "DELETED", self.action_timeout, self.action_timeout)
945
946
947

    def test_019_server_no_longer_in_server_list(self):
        """Test server is no longer in server list"""
948
949
950

        log.info("Test if server is no longer listed")

951
        servers = self.client.list_servers()
952
953
954
        self.assertNotIn(self.serverid, [s["id"] for s in servers])


955
class NetworkTestCase(unittest.TestCase):
John Giannelos's avatar
John Giannelos committed
956
    """ Testing networking in cyclades """
957

958
    @classmethod
John Giannelos's avatar
John Giannelos committed
959
960
    def setUpClass(cls):
        "Initialize kamaki, get list of current networks"
John Giannelos's avatar
John Giannelos committed
961
962
963

        cls.client = CycladesClient(API, TOKEN)
        cls.compute = ComputeClient(API, TOKEN)
964

965
966
967
        cls.servername = "%s%s for %s" % (SNF_TEST_PREFIX,
                                          TEST_RUN_ID,
                                          cls.imagename)
968
969
970
971
972

        #Dictionary initialization for the vms credentials
        cls.serverid = dict()
        cls.username = dict()
        cls.password = dict()
John Giannelos's avatar
John Giannelos committed
973
        cls.is_windows = cls.imagename.lower().find("windows") >= 0
974

975
976
        cls.result_dict = dict()

977
978
979
    def _skipIf(self, condition, msg):
        if condition:
            self.skipTest(msg)
980

981
982
983
    def _get_ipv4(self, server):
        """Get the public IPv4 of a server from the detailed server info"""

984
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
985

986
987
        for nic in nics:
            net_id = nic["network_id"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
988
            if self.client.get_network_details(net_id)["public"]:
989
                public_addrs = nic["ipv4"]
John Giannelos's avatar
John Giannelos committed
990

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
991
        self.assertTrue(public_addrs is not None)
John Giannelos's avatar
John Giannelos committed
992

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
993
        return public_addrs
994

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
995
    def _connect_loginname(self, os_value):
996
        """Return the login name for connections based on the server OS"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
997
        if os_value in ("Ubuntu", "Kubuntu", "Fedora"):
998
            return "user"
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
999
        elif os_value in ("windows", "windows_alpha1"):
1000
1001
1002
1003
            return "Administrator"
        else:
            return "root"

John Giannelos's avatar
John Giannelos committed
1004
    def _ping_once(self, ip):
1005

John Giannelos's avatar
John Giannelos committed
1006
1007
1008
1009
1010
1011
        """Test server responds to a single IPv4 or IPv6 ping"""
        cmd = "ping -c 2 -w 3 %s" % (ip)
        ping = subprocess.Popen(cmd, shell=True,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (stdout, stderr) = ping.communicate()
        ret = ping.wait()
1012

John Giannelos's avatar
John Giannelos committed
1013
1014
        return (ret == 0)

John Giannelos's avatar
John Giannelos committed
1015
    def test_00001a_submit_create_server_A(self):
1016
        """Test submit create server request"""
1017
1018
1019

        log.info("Creating test server A")

1020
        serverA = self.client.create_server(self.servername, self.flavorid,
1021
                                            self.imageid, personality=None)
1022

John Giannelos's avatar
John Giannelos committed
1023
1024
1025
1026
        self.assertEqual(serverA["name"], self.servername)
        self.assertEqual(serverA["flavorRef"], self.flavorid)
        self.assertEqual(serverA["imageRef"], self.imageid)
        self.assertEqual(serverA["status"], "BUILD")
1027
1028

        # Update class attributes to reflect data on building server
John Giannelos's avatar
John Giannelos committed
1029
1030
1031
        self.serverid['A'] = serverA["id"]
        self.username['A'] = None
        self.password['A'] = serverA["adminPass"]
1032

1033
1034
        log.info("Server A id:" + str(serverA["id"]))
        log.info("Server password " + (self.password['A']))
1035

1036
1037
        self.result_dict["Server A ID"] = str(serverA["id"])
        self.result_dict["Server A password"] = serverA["adminPass"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
1038

John Giannelos's avatar
John Giannelos committed
1039
    def test_00001b_serverA_becomes_active(self):
1040
        """Test server becomes ACTIVE"""
1041

1042
        log.info("Waiting until test server A becomes ACTIVE")
1043
        self.result_dict.clear()
1044

1045
        fail_tmout = time.time() + self.action_timeout
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
        while True:
            d = self.client.get_server_details(self.serverid['A'])
            status = d['status']
            if status == 'ACTIVE':
                active = True
                break
            elif time.time() > fail_tmout:
                self.assertLess(time.time(), fail_tmout)
            else:
                time.sleep(self.query_interval)

        self.assertTrue(active)

John Giannelos's avatar
John Giannelos committed
1059
    def test_00002a_submit_create_server_B(self):
John Giannelos's avatar
John Giannelos committed
1060
        """Test submit create server request"""
1061

1062
        log.info("Creating test server B")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
1063

John Giannelos's avatar
John Giannelos committed
1064
        serverB = self.client.create_server(self.servername, self.flavorid,
1065
1066
                                            self.imageid, personality=None)

John Giannelos's avatar
John Giannelos committed
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
        self.assertEqual(serverB["name"], self.servername)
        self.assertEqual(serverB["flavorRef"], self.flavorid)
        self.assertEqual(serverB["imageRef"], self.imageid)
        self.assertEqual(serverB["status"], "BUILD")

        # Update class attributes to reflect data on building server
        self.serverid['B'] = serverB["id"]
        self.username['B'] = None
        self.password['B'] = serverB["adminPass"]

1077
1078
        log.info("Server B id: " + str(serverB["id"]))
        log.info("Password " + (self.password['B']))
1079

1080
1081
1082
1083
        self.result_dict.clear()
        self.result_dict["Server B ID"] = str(serverB["id"])
        self.result_dict["Server B password"] = serverB["adminPass"]

John Giannelos's avatar
John Giannelos committed
1084
    def test_00002b_serverB_becomes_active(self):
1085
1086
        """Test server becomes ACTIVE"""

1087
        log.info("Waiting until test server B becomes ACTIVE")
1088
        self.result_dict.clear()
1089

1090
        fail_tmout = time.time() + self.action_timeout
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
        while True:
            d = self.client.get_server_details(self.serverid['B'])
            status = d['status']
            if status == 'ACTIVE':
                active = True
                break
            elif time.time() > fail_tmout:
                self.assertLess(time.time(), fail_tmout)
            else:
                time.sleep(self.query_interval)

        self.assertTrue(active)
John Giannelos's avatar
John Giannelos committed
1103

1104
    def test_001_create_network(self):
John Giannelos's avatar
John Giannelos committed
1105
        """Test submit create network request"""
1106
1107

        log.info("Submit new network request")
1108
        self.result_dict.clear()