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

b/snf-cyclades-app/synnefo/tools/burnin.py
1
 #!/usr/bin/env python
1
#!/usr/bin/env python
2 2

  
3 3
# Copyright 2011 GRNET S.A. All rights reserved.
4 4
#
......
354 354
        # The hostname must be of the form 'prefix-id'
355 355
        self.assertTrue(hostname.endswith("-%d\n" % self.serverid))
356 356

  
357

  
358
    def _file_md5(filename, block_size = 2**20):
359
        f = open(filename)
360
        md5 = hashlib.md5()
361
        while True:
362
            data = f.read(block_size)
363
            if not data:
364
                break
365
            md5.update(data)
366
            f.close()
367
    
368
        return md5.digest()
369

  
370
    
371 357
    def _check_file_through_ssh(self, hostip, username, password, remotepath, content):
372 358
        msg = "Trying file injection through SSH to %s, as %s/%s" % (hostip, username, password)
373 359
        log.info(msg)
......
392 378
        remote_content = b64encode(f.read())
393 379

  
394 380
        # Check if files are the same
395
        return remote_content == content
381
        return (remote_content == content)
396 382

  
397 383
    def _skipIf(self, condition, msg):
398 384
        if condition:
......
458 444
        self._insist_on_status_transition("BUILD", "ACTIVE",
459 445
                                         self.build_fail, self.build_warning)
460 446

  
461
    # def test_003a_get_server_oob_console(self):
462
    #     """Test getting OOB server console over VNC
447
    def test_003a_get_server_oob_console(self):
448
        """Test getting OOB server console over VNC
463 449

  
464
    #     Implementation of RFB protocol follows
465
    #     http://www.realvnc.com/docs/rfbproto.pdf.
450
        Implementation of RFB protocol follows
451
        http://www.realvnc.com/docs/rfbproto.pdf.
466 452

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

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

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

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

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

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

  
495
    #     # Step 6. SecurityResult (par 6.1.3)
496
    #     result = sock.recv(4)
497
    #     self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
498
    #     sock.close()
455
        console = self.cyclades.get_server_console(self.serverid)
456
        self.assertEquals(console['type'], "vnc")
457
        sock = self._insist_on_tcp_connection(socket.AF_UNSPEC,
458
                                        console["host"], console["port"])
459

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

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

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

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

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

  
481
        # Step 6. SecurityResult (par 6.1.3)
482
        result = sock.recv(4)
483
        self.assertEquals(list(result), ['\x00', '\x00', '\x00', '\x00'])
484
        sock.close()
499 485
        
500 486
    def test_004_server_has_ipv4(self):
501 487
        """Test active server has a valid IPv4 address"""
......
503 489
        ipv4 = self._get_ipv4(server)
504 490
        self.assertEquals(IP(ipv4).version(), 4)
505 491

  
506
    # def test_005_server_has_ipv6(self):
507
    #     """Test active server has a valid IPv6 address"""
508
    #     server = self.client.get_server_details(self.serverid)
509
    #     ipv6 = self._get_ipv6(server)
510
    #     self.assertEquals(IP(ipv6).version(), 6)
492
    def test_005_server_has_ipv6(self):
493
        """Test active server has a valid IPv6 address"""
494
        server = self.client.get_server_details(self.serverid)
495
        ipv6 = self._get_ipv6(server)
496
        self.assertEquals(IP(ipv6).version(), 6)
511 497

  
512 498
    def test_006_server_responds_to_ping_IPv4(self):
513 499
        """Test server responds to ping on IPv4 address"""
......
519 505
                                        self._ping_once,
520 506
                                        False, ip)
521 507

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

  
532 518
    def test_008_submit_shutdown_request(self):
533 519
        """Test submit request to shutdown server"""
......
560 546
        self._insist_on_ssh_hostname(self._get_ipv4(server),
561 547
                                     self.username, self.passwd)
562 548

  
563
    # def test_013_ssh_to_server_IPv6(self):
564
    #     """Test SSH to server public IPv6 works, verify hostname"""
565
    #     self._skipIf(self.is_windows, "only valid for Linux servers")
566
    #     server = self.client.get_server_details(self.serverid)
567
    #     self._insist_on_ssh_hostname(self._get_ipv6(server),
568
    #                                  self.username, self.passwd)
549
    def test_013_ssh_to_server_IPv6(self):
550
        """Test SSH to server public IPv6 works, verify hostname"""
551
        self._skipIf(self.is_windows, "only valid for Linux servers")
552
        server = self.client.get_server_details(self.serverid)
553
        self._insist_on_ssh_hostname(self._get_ipv6(server),
554
                                     self.username, self.passwd)
569 555

  
570 556
    def test_014_rdp_to_server_IPv4(self):
571 557
        "Test RDP connection to server public IPv4 works"""
......
579 565
        # FIXME: Use rdesktop, analyze exit code? see manpage [costasd]
580 566
        sock.close()
581 567

  
582
    # def test_015_rdp_to_server_IPv6(self):
583
    #     "Test RDP connection to server public IPv6 works"""
584
    #     self._skipIf(not self.is_windows, "only valid for Windows servers")
585
    #     server = self.client.get_server_details(self.serverid)
586
    #     ipv6 = self._get_ipv6(server)
587
    #     sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
568
    def test_015_rdp_to_server_IPv6(self):
569
        "Test RDP connection to server public IPv6 works"""
570
        self._skipIf(not self.is_windows, "only valid for Windows servers")
571
        server = self.client.get_server_details(self.serverid)
572
        ipv6 = self._get_ipv6(server)
573
        sock = _get_tcp_connection(socket.AF_INET6, ipv6, 3389)
588 574

  
589
    #     # No actual RDP processing done. We assume the RDP server is there
590
    #     # if the connection to the RDP port is successful.
591
    #     sock.close()
575
        # No actual RDP processing done. We assume the RDP server is there
576
        # if the connection to the RDP port is successful.
577
        sock.close()
592 578

  
593 579
    def test_016_personality_is_enforced(self):
594 580
        """Test file injection for personality enforcement"""
......
621 607

  
622 608
class NetworkTestCase(unittest.TestCase):
623 609
    """ Testing networking in cyclades """
610
  
624 611
    @classmethod
625 612
    def setUpClass(cls):
626 613
        "Initialize kamaki, get list of current networks"
......
640 627

  
641 628
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
642 629
        is_windows = imagename.lower().find("windows") >= 0
630

  
631
        #Run testcases for server spawning in order to ensure it is done right
643 632
        setupCase =  _spawn_server_test_case(imageid=str(imageid), flavorid=flavorid,
644 633
                                             imagename=imagename,
645 634
                                             personality=None,
......
679 668
    def test_002_connect_to_network(self):
680 669
        """Test connect VM to network"""
681 670
        servers = self.compute.list_servers()
682
        server = choice(servers)
671

  
672
        #Pick a server created only for the test
673
        server = choice([s for s in servers if s['name'].startswith(SNF_TEST_PREFIX)])
683 674
        self.client.connect_server(server['id'], self.networkid)
684 675
        
685 676
        #Update class attributes
686 677
        cls = type(self)
687 678
        cls.serverid = server['id']
688 679

  
689
        #FIXME: Insist on new connection instead of this
690
        time.sleep(15)
691

  
680
        #Insist on connecting until action timeout
692 681
        connected = (self.client.get_network_details(self.networkid))
693
        connections = len(connected['servers']['values'])
682
        fail_tmout = time.time()+self.action_timeout
694 683

  
695
        self.assertTrue(connections>=1)
696
        
684
        while True:
685
            connections = connected['servers']['values']
686
            if (self.serverid in connections):
687
                conn_exists = True
688
            if time.time() > fail_tmout:
689
                self.assertLess(time.time(), fail_tmout)
690
            else:
691
                time.sleep(self.query_interval)
692

  
693
        self.assertTrue(conn_exists)
694
            
697 695

  
698 696
    def test_003_disconnect_from_network(self):
699 697
        prev_state = self.client.get_network_details(self.networkid)
700 698
        prev_conn = len(prev_state['servers']['values'])
701 699

  
702 700
        self.client.disconnect_server(self.serverid, self.networkid)
703

  
704
        #FIXME: Insist on deleting instead of this
705 701
        time.sleep(15)
706 702

  
703
        #Insist on deleting until action timeout
707 704
        connected = (self.client.get_network_details(self.networkid))
708
        curr_conn = len(connected['servers']['values'])
705
        fail_tmout = time.time()+self.action_timeout
709 706

  
710
        self.assertTrue(curr_conn < prev_conn)
707
        while True:
708
            connections = connected['servers']['values']
709
            if (self.serverid not in connections):
710
                conn_exists = False
711
            if time.time() > fail_tmout:
712
                self.assertLess(time.time(), fail_tmout)
713
            else:
714
                time.sleep(self.query_interval)
715

  
716
        self.assertFalse(conn_exists)
711 717

  
712 718
    def test_004_destroy_network(self):
713 719
        """Test submit delete network request"""
714
        self.client.delete_network(self.networkid)
715
        
720
        self.client.delete_network(self.networkid)        
716 721
        networks = self.client.list_networks()
717
        self.assertEqual(len(networks),1)
718 722

  
723
        curr_net = []
724
        for net in networks:
725
            curr_net.appent(net['id'])
726

  
727
        self.assertTrue(self.networkid not in curr_net)
728
        
729
    def test_005_cleanup_servers(self):
730
        """Cleanup servers created for this test"""
731
        self.compute.delete_server(self.server_id)
732
        fail_tmout = time.time()+self.action_timeout
733

  
734
        #Ensure server gets deleted
735
        while True:
736
            status = self.compute.get_server_details(self.serverid)
737
            if status == 'DELETED':
738
                deleted = True
739
            elif time.time() > fail_tmout: 
740
                self.assertLess(time.time(), fail_tmout)
741
            else:
742
                time.sleep(self.query_interval)
743

  
744
        self.assertTrue(deleted)
719 745

  
720 746
class TestRunnerProcess(Process):
721 747
    """A distinct process used to execute part of the tests in parallel"""
......
803 829
    # Make sure the class can be pickled, by listing it among
804 830
    # the attributes of __main__. A PicklingError is raised otherwise.
805 831
    setattr(__main__, name, cls)
806
    return cls
832
    return cls 
833

  
834
def _spawn_network_test_case(**kwargs):
835
    """Construct a new unit test case class from NetworkTestCase"""
836

  
837
    name = "NetworkTestCase"+TEST_RUN_ID
838
    cls = type(name, (NetworkTestCase,), kwargs)
839

  
840
    # Make sure the class can be pickled, by listing it among
841
    # the attributes of __main__. A PicklingError is raised otherwise.
842
    setattr(__main__, name, cls)
843
    return cls 
807 844

  
808 845

  
809 846
def cleanup_servers(delete_stale=False):
......
909 946
                           "name starts with `%s'" % SNF_TEST_PREFIX,
910 947
                      default=False)
911 948
    parser.add_option("--force-personality",
912
                      action="store", dest="personality_path",
949
                      action="store", type="string", dest="personality_path",
913 950
                      help="Force a personality file injection. File path required. ",
914 951
                      default=None)
915 952
    
......
1003 1040
        servername = "%s%s for %s" % (SNF_TEST_PREFIX, TEST_RUN_ID, imagename)
1004 1041
        is_windows = imagename.lower().find("windows") >= 0
1005 1042
        
1006
        ServerTestCase = _spawn_server_test_case(imageid=imageid, flavorid=flavorid,
1007
                                                 imagename=imagename,
1008
                                                 personality=personality,
1009
                                                 servername=servername,
1010
                                                 is_windows=is_windows,
1011
                                                 action_timeout=opts.action_timeout,
1012
                                                 build_warning=opts.build_warning,
1013
                                                 build_fail=opts.build_fail,
1014
                                                 query_interval=opts.query_interval,
1015
                                                 )
1043
    ServerTestCase = _spawn_server_test_case(imageid=imageid, flavorid=flavorid,
1044
                                             imagename=imagename,
1045
                                             personality=personality,
1046
                                             servername=servername,
1047
                                             is_windows=is_windows,
1048
                                             action_timeout=opts.action_timeout,
1049
                                             build_warning=opts.build_warning,
1050
                                             build_fail=opts.build_fail,
1051
                                             query_interval=opts.query_interval,
1052
                                             )
1016 1053

  
1017 1054

  
1018 1055
    #Running all the testcases sequentially
1019 1056
    #seq_cases = [UnauthorizedTestCase, FlavorsTestCase, ImagesTestCase, ServerTestCase, NetworkTestCase]
1020

  
1021
    seq_cases = [NetworkTestCase]
1057
    
1058
    newNetworkTestCase = _spawn_network_test_case(action_timeout = opts.action_timeout,
1059
                                                  query_interval = opts.query_interval)
1060
    
1061
    seq_cases = [newNetworkTestCase]
1022 1062
    for case in seq_cases:
1023 1063
        suite = unittest.TestLoader().loadTestsFromTestCase(case)
1024 1064
        unittest.TextTestRunner(verbosity=2).run(suite)

Also available in: Unified diff