Project

General

Profile

Actions

Bug #42045

closed

unit test: lvm mocking isufficient

Added by Jan Fajerski over 4 years ago. Updated over 4 years ago.

Status:
Resolved
Priority:
Normal
Assignee:
-
Target version:
-
% Done:

0%

Source:
Tags:
Backport:
nautilus, mimic
Regression:
No
Severity:
3 - minor
Reviewed:
Affected Versions:
ceph-qa-suite:
Pull request ID:
Crash signature (v1):
Crash signature (v2):

Description

Test fail sometimes if lvm is not installed. We have mocks in place, but there is a hole somehwere:

self = <subprocess.Popen object at 0x7f7da9099898>
args = ['vgs', '--noheadings', '--readonly', '--units=g', '--separator=";"', '-o', ...]
executable = b'vgs', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = None, env = None, startupinfo = None, creationflags = 0, shell = False
p2cread = 12, p2cwrite = 13, c2pread = 14, c2pwrite = 15, errread = 16
errwrite = 17, restore_signals = True, start_new_session = False

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals, start_new_session):
        """Execute program (POSIX version)""" 

        if isinstance(args, (str, bytes)):
            args = [args]
        else:
            args = list(args)

        if shell:
            args = ["/bin/sh", "-c"] + args
            if executable:
                args[0] = executable

        if executable is None:
            executable = args[0]
        orig_executable = executable

        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description" 
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()

                if env is not None:
                    env_list = [os.fsencode(k) + b'=' + os.fsencode(v)
                                for k, v in env.items()]
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _posixsubprocess.fork_exec(
                        args, executable_list,
                        close_fds, sorted(fds_to_keep), cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session, preexec_fn)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)

            # self._devnull is not always defined.
            devnull_fd = getattr(self, '_devnull', None)
            if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd:
                os.close(p2cread)
            if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd:
                os.close(c2pwrite)
            if errwrite != -1 and errread != -1 and errwrite != devnull_fd:
                os.close(errwrite)
            if devnull_fd is not None:
                os.close(devnull_fd)
            # Prevent a double close of these fds from __init__ on error.
            self._closed_child_pipe_fds = True

            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)

        if errpipe_data:
            try:
                os.waitpid(self.pid, 0)
            except ChildProcessError:
                pass
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = (b'Bad exception data from child: ' +
                           repr(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            err_msg = err_msg.decode(errors="surrogatepass")
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                child_exec_never_called = (err_msg == "noexec")
                if child_exec_never_called:
                    err_msg = "" 
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                    if errno_num == errno.ENOENT:
                        if child_exec_never_called:
                            # The error must be from chdir(cwd).
                            err_msg += ': ' + repr(cwd)
                        else:
                            err_msg += ': ' + repr(orig_executable)
>               raise child_exception_type(errno_num, err_msg)
E               FileNotFoundError: [Errno 2] No such file or directory: 'vgs'

/usr/lib/python3.5/subprocess.py:1551: FileNotFoundError
----------------------------- Captured stderr call -----------------------------
--> Absolute path not found for executable: vgs
--> Ensure $PATH environment variable contains common executable locations
------------------------------ Captured log call -------------------------------
DEBUG    ceph_volume.util.prepare:prepare.py:94 block.db has no size configuration, will fallback to using as much as possible
DEBUG    ceph_volume.util.prepare:prepare.py:128 block.wal has no size configuration, will fallback to using as much as possible
WARNING  ceph_volume.util.system:terminal.py:170 Absolute path not found for executable: vgs
WARNING  ceph_volume.util.system:terminal.py:170 Ensure $PATH environment variable contains common executable locations
INFO     ceph_volume.process:process.py:191 Running command: vgs --noheadings --readonly --units=g --separator=";" -o vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free,vg_free_count

Related issues 2 (0 open2 closed)

Copied to ceph-volume - Backport #42534: mimic: unit test: lvm mocking isufficientResolvedNathan CutlerActions
Copied to ceph-volume - Backport #42535: nautilus: unit test: lvm mocking isufficientResolvedNathan CutlerActions
Actions #1

Updated by Jan Fajerski over 4 years ago

  • Status changed from New to Pending Backport
  • Backport set to nautilus, mimic
Actions #2

Updated by Jan Fajerski over 4 years ago

  • Copied to Backport #42534: mimic: unit test: lvm mocking isufficient added
Actions #3

Updated by Jan Fajerski over 4 years ago

  • Copied to Backport #42535: nautilus: unit test: lvm mocking isufficient added
Actions #4

Updated by Nathan Cutler over 4 years ago

  • Pull request ID set to 31197
Actions #5

Updated by Nathan Cutler over 4 years ago

  • Status changed from Pending Backport to Resolved

While running with --resolve-parent, the script "backport-create-issue" noticed that all backports of this issue are in status "Resolved" or "Rejected".

Actions

Also available in: Atom PDF