All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: no-reply@sashiko.virtuozzo.com
To: "Denis V. Lunev" <den@openvz.org>, svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, den@virtuozzo.com
Subject: Re: [PATCH 3/3] iotests/migrate-cpr-exec: cover chardevs with logfile= #VSTOR-148304
Date: Fri, 2 Oct 2026 16:55:54 +0000	[thread overview]
Message-ID: <010701a0fd8b38a5-970cf8c7-2955-41de-a1f2-b0b0994d7d1f-000000@eu-central-1.amazonses.com> (raw)
In-Reply-To: <20261002164853.2130337-4-den@openvz.org>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Low] iotests: socket logfile test leaves stale path=sock1 in exec command line
- [Low] iotests: assertFalse on BAD_LOG parent dir cannot detect an attempted logfile open
- [Low] iotests: _open_files() compares /proc readlink results to non-canonical paths
- [Low] iotests: pre_cpr rewrite drops logappend=on from the console chardev argument
--

commit 8a854fc4d41646d884346abb08e5af9be8f46b16
Author: Denis V. Lunev <den@openvz.org>

iotests/migrate-cpr-exec: cover chardevs with logfile= #VSTOR-148304

This adds four tests to migrate-cpr-exec. They cover a pty console, a
guest agent socket and a second QMP monitor, each with a log, through
cpr-exec with logfile paths that cannot be created. They also cover
chardev-change to a new log for pty and socket, and a failed
chardev-change whose rollback must keep the old log.

> diff --git a/tests/qemu-iotests/tests/migrate-cpr-exec b/tests/qemu-iotests/tests/migrate-cpr-exec
> index 51e6cbcd6593..e8a6c502c840 100755
> --- a/tests/qemu-iotests/tests/migrate-cpr-exec
> +++ b/tests/qemu-iotests/tests/migrate-cpr-exec

[ ... ]

> +    def _open_files(self) -> set:
> +        fd_dir = f'/proc/{self.vm.get_pid()}/fd'
> +        return {os.readlink(os.path.join(fd_dir, fd))
> +                for fd in os.listdir(fd_dir)}

[Severity: Low]
Can this comparison fail when TEST_DIR is not canonical?

os.readlink() on /proc/<pid>/fd/N returns the kernel's resolved path. The
expected paths come from _log_path() as os.path.join(iotests.test_dir, name),
and they are not passed through os.path.realpath().

If TEST_DIR is reached through a symlink, this assertion fails even though
the fds were preserved:

    self.assertLessEqual({agent_log, mon_log}, self._open_files())

In the same case, this assertion in _test_chardev_change_logfile() passes
vacuously, so it no longer checks that the old log fd was dropped:

    self.assertNotIn(log_old, self._open_files())

I did not confirm how iotests normalizes test_dir. Would applying
os.path.realpath() to the expected paths avoid the mismatch?

[ ... ]

> +        def pre_cpr():
> +            self._serial_write('before\n')
> +            for c in (console, agent, mon):
> +                self._replace_arg(c, c.replace(c[c.index('logfile='):],
> +                                               f'logfile={self.BAD_LOG}'))

[Severity: Low]
Does this rewrite drop logappend=on from the console chardev argument?

For the console chardev, c[c.index('logfile='):] is
'logfile=/dev/fdset/1,logappend=on'. The whole tail is replaced, so the new
argument becomes:

    pty,id=charserial0,logfile=<BAD_LOG>

The agent and mon arguments have logfile= last, so they are unaffected.

The combination of logappend=on with an fd-backed preserved log is
therefore not exercised after the exec. This is the combination the commit
message says libvirt sends. Today qemu_char_open() does not use the
computed flags when cpr_exec_is_incoming() is true, so the test passes. A
later change that consults logappend on the restore path would not be
caught.

Would it be better to replace only the path value and keep ",logappend=on"?

> +            args = list(self.vm._qemu_full_args)
> +            i = args.index('-add-fd')
> +            self.vm._qemu_full_args = tuple(args[:i] + args[i + 2:])
> +
> +        def post_cpr():
> +            self._serial_write('after\n')
> +            self.assertEqual(self._read(console_log), 'before\nafter\n')
> +            self.assertLessEqual({agent_log, mon_log}, self._open_files())
> +            self.assertFalse(os.path.exists(os.path.dirname(self.BAD_LOG)))

[Severity: Low]
Can this assertion ever fail?

BAD_LOG is <test_dir>/nonexistent/chr.log, and nothing creates the
"nonexistent" directory. qemu_create() called from qemu_char_open() does
not create parent directories. If a regressed QEMU tried to open the path,
it would get ENOENT and the directory would stay absent, so
assertFalse(os.path.exists(os.path.dirname(self.BAD_LOG))) always holds.

The real evidence that the path is not opened is that the new QEMU starts
at all. The docstring ("the new logfile= paths are never opened") and the
commit message suggest this assertion adds coverage that it does not.

Could the assertion be dropped, or replaced with a check that can actually
fail?

[ ... ]

> +    def test_chardev_change_logfile_socket(self) -> None:
> +        sock1 = iotests.file_path('chr-log-sock1')
> +        sock2 = iotests.file_path('chr-log-sock2')
> +        self._test_chardev_change_logfile(
> +            'socket',
> +            {'addr': {'type': 'unix', 'data': {'path': sock2}},
> +             'server': True, 'wait': False},
> +            f'path={sock1},server=on,wait=off,')

[Severity: Low]
This isn't a bug, but the rewritten exec command line for the socket case
looks inconsistent with the live chardev.

In _test_chardev_change_logfile(), change() calls:

    self._replace_arg(arg, f'{backend},id=char0,{opts_old}'
                           f'logfile={self.BAD_LOG}')

For the socket case opts_old is 'path=<sock1>,server=on,wait=off,'. After
chardev-change the live chardev listens on sock2, so the new command line
still names sock1, which is no longer in use.

The existing test_chardev_change_socket_to_socket updates the path to
sock2. The test passes here because chr_socket_cpr_restore_addr_fd() in
chardev/char-socket.c uses the inherited fd and not the path. As written,
the test does not model what a management layer would send. It would also
not catch a regression that used the path instead of the fd.

Should the rewritten argument use sock2 for the path?

[ ... ]

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261002164853.2130337-1-den@openvz.org?part=3

  reply	other threads:[~2026-10-02 16:56 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02 16:48 [PATCH 0/3] chardev: fix qemu-update destroying VMs with a console log #VSTOR-148304 Denis V. Lunev
2026-10-02 16:48 ` [PATCH 1/3] chardev: implement cpr_claim_fds() in the base class #VSTOR-148304 Denis V. Lunev
2026-10-02 16:50   ` no-reply
2026-10-05  9:05   ` Andrey Drobyshev
2026-10-02 16:48 ` [PATCH 2/3] chardev: preserve the logfile fd across cpr-exec #VSTOR-148304 Denis V. Lunev
2026-10-02 16:54   ` no-reply
2026-10-05  9:05   ` Andrey Drobyshev
2026-10-02 16:48 ` [PATCH 3/3] iotests/migrate-cpr-exec: cover chardevs with logfile= #VSTOR-148304 Denis V. Lunev
2026-10-02 16:55   ` no-reply [this message]
2026-10-05  9:05   ` Andrey Drobyshev

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=010701a0fd8b38a5-970cf8c7-2955-41de-a1f2-b0b0994d7d1f-000000@eu-central-1.amazonses.com \
    --to=no-reply@sashiko.virtuozzo.com \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=den@openvz.org \
    --cc=den@virtuozzo.com \
    --cc=svt-core@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.