diff options
-rw-r--r-- | cns-libs/cnslibs/common/cns_libs.py | 4 | ||||
-rw-r--r-- | cns-libs/cnslibs/common/openshift_ops.py | 45 | ||||
-rw-r--r-- | tests/functional/common/provisioning/test_storage_class_cases.py | 153 |
3 files changed, 194 insertions, 8 deletions
diff --git a/cns-libs/cnslibs/common/cns_libs.py b/cns-libs/cnslibs/common/cns_libs.py index 5b9a3027..370e05e3 100644 --- a/cns-libs/cnslibs/common/cns_libs.py +++ b/cns-libs/cnslibs/common/cns_libs.py @@ -414,7 +414,7 @@ def validate_multipath_pod(hostname, podname, hacount): active_node_count = 1 enable_node_count = hacount - 1 cmd = "multipath -ll | grep 'status=active' | wc -l" - ret, out, err = g.run(hostname, cmd, "root") + ret, out, err = g.run(pod_nodename, cmd, "root") if ret != 0 or out == "": g.log.error("failed to exectute cmd %s on %s, err %s" % (cmd, pod_nodename, out)) @@ -425,7 +425,7 @@ def validate_multipath_pod(hostname, podname, hacount): % (pod_nodename, podname, active_count)) return False cmd = "multipath -ll | grep 'status=enabled' | wc -l" - ret, out, err = g.run(hostname, cmd, "root") + ret, out, err = g.run(pod_nodename, cmd, "root") if ret != 0 or out == "": g.log.error("failed to exectute cmd %s on %s, err %s" % (cmd, pod_nodename, out)) diff --git a/cns-libs/cnslibs/common/openshift_ops.py b/cns-libs/cnslibs/common/openshift_ops.py index e86010cb..212e4b30 100644 --- a/cns-libs/cnslibs/common/openshift_ops.py +++ b/cns-libs/cnslibs/common/openshift_ops.py @@ -22,7 +22,10 @@ from cnslibs.common import exceptions from cnslibs.common import podcmd from cnslibs.common import utils from cnslibs.common import waiter - +from cnslibs.common.heketi_ops import ( + heketi_blockvolume_info, + heketi_volume_info, +) PODS_WIDE_RE = re.compile( '(\S+)\s+(\S+)\s+(\w+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+).*\n') @@ -744,6 +747,46 @@ def get_gluster_vol_info_by_pvc_name(ocp_node, pvc_name): return vol_info +def get_gluster_blockvol_info_by_pvc_name(ocp_node, heketi_server_url, + pvc_name): + """Get Gluster block volume info based on the PVC name. + + Args: + ocp_node (str): Node to execute OCP commands on. + heketi_server_url (str): Heketi server url + pvc_name (str): Name of a PVC to get bound Gluster block volume info. + Returns: + dict: Dictionary containting data about a Gluster block volume. + """ + + # Get block volume Name and ID from PV which is bound to our PVC + get_block_vol_data_cmd = ( + 'oc get pv --no-headers -o custom-columns=' + ':.metadata.annotations.glusterBlockShare,' + ':.metadata.annotations."gluster\.org\/volume\-id",' + ':.spec.claimRef.name | grep "%s"' % pvc_name) + out = command.cmd_run(get_block_vol_data_cmd, hostname=ocp_node) + parsed_out = filter(None, map(str.strip, out.split(" "))) + assert len(parsed_out) == 3, "Expected 3 fields in following: %s" % out + block_vol_name, block_vol_id = parsed_out[:2] + + # Get block hosting volume ID + block_hosting_vol_id = heketi_blockvolume_info( + ocp_node, heketi_server_url, block_vol_id, json=True + )["blockhostingvolume"] + + # Get block hosting volume name by it's ID + block_hosting_vol_name = heketi_volume_info( + ocp_node, heketi_server_url, block_hosting_vol_id, json=True)['name'] + + # Get Gluster block volume info + vol_info_cmd = "oc exec %s -- gluster-block info %s/%s --json" % ( + get_ocp_gluster_pod_names(ocp_node)[0], + block_hosting_vol_name, block_vol_name) + + return json.loads(command.cmd_run(vol_info_cmd, hostname=ocp_node)) + + def wait_for_pod_be_ready(hostname, pod_name, timeout=1200, wait_step=60): ''' diff --git a/tests/functional/common/provisioning/test_storage_class_cases.py b/tests/functional/common/provisioning/test_storage_class_cases.py index 8852677c..7e318eb0 100644 --- a/tests/functional/common/provisioning/test_storage_class_cases.py +++ b/tests/functional/common/provisioning/test_storage_class_cases.py @@ -1,21 +1,38 @@ +from unittest import skip + import ddt from glusto.core import Glusto as g from cnslibs.cns import cns_baseclass +from cnslibs.common.cns_libs import validate_multipath_pod from cnslibs.common.openshift_ops import ( + get_gluster_blockvol_info_by_pvc_name, + get_ocp_gluster_pod_names, + get_pod_name_from_dc, + oc_create_app_dc_with_io, oc_create_pvc, oc_create_sc, oc_create_secret, oc_delete, + scale_dc_pod_amount_and_wait, wait_for_events, - wait_for_resource_absence, + wait_for_pod_be_ready, + wait_for_resource_absence ) @ddt.ddt class TestStorageClassCases(cns_baseclass.CnsBaseClass): - def sc_incorrect_parameter(self, vol_type, parameter={}): + def create_sc_with_parameter(self, vol_type, success=False, parameter={}): + """creates storage class, pvc and validates event + + Args: + vol_type (str): storage type either gluster file or block + success (bool): if True check for successfull else failure + for pvc creation event + parameter (dict): dictionary with storage class parameters + """ if vol_type == "glusterfile": sc = self.cns_storage_class['storage_class1'] secret = self.cns_secret['secret1'] @@ -67,10 +84,55 @@ class TestStorageClassCases(cns_baseclass.CnsBaseClass): 'pvc', self.pvc_name) # Wait for event with error + event_reason = 'ProvisioningFailed' + if success: + event_reason = 'ProvisioningSucceeded' wait_for_events(self.ocp_master_node[0], obj_name=self.pvc_name, obj_type='PersistentVolumeClaim', - event_reason='ProvisioningFailed') + event_reason=event_reason) + + def validate_gluster_block_volume_info(self, assertion_method, key, value): + """Validates block volume info paramters value + + Args: + assertion_method (func): assert method to be asserted + key (str): block volume parameter to be asserted with value + value (str): block volume parameter value to be asserted + """ + # get block hosting volume of pvc created above + gluster_blockvol_info = get_gluster_blockvol_info_by_pvc_name( + self.ocp_master_node[0], self.heketi_server_url, self.pvc_name + ) + + # asserts value and keys + assertion_method(gluster_blockvol_info[key], value) + + def validate_multipath_info(self, hacount): + """validates multipath command on the pod node + + Args: + hacount (int): hacount for which multipath to be checked + """ + # create pod using pvc created + dc_name = oc_create_app_dc_with_io( + self.ocp_master_node[0], self.pvc_name + ) + pod_name = get_pod_name_from_dc(self.ocp_master_node[0], dc_name) + self.addCleanup(oc_delete, self.ocp_master_node[0], "dc", dc_name) + self.addCleanup( + scale_dc_pod_amount_and_wait, self.ocp_master_node[0], dc_name, 0 + ) + + wait_for_pod_be_ready( + self.ocp_master_node[0], pod_name, timeout=120, wait_step=3 + ) + + # validates multipath for pod created with hacount + self.assertTrue( + validate_multipath_pod(self.ocp_master_node[0], pod_name, hacount), + "multipath validation failed" + ) @ddt.data( {"volumetype": "dist-rep:3"}, @@ -82,7 +144,7 @@ class TestStorageClassCases(cns_baseclass.CnsBaseClass): ) def test_sc_glusterfile_incorrect_parameter(self, parameter={}): """Polarion testcase id- CNS-708,709,713,714,715,921""" - self.sc_incorrect_parameter("glusterfile", parameter) + self.create_sc_with_parameter("glusterfile", parameter=parameter) @ddt.data( {"resturl": "http://10.0.0.1:8080"}, @@ -92,4 +154,85 @@ class TestStorageClassCases(cns_baseclass.CnsBaseClass): ) def test_sc_glusterblock_incorrect_parameter(self, parameter={}): """ Polarion testcase id- CNS-727,725,728""" - self.sc_incorrect_parameter("glusterblock", parameter) + self.create_sc_with_parameter("glusterblock", parameter=parameter) + + @skip("Blocked by BZ-1609703") + @ddt.data(1, 2) + def test_gluster_block_provisioning_with_valid_ha_count(self, hacount): + '''[CNS-544][CNS-1453] gluster-block provisioning with different valid + 'hacount' values + ''' + # create storage class and pvc with given parameters + self.create_sc_with_parameter( + 'glusterblock', success=True, parameter={'hacount': str(hacount)} + ) + + # validate HA parameter with gluster block volume + self.validate_gluster_block_volume_info( + self.assertEqual, 'HA', hacount + ) + + # TODO: need more info on hacount=1 for multipath validation hence + # skipping multipath validation + if hacount > 1: + self.validate_multipath_info(hacount) + + def test_gluster_block_provisioning_with_ha_count_as_glusterpod(self): + '''[CNS-1443] gluster-block provisioning with "hacount" value equal to + gluster pods count + ''' + # get hacount as no of gluster pods the pvc creation + hacount = len(get_ocp_gluster_pod_names(self.ocp_master_node[0])) + + # create storage class and pvc with given parameters + self.create_sc_with_parameter( + 'glusterblock', success=True, parameter={'hacount': str(hacount)} + ) + + # validate HA parameter with gluster block volume + self.validate_gluster_block_volume_info( + self.assertEqual, 'HA', hacount + ) + self.validate_multipath_info(hacount) + + @skip("Blocked by BZ-1644685") + def test_gluster_block_provisioning_with_invalid_ha_count(self): + '''[CNS-1444] gluster-block provisioning with any invalid 'hacount' + value + ''' + # get hacount as no of gluster pods + 1 to fail the pvc creation + hacount = len(get_ocp_gluster_pod_names(self.ocp_master_node[0])) + 1 + + # create storage class and pvc with given parameters + self.create_sc_with_parameter( + 'glusterblock', parameter={'hacount': str(hacount)} + ) + + @ddt.data('true', 'false', '') + def test_gluster_block_chapauthenabled_parameter(self, chapauthenabled): + '''[CNS-545][CNS-1445][CNS-1446] gluster-block provisioning with + different 'chapauthenabled' values + ''' + parameter = {} + if chapauthenabled: + parameter = {"chapauthenabled": chapauthenabled} + + # create storage class and pvc with given parameters + self.create_sc_with_parameter( + "glusterblock", success=True, parameter=parameter + ) + + if chapauthenabled == 'true' or chapauthenabled == '': + # validate if password is set in gluster block volume info + self.validate_gluster_block_volume_info( + self.assertNotEqual, 'PASSWORD', '' + ) + elif chapauthenabled == 'false': + # validate if password is not set in gluster block volume info + self.validate_gluster_block_volume_info( + self.assertEqual, 'PASSWORD', '' + ) + else: + raise AssertionError( + "Invalid chapauthenabled value '%s'" % chapauthenabled + ) |