summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openshift-storage-libs/openshiftstoragelibs/baseclass.py10
-rw-r--r--tests/functional/heketi/test_heketi_zones.py201
-rw-r--r--tests/glusterfs-containers-tests-config.yaml1
3 files changed, 211 insertions, 1 deletions
diff --git a/openshift-storage-libs/openshiftstoragelibs/baseclass.py b/openshift-storage-libs/openshiftstoragelibs/baseclass.py
index ae433872..3bd0f29e 100644
--- a/openshift-storage-libs/openshiftstoragelibs/baseclass.py
+++ b/openshift-storage-libs/openshiftstoragelibs/baseclass.py
@@ -166,7 +166,8 @@ class BaseClass(unittest.TestCase):
allow_volume_expansion=False,
reclaim_policy="Delete",
set_hacount=None,
- is_arbiter_vol=False, arbiter_avg_file_size=None):
+ is_arbiter_vol=False, arbiter_avg_file_size=None,
+ heketi_zone_checking=None):
# Create secret if one is not specified
if not secret_name:
@@ -194,6 +195,13 @@ class BaseClass(unittest.TestCase):
parameters["volumeoptions"] += (
",user.heketi.average-file-size %s" % (
arbiter_avg_file_size))
+ if heketi_zone_checking:
+ if parameters.get("volumeoptions"):
+ parameters["volumeoptions"] += (
+ ",user.heketi.zone-checking %s" % heketi_zone_checking)
+ else:
+ parameters["volumeoptions"] = (
+ "user.heketi.zone-checking %s" % heketi_zone_checking)
if vol_name_prefix:
parameters["volumenameprefix"] = vol_name_prefix
elif create_vol_name_prefix:
diff --git a/tests/functional/heketi/test_heketi_zones.py b/tests/functional/heketi/test_heketi_zones.py
new file mode 100644
index 00000000..d948c0e1
--- /dev/null
+++ b/tests/functional/heketi/test_heketi_zones.py
@@ -0,0 +1,201 @@
+import itertools
+try:
+ # py2/3
+ import simplejson as json
+except ImportError:
+ # py2
+ import json
+
+import ddt
+from glusto.core import Glusto as g
+
+from openshiftstoragelibs import baseclass
+from openshiftstoragelibs import heketi_ops
+from openshiftstoragelibs import openshift_ops
+
+
+@ddt.ddt
+class TestHeketiZones(baseclass.BaseClass):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestHeketiZones, cls).setUpClass()
+ clusters = heketi_ops.heketi_cluster_list(
+ cls.heketi_client_node, cls.heketi_server_url, json=True)
+ cls.cluster_id = clusters['clusters'][0]
+ cls.allow_heketi_zones_update = g.config.get("common", {}).get(
+ "allow_heketi_zones_update", False)
+
+ def setUp(self):
+ super(TestHeketiZones, self).setUp()
+ self.node = self.ocp_master_node[0]
+
+ def _set_heketi_zones(self, unique_zones_amount=1):
+ h = heketi_ops.cmd_run_on_heketi_pod
+ heketi_db_dir = h("mount | grep heketidbstorage | awk '{print $3}'")
+
+ # Copy the Heketi DB file to a backup file to be able to export it
+ h("cp %s/heketi.db heketi_export.db" % heketi_db_dir)
+
+ # Export the Heketi DB to a json file
+ h("rm -f heketi_db_export.json")
+ h("heketi db export --dbfile=heketi_export.db "
+ "--jsonfile=heketi_db_export.json")
+ h("rm heketi_export.db")
+
+ # Read json file info
+ heketi_db_original_str = h("cat heketi_db_export.json")
+ heketi_db_data = json.loads(heketi_db_original_str)
+
+ # Process the Heketi DB data
+ node_count = len([
+ n for n in heketi_db_data['nodeentries'].values()
+ if (n['State'] == 'online'
+ and n['Info']['cluster'] == self.cluster_id)
+ ])
+ if node_count < unique_zones_amount:
+ self.skipTest(
+ "Not enough amount of online nodes (%s) to set '%s' unique "
+ "zones." % (node_count, unique_zones_amount))
+ zones = itertools.cycle(range(unique_zones_amount))
+ for n in heketi_db_data['nodeentries'].values():
+ if (n['State'] == 'online'
+ and n['Info']['cluster'] == self.cluster_id):
+ n['Info']['zone'] = next(zones) + 1
+
+ # Schedule revert-back of the Heketi DB
+ cmd_put_data_to_a_json_file = (
+ "bash -ec \"\"\"echo '%s' > heketi_db_import.json\"\"\"")
+ cmd_remove_heketi_import_db_file = "rm -f heketi_import.db"
+ cmd_import_json_file = (
+ "heketi db import --jsonfile=heketi_db_import.json "
+ "--dbfile=heketi_import.db")
+ cmd_apply_imported_data = (
+ "mv -f heketi_import.db %s/heketi.db" % heketi_db_dir)
+ cmd_remove_heketi_import_json_files = (
+ "rm -f heketi_db_export.json heketi_db_import.json")
+
+ for i in (1, 0):
+ self.addCleanup(
+ openshift_ops.scale_dc_pod_amount_and_wait,
+ self.node, self.heketi_dc_name, i, wait_step=3)
+ for cmd in (cmd_remove_heketi_import_json_files,
+ cmd_apply_imported_data,
+ cmd_import_json_file,
+ cmd_remove_heketi_import_db_file,
+ cmd_put_data_to_a_json_file % (
+ heketi_db_original_str.replace('"', '\\"'))):
+ self.addCleanup(h, cmd)
+
+ # Import the Heketi DB data
+ heketi_db_updated_str = json.dumps(heketi_db_data).replace('"', '\\"')
+ for cmd in (cmd_put_data_to_a_json_file % heketi_db_updated_str,
+ cmd_remove_heketi_import_db_file,
+ cmd_import_json_file,
+ cmd_apply_imported_data,
+ cmd_remove_heketi_import_json_files):
+ h(cmd)
+
+ # Apply changes by recreating the Heketi POD
+ openshift_ops.scale_dc_pod_amount_and_wait(
+ self.node, self.heketi_dc_name, 0, wait_step=3)
+ openshift_ops.scale_dc_pod_amount_and_wait(
+ self.node, self.heketi_dc_name, 1, wait_step=3)
+
+ return heketi_db_data
+
+ def _get_online_nodes(self):
+ node_ids = heketi_ops.heketi_node_list(
+ self.heketi_client_node, self.heketi_server_url)
+ online_nodes = []
+ for node_id in node_ids:
+ node_info = heketi_ops.heketi_node_info(
+ self.heketi_client_node, self.heketi_server_url,
+ node_id, json=True)
+ if (node_info["state"] == "online"
+ and node_info['cluster'] == self.cluster_id):
+ online_nodes.append(
+ (node_info["zone"], node_info['hostnames']['storage']))
+ return online_nodes
+
+ @ddt.data(
+ (3, "strict", False),
+ (3, "strict", True),
+ (4, "strict", False),
+ (4, "strict", True),
+ (1, "none", False),
+ (1, "none", True),
+ (2, "none", False),
+ (2, "none", True),
+ (3, "none", False),
+ (3, "none", True),
+ )
+ @ddt.unpack
+ def test_check_pvc_placement_based_on_the_heketi_zones(
+ self, zone_count, heketi_zone_checking, is_arbiter_vol):
+ # Check amount of available online heketi nodes
+ online_nodes = self._get_online_nodes()
+ node_count = len(online_nodes)
+
+ # Check current amount of the Heketi zones
+ actual_heketi_zones_amount = len(set([n[0] for n in online_nodes]))
+ if zone_count != actual_heketi_zones_amount:
+ if self.allow_heketi_zones_update:
+ if zone_count > node_count:
+ self.skipTest(
+ "Not enough online nodes '%s' to test '%s' "
+ "unique Heketi zones." % (node_count, zone_count))
+ heketi_db_data = self._set_heketi_zones(zone_count)
+ online_nodes = [
+ (n['Info']['zone'], n['Info']['hostnames']['storage'])
+ for n in heketi_db_data['nodeentries'].values()
+ ]
+ else:
+ self.skipTest(
+ "Required amount of the Heketi zones (%s < %s) is not "
+ "satisfied and 'common.allow_heketi_zones_update' config "
+ "option is set to 'False'." % (
+ zone_count, actual_heketi_zones_amount))
+
+ # Create storage class setting "user.heketi.zone-checking" option up
+ prefix = "autotests-heketi-zones"
+ sc_name = self.create_storage_class(
+ sc_name_prefix=prefix, vol_name_prefix=prefix,
+ is_arbiter_vol=is_arbiter_vol,
+ heketi_zone_checking=heketi_zone_checking)
+
+ # Create PVC using above storage class
+ pvc_name = self.create_and_wait_for_pvc(
+ pvc_name_prefix=prefix, sc_name=sc_name)
+
+ # Validate brick placement if heketi zone checking is set to 'strict'
+ if heketi_zone_checking == 'strict':
+ brick_hosts_ips = openshift_ops.get_gluster_host_ips_by_pvc_name(
+ self.node, pvc_name)
+ placement_zones = set()
+ for brick_host_ip in brick_hosts_ips:
+ for node_zone, node_ips in online_nodes:
+ if brick_host_ip not in node_ips:
+ continue
+ placement_zones.add(node_zone)
+ break
+ self.assertEqual(
+ zone_count, len(placement_zones),
+ "PVC '%s' is incorrectly placed on the Heketi nodes "
+ "according to their zones. Expected '%s' unique zones, got "
+ "'%s'." % (pvc_name, zone_count, len(placement_zones)))
+
+ # Make sure that gluster vol has appropriate option set
+ vol_info = openshift_ops.get_gluster_vol_info_by_pvc_name(
+ self.node, pvc_name)
+ self.assertIn('user.heketi.zone-checking', vol_info['options'])
+ self.assertEqual(
+ vol_info['options']['user.heketi.zone-checking'],
+ heketi_zone_checking)
+ if is_arbiter_vol:
+ self.assertIn('user.heketi.arbiter', vol_info['options'])
+ self.assertEqual(
+ vol_info['options']['user.heketi.arbiter'], 'true')
+
+ # Create app DC with the above PVC
+ self.create_dc_with_pvc(pvc_name, timeout=120, wait_step=3)
diff --git a/tests/glusterfs-containers-tests-config.yaml b/tests/glusterfs-containers-tests-config.yaml
index d92dbaac..fde977fc 100644
--- a/tests/glusterfs-containers-tests-config.yaml
+++ b/tests/glusterfs-containers-tests-config.yaml
@@ -57,6 +57,7 @@ openshift:
volumenameprefix: "autotests-block"
common:
+ allow_heketi_zones_update: False
stop_on_first_failure: False
heketi_command_timeout: 120