1818import logging
1919import random
2020import time
21+ import socket
2122
2223# All tests inherit from cloudstackTestCase
2324from marvin .cloudstackTestCase import cloudstackTestCase
2425
2526# Import Integration Libraries
2627# base - contains all resources as entities and defines create, delete, list operations on them
27- from marvin .lib .base import Account , DiskOffering , ServiceOffering , Snapshot , StoragePool , Template , User , \
28- VirtualMachine , Volume
28+ from marvin .lib .base import Account , DiskOffering , ServiceOffering , Snapshot , StoragePool , Template , User
29+ from marvin . lib . base import VirtualMachine , Volume , VmSnapshot
2930
3031# common - commonly used methods for all tests are listed here
3132from marvin .lib .common import get_domain , get_template , get_zone , list_clusters , list_hosts , list_virtual_machines , \
@@ -97,8 +98,7 @@ class TestData:
9798 # hypervisor type to test
9899 hypervisor_type = kvm
99100
100- def __init__ (self ):
101- linstor_controller_url = "http://10.43.224.8"
101+ def __init__ (self , linstor_controller_url ):
102102 self .testdata = {
103103 TestData .kvm : {
104104 TestData .username : "admin" ,
@@ -197,7 +197,7 @@ def __init__(self):
197197 "resourceGroup" : "acs-test-same"
198198 }
199199 },
200- # Linstor storage pool on different ScaleIO storage instance
200+ # Linstor storage pool on different Linstor storage instance
201201 TestData .primaryStorageDistinctInstance : {
202202 "name" : "Linstor-%d" % random .randint (0 , 100 ),
203203 TestData .scope : "ZONE" ,
@@ -225,6 +225,44 @@ def __init__(self):
225225 },
226226 }
227227
228+ class ServiceReady :
229+ @classmethod
230+ def ready (cls , hostname : str , port : int ) -> bool :
231+ try :
232+ s = socket .create_connection ((hostname , port ), timeout = 1 )
233+ s .close ()
234+ return True
235+ except (ConnectionRefusedError , socket .timeout , OSError ):
236+ return False
237+
238+ @classmethod
239+ def wait (
240+ cls ,
241+ hostname ,
242+ port ,
243+ wait_interval = 5 ,
244+ timeout = 90 ,
245+ service_name = 'ssh' ) -> bool :
246+ """
247+ Wait until the controller can be reached.
248+ :param hostname:
249+ :param port: port of the application
250+ :param wait_interval:
251+ :param timeout: time to wait until exit with False
252+ :param service_name: name of the service to wait
253+ :return:
254+ """
255+ starttime = int (round (time .time () * 1000 ))
256+ while not cls .ready (hostname , port ):
257+ if starttime + timeout * 1000 < int (round (time .time () * 1000 )):
258+ raise RuntimeError ("{s} {h} cannot be reached." .format (s = service_name , h = hostname ))
259+ time .sleep (wait_interval )
260+ return True
261+
262+ @classmethod
263+ def wait_ssh_ready (cls , hostname , wait_interval = 1 , timeout = 90 ):
264+ return cls .wait (hostname , 22 , wait_interval , timeout , "ssh" )
265+
228266
229267class TestLinstorVolumes (cloudstackTestCase ):
230268 _volume_vm_id_and_vm_id_do_not_match_err_msg = "The volume's VM ID and the VM's ID do not match."
@@ -239,7 +277,11 @@ def setUpClass(cls):
239277 cls .apiClient = testclient .getApiClient ()
240278 cls .configData = testclient .getParsedTestDataConfig ()
241279 cls .dbConnection = testclient .getDbConnection ()
242- cls .testdata = TestData ().testdata
280+
281+ # first host has the linstor controller
282+ first_host = list_hosts (cls .apiClient )[0 ]
283+
284+ cls .testdata = TestData (first_host .ipaddress ).testdata
243285
244286 # Get Resources from Cloud Infrastructure
245287 cls .zone = get_zone (cls .apiClient , zone_id = cls .testdata [TestData .zoneId ])
@@ -326,7 +368,8 @@ def setUpClass(cls):
326368 serviceofferingid = cls .compute_offering .id ,
327369 templateid = cls .template .id ,
328370 domainid = cls .domain .id ,
329- startvm = False
371+ startvm = False ,
372+ mode = 'basic' ,
330373 )
331374
332375 TestLinstorVolumes ._start_vm (cls .virtual_machine )
@@ -394,7 +437,8 @@ def test_01_create_vm_with_volume(self):
394437 serviceofferingid = self .compute_offering .id ,
395438 templateid = self .template .id ,
396439 domainid = self .domain .id ,
397- startvm = False
440+ startvm = False ,
441+ mode = 'basic' ,
398442 )
399443
400444 TestLinstorVolumes ._start_vm (test_virtual_machine )
@@ -887,8 +931,31 @@ def test_08_delete_volume_was_attached(self):
887931 "Check volume was deleted"
888932 )
889933
934+ @attr (tags = ['basic' ], required_hardware = False )
935+ def test_09_create_snapshot (self ):
936+ """Create snapshot of root disk"""
937+ self .virtual_machine .stop (self .apiClient )
938+
939+ volume = list_volumes (
940+ self .apiClient ,
941+ virtualmachineid = self .virtual_machine .id ,
942+ type = "ROOT" ,
943+ listall = True ,
944+ )
945+ snapshot = Snapshot .create (
946+ self .apiClient ,
947+ volume_id = volume [0 ].id ,
948+ account = self .account .name ,
949+ domainid = self .domain .id ,
950+ )
951+
952+ self .assertIsNotNone (snapshot , "Could not create snapshot" )
953+
954+ snapshot .delete (self .apiClient )
955+
956+
890957 @attr (tags = ['advanced' , 'migration' ], required_hardware = False )
891- def test_09_migrate_volume_to_same_instance_pool (self ):
958+ def test_10_migrate_volume_to_same_instance_pool (self ):
892959 """Migrate volume to the same instance pool"""
893960
894961 if not self .testdata [TestData .migrationTests ]:
@@ -906,7 +973,8 @@ def test_09_migrate_volume_to_same_instance_pool(self):
906973 serviceofferingid = self .compute_offering .id ,
907974 templateid = self .template .id ,
908975 domainid = self .domain .id ,
909- startvm = False
976+ startvm = False ,
977+ mode = 'basic' ,
910978 )
911979
912980 TestLinstorVolumes ._start_vm (test_virtual_machine )
@@ -1020,7 +1088,7 @@ def test_09_migrate_volume_to_same_instance_pool(self):
10201088 test_virtual_machine .delete (self .apiClient , True )
10211089
10221090 @attr (tags = ['advanced' , 'migration' ], required_hardware = False )
1023- def test_10_migrate_volume_to_distinct_instance_pool (self ):
1091+ def test_11_migrate_volume_to_distinct_instance_pool (self ):
10241092 """Migrate volume to distinct instance pool"""
10251093
10261094 if not self .testdata [TestData .migrationTests ]:
@@ -1038,7 +1106,8 @@ def test_10_migrate_volume_to_distinct_instance_pool(self):
10381106 serviceofferingid = self .compute_offering .id ,
10391107 templateid = self .template .id ,
10401108 domainid = self .domain .id ,
1041- startvm = False
1109+ startvm = False ,
1110+ mode = 'basic' ,
10421111 )
10431112
10441113 TestLinstorVolumes ._start_vm (test_virtual_machine )
@@ -1151,6 +1220,132 @@ def test_10_migrate_volume_to_distinct_instance_pool(self):
11511220
11521221 test_virtual_machine .delete (self .apiClient , True )
11531222
1223+ @attr (tags = ["basic" ], required_hardware = False )
1224+ def test_12_create_vm_snapshots (self ):
1225+ """Test to create VM snapshots
1226+ """
1227+ vm = TestLinstorVolumes ._start_vm (self .virtual_machine )
1228+
1229+ try :
1230+ # Login to VM and write data to file system
1231+ self .debug ("virt: {}" .format (vm ))
1232+ ssh_client = self .virtual_machine .get_ssh_client (vm .ipaddress , retries = 5 )
1233+ ssh_client .execute ("echo 'hello world' > testfile" )
1234+ ssh_client .execute ("sync" )
1235+ except Exception as exc :
1236+ self .fail ("SSH failed for Virtual machine {}: {}" .format (self .virtual_machine .ssh_ip , exc ))
1237+
1238+ time .sleep (10 )
1239+ memory_snapshot = False
1240+ vm_snapshot = VmSnapshot .create (
1241+ self .apiClient ,
1242+ self .virtual_machine .id ,
1243+ memory_snapshot ,
1244+ "VMSnapshot1" ,
1245+ "test snapshot"
1246+ )
1247+ self .assertEqual (
1248+ vm_snapshot .state ,
1249+ "Ready" ,
1250+ "Check the snapshot of vm is ready!"
1251+ )
1252+
1253+ @attr (tags = ["basic" ], required_hardware = False )
1254+ def test_13_revert_vm_snapshots (self ):
1255+ """Test to revert VM snapshots
1256+ """
1257+
1258+ result = None
1259+ try :
1260+ ssh_client = self .virtual_machine .get_ssh_client (reconnect = True )
1261+ result = ssh_client .execute ("rm -rf testfile" )
1262+ except Exception as exc :
1263+ self .fail ("SSH failed for Virtual machine %s: %s" .format (self .virtual_machine .ipaddress , exc ))
1264+
1265+ if result is not None and "No such file or directory" in str (result ):
1266+ self .fail ("testfile not deleted" )
1267+
1268+ time .sleep (5 )
1269+
1270+ list_snapshot_response = VmSnapshot .list (
1271+ self .apiClient ,
1272+ virtualmachineid = self .virtual_machine .id ,
1273+ listall = True )
1274+
1275+ self .assertEqual (
1276+ isinstance (list_snapshot_response , list ),
1277+ True ,
1278+ "Check list response returns a valid list"
1279+ )
1280+ self .assertNotEqual (
1281+ list_snapshot_response ,
1282+ None ,
1283+ "Check if snapshot exists in ListSnapshot"
1284+ )
1285+
1286+ self .assertEqual (
1287+ list_snapshot_response [0 ].state ,
1288+ "Ready" ,
1289+ "Check the snapshot of vm is ready!"
1290+ )
1291+
1292+ self .virtual_machine .stop (self .apiClient , forced = True )
1293+
1294+ VmSnapshot .revertToSnapshot (
1295+ self .apiClient ,
1296+ list_snapshot_response [0 ].id
1297+ )
1298+
1299+ TestLinstorVolumes ._start_vm (self .virtual_machine )
1300+
1301+ try :
1302+ ssh_client = self .virtual_machine .get_ssh_client (reconnect = True )
1303+
1304+ result = ssh_client .execute ("cat testfile" )
1305+
1306+ except Exception as exc :
1307+ self .fail ("SSH failed for Virtual machine {}: {}" .format (self .virtual_machine .ipaddress , exc ))
1308+
1309+ self .assertEqual (
1310+ "hello world" ,
1311+ result [0 ],
1312+ "Check the content is the same as originaly written"
1313+ )
1314+
1315+ @attr (tags = ["basic" ], required_hardware = False )
1316+ def test_14_delete_vm_snapshots (self ):
1317+ """Test to delete vm snapshots
1318+ """
1319+
1320+ list_snapshot_response = VmSnapshot .list (
1321+ self .apiClient ,
1322+ virtualmachineid = self .virtual_machine .id ,
1323+ listall = True )
1324+
1325+ self .assertEqual (
1326+ isinstance (list_snapshot_response , list ),
1327+ True ,
1328+ "Check list response returns a valid list"
1329+ )
1330+ self .assertNotEqual (
1331+ list_snapshot_response ,
1332+ None ,
1333+ "Check if snapshot exists in ListSnapshot"
1334+ )
1335+ VmSnapshot .deleteVMSnapshot (
1336+ self .apiClient ,
1337+ list_snapshot_response [0 ].id )
1338+
1339+ time .sleep (5 )
1340+
1341+ list_snapshot_response = VmSnapshot .list (
1342+ self .apiClient ,
1343+ virtualmachineid = self .virtual_machine .id ,
1344+ listall = False )
1345+ self .debug ('list_snapshot_response -------------------- {}' .format (list_snapshot_response ))
1346+
1347+ self .assertIsNone (list_snapshot_response , "snapshot is already deleted" )
1348+
11541349 def _create_vm_using_template_and_destroy_vm (self , template ):
11551350 vm_name = "VM-%d" % random .randint (0 , 100 )
11561351
@@ -1177,42 +1372,31 @@ def _create_vm_using_template_and_destroy_vm(self, template):
11771372
11781373 virtual_machine .delete (self .apiClient , True )
11791374
1180- @staticmethod
1181- def _get_bytes_from_gb (number_in_gb ):
1182- return number_in_gb * 1024 * 1024 * 1024
1183-
11841375 def _get_volume (self , volume_id ):
11851376 list_vols_response = list_volumes (self .apiClient , id = volume_id )
11861377 return list_vols_response [0 ]
11871378
1188- def _get_vm (self , vm_id ):
1189- list_vms_response = list_virtual_machines (self .apiClient , id = vm_id )
1379+ @classmethod
1380+ def _get_vm (cls , vm_id ):
1381+ list_vms_response = list_virtual_machines (cls .apiClient , id = vm_id )
11901382 return list_vms_response [0 ]
11911383
1192- def _get_template_cache_name (self ):
1193- if TestData .hypervisor_type == TestData .kvm :
1194- return TestData .templateCacheNameKvm
1195-
1196- self .assert_ (False , "Invalid hypervisor type" )
1197-
11981384 @classmethod
11991385 def _start_vm (cls , vm ):
1200- vm_for_check = list_virtual_machines (
1201- cls .apiClient ,
1202- id = vm .id
1203- )[0 ]
1386+ vm_for_check = cls ._get_vm (vm .id )
12041387
12051388 if vm_for_check .state == VirtualMachine .STOPPED :
12061389 vm .start (cls .apiClient )
12071390
1208- # For KVM, just give it 90 seconds to boot up.
1209- if TestData . hypervisor_type == TestData . kvm :
1210- time . sleep ( 90 )
1391+ vm_for_check = cls . _get_vm ( vm . id )
1392+ ServiceReady . wait_ssh_ready ( vm_for_check . ipaddress )
1393+ return vm_for_check
12111394
12121395 @classmethod
12131396 def _reboot_vm (cls , vm ):
1397+ vm_for_check = cls ._get_vm (vm .id )
12141398 vm .reboot (cls .apiClient )
12151399
1216- # For KVM, just give it 90 seconds to boot up.
1217- if TestData . hypervisor_type == TestData . kvm :
1218- time . sleep ( 90 )
1400+ time . sleep ( 5 )
1401+
1402+ ServiceReady . wait_ssh_ready ( vm_for_check . ipaddress )
0 commit comments