Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions test/integration/plugins/ontap/TEST_CASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Each suite is sequential — tests must run in numbered order; each step builds

| # | Test method | Goal | Depends on | CloudStack success criteria | ONTAP success criteria | Type |
|---|-------------|------|------------|-----------------------------|------------------------|------|
| 01 | `test_01_create_primary_storage_pool` | Create a cluster-scoped iSCSI primary storage pool | setUpClass | `pool.state == "Up"`, `pool.type == "Iscsi"` | FlexVol `online`; one igroup per cluster host (named `cs_{svmName}_{hostShortName}`) with host IQN as initiator | positive |
| 01 | `test_01_create_primary_storage_pool` | Create a cluster-scoped iSCSI primary storage pool | setUpClass | `pool.state == "Up"`, `pool.type == "OntapiSCSI"` | FlexVol `online`; one igroup per cluster host (named `cs_{svmName}_{hostShortName}`) with host IQN as initiator | positive |
| 02 | `test_02_disable_storage_pool` | Disable the pool | test_01 (`pool`) | `pool.state == "Disabled"` | FlexVol still `online` | positive |
| 03 | `test_03_enable_storage_pool` | Re-enable the pool | test_02 | `pool.state == "Up"` | FlexVol still `online` | positive |
| 04 | `test_04_enter_maintenance_mode` | Put pool into maintenance | test_03 | `pool.state == "Maintenance"` | FlexVol still `online`; igroups unchanged | positive |
Expand Down Expand Up @@ -210,7 +210,7 @@ Each suite is sequential — tests must run in numbered order; each step builds

| # | Test method | Goal | Depends on | CloudStack success criteria | ONTAP success criteria | Type |
|---|-------------|------|------------|-----------------------------|------------------------|------|
| 01 | `test_01_create_iscsi_pool` | Create iSCSI ONTAP primary storage pool | setUpClass | `pool.state == "Up"`, `pool.type == "Iscsi"` | FlexVol `online`; igroup per cluster host with host IQN | positive |
| 01 | `test_01_create_iscsi_pool` | Create iSCSI ONTAP primary storage pool | setUpClass | `pool.state == "Up"`, `pool.type == "OntapiSCSI"` | FlexVol `online`; igroup per cluster host with host IQN | positive |
| 02 | `test_02_create_ontap_data_volume` | Allocate a CloudStack data volume (creates a LUN in the FlexVol) | test_01 (`pool`) | Volume non-None | ≥1 LUN in FlexVol | positive |
| 03 | `test_03_deploy_vm` | Deploy VM using first ready KVM template; verify 0 LUN-maps exist before attach | test_02 (`volume`) | `vm.state == "Running"`; 0 LUN-maps on ONTAP | 0 LUN-maps (`list_lun_maps_for_volume` returns empty) | positive |
| 04 | `test_04_attach_volume_to_vm` | Hot-attach the ONTAP iSCSI volume to the running VM — a LUN-map is created (TDS SN 27) | test_03 (`vm`, `volume`) | `volume.virtualmachineid == vm.id` | ≥1 LUN-map linking the LUN to the host's igroup | positive |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,12 +404,42 @@ def _poll_volume_field(self, vol_id, field, target, timeout=120,
time.sleep(interval)
return None

def _lun_maps(self):
"""Return current LUN-maps for the pool's FlexVol."""
if self.__class__.pool is None:
def _data_lun_name(self, volume=None):
"""
Full ONTAP path of the LUN backing the CloudStack data volume.

UnifiedSANStrategy names the LUN after the CloudStack volume with
hyphens replaced by underscores, inside the pool's FlexVol.
"""
volume = volume or self.__class__.volume
if volume is None or self.__class__.pool is None:
return None
return "/vol/%s/%s" % (self.__class__.pool.name,
volume.name.replace("-", "_"))

def _lun_maps(self, volume=None):
"""
Return LUN-maps belonging to the data volume's LUN only.

The same FlexVol also holds the VM's ROOT LUN whenever the allocator
places the root disk on this pool, so the unfiltered map list is not a
measure of the data volume's accessibility.
"""
lun_name = self._data_lun_name(volume)
if lun_name is None:
return []
return [m for m in self.ontap.list_lun_maps_for_volume(
self.svm_name, self.__class__.pool.name)
if m.get("lun", {}).get("name") == lun_name]

def _data_luns(self, volume=None):
"""Return LUN records for the data volume's LUN only."""
lun_name = self._data_lun_name(volume)
if lun_name is None:
return []
return self.ontap.list_lun_maps_for_volume(
self.svm_name, self.__class__.pool.name)
return [lun for lun in self.ontap.list_luns_in_volume(
self.svm_name, self.__class__.pool.name)
if lun.get("name") == lun_name]

# ==================================================================
# Test steps
Expand All @@ -424,7 +454,7 @@ def test_01_create_iscsi_pool(self):
"""
Create an iSCSI primary storage pool on ONTAP.
Verifies:
- Pool reaches 'Up' state; type is 'Iscsi'
- Pool reaches 'Up' state; type is 'OntapiSCSI'
- ONTAP: FlexVol is online
- ONTAP: igroup exists for every host in the cluster that has an IQN
"""
Expand All @@ -433,8 +463,8 @@ def test_01_create_iscsi_pool(self):

self.assertEqual(pool.state, "Up",
"Pool state should be 'Up', got '%s'" % pool.state)
self.assertEqual(pool.type, "Iscsi",
"Pool type should be 'Iscsi', got '%s'" % pool.type)
self.assertEqual(pool.type, "OntapiSCSI",
"Pool type should be 'OntapiSCSI', got '%s'" % pool.type)

ontap_vol = self.ontap.get_volume(pool.name)
self.assertIsNotNone(
Expand Down Expand Up @@ -462,12 +492,12 @@ def test_02_create_ontap_data_volume(self):
self.__class__.volume = vol
self.assertIsNotNone(vol, "createVolume returned None")

luns = self.ontap.list_luns_in_volume(
self.svm_name, self.__class__.pool.name)
luns = self._data_luns()
self.assertTrue(
len(luns) > 0,
"Expected ≥1 LUN in ONTAP FlexVol '%s' after volume creation, "
"found 0" % self.__class__.pool.name
"Expected the data volume's LUN '%s' in ONTAP FlexVol '%s' after "
"volume creation, found none"
% (self._data_lun_name(), self.__class__.pool.name)
)

# ------------------------------------------------------------------
Expand Down Expand Up @@ -511,7 +541,7 @@ def test_03_deploy_vm(self):
lun_maps = self._lun_maps()
self.assertEqual(
len(lun_maps), 0,
"Expected 0 LUN-maps before volume attach, found %d: %s"
"Expected 0 data-volume LUN-maps before volume attach, found %d: %s"
% (len(lun_maps), lun_maps)
)

Expand Down Expand Up @@ -590,16 +620,15 @@ def test_05_stop_vm_lun_unmapped(self):
lun_maps = self._lun_maps()
self.assertEqual(
len(lun_maps), 0,
"Expected 0 LUN-maps after VM stop, found %d: %s"
"Expected 0 data-volume LUN-maps after VM stop, found %d: %s"
% (len(lun_maps), lun_maps)
)

# ONTAP: LUN itself must still exist in the FlexVol
luns = self.ontap.list_luns_in_volume(
self.svm_name, self.__class__.pool.name)
luns = self._data_luns()
self.assertTrue(
len(luns) > 0,
"LUN should still exist in ONTAP FlexVol after VM stop"
"Data volume LUN should still exist in ONTAP FlexVol after VM stop"
)

# ------------------------------------------------------------------
Expand Down Expand Up @@ -689,16 +718,15 @@ def test_07_detach_volume_from_vm(self):
lun_maps = self._lun_maps()
self.assertEqual(
len(lun_maps), 0,
"Expected 0 LUN-maps after volume detach, found %d: %s"
"Expected 0 data-volume LUN-maps after volume detach, found %d: %s"
% (len(lun_maps), lun_maps)
)

# ONTAP: LUN still exists in FlexVol
luns = self.ontap.list_luns_in_volume(
self.svm_name, self.__class__.pool.name)
luns = self._data_luns()
self.assertTrue(
len(luns) > 0,
"LUN should still exist in ONTAP FlexVol after detach"
"Data volume LUN should still exist in ONTAP FlexVol after detach"
)

# ------------------------------------------------------------------
Expand Down Expand Up @@ -751,11 +779,11 @@ def test_08_destroy_vm_and_cleanup(self):
self.__class__.volume = None

# ONTAP: LUN must be removed after volume deletion
luns = self.ontap.list_luns_in_volume(self.svm_name, pool_name)
luns = self._data_luns(vol)
self.assertEqual(
len(luns), 0,
"Expected 0 LUNs in FlexVol '%s' after volume delete, "
"found %d" % (pool_name, len(luns))
"Expected the data volume's LUN to be gone from FlexVol '%s' "
"after volume delete, found %d" % (pool_name, len(luns))
)

# Enter maintenance and force-delete the pool
Expand Down
12 changes: 6 additions & 6 deletions test/integration/plugins/ontap/iscsi/pool/test_pool_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def _assert_pool_capacity(self, pool, label):
def test_01_create_primary_storage_pool(self):
"""
Create an iSCSI primary storage pool and verify:
- CloudStack state is Up, type is Iscsi
- CloudStack state is Up, type is OntapiSCSI
- ONTAP: FlexVol exists and is online
- ONTAP: one igroup per cluster host exists with the correct IQN initiator
"""
Expand All @@ -288,8 +288,8 @@ def test_01_create_primary_storage_pool(self):
"Pool state should be 'Up', got '%s'" % pool.state
)
self.assertEqual(
pool.type, "Iscsi",
"Pool type should be 'Iscsi', got '%s'" % pool.type
pool.type, "OntapiSCSI",
"Pool type should be 'OntapiSCSI', got '%s'" % pool.type
)

# ONTAP: FlexVol must be online
Expand Down Expand Up @@ -502,7 +502,7 @@ def test_07_create_volume_on_pool(self):
Create a new iSCSI pool and allocate a CloudStack data volume.
For iSCSI, createAsync creates a LUN inside the pool's ONTAP FlexVol.
Verifies:
- pool.state is Up, type is Iscsi
- pool.state is Up, type is OntapiSCSI
- createVolume returns a non-None volume object
- ONTAP: FlexVol is still online
- ONTAP: at least one LUN is present in the FlexVol
Expand All @@ -520,8 +520,8 @@ def test_07_create_volume_on_pool(self):
"Pool state should be 'Up', got '%s'" % pool.state
)
self.assertEqual(
pool.type, "Iscsi",
"Pool type should be 'Iscsi', got '%s'" % pool.type
pool.type, "OntapiSCSI",
"Pool type should be 'OntapiSCSI', got '%s'" % pool.type
)

vol = self._create_volume(pool.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def test_01_create_pool_and_volume(self):
Create an iSCSI primary storage pool and allocate a CloudStack data
volume on it.
Verifies:
- Pool state is Up; pool type is Iscsi
- Pool state is Up; pool type is OntapiSCSI
- ONTAP: FlexVol is online
- ONTAP: at least one igroup exists (one per cluster host with IQN)
- ONTAP: after createVolume, a LUN exists in the FlexVol
Expand All @@ -324,8 +324,8 @@ def test_01_create_pool_and_volume(self):
"Pool state should be 'Up', got '%s'" % pool.state
)
self.assertEqual(
pool.type, "Iscsi",
"Pool type should be 'Iscsi', got '%s'" % pool.type
pool.type, "OntapiSCSI",
"Pool type should be 'OntapiSCSI', got '%s'" % pool.type
)

# ONTAP: FlexVol must be online
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def test_01_create_zone_scoped_pool(self):
CloudStack calls attachZone(), which connects all eligible KVM hosts
in the zone and creates igroups for each host's IQN.
Verifies:
- pool.state is Up, type is Iscsi
- pool.state is Up, type is OntapiSCSI
- ONTAP: FlexVol is online
- ONTAP: igroup exists for each cluster host with the correct IQN
"""
Expand All @@ -261,8 +261,8 @@ def test_01_create_zone_scoped_pool(self):
"Pool state should be 'Up', got '%s'" % pool.state
)
self.assertEqual(
pool.type, "Iscsi",
"Pool type should be 'Iscsi', got '%s'" % pool.type
pool.type, "OntapiSCSI",
"Pool type should be 'OntapiSCSI', got '%s'" % pool.type
)

# ONTAP: FlexVol must be online
Expand Down
Loading