From df94fee1f59247cc4460a38b614d58974804fc88 Mon Sep 17 00:00:00 2001 From: "Srivastava, Piyush" Date: Fri, 28 Aug 2026 13:02:07 +0530 Subject: [PATCH] feature/CSTACKEX-251: automation changes for ontapIscsi pool type introduction --- test/integration/plugins/ontap/TEST_CASES.md | 4 +- .../iscsi/instance/test_vm_volume_attach.py | 76 +++++++++++++------ .../ontap/iscsi/pool/test_pool_lifecycle.py | 12 +-- .../iscsi/pool/test_pool_with_volumes.py | 6 +- .../ontap/iscsi/pool/test_zone_scoped_pool.py | 6 +- 5 files changed, 66 insertions(+), 38 deletions(-) diff --git a/test/integration/plugins/ontap/TEST_CASES.md b/test/integration/plugins/ontap/TEST_CASES.md index 5ef8e8a1eb6d..73dc1990a5b6 100644 --- a/test/integration/plugins/ontap/TEST_CASES.md +++ b/test/integration/plugins/ontap/TEST_CASES.md @@ -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 | @@ -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 | diff --git a/test/integration/plugins/ontap/iscsi/instance/test_vm_volume_attach.py b/test/integration/plugins/ontap/iscsi/instance/test_vm_volume_attach.py index 716659b88c91..4c843bfbd90f 100644 --- a/test/integration/plugins/ontap/iscsi/instance/test_vm_volume_attach.py +++ b/test/integration/plugins/ontap/iscsi/instance/test_vm_volume_attach.py @@ -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 @@ -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 """ @@ -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( @@ -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) ) # ------------------------------------------------------------------ @@ -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) ) @@ -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" ) # ------------------------------------------------------------------ @@ -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" ) # ------------------------------------------------------------------ @@ -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 diff --git a/test/integration/plugins/ontap/iscsi/pool/test_pool_lifecycle.py b/test/integration/plugins/ontap/iscsi/pool/test_pool_lifecycle.py index 01abd239b0b7..cc87bacf0e76 100644 --- a/test/integration/plugins/ontap/iscsi/pool/test_pool_lifecycle.py +++ b/test/integration/plugins/ontap/iscsi/pool/test_pool_lifecycle.py @@ -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 """ @@ -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 @@ -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 @@ -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) diff --git a/test/integration/plugins/ontap/iscsi/pool/test_pool_with_volumes.py b/test/integration/plugins/ontap/iscsi/pool/test_pool_with_volumes.py index 03e332740f58..9dd49761c1dc 100644 --- a/test/integration/plugins/ontap/iscsi/pool/test_pool_with_volumes.py +++ b/test/integration/plugins/ontap/iscsi/pool/test_pool_with_volumes.py @@ -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 @@ -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 diff --git a/test/integration/plugins/ontap/iscsi/pool/test_zone_scoped_pool.py b/test/integration/plugins/ontap/iscsi/pool/test_zone_scoped_pool.py index c7ee726ef460..847a026a3bd5 100644 --- a/test/integration/plugins/ontap/iscsi/pool/test_zone_scoped_pool.py +++ b/test/integration/plugins/ontap/iscsi/pool/test_zone_scoped_pool.py @@ -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 """ @@ -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