From 5fc532045c12cbaefdff1a02c7d34b7356f430a6 Mon Sep 17 00:00:00 2001 From: Valerii Ponomarov Date: Tue, 17 Sep 2019 22:45:57 +0530 Subject: Add waiter for multiple resources absence Add new function called 'wait_for_resources_absence', which allows us to wait for absense of multiple resources using single timeout. Also, reuse this new function in the baseclass.py for PVC/PV removal waitings. Change-Id: I6e3daae012199e50d3aeb68b697cfae801b0b49b --- .../openshiftstoragelibs/baseclass.py | 6 +- .../openshiftstoragelibs/openshift_ops.py | 97 ++++++++++++++-------- 2 files changed, 65 insertions(+), 38 deletions(-) (limited to 'openshift-storage-libs') diff --git a/openshift-storage-libs/openshiftstoragelibs/baseclass.py b/openshift-storage-libs/openshiftstoragelibs/baseclass.py index c8cb1d37..156aee86 100644 --- a/openshift-storage-libs/openshiftstoragelibs/baseclass.py +++ b/openshift-storage-libs/openshiftstoragelibs/baseclass.py @@ -35,7 +35,7 @@ from openshiftstoragelibs.openshift_ops import ( scale_dcs_pod_amount_and_wait, switch_oc_project, verify_pvc_status_is_bound, - wait_for_resource_absence, + wait_for_resources_absence, ) from openshiftstoragelibs.openshift_storage_libs import ( get_iscsi_block_devices_by_path, @@ -245,8 +245,8 @@ class BaseClass(unittest.TestCase): node, sc_name, pvc_name_prefix=pvc_name_prefix, pvc_size=pvc_size) pvc_names.append(pvc_name) - self.addCleanup( - wait_for_resource_absence, node, 'pvc', pvc_name) + self.addCleanup( + wait_for_resources_absence, node, 'pvc', pvc_names) # Wait for PVCs to be in bound state try: diff --git a/openshift-storage-libs/openshiftstoragelibs/openshift_ops.py b/openshift-storage-libs/openshiftstoragelibs/openshift_ops.py index 8aaa1418..9516e133 100644 --- a/openshift-storage-libs/openshiftstoragelibs/openshift_ops.py +++ b/openshift-storage-libs/openshiftstoragelibs/openshift_ops.py @@ -567,52 +567,76 @@ def oc_get_all_pvs(ocp_node): return oc_get_yaml(ocp_node, 'pv', None) -def wait_for_resource_absence(ocp_node, rtype, name, - interval=5, timeout=600): +def wait_for_resources_absence(ocp_node, rtype, names, + interval=5, timeout=600): + """Wait for an absence of any set of resources of one type. + + If provided resource type is 'pvc' then 'pv's are also checked. + + Args: + ocp_node (str): OCP node to perform oc client operations on. + rtype (str): type of a resource(s) such as 'pvc', 'pv' and 'pod'. + names (iterable): any iterable with names of objects to wait for. + interval (int): interval in seconds between waiting attempts. + timeout (int): overall timeout for waiting. + """ _waiter = waiter.Waiter(timeout=timeout, interval=interval) - resource, pv_name, _pv_name = None, None, None - for w in _waiter: - try: - resource = oc_get_yaml(ocp_node, rtype, name, raise_on_error=True) - except AssertionError: - # NOTE(vponomar): Reset attempts for waiter to avoid redundant - # sleep equal to 'interval' on the next usage. - _waiter._attempt = 0 - break - if rtype == 'pvc': - cmd = "oc get pv -o=custom-columns=:.spec.claimRef.name | grep %s" % ( - name) + if len(names[0]) == 1: + names = (names, ) + resources = {name: {'resource': 'not_checked'} for name in names} + for name in names: for w in _waiter: try: - _pv_name = command.cmd_run(cmd, hostname=ocp_node) + resources[name]['resource'] = oc_get_yaml( + ocp_node, rtype, name, raise_on_error=True) except AssertionError: + # NOTE(vponomar): Reset attempts for waiter to avoid redundant + # sleep equal to 'interval' on the next usage. _waiter._attempt = 0 + resources[name]['resource'] = 'absent' break - finally: - if _pv_name and not pv_name: - pv_name = _pv_name - if w.expired: + if rtype == 'pvc': + for name in names: + resources[name]['pv_name'] = '?' + for w in _waiter: + try: + _pv_name = get_pv_name_from_pvc(ocp_node, name) + if resources[name]['pv_name'] == '?': + resources[name]['pv_name'] = _pv_name + except AssertionError: + _waiter._attempt = 0 + resources[name]['pv_name'] = 'absent' + break + if _waiter.expired: # Gather more info for ease of debugging - try: - r_events = get_events(ocp_node, obj_name=name) - except Exception: - r_events = '?' - error_msg = ( - "%s '%s' still exists after waiting for it %d seconds.\n" - "Resource info: %s\n" - "Resource related events: %s" % ( - rtype, name, timeout, resource, r_events)) - if rtype == 'pvc' and pv_name: + for name in names: try: - pv_events = get_events(ocp_node, obj_name=pv_name) + r_events = get_events(ocp_node, obj_name=name) except Exception: - pv_events = '?' - error_msg += "\nPV events: %s" % pv_events - + r_events = '?' + resources[name]['events'] = r_events + if rtype == 'pvc' and resources[name]['pv_name'] != '?': + try: + pv_events = get_events( + ocp_node, obj_name=resources[name]['pv_name']) + except Exception: + pv_events = '?' + resources[name]['pv_events'] = pv_events + error_msg = ( + "Failed to wait %d seconds for some of the provided resources " + "to be absent.\nResource type: '%s'\nResource names: %s\n" + "Resources info: \n%s" % ( + timeout, rtype, ' | '.join(names), + '\n'.join([six.text_type(r) for r in resources.items()]))) g.log.error(error_msg) raise exceptions.ExecutionError(error_msg) +def wait_for_resource_absence(ocp_node, rtype, name, interval=5, timeout=600): + return wait_for_resources_absence( + ocp_node, rtype, name, interval=interval, timeout=timeout) + + def scale_dcs_pod_amount_and_wait(hostname, dc_names, pod_amount=1, namespace=None, timeout=600, wait_step=5): """Scale amount of PODs for a list of DCs. @@ -1166,9 +1190,12 @@ def get_pv_name_from_pvc(hostname, pvc_name): pv_name (str): pv name if successful, otherwise raise Exception ''' - cmd = ("oc get pvc %s -o=custom-columns=:." - "spec.volumeName" % pvc_name) + # NOTE(vponomar): following command allows to get PV even if PVC is deleted + cmd = ("oc get pv -o jsonpath='{.items[?(@.spec.claimRef.name==\"%s\")]" + ".metadata.name}'" % pvc_name) pv_name = command.cmd_run(cmd, hostname=hostname) + assert pv_name.strip(), ( + "Failed to find PV with PVC name '%s' as filter" % pvc_name) g.log.info("pv name is %s for pvc %s" % (pv_name, pvc_name)) return pv_name -- cgit