Skip to content

Commit e860249

Browse files
committed
Merge pull request #1847 from anshul1886/CLOUDSTACK-9691
CLOUDSTACK-9691: Fixed unhandeled excetion in list snapshot command when a primary store is deleted related to it@mike-tutkowski After support for snapshots on solidifire there are many places which are prone to these NullPointer exceptions resulting in various issues. Root cause for these issues is that we get the primary storage associated with snapshot and then figure out how to handle but if that store is deleted then it results in NullPointer exceptions. Without solidfire we don't need to access primary storage. Should we handle that as issues are found or could there be other way to fix all of these issues? * pr/1847: CLOUDSTACK-9691: Added test list_snapshots_with_removed_data_store CLOUDSTACK-9691: Fixed unhandeled excetion in list snapshot command when a primary store is deleted related to it Signed-off-by: Rajani Karuturi <rajani.karuturi@accelerite.com>
2 parents 6dced70 + 3caedb9 commit e860249

File tree

2 files changed

+139
-3
lines changed

2 files changed

+139
-3
lines changed

server/src/com/cloud/api/ApiResponseHelper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@ public static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStor
527527

528528
long storagePoolId = snapshotStore.getDataStoreId();
529529
DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
530+
if (dataStore == null) {
531+
return DataStoreRole.Image;
532+
}
530533

531534
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
532535

test/integration/smoke/test_snapshots.py

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@
2424
Account,
2525
Template,
2626
ServiceOffering,
27-
Snapshot)
27+
Snapshot,
28+
StoragePool,
29+
Volume)
2830
from marvin.lib.common import (get_domain,
2931
get_template,
3032
get_zone,
33+
get_pod,
3134
list_volumes,
32-
list_snapshots)
35+
list_snapshots,
36+
list_storage_pools,
37+
list_clusters)
3338
from marvin.lib.decoratorGenerators import skipTestIf
3439

3540

@@ -95,6 +100,7 @@ def setUpClass(cls):
95100
# Get Zone, Domain and templates
96101
cls.domain = get_domain(cls.apiclient)
97102
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
103+
cls.pod = get_pod(cls.apiclient, cls.zone.id)
98104
cls.services['mode'] = cls.zone.networktype
99105

100106
cls.hypervisorNotSupported = False
@@ -140,7 +146,6 @@ def setUpClass(cls):
140146
mode=cls.services["mode"]
141147
)
142148

143-
cls._cleanup.append(cls.virtual_machine)
144149
cls._cleanup.append(cls.service_offering)
145150
cls._cleanup.append(cls.account)
146151
cls._cleanup.append(cls.template)
@@ -255,3 +260,131 @@ def test_01_snapshot_root_disk(self):
255260
self.assertTrue(is_snapshot_on_nfs(
256261
self.apiclient, self.dbclient, self.config, self.zone.id, snapshot.id))
257262
return
263+
264+
@skipTestIf("hypervisorNotSupported")
265+
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true")
266+
def test_02_list_snapshots_with_removed_data_store(self):
267+
"""Test listing volume snapshots with removed data stores
268+
"""
269+
270+
# 1) Create new Primary Storage
271+
clusters = list_clusters(
272+
self.apiclient,
273+
zoneid=self.zone.id
274+
)
275+
assert isinstance(clusters,list) and len(clusters)>0
276+
277+
storage = StoragePool.create(self.apiclient,
278+
self.services["nfs"],
279+
clusterid=clusters[0].id,
280+
zoneid=self.zone.id,
281+
podid=self.pod.id
282+
)
283+
self.assertEqual(
284+
storage.state,
285+
'Up',
286+
"Check primary storage state"
287+
)
288+
self.assertEqual(
289+
storage.type,
290+
'NetworkFilesystem',
291+
"Check storage pool type"
292+
)
293+
storage_pools_response = list_storage_pools(self.apiclient,
294+
id=storage.id)
295+
self.assertEqual(
296+
isinstance(storage_pools_response, list),
297+
True,
298+
"Check list response returns a valid list"
299+
)
300+
self.assertNotEqual(
301+
len(storage_pools_response),
302+
0,
303+
"Check list Hosts response"
304+
)
305+
storage_response = storage_pools_response[0]
306+
self.assertEqual(
307+
storage_response.id,
308+
storage.id,
309+
"Check storage pool ID"
310+
)
311+
self.assertEqual(
312+
storage.type,
313+
storage_response.type,
314+
"Check storage pool type "
315+
)
316+
317+
# 2) Migrate VM ROOT volume to new Primary Storage
318+
volumes = list_volumes(
319+
self.apiclient,
320+
virtualmachineid=self.virtual_machine_with_disk.id,
321+
type='ROOT',
322+
listall=True
323+
)
324+
Volume.migrate(self.apiclient,
325+
storageid=storage.id,
326+
volumeid=volumes[0].id,
327+
livemigrate="true"
328+
)
329+
330+
volume_response = list_volumes(
331+
self.apiclient,
332+
id=volumes[0].id,
333+
)
334+
self.assertNotEqual(
335+
len(volume_response),
336+
0,
337+
"Check list Volumes response"
338+
)
339+
volume_migrated = volume_response[0]
340+
self.assertEqual(
341+
volume_migrated.storageid,
342+
storage.id,
343+
"Check volume storage id"
344+
)
345+
self.cleanup.append(self.virtual_machine_with_disk)
346+
self.cleanup.append(storage)
347+
348+
# 3) Take snapshot of VM ROOT volume
349+
snapshot = Snapshot.create(
350+
self.apiclient,
351+
volume_migrated.id,
352+
account=self.account.name,
353+
domainid=self.account.domainid
354+
)
355+
self.debug("Snapshot created: ID - %s" % snapshot.id)
356+
357+
# 4) Delete VM and created Primery Storage
358+
cleanup_resources(self.apiclient, self.cleanup)
359+
360+
# 5) List snapshot and verify it gets properly listed although Primary Storage was removed
361+
snapshot_response = Snapshot.list(
362+
self.apiclient,
363+
id=snapshot.id
364+
)
365+
self.assertNotEqual(
366+
len(snapshot_response),
367+
0,
368+
"Check list Snapshot response"
369+
)
370+
self.assertEqual(
371+
snapshot_response[0].id,
372+
snapshot.id,
373+
"Check snapshot id"
374+
)
375+
376+
# 6) Delete snapshot and verify it gets properly deleted (should not be listed)
377+
self.cleanup = [snapshot]
378+
cleanup_resources(self.apiclient, self.cleanup)
379+
380+
snapshot_response_2 = Snapshot.list(
381+
self.apiclient,
382+
id=snapshot.id
383+
)
384+
self.assertEqual(
385+
snapshot_response_2,
386+
None,
387+
"Check list Snapshot response"
388+
)
389+
390+
return

0 commit comments

Comments
 (0)