"""
Test to verify performance of PVC creation and deletion
for RBD, CephFS and RBD-Thick interfaces
"""
import time
import logging
import datetime
import pytest
import ocs_ci.ocs.exceptions as ex
import threading
import statistics
from concurrent.futures import ThreadPoolExecutor
from uuid import uuid4

from ocs_ci.framework.testlib import performance
from ocs_ci.ocs.perftests import PASTest
from ocs_ci.helpers import helpers, performance_lib
from ocs_ci.ocs import constants
from ocs_ci.helpers.helpers import get_full_test_logs_path
from ocs_ci.ocs.perfresult import PerfResult
from ocs_ci.framework import config


log = logging.getLogger(__name__)


class ResultsAnalyse(PerfResult):
    """
    This class generates results for all tests as one unit
    and saves them to an elastic search server on the cluster

    """

    def __init__(self, uuid, crd, full_log_path):
        """
        Initialize the object by reading some of the data from the CRD file and
        by connecting to the ES server and read all results from it.

        Args:
            uuid (str): the unique uid of the test
            crd (dict): dictionary with test parameters - the test yaml file
                        that modify it in the test itself.
            full_log_path (str): the path of the results files to be found

        """
        super(ResultsAnalyse, self).__init__(uuid, crd)
        self.new_index = "pvc_create_delete_fullres"
        self.full_log_path = full_log_path
        # make sure we have connection to the elastic search server
        self.es_connect()


@performance
class TestPVCCreationDeletionPerformance(PASTest):
    """
    Test to verify performance of PVC creation and deletion
    """

    def setup(self):
        """
        Setting up test parameters
        """
        log.info("Starting the test setup")
        super(TestPVCCreationDeletionPerformance, self).setup()
        self.benchmark_name = "PVC_Creation-Deletion"
        self.uuid = uuid4().hex
        self.crd_data = {
            "spec": {
                "test_user": "Homer simpson",
                "clustername": "test_cluster",
                "elasticsearch": {
                    "server": config.PERF.get("production_es_server"),
                    "port": config.PERF.get("production_es_port"),
                    "url": f"http://{config.PERF.get('production_es_server')}:{config.PERF.get('production_es_port')}",
                },
            }
        }
        if self.dev_mode:
            self.crd_data["spec"]["elasticsearch"] = {
                "server": config.PERF.get("dev_es_server"),
                "port": config.PERF.get("dev_es_port"),
                "url": f"http://{config.PERF.get('dev_es_server')}:{config.PERF.get('dev_es_port')}",
            }

    @pytest.fixture()
    def base_setup(self, interface_type, storageclass_factory, pod_factory):
        """
        A setup phase for the test

        Args:
            interface_type: A fixture to iterate over ceph interfaces
            storageclass_factory: A fixture to create everything needed for a
                storageclass
            pod_factory: A fixture to create new pod
        """
        self.interface = interface_type
        if self.interface == constants.CEPHBLOCKPOOL_THICK:
            self.sc_obj = storageclass_factory(
                interface=constants.CEPHBLOCKPOOL,
                new_rbd_pool=True,
                rbd_thick_provision=True,
            )
        else:
            self.sc_obj = storageclass_factory(self.interface)
        self.pod_factory = pod_factory

    @pytest.fixture()
    def namespace(self, project_factory):
        """
        Create a new project
        """
        proj_obj = project_factory()
        self.namespace = proj_obj.namespace

    def init_full_results(self, full_results):
        """
        Initialize the full results object which will send to the ES server

        Args:
            full_results (obj): an empty FIOResultsAnalyse object

        Returns:
            FIOResultsAnalyse (obj): the input object fill with data

        """
        for key in self.environment:
            full_results.add_key(key, self.environment[key])
        full_results.add_key("storageclass", self.sc)
        full_results.add_key("index", full_results.new_index)
        return full_results

    @pytest.mark.parametrize(
        argnames=["interface_type", "pvc_size"],
        argvalues=[
            pytest.param(
                *[constants.CEPHBLOCKPOOL, "5Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL, "15Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL, "25Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHFILESYSTEM, "5Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHFILESYSTEM, "15Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHFILESYSTEM, "25Gi"],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL_THICK, "5Gi"],
                marks=[pytest.mark.performance_extended],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL_THICK, "15Gi"],
                marks=[pytest.mark.performance_extended],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL_THICK, "25Gi"],
                marks=[pytest.mark.performance_extended],
            ),
        ],
    )
    @pytest.mark.usefixtures(base_setup.__name__)
    def test_pvc_creation_deletion_measurement_performance(
        self, teardown_factory, pvc_size
    ):
        """
        Measuring PVC creation and deletion times for pvc samples
        Verifying that those times are within the required limits
        """

        # Getting the full path for the test logs
        self.full_log_path = get_full_test_logs_path(cname=self)
        if self.interface == constants.CEPHBLOCKPOOL:
            self.sc = "RBD"
        elif self.interface == constants.CEPHFILESYSTEM:
            self.sc = "CephFS"
        elif self.interface == constants.CEPHBLOCKPOOL_THICK:
            self.sc = "RBD-Thick"
        self.full_log_path += f"-{self.sc}-{pvc_size}"
        log.info(f"Logs file path name is : {self.full_log_path}")

        self.start_time = time.strftime("%Y-%m-%dT%H:%M:%SGMT", time.gmtime())

        self.get_env_info()

        # Initialize the results doc file.
        self.full_results = self.init_full_results(
            ResultsAnalyse(self.uuid, self.crd_data, self.full_log_path)
        )
        self.full_results.add_key("pvc_size", pvc_size)
        num_of_samples = 5
        accepted_creation_time = (
            600 if self.interface == constants.CEPHBLOCKPOOL_THICK else 1
        )

        # accepted deletion time for RBD is 1 sec, for CephFS is 2 secs and for RBD Thick is 5 secs
        if self.interface == constants.CEPHFILESYSTEM:
            accepted_deletion_time = 2
        elif self.interface == constants.CEPHBLOCKPOOL:
            accepted_deletion_time = 1
        else:
            accepted_deletion_time = 5

        self.full_results.add_key("samples", num_of_samples)

        accepted_creation_deviation_percent = 50
        accepted_deletion_deviation_percent = 50

        creation_time_measures = []
        deletion_time_measures = []
        msg_prefix = f"Interface: {self.interface}, PVC size: {pvc_size}."

        for i in range(num_of_samples):
            logging.info(f"{msg_prefix} Start creating PVC number {i + 1}.")
            start_time = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
            pvc_obj = helpers.create_pvc(sc_name=self.sc_obj.name, size=pvc_size)
            timeout = 600 if self.interface == constants.CEPHBLOCKPOOL_THICK else 60
            helpers.wait_for_resource_state(
                pvc_obj, constants.STATUS_BOUND, timeout=timeout
            )
            pvc_obj.reload()

            creation_time = performance_lib.measure_pvc_creation_time(
                self.interface, pvc_obj.name, start_time
            )

            logging.info(
                f"{msg_prefix} PVC number {i + 1} was created in {creation_time} seconds."
            )
            if creation_time > accepted_creation_time:
                raise ex.PerformanceException(
                    f"{msg_prefix} PVC creation time is {creation_time} and is greater than "
                    f"{accepted_creation_time} seconds."
                )
            creation_time_measures.append(creation_time)

            pv_name = pvc_obj.backed_pv
            pvc_reclaim_policy = pvc_obj.reclaim_policy

            pod_obj = self.write_file_on_pvc(pvc_obj)
            pod_obj.delete(wait=True)
            teardown_factory(pvc_obj)
            logging.info(f"{msg_prefix} Start deleting PVC number {i + 1}")
            if pvc_reclaim_policy == constants.RECLAIM_POLICY_DELETE:
                pvc_obj.delete()
                pvc_obj.ocp.wait_for_delete(pvc_obj.name)
                helpers.validate_pv_delete(pvc_obj.backed_pv)
                deletion_time = helpers.measure_pvc_deletion_time(
                    self.interface, pv_name
                )
                logging.info(
                    f"{msg_prefix} PVC number {i + 1} was deleted in {deletion_time} seconds."
                )
                if deletion_time > accepted_deletion_time:
                    raise ex.PerformanceException(
                        f"{msg_prefix} PVC deletion time is {deletion_time} and is greater than "
                        f"{accepted_deletion_time} seconds."
                    )
                deletion_time_measures.append(deletion_time)
            else:
                logging.info(
                    f"Reclaim policy of the PVC {pvc_obj.name} is not Delete;"
                    f" therefore not measuring deletion time for this PVC."
                )

        creation_average = self.process_time_measurements(
            "creation",
            creation_time_measures,
            accepted_creation_deviation_percent,
            msg_prefix,
        )
        self.full_results.add_key("creation-time", creation_average)
        deletion_average = self.process_time_measurements(
            "deletion",
            deletion_time_measures,
            accepted_deletion_deviation_percent,
            msg_prefix,
        )
        self.full_results.add_key("deletion-time", deletion_average)
        self.full_results.all_results["creation"] = creation_time_measures
        self.full_results.all_results["deletion"] = deletion_time_measures
        self.end_time = time.strftime("%Y-%m-%dT%H:%M:%SGMT", time.gmtime())
        self.full_results.add_key(
            "test_time", {"start": self.start_time, "end": self.end_time}
        )
        self.full_results.es_write()
        log.info(f"The Result can be found at : {self.full_results.results_link()}")

    def process_time_measurements(
        self, action_name, time_measures, accepted_deviation_percent, msg_prefix
    ):
        """
           Analyses the given time measured. If the standard deviation of these times is bigger than the
           provided accepted deviation percent, fails the test

        Args:
            action_name (str): Name of the action for which these measurements were collected; used for the logging
            time_measures (list of floats): A list of time measurements
            accepted_deviation_percent (int): Accepted deviation percent to which computed standard deviation may be
                    compared
            msg_prefix (str) : A string for comprehensive logging

        Returns:
            (float) The average value of the provided time measurements
        """
        average = statistics.mean(time_measures)
        log.info(
            f"{msg_prefix} The average {action_name} time for the sampled {len(time_measures)} "
            f"PVCs is {average} seconds."
        )

        if self.interface == constants.CEPHBLOCKPOOL_THICK:
            st_deviation = statistics.stdev(time_measures)
            st_deviation_percent = st_deviation / average * 100.0
            if st_deviation_percent > accepted_deviation_percent:
                log.error(
                    f"{msg_prefix} The standard deviation percent for {action_name} of {len(time_measures)} sampled "
                    f"PVCs is {st_deviation_percent}% which is bigger than accepted {accepted_deviation_percent}."
                )
            else:
                log.info(
                    f"{msg_prefix} The standard deviation percent for {action_name} of {len(time_measures)} sampled "
                    f"PVCs is {st_deviation_percent}% and is within the accepted range."
                )
            self.full_results.add_key(
                f"{action_name}_deviation_pct", st_deviation_percent
            )

        return average

    def write_file_on_pvc(self, pvc_obj, filesize=1):
        """
        Writes a file on given PVC
        Args:
            pvc_obj: PVC object to write a file on
            filesize: size of file to write (in GB - default is 1GB)

        Returns:
            Pod on this pvc on which the file was written
        """
        pod_obj = self.pod_factory(
            interface=self.interface, pvc=pvc_obj, status=constants.STATUS_RUNNING
        )

        # filesize to be written is always 1 GB
        file_size = f"{int(filesize * 1024)}M"

        log.info(f"Starting IO on the POD {pod_obj.name}")
        # Going to run only write IO
        pod_obj.fillup_fs(size=file_size, fio_filename=f"{pod_obj.name}_file")

        # Wait for the fio to finish
        fio_result = pod_obj.get_fio_results()
        err_count = fio_result.get("jobs")[0].get("error")
        assert (
            err_count == 0
        ), f"IO error on pod {pod_obj.name}. FIO result: {fio_result}"
        log.info("IO on the PVC has finished")
        return pod_obj

    @pytest.mark.parametrize(
        argnames=["interface_type"],
        argvalues=[
            pytest.param(
                *[constants.CEPHBLOCKPOOL],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHFILESYSTEM],
                marks=[pytest.mark.performance],
            ),
            pytest.param(
                *[constants.CEPHBLOCKPOOL_THICK],
                marks=[pytest.mark.performance_extended],
            ),
        ],
    )
    @pytest.mark.usefixtures(base_setup.__name__)
    @pytest.mark.usefixtures(namespace.__name__)
    @pytest.mark.polarion_id("OCS-2618")
    def test_multiple_pvc_deletion_measurement_performance(self, teardown_factory):
        """
        Measuring PVC deletion time of 120 PVCs in 180 seconds

        Args:
            teardown_factory: A fixture used when we want a new resource that was created during the tests
                               to be removed in the teardown phase.
        Returns:

        """
        number_of_pvcs = 120
        pvc_size = "1Gi"
        msg_prefix = f"Interface: {self.interface}, PVC size: {pvc_size}."

        log.info(f"{msg_prefix} Start creating new 120 PVCs")

        pvc_objs, _ = helpers.create_multiple_pvcs(
            sc_name=self.sc_obj.name,
            namespace=self.namespace,
            number_of_pvc=number_of_pvcs,
            size=pvc_size,
            burst=True,
        )

        for pvc_obj in pvc_objs:
            pvc_obj.reload()
            teardown_factory(pvc_obj)

        timeout = 600 if self.interface == constants.CEPHBLOCKPOOL_THICK else 60
        with ThreadPoolExecutor(max_workers=5) as executor:
            for pvc_obj in pvc_objs:
                executor.submit(
                    helpers.wait_for_resource_state,
                    pvc_obj,
                    constants.STATUS_BOUND,
                    timeout=timeout,
                )
                executor.submit(pvc_obj.reload)

        pod_objs = []
        for pvc_obj in pvc_objs:
            pod_obj = self.write_file_on_pvc(pvc_obj, 0.3)
            pod_objs.append(pod_obj)

        # Get pvc_name, require pvc_name to fetch deletion time data from log
        threads = list()
        for pvc_obj in pvc_objs:
            process = threading.Thread(target=pvc_obj.reload)
            process.start()
            threads.append(process)
        for process in threads:
            process.join()

        pvc_name_list, pv_name_list = ([] for i in range(2))
        threads = list()
        for pvc_obj in pvc_objs:
            process1 = threading.Thread(target=pvc_name_list.append(pvc_obj.name))
            process2 = threading.Thread(target=pv_name_list.append(pvc_obj.backed_pv))
            process1.start()
            process2.start()
            threads.append(process1)
            threads.append(process2)
        for process in threads:
            process.join()
        log.info(f"{msg_prefix} Preparing to delete 120 PVC")

        # Delete PVC
        for pvc_obj, pod_obj in zip(pvc_objs, pod_objs):
            pod_obj.delete(wait=True)
            pvc_obj.delete()
            pvc_obj.ocp.wait_for_delete(pvc_obj.name)

        # Get PVC deletion time
        pvc_deletion_time = helpers.measure_pv_deletion_time_bulk(
            interface=self.interface, pv_name_list=pv_name_list
        )
        log.info(
            f"{msg_prefix} {number_of_pvcs} bulk deletion time is {pvc_deletion_time}"
        )

        # accepted deletion time is 2 secs for each PVC
        accepted_pvc_deletion_time = number_of_pvcs * 2

        for del_time in pvc_deletion_time.values():
            if del_time > accepted_pvc_deletion_time:
                raise ex.PerformanceException(
                    f"{msg_prefix} {number_of_pvcs} PVCs deletion time is {pvc_deletion_time.values()} and is "
                    f"greater than {accepted_pvc_deletion_time} seconds"
                )

        logging.info(f"{msg_prefix} {number_of_pvcs} PVCs deletion times are:")
        for name, a_time in pvc_deletion_time.items():
            logging.info(f"{name} deletion time is: {a_time} seconds")

[((24, 6, 24, 33), 'logging.getLogger', 'logging.getLogger', ({(24, 24, 24, 32): '__name__'}, {}), '(__name__)', False, 'import logging\n'), ((85, 5, 85, 21), 'pytest.fixture', 'pytest.fixture', ({}, {}), '()', False, 'import pytest\n'), ((107, 5, 107, 21), 'pytest.fixture', 'pytest.fixture', ({}, {}), '()', False, 'import pytest\n'), ((173, 5, 173, 49), 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', ({(173, 29, 173, 48): 'base_setup.__name__'}, {}), '(base_setup.__name__)', False, 'import pytest\n'), ((389, 5, 389, 49), 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', ({(389, 29, 389, 48): 'base_setup.__name__'}, {}), '(base_setup.__name__)', False, 'import pytest\n'), ((390, 5, 390, 48), 'pytest.mark.usefixtures', 'pytest.mark.usefixtures', ({(390, 29, 390, 47): 'namespace.__name__'}, {}), '(namespace.__name__)', False, 'import pytest\n'), ((391, 5, 391, 40), 'pytest.mark.polarion_id', 'pytest.mark.polarion_id', ({(391, 29, 391, 39): '"""OCS-2618"""'}, {}), "('OCS-2618')", False, 'import pytest\n'), ((183, 29, 183, 64), 'ocs_ci.helpers.helpers.get_full_test_logs_path', 'get_full_test_logs_path', (), '', False, 'from ocs_ci.helpers.helpers import get_full_test_logs_path\n'), ((317, 18, 317, 48), 'statistics.mean', 'statistics.mean', ({(317, 34, 317, 47): 'time_measures'}, {}), '(time_measures)', False, 'import statistics\n'), ((408, 22, 414, 9), 'ocs_ci.helpers.helpers.create_multiple_pvcs', 'helpers.create_multiple_pvcs', (), '', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((465, 28, 467, 9), 'ocs_ci.helpers.helpers.measure_pv_deletion_time_bulk', 'helpers.measure_pv_deletion_time_bulk', (), '', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((482, 8, 482, 79), 'logging.info', 'logging.info', ({(482, 21, 482, 78): 'f"""{msg_prefix} {number_of_pvcs} PVCs deletion times are:"""'}, {}), "(f'{msg_prefix} {number_of_pvcs} PVCs deletion times are:')", False, 'import logging\n'), ((66, 20, 66, 27), 'uuid.uuid4', 'uuid4', ({}, {}), '()', False, 'from uuid import uuid4\n'), ((193, 64, 193, 77), 'time.gmtime', 'time.gmtime', ({}, {}), '()', False, 'import time\n'), ((225, 12, 225, 76), 'logging.info', 'logging.info', ({(225, 25, 225, 75): 'f"""{msg_prefix} Start creating PVC number {i + 1}."""'}, {}), "(f'{msg_prefix} Start creating PVC number {i + 1}.')", False, 'import logging\n'), ((227, 22, 227, 81), 'ocs_ci.helpers.helpers.create_pvc', 'helpers.create_pvc', (), '', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((229, 12, 231, 13), 'ocs_ci.helpers.helpers.wait_for_resource_state', 'helpers.wait_for_resource_state', (), '', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((234, 28, 236, 13), 'ocs_ci.helpers.performance_lib.measure_pvc_creation_time', 'performance_lib.measure_pvc_creation_time', ({(235, 16, 235, 30): 'self.interface', (235, 32, 235, 44): 'pvc_obj.name', (235, 46, 235, 56): 'start_time'}, {}), '(self.interface, pvc_obj.name,\n    start_time)', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((238, 12, 240, 13), 'logging.info', 'logging.info', ({(239, 16, 239, 90): 'f"""{msg_prefix} PVC number {i + 1} was created in {creation_time} seconds."""'}, {}), "(\n    f'{msg_prefix} PVC number {i + 1} was created in {creation_time} seconds.')", False, 'import logging\n'), ((254, 12, 254, 75), 'logging.info', 'logging.info', ({(254, 25, 254, 74): 'f"""{msg_prefix} Start deleting PVC number {i + 1}"""'}, {}), "(f'{msg_prefix} Start deleting PVC number {i + 1}')", False, 'import logging\n'), ((293, 62, 293, 75), 'time.gmtime', 'time.gmtime', ({}, {}), '()', False, 'import time\n'), ((324, 27, 324, 58), 'statistics.stdev', 'statistics.stdev', ({(324, 44, 324, 57): 'time_measures'}, {}), '(time_measures)', False, 'import statistics\n'), ((421, 13, 421, 46), 'concurrent.futures.ThreadPoolExecutor', 'ThreadPoolExecutor', (), '', False, 'from concurrent.futures import ThreadPoolExecutor\n'), ((439, 22, 439, 61), 'threading.Thread', 'threading.Thread', (), '', False, 'import threading\n'), ((484, 12, 484, 70), 'logging.info', 'logging.info', ({(484, 25, 484, 69): 'f"""{name} deletion time is: {a_time} seconds"""'}, {}), "(f'{name} deletion time is: {a_time} seconds')", False, 'import logging\n'), ((80, 26, 80, 58), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(80, 42, 80, 57): '"""dev_es_server"""'}, {}), "('dev_es_server')", False, 'from ocs_ci.framework import config\n'), ((81, 24, 81, 54), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(81, 40, 81, 53): '"""dev_es_port"""'}, {}), "('dev_es_port')", False, 'from ocs_ci.framework import config\n'), ((242, 22, 245, 17), 'ocs_ci.ocs.exceptions.PerformanceException', 'ex.PerformanceException', ({(243, 20, 244, 56): 'f"""{msg_prefix} PVC creation time is {creation_time} and is greater than {accepted_creation_time} seconds."""'}, {}), "(\n    f'{msg_prefix} PVC creation time is {creation_time} and is greater than {accepted_creation_time} seconds.'\n    )", True, 'import ocs_ci.ocs.exceptions as ex\n'), ((258, 16, 258, 61), 'ocs_ci.helpers.helpers.validate_pv_delete', 'helpers.validate_pv_delete', ({(258, 43, 258, 60): 'pvc_obj.backed_pv'}, {}), '(pvc_obj.backed_pv)', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((259, 32, 261, 17), 'ocs_ci.helpers.helpers.measure_pvc_deletion_time', 'helpers.measure_pvc_deletion_time', ({(260, 20, 260, 34): 'self.interface', (260, 36, 260, 43): 'pv_name'}, {}), '(self.interface, pv_name)', False, 'from ocs_ci.helpers import helpers, performance_lib\n'), ((262, 16, 264, 17), 'logging.info', 'logging.info', ({(263, 20, 263, 94): 'f"""{msg_prefix} PVC number {i + 1} was deleted in {deletion_time} seconds."""'}, {}), "(\n    f'{msg_prefix} PVC number {i + 1} was deleted in {deletion_time} seconds.')", False, 'import logging\n'), ((272, 16, 275, 17), 'logging.info', 'logging.info', ({(273, 20, 274, 75): 'f"""Reclaim policy of the PVC {pvc_obj.name} is not Delete; therefore not measuring deletion time for this PVC."""'}, {}), "(\n    f'Reclaim policy of the PVC {pvc_obj.name} is not Delete; therefore not measuring deletion time for this PVC.'\n    )", False, 'import logging\n'), ((135, 12, 138, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((139, 12, 142, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((143, 12, 146, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((147, 12, 150, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((151, 12, 154, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((155, 12, 158, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((159, 12, 162, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((163, 12, 166, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((167, 12, 170, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((375, 12, 378, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((379, 12, 382, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((383, 12, 386, 13), 'pytest.param', 'pytest.param', (), '', False, 'import pytest\n'), ((72, 30, 72, 69), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(72, 46, 72, 68): '"""production_es_server"""'}, {}), "('production_es_server')", False, 'from ocs_ci.framework import config\n'), ((73, 28, 73, 65), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(73, 44, 73, 64): '"""production_es_port"""'}, {}), "('production_es_port')", False, 'from ocs_ci.framework import config\n'), ((226, 25, 226, 51), 'datetime.datetime.utcnow', 'datetime.datetime.utcnow', ({}, {}), '()', False, 'import datetime\n'), ((266, 26, 269, 21), 'ocs_ci.ocs.exceptions.PerformanceException', 'ex.PerformanceException', ({(267, 24, 268, 60): 'f"""{msg_prefix} PVC deletion time is {deletion_time} and is greater than {accepted_deletion_time} seconds."""'}, {}), "(\n    f'{msg_prefix} PVC deletion time is {deletion_time} and is greater than {accepted_deletion_time} seconds.'\n    )", True, 'import ocs_ci.ocs.exceptions as ex\n'), ((82, 33, 82, 65), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(82, 49, 82, 64): '"""dev_es_server"""'}, {}), "('dev_es_server')", False, 'from ocs_ci.framework import config\n'), ((82, 68, 82, 98), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(82, 84, 82, 97): '"""dev_es_port"""'}, {}), "('dev_es_port')", False, 'from ocs_ci.framework import config\n'), ((74, 37, 74, 76), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(74, 53, 74, 75): '"""production_es_server"""'}, {}), "('production_es_server')", False, 'from ocs_ci.framework import config\n'), ((74, 79, 74, 116), 'ocs_ci.framework.config.PERF.get', 'config.PERF.get', ({(74, 95, 74, 115): '"""production_es_port"""'}, {}), "('production_es_port')", False, 'from ocs_ci.framework import config\n')]