]>
git.proxmox.com Git - mirror_qemu.git/blob - python/qemu/machine/machine.py
4 The machine module primarily provides the QEMUMachine class,
5 which provides facilities for managing the lifetime of a QEMU VM.
8 # Copyright (C) 2015-2016 Red Hat Inc.
9 # Copyright (C) 2012 IBM Corp.
12 # Fam Zheng <famz@redhat.com>
14 # This work is licensed under the terms of the GNU GPL, version 2. See
15 # the COPYING file in the top-level directory.
21 from itertools
import chain
30 from types
import TracebackType
43 from qemu
.qmp
import SocketAddrT
44 from qemu
.qmp
.legacy
import (
50 from . import console_socket
53 LOG
= logging
.getLogger(__name__
)
56 class QEMUMachineError(Exception):
58 Exception called when an error in QEMUMachine happens.
62 class QEMUMachineAddDeviceError(QEMUMachineError
):
64 Exception raised when a request to add a device can not be fulfilled
66 The failures are caused by limitations, lack of information or conflicting
67 requests on the QEMUMachine methods. This exception does not represent
68 failures reported by the QEMU binary itself.
72 class VMLaunchFailure(QEMUMachineError
):
74 Exception raised when a VM launch was attempted, but failed.
76 def __init__(self
, exitcode
: Optional
[int],
77 command
: str, output
: Optional
[str]):
78 super().__init
__(exitcode
, command
, output
)
79 self
.exitcode
= exitcode
80 self
.command
= command
83 def __str__(self
) -> str:
85 if self
.__cause
__ is not None:
86 name
= type(self
.__cause
__).__name
__
87 reason
= str(self
.__cause
__)
89 ret
+= f
"{name}: {reason}"
94 if self
.exitcode
is not None:
95 ret
+= f
"\tExit code: {self.exitcode}\n"
96 ret
+= f
"\tCommand: {self.command}\n"
97 ret
+= f
"\tOutput: {self.output}\n"
101 class AbnormalShutdown(QEMUMachineError
):
103 Exception raised when a graceful shutdown was requested, but not performed.
107 _T
= TypeVar('_T', bound
='QEMUMachine')
114 Use this object as a context manager to ensure
115 the QEMU process terminates::
117 with VM(binary) as vm:
119 # vm is guaranteed to be shut down here
121 # pylint: disable=too-many-instance-attributes, too-many-public-methods
125 args
: Sequence
[str] = (),
126 wrapper
: Sequence
[str] = (),
127 name
: Optional
[str] = None,
128 base_temp_dir
: str = "/var/tmp",
129 monitor_address
: Optional
[SocketAddrT
] = None,
130 sock_dir
: Optional
[str] = None,
131 drain_console
: bool = False,
132 console_log
: Optional
[str] = None,
133 log_dir
: Optional
[str] = None,
134 qmp_timer
: Optional
[float] = 30):
136 Initialize a QEMUMachine
138 @param binary: path to the qemu binary
139 @param args: list of extra arguments
140 @param wrapper: list of arguments used as prefix to qemu binary
141 @param name: prefix for socket and log file names (default: qemu-PID)
142 @param base_temp_dir: default location where temp files are created
143 @param monitor_address: address for QMP monitor
144 @param sock_dir: where to create socket (defaults to base_temp_dir)
145 @param drain_console: (optional) True to drain console socket to buffer
146 @param console_log: (optional) path to console log file
147 @param log_dir: where to create and keep log files
148 @param qmp_timer: (optional) default QMP socket timeout
149 @note: Qemu process is not started until launch() is used.
151 # pylint: disable=too-many-arguments
153 # Direct user configuration
155 self
._binary
= binary
156 self
._args
= list(args
)
157 self
._wrapper
= wrapper
158 self
._qmp
_timer
= qmp_timer
160 self
._name
= name
or f
"{id(self):x}"
161 self
._sock
_pair
: Optional
[Tuple
[socket
.socket
, socket
.socket
]] = None
162 self
._temp
_dir
: Optional
[str] = None
163 self
._base
_temp
_dir
= base_temp_dir
164 self
._sock
_dir
= sock_dir
165 self
._log
_dir
= log_dir
167 self
._monitor
_address
= monitor_address
169 self
._console
_log
_path
= console_log
170 if self
._console
_log
_path
:
171 # In order to log the console, buffering needs to be enabled.
172 self
._drain
_console
= True
174 self
._drain
_console
= drain_console
177 self
._qemu
_log
_path
: Optional
[str] = None
178 self
._qemu
_log
_file
: Optional
[BinaryIO
] = None
179 self
._popen
: Optional
['subprocess.Popen[bytes]'] = None
180 self
._events
: List
[QMPMessage
] = []
181 self
._iolog
: Optional
[str] = None
182 self
._qmp
_set
= True # Enable QMP monitor by default.
183 self
._qmp
_connection
: Optional
[QEMUMonitorProtocol
] = None
184 self
._qemu
_full
_args
: Tuple
[str, ...] = ()
185 self
._launched
= False
186 self
._machine
: Optional
[str] = None
187 self
._console
_index
= 0
188 self
._console
_set
= False
189 self
._console
_device
_type
: Optional
[str] = None
190 self
._console
_address
= os
.path
.join(
191 self
.sock_dir
, f
"{self._name}.con"
193 self
._console
_socket
: Optional
[socket
.socket
] = None
194 self
._remove
_files
: List
[str] = []
195 self
._user
_killed
= False
196 self
._quit
_issued
= False
198 def __enter__(self
: _T
) -> _T
:
202 exc_type
: Optional
[Type
[BaseException
]],
203 exc_val
: Optional
[BaseException
],
204 exc_tb
: Optional
[TracebackType
]) -> None:
207 def add_monitor_null(self
) -> None:
209 This can be used to add an unused monitor instance.
211 self
._args
.append('-monitor')
212 self
._args
.append('null')
214 def add_fd(self
: _T
, fd
: int, fdset
: int,
215 opaque
: str, opts
: str = '') -> _T
:
217 Pass a file descriptor to the VM
219 options
= ['fd=%d' % fd
,
221 'opaque=%s' % opaque
]
225 # This did not exist before 3.4, but since then it is
226 # mandatory for our purpose
227 if hasattr(os
, 'set_inheritable'):
228 os
.set_inheritable(fd
, True)
230 self
._args
.append('-add-fd')
231 self
._args
.append(','.join(options
))
234 def send_fd_scm(self
, fd
: Optional
[int] = None,
235 file_path
: Optional
[str] = None) -> int:
237 Send an fd or file_path to the remote via SCM_RIGHTS.
239 Exactly one of fd and file_path must be given. If it is
240 file_path, the file will be opened read-only and the new file
241 descriptor will be sent to the remote.
243 if file_path
is not None:
245 with
open(file_path
, "rb") as passfile
:
246 fd
= passfile
.fileno()
247 self
._qmp
.send_fd_scm(fd
)
249 assert fd
is not None
250 self
._qmp
.send_fd_scm(fd
)
255 def _remove_if_exists(path
: str) -> None:
257 Remove file object at path if it exists
261 except OSError as exception
:
262 if exception
.errno
== errno
.ENOENT
:
266 def is_running(self
) -> bool:
267 """Returns true if the VM is running."""
268 return self
._popen
is not None and self
._popen
.poll() is None
271 def _subp(self
) -> 'subprocess.Popen[bytes]':
272 if self
._popen
is None:
273 raise QEMUMachineError('Subprocess pipe not present')
276 def exitcode(self
) -> Optional
[int]:
277 """Returns the exit code if possible, or None."""
278 if self
._popen
is None:
280 return self
._popen
.poll()
282 def get_pid(self
) -> Optional
[int]:
283 """Returns the PID of the running process, or None."""
284 if not self
.is_running():
286 return self
._subp
.pid
288 def _load_io_log(self
) -> None:
289 # Assume that the output encoding of QEMU's terminal output is
290 # defined by our locale. If indeterminate, allow open() to fall
291 # back to the platform default.
292 _
, encoding
= locale
.getlocale()
293 if self
._qemu
_log
_path
is not None:
294 with
open(self
._qemu
_log
_path
, "r", encoding
=encoding
) as iolog
:
295 self
._iolog
= iolog
.read()
298 def _base_args(self
) -> List
[str]:
299 args
= ['-display', 'none', '-vga', 'none']
303 fd
= self
._sock
_pair
[0].fileno()
304 os
.set_inheritable(fd
, True)
305 moncdev
= f
"socket,id=mon,fd={fd}"
306 elif isinstance(self
._monitor
_address
, tuple):
307 moncdev
= "socket,id=mon,host={},port={}".format(
308 *self
._monitor
_address
311 moncdev
= f
"socket,id=mon,path={self._monitor_address}"
312 args
.extend(['-chardev', moncdev
, '-mon',
313 'chardev=mon,mode=control'])
315 if self
._machine
is not None:
316 args
.extend(['-machine', self
._machine
])
317 for _
in range(self
._console
_index
):
318 args
.extend(['-serial', 'null'])
319 if self
._console
_set
:
320 chardev
= ('socket,id=console,path=%s,server=on,wait=off' %
321 self
._console
_address
)
322 args
.extend(['-chardev', chardev
])
323 if self
._console
_device
_type
is None:
324 args
.extend(['-serial', 'chardev:console'])
326 device
= '%s,chardev=console' % self
._console
_device
_type
327 args
.extend(['-device', device
])
331 def args(self
) -> List
[str]:
332 """Returns the list of arguments given to the QEMU binary."""
335 def _pre_launch(self
) -> None:
336 if self
._console
_set
:
337 self
._remove
_files
.append(self
._console
_address
)
340 monitor_address
= None
342 if self
._monitor
_address
is None:
343 self
._sock
_pair
= socket
.socketpair()
344 sock
= self
._sock
_pair
[1]
345 if isinstance(self
._monitor
_address
, str):
346 self
._remove
_files
.append(self
._monitor
_address
)
347 monitor_address
= self
._monitor
_address
348 self
._qmp
_connection
= QEMUMonitorProtocol(
349 address
=monitor_address
,
355 # NOTE: Make sure any opened resources are *definitely* freed in
357 # pylint: disable=consider-using-with
358 self
._qemu
_log
_path
= os
.path
.join(self
.log_dir
, self
._name
+ ".log")
359 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
362 self
._qemu
_full
_args
= tuple(chain(
369 def _post_launch(self
) -> None:
371 self
._sock
_pair
[0].close()
372 if self
._qmp
_connection
:
373 self
._qmp
.accept(self
._qmp
_timer
)
375 def _close_qemu_log_file(self
) -> None:
376 if self
._qemu
_log
_file
is not None:
377 self
._qemu
_log
_file
.close()
378 self
._qemu
_log
_file
= None
380 def _post_shutdown(self
) -> None:
382 Called to cleanup the VM instance after the process has exited.
383 May also be called after a failed launch.
385 LOG
.debug("Cleaning up after VM process")
387 self
._close
_qmp
_connection
()
388 except Exception as err
: # pylint: disable=broad-except
390 "Exception closing QMP connection: %s",
391 str(err
) if str(err
) else type(err
).__name
__
394 assert self
._qmp
_connection
is None
396 self
._close
_qemu
_log
_file
()
400 self
._qemu
_log
_path
= None
402 if self
._temp
_dir
is not None:
403 shutil
.rmtree(self
._temp
_dir
)
404 self
._temp
_dir
= None
406 while len(self
._remove
_files
) > 0:
407 self
._remove
_if
_exists
(self
._remove
_files
.pop())
409 exitcode
= self
.exitcode()
410 if (exitcode
is not None and exitcode
< 0
411 and not (self
._user
_killed
and exitcode
== -signal
.SIGKILL
)):
412 msg
= 'qemu received signal %i; command: "%s"'
413 if self
._qemu
_full
_args
:
414 command
= ' '.join(self
._qemu
_full
_args
)
417 LOG
.warning(msg
, -int(exitcode
), command
)
419 self
._quit
_issued
= False
420 self
._user
_killed
= False
421 self
._launched
= False
423 def launch(self
) -> None:
425 Launch the VM and make sure we cleanup and expose the
426 command line/output in case of exception
430 raise QEMUMachineError('VM already launched')
434 except BaseException
as exc
:
435 # We may have launched the process but it may
436 # have exited before we could connect via QMP.
437 # Assume the VM didn't launch or is exiting.
438 # If we don't wait for the process, exitcode() may still be
439 # 'None' by the time control is ceded back to the caller.
443 self
._post
_shutdown
()
445 if isinstance(exc
, Exception):
446 raise VMLaunchFailure(
447 exitcode
=self
.exitcode(),
448 command
=' '.join(self
._qemu
_full
_args
),
452 # Don't wrap 'BaseException'; doing so would downgrade
453 # that exception. However, we still want to clean up.
456 def _launch(self
) -> None:
458 Launch the VM and establish a QMP connection
461 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
463 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
464 # pylint: disable=consider-using-with
465 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
466 stdin
=subprocess
.DEVNULL
,
467 stdout
=self
._qemu
_log
_file
,
468 stderr
=subprocess
.STDOUT
,
471 self
._launched
= True
474 def _close_qmp_connection(self
) -> None:
476 Close the underlying QMP connection, if any.
478 Dutifully report errors that occurred while closing, but assume
479 that any error encountered indicates an abnormal termination
480 process and not a failure to close.
482 if self
._qmp
_connection
is None:
488 # EOF can occur as an Exception here when using the Async
489 # QMP backend. It indicates that the server closed the
490 # stream. If we successfully issued 'quit' at any point,
491 # then this was expected. If the remote went away without
492 # our permission, it's worth reporting that as an abnormal
494 if not (self
._user
_killed
or self
._quit
_issued
):
497 self
._qmp
_connection
= None
499 def _early_cleanup(self
) -> None:
501 Perform any cleanup that needs to happen before the VM exits.
503 This method may be called twice upon shutdown, once each by soft
504 and hard shutdown in failover scenarios.
506 # If we keep the console socket open, we may deadlock waiting
507 # for QEMU to exit, while QEMU is waiting for the socket to
509 if self
._console
_socket
is not None:
510 LOG
.debug("Closing console socket")
511 self
._console
_socket
.close()
512 self
._console
_socket
= None
514 def _hard_shutdown(self
) -> None:
516 Perform early cleanup, kill the VM, and wait for it to terminate.
518 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
519 waiting for the QEMU process to terminate.
521 LOG
.debug("Performing hard shutdown")
522 self
._early
_cleanup
()
524 self
._subp
.wait(timeout
=60)
526 def _soft_shutdown(self
, timeout
: Optional
[int]) -> None:
528 Perform early cleanup, attempt to gracefully shut down the VM, and wait
531 :param timeout: Timeout in seconds for graceful shutdown.
532 A value of None is an infinite wait.
534 :raise ConnectionReset: On QMP communication errors
535 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
536 the QEMU process to terminate.
538 LOG
.debug("Attempting graceful termination")
540 self
._early
_cleanup
()
542 if self
._quit
_issued
:
544 "Anticipating QEMU termination due to prior 'quit' command, "
545 "or explicit call to wait()"
548 LOG
.debug("Politely asking QEMU to terminate")
550 if self
._qmp
_connection
:
552 if not self
._quit
_issued
:
553 # May raise ExecInterruptedError or StateError if the
554 # connection dies or has *already* died.
557 # Regardless, we want to quiesce the connection.
558 self
._close
_qmp
_connection
()
559 elif not self
._quit
_issued
:
561 "Not anticipating QEMU quit and no QMP connection present, "
564 self
._subp
.terminate()
566 # May raise subprocess.TimeoutExpired
568 "Waiting (timeout=%s) for QEMU process (pid=%s) to terminate",
569 timeout
, self
._subp
.pid
571 self
._subp
.wait(timeout
=timeout
)
573 def _do_shutdown(self
, timeout
: Optional
[int]) -> None:
575 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
577 :param timeout: Timeout in seconds for graceful shutdown.
578 A value of None is an infinite wait.
580 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
581 The inner exception will likely be ConnectionReset or
582 subprocess.TimeoutExpired. In rare cases, non-graceful termination
583 may result in its own exceptions, likely subprocess.TimeoutExpired.
586 self
._soft
_shutdown
(timeout
)
587 except Exception as exc
:
588 if isinstance(exc
, subprocess
.TimeoutExpired
):
589 LOG
.debug("Timed out waiting for QEMU process to exit")
590 LOG
.debug("Graceful shutdown failed", exc_info
=True)
591 LOG
.debug("Falling back to hard shutdown")
592 self
._hard
_shutdown
()
593 raise AbnormalShutdown("Could not perform graceful shutdown") \
598 timeout
: Optional
[int] = 30) -> None:
600 Terminate the VM (gracefully if possible) and perform cleanup.
601 Cleanup will always be performed.
603 If the VM has not yet been launched, or shutdown(), wait(), or kill()
604 have already been called, this method does nothing.
606 :param hard: When true, do not attempt graceful shutdown, and
607 suppress the SIGKILL warning log message.
608 :param timeout: Optional timeout in seconds for graceful shutdown.
609 Default 30 seconds, A `None` value is an infinite wait.
611 if not self
._launched
:
614 LOG
.debug("Shutting down VM appliance; timeout=%s", timeout
)
616 LOG
.debug("Caller requests immediate termination of QEMU process.")
620 self
._user
_killed
= True
621 self
._hard
_shutdown
()
623 self
._do
_shutdown
(timeout
)
625 self
._post
_shutdown
()
627 def kill(self
) -> None:
629 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
631 self
.shutdown(hard
=True)
633 def wait(self
, timeout
: Optional
[int] = 30) -> None:
635 Wait for the VM to power off and perform post-shutdown cleanup.
637 :param timeout: Optional timeout in seconds. Default 30 seconds.
638 A value of `None` is an infinite wait.
640 self
._quit
_issued
= True
641 self
.shutdown(timeout
=timeout
)
643 def set_qmp_monitor(self
, enabled
: bool = True) -> None:
647 @param enabled: if False, qmp monitor options will be removed from
648 the base arguments of the resulting QEMU command
649 line. Default is True.
651 .. note:: Call this function before launch().
653 self
._qmp
_set
= enabled
656 def _qmp(self
) -> QEMUMonitorProtocol
:
657 if self
._qmp
_connection
is None:
658 raise QEMUMachineError("Attempt to access QMP with no connection")
659 return self
._qmp
_connection
662 def _qmp_args(cls
, conv_keys
: bool,
663 args
: Dict
[str, Any
]) -> Dict
[str, object]:
665 return {k
.replace('_', '-'): v
for k
, v
in args
.items()}
669 def qmp(self
, cmd
: str,
670 args_dict
: Optional
[Dict
[str, object]] = None,
671 conv_keys
: Optional
[bool] = None,
672 **args
: Any
) -> QMPMessage
:
674 Invoke a QMP command and return the response dict
676 if args_dict
is not None:
678 assert conv_keys
is None
682 if conv_keys
is None:
685 qmp_args
= self
._qmp
_args
(conv_keys
, args
)
686 ret
= self
._qmp
.cmd(cmd
, args
=qmp_args
)
687 if cmd
== 'quit' and 'error' not in ret
and 'return' in ret
:
688 self
._quit
_issued
= True
691 def command(self
, cmd
: str,
692 conv_keys
: bool = True,
693 **args
: Any
) -> QMPReturnValue
:
695 Invoke a QMP command.
696 On success return the response dict.
697 On failure raise an exception.
699 qmp_args
= self
._qmp
_args
(conv_keys
, args
)
700 ret
= self
._qmp
.command(cmd
, **qmp_args
)
702 self
._quit
_issued
= True
705 def get_qmp_event(self
, wait
: bool = False) -> Optional
[QMPMessage
]:
707 Poll for one queued QMP events and return it
710 return self
._events
.pop(0)
711 return self
._qmp
.pull_event(wait
=wait
)
713 def get_qmp_events(self
, wait
: bool = False) -> List
[QMPMessage
]:
715 Poll for queued QMP events and return a list of dicts
717 events
= self
._qmp
.get_events(wait
=wait
)
718 events
.extend(self
._events
)
723 def event_match(event
: Any
, match
: Optional
[Any
]) -> bool:
725 Check if an event matches optional match criteria.
727 The match criteria takes the form of a matching subdict. The event is
728 checked to be a superset of the subdict, recursively, with matching
729 values whenever the subdict values are not None.
731 This has a limitation that you cannot explicitly check for None values.
733 Examples, with the subdict queries on the left:
734 - None matches any object.
735 - {"foo": None} matches {"foo": {"bar": 1}}
736 - {"foo": None} matches {"foo": 5}
737 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
738 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
746 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
752 # either match or event wasn't iterable (not a dict)
753 return bool(match
== event
)
755 def event_wait(self
, name
: str,
756 timeout
: float = 60.0,
757 match
: Optional
[QMPMessage
] = None) -> Optional
[QMPMessage
]:
759 event_wait waits for and returns a named event from QMP with a timeout.
761 name: The event to wait for.
762 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
763 match: Optional match criteria. See event_match for details.
765 return self
.events_wait([(name
, match
)], timeout
)
767 def events_wait(self
,
768 events
: Sequence
[Tuple
[str, Any
]],
769 timeout
: float = 60.0) -> Optional
[QMPMessage
]:
771 events_wait waits for and returns a single named event from QMP.
772 In the case of multiple qualifying events, this function returns the
775 :param events: A sequence of (name, match_criteria) tuples.
776 The match criteria are optional and may be None.
777 See event_match for details.
778 :param timeout: Optional timeout, in seconds.
779 See QEMUMonitorProtocol.pull_event.
781 :raise asyncio.TimeoutError:
782 If timeout was non-zero and no matching events were found.
784 :return: A QMP event matching the filter criteria.
785 If timeout was 0 and no event matched, None.
787 def _match(event
: QMPMessage
) -> bool:
788 for name
, match
in events
:
789 if event
['event'] == name
and self
.event_match(event
, match
):
793 event
: Optional
[QMPMessage
]
795 # Search cached events
796 for event
in self
._events
:
798 self
._events
.remove(event
)
801 # Poll for new events
803 event
= self
._qmp
.pull_event(wait
=timeout
)
805 # NB: None is only returned when timeout is false-ish.
806 # Timeouts raise asyncio.TimeoutError instead!
810 self
._events
.append(event
)
814 def get_log(self
) -> Optional
[str]:
816 After self.shutdown or failed qemu execution, this returns the output
821 def add_args(self
, *args
: str) -> None:
823 Adds to the list of extra arguments to be given to the QEMU binary
825 self
._args
.extend(args
)
827 def set_machine(self
, machine_type
: str) -> None:
829 Sets the machine type
831 If set, the machine type will be added to the base arguments
832 of the resulting QEMU command line.
834 self
._machine
= machine_type
836 def set_console(self
,
837 device_type
: Optional
[str] = None,
838 console_index
: int = 0) -> None:
840 Sets the device type for a console device
842 If set, the console device and a backing character device will
843 be added to the base arguments of the resulting QEMU command
846 This is a convenience method that will either use the provided
847 device type, or default to a "-serial chardev:console" command
850 The actual setting of command line arguments will be be done at
851 machine launch time, as it depends on the temporary directory
854 @param device_type: the device type, such as "isa-serial". If
855 None is given (the default value) a "-serial
856 chardev:console" command line argument will
857 be used instead, resorting to the machine's
859 @param console_index: the index of the console device to use.
860 If not zero, the command line will create
861 'index - 1' consoles and connect them to
862 the 'null' backing character device.
864 self
._console
_set
= True
865 self
._console
_device
_type
= device_type
866 self
._console
_index
= console_index
869 def console_socket(self
) -> socket
.socket
:
871 Returns a socket connected to the console
873 if self
._console
_socket
is None:
874 self
._console
_socket
= console_socket
.ConsoleSocket(
875 self
._console
_address
,
876 file=self
._console
_log
_path
,
877 drain
=self
._drain
_console
)
878 return self
._console
_socket
881 def temp_dir(self
) -> str:
883 Returns a temporary directory to be used for this machine
885 if self
._temp
_dir
is None:
886 self
._temp
_dir
= tempfile
.mkdtemp(prefix
="qemu-machine-",
887 dir=self
._base
_temp
_dir
)
888 return self
._temp
_dir
891 def sock_dir(self
) -> str:
893 Returns the directory used for sockfiles by this machine.
896 return self
._sock
_dir
900 def log_dir(self
) -> str:
902 Returns a directory to be used for writing logs
904 if self
._log
_dir
is None: