diff --git a/test/functional/api/cas/progress_bar.py b/test/functional/api/cas/progress_bar.py new file mode 100644 index 0000000..77cf614 --- /dev/null +++ b/test/functional/api/cas/progress_bar.py @@ -0,0 +1,50 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import re +from datetime import timedelta + +import paramiko + +from core.test_run import TestRun +from test_utils.os_utils import wait + + +def check_progress_bar(command: str, progress_bar_expected: bool = True): + TestRun.LOGGER.info(f"Check progress for command: {command}") + try: + stdin, stdout, stderr = TestRun.executor.ssh.exec_command(command, get_pty=True) + except paramiko.SSHException as e: + raise ConnectionError(f"An exception occurred while executing command: {command}\n{e}") + + if not wait(lambda: stdout.channel.recv_ready(), timedelta(seconds=10), timedelta(seconds=1)): + if not progress_bar_expected: + TestRun.LOGGER.info("Progress bar did not appear when output was redirected to a file.") + return + else: + TestRun.fail("Progress bar did not appear in 10 seconds.") + else: + if not progress_bar_expected: + TestRun.fail("Progress bar appear when output was redirected to a file.") + + percentage = 0 + while True: + output = stdout.channel.recv(1024).decode('utf-8') + search = re.search(r'\d+.\d+', output) + last_percentage = percentage + if search: + TestRun.LOGGER.info(output) + percentage = float(search.group()) + if last_percentage > percentage: + TestRun.fail(f"Progress decrease from {last_percentage}% to {percentage}%.") + elif percentage < 0: + TestRun.fail(f"Progress must be greater than 0%. Actual: {percentage}%.") + elif percentage > 100: + TestRun.fail(f"Progress cannot be greater than 100%. Actual: {percentage}%.") + elif (stdout.channel.exit_status_ready() or not output) and last_percentage > 0: + TestRun.LOGGER.info("Progress complete.") + break + elif stdout.channel.exit_status_ready() and last_percentage == 0: + TestRun.fail("Process has exited but progress doesn't complete.") diff --git a/test/functional/tests/progress_bar/test_progress_bar_big_files.py b/test/functional/tests/progress_bar/test_progress_bar_big_files.py new file mode 100644 index 0000000..dcf0068 --- /dev/null +++ b/test/functional/tests/progress_bar/test_progress_bar_big_files.py @@ -0,0 +1,56 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest +from api.cas import casadm, cli +from api.cas.cache_config import CacheMode, CleaningPolicy, SeqCutOffPolicy +from api.cas.progress_bar import check_progress_bar +from core.test_run_utils import TestRun +from storage_devices.disk import DiskTypeSet, DiskType +from test_tools.disk_utils import Filesystem +from test_tools.fs_utils import create_random_test_file +from test_utils.size import Size, Unit + + +mount_point = "/mnt/test" +test_file_path = f"{mount_point}/test_file" + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeSet([DiskType.hdd])) +def test_progress_bar_big_files(): + """ + title: Progress bar validation for big files. + description: Validate the ability of the CAS to display data flushing progress + for big amount of dirty data. + pass_criteria: + - progress bar appear correctly + - progress only increase + """ + with TestRun.step("Prepare devices."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(5, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(20, Unit.GibiByte)]) + core_dev = core_disk.partitions[0] + + with TestRun.step("Start cache in Write-Back mode and add core."): + cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) + core = cache.add_core(core_dev) + cache.set_cleaning_policy(CleaningPolicy.nop) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + + with TestRun.step("Make xfs filesystem on OpenCAS device and mount it."): + core.create_filesystem(Filesystem.xfs) + core.mount(mount_point) + + with TestRun.step("Create file on OpenCAS device."): + create_random_test_file(test_file_path, cache.size + Size(1, Unit.GibiByte)) + + with TestRun.step("Run command and check progress."): + cmd = cli.flush_cache_cmd(str(cache.cache_id)) + check_progress_bar(cmd) diff --git a/test/functional/tests/progress_bar/test_progress_bar_during_io.py b/test/functional/tests/progress_bar/test_progress_bar_during_io.py new file mode 100644 index 0000000..55b017f --- /dev/null +++ b/test/functional/tests/progress_bar/test_progress_bar_during_io.py @@ -0,0 +1,65 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +from datetime import timedelta +from time import sleep + +import pytest +from api.cas import casadm, cli +from api.cas.cache_config import CacheMode, CleaningPolicy, SeqCutOffPolicy +from api.cas.progress_bar import check_progress_bar +from core.test_run_utils import TestRun +from storage_devices.disk import DiskTypeSet, DiskType +from test_tools.fio.fio import Fio +from test_tools.fio.fio_param import ReadWrite, IoEngine +from test_utils.size import Size, Unit + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeSet([DiskType.hdd])) +def test_progress_bar_during_io(): + """ + title: Progress bar validation during IO. + description: Validate the ability of the CAS to flush data after intensive FIO workload. + pass_criteria: + - progress bar appear correctly + - progress only increase + """ + with TestRun.step("Prepare devices."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(5, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(5, Unit.GibiByte)] * 4) + core_devices = core_disk.partitions + + with TestRun.step("Start cache in Write-Back mode and add cores."): + cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) + cores = [cache.add_core(dev) for dev in core_devices] + cache.set_cleaning_policy(CleaningPolicy.nop) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + + with TestRun.step("Start fio on all OpenCAS devices."): + fio = (Fio().create_command() + .time_based() + .run_time(timedelta(minutes=25)) + .read_write(ReadWrite.write) + .block_size(Size(1, Unit.Blocks4096)) + .direct() + .io_engine(IoEngine.libaio)) + for i, core in enumerate(cores): + fio.add_job(f"core{i}").target(core.path) + fio_pid = fio.run_in_background() + + TestRun.LOGGER.info("Wait 8 minutes.") + sleep(480) + + with TestRun.step("Run command and check progress."): + cmd = cli.flush_cache_cmd(str(cache.cache_id)) + check_progress_bar(cmd) + + with TestRun.step("Wait for fio to finish."): + TestRun.executor.wait_cmd_finish(fio_pid) diff --git a/test/functional/tests/progress_bar/test_progress_bar_output_redirection.py b/test/functional/tests/progress_bar/test_progress_bar_output_redirection.py new file mode 100644 index 0000000..77d4c07 --- /dev/null +++ b/test/functional/tests/progress_bar/test_progress_bar_output_redirection.py @@ -0,0 +1,55 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest +from api.cas import casadm, cli +from api.cas.cache_config import CacheMode, CleaningPolicy, SeqCutOffPolicy +from api.cas.progress_bar import check_progress_bar +from core.test_run_utils import TestRun +from storage_devices.disk import DiskTypeSet, DiskType +from test_tools.disk_utils import Filesystem +from test_tools.fs_utils import create_random_test_file +from test_utils.size import Size, Unit + + +mount_point = "/mnt/test" +test_file_path = f"{mount_point}/test_file" + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeSet([DiskType.hdd])) +def test_progress_bar_output_redirection(): + """ + title: Progress bar validation for output redirection. + description: Validate the ability of the CAS to display data flushing progress + when output is redirected to file - negative scenario. + pass_criteria: + - progress bar did not appear + """ + with TestRun.step("Prepare devices."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(5, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(20, Unit.GibiByte)]) + core_dev = core_disk.partitions[0] + + with TestRun.step("Start cache in Write-Back mode and add core."): + cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) + core = cache.add_core(core_dev) + cache.set_cleaning_policy(CleaningPolicy.nop) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + + with TestRun.step("Make xfs filesystem on OpenCAS device and mount it."): + core.create_filesystem(Filesystem.xfs) + core.mount(mount_point) + + with TestRun.step("Create file on OpenCAS device."): + create_random_test_file(test_file_path, cache.size) + + with TestRun.step("Run command and check if progress bar did not appear - negative test."): + cmd = cli.flush_cache_cmd(str(cache.cache_id)) + " > ./test.log" + check_progress_bar(cmd, progress_bar_expected=False) diff --git a/test/functional/tests/progress_bar/test_progress_bar_supported_commands.py b/test/functional/tests/progress_bar/test_progress_bar_supported_commands.py new file mode 100644 index 0000000..c197d1e --- /dev/null +++ b/test/functional/tests/progress_bar/test_progress_bar_supported_commands.py @@ -0,0 +1,73 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest +from api.cas import casadm, cli +from api.cas.cache_config import CacheMode, CleaningPolicy, SeqCutOffPolicy +from api.cas.progress_bar import check_progress_bar +from core.test_run_utils import TestRun +from storage_devices.disk import DiskTypeSet, DiskType +from test_tools.fio.fio import Fio +from test_tools.fio.fio_param import ReadWrite, IoEngine +from test_utils.size import Size, Unit + + +progress_bar_cmd_cache = [cli.stop_cmd, cli.flush_cache_cmd, cli.script_purge_cache_cmd] +progress_bar_cmd_core = [cli.flush_core_cmd, cli.remove_core_cmd, cli.script_purge_core_cmd, + cli.script_remove_core_cmd] +progress_bar_cmd_other = [cli.set_cache_mode_cmd] +progress_bar_cmd = progress_bar_cmd_cache + progress_bar_cmd_core + progress_bar_cmd_other + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeSet([DiskType.hdd])) +def test_progress_bar_supported_commands(): + """ + title: Progress bar validation for all supported commands - buffered IO, WB mode. + description: Validate the ability of the CAS to display data flashing progress + for direct IO in WB mode. + pass_criteria: + - progress bar appear correctly + - progress only increase + """ + with TestRun.step("Prepare devices."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(5, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(2, Unit.GibiByte)] * 4) + core_devices = core_disk.partitions + + for command in TestRun.iteration(progress_bar_cmd): + + with TestRun.step("Start cache in Write-Back mode and add cores."): + cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) + cores = [cache.add_core(dev) for dev in core_devices] + cache.set_cleaning_policy(CleaningPolicy.nop) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + + with TestRun.step("Run fio on all OpenCAS devices."): + fio = (Fio().create_command() + .size(Size(1, Unit.GibiByte)) + .read_write(ReadWrite.randwrite) + .io_engine(IoEngine.sync) + .direct()) + for i, core in enumerate(cores): + fio.add_job(f"core{i}").target(core.path) + fio.run() + + with TestRun.step("Run command and check progress."): + if command in progress_bar_cmd_cache: + cmd = command(str(cache.cache_id)) + elif command in progress_bar_cmd_core: + cmd = command(str(cache.cache_id), str(cores[0].core_id)) + elif command in progress_bar_cmd_other: + cmd = command(str(CacheMode.WT.name).lower(), str(cache.cache_id), 'yes') + + check_progress_bar(cmd) + + with TestRun.step("Stopping cache."): + casadm.stop_all_caches() diff --git a/test/functional/tests/progress_bar/test_progress_bar_wt_cache_mode.py b/test/functional/tests/progress_bar/test_progress_bar_wt_cache_mode.py new file mode 100644 index 0000000..f5ca2a9 --- /dev/null +++ b/test/functional/tests/progress_bar/test_progress_bar_wt_cache_mode.py @@ -0,0 +1,61 @@ +# +# Copyright(c) 2022 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest +from api.cas import casadm, cli +from api.cas.cache_config import CacheMode, CleaningPolicy, SeqCutOffPolicy +from api.cas.progress_bar import check_progress_bar +from core.test_run_utils import TestRun +from storage_devices.disk import DiskTypeSet, DiskType +from test_tools.disk_utils import Filesystem +from test_tools.fs_utils import create_random_test_file +from test_utils.size import Size, Unit + + +mount_point = "/mnt/test" +test_file = "/test_file" + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeSet([DiskType.hdd])) +def test_progress_bar_wt_cache_mode(): + """ + title: Progress bar validation for WT cache mode. + description: Validate the ability of the CAS to display data flushing progress + for Write-Through cache mode + pass_criteria: + - progress bar appear correctly + - progress only increase + """ + with TestRun.step("Prepare devices."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(5, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(20, Unit.GibiByte)] * 4) + core_devices = core_disk.partitions + + with TestRun.step("Start cache in Write-Back mode and add core."): + cache = casadm.start_cache(cache_dev, cache_mode=CacheMode.WB, force=True) + cores = [cache.add_core(dev) for dev in core_devices] + cache.set_cleaning_policy(CleaningPolicy.nop) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + + with TestRun.step("Make xfs filesystem on OpenCAS devices and mount it."): + for i, core in enumerate(cores): + core.create_filesystem(Filesystem.xfs) + core.mount(f"{mount_point}{i}") + + with TestRun.step("Create 2 GiB file on OpenCAS devices."): + for i, core in enumerate(cores): + create_random_test_file(f"{mount_point}{i}{test_file}", Size(2, Unit.GibiByte)) + + with TestRun.step("Change cache mode to Write-Through."): + cache.set_cache_mode(CacheMode.WT, flush=False) + + with TestRun.step("Run command and check progress."): + cmd = cli.flush_cache_cmd(str(cache.cache_id)) + check_progress_bar(cmd)