]>
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
._console
_file
: Optional
[socket
.SocketIO
] = None
195 self
._remove
_files
: List
[str] = []
196 self
._user
_killed
= False
197 self
._quit
_issued
= False
199 def __enter__(self
: _T
) -> _T
:
203 exc_type
: Optional
[Type
[BaseException
]],
204 exc_val
: Optional
[BaseException
],
205 exc_tb
: Optional
[TracebackType
]) -> None:
208 def add_monitor_null(self
) -> None:
210 This can be used to add an unused monitor instance.
212 self
._args
.append('-monitor')
213 self
._args
.append('null')
215 def add_fd(self
: _T
, fd
: int, fdset
: int,
216 opaque
: str, opts
: str = '') -> _T
:
218 Pass a file descriptor to the VM
220 options
= ['fd=%d' % fd
,
222 'opaque=%s' % opaque
]
226 # This did not exist before 3.4, but since then it is
227 # mandatory for our purpose
228 if hasattr(os
, 'set_inheritable'):
229 os
.set_inheritable(fd
, True)
231 self
._args
.append('-add-fd')
232 self
._args
.append(','.join(options
))
235 def send_fd_scm(self
, fd
: Optional
[int] = None,
236 file_path
: Optional
[str] = None) -> int:
238 Send an fd or file_path to the remote via SCM_RIGHTS.
240 Exactly one of fd and file_path must be given. If it is
241 file_path, the file will be opened read-only and the new file
242 descriptor will be sent to the remote.
244 if file_path
is not None:
246 with
open(file_path
, "rb") as passfile
:
247 fd
= passfile
.fileno()
248 self
._qmp
.send_fd_scm(fd
)
250 assert fd
is not None
251 self
._qmp
.send_fd_scm(fd
)
256 def _remove_if_exists(path
: str) -> None:
258 Remove file object at path if it exists
262 except OSError as exception
:
263 if exception
.errno
== errno
.ENOENT
:
267 def is_running(self
) -> bool:
268 """Returns true if the VM is running."""
269 return self
._popen
is not None and self
._popen
.poll() is None
272 def _subp(self
) -> 'subprocess.Popen[bytes]':
273 if self
._popen
is None:
274 raise QEMUMachineError('Subprocess pipe not present')
277 def exitcode(self
) -> Optional
[int]:
278 """Returns the exit code if possible, or None."""
279 if self
._popen
is None:
281 return self
._popen
.poll()
283 def get_pid(self
) -> Optional
[int]:
284 """Returns the PID of the running process, or None."""
285 if not self
.is_running():
287 return self
._subp
.pid
289 def _load_io_log(self
) -> None:
290 # Assume that the output encoding of QEMU's terminal output is
291 # defined by our locale. If indeterminate, allow open() to fall
292 # back to the platform default.
293 _
, encoding
= locale
.getlocale()
294 if self
._qemu
_log
_path
is not None:
295 with
open(self
._qemu
_log
_path
, "r", encoding
=encoding
) as iolog
:
296 self
._iolog
= iolog
.read()
299 def _base_args(self
) -> List
[str]:
300 args
= ['-display', 'none', '-vga', 'none']
304 fd
= self
._sock
_pair
[0].fileno()
305 os
.set_inheritable(fd
, True)
306 moncdev
= f
"socket,id=mon,fd={fd}"
307 elif isinstance(self
._monitor
_address
, tuple):
308 moncdev
= "socket,id=mon,host={},port={}".format(
309 *self
._monitor
_address
312 moncdev
= f
"socket,id=mon,path={self._monitor_address}"
313 args
.extend(['-chardev', moncdev
, '-mon',
314 'chardev=mon,mode=control'])
316 if self
._machine
is not None:
317 args
.extend(['-machine', self
._machine
])
318 for _
in range(self
._console
_index
):
319 args
.extend(['-serial', 'null'])
320 if self
._console
_set
:
321 chardev
= ('socket,id=console,path=%s,server=on,wait=off' %
322 self
._console
_address
)
323 args
.extend(['-chardev', chardev
])
324 if self
._console
_device
_type
is None:
325 args
.extend(['-serial', 'chardev:console'])
327 device
= '%s,chardev=console' % self
._console
_device
_type
328 args
.extend(['-device', device
])
332 def args(self
) -> List
[str]:
333 """Returns the list of arguments given to the QEMU binary."""
336 def _pre_launch(self
) -> None:
337 if self
._console
_set
:
338 self
._remove
_files
.append(self
._console
_address
)
341 if self
._monitor
_address
is None:
342 self
._sock
_pair
= socket
.socketpair()
343 sock
= self
._sock
_pair
[1]
344 if isinstance(self
._monitor
_address
, str):
345 self
._remove
_files
.append(self
._monitor
_address
)
347 sock_or_addr
= self
._monitor
_address
or sock
348 assert sock_or_addr
is not None
350 self
._qmp
_connection
= QEMUMonitorProtocol(
352 server
=bool(self
._monitor
_address
),
356 # NOTE: Make sure any opened resources are *definitely* freed in
358 # pylint: disable=consider-using-with
359 self
._qemu
_log
_path
= os
.path
.join(self
.log_dir
, self
._name
+ ".log")
360 self
._qemu
_log
_file
= open(self
._qemu
_log
_path
, 'wb')
363 self
._qemu
_full
_args
= tuple(chain(
370 def _post_launch(self
) -> None:
372 self
._sock
_pair
[0].close()
373 if self
._qmp
_connection
:
377 self
._qmp
.accept(self
._qmp
_timer
)
379 def _close_qemu_log_file(self
) -> None:
380 if self
._qemu
_log
_file
is not None:
381 self
._qemu
_log
_file
.close()
382 self
._qemu
_log
_file
= None
384 def _post_shutdown(self
) -> None:
386 Called to cleanup the VM instance after the process has exited.
387 May also be called after a failed launch.
389 LOG
.debug("Cleaning up after VM process")
391 self
._close
_qmp
_connection
()
392 except Exception as err
: # pylint: disable=broad-except
394 "Exception closing QMP connection: %s",
395 str(err
) if str(err
) else type(err
).__name
__
398 assert self
._qmp
_connection
is None
400 self
._close
_qemu
_log
_file
()
404 self
._qemu
_log
_path
= None
406 if self
._temp
_dir
is not None:
407 shutil
.rmtree(self
._temp
_dir
)
408 self
._temp
_dir
= None
410 while len(self
._remove
_files
) > 0:
411 self
._remove
_if
_exists
(self
._remove
_files
.pop())
413 exitcode
= self
.exitcode()
414 if (exitcode
is not None and exitcode
< 0
415 and not (self
._user
_killed
and exitcode
== -signal
.SIGKILL
)):
416 msg
= 'qemu received signal %i; command: "%s"'
417 if self
._qemu
_full
_args
:
418 command
= ' '.join(self
._qemu
_full
_args
)
421 LOG
.warning(msg
, -int(exitcode
), command
)
423 self
._quit
_issued
= False
424 self
._user
_killed
= False
425 self
._launched
= False
427 def launch(self
) -> None:
429 Launch the VM and make sure we cleanup and expose the
430 command line/output in case of exception
434 raise QEMUMachineError('VM already launched')
438 except BaseException
as exc
:
439 # We may have launched the process but it may
440 # have exited before we could connect via QMP.
441 # Assume the VM didn't launch or is exiting.
442 # If we don't wait for the process, exitcode() may still be
443 # 'None' by the time control is ceded back to the caller.
447 self
._post
_shutdown
()
449 if isinstance(exc
, Exception):
450 raise VMLaunchFailure(
451 exitcode
=self
.exitcode(),
452 command
=' '.join(self
._qemu
_full
_args
),
456 # Don't wrap 'BaseException'; doing so would downgrade
457 # that exception. However, we still want to clean up.
460 def _launch(self
) -> None:
462 Launch the VM and establish a QMP connection
465 LOG
.debug('VM launch command: %r', ' '.join(self
._qemu
_full
_args
))
467 # Cleaning up of this subprocess is guaranteed by _do_shutdown.
468 # pylint: disable=consider-using-with
469 self
._popen
= subprocess
.Popen(self
._qemu
_full
_args
,
470 stdin
=subprocess
.DEVNULL
,
471 stdout
=self
._qemu
_log
_file
,
472 stderr
=subprocess
.STDOUT
,
475 self
._launched
= True
478 def _close_qmp_connection(self
) -> None:
480 Close the underlying QMP connection, if any.
482 Dutifully report errors that occurred while closing, but assume
483 that any error encountered indicates an abnormal termination
484 process and not a failure to close.
486 if self
._qmp
_connection
is None:
492 # EOF can occur as an Exception here when using the Async
493 # QMP backend. It indicates that the server closed the
494 # stream. If we successfully issued 'quit' at any point,
495 # then this was expected. If the remote went away without
496 # our permission, it's worth reporting that as an abnormal
498 if not (self
._user
_killed
or self
._quit
_issued
):
501 self
._qmp
_connection
= None
503 def _early_cleanup(self
) -> None:
505 Perform any cleanup that needs to happen before the VM exits.
507 This method may be called twice upon shutdown, once each by soft
508 and hard shutdown in failover scenarios.
510 # If we keep the console socket open, we may deadlock waiting
511 # for QEMU to exit, while QEMU is waiting for the socket to
513 if self
._console
_file
is not None:
514 LOG
.debug("Closing console file")
515 self
._console
_file
.close()
516 self
._console
_file
= None
518 if self
._console
_socket
is not None:
519 LOG
.debug("Closing console socket")
520 self
._console
_socket
.close()
521 self
._console
_socket
= None
523 def _hard_shutdown(self
) -> None:
525 Perform early cleanup, kill the VM, and wait for it to terminate.
527 :raise subprocess.Timeout: When timeout is exceeds 60 seconds
528 waiting for the QEMU process to terminate.
530 LOG
.debug("Performing hard shutdown")
531 self
._early
_cleanup
()
533 self
._subp
.wait(timeout
=60)
535 def _soft_shutdown(self
, timeout
: Optional
[int]) -> None:
537 Perform early cleanup, attempt to gracefully shut down the VM, and wait
540 :param timeout: Timeout in seconds for graceful shutdown.
541 A value of None is an infinite wait.
543 :raise ConnectionReset: On QMP communication errors
544 :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
545 the QEMU process to terminate.
547 LOG
.debug("Attempting graceful termination")
549 self
._early
_cleanup
()
551 if self
._quit
_issued
:
553 "Anticipating QEMU termination due to prior 'quit' command, "
554 "or explicit call to wait()"
557 LOG
.debug("Politely asking QEMU to terminate")
559 if self
._qmp
_connection
:
561 if not self
._quit
_issued
:
562 # May raise ExecInterruptedError or StateError if the
563 # connection dies or has *already* died.
566 # Regardless, we want to quiesce the connection.
567 self
._close
_qmp
_connection
()
568 elif not self
._quit
_issued
:
570 "Not anticipating QEMU quit and no QMP connection present, "
573 self
._subp
.terminate()
575 # May raise subprocess.TimeoutExpired
577 "Waiting (timeout=%s) for QEMU process (pid=%s) to terminate",
578 timeout
, self
._subp
.pid
580 self
._subp
.wait(timeout
=timeout
)
582 def _do_shutdown(self
, timeout
: Optional
[int]) -> None:
584 Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
586 :param timeout: Timeout in seconds for graceful shutdown.
587 A value of None is an infinite wait.
589 :raise AbnormalShutdown: When the VM could not be shut down gracefully.
590 The inner exception will likely be ConnectionReset or
591 subprocess.TimeoutExpired. In rare cases, non-graceful termination
592 may result in its own exceptions, likely subprocess.TimeoutExpired.
595 self
._soft
_shutdown
(timeout
)
596 except Exception as exc
:
597 if isinstance(exc
, subprocess
.TimeoutExpired
):
598 LOG
.debug("Timed out waiting for QEMU process to exit")
599 LOG
.debug("Graceful shutdown failed", exc_info
=True)
600 LOG
.debug("Falling back to hard shutdown")
601 self
._hard
_shutdown
()
602 raise AbnormalShutdown("Could not perform graceful shutdown") \
607 timeout
: Optional
[int] = 30) -> None:
609 Terminate the VM (gracefully if possible) and perform cleanup.
610 Cleanup will always be performed.
612 If the VM has not yet been launched, or shutdown(), wait(), or kill()
613 have already been called, this method does nothing.
615 :param hard: When true, do not attempt graceful shutdown, and
616 suppress the SIGKILL warning log message.
617 :param timeout: Optional timeout in seconds for graceful shutdown.
618 Default 30 seconds, A `None` value is an infinite wait.
620 if not self
._launched
:
623 LOG
.debug("Shutting down VM appliance; timeout=%s", timeout
)
625 LOG
.debug("Caller requests immediate termination of QEMU process.")
629 self
._user
_killed
= True
630 self
._hard
_shutdown
()
632 self
._do
_shutdown
(timeout
)
634 self
._post
_shutdown
()
636 def kill(self
) -> None:
638 Terminate the VM forcefully, wait for it to exit, and perform cleanup.
640 self
.shutdown(hard
=True)
642 def wait(self
, timeout
: Optional
[int] = 30) -> None:
644 Wait for the VM to power off and perform post-shutdown cleanup.
646 :param timeout: Optional timeout in seconds. Default 30 seconds.
647 A value of `None` is an infinite wait.
649 self
._quit
_issued
= True
650 self
.shutdown(timeout
=timeout
)
652 def set_qmp_monitor(self
, enabled
: bool = True) -> None:
656 @param enabled: if False, qmp monitor options will be removed from
657 the base arguments of the resulting QEMU command
658 line. Default is True.
660 .. note:: Call this function before launch().
662 self
._qmp
_set
= enabled
665 def _qmp(self
) -> QEMUMonitorProtocol
:
666 if self
._qmp
_connection
is None:
667 raise QEMUMachineError("Attempt to access QMP with no connection")
668 return self
._qmp
_connection
671 def _qmp_args(cls
, conv_keys
: bool,
672 args
: Dict
[str, Any
]) -> Dict
[str, object]:
674 return {k
.replace('_', '-'): v
for k
, v
in args
.items()}
678 def qmp(self
, cmd
: str,
679 args_dict
: Optional
[Dict
[str, object]] = None,
680 conv_keys
: Optional
[bool] = None,
681 **args
: Any
) -> QMPMessage
:
683 Invoke a QMP command and return the response dict
685 if args_dict
is not None:
687 assert conv_keys
is None
691 if conv_keys
is None:
694 qmp_args
= self
._qmp
_args
(conv_keys
, args
)
695 ret
= self
._qmp
.cmd(cmd
, args
=qmp_args
)
696 if cmd
== 'quit' and 'error' not in ret
and 'return' in ret
:
697 self
._quit
_issued
= True
700 def command(self
, cmd
: str,
701 conv_keys
: bool = True,
702 **args
: Any
) -> QMPReturnValue
:
704 Invoke a QMP command.
705 On success return the response dict.
706 On failure raise an exception.
708 qmp_args
= self
._qmp
_args
(conv_keys
, args
)
709 ret
= self
._qmp
.command(cmd
, **qmp_args
)
711 self
._quit
_issued
= True
714 def get_qmp_event(self
, wait
: bool = False) -> Optional
[QMPMessage
]:
716 Poll for one queued QMP events and return it
719 return self
._events
.pop(0)
720 return self
._qmp
.pull_event(wait
=wait
)
722 def get_qmp_events(self
, wait
: bool = False) -> List
[QMPMessage
]:
724 Poll for queued QMP events and return a list of dicts
726 events
= self
._qmp
.get_events(wait
=wait
)
727 events
.extend(self
._events
)
732 def event_match(event
: Any
, match
: Optional
[Any
]) -> bool:
734 Check if an event matches optional match criteria.
736 The match criteria takes the form of a matching subdict. The event is
737 checked to be a superset of the subdict, recursively, with matching
738 values whenever the subdict values are not None.
740 This has a limitation that you cannot explicitly check for None values.
742 Examples, with the subdict queries on the left:
743 - None matches any object.
744 - {"foo": None} matches {"foo": {"bar": 1}}
745 - {"foo": None} matches {"foo": 5}
746 - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
747 - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
755 if not QEMUMachine
.event_match(event
[key
], match
[key
]):
761 # either match or event wasn't iterable (not a dict)
762 return bool(match
== event
)
764 def event_wait(self
, name
: str,
765 timeout
: float = 60.0,
766 match
: Optional
[QMPMessage
] = None) -> Optional
[QMPMessage
]:
768 event_wait waits for and returns a named event from QMP with a timeout.
770 name: The event to wait for.
771 timeout: QEMUMonitorProtocol.pull_event timeout parameter.
772 match: Optional match criteria. See event_match for details.
774 return self
.events_wait([(name
, match
)], timeout
)
776 def events_wait(self
,
777 events
: Sequence
[Tuple
[str, Any
]],
778 timeout
: float = 60.0) -> Optional
[QMPMessage
]:
780 events_wait waits for and returns a single named event from QMP.
781 In the case of multiple qualifying events, this function returns the
784 :param events: A sequence of (name, match_criteria) tuples.
785 The match criteria are optional and may be None.
786 See event_match for details.
787 :param timeout: Optional timeout, in seconds.
788 See QEMUMonitorProtocol.pull_event.
790 :raise asyncio.TimeoutError:
791 If timeout was non-zero and no matching events were found.
793 :return: A QMP event matching the filter criteria.
794 If timeout was 0 and no event matched, None.
796 def _match(event
: QMPMessage
) -> bool:
797 for name
, match
in events
:
798 if event
['event'] == name
and self
.event_match(event
, match
):
802 event
: Optional
[QMPMessage
]
804 # Search cached events
805 for event
in self
._events
:
807 self
._events
.remove(event
)
810 # Poll for new events
812 event
= self
._qmp
.pull_event(wait
=timeout
)
814 # NB: None is only returned when timeout is false-ish.
815 # Timeouts raise asyncio.TimeoutError instead!
819 self
._events
.append(event
)
823 def get_log(self
) -> Optional
[str]:
825 After self.shutdown or failed qemu execution, this returns the output
830 def add_args(self
, *args
: str) -> None:
832 Adds to the list of extra arguments to be given to the QEMU binary
834 self
._args
.extend(args
)
836 def set_machine(self
, machine_type
: str) -> None:
838 Sets the machine type
840 If set, the machine type will be added to the base arguments
841 of the resulting QEMU command line.
843 self
._machine
= machine_type
845 def set_console(self
,
846 device_type
: Optional
[str] = None,
847 console_index
: int = 0) -> None:
849 Sets the device type for a console device
851 If set, the console device and a backing character device will
852 be added to the base arguments of the resulting QEMU command
855 This is a convenience method that will either use the provided
856 device type, or default to a "-serial chardev:console" command
859 The actual setting of command line arguments will be be done at
860 machine launch time, as it depends on the temporary directory
863 @param device_type: the device type, such as "isa-serial". If
864 None is given (the default value) a "-serial
865 chardev:console" command line argument will
866 be used instead, resorting to the machine's
868 @param console_index: the index of the console device to use.
869 If not zero, the command line will create
870 'index - 1' consoles and connect them to
871 the 'null' backing character device.
873 self
._console
_set
= True
874 self
._console
_device
_type
= device_type
875 self
._console
_index
= console_index
878 def console_socket(self
) -> socket
.socket
:
880 Returns a socket connected to the console
882 if self
._console
_socket
is None:
883 LOG
.debug("Opening console socket")
884 self
._console
_socket
= console_socket
.ConsoleSocket(
885 self
._console
_address
,
886 file=self
._console
_log
_path
,
887 drain
=self
._drain
_console
)
888 return self
._console
_socket
891 def console_file(self
) -> socket
.SocketIO
:
893 Returns a file associated with the console socket
895 if self
._console
_file
is None:
896 LOG
.debug("Opening console file")
897 self
._console
_file
= self
.console_socket
.makefile(mode
='rb',
900 return self
._console
_file
903 def temp_dir(self
) -> str:
905 Returns a temporary directory to be used for this machine
907 if self
._temp
_dir
is None:
908 self
._temp
_dir
= tempfile
.mkdtemp(prefix
="qemu-machine-",
909 dir=self
._base
_temp
_dir
)
910 return self
._temp
_dir
913 def sock_dir(self
) -> str:
915 Returns the directory used for sockfiles by this machine.
918 return self
._sock
_dir
922 def log_dir(self
) -> str:
924 Returns a directory to be used for writing logs
926 if self
._log
_dir
is None: