burnin.py 63.5 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"""

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

John Giannelos's avatar
John Giannelos committed
56
57
from kamaki.clients.compute import ComputeClient
from kamaki.clients.cyclades import CycladesClient
58
from kamaki.clients.image import ImageClient
John Giannelos's avatar
John Giannelos committed
59
from kamaki.clients import ClientError
John Giannelos's avatar
John Giannelos committed
60

John Giannelos's avatar
John Giannelos committed
61
from fabric.api import *
62

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

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


74
75
API = None
TOKEN = None
John Giannelos's avatar
John Giannelos committed
76
DEFAULT_API = "https://cyclades.okeanos.grnet.gr/api/v1.1"
77
78
DEFAULT_PLANKTON = "https://cyclades.okeanos.grnet.gr/plankton"
DEFAULT_PLANKTON_USER = "images@okeanos.grnet.gr"
79

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

John Giannelos's avatar
John Giannelos committed
85
86
87
88
89
90
91
92
red = '\x1b[31m'
yellow = '\x1b[33m'
green = '\x1b[32m'
normal = '\x1b[0m'

class burninFormatter(logging.Formatter):

    err_fmt = red + "ERROR: %(msg)s" + normal
John Giannelos's avatar
John Giannelos committed
93
    dbg_fmt = green + "* %(msg)s" + normal
John Giannelos's avatar
John Giannelos committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    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

118
log = logging.getLogger("burnin")
John Giannelos's avatar
John Giannelos committed
119
120
121
122
log.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(burninFormatter())
log.addHandler(handler)
123

124

125
126
127
class UnauthorizedTestCase(unittest.TestCase):
    def test_unauthorized_access(self):
        """Test access without a valid token fails"""
128
129
        log.info("Authentication test")

130
        falseToken = '12345'
131
        c = ComputeClient(API, falseToken)
132

133
134
        with self.assertRaises(ClientError) as cm:
            c.list_servers()
John Giannelos's avatar
John Giannelos committed
135
            self.assertEqual(cm.exception.status, 401)
136
137
138
139
140
141
142
143


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")
144

John Giannelos's avatar
John Giannelos committed
145
        cls.client = ComputeClient(API, TOKEN)
146
147
148
        cls.plankton = ImageClient(PLANKTON, TOKEN)
        cls.images = cls.plankton.list_public()
        cls.dimages = cls.plankton.list_public(detail=True)
149
150
151

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

154
155
    def test_002_list_images_detailed(self):
        """Test detailed image list is the same length as list"""
John Giannelos's avatar
John Giannelos committed
156
        self.assertEqual(len(self.dimages), len(self.images))
157

158
159
160
161
162
163
164
    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):
165
166
167
168
        """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))
169
170
171
172
        self.assertEqual(sorted(list(set(names))), names)

    def test_005_image_metadata(self):
        """Test every image has specific metadata defined"""
173
        keys = frozenset(["osfamily", "root_partition"])
John Giannelos's avatar
John Giannelos committed
174
175
        details = self.client.list_images(detail=True)
        for i in details:
176
177
178
179
180
181
182
183
184
            self.assertTrue(keys.issubset(i["metadata"]["values"].keys()))


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")
185

John Giannelos's avatar
John Giannelos committed
186
        cls.client = ComputeClient(API, TOKEN)
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
        cls.flavors = cls.client.list_flavors()
        cls.dflavors = cls.client.list_flavors(detail=True)

    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"])


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")
227

John Giannelos's avatar
John Giannelos committed
228
        cls.client = ComputeClient(API, TOKEN)
229
230
231
        cls.servers = cls.client.list_servers()
        cls.dservers = cls.client.list_servers(detail=True)

232
233
234
    # def test_001_list_servers(self):
    #     """Test server list actually returns servers"""
    #     self.assertGreater(len(self.servers), 0)
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

    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)


# 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"""
254
        log.info("Spawning server for image `%s'" %cls.imagename)
John Giannelos's avatar
John Giannelos committed
255
256
        cls.client = ComputeClient(API, TOKEN)
        cls.cyclades = CycladesClient(API, TOKEN)
257
258

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

261
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
262

263
264
265
266
267
268
        for nic in nics:
            net_id = nic["network_id"]
            if self.cyclades.get_network_details(net_id)["public"] == True:
                public_addrs = nic["ipv4"]
        
        self.assertTrue(public_addrs != None)
John Giannelos's avatar
John Giannelos committed
269

270
        return public_addrs
271
272

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

275
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
276

277
278
279
280
281
282
        for nic in nics:
            net_id = nic["network_id"]
            if self.cyclades.get_network_details(net_id)["public"] == True:
                public_addrs = nic["ipv6"]
        
        self.assertTrue(public_addrs != None)
John Giannelos's avatar
John Giannelos committed
283

284
        return public_addrs
John Giannelos's avatar
John Giannelos committed
285

286

287
288
    def _connect_loginname(self, os):
        """Return the login name for connections based on the server OS"""
289
        if os in ("Ubuntu", "Kubuntu", "Fedora"):
290
            return "user"
291
        elif os in ("windows", "windows_alpha1"):
292
            return "Administrator"
293
        else:
294
            return "root"
295
296
297
298

    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)
299
300
        if server["status"] not in (current_status, new_status):
            return None  # Do not raise exception, return so the test fails
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
        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)
            except socket.error as msg:
                sock = None
                continue
            try:
                sock.connect(sa)
            except socket.error as msg:
                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)
332

333
334
335
336
337
338
339
340
341
342
    def _get_hostname_over_ssh(self, hostip, username, password):
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh.connect(hostip, username=username, password=password)
        except socket.error:
            raise AssertionError
        stdin, stdout, stderr = ssh.exec_command("hostname")
        lines = stdout.readlines()
        self.assertEqual(len(lines), 1)
343
        return lines[0]
344
345
346
347

    def _try_until_timeout_expires(self, warn_timeout, fail_timeout,
                                   opmsg, callable, *args, **kwargs):
        if warn_timeout == fail_timeout:
348
349
350
351
            warn_timeout = fail_timeout + 1
        warn_tmout = time.time() + warn_timeout
        fail_tmout = time.time() + fail_timeout
        while True:
352
            self.assertLess(time.time(), fail_tmout,
353
                            "operation `%s' timed out" % opmsg)
354
            if time.time() > warn_tmout:
355
356
                log.warning("Server %d: `%s' operation `%s' not done yet",
                            self.serverid, self.servername, opmsg)
357
            try:
358
                log.info("%s... " % opmsg)
359
360
361
                return callable(*args, **kwargs)
            except AssertionError:
                pass
362
363
            time.sleep(self.query_interval)

364
    def _insist_on_tcp_connection(self, family, host, port):
365
366
        familystr = {socket.AF_INET: "IPv4", socket.AF_INET6: "IPv6",
                     socket.AF_UNSPEC: "Unspecified-IPv4/6"}
367
368
369
370
371
372
373
374
375
376
        msg = "connect over %s to %s:%s" % \
              (familystr.get(family, "Unknown"), host, port)
        sock = self._try_until_timeout_expires(
                self.action_timeout, self.action_timeout,
                msg, self._get_connected_tcp_socket,
                family, host, port)
        return sock

    def _insist_on_status_transition(self, current_status, new_status,
                                    fail_timeout, warn_timeout=None):
377
378
        msg = "Server %d: `%s', waiting for %s -> %s" % \
              (self.serverid, self.servername, current_status, new_status)
379
380
381
382
383
        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)
384
385
386
        # Ensure the status is actually the expected one
        server = self.client.get_server_details(self.serverid)
        self.assertEquals(server["status"], new_status)
387
388

    def _insist_on_ssh_hostname(self, hostip, username, password):
389
        msg = "SSH to %s, as %s/%s" % (hostip, username, password)
390
391
392
393
394
395
396
        hostname = self._try_until_timeout_expires(
                self.action_timeout, self.action_timeout,
                msg, self._get_hostname_over_ssh,
                hostip, username, password)

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

398
399
400
401
    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)
402
        log.info(msg)
403
404
405
406
407
408
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostip, username=username, password=password)
        except socket.error:
            raise AssertionError
409

410
411
412
413
        transport = paramiko.Transport((hostip, 22))
        transport.connect(username=username, password=password)

        localpath = '/tmp/' + SNF_TEST_PREFIX + 'injection'
414
        sftp = paramiko.SFTPClient.from_transport(transport)
415
        sftp.get(remotepath, localpath)
416
417
418
        sftp.close()
        transport.close()

419
420
421
        f = open(localpath)
        remote_content = b64encode(f.read())

422
        # Check if files are the same
423
        return (remote_content == content)
424

425
426
427
428
429
430
    def _skipIf(self, condition, msg):
        if condition:
            self.skipTest(msg)

    def test_001_submit_create_server(self):
        """Test submit create server request"""
431
432

        log.info("Submit new server request")
433
434
        server = self.client.create_server(self.servername, self.flavorid,
                                           self.imageid, self.personality)
435

436
437
        log.info("Server id: " + str(server["id"]))
        log.info("Server password: " + server["adminPass"])
438
439
440
441
442
443
444
445
        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"]
446
        cls.username = None
447
448
449
450
        cls.passwd = server["adminPass"]

    def test_002a_server_is_building_in_list(self):
        """Test server is in BUILD state, in server list"""
451
452
        log.info("Server in BUILD state in server list")

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

456
457
458
459
460
461
462
463
        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"""
464
465
466

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

467
468
469
470
471
472
473
        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):
474
475
476

        log.info("Creating server metadata")

477
        image = self.client.get_image_details(self.imageid)
478
        os = image["metadata"]["values"]["os"]
479
        users = image["metadata"]["values"].get("users", None)
480
        self.client.update_server_metadata(self.serverid, OS=os)
John Giannelos's avatar
John Giannelos committed
481

482
        userlist = users.split()
483

484
485
486
        # Determine the username to use for future connections
        # to this host
        cls = type(self)
487
488
489
490

        if "root" in userlist:
            cls.username = "root"
        elif users == None:
491
            cls.username = self._connect_loginname(os)
492
493
494
        else:
            cls.username = choice(userlist)

495
        self.assertIsNotNone(cls.username)
496
497
498

    def test_002d_verify_server_metadata(self):
        """Test server metadata keys are set based on image metadata"""
499
500
501

        log.info("Verifying image metadata")

502
503
        servermeta = self.client.get_server_metadata(self.serverid)
        imagemeta = self.client.get_image_metadata(self.imageid)
504

John Giannelos's avatar
John Giannelos committed
505
        self.assertEqual(servermeta["OS"], imagemeta["os"])
506
507
508

    def test_003_server_becomes_active(self):
        """Test server becomes ACTIVE"""
509
510
511

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

512
        self._insist_on_status_transition("BUILD", "ACTIVE",
513
514
                                         self.build_fail, self.build_warning)

John Giannelos's avatar
John Giannelos committed
515
516
    # def test_003a_get_server_oob_console(self):
    #     """Test getting OOB server console over VNC
517

John Giannelos's avatar
John Giannelos committed
518
519
    #     Implementation of RFB protocol follows
    #     http://www.realvnc.com/docs/rfbproto.pdf.
520

John Giannelos's avatar
John Giannelos committed
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
    #     """
    #     console = self.cyclades.get_server_console(self.serverid)
    #     self.assertEquals(console['type'], "vnc")
    #     sock = self._insist_on_tcp_connection(socket.AF_INET,
    #                                     console["host"], console["port"])

    #     # 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()
552

553
554
    def test_004_server_has_ipv4(self):
        """Test active server has a valid IPv4 address"""
555

556
        log.info("Validate server's IPv4")
557

558
559
560
561
        server = self.client.get_server_details(self.serverid)
        ipv4 = self._get_ipv4(server)
        self.assertEquals(IP(ipv4).version(), 4)

562
563
    def test_005_server_has_ipv6(self):
        """Test active server has a valid IPv6 address"""
564
        self._skipIf(NO_IPV6, "--no-ipv6 flag enabled")
565

566
        log.info("Validate server's IPv6")
567

568
569
570
        server = self.client.get_server_details(self.serverid)
        ipv6 = self._get_ipv6(server)
        self.assertEquals(IP(ipv6).version(), 6)
571

572
573
    # def test_006_server_responds_to_ping_IPv4(self):
    #     """Test server responds to ping on IPv4 address"""
574

575
    #     log.info("Testing if server responds to pings in IPv4")
576

577
578
579
580
581
582
583
    #     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)
584

585
586
587
588
589
590
591
592
593
594
595
596
    # 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)
597
598
599

    def test_008_submit_shutdown_request(self):
        """Test submit request to shutdown server"""
600
601
602

        log.info("Shutting down server")

603
        self.cyclades.shutdown_server(self.serverid)
604
605
606

    def test_009_server_becomes_stopped(self):
        """Test server becomes STOPPED"""
607
608

        log.info("Waiting until server becomes STOPPED")
609
610
        self._insist_on_status_transition("ACTIVE", "STOPPED",
                                         self.action_timeout,
611
612
613
614
                                         self.action_timeout)

    def test_010_submit_start_request(self):
        """Test submit start server request"""
615
616
617

        log.info("Starting server")

618
        self.cyclades.start_server(self.serverid)
619
620
621

    def test_011_server_becomes_active(self):
        """Test server becomes ACTIVE again"""
622
623

        log.info("Waiting until server becomes ACTIVE")
624
625
        self._insist_on_status_transition("STOPPED", "ACTIVE",
                                         self.action_timeout,
626
627
                                         self.action_timeout)

628
629
    # def test_011a_server_responds_to_ping_IPv4(self):
    #     """Test server OS is actually up and running again"""
630

631
    #     log.info("Testing if server is actually up and running")
632

633
    #     self.test_006_server_responds_to_ping_IPv4()
634

635
636
    # def test_012_ssh_to_server_IPv4(self):
    #     """Test SSH to server public IPv4 works, verify hostname"""
637

638
639
640
641
    #     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)
642

643
644
645
646
    # 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")
647

648
649
650
    #     server = self.client.get_server_details(self.serverid)
    #     self._insist_on_ssh_hostname(self._get_ipv6(server),
    #                                  self.username, self.passwd)
651

652
653
654
655
656
657
    # 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)
    #     sock = _insist_on_tcp_connection(socket.AF_INET, ipv4, 3389)
658

659
660
661
662
    #     # 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()
663

664
665
666
667
    # 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")
668

669
670
671
    #     server = self.client.get_server_details(self.serverid)
    #     ipv6 = self._get_ipv6(server)
    #     sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
672

673
674
675
    #     # No actual RDP processing done. We assume the RDP server is there
    #     # if the connection to the RDP port is successful.
    #     sock.close()
676

677
678
679
680
    # def test_016_personality_is_enforced(self):
    #     """Test file injection for personality enforcement"""
    #     self._skipIf(self.is_windows, "only implemented for Linux servers")
    #     self._skipIf(self.personality == None, "No personality file selected")
681

682
    #     log.info("Trying to inject file for personality enforcement")
683

684
    #     server = self.client.get_server_details(self.serverid)
685

686
687
688
689
690
691
692
    #     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)
693

694
695
    def test_017_submit_delete_request(self):
        """Test submit request to delete server"""
696
697
698

        log.info("Deleting server")

699
700
701
702
        self.client.delete_server(self.serverid)

    def test_018_server_becomes_deleted(self):
        """Test server becomes DELETED"""
703
704
705

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

706
707
708
709
710
711
        self._insist_on_status_transition("ACTIVE", "DELETED",
                                         self.action_timeout,
                                         self.action_timeout)

    def test_019_server_no_longer_in_server_list(self):
        """Test server is no longer in server list"""
712
713
714

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

715
        servers = self.client.list_servers()
716
717
718
        self.assertNotIn(self.serverid, [s["id"] for s in servers])


719
class NetworkTestCase(unittest.TestCase):
John Giannelos's avatar
John Giannelos committed
720
    """ Testing networking in cyclades """
721

722
    @classmethod
John Giannelos's avatar
John Giannelos committed
723
724
    def setUpClass(cls):
        "Initialize kamaki, get list of current networks"
John Giannelos's avatar
John Giannelos committed
725
726
727

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

729
730
731
        cls.servername = "%s%s for %s" % (SNF_TEST_PREFIX,
                                          TEST_RUN_ID,
                                          cls.imagename)
732
733
734
735
736

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

    def _skipIf(self, condition, msg):
        if condition:
            self.skipTest(msg)
742

743
744
745
    def _get_ipv4(self, server):
        """Get the public IPv4 of a server from the detailed server info"""

746
        nics = server["attachments"]["values"]
John Giannelos's avatar
John Giannelos committed
747

748
749
750
751
752
753
        for nic in nics:
            net_id = nic["network_id"]
            if self.client.get_network_details(net_id)["public"] == True:
                public_addrs = nic["ipv4"]
        
        self.assertTrue(public_addrs != None)
John Giannelos's avatar
John Giannelos committed
754

755
        return public_addrs
John Giannelos's avatar
John Giannelos committed
756

757

758
759
760
761
762
763
764
765
766
    def _connect_loginname(self, os):
        """Return the login name for connections based on the server OS"""
        if os in ("Ubuntu", "Kubuntu", "Fedora"):
            return "user"
        elif os in ("windows", "windows_alpha1"):
            return "Administrator"
        else:
            return "root"

John Giannelos's avatar
John Giannelos committed
767
    def _ping_once(self, ip):
768

John Giannelos's avatar
John Giannelos committed
769
770
771
772
773
774
        """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()
775

John Giannelos's avatar
John Giannelos committed
776
777
        return (ret == 0)

John Giannelos's avatar
John Giannelos committed
778
    def test_00001a_submit_create_server_A(self):
779
        """Test submit create server request"""
780
781
782

        log.info("Creating test server A")

783
        serverA = self.client.create_server(self.servername, self.flavorid,
784
                                            self.imageid, personality=None)
785

John Giannelos's avatar
John Giannelos committed
786
787
788
789
        self.assertEqual(serverA["name"], self.servername)
        self.assertEqual(serverA["flavorRef"], self.flavorid)
        self.assertEqual(serverA["imageRef"], self.imageid)
        self.assertEqual(serverA["status"], "BUILD")
790
791

        # Update class attributes to reflect data on building server
John Giannelos's avatar
John Giannelos committed
792
793
794
        self.serverid['A'] = serverA["id"]
        self.username['A'] = None
        self.password['A'] = serverA["adminPass"]
795

796
797
        log.info("Server A id:" + str(serverA["id"]))
        log.info("Server password " + (self.password['A']))
798

John Giannelos's avatar
John Giannelos committed
799
    def test_00001b_serverA_becomes_active(self):
800
        """Test server becomes ACTIVE"""
801

802
        log.info("Waiting until test server A becomes ACTIVE")
803

804
        fail_tmout = time.time() + self.action_timeout
805
806
807
808
809
810
811
812
813
814
815
816
817
        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
818
    def test_00002a_submit_create_server_B(self):
John Giannelos's avatar
John Giannelos committed
819
        """Test submit create server request"""
820

821
822
        log.info("Creating test server B")

John Giannelos's avatar
John Giannelos committed
823
        serverB = self.client.create_server(self.servername, self.flavorid,
824
825
                                            self.imageid, personality=None)

John Giannelos's avatar
John Giannelos committed
826
827
828
829
830
831
832
833
834
835
        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"]

836
837
        log.info("Server B id: " + str(serverB["id"]))
        log.info("Password " + (self.password['B']))
838

John Giannelos's avatar
John Giannelos committed
839
    def test_00002b_serverB_becomes_active(self):
840
841
        """Test server becomes ACTIVE"""

842
843
        log.info("Waiting until test server B becomes ACTIVE")

844
        fail_tmout = time.time() + self.action_timeout
845
846
847
848
849
850
851
852
853
854
855
856
        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
857

858
    def test_001_create_network(self):
John Giannelos's avatar
John Giannelos committed
859
        """Test submit create network request"""
860
861
862

        log.info("Submit new network request")

863
        name = SNF_TEST_PREFIX + TEST_RUN_ID
864
        previous_num = len(self.client.list_networks())
John Giannelos's avatar
John Giannelos committed
865
        network = self.client.create_network(name,cidr='10.0.0.1/28')
866

867
        #Test if right name is assigned
John Giannelos's avatar
John Giannelos committed
868
        self.assertEqual(network['name'], name)
869

870
        # Update class attributes
John Giannelos's avatar
John Giannelos committed
871
872
        cls = type(self)
        cls.networkid = network['id']
873
874
        networks = self.client.list_networks()

John Giannelos's avatar
John Giannelos committed
875
876
        fail_tmout = time.time() + self.action_timeout

877
        #Test if new network is created
John Giannelos's avatar
John Giannelos committed
878
879
880
881
882
883
884
885
886
887
888
889
        while True:
            d = self.client.get_network_details(network['id'])
            if d['status'] == 'ACTIVE':
                connected = True
                break
            elif time.time() > fail_tmout:
                self.assertLess(time.time(), fail_tmout)
            else:
                log.info("Waiting for network to become ACTIVE")
                time.sleep(self.query_interval)

        self.assertTrue(connected)
890

891
    def test_002_connect_to_network(self):
892
        """Test connect VMs to network"""
893

894
895
        log.info("Connect VMs to private network")

896
897
        self.client.connect_server(self.serverid['A'], self.networkid)
        self.client.connect_server(self.serverid['B'], self.networkid)
898

899
        #Insist on connecting until action timeout
900
        fail_tmout = time.time() + self.action_timeout
901

902
        while True:
John Giannelos's avatar
John Giannelos committed
903
904
905
906
907

            netsA=[x['network_id'] for x in self.client.get_server_details(self.serverid['A'])['attachments']['values']]
            netsB=[x['network_id'] for x in self.client.get_server_details(self.serverid['B'])['attachments']['values']]

            if (self.networkid in netsA) and (self.networkid in netsB):
908
                conn_exists = True
John Giannelos's avatar
John Giannelos committed
909
910
                break
            elif time.time() > fail_tmout:
911
912
913
914
915
                self.assertLess(time.time(), fail_tmout)
            else:
                time.sleep(self.query_interval)

        self.assertTrue(conn_exists)
916

John Giannelos's avatar
John Giannelos committed
917
    def test_002a_reboot(self):
918
919
920
921
        """Rebooting server A"""

        log.info("Rebooting server A")

922
923
        self.client.shutdown_server(self.serverid['A'])

924
        fail_tmout = time.time() + self.action_timeout
925
926
927
928
929
930
931
932
933
934
935
936
        while True:
            d = self.client.get_server_details(self.serverid['A'])
            status = d['status']
            if status == 'STOPPED':
                break
            elif time.time() > fail_tmout:
                self.assertLess(time.time(), fail_tmout)
            else:
                time.sleep(self.query_interval)

        self.client.start_server(self.serverid['A'])

937
938
939
940
941
942
943
944
945
946
        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)
947

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

950
951
    # def test_002b_ping_server_A(self):
    #     "Test if server A responds to IPv4 pings"
952

953
    #     log.info("Testing if server A responds to IPv4 pings ")
954

955
956
    #     server = self.client.get_server_details(self.serverid['A'])
    #     ip = self._get_ipv4(server)
957

958
    #     fail_tmout = time.time() + self.action_timeout
959

960
    #     s = False
John Giannelos's avatar
John Giannelos committed
961

962
    #     while True:
John Giannelos's avatar
John Giannelos committed
963

964
965
966
    #         if self._ping_once(ip):
    #             s = True
    #             break
John Giannelos's avatar
John Giannelos committed
967

968
969
    #         elif time.time() > fail_tmout:
    #             self.assertLess(time.time(), fail_tmout)
John Giannelos's avatar
John Giannelos committed
970

971
972
    #         else:
    #             time.sleep(self.query_interval)
John Giannelos's avatar
John Giannelos committed
973

974
    #     self.assertTrue(s)
975
976

    def test_002c_reboot(self):
977
978
979
980
        """Reboot server B"""

        log.info("Rebooting server B")

981
982
        self.client.shutdown_server(self.serverid['B'])

983
        fail_tmout = time.time() + self.action_timeout
984
985
986
987
988
989
990
991
992
993
994
995
        while True:
            d = self.client.get_server_details(self.serverid['B'])
            status = d['status']
            if status == 'STOPPED':
                break
            elif time.time() > fail_tmout:
                self.assertLess(time.time(), fail_tmout)
            else:
                time.sleep(self.query_interval)

        self.client.start_server(self.serverid['B'])

996
997
998
999
1000
1001
1002
1003
1004
1005
        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)
1006

1007
        self.assertTrue(active)
1008

1009
1010
    # def test_002d_ping_server_B(self):
    #     """Test if server B responds to IPv4 pings"""
1011

1012
1013
1014
    #     log.info("Testing if server B responds to IPv4 pings")
    #     server = self.client.get_server_details(self.serverid['B'])
    #     ip = self._get_ipv4(server)
1015

1016
    #     fail_tmout = time.time() + self.action_timeout
1017

1018
    #     s = False
John Giannelos's avatar
John Giannelos committed
1019

1020
1021
1022
1023
    #     while True:
    #         if self._ping_once(ip):
    #             s = True
    #             break
John Giannelos's avatar
John Giannelos committed
1024

1025
1026
    #         elif time.time() > fail_tmout:
    #             self.assertLess(time.time(), fail_tmout)
John Giannelos's avatar
John Giannelos committed
1027

1028
1029
    #         else:
    #             time.sleep(self.query_interval)
John Giannelos's avatar
John Giannelos committed
1030

1031
    #     self.assertTrue(s)
John Giannelos's avatar
John Giannelos committed
1032

1033
1034
    # def test_003a_setup_interface_A(self):
    #     """Set up eth1 for server A"""
1035

1036
    #     self._skipIf(self.is_windows, "only valid for Linux servers")
1037

1038
    #     log.info("Setting up interface eth1 for server A")
1039

1040
1041
1042
    #     server = self.client.get_server_details(self.serverid['A'])
    #     image = self.client.get_image_details(self.imageid)
    #     os = image['metadata']['values']['os']
1043

1044
1045
    #     users = image["metadata"]["values"].get("users", None)
    #     userlist = users.split()
John Giannelos's avatar
John Giannelos committed
1046

1047
1048
1049
1050
1051
1052
    #     if "root" in userlist:
    #         loginname = "root"
    #     elif users == None:
    #         loginname = self._connect_loginname(os)
    #     else:
    #         loginname = choice(userlist)
1053

1054
1055
    #     hostip = self._get_ipv4(server)
    #     myPass = self.password['A']
1056

1057
    #     log.info("SSH in server A as %s/%s" % (loginname, myPass))
1058

1059
    #     res = False
John Giannelos's avatar