diff --git a/test/functional/tests/incremental_load/test_inactive_cores.py b/test/functional/tests/incremental_load/test_inactive_cores.py index 5dc6af1..57ef360 100644 --- a/test/functional/tests/incremental_load/test_inactive_cores.py +++ b/test/functional/tests/incremental_load/test_inactive_cores.py @@ -1,13 +1,15 @@ # # Copyright(c) 2019-2021 Intel Corporation +# Copyright(c) 2024 Huawei Technologies Co., Ltd. # SPDX-License-Identifier: BSD-3-Clause # - import pytest from api.cas import casadm from api.cas.cache_config import CacheMode +from api.cas.casadm_parser import get_cas_devices_dict +from api.cas.core import Core, CoreStatus from core.test_run import TestRun from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan from test_tools.dd import Dd @@ -16,152 +18,238 @@ from test_utils.size import Size, Unit @pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) @pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) -def test_core_inactive(): +def test_core_inactive_stats_conf(): """ - 1. Start cache with 3 cores. - 2. Stop cache. - 3. Remove one of core devices. - 4. Load cache. - 5. Check if cache has appropriate number of valid and inactive core devices. + title: Test for inactive core configuration statistics. + description: | + Test the cache inactive core configuration statistics after removing one of core devices + and loading cache. + pass_criteria: + - Cache can be loaded with inactive core device. + - CAS correctly reports inactive core statistics in cache configuration statistics after + loading cache. """ - cache, core_device = prepare() + core_number = 3 - cache_device = cache.cache_device - stats = cache.get_statistics() + with TestRun.step("Prepare cache and core devices"): + cache_device = TestRun.disks["cache"] + core_device = TestRun.disks["core"] - assert stats.config_stats.core_dev == 3 - assert stats.config_stats.inactive_core_dev == 0 + cache_device.create_partitions([Size(500, Unit.MebiByte)]) + core_device.create_partitions([Size(1, Unit.GibiByte)] * core_number) - TestRun.LOGGER.info("Stopping cache") - cache.stop() + cache_device = cache_device.partitions[0] + core_device_partitions = core_device.partitions - TestRun.LOGGER.info("Removing one of core devices") - core_device.remove_partitions() - core_device.create_partitions([Size(1, Unit.GibiByte), Size(1, Unit.GibiByte)]) + with TestRun.step("Start cache"): + cache = casadm.start_cache(cache_device, force=True) - TestRun.LOGGER.info("Loading cache with missing core device") - cache = casadm.start_cache(cache_device, load=True) - stats = cache.get_statistics() + with TestRun.step("Add cores to the cache"): + for core_device_part in core_device_partitions: + cache.add_core(core_dev=core_device_part) - assert stats.config_stats.core_dev == 3 - assert stats.config_stats.inactive_core_dev == 1 + with TestRun.step("Check if correct number of inactive cores is displayed in cache statistics"): + stats = cache.get_statistics() + if stats.config_stats.inactive_core_devices != 0: + TestRun.fail("Inactive core in statistics after starting cache") + + with TestRun.step("Stop cache"): + cache.stop() + + with TestRun.step("Remove last core device"): + core_device.remove_partition(part=core_device_partitions[-1]) + + with TestRun.step("Load cache with missing core device"): + cache = casadm.start_cache(cache_device, load=True) + + with TestRun.step( + "Check if correct number of cores and inactive cores is displayed in cache statistics" + ): + stats = cache.get_statistics() + if stats.config_stats.core_dev != core_number: + TestRun.fail( + "Wrong number of cores after loading the cache\n" + f"Actual number of cores: {stats.config_stats.core_dev}\n" + f"Expected number of cores: {core_number}" + ) + if stats.config_stats.inactive_core_devices != 1: + TestRun.fail( + "Wrong number of inactive cores after loading the cache\n" + f"Actual number of inactive cores: {stats.config_stats.inactive_core_devices}\n" + "Expected number of inactive cores: 1" + ) @pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) @pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) -def test_core_inactive_stats(): +def test_core_inactive_stats_usage(): """ - 1. Start cache with 3 cores. - 2. Switch cache into WB mode. - 3. Issue IO to each core. - 4. Stop cache without flush. - 5. Remove two core devices. - 6. Load cache. - 7. Check if cache stats are equal to sum of valid and inactive cores stats. - 8. Check if percentage values are calculated properly. + title: Test for inactive core usage statistics. + description: | + Test the cache inactive core usage statistics after removing one of core devices and loading + cache. + pass_criteria: + - Cache can be loaded with inactive core device. + - CAS correctly reports inactive core statistics in cache usage statistics after loading + cache. """ - cache, core_device = prepare() - cache_device = cache.cache_device + core_number = 3 - TestRun.LOGGER.info("Switching cache mode to WB") - cache.set_cache_mode(cache_mode=CacheMode.WB) - cores = cache.get_core_devices() - TestRun.LOGGER.info("Issue IO to each core") - for core in cores: - dd = ( - Dd() - .input("/dev/zero") - .output(core.path) - .count(1000) - .block_size(Size(4, Unit.KibiByte)) - ).run() + with TestRun.step("Prepare cache and core devices"): + cache_device = TestRun.disks["cache"] + core_device = TestRun.disks["core"] - TestRun.LOGGER.info("Stopping cache with dirty data") - cores[2].flush_core() - cache.stop(no_data_flush=True) + cache_device.create_partitions([Size(500, Unit.MebiByte)]) + core_device.create_partitions([Size(1, Unit.GibiByte)] * core_number) - TestRun.LOGGER.info("Removing two of core devices") - core_device.remove_partitions() - core_device.create_partitions([Size(1, Unit.GibiByte)]) + cache_device = cache_device.partitions[0] + core_device_partitions = core_device.partitions - TestRun.LOGGER.info("Loading cache with missing core device") - cache = casadm.start_cache(cache_device, load=True) + with TestRun.step("Start cache"): + cache = casadm.start_cache(cache_device, force=True, cache_mode=CacheMode.WB) - # Accumulate valid cores stats - cores_occupancy = 0 - cores_clean = 0 - cores_dirty = 0 - cores = cache.get_core_devices() - for core in cores: - core_stats = core.get_statistics() - cores_occupancy += core_stats.usage_stats.occupancy.value - cores_clean += core_stats.usage_stats.clean.value - cores_dirty += core_stats.usage_stats.dirty.value + with TestRun.step("Add cores to the cache"): + core_list = [ + cache.add_core(core_dev=core_device_part) for core_device_part in core_device_partitions + ] - cache_stats = cache.get_statistics() - # Add inactive core stats - cores_occupancy += cache_stats.inactive_usage_stats.inactive_occupancy.value - cores_clean += cache_stats.inactive_usage_stats.inactive_clean.value - cores_dirty += cache_stats.inactive_usage_stats.inactive_dirty.value + with TestRun.step("Run I/O to each core"): + for core in core_list: + dd = ( + Dd() + .input("/dev/zero") + .output(core.path) + .count(1000) + .block_size(Size(4, Unit.KibiByte)) + ) + dd.run() - assert cache_stats.usage_stats.occupancy.value == cores_occupancy - assert cache_stats.usage_stats.dirty.value == cores_dirty - assert cache_stats.usage_stats.clean.value == cores_clean + with TestRun.step("Flush last core"): + core_list[-1].flush_core() - cache_stats_percentage = cache.get_statistics(percentage_val=True) - # Calculate expected percentage value of inactive core stats - inactive_occupancy_perc = ( - cache_stats.inactive_usage_stats.inactive_occupancy.value - / cache_stats.config_stats.cache_size.value - ) - inactive_clean_perc = ( - cache_stats.inactive_usage_stats.inactive_clean.value - / cache_stats.usage_stats.occupancy.value - ) - inactive_dirty_perc = ( - cache_stats.inactive_usage_stats.inactive_dirty.value - / cache_stats.usage_stats.occupancy.value - ) + with TestRun.step("Stop cache with dirty data"): + cache.stop(no_data_flush=True) - inactive_occupancy_perc = round(100 * inactive_occupancy_perc, 1) - inactive_clean_perc = round(100 * inactive_clean_perc, 1) - inactive_dirty_perc = round(100 * inactive_dirty_perc, 1) + with TestRun.step("Removing two of core devices"): + core_device.remove_partition(part=core_device_partitions[0]) + core_device.remove_partition(part=core_device_partitions[1]) - TestRun.LOGGER.info(str(cache_stats_percentage)) - assert ( - inactive_occupancy_perc - == cache_stats_percentage.inactive_usage_stats.inactive_occupancy - ) - assert ( - inactive_clean_perc - == cache_stats_percentage.inactive_usage_stats.inactive_clean - ) - assert ( - inactive_dirty_perc - == cache_stats_percentage.inactive_usage_stats.inactive_dirty - ) + with TestRun.step("Load cache with missing core devices"): + cache = casadm.start_cache(cache_device, load=True) + + with TestRun.step("Check cores statistics"): + active_cores_occupancy_stats = 0 + active_cores_clean_stats = 0 + active_cores_dirty_stats = 0 + + active_cores = cache.get_core_devices() + for core in active_cores: + core_stats = core.get_statistics() + active_cores_occupancy_stats += core_stats.usage_stats.occupancy + active_cores_clean_stats += core_stats.usage_stats.clean + active_cores_dirty_stats += core_stats.usage_stats.dirty + + inactive_cores_occupancy_stats = 0 + inactive_cores_clean_stats = 0 + inactive_cores_dirty_stats = 0 + + inactive_cores = get_inactive_cores(cache_id=cache.cache_id) + for core in inactive_cores: + core_stats = core.get_statistics() + inactive_cores_occupancy_stats += core_stats.usage_stats.occupancy + inactive_cores_clean_stats += core_stats.usage_stats.clean + inactive_cores_dirty_stats += core_stats.usage_stats.dirty + + cache_stats = cache.get_statistics() + cache_usage_stats = cache_stats.usage_stats + + total_cores_occupancy_stats = active_cores_occupancy_stats + inactive_cores_occupancy_stats + total_cores_dirty_stats = active_cores_dirty_stats + inactive_cores_dirty_stats + total_cores_clean_stats = active_cores_clean_stats + inactive_cores_clean_stats + + if cache_usage_stats.occupancy != total_cores_occupancy_stats: + TestRun.LOGGER.error( + "Wrong number of occupancy blocks in cache usage stats\n" + f"Actual number of occupancy blocks: {cache_usage_stats.occupancy}\n" + f"Expected number of occupancy blocks: {total_cores_occupancy_stats}" + ) + if cache_usage_stats.dirty != total_cores_dirty_stats: + TestRun.LOGGER.error( + "Wrong number of dirty blocks in cache usage stats\n" + f"Actual number of dirty blocks: {cache_usage_stats.dirty}\n" + f"Expected number of dirty blocks: {total_cores_dirty_stats}" + ) + if cache_usage_stats.clean != total_cores_clean_stats: + TestRun.LOGGER.error( + "Wrong number of clean blocks in cache usage stats\n" + f"Actual number of clean blocks: {cache_usage_stats.clean}\n" + f"Expected number of clean blocks: {total_cores_clean_stats}" + ) + if cache_usage_stats.inactive_occupancy != inactive_cores_occupancy_stats: + TestRun.LOGGER.error( + "Wrong number of occupancy blocks in inactive cache usage stats\n" + f"Actual number of occupancy blocks: {cache_usage_stats.inactive_occupancy}\n" + f"Expected number of occupancy blocks: {inactive_cores_occupancy_stats}" + ) + if cache_usage_stats.inactive_dirty != inactive_cores_dirty_stats: + TestRun.LOGGER.error( + "Wrong number of dirty blocks in cache inactive usage stats\n" + f"Actual number of dirty blocks: {cache_usage_stats.inactive_dirty}\n" + f"Expected number of dirty blocks: {inactive_cores_dirty_stats}" + ) + if cache_usage_stats.inactive_clean != inactive_cores_clean_stats: + TestRun.LOGGER.error( + "Wrong number of clean blocks in cache inactive usage stats\n" + f"Actual number of clean blocks: {cache_usage_stats.inactive_clean}\n" + f"Expected number of clean blocks: {inactive_cores_clean_stats}" + ) + + cache_usage_stats_percentage = cache.get_statistics(percentage_val=True).usage_stats + + # Calculate expected percentage value of inactive core stats + inactive_occupancy_perc = round( + 100 * (cache_usage_stats.inactive_occupancy / cache_stats.config_stats.cache_size), 1 + ) + inactive_dirty_perc = round( + 100 * (cache_usage_stats.inactive_dirty / cache_stats.usage_stats.occupancy), 1 + ) + inactive_clean_perc = round( + 100 * (cache_usage_stats.inactive_clean / cache_stats.usage_stats.occupancy), 1 + ) + + if cache_usage_stats_percentage.inactive_occupancy != inactive_occupancy_perc: + TestRun.LOGGER.error( + "Wrong occupancy blocks percentage in usage stats\n" + f"Actual number of occupancy blocks percentage:" + f" {cache_usage_stats_percentage.inactive_occupancy}\n" + f"Expected number of occupancy blocks percentage: {inactive_occupancy_perc}" + ) + if cache_usage_stats_percentage.inactive_dirty != inactive_dirty_perc: + TestRun.LOGGER.error( + "Wrong dirty blocks percentage in usage stats\n " + "Actual number of dirty blocks percentage: " + f"{cache_usage_stats_percentage.inactive_dirty}\n" + f"Expected number of dirty blocks percentage: {inactive_dirty_perc}" + ) + if cache_usage_stats_percentage.inactive_clean != inactive_clean_perc: + TestRun.LOGGER.error( + "Wrong clean blocks percentage in usage stats\n" + "Actual number of clean blocks percentage: " + f"{cache_usage_stats.inactive_clean}\n" + f"Expected number of clean blocks percentage: {inactive_cores_clean_stats}" + ) -def prepare(): - cache_device = TestRun.disks["cache"] - core_device = TestRun.disks["core"] +def get_inactive_cores(cache_id: int) -> list: + cores_dict = get_cas_devices_dict()["cores"].values() - cache_device.create_partitions([Size(500, Unit.MebiByte)]) - core_device.create_partitions( - [Size(1, Unit.GibiByte), Size(1, Unit.GibiByte), Size(1, Unit.GibiByte)] - ) + def is_active(core): - cache_device = cache_device.partitions[0] - core_device_1 = core_device.partitions[0] - core_device_2 = core_device.partitions[1] - core_device_3 = core_device.partitions[2] + return CoreStatus[core["status"].lower()] == CoreStatus.inactive - TestRun.LOGGER.info("Staring cache") - cache = casadm.start_cache(cache_device, force=True) - TestRun.LOGGER.info("Adding core device") - core_1 = cache.add_core(core_dev=core_device_1) - core_2 = cache.add_core(core_dev=core_device_2) - core_3 = cache.add_core(core_dev=core_device_3) - - return cache, core_device + return [ + Core(core["device_path"], core["cache_id"]) + for core in cores_dict + if is_active(core) and core["cache_id"] == cache_id + ]