Virtuozzo QEMU development (svt-core@virtuozzo.com)
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox