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