Example #1
0
    def setUp(self):
        """Set up each test case."""
        # Start the servers and agents
        super(DataMoverTestBase, self).setUp()

        self.dfuse_hosts = self.agent_managers[0].hosts

        # Get the parameters for DataMover
        self.dm_cmd = DataMover(self.hostlist_clients)
        self.dm_cmd.get_params(self)
        self.processes = self.params.get("np", '/run/datamover/processes/*')
Example #2
0
    def run_dcp(self, src_pool=None, dst_pool=None, src_cont=None,
                dst_cont=None, update_dest=False):
        """Initialize and run DatamoverCommand object

          Args:
            src_pool(TestPool): source pool object
            dst_pool(TestPool): destination pool object
            src_cont(TestContainer): source container object
            dst_cont(TestContainer): destination container object
            update_dest(bool): Update destination path

          Raise:
            raises Commandfailure
        """
        # param for dcp processes
        processes = self.params.get("processes", "/run/datamover/*")

        dcp = DataMover(self.hostlist_clients, self.tmp)
        dcp.get_params(self)
        # update dest path
        if update_dest:
            dcp.dest_path.update(self.dfuse.mount_dir.value)
        # set datamover params
        dcp.set_datamover_params(src_pool, dst_pool, src_cont, dst_cont)

        try:
            # run dcp
            dcp.run(self.workdir, processes)

        except CommandFailure as error:
            self.log.error("DCP command failed: %s", str(error))
            self.fail("Test was expected to pass but it failed.\n")
Example #3
0
class DataMoverTestBase(IorTestBase):
    # pylint: disable=too-many-ancestors
    """Base DataMover test class.

    Sample Use Case:
        set_ior_location_and_run("DAOS_UUID", "/testFile, pool1, cont1,
                                 flags="-w -K")
        set_src_location("DAOS_UUID", "/testFile", pool1, cont1)
        set_dst_location("POSIX", "/some/posix/path/testFile")
        set_ior_location_and_run("POSIX", "/some/posix/path/testFile",
                                 flags="-r -R")
    :avocado: recursive
    """

    # The valid parameter types for setting param locations.
    PARAM_TYPES = ("POSIX", "DAOS_UUID", "DAOS_UNS")

    def __init__(self, *args, **kwargs):
        """Initialize a DataMoverTestBase object."""
        super(DataMoverTestBase, self).__init__(*args, **kwargs)
        self.dm_cmd = None
        self.processes = None
        self.pool = []
        self.containers = []
        self.uuids = []
        self._gen_daos_path_v = 0
        self.dfuse_hosts = None

    def setUp(self):
        """Set up each test case."""
        # Start the servers and agents
        super(DataMoverTestBase, self).setUp()

        self.dfuse_hosts = self.agent_managers[0].hosts

        # Get the parameters for DataMover
        self.dm_cmd = DataMover(self.hostlist_clients, self.tmp)
        self.dm_cmd.get_params(self)
        self.processes = self.params.get("np", '/run/datamover/processes/*')

        # List of test paths to create and remove
        self.posix_test_paths = []

    def pre_tear_down(self):
        """Tear down steps to run before tearDown().

        Returns:
            list: a list of error strings to report at the end of tearDown().

        """
        error_list = []
        # Remove the created directories
        if self.posix_test_paths:
            command = "rm -rf {}".format(self.get_posix_test_path_string())
            try:
                self._execute_command(command)
            except CommandFailure as error:
                error_list.append(
                    "Error removing created directories: {}".format(error))
        return error_list

    def get_posix_test_path_list(self):
        """Get a list of quoted posix test path strings.

        Returns:
            list: a list of quoted posix test path strings

        """
        return ["'{}'".format(item) for item in self.posix_test_paths]

    def get_posix_test_path_string(self):
        """Get a string of all of the quoted posix test path strings.

        Returns:
            str: a string of all of the quoted posix test path strings

        """
        return " ".join(self.get_posix_test_path_list())

    def validate_param_type(self, param_type):
        """Validates the param_type.

        It converts param_types to upper-case and handles shorthand types.

        Args:
            param_type (str): The param_type to be validated.

        Returns:
            (str) A valid param_type
        """
        _type = str(param_type).upper()
        if _type == "DAOS":
            return "DAOS_UUID"
        if _type in self.PARAM_TYPES:
            return _type
        self.fail("Invalid param_type: {}".format(_type))

    def create_pool(self):
        """Create a TestPool object.

        Returns:
            TestPool: the created pool

        """
        # Get the pool params
        pool = TestPool(self.context, dmg_command=self.get_dmg_command())
        pool.get_params(self)

        # Create the pool
        pool.create()

        # Save the pool and uuid
        self.pool.append(pool)
        self.uuids.append(str(pool.uuid))

        return pool

    def create_cont(self,
                    pool,
                    use_dfuse_uns=False,
                    dfuse_uns_pool=None,
                    dfuse_uns_cont=None):
        # pylint: disable=arguments-differ
        """Create a TestContainer object.

        Args:
            pool (TestPool): pool to create the container in.
            use_dfuse_uns (bool, optional): whether to create a
                UNS path in the dfuse mount.
                Default is False.
            dfuse_uns_pool (TestPool, optional): pool in the
                dfuse mount for which to create a UNS path.
                Default assumes dfuse is running for a specific pool.
            dfuse_uns_cont (TestContainer, optional): container in the
                dfuse mount for which to create a UNS path.
                Default assumes dfuse is running for a specific container.

        Returns:
            The TestContainer

        Note about uns path:
            These are only created within a dfuse mount.
            The full UNS path will be created as:
            <dfuse.mount_dir>/[pool_uuid]/[cont_uuid]/<dir_name>
            dfuse_uns_pool and dfuse_uns_cont should only be supplied
            when dfuse was not started for a specific pool/container.

        """
        # Get container params
        container = TestContainer(pool, daos_command=DaosCommand(self.bin))
        container.get_params(self)

        if use_dfuse_uns:
            path = str(self.dfuse.mount_dir.value)
            if dfuse_uns_pool:
                path = join(path, dfuse_uns_pool.uuid)
            if dfuse_uns_cont:
                path = join(path, dfuse_uns_cont.uuid)
            path = join(path, "uns{}".format(str(len(self.containers))))
            container.path.update(path)

        # Create container
        container.create()

        # Save container and uuid
        self.containers.append(container)
        self.uuids.append(str(container.uuid))

        return container

    def gen_uuid(self):
        """Generate a unique uuid."""
        new_uuid = str(uuid.uuid4())
        while new_uuid in self.uuids:
            new_uuid = str(uuid.uuid4())
        return new_uuid

    def gen_daos_path(self, prefix=None):
        """Returns the next unique container path.

        Args:
            prefix (str, optional): Path to prepend to the
                beginning of the new path.
                I.e. <prefix>/unique_dir
        """
        daos_dir = "dir{}".format(str(self._gen_daos_path_v))
        self._gen_daos_path_v += 1
        if prefix:
            return join(prefix, daos_dir)
        return join("/", daos_dir)

    @staticmethod
    def svcl_from_pool(pool):
        """Get the string svc for a pool."""
        if not hasattr(pool, "svc_ranks"):
            return None
        return ":".join(map(str, pool.svc_ranks))

    def set_src_location(self, *args, **kwargs):
        """Shorthand for set_location("src", ...)."""
        self.set_location("src", *args, **kwargs)

    def set_dst_location(self, *args, **kwargs):
        """Shorthand for set_location("dst", ...)."""
        self.set_location("dst", *args, **kwargs)

    def set_location(self,
                     src_or_dst,
                     param_type,
                     path,
                     pool=None,
                     cont=None,
                     display=True):
        """Set the src or dst params based on the location.

        Args:
            src_or_dst (str): set params for src or dst
            param_type (str): how to interpret the location
            path (str): posix-style path.
                For containers, this is relative to the container root
            pool (TestPool, optional): the pool object.
                Alternatively, this can the pool uuid,
                which ignores other pool attributes
            cont (TestContainer, optional): the container object.
                Alternatively, this can be the container uuid,
                which ignores other container attributes
            display (bool, optional): print updated params. Defaults to True.
        """
        assert src_or_dst in ["src", "dst"]  # nosec
        param_type = self.validate_param_type(param_type)

        # Get refs to either src or dst
        if src_or_dst == "src":
            dm_path = self.dm_cmd.src_path
            dm_pool = self.dm_cmd.daos_src_pool
            dm_svcl = self.dm_cmd.daos_src_svcl
            dm_cont = self.dm_cmd.daos_src_cont
            display_path = "src_path" if display else None
            display_pool = "daos_src_pool" if display else None
            display_svcl = "daos_src_svcl" if display else None
            display_cont = "daos_src_cont" if display else None
        elif src_or_dst == "dst":
            dm_path = self.dm_cmd.dest_path
            dm_pool = self.dm_cmd.daos_dst_pool
            dm_svcl = self.dm_cmd.daos_dst_svcl
            dm_cont = self.dm_cmd.daos_dst_cont
            display_path = "dest_path" if display else None
            display_pool = "daos_dst_pool" if display else None
            display_svcl = "daos_dst_svcl" if display else None
            display_cont = "daos_dst_cont" if display else None

        # Only a single prefix supported at this time.
        # When needing to use a prefix, the last call
        # to this function will determine the prefix value.
        dm_prefix = self.dm_cmd.daos_prefix
        display_prefix = "daos_prefix" if display else None

        # Reset params
        dm_path.update(None)
        dm_pool.update(None)
        dm_svcl.update(None)
        dm_cont.update(None)
        dm_prefix.update(None)

        # Allow cont to be either the container or the uuid
        cont_uuid = cont.uuid if hasattr(cont, "uuid") else cont

        # Allow pool to be either the pool or the uuid
        if hasattr(pool, "uuid"):
            pool_uuid = pool.uuid
            pool_svcl = self.svcl_from_pool(pool)
        else:
            pool_uuid = pool
            pool_svcl = None

        if param_type == "POSIX":
            dm_path.update(path, display_path)
        elif param_type == "DAOS_UUID":
            dm_path.update(path, display_path)
            if pool_uuid:
                dm_pool.update(pool_uuid, display_pool)
            if pool_svcl:
                dm_svcl.update(pool_svcl, display_svcl)
            if cont_uuid:
                dm_cont.update(cont_uuid, display_cont)
        elif param_type == "DAOS_UNS":
            if cont:
                if path == "/":
                    dm_path.update(cont.path, display_path)
                else:
                    dm_prefix.update(cont.path, display_prefix)
                    dm_path.update(str(cont.path) + path, display_path)
            if pool_svcl:
                dm_svcl.update(pool_svcl, display_svcl)

    def set_ior_location(self,
                         param_type,
                         path,
                         pool=None,
                         cont=None,
                         path_suffix=None,
                         display=True):
        """Set the ior params based on the location.

        Args:
            param_type (str): how to interpret the location
            path (str): posix-style path.
                For containers, this is relative to the container root
            pool (TestPool, optional): the pool object
            cont (TestContainer, optional): the container object.
                Alternatively, this can be the container uuid
            path_suffix (str, optional): suffix to append to the path.
                E.g. path="/some/path", path_suffix="testFile"
            display (bool, optional): print updated params. Defaults to True.
        """
        param_type = self.validate_param_type(param_type)

        # Reset params
        self.ior_cmd.api.update(None)
        self.ior_cmd.test_file.update(None)
        self.ior_cmd.dfs_pool.update(None)
        self.ior_cmd.dfs_cont.update(None)
        self.ior_cmd.dfs_group.update(None)
        self.ior_cmd.dfs_svcl.update(None)

        display_api = "api" if display else None
        display_test_file = "test_file" if display else None

        # Allow cont to be either the container or the uuid
        cont_uuid = cont.uuid if hasattr(cont, "uuid") else cont

        # Optionally append suffix
        if path_suffix:
            if path_suffix[0] == "/":
                path_suffix = path_suffix[1:]
            path = join(path, path_suffix)

        if param_type == "POSIX":
            self.ior_cmd.api.update("POSIX", display_api)
            self.ior_cmd.test_file.update(path, display_test_file)
        elif param_type in ("DAOS_UUID", "DAOS_UNS"):
            self.ior_cmd.api.update("DFS", display_api)
            self.ior_cmd.test_file.update(path, display_test_file)
            if pool and cont_uuid:
                self.ior_cmd.set_daos_params(self.server_group, pool,
                                             cont_uuid)
            elif pool:
                self.ior_cmd.set_daos_params(self.server_group, pool, None)

    def set_ior_location_and_run(self,
                                 param_type,
                                 path,
                                 pool=None,
                                 cont=None,
                                 path_suffix=None,
                                 flags=None,
                                 display=True):
        """Set the ior params based on the location and run ior with some flags.

        Args:
            param_type: see set_ior_location
            path: see set_ior location
            pool: see set_ior location
            cont: see set_ior location
            path_suffix: see set_ior location
            flags (str, optional): ior_cmd flags to set
            display (bool, optional): print updated params. Defaults to True.
        """
        self.set_ior_location(param_type, path, pool, cont, path_suffix)
        if flags:
            self.ior_cmd.flags.update(flags, "flags" if display else None)
        self.run_ior(self.get_ior_job_manager_command(),
                     self.processes,
                     display_space=(True if pool else False),
                     pool=pool)

    def run_datamover(self,
                      test_desc=None,
                      expected_rc=0,
                      expected_output=None,
                      processes=None):
        """Run the DataMover command.

        Currently, this only uses "dcp".

        Args:
            test_desc (str, optional): description to print before running
            expected_rc (int, optional): rc expected to be returned
            expected_output (list, optional): substrings expected to be output
            processes (int, optional): number of mpi processes.
                defaults to self.processes

        Returns:
            The result "run" object

        """
        if not processes:
            processes = self.processes

        # Default expected_output to empty list
        if not expected_output:
            expected_output = []

        # Convert singular value to list
        if not isinstance(expected_output, list):
            expected_output = [expected_output]

        if test_desc is not None:
            self.log.info("Running DCP: %s", test_desc)

        # If we expect an rc other than 0, don't fail
        self.dm_cmd.exit_status_exception = (expected_rc == 0)

        try:
            result = self.dm_cmd.run(self.workdir, processes)
        except CommandFailure as error:
            self.log.error("DCP command failed: %s", str(error))
            self.fail("Test was expected to pass but it failed: {}\n".format(
                test_desc))

        # Check the return code
        actual_rc = result.exit_status
        if actual_rc != expected_rc:
            self.fail("Expected (rc={}) but got (rc={}): {}\n".format(
                expected_rc, actual_rc, test_desc))

        # Check for expected output
        for s in expected_output:
            if s not in result.stdout:
                self.fail("Expected {}: {}".format(s, test_desc))

        return result