Revision 9659e075 snf-cyclades-app/synnefo/tools/burnin.py

b/snf-cyclades-app/synnefo/tools/burnin.py
49 49
import sys
50 50
import time
51 51
import hashlib
52

  
52
from base64 import b64encode
53
from pwd import getpwuid
54
from grp import getgrgid
53 55
from IPy import IP
54 56
from multiprocessing import Process, Queue
55 57
from random import choice
......
366 368
        return md5.digest()
367 369

  
368 370
    
369
    def _check_file_through_ssh(self, hostip, username, pavssword, path):
370
        msg = "SSH to %s, as %s/%s" % (hostip, username, password)
371
    def _check_file_through_ssh(self, hostip, username, password, remotepath, content):
372
        msg = "Trying file injection through SSH to %s, as %s/%s" % (hostip, username, password)
371 373
        try:
372 374
            ssh = paramiko.SSHClient()
373 375
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
......
375 377
        except socket.error:
376 378
            raise AssertionError
377 379
        
380
        transport = paramiko.Transport((hostip,22))
378 381
        transport.connect(username = username, password = password)
379
        remotepath = path
382

  
380 383
        localpath = '/tmp/'+SNF_TEST_PREFIX+'injection'
381 384
        sftp = paramiko.SFTPClient.from_transport(transport)
382 385
        sftp.get(remotepath, localpath)
......
384 387
        sftp.close()
385 388
        transport.close()
386 389

  
390
        f = open(localpath)
391
        remote_content = b64encode(f.read())
392

  
387 393
        # Check if files are the same
388
        return _file_md5(localpath) == _file_md5('test.txt')
394
        return remote_content == content
389 395

  
390 396
    def _skipIf(self, condition, msg):
391 397
        if condition:
......
395 401
        """Test submit create server request"""
396 402
        server = self.client.create_server(self.servername, self.flavorid,
397 403
                                           self.imageid, self.personality)
404

  
398 405
        self.assertEqual(server["name"], self.servername)
399 406
        self.assertEqual(server["flavorRef"], self.flavorid)
400 407
        self.assertEqual(server["imageRef"], self.imageid)
......
450 457
        self._insist_on_status_transition("BUILD", "ACTIVE",
451 458
                                         self.build_fail, self.build_warning)
452 459

  
453
    def test_003a_get_server_oob_console(self):
454
        """Test getting OOB server console over VNC
460
    # def test_003a_get_server_oob_console(self):
461
    #     """Test getting OOB server console over VNC
455 462

  
456
        Implementation of RFB protocol follows
457
        http://www.realvnc.com/docs/rfbproto.pdf.
463
    #     Implementation of RFB protocol follows
464
    #     http://www.realvnc.com/docs/rfbproto.pdf.
458 465

  
459
        """
466
    #     """
467
        
468
    #     console = self.cyclades.get_server_console(self.serverid)
469
    #     self.assertEquals(console['type'], "vnc")
470
    #     sock = self._insist_on_tcp_connection(socket.AF_UNSPEC,
471
    #                                     console["host"], console["port"])
472

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

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

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

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

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

  
494
    #     # Step 6. SecurityResult (par 6.1.3)
495
    #     result = sock.recv(4)
496
    #     self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
497
    #     sock.close()
460 498
        
461
        console = self.cyclades.get_server_console(self.serverid)
462
        self.assertEquals(console['type'], "vnc")
463
        sock = self._insist_on_tcp_connection(socket.AF_UNSPEC,
464
                                        console["host"], console["port"])
465

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

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

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

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

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

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

  
492 499
    def test_004_server_has_ipv4(self):
493 500
        """Test active server has a valid IPv4 address"""
494 501
        server = self.client.get_server_details(self.serverid)
495 502
        ipv4 = self._get_ipv4(server)
496 503
        self.assertEquals(IP(ipv4).version(), 4)
497 504

  
498
    def test_005_server_has_ipv6(self):
499
        """Test active server has a valid IPv6 address"""
500
        server = self.client.get_server_details(self.serverid)
501
        ipv6 = self._get_ipv6(server)
502
        self.assertEquals(IP(ipv6).version(), 6)
505
    # def test_005_server_has_ipv6(self):
506
    #     """Test active server has a valid IPv6 address"""
507
    #     server = self.client.get_server_details(self.serverid)
508
    #     ipv6 = self._get_ipv6(server)
509
    #     self.assertEquals(IP(ipv6).version(), 6)
503 510

  
504 511
    def test_006_server_responds_to_ping_IPv4(self):
505 512
        """Test server responds to ping on IPv4 address"""
......
511 518
                                        self._ping_once,
512 519
                                        False, ip)
513 520

  
514
    def test_007_server_responds_to_ping_IPv6(self):
515
        """Test server responds to ping on IPv6 address"""
516
        server = self.client.get_server_details(self.serverid)
517
        ip = self._get_ipv6(server)
518
        self._try_until_timeout_expires(self.action_timeout,
519
                                        self.action_timeout,
520
                                        "PING IPv6 to %s" % ip,
521
                                        self._ping_once,
522
                                        True, ip)
521
    # def test_007_server_responds_to_ping_IPv6(self):
522
    #     """Test server responds to ping on IPv6 address"""
523
    #     server = self.client.get_server_details(self.serverid)
524
    #     ip = self._get_ipv6(server)
525
    #     self._try_until_timeout_expires(self.action_timeout,
526
    #                                     self.action_timeout,
527
    #                                     "PING IPv6 to %s" % ip,
528
    #                                     self._ping_once,
529
    #                                     True, ip)
523 530

  
524 531
    def test_008_submit_shutdown_request(self):
525 532
        """Test submit request to shutdown server"""
......
552 559
        self._insist_on_ssh_hostname(self._get_ipv4(server),
553 560
                                     self.username, self.passwd)
554 561

  
555
    def test_013_ssh_to_server_IPv6(self):
556
        """Test SSH to server public IPv6 works, verify hostname"""
557
        self._skipIf(self.is_windows, "only valid for Linux servers")
558
        server = self.client.get_server_details(self.serverid)
559
        self._insist_on_ssh_hostname(self._get_ipv6(server),
560
                                     self.username, self.passwd)
562
    # def test_013_ssh_to_server_IPv6(self):
563
    #     """Test SSH to server public IPv6 works, verify hostname"""
564
    #     self._skipIf(self.is_windows, "only valid for Linux servers")
565
    #     server = self.client.get_server_details(self.serverid)
566
    #     self._insist_on_ssh_hostname(self._get_ipv6(server),
567
    #                                  self.username, self.passwd)
561 568

  
562 569
    def test_014_rdp_to_server_IPv4(self):
563 570
        "Test RDP connection to server public IPv4 works"""
......
571 578
        # FIXME: Use rdesktop, analyze exit code? see manpage [costasd]
572 579
        sock.close()
573 580

  
574
    def test_015_rdp_to_server_IPv6(self):
575
        "Test RDP connection to server public IPv6 works"""
576
        self._skipIf(not self.is_windows, "only valid for Windows servers")
577
        server = self.client.get_server_details(self.serverid)
578
        ipv6 = self._get_ipv6(server)
579
        sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
581
    # def test_015_rdp_to_server_IPv6(self):
582
    #     "Test RDP connection to server public IPv6 works"""
583
    #     self._skipIf(not self.is_windows, "only valid for Windows servers")
584
    #     server = self.client.get_server_details(self.serverid)
585
    #     ipv6 = self._get_ipv6(server)
586
    #     sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
580 587

  
581
        # No actual RDP processing done. We assume the RDP server is there
582
        # if the connection to the RDP port is successful.
583
        sock.close()
588
    #     # No actual RDP processing done. We assume the RDP server is there
589
    #     # if the connection to the RDP port is successful.
590
    #     sock.close()
584 591

  
585 592
    def test_016_personality_is_enforced(self):
586 593
        """Test file injection for personality enforcement"""
587 594
        self._skipIf(self.is_windows, "only implemented for Linux servers")
588
        
595
        self._skipIf(self.personality == None, "No personality file selected")
589 596

  
590
        #Create new server
591
        server = self.client.create_server(self.servername, self.flavorid,
592
                                           self.imageid, self.personality) #/path/to/file
593
        self.assertEqual(server["name"], self.servername)
594
        self.assertEqual(server["flavorRef"], self.flavorid)
595
        self.assertEqual(server["imageRef"], self.imageid)
596
        self.assertEqual(server["status"], "BUILD")
597
        
598
        #Test if is in active state
599
        servers = self.client.list_servers(detail=True)
600
        servers = filter(lambda x: x["name"] == self.servername, servers)
601
        self.assertEqual(len(servers), 1)
602

  
603
        #Test if server is building in details
604 597
        server = self.client.get_server_details(self.serverid)
605
        self.assertEqual(server["name"], self.servername)
606
        self.assertEqual(server["flavorRef"], self.flavorid)
607
        self.assertEqual(server["imageRef"], self.imageid)
608
        self.assertEqual(server["status"], "BUILD")
609 598

  
610
        #Insist on transition
611
        self._insist_on_status_transition("BUILD", "ACTIVE",
612
                                          self.build_fail, self.build_warning)
613
        
614
        #Test if file injected exists
615
        equal = self._check_file_through_ssh(self._get_ipv4(server), self.username, self.password)
599
        for inj_file in self.personality:
600
            equal_files = self._check_file_through_ssh(self._get_ipv4(server), inj_file['owner'], 
601
                                                       self.passwd, inj_file['path'], inj_file['contents'])
602
            self.assertTrue(equal_files)
616 603
        
617
        self.assertTrue(equal)
618 604

  
619 605
    def test_017_submit_delete_request(self):
620 606
        """Test submit request to delete server"""
......
918 904
                      help="Delete stale servers from previous runs, whose "\
919 905
                           "name starts with `%s'" % SNF_TEST_PREFIX,
920 906
                      default=False)
907
    parser.add_option("--force-personality",
908
                      action="store", dest="personality_path",
909
                      help="Force a personality file injection. File path required. ",
910
                      default=None)
911
    
921 912

  
922 913
    # FIXME: Change the default for build-fanout to 10
923 914
    # FIXME: Allow the user to specify a specific set of Images to test
......
988 979
        imageid = str(image["id"])
989 980
        flavorid = choice([f["id"] for f in DFLAVORS if f["disk"] >= 20])
990 981
        imagename = image["name"]
991
        personality = None   # FIXME
982
        
983
        
984
        if opts.personality_path != None:
985
            f = open(opts.personality_path)
986
            content = b64encode(f.read())
987
            personality = []
988
            st = os.stat(opts.personality_path)
989
            personality.append({
990
                    'path': '/root/test_inj_file',
991
                    'owner': 'root',
992
                    'group': 'root',
993
                    'mode': 0x7777 & st.st_mode,
994
                    'contents': content
995
                    })
996
        else:
997
            personality = None
998

  
992 999
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
993 1000
        is_windows = imagename.lower().find("windows") >= 0
994 1001
        
......
1000 1007
                                                 action_timeout=opts.action_timeout,
1001 1008
                                                 build_warning=opts.build_warning,
1002 1009
                                                 build_fail=opts.build_fail,
1003
                                                 query_interval=opts.query_interval)
1010
                                                 query_interval=opts.query_interval,
1011
                                                 )
1004 1012

  
1005 1013

  
1006 1014
    #Running all the testcases sequentially
1007 1015
    #seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase, ServerTestCase, NetworkTestCase]
1008 1016

  
1009
    seq_cases = [NetworkTestCase]
1017
    seq_cases = [ServerTestCase]
1010 1018
    for case in seq_cases:
1011 1019
        suite = unittest.TestLoader().loadTestsFromTestCase(case)
1012 1020
        unittest.TextTestRunner(verbosity=2).run(suite)

Also available in: Unified diff