python/machine: remove has_quit argument
If we spy on the QMP commands instead, we don't need callers to remember to pass it. Seems like a fair trade-off. The one slightly weird bit is overloading this instance variable for wait(), where we use it to mean "don't issue the qmp 'quit' command". This means that wait() will "fail" if the QEMU process does not terminate of its own accord. In most cases, we probably did already actually issue quit -- some iotests do this -- but in some others, we may be waiting for QEMU to terminate for some other reason, such as a test wherein we tell the guest (directly) to shut down. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Hanna Reitz <hreitz@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-id: 20211026175612.4127598-2-jsnow@redhat.com Signed-off-by: John Snow <jsnow@redhat.com>
This commit is contained in:
parent
461044ceb4
commit
b9420e4f4b
@ -170,6 +170,7 @@ class QEMUMachine:
|
||||
self._console_socket: Optional[socket.socket] = None
|
||||
self._remove_files: List[str] = []
|
||||
self._user_killed = False
|
||||
self._quit_issued = False
|
||||
|
||||
def __enter__(self: _T) -> _T:
|
||||
return self
|
||||
@ -368,6 +369,7 @@ class QEMUMachine:
|
||||
command = ''
|
||||
LOG.warning(msg, -int(exitcode), command)
|
||||
|
||||
self._quit_issued = False
|
||||
self._user_killed = False
|
||||
self._launched = False
|
||||
|
||||
@ -443,15 +445,13 @@ class QEMUMachine:
|
||||
self._subp.kill()
|
||||
self._subp.wait(timeout=60)
|
||||
|
||||
def _soft_shutdown(self, timeout: Optional[int],
|
||||
has_quit: bool = False) -> None:
|
||||
def _soft_shutdown(self, timeout: Optional[int]) -> None:
|
||||
"""
|
||||
Perform early cleanup, attempt to gracefully shut down the VM, and wait
|
||||
for it to terminate.
|
||||
|
||||
:param timeout: Timeout in seconds for graceful shutdown.
|
||||
A value of None is an infinite wait.
|
||||
:param has_quit: When True, don't attempt to issue 'quit' QMP command
|
||||
|
||||
:raise ConnectionReset: On QMP communication errors
|
||||
:raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
|
||||
@ -460,21 +460,19 @@ class QEMUMachine:
|
||||
self._early_cleanup()
|
||||
|
||||
if self._qmp_connection:
|
||||
if not has_quit:
|
||||
if not self._quit_issued:
|
||||
# Might raise ConnectionReset
|
||||
self._qmp.cmd('quit')
|
||||
self.qmp('quit')
|
||||
|
||||
# May raise subprocess.TimeoutExpired
|
||||
self._subp.wait(timeout=timeout)
|
||||
|
||||
def _do_shutdown(self, timeout: Optional[int],
|
||||
has_quit: bool = False) -> None:
|
||||
def _do_shutdown(self, timeout: Optional[int]) -> None:
|
||||
"""
|
||||
Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
|
||||
|
||||
:param timeout: Timeout in seconds for graceful shutdown.
|
||||
A value of None is an infinite wait.
|
||||
:param has_quit: When True, don't attempt to issue 'quit' QMP command
|
||||
|
||||
:raise AbnormalShutdown: When the VM could not be shut down gracefully.
|
||||
The inner exception will likely be ConnectionReset or
|
||||
@ -482,13 +480,13 @@ class QEMUMachine:
|
||||
may result in its own exceptions, likely subprocess.TimeoutExpired.
|
||||
"""
|
||||
try:
|
||||
self._soft_shutdown(timeout, has_quit)
|
||||
self._soft_shutdown(timeout)
|
||||
except Exception as exc:
|
||||
self._hard_shutdown()
|
||||
raise AbnormalShutdown("Could not perform graceful shutdown") \
|
||||
from exc
|
||||
|
||||
def shutdown(self, has_quit: bool = False,
|
||||
def shutdown(self,
|
||||
hard: bool = False,
|
||||
timeout: Optional[int] = 30) -> None:
|
||||
"""
|
||||
@ -498,7 +496,6 @@ class QEMUMachine:
|
||||
If the VM has not yet been launched, or shutdown(), wait(), or kill()
|
||||
have already been called, this method does nothing.
|
||||
|
||||
:param has_quit: When true, do not attempt to issue 'quit' QMP command.
|
||||
:param hard: When true, do not attempt graceful shutdown, and
|
||||
suppress the SIGKILL warning log message.
|
||||
:param timeout: Optional timeout in seconds for graceful shutdown.
|
||||
@ -512,7 +509,7 @@ class QEMUMachine:
|
||||
self._user_killed = True
|
||||
self._hard_shutdown()
|
||||
else:
|
||||
self._do_shutdown(timeout, has_quit)
|
||||
self._do_shutdown(timeout)
|
||||
finally:
|
||||
self._post_shutdown()
|
||||
|
||||
@ -529,7 +526,8 @@ class QEMUMachine:
|
||||
:param timeout: Optional timeout in seconds. Default 30 seconds.
|
||||
A value of `None` is an infinite wait.
|
||||
"""
|
||||
self.shutdown(has_quit=True, timeout=timeout)
|
||||
self._quit_issued = True
|
||||
self.shutdown(timeout=timeout)
|
||||
|
||||
def set_qmp_monitor(self, enabled: bool = True) -> None:
|
||||
"""
|
||||
@ -574,7 +572,10 @@ class QEMUMachine:
|
||||
conv_keys = True
|
||||
|
||||
qmp_args = self._qmp_args(conv_keys, args)
|
||||
return self._qmp.cmd(cmd, args=qmp_args)
|
||||
ret = self._qmp.cmd(cmd, args=qmp_args)
|
||||
if cmd == 'quit' and 'error' not in ret and 'return' in ret:
|
||||
self._quit_issued = True
|
||||
return ret
|
||||
|
||||
def command(self, cmd: str,
|
||||
conv_keys: bool = True,
|
||||
@ -585,7 +586,10 @@ class QEMUMachine:
|
||||
On failure raise an exception.
|
||||
"""
|
||||
qmp_args = self._qmp_args(conv_keys, args)
|
||||
return self._qmp.command(cmd, **qmp_args)
|
||||
ret = self._qmp.command(cmd, **qmp_args)
|
||||
if cmd == 'quit':
|
||||
self._quit_issued = True
|
||||
return ret
|
||||
|
||||
def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
|
||||
"""
|
||||
|
@ -92,10 +92,9 @@ class TestSingleDrive(ImageCommitTestCase):
|
||||
self.vm.add_device('virtio-scsi')
|
||||
self.vm.add_device("scsi-hd,id=scsi0,drive=drive0")
|
||||
self.vm.launch()
|
||||
self.has_quit = False
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown(has_quit=self.has_quit)
|
||||
self.vm.shutdown()
|
||||
os.remove(test_img)
|
||||
os.remove(mid_img)
|
||||
os.remove(backing_img)
|
||||
@ -127,8 +126,6 @@ class TestSingleDrive(ImageCommitTestCase):
|
||||
result = self.vm.qmp('quit')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
self.has_quit = True
|
||||
|
||||
# Same as above, but this time we add the filter after starting the job
|
||||
@iotests.skip_if_unsupported(['throttle'])
|
||||
def test_commit_plus_filter_and_quit(self):
|
||||
@ -147,8 +144,6 @@ class TestSingleDrive(ImageCommitTestCase):
|
||||
result = self.vm.qmp('quit')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
self.has_quit = True
|
||||
|
||||
def test_device_not_found(self):
|
||||
result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img)
|
||||
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
|
||||
|
@ -187,4 +187,4 @@ with iotests.VM() as vm, \
|
||||
log(vm.qmp('quit'))
|
||||
|
||||
with iotests.Timeout(5, 'Timeout waiting for VM to quit'):
|
||||
vm.shutdown(has_quit=True)
|
||||
vm.shutdown()
|
||||
|
@ -123,4 +123,4 @@ with iotests.FilePath('src.qcow2') as src_path, \
|
||||
vm.qmp_log('block-job-cancel', device='job0')
|
||||
vm.qmp_log('quit')
|
||||
|
||||
vm.shutdown(has_quit=True)
|
||||
vm.shutdown()
|
||||
|
Loading…
Reference in New Issue
Block a user