]> Git Repo - qemu.git/blob - python/qemu/machine/machine.py
Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2022-03-07' into staging
[qemu.git] / python / qemu / machine / machine.py
1 """
2 QEMU machine module:
3
4 The machine module primarily provides the QEMUMachine class,
5 which provides facilities for managing the lifetime of a QEMU VM.
6 """
7
8 # Copyright (C) 2015-2016 Red Hat Inc.
9 # Copyright (C) 2012 IBM Corp.
10 #
11 # Authors:
12 #  Fam Zheng <[email protected]>
13 #
14 # This work is licensed under the terms of the GNU GPL, version 2.  See
15 # the COPYING file in the top-level directory.
16 #
17 # Based on qmp.py.
18 #
19
20 import errno
21 from itertools import chain
22 import locale
23 import logging
24 import os
25 import shutil
26 import signal
27 import socket
28 import subprocess
29 import tempfile
30 from types import TracebackType
31 from typing import (
32     Any,
33     BinaryIO,
34     Dict,
35     List,
36     Optional,
37     Sequence,
38     Tuple,
39     Type,
40     TypeVar,
41 )
42
43 from qemu.qmp import (  # pylint: disable=import-error
44     QMPMessage,
45     QMPReturnValue,
46     SocketAddrT,
47 )
48
49 from . import console_socket
50
51
52 if os.environ.get('QEMU_PYTHON_LEGACY_QMP'):
53     from qemu.qmp import QEMUMonitorProtocol
54 else:
55     from qemu.aqmp.legacy import QEMUMonitorProtocol
56
57
58 LOG = logging.getLogger(__name__)
59
60
61 class QEMUMachineError(Exception):
62     """
63     Exception called when an error in QEMUMachine happens.
64     """
65
66
67 class QEMUMachineAddDeviceError(QEMUMachineError):
68     """
69     Exception raised when a request to add a device can not be fulfilled
70
71     The failures are caused by limitations, lack of information or conflicting
72     requests on the QEMUMachine methods.  This exception does not represent
73     failures reported by the QEMU binary itself.
74     """
75
76
77 class VMLaunchFailure(QEMUMachineError):
78     """
79     Exception raised when a VM launch was attempted, but failed.
80     """
81     def __init__(self, exitcode: Optional[int],
82                  command: str, output: Optional[str]):
83         super().__init__(exitcode, command, output)
84         self.exitcode = exitcode
85         self.command = command
86         self.output = output
87
88     def __str__(self) -> str:
89         ret = ''
90         if self.__cause__ is not None:
91             name = type(self.__cause__).__name__
92             reason = str(self.__cause__)
93             if reason:
94                 ret += f"{name}: {reason}"
95             else:
96                 ret += f"{name}"
97         ret += '\n'
98
99         if self.exitcode is not None:
100             ret += f"\tExit code: {self.exitcode}\n"
101         ret += f"\tCommand: {self.command}\n"
102         ret += f"\tOutput: {self.output}\n"
103         return ret
104
105
106 class AbnormalShutdown(QEMUMachineError):
107     """
108     Exception raised when a graceful shutdown was requested, but not performed.
109     """
110
111
112 _T = TypeVar('_T', bound='QEMUMachine')
113
114
115 class QEMUMachine:
116     """
117     A QEMU VM.
118
119     Use this object as a context manager to ensure
120     the QEMU process terminates::
121
122         with VM(binary) as vm:
123             ...
124         # vm is guaranteed to be shut down here
125     """
126     # pylint: disable=too-many-instance-attributes, too-many-public-methods
127
128     def __init__(self,
129                  binary: str,
130                  args: Sequence[str] = (),
131                  wrapper: Sequence[str] = (),
132                  name: Optional[str] = None,
133                  base_temp_dir: str = "/var/tmp",
134                  monitor_address: Optional[SocketAddrT] = None,
135                  sock_dir: Optional[str] = None,
136                  drain_console: bool = False,
137                  console_log: Optional[str] = None,
138                  log_dir: Optional[str] = None,
139                  qmp_timer: Optional[float] = None):
140         '''
141         Initialize a QEMUMachine
142
143         @param binary: path to the qemu binary
144         @param args: list of extra arguments
145         @param wrapper: list of arguments used as prefix to qemu binary
146         @param name: prefix for socket and log file names (default: qemu-PID)
147         @param base_temp_dir: default location where temp files are created
148         @param monitor_address: address for QMP monitor
149         @param sock_dir: where to create socket (defaults to base_temp_dir)
150         @param drain_console: (optional) True to drain console socket to buffer
151         @param console_log: (optional) path to console log file
152         @param log_dir: where to create and keep log files
153         @param qmp_timer: (optional) default QMP socket timeout
154         @note: Qemu process is not started until launch() is used.
155         '''
156         # pylint: disable=too-many-arguments
157
158         # Direct user configuration
159
160         self._binary = binary
161         self._args = list(args)
162         self._wrapper = wrapper
163         self._qmp_timer = qmp_timer
164
165         self._name = name or f"qemu-{os.getpid()}-{id(self):02x}"
166         self._temp_dir: Optional[str] = None
167         self._base_temp_dir = base_temp_dir
168         self._sock_dir = sock_dir
169         self._log_dir = log_dir
170
171         if monitor_address is not None:
172             self._monitor_address = monitor_address
173         else:
174             self._monitor_address = os.path.join(
175                 self.sock_dir, f"{self._name}-monitor.sock"
176             )
177
178         self._console_log_path = console_log
179         if self._console_log_path:
180             # In order to log the console, buffering needs to be enabled.
181             self._drain_console = True
182         else:
183             self._drain_console = drain_console
184
185         # Runstate
186         self._qemu_log_path: Optional[str] = None
187         self._qemu_log_file: Optional[BinaryIO] = None
188         self._popen: Optional['subprocess.Popen[bytes]'] = None
189         self._events: List[QMPMessage] = []
190         self._iolog: Optional[str] = None
191         self._qmp_set = True   # Enable QMP monitor by default.
192         self._qmp_connection: Optional[QEMUMonitorProtocol] = None
193         self._qemu_full_args: Tuple[str, ...] = ()
194         self._launched = False
195         self._machine: Optional[str] = None
196         self._console_index = 0
197         self._console_set = False
198         self._console_device_type: Optional[str] = None
199         self._console_address = os.path.join(
200             self.sock_dir, f"{self._name}-console.sock"
201         )
202         self._console_socket: Optional[socket.socket] = None
203         self._remove_files: List[str] = []
204         self._user_killed = False
205         self._quit_issued = False
206
207     def __enter__(self: _T) -> _T:
208         return self
209
210     def __exit__(self,
211                  exc_type: Optional[Type[BaseException]],
212                  exc_val: Optional[BaseException],
213                  exc_tb: Optional[TracebackType]) -> None:
214         self.shutdown()
215
216     def add_monitor_null(self) -> None:
217         """
218         This can be used to add an unused monitor instance.
219         """
220         self._args.append('-monitor')
221         self._args.append('null')
222
223     def add_fd(self: _T, fd: int, fdset: int,
224                opaque: str, opts: str = '') -> _T:
225         """
226         Pass a file descriptor to the VM
227         """
228         options = ['fd=%d' % fd,
229                    'set=%d' % fdset,
230                    'opaque=%s' % opaque]
231         if opts:
232             options.append(opts)
233
234         # This did not exist before 3.4, but since then it is
235         # mandatory for our purpose
236         if hasattr(os, 'set_inheritable'):
237             os.set_inheritable(fd, True)
238
239         self._args.append('-add-fd')
240         self._args.append(','.join(options))
241         return self
242
243     def send_fd_scm(self, fd: Optional[int] = None,
244                     file_path: Optional[str] = None) -> int:
245         """
246         Send an fd or file_path to the remote via SCM_RIGHTS.
247
248         Exactly one of fd and file_path must be given.  If it is
249         file_path, the file will be opened read-only and the new file
250         descriptor will be sent to the remote.
251         """
252         if file_path is not None:
253             assert fd is None
254             with open(file_path, "rb") as passfile:
255                 fd = passfile.fileno()
256                 self._qmp.send_fd_scm(fd)
257         else:
258             assert fd is not None
259             self._qmp.send_fd_scm(fd)
260
261         return 0
262
263     @staticmethod
264     def _remove_if_exists(path: str) -> None:
265         """
266         Remove file object at path if it exists
267         """
268         try:
269             os.remove(path)
270         except OSError as exception:
271             if exception.errno == errno.ENOENT:
272                 return
273             raise
274
275     def is_running(self) -> bool:
276         """Returns true if the VM is running."""
277         return self._popen is not None and self._popen.poll() is None
278
279     @property
280     def _subp(self) -> 'subprocess.Popen[bytes]':
281         if self._popen is None:
282             raise QEMUMachineError('Subprocess pipe not present')
283         return self._popen
284
285     def exitcode(self) -> Optional[int]:
286         """Returns the exit code if possible, or None."""
287         if self._popen is None:
288             return None
289         return self._popen.poll()
290
291     def get_pid(self) -> Optional[int]:
292         """Returns the PID of the running process, or None."""
293         if not self.is_running():
294             return None
295         return self._subp.pid
296
297     def _load_io_log(self) -> None:
298         # Assume that the output encoding of QEMU's terminal output is
299         # defined by our locale. If indeterminate, allow open() to fall
300         # back to the platform default.
301         _, encoding = locale.getlocale()
302         if self._qemu_log_path is not None:
303             with open(self._qemu_log_path, "r", encoding=encoding) as iolog:
304                 self._iolog = iolog.read()
305
306     @property
307     def _base_args(self) -> List[str]:
308         args = ['-display', 'none', '-vga', 'none']
309
310         if self._qmp_set:
311             if isinstance(self._monitor_address, tuple):
312                 moncdev = "socket,id=mon,host={},port={}".format(
313                     *self._monitor_address
314                 )
315             else:
316                 moncdev = f"socket,id=mon,path={self._monitor_address}"
317             args.extend(['-chardev', moncdev, '-mon',
318                          'chardev=mon,mode=control'])
319
320         if self._machine is not None:
321             args.extend(['-machine', self._machine])
322         for _ in range(self._console_index):
323             args.extend(['-serial', 'null'])
324         if self._console_set:
325             chardev = ('socket,id=console,path=%s,server=on,wait=off' %
326                        self._console_address)
327             args.extend(['-chardev', chardev])
328             if self._console_device_type is None:
329                 args.extend(['-serial', 'chardev:console'])
330             else:
331                 device = '%s,chardev=console' % self._console_device_type
332                 args.extend(['-device', device])
333         return args
334
335     @property
336     def args(self) -> List[str]:
337         """Returns the list of arguments given to the QEMU binary."""
338         return self._args
339
340     def _pre_launch(self) -> None:
341         if self._console_set:
342             self._remove_files.append(self._console_address)
343
344         if self._qmp_set:
345             if isinstance(self._monitor_address, str):
346                 self._remove_files.append(self._monitor_address)
347             self._qmp_connection = QEMUMonitorProtocol(
348                 self._monitor_address,
349                 server=True,
350                 nickname=self._name
351             )
352
353         # NOTE: Make sure any opened resources are *definitely* freed in
354         # _post_shutdown()!
355         # pylint: disable=consider-using-with
356         self._qemu_log_path = os.path.join(self.log_dir, self._name + ".log")
357         self._qemu_log_file = open(self._qemu_log_path, 'wb')
358
359         self._iolog = None
360         self._qemu_full_args = tuple(chain(
361             self._wrapper,
362             [self._binary],
363             self._base_args,
364             self._args
365         ))
366
367     def _post_launch(self) -> None:
368         if self._qmp_connection:
369             self._qmp.accept(self._qmp_timer)
370
371     def _close_qemu_log_file(self) -> None:
372         if self._qemu_log_file is not None:
373             self._qemu_log_file.close()
374             self._qemu_log_file = None
375
376     def _post_shutdown(self) -> None:
377         """
378         Called to cleanup the VM instance after the process has exited.
379         May also be called after a failed launch.
380         """
381         try:
382             self._close_qmp_connection()
383         except Exception as err:  # pylint: disable=broad-except
384             LOG.warning(
385                 "Exception closing QMP connection: %s",
386                 str(err) if str(err) else type(err).__name__
387             )
388         finally:
389             assert self._qmp_connection is None
390
391         self._close_qemu_log_file()
392
393         self._load_io_log()
394
395         self._qemu_log_path = None
396
397         if self._temp_dir is not None:
398             shutil.rmtree(self._temp_dir)
399             self._temp_dir = None
400
401         while len(self._remove_files) > 0:
402             self._remove_if_exists(self._remove_files.pop())
403
404         exitcode = self.exitcode()
405         if (exitcode is not None and exitcode < 0
406                 and not (self._user_killed and exitcode == -signal.SIGKILL)):
407             msg = 'qemu received signal %i; command: "%s"'
408             if self._qemu_full_args:
409                 command = ' '.join(self._qemu_full_args)
410             else:
411                 command = ''
412             LOG.warning(msg, -int(exitcode), command)
413
414         self._quit_issued = False
415         self._user_killed = False
416         self._launched = False
417
418     def launch(self) -> None:
419         """
420         Launch the VM and make sure we cleanup and expose the
421         command line/output in case of exception
422         """
423
424         if self._launched:
425             raise QEMUMachineError('VM already launched')
426
427         try:
428             self._launch()
429         except BaseException as exc:
430             # We may have launched the process but it may
431             # have exited before we could connect via QMP.
432             # Assume the VM didn't launch or is exiting.
433             # If we don't wait for the process, exitcode() may still be
434             # 'None' by the time control is ceded back to the caller.
435             if self._launched:
436                 self.wait()
437             else:
438                 self._post_shutdown()
439
440             if isinstance(exc, Exception):
441                 raise VMLaunchFailure(
442                     exitcode=self.exitcode(),
443                     command=' '.join(self._qemu_full_args),
444                     output=self._iolog
445                 ) from exc
446
447             # Don't wrap 'BaseException'; doing so would downgrade
448             # that exception. However, we still want to clean up.
449             raise
450
451     def _launch(self) -> None:
452         """
453         Launch the VM and establish a QMP connection
454         """
455         self._pre_launch()
456         LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
457
458         # Cleaning up of this subprocess is guaranteed by _do_shutdown.
459         # pylint: disable=consider-using-with
460         self._popen = subprocess.Popen(self._qemu_full_args,
461                                        stdin=subprocess.DEVNULL,
462                                        stdout=self._qemu_log_file,
463                                        stderr=subprocess.STDOUT,
464                                        shell=False,
465                                        close_fds=False)
466         self._launched = True
467         self._post_launch()
468
469     def _close_qmp_connection(self) -> None:
470         """
471         Close the underlying QMP connection, if any.
472
473         Dutifully report errors that occurred while closing, but assume
474         that any error encountered indicates an abnormal termination
475         process and not a failure to close.
476         """
477         if self._qmp_connection is None:
478             return
479
480         try:
481             self._qmp.close()
482         except EOFError:
483             # EOF can occur as an Exception here when using the Async
484             # QMP backend. It indicates that the server closed the
485             # stream. If we successfully issued 'quit' at any point,
486             # then this was expected. If the remote went away without
487             # our permission, it's worth reporting that as an abnormal
488             # shutdown case.
489             if not (self._user_killed or self._quit_issued):
490                 raise
491         finally:
492             self._qmp_connection = None
493
494     def _early_cleanup(self) -> None:
495         """
496         Perform any cleanup that needs to happen before the VM exits.
497
498         This method may be called twice upon shutdown, once each by soft
499         and hard shutdown in failover scenarios.
500         """
501         # If we keep the console socket open, we may deadlock waiting
502         # for QEMU to exit, while QEMU is waiting for the socket to
503         # become writeable.
504         if self._console_socket is not None:
505             self._console_socket.close()
506             self._console_socket = None
507
508     def _hard_shutdown(self) -> None:
509         """
510         Perform early cleanup, kill the VM, and wait for it to terminate.
511
512         :raise subprocess.Timeout: When timeout is exceeds 60 seconds
513             waiting for the QEMU process to terminate.
514         """
515         self._early_cleanup()
516         self._subp.kill()
517         self._subp.wait(timeout=60)
518
519     def _soft_shutdown(self, timeout: Optional[int]) -> None:
520         """
521         Perform early cleanup, attempt to gracefully shut down the VM, and wait
522         for it to terminate.
523
524         :param timeout: Timeout in seconds for graceful shutdown.
525                         A value of None is an infinite wait.
526
527         :raise ConnectionReset: On QMP communication errors
528         :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
529             the QEMU process to terminate.
530         """
531         self._early_cleanup()
532
533         if self._qmp_connection:
534             try:
535                 if not self._quit_issued:
536                     # May raise ExecInterruptedError or StateError if the
537                     # connection dies or has *already* died.
538                     self.qmp('quit')
539             finally:
540                 # Regardless, we want to quiesce the connection.
541                 self._close_qmp_connection()
542
543         # May raise subprocess.TimeoutExpired
544         self._subp.wait(timeout=timeout)
545
546     def _do_shutdown(self, timeout: Optional[int]) -> None:
547         """
548         Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
549
550         :param timeout: Timeout in seconds for graceful shutdown.
551                         A value of None is an infinite wait.
552
553         :raise AbnormalShutdown: When the VM could not be shut down gracefully.
554             The inner exception will likely be ConnectionReset or
555             subprocess.TimeoutExpired. In rare cases, non-graceful termination
556             may result in its own exceptions, likely subprocess.TimeoutExpired.
557         """
558         try:
559             self._soft_shutdown(timeout)
560         except Exception as exc:
561             self._hard_shutdown()
562             raise AbnormalShutdown("Could not perform graceful shutdown") \
563                 from exc
564
565     def shutdown(self,
566                  hard: bool = False,
567                  timeout: Optional[int] = 30) -> None:
568         """
569         Terminate the VM (gracefully if possible) and perform cleanup.
570         Cleanup will always be performed.
571
572         If the VM has not yet been launched, or shutdown(), wait(), or kill()
573         have already been called, this method does nothing.
574
575         :param hard: When true, do not attempt graceful shutdown, and
576                      suppress the SIGKILL warning log message.
577         :param timeout: Optional timeout in seconds for graceful shutdown.
578                         Default 30 seconds, A `None` value is an infinite wait.
579         """
580         if not self._launched:
581             return
582
583         try:
584             if hard:
585                 self._user_killed = True
586                 self._hard_shutdown()
587             else:
588                 self._do_shutdown(timeout)
589         finally:
590             self._post_shutdown()
591
592     def kill(self) -> None:
593         """
594         Terminate the VM forcefully, wait for it to exit, and perform cleanup.
595         """
596         self.shutdown(hard=True)
597
598     def wait(self, timeout: Optional[int] = 30) -> None:
599         """
600         Wait for the VM to power off and perform post-shutdown cleanup.
601
602         :param timeout: Optional timeout in seconds. Default 30 seconds.
603                         A value of `None` is an infinite wait.
604         """
605         self._quit_issued = True
606         self.shutdown(timeout=timeout)
607
608     def set_qmp_monitor(self, enabled: bool = True) -> None:
609         """
610         Set the QMP monitor.
611
612         @param enabled: if False, qmp monitor options will be removed from
613                         the base arguments of the resulting QEMU command
614                         line. Default is True.
615
616         .. note:: Call this function before launch().
617         """
618         self._qmp_set = enabled
619
620     @property
621     def _qmp(self) -> QEMUMonitorProtocol:
622         if self._qmp_connection is None:
623             raise QEMUMachineError("Attempt to access QMP with no connection")
624         return self._qmp_connection
625
626     @classmethod
627     def _qmp_args(cls, conv_keys: bool,
628                   args: Dict[str, Any]) -> Dict[str, object]:
629         if conv_keys:
630             return {k.replace('_', '-'): v for k, v in args.items()}
631
632         return args
633
634     def qmp(self, cmd: str,
635             args_dict: Optional[Dict[str, object]] = None,
636             conv_keys: Optional[bool] = None,
637             **args: Any) -> QMPMessage:
638         """
639         Invoke a QMP command and return the response dict
640         """
641         if args_dict is not None:
642             assert not args
643             assert conv_keys is None
644             args = args_dict
645             conv_keys = False
646
647         if conv_keys is None:
648             conv_keys = True
649
650         qmp_args = self._qmp_args(conv_keys, args)
651         ret = self._qmp.cmd(cmd, args=qmp_args)
652         if cmd == 'quit' and 'error' not in ret and 'return' in ret:
653             self._quit_issued = True
654         return ret
655
656     def command(self, cmd: str,
657                 conv_keys: bool = True,
658                 **args: Any) -> QMPReturnValue:
659         """
660         Invoke a QMP command.
661         On success return the response dict.
662         On failure raise an exception.
663         """
664         qmp_args = self._qmp_args(conv_keys, args)
665         ret = self._qmp.command(cmd, **qmp_args)
666         if cmd == 'quit':
667             self._quit_issued = True
668         return ret
669
670     def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]:
671         """
672         Poll for one queued QMP events and return it
673         """
674         if self._events:
675             return self._events.pop(0)
676         return self._qmp.pull_event(wait=wait)
677
678     def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]:
679         """
680         Poll for queued QMP events and return a list of dicts
681         """
682         events = self._qmp.get_events(wait=wait)
683         events.extend(self._events)
684         del self._events[:]
685         return events
686
687     @staticmethod
688     def event_match(event: Any, match: Optional[Any]) -> bool:
689         """
690         Check if an event matches optional match criteria.
691
692         The match criteria takes the form of a matching subdict. The event is
693         checked to be a superset of the subdict, recursively, with matching
694         values whenever the subdict values are not None.
695
696         This has a limitation that you cannot explicitly check for None values.
697
698         Examples, with the subdict queries on the left:
699          - None matches any object.
700          - {"foo": None} matches {"foo": {"bar": 1}}
701          - {"foo": None} matches {"foo": 5}
702          - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
703          - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
704         """
705         if match is None:
706             return True
707
708         try:
709             for key in match:
710                 if key in event:
711                     if not QEMUMachine.event_match(event[key], match[key]):
712                         return False
713                 else:
714                     return False
715             return True
716         except TypeError:
717             # either match or event wasn't iterable (not a dict)
718             return bool(match == event)
719
720     def event_wait(self, name: str,
721                    timeout: float = 60.0,
722                    match: Optional[QMPMessage] = None) -> Optional[QMPMessage]:
723         """
724         event_wait waits for and returns a named event from QMP with a timeout.
725
726         name: The event to wait for.
727         timeout: QEMUMonitorProtocol.pull_event timeout parameter.
728         match: Optional match criteria. See event_match for details.
729         """
730         return self.events_wait([(name, match)], timeout)
731
732     def events_wait(self,
733                     events: Sequence[Tuple[str, Any]],
734                     timeout: float = 60.0) -> Optional[QMPMessage]:
735         """
736         events_wait waits for and returns a single named event from QMP.
737         In the case of multiple qualifying events, this function returns the
738         first one.
739
740         :param events: A sequence of (name, match_criteria) tuples.
741                        The match criteria are optional and may be None.
742                        See event_match for details.
743         :param timeout: Optional timeout, in seconds.
744                         See QEMUMonitorProtocol.pull_event.
745
746         :raise QMPTimeoutError: If timeout was non-zero and no matching events
747                                 were found.
748         :return: A QMP event matching the filter criteria.
749                  If timeout was 0 and no event matched, None.
750         """
751         def _match(event: QMPMessage) -> bool:
752             for name, match in events:
753                 if event['event'] == name and self.event_match(event, match):
754                     return True
755             return False
756
757         event: Optional[QMPMessage]
758
759         # Search cached events
760         for event in self._events:
761             if _match(event):
762                 self._events.remove(event)
763                 return event
764
765         # Poll for new events
766         while True:
767             event = self._qmp.pull_event(wait=timeout)
768             if event is None:
769                 # NB: None is only returned when timeout is false-ish.
770                 # Timeouts raise QMPTimeoutError instead!
771                 break
772             if _match(event):
773                 return event
774             self._events.append(event)
775
776         return None
777
778     def get_log(self) -> Optional[str]:
779         """
780         After self.shutdown or failed qemu execution, this returns the output
781         of the qemu process.
782         """
783         return self._iolog
784
785     def add_args(self, *args: str) -> None:
786         """
787         Adds to the list of extra arguments to be given to the QEMU binary
788         """
789         self._args.extend(args)
790
791     def set_machine(self, machine_type: str) -> None:
792         """
793         Sets the machine type
794
795         If set, the machine type will be added to the base arguments
796         of the resulting QEMU command line.
797         """
798         self._machine = machine_type
799
800     def set_console(self,
801                     device_type: Optional[str] = None,
802                     console_index: int = 0) -> None:
803         """
804         Sets the device type for a console device
805
806         If set, the console device and a backing character device will
807         be added to the base arguments of the resulting QEMU command
808         line.
809
810         This is a convenience method that will either use the provided
811         device type, or default to a "-serial chardev:console" command
812         line argument.
813
814         The actual setting of command line arguments will be be done at
815         machine launch time, as it depends on the temporary directory
816         to be created.
817
818         @param device_type: the device type, such as "isa-serial".  If
819                             None is given (the default value) a "-serial
820                             chardev:console" command line argument will
821                             be used instead, resorting to the machine's
822                             default device type.
823         @param console_index: the index of the console device to use.
824                               If not zero, the command line will create
825                               'index - 1' consoles and connect them to
826                               the 'null' backing character device.
827         """
828         self._console_set = True
829         self._console_device_type = device_type
830         self._console_index = console_index
831
832     @property
833     def console_socket(self) -> socket.socket:
834         """
835         Returns a socket connected to the console
836         """
837         if self._console_socket is None:
838             self._console_socket = console_socket.ConsoleSocket(
839                 self._console_address,
840                 file=self._console_log_path,
841                 drain=self._drain_console)
842         return self._console_socket
843
844     @property
845     def temp_dir(self) -> str:
846         """
847         Returns a temporary directory to be used for this machine
848         """
849         if self._temp_dir is None:
850             self._temp_dir = tempfile.mkdtemp(prefix="qemu-machine-",
851                                               dir=self._base_temp_dir)
852         return self._temp_dir
853
854     @property
855     def sock_dir(self) -> str:
856         """
857         Returns the directory used for sockfiles by this machine.
858         """
859         if self._sock_dir:
860             return self._sock_dir
861         return self.temp_dir
862
863     @property
864     def log_dir(self) -> str:
865         """
866         Returns a directory to be used for writing logs
867         """
868         if self._log_dir is None:
869             return self.temp_dir
870         return self._log_dir
This page took 0.081736 seconds and 4 git commands to generate.