You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

machine.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. """
  2. QEMU machine module:
  3. The machine module primarily provides the QEMUMachine class,
  4. which provides facilities for managing the lifetime of a QEMU VM.
  5. """
  6. # Copyright (C) 2015-2016 Red Hat Inc.
  7. # Copyright (C) 2012 IBM Corp.
  8. #
  9. # Authors:
  10. # Fam Zheng <famz@redhat.com>
  11. #
  12. # This work is licensed under the terms of the GNU GPL, version 2. See
  13. # the COPYING file in the top-level directory.
  14. #
  15. # Based on qmp.py.
  16. #
  17. import errno
  18. import logging
  19. import os
  20. import subprocess
  21. import shutil
  22. import socket
  23. import tempfile
  24. from . import qmp
  25. LOG = logging.getLogger(__name__)
  26. class QEMUMachineError(Exception):
  27. """
  28. Exception called when an error in QEMUMachine happens.
  29. """
  30. class QEMUMachineAddDeviceError(QEMUMachineError):
  31. """
  32. Exception raised when a request to add a device can not be fulfilled
  33. The failures are caused by limitations, lack of information or conflicting
  34. requests on the QEMUMachine methods. This exception does not represent
  35. failures reported by the QEMU binary itself.
  36. """
  37. class MonitorResponseError(qmp.QMPError):
  38. """
  39. Represents erroneous QMP monitor reply
  40. """
  41. def __init__(self, reply):
  42. try:
  43. desc = reply["error"]["desc"]
  44. except KeyError:
  45. desc = reply
  46. super(MonitorResponseError, self).__init__(desc)
  47. self.reply = reply
  48. class QEMUMachine(object):
  49. """
  50. A QEMU VM
  51. Use this object as a context manager to ensure the QEMU process terminates::
  52. with VM(binary) as vm:
  53. ...
  54. # vm is guaranteed to be shut down here
  55. """
  56. def __init__(self, binary, args=None, wrapper=None, name=None,
  57. test_dir="/var/tmp", monitor_address=None,
  58. socket_scm_helper=None):
  59. '''
  60. Initialize a QEMUMachine
  61. @param binary: path to the qemu binary
  62. @param args: list of extra arguments
  63. @param wrapper: list of arguments used as prefix to qemu binary
  64. @param name: prefix for socket and log file names (default: qemu-PID)
  65. @param test_dir: where to create socket and log file
  66. @param monitor_address: address for QMP monitor
  67. @param socket_scm_helper: helper program, required for send_fd_scm()
  68. @note: Qemu process is not started until launch() is used.
  69. '''
  70. if args is None:
  71. args = []
  72. if wrapper is None:
  73. wrapper = []
  74. if name is None:
  75. name = "qemu-%d" % os.getpid()
  76. self._name = name
  77. self._monitor_address = monitor_address
  78. self._vm_monitor = None
  79. self._qemu_log_path = None
  80. self._qemu_log_file = None
  81. self._popen = None
  82. self._binary = binary
  83. self._args = list(args) # Force copy args in case we modify them
  84. self._wrapper = wrapper
  85. self._events = []
  86. self._iolog = None
  87. self._socket_scm_helper = socket_scm_helper
  88. self._qmp = None
  89. self._qemu_full_args = None
  90. self._test_dir = test_dir
  91. self._temp_dir = None
  92. self._launched = False
  93. self._machine = None
  94. self._console_set = False
  95. self._console_device_type = None
  96. self._console_address = None
  97. self._console_socket = None
  98. # just in case logging wasn't configured by the main script:
  99. logging.basicConfig()
  100. def __enter__(self):
  101. return self
  102. def __exit__(self, exc_type, exc_val, exc_tb):
  103. self.shutdown()
  104. return False
  105. def add_monitor_null(self):
  106. """
  107. This can be used to add an unused monitor instance.
  108. """
  109. self._args.append('-monitor')
  110. self._args.append('null')
  111. def add_fd(self, fd, fdset, opaque, opts=''):
  112. """
  113. Pass a file descriptor to the VM
  114. """
  115. options = ['fd=%d' % fd,
  116. 'set=%d' % fdset,
  117. 'opaque=%s' % opaque]
  118. if opts:
  119. options.append(opts)
  120. # This did not exist before 3.4, but since then it is
  121. # mandatory for our purpose
  122. if hasattr(os, 'set_inheritable'):
  123. os.set_inheritable(fd, True)
  124. self._args.append('-add-fd')
  125. self._args.append(','.join(options))
  126. return self
  127. def send_fd_scm(self, fd=None, file_path=None):
  128. """
  129. Send an fd or file_path to socket_scm_helper.
  130. Exactly one of fd and file_path must be given.
  131. If it is file_path, the helper will open that file and pass its own fd.
  132. """
  133. # In iotest.py, the qmp should always use unix socket.
  134. assert self._qmp.is_scm_available()
  135. if self._socket_scm_helper is None:
  136. raise QEMUMachineError("No path to socket_scm_helper set")
  137. if not os.path.exists(self._socket_scm_helper):
  138. raise QEMUMachineError("%s does not exist" %
  139. self._socket_scm_helper)
  140. # This did not exist before 3.4, but since then it is
  141. # mandatory for our purpose
  142. if hasattr(os, 'set_inheritable'):
  143. os.set_inheritable(self._qmp.get_sock_fd(), True)
  144. if fd is not None:
  145. os.set_inheritable(fd, True)
  146. fd_param = ["%s" % self._socket_scm_helper,
  147. "%d" % self._qmp.get_sock_fd()]
  148. if file_path is not None:
  149. assert fd is None
  150. fd_param.append(file_path)
  151. else:
  152. assert fd is not None
  153. fd_param.append(str(fd))
  154. devnull = open(os.path.devnull, 'rb')
  155. proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
  156. stderr=subprocess.STDOUT, close_fds=False)
  157. output = proc.communicate()[0]
  158. if output:
  159. LOG.debug(output)
  160. return proc.returncode
  161. @staticmethod
  162. def _remove_if_exists(path):
  163. """
  164. Remove file object at path if it exists
  165. """
  166. try:
  167. os.remove(path)
  168. except OSError as exception:
  169. if exception.errno == errno.ENOENT:
  170. return
  171. raise
  172. def is_running(self):
  173. """Returns true if the VM is running."""
  174. return self._popen is not None and self._popen.poll() is None
  175. def exitcode(self):
  176. """Returns the exit code if possible, or None."""
  177. if self._popen is None:
  178. return None
  179. return self._popen.poll()
  180. def get_pid(self):
  181. """Returns the PID of the running process, or None."""
  182. if not self.is_running():
  183. return None
  184. return self._popen.pid
  185. def _load_io_log(self):
  186. if self._qemu_log_path is not None:
  187. with open(self._qemu_log_path, "r") as iolog:
  188. self._iolog = iolog.read()
  189. def _base_args(self):
  190. if isinstance(self._monitor_address, tuple):
  191. moncdev = "socket,id=mon,host=%s,port=%s" % (
  192. self._monitor_address[0],
  193. self._monitor_address[1])
  194. else:
  195. moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
  196. args = ['-chardev', moncdev,
  197. '-mon', 'chardev=mon,mode=control',
  198. '-display', 'none', '-vga', 'none']
  199. if self._machine is not None:
  200. args.extend(['-machine', self._machine])
  201. if self._console_set:
  202. self._console_address = os.path.join(self._temp_dir,
  203. self._name + "-console.sock")
  204. chardev = ('socket,id=console,path=%s,server,nowait' %
  205. self._console_address)
  206. args.extend(['-chardev', chardev])
  207. if self._console_device_type is None:
  208. args.extend(['-serial', 'chardev:console'])
  209. else:
  210. device = '%s,chardev=console' % self._console_device_type
  211. args.extend(['-device', device])
  212. return args
  213. def _pre_launch(self):
  214. self._temp_dir = tempfile.mkdtemp(dir=self._test_dir)
  215. if self._monitor_address is not None:
  216. self._vm_monitor = self._monitor_address
  217. else:
  218. self._vm_monitor = os.path.join(self._temp_dir,
  219. self._name + "-monitor.sock")
  220. self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
  221. self._qemu_log_file = open(self._qemu_log_path, 'wb')
  222. self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
  223. server=True)
  224. def _post_launch(self):
  225. self._qmp.accept()
  226. def _post_shutdown(self):
  227. if self._qemu_log_file is not None:
  228. self._qemu_log_file.close()
  229. self._qemu_log_file = None
  230. self._qemu_log_path = None
  231. if self._console_socket is not None:
  232. self._console_socket.close()
  233. self._console_socket = None
  234. if self._temp_dir is not None:
  235. shutil.rmtree(self._temp_dir)
  236. self._temp_dir = None
  237. def launch(self):
  238. """
  239. Launch the VM and make sure we cleanup and expose the
  240. command line/output in case of exception
  241. """
  242. if self._launched:
  243. raise QEMUMachineError('VM already launched')
  244. self._iolog = None
  245. self._qemu_full_args = None
  246. try:
  247. self._launch()
  248. self._launched = True
  249. except:
  250. self.shutdown()
  251. LOG.debug('Error launching VM')
  252. if self._qemu_full_args:
  253. LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
  254. if self._iolog:
  255. LOG.debug('Output: %r', self._iolog)
  256. raise
  257. def _launch(self):
  258. """
  259. Launch the VM and establish a QMP connection
  260. """
  261. devnull = open(os.path.devnull, 'rb')
  262. self._pre_launch()
  263. self._qemu_full_args = (self._wrapper + [self._binary] +
  264. self._base_args() + self._args)
  265. LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
  266. self._popen = subprocess.Popen(self._qemu_full_args,
  267. stdin=devnull,
  268. stdout=self._qemu_log_file,
  269. stderr=subprocess.STDOUT,
  270. shell=False,
  271. close_fds=False)
  272. self._post_launch()
  273. def wait(self):
  274. """
  275. Wait for the VM to power off
  276. """
  277. self._popen.wait()
  278. self._qmp.close()
  279. self._load_io_log()
  280. self._post_shutdown()
  281. def shutdown(self, has_quit=False):
  282. """
  283. Terminate the VM and clean up
  284. """
  285. if self.is_running():
  286. try:
  287. if not has_quit:
  288. self._qmp.cmd('quit')
  289. self._qmp.close()
  290. except:
  291. self._popen.kill()
  292. self._popen.wait()
  293. self._load_io_log()
  294. self._post_shutdown()
  295. exitcode = self.exitcode()
  296. if exitcode is not None and exitcode < 0:
  297. msg = 'qemu received signal %i: %s'
  298. if self._qemu_full_args:
  299. command = ' '.join(self._qemu_full_args)
  300. else:
  301. command = ''
  302. LOG.warning(msg, -exitcode, command)
  303. self._launched = False
  304. def qmp(self, cmd, conv_keys=True, **args):
  305. """
  306. Invoke a QMP command and return the response dict
  307. """
  308. qmp_args = dict()
  309. for key, value in args.items():
  310. if conv_keys:
  311. qmp_args[key.replace('_', '-')] = value
  312. else:
  313. qmp_args[key] = value
  314. return self._qmp.cmd(cmd, args=qmp_args)
  315. def command(self, cmd, conv_keys=True, **args):
  316. """
  317. Invoke a QMP command.
  318. On success return the response dict.
  319. On failure raise an exception.
  320. """
  321. reply = self.qmp(cmd, conv_keys, **args)
  322. if reply is None:
  323. raise qmp.QMPError("Monitor is closed")
  324. if "error" in reply:
  325. raise MonitorResponseError(reply)
  326. return reply["return"]
  327. def get_qmp_event(self, wait=False):
  328. """
  329. Poll for one queued QMP events and return it
  330. """
  331. if self._events:
  332. return self._events.pop(0)
  333. return self._qmp.pull_event(wait=wait)
  334. def get_qmp_events(self, wait=False):
  335. """
  336. Poll for queued QMP events and return a list of dicts
  337. """
  338. events = self._qmp.get_events(wait=wait)
  339. events.extend(self._events)
  340. del self._events[:]
  341. self._qmp.clear_events()
  342. return events
  343. @staticmethod
  344. def event_match(event, match=None):
  345. """
  346. Check if an event matches optional match criteria.
  347. The match criteria takes the form of a matching subdict. The event is
  348. checked to be a superset of the subdict, recursively, with matching
  349. values whenever the subdict values are not None.
  350. This has a limitation that you cannot explicitly check for None values.
  351. Examples, with the subdict queries on the left:
  352. - None matches any object.
  353. - {"foo": None} matches {"foo": {"bar": 1}}
  354. - {"foo": None} matches {"foo": 5}
  355. - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
  356. - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
  357. """
  358. if match is None:
  359. return True
  360. try:
  361. for key in match:
  362. if key in event:
  363. if not QEMUMachine.event_match(event[key], match[key]):
  364. return False
  365. else:
  366. return False
  367. return True
  368. except TypeError:
  369. # either match or event wasn't iterable (not a dict)
  370. return match == event
  371. def event_wait(self, name, timeout=60.0, match=None):
  372. """
  373. event_wait waits for and returns a named event from QMP with a timeout.
  374. name: The event to wait for.
  375. timeout: QEMUMonitorProtocol.pull_event timeout parameter.
  376. match: Optional match criteria. See event_match for details.
  377. """
  378. return self.events_wait([(name, match)], timeout)
  379. def events_wait(self, events, timeout=60.0):
  380. """
  381. events_wait waits for and returns a named event from QMP with a timeout.
  382. events: a sequence of (name, match_criteria) tuples.
  383. The match criteria are optional and may be None.
  384. See event_match for details.
  385. timeout: QEMUMonitorProtocol.pull_event timeout parameter.
  386. """
  387. def _match(event):
  388. for name, match in events:
  389. if event['event'] == name and self.event_match(event, match):
  390. return True
  391. return False
  392. # Search cached events
  393. for event in self._events:
  394. if _match(event):
  395. self._events.remove(event)
  396. return event
  397. # Poll for new events
  398. while True:
  399. event = self._qmp.pull_event(wait=timeout)
  400. if _match(event):
  401. return event
  402. self._events.append(event)
  403. return None
  404. def get_log(self):
  405. """
  406. After self.shutdown or failed qemu execution, this returns the output
  407. of the qemu process.
  408. """
  409. return self._iolog
  410. def add_args(self, *args):
  411. """
  412. Adds to the list of extra arguments to be given to the QEMU binary
  413. """
  414. self._args.extend(args)
  415. def set_machine(self, machine_type):
  416. """
  417. Sets the machine type
  418. If set, the machine type will be added to the base arguments
  419. of the resulting QEMU command line.
  420. """
  421. self._machine = machine_type
  422. def set_console(self, device_type=None):
  423. """
  424. Sets the device type for a console device
  425. If set, the console device and a backing character device will
  426. be added to the base arguments of the resulting QEMU command
  427. line.
  428. This is a convenience method that will either use the provided
  429. device type, or default to a "-serial chardev:console" command
  430. line argument.
  431. The actual setting of command line arguments will be be done at
  432. machine launch time, as it depends on the temporary directory
  433. to be created.
  434. @param device_type: the device type, such as "isa-serial". If
  435. None is given (the default value) a "-serial
  436. chardev:console" command line argument will
  437. be used instead, resorting to the machine's
  438. default device type.
  439. """
  440. self._console_set = True
  441. self._console_device_type = device_type
  442. @property
  443. def console_socket(self):
  444. """
  445. Returns a socket connected to the console
  446. """
  447. if self._console_socket is None:
  448. self._console_socket = socket.socket(socket.AF_UNIX,
  449. socket.SOCK_STREAM)
  450. self._console_socket.connect(self._console_address)
  451. return self._console_socket