Skip to content

Commit 49d972d

Browse files
committed
linstor: improve integration-tests
make tests easier to run and without modifying the test to match the correct system. Also add tests for volume and vm instance snapshots. Improve runtime by ~6 minutes.
1 parent e196275 commit 49d972d

File tree

1 file changed

+218
-34
lines changed

1 file changed

+218
-34
lines changed

test/integration/plugins/linstor/test_linstor_volumes.py

Lines changed: 218 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
import logging
1919
import random
2020
import time
21+
import socket
2122

2223
# All tests inherit from cloudstackTestCase
2324
from 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
3132
from 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

229267
class 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

Comments
 (0)