Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm / monitor.py @ d9982f38

History | View | Annotate | Download (21.8 kB)

1 904d32bc Dimitris Aragiorgis
#
2 904d32bc Dimitris Aragiorgis
#
3 904d32bc Dimitris Aragiorgis
4 904d32bc Dimitris Aragiorgis
# Copyright (C) 2014 Google Inc.
5 904d32bc Dimitris Aragiorgis
# All rights reserved.
6 904d32bc Dimitris Aragiorgis
#
7 904d32bc Dimitris Aragiorgis
# Redistribution and use in source and binary forms, with or without
8 904d32bc Dimitris Aragiorgis
# modification, are permitted provided that the following conditions are
9 904d32bc Dimitris Aragiorgis
# met:
10 904d32bc Dimitris Aragiorgis
#
11 904d32bc Dimitris Aragiorgis
# 1. Redistributions of source code must retain the above copyright notice,
12 904d32bc Dimitris Aragiorgis
# this list of conditions and the following disclaimer.
13 904d32bc Dimitris Aragiorgis
#
14 904d32bc Dimitris Aragiorgis
# 2. Redistributions in binary form must reproduce the above copyright
15 904d32bc Dimitris Aragiorgis
# notice, this list of conditions and the following disclaimer in the
16 904d32bc Dimitris Aragiorgis
# documentation and/or other materials provided with the distribution.
17 904d32bc Dimitris Aragiorgis
#
18 904d32bc Dimitris Aragiorgis
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 904d32bc Dimitris Aragiorgis
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 904d32bc Dimitris Aragiorgis
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 904d32bc Dimitris Aragiorgis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 904d32bc Dimitris Aragiorgis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 904d32bc Dimitris Aragiorgis
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 904d32bc Dimitris Aragiorgis
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 904d32bc Dimitris Aragiorgis
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 904d32bc Dimitris Aragiorgis
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 904d32bc Dimitris Aragiorgis
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 904d32bc Dimitris Aragiorgis
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 904d32bc Dimitris Aragiorgis
30 904d32bc Dimitris Aragiorgis
31 904d32bc Dimitris Aragiorgis
"""Qemu monitor control classes
32 904d32bc Dimitris Aragiorgis

33 904d32bc Dimitris Aragiorgis
"""
34 904d32bc Dimitris Aragiorgis
35 904d32bc Dimitris Aragiorgis
import os
36 904d32bc Dimitris Aragiorgis
import stat
37 904d32bc Dimitris Aragiorgis
import errno
38 904d32bc Dimitris Aragiorgis
import socket
39 904d32bc Dimitris Aragiorgis
import StringIO
40 655188a2 Dimitris Aragiorgis
import logging
41 655188a2 Dimitris Aragiorgis
try:
42 655188a2 Dimitris Aragiorgis
  import fdsend   # pylint: disable=F0401
43 655188a2 Dimitris Aragiorgis
except ImportError:
44 655188a2 Dimitris Aragiorgis
  fdsend = None
45 904d32bc Dimitris Aragiorgis
46 565fa7f1 Dimitris Aragiorgis
from bitarray import bitarray
47 565fa7f1 Dimitris Aragiorgis
48 904d32bc Dimitris Aragiorgis
from ganeti import errors
49 904d32bc Dimitris Aragiorgis
from ganeti import utils
50 904d32bc Dimitris Aragiorgis
from ganeti import serializer
51 904d32bc Dimitris Aragiorgis
52 904d32bc Dimitris Aragiorgis
53 6c464d30 Apollon Oikonomopoulos
class QmpCommandNotSupported(errors.HypervisorError):
54 6c464d30 Apollon Oikonomopoulos
  """QMP command not supported by the monitor.
55 6c464d30 Apollon Oikonomopoulos

56 6c464d30 Apollon Oikonomopoulos
  This is raised in case a QmpMonitor instance is asked to execute a command
57 6c464d30 Apollon Oikonomopoulos
  not supported by the instance.
58 6c464d30 Apollon Oikonomopoulos

59 6c464d30 Apollon Oikonomopoulos
  This is a KVM-specific exception, intended to assist in falling back to using
60 6c464d30 Apollon Oikonomopoulos
  the human monitor for operations QMP does not support.
61 6c464d30 Apollon Oikonomopoulos

62 6c464d30 Apollon Oikonomopoulos
  """
63 6c464d30 Apollon Oikonomopoulos
  pass
64 6c464d30 Apollon Oikonomopoulos
65 6c464d30 Apollon Oikonomopoulos
66 8acd00fc Apollon Oikonomopoulos
class QmpMessage(object):
67 904d32bc Dimitris Aragiorgis
  """QEMU Messaging Protocol (QMP) message.
68 904d32bc Dimitris Aragiorgis

69 904d32bc Dimitris Aragiorgis
  """
70 904d32bc Dimitris Aragiorgis
  def __init__(self, data):
71 904d32bc Dimitris Aragiorgis
    """Creates a new QMP message based on the passed data.
72 904d32bc Dimitris Aragiorgis

73 904d32bc Dimitris Aragiorgis
    """
74 904d32bc Dimitris Aragiorgis
    if not isinstance(data, dict):
75 904d32bc Dimitris Aragiorgis
      raise TypeError("QmpMessage must be initialized with a dict")
76 904d32bc Dimitris Aragiorgis
77 904d32bc Dimitris Aragiorgis
    self.data = data
78 904d32bc Dimitris Aragiorgis
79 904d32bc Dimitris Aragiorgis
  def __getitem__(self, field_name):
80 904d32bc Dimitris Aragiorgis
    """Get the value of the required field if present, or None.
81 904d32bc Dimitris Aragiorgis

82 904d32bc Dimitris Aragiorgis
    Overrides the [] operator to provide access to the message data,
83 904d32bc Dimitris Aragiorgis
    returning None if the required item is not in the message
84 904d32bc Dimitris Aragiorgis
    @return: the value of the field_name field, or None if field_name
85 904d32bc Dimitris Aragiorgis
             is not contained in the message
86 904d32bc Dimitris Aragiorgis

87 904d32bc Dimitris Aragiorgis
    """
88 904d32bc Dimitris Aragiorgis
    return self.data.get(field_name, None)
89 904d32bc Dimitris Aragiorgis
90 904d32bc Dimitris Aragiorgis
  def __setitem__(self, field_name, field_value):
91 904d32bc Dimitris Aragiorgis
    """Set the value of the required field_name to field_value.
92 904d32bc Dimitris Aragiorgis

93 904d32bc Dimitris Aragiorgis
    """
94 904d32bc Dimitris Aragiorgis
    self.data[field_name] = field_value
95 904d32bc Dimitris Aragiorgis
96 904d32bc Dimitris Aragiorgis
  def __len__(self):
97 904d32bc Dimitris Aragiorgis
    """Return the number of fields stored in this QmpMessage.
98 904d32bc Dimitris Aragiorgis

99 904d32bc Dimitris Aragiorgis
    """
100 904d32bc Dimitris Aragiorgis
    return len(self.data)
101 904d32bc Dimitris Aragiorgis
102 904d32bc Dimitris Aragiorgis
  def __delitem__(self, key):
103 904d32bc Dimitris Aragiorgis
    """Delete the specified element from the QmpMessage.
104 904d32bc Dimitris Aragiorgis

105 904d32bc Dimitris Aragiorgis
    """
106 904d32bc Dimitris Aragiorgis
    del(self.data[key])
107 904d32bc Dimitris Aragiorgis
108 904d32bc Dimitris Aragiorgis
  @staticmethod
109 904d32bc Dimitris Aragiorgis
  def BuildFromJsonString(json_string):
110 904d32bc Dimitris Aragiorgis
    """Build a QmpMessage from a JSON encoded string.
111 904d32bc Dimitris Aragiorgis

112 904d32bc Dimitris Aragiorgis
    @type json_string: str
113 904d32bc Dimitris Aragiorgis
    @param json_string: JSON string representing the message
114 904d32bc Dimitris Aragiorgis
    @rtype: L{QmpMessage}
115 904d32bc Dimitris Aragiorgis
    @return: a L{QmpMessage} built from json_string
116 904d32bc Dimitris Aragiorgis

117 904d32bc Dimitris Aragiorgis
    """
118 904d32bc Dimitris Aragiorgis
    # Parse the string
119 904d32bc Dimitris Aragiorgis
    data = serializer.LoadJson(json_string)
120 904d32bc Dimitris Aragiorgis
    return QmpMessage(data)
121 904d32bc Dimitris Aragiorgis
122 904d32bc Dimitris Aragiorgis
  def __str__(self):
123 904d32bc Dimitris Aragiorgis
    # The protocol expects the JSON object to be sent as a single line.
124 904d32bc Dimitris Aragiorgis
    return serializer.DumpJson(self.data)
125 904d32bc Dimitris Aragiorgis
126 904d32bc Dimitris Aragiorgis
  def __eq__(self, other):
127 904d32bc Dimitris Aragiorgis
    # When comparing two QmpMessages, we are interested in comparing
128 904d32bc Dimitris Aragiorgis
    # their internal representation of the message data
129 904d32bc Dimitris Aragiorgis
    return self.data == other.data
130 904d32bc Dimitris Aragiorgis
131 904d32bc Dimitris Aragiorgis
132 904d32bc Dimitris Aragiorgis
class MonitorSocket(object):
133 904d32bc Dimitris Aragiorgis
  _SOCKET_TIMEOUT = 5
134 904d32bc Dimitris Aragiorgis
135 904d32bc Dimitris Aragiorgis
  def __init__(self, monitor_filename):
136 904d32bc Dimitris Aragiorgis
    """Instantiates the MonitorSocket object.
137 904d32bc Dimitris Aragiorgis

138 904d32bc Dimitris Aragiorgis
    @type monitor_filename: string
139 904d32bc Dimitris Aragiorgis
    @param monitor_filename: the filename of the UNIX raw socket on which the
140 904d32bc Dimitris Aragiorgis
                             monitor (QMP or simple one) is listening
141 904d32bc Dimitris Aragiorgis

142 904d32bc Dimitris Aragiorgis
    """
143 904d32bc Dimitris Aragiorgis
    self.monitor_filename = monitor_filename
144 904d32bc Dimitris Aragiorgis
    self._connected = False
145 904d32bc Dimitris Aragiorgis
146 904d32bc Dimitris Aragiorgis
  def _check_socket(self):
147 904d32bc Dimitris Aragiorgis
    sock_stat = None
148 904d32bc Dimitris Aragiorgis
    try:
149 904d32bc Dimitris Aragiorgis
      sock_stat = os.stat(self.monitor_filename)
150 904d32bc Dimitris Aragiorgis
    except EnvironmentError, err:
151 904d32bc Dimitris Aragiorgis
      if err.errno == errno.ENOENT:
152 904d32bc Dimitris Aragiorgis
        raise errors.HypervisorError("No monitor socket found")
153 904d32bc Dimitris Aragiorgis
      else:
154 904d32bc Dimitris Aragiorgis
        raise errors.HypervisorError("Error checking monitor socket: %s",
155 904d32bc Dimitris Aragiorgis
                                     utils.ErrnoOrStr(err))
156 904d32bc Dimitris Aragiorgis
    if not stat.S_ISSOCK(sock_stat.st_mode):
157 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Monitor socket is not a socket")
158 904d32bc Dimitris Aragiorgis
159 904d32bc Dimitris Aragiorgis
  def _check_connection(self):
160 904d32bc Dimitris Aragiorgis
    """Make sure that the connection is established.
161 904d32bc Dimitris Aragiorgis

162 904d32bc Dimitris Aragiorgis
    """
163 904d32bc Dimitris Aragiorgis
    if not self._connected:
164 904d32bc Dimitris Aragiorgis
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
165 904d32bc Dimitris Aragiorgis
                                   " invoke connect() on it")
166 904d32bc Dimitris Aragiorgis
167 904d32bc Dimitris Aragiorgis
  def connect(self):
168 fcad5eb4 Dimitris Aragiorgis
    """Connect to the monitor socket if not already connected.
169 fcad5eb4 Dimitris Aragiorgis

170 fcad5eb4 Dimitris Aragiorgis
    """
171 fcad5eb4 Dimitris Aragiorgis
    if not self._connected:
172 fcad5eb4 Dimitris Aragiorgis
      self._connect()
173 fcad5eb4 Dimitris Aragiorgis
174 d9982f38 Dimitris Aragiorgis
  def is_connected(self):
175 d9982f38 Dimitris Aragiorgis
    """Return whether there is a connection to the socket or not.
176 d9982f38 Dimitris Aragiorgis

177 d9982f38 Dimitris Aragiorgis
    """
178 d9982f38 Dimitris Aragiorgis
    return self._connected
179 d9982f38 Dimitris Aragiorgis
180 fcad5eb4 Dimitris Aragiorgis
  def _connect(self):
181 904d32bc Dimitris Aragiorgis
    """Connects to the monitor.
182 904d32bc Dimitris Aragiorgis

183 904d32bc Dimitris Aragiorgis
    Connects to the UNIX socket
184 904d32bc Dimitris Aragiorgis

185 904d32bc Dimitris Aragiorgis
    @raise errors.HypervisorError: when there are communication errors
186 904d32bc Dimitris Aragiorgis

187 904d32bc Dimitris Aragiorgis
    """
188 904d32bc Dimitris Aragiorgis
    if self._connected:
189 904d32bc Dimitris Aragiorgis
      raise errors.ProgrammerError("Cannot connect twice")
190 904d32bc Dimitris Aragiorgis
191 904d32bc Dimitris Aragiorgis
    self._check_socket()
192 904d32bc Dimitris Aragiorgis
193 904d32bc Dimitris Aragiorgis
    # Check file existance/stuff
194 904d32bc Dimitris Aragiorgis
    try:
195 fcad5eb4 Dimitris Aragiorgis
      self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
196 fcad5eb4 Dimitris Aragiorgis
      # We want to fail if the server doesn't send a complete message
197 fcad5eb4 Dimitris Aragiorgis
      # in a reasonable amount of time
198 fcad5eb4 Dimitris Aragiorgis
      self.sock.settimeout(self._SOCKET_TIMEOUT)
199 904d32bc Dimitris Aragiorgis
      self.sock.connect(self.monitor_filename)
200 904d32bc Dimitris Aragiorgis
    except EnvironmentError:
201 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Can't connect to qmp socket")
202 904d32bc Dimitris Aragiorgis
    self._connected = True
203 904d32bc Dimitris Aragiorgis
204 904d32bc Dimitris Aragiorgis
  def close(self):
205 904d32bc Dimitris Aragiorgis
    """Closes the socket
206 904d32bc Dimitris Aragiorgis

207 904d32bc Dimitris Aragiorgis
    It cannot be used after this call.
208 904d32bc Dimitris Aragiorgis

209 904d32bc Dimitris Aragiorgis
    """
210 d36a401b Dimitris Aragiorgis
    if self._connected:
211 d36a401b Dimitris Aragiorgis
      self._close()
212 d36a401b Dimitris Aragiorgis
213 d36a401b Dimitris Aragiorgis
  def _close(self):
214 904d32bc Dimitris Aragiorgis
    self.sock.close()
215 fcad5eb4 Dimitris Aragiorgis
    self._connected = False
216 904d32bc Dimitris Aragiorgis
217 904d32bc Dimitris Aragiorgis
218 d9982f38 Dimitris Aragiorgis
def _ensure_connection(fn):
219 d9982f38 Dimitris Aragiorgis
  """Decorator that wraps MonitorSocket external methods"""
220 d9982f38 Dimitris Aragiorgis
  def wrapper(*args, **kwargs):
221 d9982f38 Dimitris Aragiorgis
    """Ensure proper connect/close and exception propagation"""
222 d9982f38 Dimitris Aragiorgis
    mon = args[0]
223 d9982f38 Dimitris Aragiorgis
    already_connected = mon.is_connected()
224 d9982f38 Dimitris Aragiorgis
    mon.connect()
225 d9982f38 Dimitris Aragiorgis
    try:
226 d9982f38 Dimitris Aragiorgis
      ret = fn(*args, **kwargs)
227 d9982f38 Dimitris Aragiorgis
    finally:
228 d9982f38 Dimitris Aragiorgis
      # In general this decorator wraps external methods.
229 d9982f38 Dimitris Aragiorgis
      # Here we close the connection only if we initiated it before,
230 d9982f38 Dimitris Aragiorgis
      # to protect us from using the socket after closing it
231 d9982f38 Dimitris Aragiorgis
      # in case we invoke a decorated method internally by accident.
232 d9982f38 Dimitris Aragiorgis
      if not already_connected:
233 d9982f38 Dimitris Aragiorgis
        mon.close()
234 d9982f38 Dimitris Aragiorgis
    return ret
235 d9982f38 Dimitris Aragiorgis
  return wrapper
236 d9982f38 Dimitris Aragiorgis
237 d9982f38 Dimitris Aragiorgis
238 904d32bc Dimitris Aragiorgis
class QmpConnection(MonitorSocket):
239 904d32bc Dimitris Aragiorgis
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
240 904d32bc Dimitris Aragiorgis

241 904d32bc Dimitris Aragiorgis
  """
242 904d32bc Dimitris Aragiorgis
  _FIRST_MESSAGE_KEY = "QMP"
243 904d32bc Dimitris Aragiorgis
  _EVENT_KEY = "event"
244 904d32bc Dimitris Aragiorgis
  _ERROR_KEY = "error"
245 546cdf7a Apollon Oikonomopoulos
  _RETURN_KEY = "return"
246 904d32bc Dimitris Aragiorgis
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
247 904d32bc Dimitris Aragiorgis
  _ERROR_CLASS_KEY = "class"
248 904d32bc Dimitris Aragiorgis
  _ERROR_DESC_KEY = "desc"
249 904d32bc Dimitris Aragiorgis
  _EXECUTE_KEY = "execute"
250 904d32bc Dimitris Aragiorgis
  _ARGUMENTS_KEY = "arguments"
251 7b648a55 Apollon Oikonomopoulos
  _VERSION_KEY = "version"
252 7b648a55 Apollon Oikonomopoulos
  _PACKAGE_KEY = "package"
253 7b648a55 Apollon Oikonomopoulos
  _QEMU_KEY = "qemu"
254 904d32bc Dimitris Aragiorgis
  _CAPABILITIES_COMMAND = "qmp_capabilities"
255 9b7ef3eb Apollon Oikonomopoulos
  _QUERY_COMMANDS = "query-commands"
256 904d32bc Dimitris Aragiorgis
  _MESSAGE_END_TOKEN = "\r\n"
257 565fa7f1 Dimitris Aragiorgis
  _QEMU_PCI_SLOTS = 32 # The number of PCI slots QEMU exposes by default
258 904d32bc Dimitris Aragiorgis
259 904d32bc Dimitris Aragiorgis
  def __init__(self, monitor_filename):
260 904d32bc Dimitris Aragiorgis
    super(QmpConnection, self).__init__(monitor_filename)
261 904d32bc Dimitris Aragiorgis
    self._buf = ""
262 6c464d30 Apollon Oikonomopoulos
    self.supported_commands = None
263 904d32bc Dimitris Aragiorgis
264 13bb1b4c Apollon Oikonomopoulos
  def __enter__(self):
265 13bb1b4c Apollon Oikonomopoulos
    self.connect()
266 13bb1b4c Apollon Oikonomopoulos
    return self
267 13bb1b4c Apollon Oikonomopoulos
268 13bb1b4c Apollon Oikonomopoulos
  def __exit__(self, exc_type, exc_value, tb):
269 13bb1b4c Apollon Oikonomopoulos
    self.close()
270 13bb1b4c Apollon Oikonomopoulos
271 904d32bc Dimitris Aragiorgis
  def connect(self):
272 904d32bc Dimitris Aragiorgis
    """Connects to the QMP monitor.
273 904d32bc Dimitris Aragiorgis

274 904d32bc Dimitris Aragiorgis
    Connects to the UNIX socket and makes sure that we can actually send and
275 904d32bc Dimitris Aragiorgis
    receive data to the kvm instance via QMP.
276 904d32bc Dimitris Aragiorgis

277 904d32bc Dimitris Aragiorgis
    @raise errors.HypervisorError: when there are communication errors
278 904d32bc Dimitris Aragiorgis
    @raise errors.ProgrammerError: when there are data serialization errors
279 904d32bc Dimitris Aragiorgis

280 904d32bc Dimitris Aragiorgis
    """
281 904d32bc Dimitris Aragiorgis
    super(QmpConnection, self).connect()
282 904d32bc Dimitris Aragiorgis
    # Check if we receive a correct greeting message from the server
283 904d32bc Dimitris Aragiorgis
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
284 904d32bc Dimitris Aragiorgis
    greeting = self._Recv()
285 904d32bc Dimitris Aragiorgis
    if not greeting[self._FIRST_MESSAGE_KEY]:
286 904d32bc Dimitris Aragiorgis
      self._connected = False
287 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
288 904d32bc Dimitris Aragiorgis
                                   " server greeting")
289 904d32bc Dimitris Aragiorgis
290 7b648a55 Apollon Oikonomopoulos
    # Extract the version info from the greeting and make it available to users
291 7b648a55 Apollon Oikonomopoulos
    # of the monitor.
292 7b648a55 Apollon Oikonomopoulos
    version_info = greeting[self._FIRST_MESSAGE_KEY][self._VERSION_KEY]
293 7b648a55 Apollon Oikonomopoulos
294 7b648a55 Apollon Oikonomopoulos
    self.version = (version_info[self._QEMU_KEY]["major"],
295 7b648a55 Apollon Oikonomopoulos
                    version_info[self._QEMU_KEY]["minor"],
296 7b648a55 Apollon Oikonomopoulos
                    version_info[self._QEMU_KEY]["micro"])
297 7b648a55 Apollon Oikonomopoulos
    self.package = version_info[self._PACKAGE_KEY].strip()
298 7b648a55 Apollon Oikonomopoulos
299 904d32bc Dimitris Aragiorgis
    # This is needed because QMP can return more than one greetings
300 904d32bc Dimitris Aragiorgis
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
301 904d32bc Dimitris Aragiorgis
    self._buf = ""
302 904d32bc Dimitris Aragiorgis
303 904d32bc Dimitris Aragiorgis
    # Let's put the monitor in command mode using the qmp_capabilities
304 904d32bc Dimitris Aragiorgis
    # command, or else no command will be executable.
305 904d32bc Dimitris Aragiorgis
    # (As per the QEMU Protocol Specification 0.1 - section 4)
306 904d32bc Dimitris Aragiorgis
    self.Execute(self._CAPABILITIES_COMMAND)
307 9b7ef3eb Apollon Oikonomopoulos
    self.supported_commands = self._GetSupportedCommands()
308 904d32bc Dimitris Aragiorgis
309 904d32bc Dimitris Aragiorgis
  def _ParseMessage(self, buf):
310 904d32bc Dimitris Aragiorgis
    """Extract and parse a QMP message from the given buffer.
311 904d32bc Dimitris Aragiorgis

312 904d32bc Dimitris Aragiorgis
    Seeks for a QMP message in the given buf. If found, it parses it and
313 904d32bc Dimitris Aragiorgis
    returns it together with the rest of the characters in the buf.
314 904d32bc Dimitris Aragiorgis
    If no message is found, returns None and the whole buffer.
315 904d32bc Dimitris Aragiorgis

316 904d32bc Dimitris Aragiorgis
    @raise errors.ProgrammerError: when there are data serialization errors
317 904d32bc Dimitris Aragiorgis

318 904d32bc Dimitris Aragiorgis
    """
319 904d32bc Dimitris Aragiorgis
    message = None
320 904d32bc Dimitris Aragiorgis
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
321 904d32bc Dimitris Aragiorgis
    # Specification 0.1 - Section 2.1.1)
322 904d32bc Dimitris Aragiorgis
    pos = buf.find(self._MESSAGE_END_TOKEN)
323 904d32bc Dimitris Aragiorgis
    if pos >= 0:
324 904d32bc Dimitris Aragiorgis
      try:
325 904d32bc Dimitris Aragiorgis
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
326 904d32bc Dimitris Aragiorgis
      except Exception, err:
327 904d32bc Dimitris Aragiorgis
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
328 904d32bc Dimitris Aragiorgis
      buf = buf[pos + 1:]
329 904d32bc Dimitris Aragiorgis
330 904d32bc Dimitris Aragiorgis
    return (message, buf)
331 904d32bc Dimitris Aragiorgis
332 904d32bc Dimitris Aragiorgis
  def _Recv(self):
333 904d32bc Dimitris Aragiorgis
    """Receives a message from QMP and decodes the received JSON object.
334 904d32bc Dimitris Aragiorgis

335 904d32bc Dimitris Aragiorgis
    @rtype: QmpMessage
336 904d32bc Dimitris Aragiorgis
    @return: the received message
337 904d32bc Dimitris Aragiorgis
    @raise errors.HypervisorError: when there are communication errors
338 904d32bc Dimitris Aragiorgis
    @raise errors.ProgrammerError: when there are data serialization errors
339 904d32bc Dimitris Aragiorgis

340 904d32bc Dimitris Aragiorgis
    """
341 904d32bc Dimitris Aragiorgis
    self._check_connection()
342 904d32bc Dimitris Aragiorgis
343 904d32bc Dimitris Aragiorgis
    # Check if there is already a message in the buffer
344 904d32bc Dimitris Aragiorgis
    (message, self._buf) = self._ParseMessage(self._buf)
345 904d32bc Dimitris Aragiorgis
    if message:
346 904d32bc Dimitris Aragiorgis
      return message
347 904d32bc Dimitris Aragiorgis
348 904d32bc Dimitris Aragiorgis
    recv_buffer = StringIO.StringIO(self._buf)
349 904d32bc Dimitris Aragiorgis
    recv_buffer.seek(len(self._buf))
350 904d32bc Dimitris Aragiorgis
    try:
351 904d32bc Dimitris Aragiorgis
      while True:
352 904d32bc Dimitris Aragiorgis
        data = self.sock.recv(4096)
353 904d32bc Dimitris Aragiorgis
        if not data:
354 904d32bc Dimitris Aragiorgis
          break
355 904d32bc Dimitris Aragiorgis
        recv_buffer.write(data)
356 904d32bc Dimitris Aragiorgis
357 904d32bc Dimitris Aragiorgis
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
358 904d32bc Dimitris Aragiorgis
        if message:
359 904d32bc Dimitris Aragiorgis
          return message
360 904d32bc Dimitris Aragiorgis
361 904d32bc Dimitris Aragiorgis
    except socket.timeout, err:
362 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
363 904d32bc Dimitris Aragiorgis
                                   "%s" % (err))
364 904d32bc Dimitris Aragiorgis
    except socket.error, err:
365 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Unable to receive data from KVM using the"
366 904d32bc Dimitris Aragiorgis
                                   " QMP protocol: %s" % err)
367 904d32bc Dimitris Aragiorgis
368 904d32bc Dimitris Aragiorgis
  def _Send(self, message):
369 904d32bc Dimitris Aragiorgis
    """Encodes and sends a message to KVM using QMP.
370 904d32bc Dimitris Aragiorgis

371 904d32bc Dimitris Aragiorgis
    @type message: QmpMessage
372 904d32bc Dimitris Aragiorgis
    @param message: message to send to KVM
373 904d32bc Dimitris Aragiorgis
    @raise errors.HypervisorError: when there are communication errors
374 904d32bc Dimitris Aragiorgis
    @raise errors.ProgrammerError: when there are data serialization errors
375 904d32bc Dimitris Aragiorgis

376 904d32bc Dimitris Aragiorgis
    """
377 904d32bc Dimitris Aragiorgis
    self._check_connection()
378 904d32bc Dimitris Aragiorgis
    try:
379 904d32bc Dimitris Aragiorgis
      message_str = str(message)
380 904d32bc Dimitris Aragiorgis
    except Exception, err:
381 904d32bc Dimitris Aragiorgis
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
382 904d32bc Dimitris Aragiorgis
383 904d32bc Dimitris Aragiorgis
    try:
384 904d32bc Dimitris Aragiorgis
      self.sock.sendall(message_str)
385 904d32bc Dimitris Aragiorgis
    except socket.timeout, err:
386 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Timeout while sending a QMP message: "
387 904d32bc Dimitris Aragiorgis
                                   "%s (%s)" % (err.string, err.errno))
388 904d32bc Dimitris Aragiorgis
    except socket.error, err:
389 904d32bc Dimitris Aragiorgis
      raise errors.HypervisorError("Unable to send data from KVM using the"
390 904d32bc Dimitris Aragiorgis
                                   " QMP protocol: %s" % err)
391 904d32bc Dimitris Aragiorgis
392 9b7ef3eb Apollon Oikonomopoulos
  def _GetSupportedCommands(self):
393 9b7ef3eb Apollon Oikonomopoulos
    """Update the list of supported commands.
394 9b7ef3eb Apollon Oikonomopoulos

395 9b7ef3eb Apollon Oikonomopoulos
    """
396 9b7ef3eb Apollon Oikonomopoulos
    result = self.Execute(self._QUERY_COMMANDS)
397 546cdf7a Apollon Oikonomopoulos
    return frozenset(com["name"] for com in result)
398 9b7ef3eb Apollon Oikonomopoulos
399 904d32bc Dimitris Aragiorgis
  def Execute(self, command, arguments=None):
400 904d32bc Dimitris Aragiorgis
    """Executes a QMP command and returns the response of the server.
401 904d32bc Dimitris Aragiorgis

402 904d32bc Dimitris Aragiorgis
    @type command: str
403 904d32bc Dimitris Aragiorgis
    @param command: the command to execute
404 904d32bc Dimitris Aragiorgis
    @type arguments: dict
405 904d32bc Dimitris Aragiorgis
    @param arguments: dictionary of arguments to be passed to the command
406 904d32bc Dimitris Aragiorgis
    @rtype: dict
407 904d32bc Dimitris Aragiorgis
    @return: dictionary representing the received JSON object
408 904d32bc Dimitris Aragiorgis
    @raise errors.HypervisorError: when there are communication errors
409 904d32bc Dimitris Aragiorgis
    @raise errors.ProgrammerError: when there are data serialization errors
410 904d32bc Dimitris Aragiorgis

411 904d32bc Dimitris Aragiorgis
    """
412 904d32bc Dimitris Aragiorgis
    self._check_connection()
413 6c464d30 Apollon Oikonomopoulos
414 6c464d30 Apollon Oikonomopoulos
    # During the first calls of Execute, the list of supported commands has not
415 6c464d30 Apollon Oikonomopoulos
    # yet been populated, so we can't use it.
416 6c464d30 Apollon Oikonomopoulos
    if (self.supported_commands is not None and
417 6c464d30 Apollon Oikonomopoulos
        command not in self.supported_commands):
418 6c464d30 Apollon Oikonomopoulos
      raise QmpCommandNotSupported("Instance does not support the '%s'"
419 6c464d30 Apollon Oikonomopoulos
                                    " QMP command." % command)
420 6c464d30 Apollon Oikonomopoulos
421 904d32bc Dimitris Aragiorgis
    message = QmpMessage({self._EXECUTE_KEY: command})
422 904d32bc Dimitris Aragiorgis
    if arguments:
423 904d32bc Dimitris Aragiorgis
      message[self._ARGUMENTS_KEY] = arguments
424 904d32bc Dimitris Aragiorgis
    self._Send(message)
425 904d32bc Dimitris Aragiorgis
426 e31571c1 Dimitris Aragiorgis
    ret = self._GetResponse(command)
427 e31571c1 Dimitris Aragiorgis
    # log important qmp commands..
428 e31571c1 Dimitris Aragiorgis
    if command not in [self._QUERY_COMMANDS, self._CAPABILITIES_COMMAND]:
429 e31571c1 Dimitris Aragiorgis
      logging.debug("QMP %s %s: %s\n", command, arguments, ret)
430 e31571c1 Dimitris Aragiorgis
    return ret
431 655188a2 Dimitris Aragiorgis
432 655188a2 Dimitris Aragiorgis
  def _GetResponse(self, command):
433 655188a2 Dimitris Aragiorgis
    """Parse the QMP response
434 655188a2 Dimitris Aragiorgis

435 655188a2 Dimitris Aragiorgis
    If error key found in the response message raise HypervisorError.
436 655188a2 Dimitris Aragiorgis
    Ignore any async event and thus return the response message
437 655188a2 Dimitris Aragiorgis
    related to command.
438 655188a2 Dimitris Aragiorgis

439 655188a2 Dimitris Aragiorgis
    """
440 546cdf7a Apollon Oikonomopoulos
    # According the the QMP specification, there are only two reply types to a
441 546cdf7a Apollon Oikonomopoulos
    # command: either error (containing the "error" key) or success (containing
442 546cdf7a Apollon Oikonomopoulos
    # the "return" key). There is also a third possibility, that of an
443 546cdf7a Apollon Oikonomopoulos
    # (unrelated to the command) asynchronous event notification, identified by
444 546cdf7a Apollon Oikonomopoulos
    # the "event" key.
445 904d32bc Dimitris Aragiorgis
    while True:
446 904d32bc Dimitris Aragiorgis
      response = self._Recv()
447 904d32bc Dimitris Aragiorgis
      err = response[self._ERROR_KEY]
448 904d32bc Dimitris Aragiorgis
      if err:
449 904d32bc Dimitris Aragiorgis
        raise errors.HypervisorError("kvm: error executing the %s"
450 904d32bc Dimitris Aragiorgis
                                     " command: %s (%s):" %
451 904d32bc Dimitris Aragiorgis
                                     (command,
452 904d32bc Dimitris Aragiorgis
                                      err[self._ERROR_DESC_KEY],
453 904d32bc Dimitris Aragiorgis
                                      err[self._ERROR_CLASS_KEY]))
454 904d32bc Dimitris Aragiorgis
455 546cdf7a Apollon Oikonomopoulos
      elif response[self._EVENT_KEY]:
456 546cdf7a Apollon Oikonomopoulos
        # Filter-out any asynchronous events
457 546cdf7a Apollon Oikonomopoulos
        continue
458 546cdf7a Apollon Oikonomopoulos
459 546cdf7a Apollon Oikonomopoulos
      return response[self._RETURN_KEY]
460 655188a2 Dimitris Aragiorgis
461 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
462 57c24313 Dimitris Aragiorgis
  def HotAddNic(self, nic, devid, tapfds=None, vhostfds=None, features=None):
463 cc6a7f5f Dimitris Aragiorgis
    """Hot-add a NIC
464 cc6a7f5f Dimitris Aragiorgis

465 57c24313 Dimitris Aragiorgis
    First pass the tapfds, then netdev_add and then device_add
466 cc6a7f5f Dimitris Aragiorgis

467 cc6a7f5f Dimitris Aragiorgis
    """
468 57c24313 Dimitris Aragiorgis
    if tapfds is None:
469 57c24313 Dimitris Aragiorgis
      tapfds = []
470 57c24313 Dimitris Aragiorgis
    if vhostfds is None:
471 57c24313 Dimitris Aragiorgis
      vhostfds = []
472 57c24313 Dimitris Aragiorgis
    if features is None:
473 57c24313 Dimitris Aragiorgis
      features = {}
474 57c24313 Dimitris Aragiorgis
475 57c24313 Dimitris Aragiorgis
    enable_vhost = features.get("vhost", False)
476 57c24313 Dimitris Aragiorgis
    enable_mq, virtio_net_queues = features.get("mq", (False, 1))
477 57c24313 Dimitris Aragiorgis
478 57c24313 Dimitris Aragiorgis
    fdnames = []
479 57c24313 Dimitris Aragiorgis
    for i, fd in enumerate(tapfds):
480 57c24313 Dimitris Aragiorgis
      fdname = "%s-%d" % (devid, i)
481 d9982f38 Dimitris Aragiorgis
      self._GetFd(fd, fdname)
482 57c24313 Dimitris Aragiorgis
      fdnames.append(fdname)
483 cc6a7f5f Dimitris Aragiorgis
484 cc6a7f5f Dimitris Aragiorgis
    arguments = {
485 cc6a7f5f Dimitris Aragiorgis
      "type": "tap",
486 cc6a7f5f Dimitris Aragiorgis
      "id": devid,
487 57c24313 Dimitris Aragiorgis
      "fds": ":".join(fdnames),
488 cc6a7f5f Dimitris Aragiorgis
    }
489 57c24313 Dimitris Aragiorgis
    if enable_vhost:
490 57c24313 Dimitris Aragiorgis
      fdnames = []
491 57c24313 Dimitris Aragiorgis
      for i, fd in enumerate(vhostfds):
492 57c24313 Dimitris Aragiorgis
        fdname = "%s-vhost-%d" % (devid, i)
493 d9982f38 Dimitris Aragiorgis
        self._GetFd(fd, fdname)
494 57c24313 Dimitris Aragiorgis
        fdnames.append(fdname)
495 57c24313 Dimitris Aragiorgis
496 57c24313 Dimitris Aragiorgis
      arguments.update({
497 57c24313 Dimitris Aragiorgis
        "vhost": "on",
498 57c24313 Dimitris Aragiorgis
        "vhostfds": ":".join(fdnames),
499 57c24313 Dimitris Aragiorgis
        })
500 cc6a7f5f Dimitris Aragiorgis
    self.Execute("netdev_add", arguments)
501 cc6a7f5f Dimitris Aragiorgis
502 cc6a7f5f Dimitris Aragiorgis
    arguments = {
503 cc6a7f5f Dimitris Aragiorgis
      "driver": "virtio-net-pci",
504 cc6a7f5f Dimitris Aragiorgis
      "id": devid,
505 cc6a7f5f Dimitris Aragiorgis
      "bus": "pci.0",
506 cc6a7f5f Dimitris Aragiorgis
      "addr": hex(nic.pci),
507 cc6a7f5f Dimitris Aragiorgis
      "netdev": devid,
508 cc6a7f5f Dimitris Aragiorgis
      "mac": nic.mac,
509 cc6a7f5f Dimitris Aragiorgis
    }
510 57c24313 Dimitris Aragiorgis
    if enable_mq:
511 57c24313 Dimitris Aragiorgis
      arguments.update({
512 57c24313 Dimitris Aragiorgis
        "mq": "on",
513 57c24313 Dimitris Aragiorgis
        "vectors": (2 * virtio_net_queues + 1),
514 57c24313 Dimitris Aragiorgis
        })
515 cc6a7f5f Dimitris Aragiorgis
    self.Execute("device_add", arguments)
516 cc6a7f5f Dimitris Aragiorgis
517 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
518 cc6a7f5f Dimitris Aragiorgis
  def HotDelNic(self, devid):
519 cc6a7f5f Dimitris Aragiorgis
    """Hot-del a NIC
520 cc6a7f5f Dimitris Aragiorgis

521 cc6a7f5f Dimitris Aragiorgis
    """
522 cc6a7f5f Dimitris Aragiorgis
    self.Execute("device_del", {"id": devid})
523 cc6a7f5f Dimitris Aragiorgis
    self.Execute("netdev_del", {"id": devid})
524 cc6a7f5f Dimitris Aragiorgis
525 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
526 af4b620c Dimitris Aragiorgis
  def HotAddDisk(self, disk, devid, uri):
527 af4b620c Dimitris Aragiorgis
    """Hot-add a disk
528 af4b620c Dimitris Aragiorgis

529 af4b620c Dimitris Aragiorgis
    Try opening the device to obtain a fd and pass it with SCM_RIGHTS. This
530 af4b620c Dimitris Aragiorgis
    will be omitted in case of userspace access mode (open will fail).
531 af4b620c Dimitris Aragiorgis
    Then use blockdev-add and then device_add.
532 af4b620c Dimitris Aragiorgis

533 af4b620c Dimitris Aragiorgis
    """
534 af4b620c Dimitris Aragiorgis
    if os.path.exists(uri):
535 af4b620c Dimitris Aragiorgis
      fd = os.open(uri, os.O_RDWR)
536 d9982f38 Dimitris Aragiorgis
      fdset = self._AddFd(fd)
537 af4b620c Dimitris Aragiorgis
      os.close(fd)
538 af4b620c Dimitris Aragiorgis
      filename = "/dev/fdset/%s" % fdset
539 af4b620c Dimitris Aragiorgis
    else:
540 af4b620c Dimitris Aragiorgis
      # The uri is not a file.
541 af4b620c Dimitris Aragiorgis
      # This can happen if a userspace uri is provided.
542 af4b620c Dimitris Aragiorgis
      filename = uri
543 af4b620c Dimitris Aragiorgis
      fdset = None
544 af4b620c Dimitris Aragiorgis
545 af4b620c Dimitris Aragiorgis
    arguments = {
546 af4b620c Dimitris Aragiorgis
      "options": {
547 af4b620c Dimitris Aragiorgis
        "driver": "raw",
548 af4b620c Dimitris Aragiorgis
        "id": devid,
549 af4b620c Dimitris Aragiorgis
        "file": {
550 af4b620c Dimitris Aragiorgis
          "driver": "file",
551 af4b620c Dimitris Aragiorgis
          "filename": filename,
552 af4b620c Dimitris Aragiorgis
        }
553 af4b620c Dimitris Aragiorgis
      }
554 af4b620c Dimitris Aragiorgis
    }
555 af4b620c Dimitris Aragiorgis
    self.Execute("blockdev-add", arguments)
556 af4b620c Dimitris Aragiorgis
557 af4b620c Dimitris Aragiorgis
    if fdset is not None:
558 d9982f38 Dimitris Aragiorgis
      self._RemoveFdset(fdset)
559 af4b620c Dimitris Aragiorgis
560 af4b620c Dimitris Aragiorgis
    arguments = {
561 af4b620c Dimitris Aragiorgis
      "driver": "virtio-blk-pci",
562 af4b620c Dimitris Aragiorgis
      "id": devid,
563 af4b620c Dimitris Aragiorgis
      "bus": "pci.0",
564 af4b620c Dimitris Aragiorgis
      "addr": hex(disk.pci),
565 af4b620c Dimitris Aragiorgis
      "drive": devid,
566 af4b620c Dimitris Aragiorgis
    }
567 af4b620c Dimitris Aragiorgis
    self.Execute("device_add", arguments)
568 af4b620c Dimitris Aragiorgis
569 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
570 af4b620c Dimitris Aragiorgis
  def HotDelDisk(self, devid):
571 af4b620c Dimitris Aragiorgis
    """Hot-del a Disk
572 af4b620c Dimitris Aragiorgis

573 af4b620c Dimitris Aragiorgis
    Note that drive_del is not supported yet in qmp and thus should
574 af4b620c Dimitris Aragiorgis
    be invoked from HMP.
575 af4b620c Dimitris Aragiorgis

576 af4b620c Dimitris Aragiorgis
    """
577 af4b620c Dimitris Aragiorgis
    self.Execute("device_del", {"id": devid})
578 af4b620c Dimitris Aragiorgis
    #TODO: uncomment when drive_del gets implemented in upstream qemu
579 af4b620c Dimitris Aragiorgis
    # self.Execute("drive_del", {"id": devid})
580 af4b620c Dimitris Aragiorgis
581 d9982f38 Dimitris Aragiorgis
  def _GetPCIDevices(self):
582 565fa7f1 Dimitris Aragiorgis
    """Get the devices of the first PCI bus of a running instance.
583 565fa7f1 Dimitris Aragiorgis

584 565fa7f1 Dimitris Aragiorgis
    """
585 565fa7f1 Dimitris Aragiorgis
    self._check_connection()
586 565fa7f1 Dimitris Aragiorgis
    pci = self.Execute("query-pci")
587 565fa7f1 Dimitris Aragiorgis
    bus = pci[0]
588 565fa7f1 Dimitris Aragiorgis
    devices = bus["devices"]
589 565fa7f1 Dimitris Aragiorgis
    return devices
590 565fa7f1 Dimitris Aragiorgis
591 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
592 565fa7f1 Dimitris Aragiorgis
  def HasPCIDevice(self, device, devid):
593 565fa7f1 Dimitris Aragiorgis
    """Check if a specific device exists or not on a running instance.
594 565fa7f1 Dimitris Aragiorgis

595 565fa7f1 Dimitris Aragiorgis
    It will match the PCI slot of the device and the id currently
596 565fa7f1 Dimitris Aragiorgis
    obtained by _GenerateDeviceKVMId().
597 565fa7f1 Dimitris Aragiorgis

598 565fa7f1 Dimitris Aragiorgis
    """
599 d9982f38 Dimitris Aragiorgis
    for d in self._GetPCIDevices():
600 565fa7f1 Dimitris Aragiorgis
      if d["qdev_id"] == devid and d["slot"] == device.pci:
601 565fa7f1 Dimitris Aragiorgis
        return True
602 565fa7f1 Dimitris Aragiorgis
603 565fa7f1 Dimitris Aragiorgis
    return False
604 565fa7f1 Dimitris Aragiorgis
605 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
606 565fa7f1 Dimitris Aragiorgis
  def GetFreePCISlot(self):
607 565fa7f1 Dimitris Aragiorgis
    """Get the first available PCI slot of a running instance.
608 565fa7f1 Dimitris Aragiorgis

609 565fa7f1 Dimitris Aragiorgis
    """
610 565fa7f1 Dimitris Aragiorgis
    slots = bitarray(self._QEMU_PCI_SLOTS)
611 565fa7f1 Dimitris Aragiorgis
    slots.setall(False) # pylint: disable=E1101
612 d9982f38 Dimitris Aragiorgis
    for d in self._GetPCIDevices():
613 565fa7f1 Dimitris Aragiorgis
      slot = d["slot"]
614 565fa7f1 Dimitris Aragiorgis
      slots[slot] = True
615 565fa7f1 Dimitris Aragiorgis
616 565fa7f1 Dimitris Aragiorgis
    return utils.GetFreeSlot(slots)
617 565fa7f1 Dimitris Aragiorgis
618 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
619 ff81c659 Dimitris Aragiorgis
  def CheckDiskHotAddSupport(self):
620 ff81c659 Dimitris Aragiorgis
    """Check if disk hotplug is possible
621 ff81c659 Dimitris Aragiorgis

622 ff81c659 Dimitris Aragiorgis
    Hotplug is *not* supported in case:
623 ff81c659 Dimitris Aragiorgis
     - fdsend module is missing
624 ff81c659 Dimitris Aragiorgis
     - add-fd and blockdev-add qmp commands are not supported
625 ff81c659 Dimitris Aragiorgis

626 ff81c659 Dimitris Aragiorgis
    """
627 ff81c659 Dimitris Aragiorgis
    def _raise(reason):
628 ff81c659 Dimitris Aragiorgis
      raise errors.HotplugError("Cannot hot-add disk: %s." % reason)
629 ff81c659 Dimitris Aragiorgis
630 ff81c659 Dimitris Aragiorgis
    if not fdsend:
631 ff81c659 Dimitris Aragiorgis
      _raise("fdsend python module is missing")
632 ff81c659 Dimitris Aragiorgis
633 ff81c659 Dimitris Aragiorgis
    if "add-fd" not in self.supported_commands:
634 ff81c659 Dimitris Aragiorgis
      _raise("add-fd qmp command is not supported")
635 ff81c659 Dimitris Aragiorgis
636 ff81c659 Dimitris Aragiorgis
    if "blockdev-add" not in self.supported_commands:
637 ff81c659 Dimitris Aragiorgis
      _raise("blockdev-add qmp command is not supported")
638 ff81c659 Dimitris Aragiorgis
639 d9982f38 Dimitris Aragiorgis
  @_ensure_connection
640 ff81c659 Dimitris Aragiorgis
  def CheckNicHotAddSupport(self):
641 ff81c659 Dimitris Aragiorgis
    """Check if NIC hotplug is possible
642 ff81c659 Dimitris Aragiorgis

643 ff81c659 Dimitris Aragiorgis
    Hotplug is *not* supported in case:
644 ff81c659 Dimitris Aragiorgis
     - fdsend module is missing
645 ff81c659 Dimitris Aragiorgis
     - getfd and netdev_add qmp commands are not supported
646 ff81c659 Dimitris Aragiorgis

647 ff81c659 Dimitris Aragiorgis
    """
648 ff81c659 Dimitris Aragiorgis
    def _raise(reason):
649 ff81c659 Dimitris Aragiorgis
      raise errors.HotplugError("Cannot hot-add NIC: %s." % reason)
650 ff81c659 Dimitris Aragiorgis
651 ff81c659 Dimitris Aragiorgis
    if not fdsend:
652 ff81c659 Dimitris Aragiorgis
      _raise("fdsend python module is missing")
653 ff81c659 Dimitris Aragiorgis
654 ff81c659 Dimitris Aragiorgis
    if "getfd" not in self.supported_commands:
655 ff81c659 Dimitris Aragiorgis
      _raise("getfd qmp command is not supported")
656 ff81c659 Dimitris Aragiorgis
657 ff81c659 Dimitris Aragiorgis
    if "netdev_add" not in self.supported_commands:
658 ff81c659 Dimitris Aragiorgis
      _raise("netdev_add qmp command is not supported")
659 ff81c659 Dimitris Aragiorgis
660 d9982f38 Dimitris Aragiorgis
  def _GetFd(self, fd, fdname):
661 81bd73d1 Dimitris Aragiorgis
    """Wrapper around the getfd qmp command
662 81bd73d1 Dimitris Aragiorgis

663 81bd73d1 Dimitris Aragiorgis
    Use fdsend to send an fd to a running process via SCM_RIGHTS and then use
664 81bd73d1 Dimitris Aragiorgis
    the getfd qmp command to name it properly so that it can be used
665 81bd73d1 Dimitris Aragiorgis
    later by NIC hotplugging.
666 81bd73d1 Dimitris Aragiorgis

667 81bd73d1 Dimitris Aragiorgis
    @type fd: int
668 81bd73d1 Dimitris Aragiorgis
    @param fd: The file descriptor to pass
669 81bd73d1 Dimitris Aragiorgis
    @raise errors.HypervisorError: If getfd fails for some reason
670 81bd73d1 Dimitris Aragiorgis

671 81bd73d1 Dimitris Aragiorgis
    """
672 81bd73d1 Dimitris Aragiorgis
    self._check_connection()
673 81bd73d1 Dimitris Aragiorgis
    try:
674 81bd73d1 Dimitris Aragiorgis
      fdsend.sendfds(self.sock, " ", fds=[fd])
675 81bd73d1 Dimitris Aragiorgis
      arguments = {
676 81bd73d1 Dimitris Aragiorgis
          "fdname": fdname,
677 81bd73d1 Dimitris Aragiorgis
          }
678 81bd73d1 Dimitris Aragiorgis
      self.Execute("getfd", arguments)
679 81bd73d1 Dimitris Aragiorgis
    except errors.HypervisorError, err:
680 81bd73d1 Dimitris Aragiorgis
      logging.info("Passing fd %s via SCM_RIGHTS failed: %s", fd, err)
681 81bd73d1 Dimitris Aragiorgis
      raise
682 81bd73d1 Dimitris Aragiorgis
683 d9982f38 Dimitris Aragiorgis
  def _AddFd(self, fd):
684 105a40f2 Dimitris Aragiorgis
    """Wrapper around add-fd qmp command
685 655188a2 Dimitris Aragiorgis

686 105a40f2 Dimitris Aragiorgis
    Use fdsend to send fd to a running process via SCM_RIGHTS and then add-fd
687 105a40f2 Dimitris Aragiorgis
    qmp command to add it to an fdset so that it can be used later by
688 105a40f2 Dimitris Aragiorgis
    disk hotplugging.
689 655188a2 Dimitris Aragiorgis

690 105a40f2 Dimitris Aragiorgis
    @type fd: int
691 105a40f2 Dimitris Aragiorgis
    @param fd: The file descriptor to pass
692 655188a2 Dimitris Aragiorgis

693 105a40f2 Dimitris Aragiorgis
    @return: The fdset ID that the fd has been added to
694 105a40f2 Dimitris Aragiorgis
    @raise errors.HypervisorError: If add-fd fails for some reason
695 655188a2 Dimitris Aragiorgis

696 655188a2 Dimitris Aragiorgis
    """
697 655188a2 Dimitris Aragiorgis
    self._check_connection()
698 655188a2 Dimitris Aragiorgis
    try:
699 105a40f2 Dimitris Aragiorgis
      fdsend.sendfds(self.sock, " ", fds=[fd])
700 655188a2 Dimitris Aragiorgis
      # Omit fdset-id and let qemu create a new one (see qmp-commands.hx)
701 105a40f2 Dimitris Aragiorgis
      response = self.Execute("add-fd")
702 655188a2 Dimitris Aragiorgis
      fdset = response["fdset-id"]
703 655188a2 Dimitris Aragiorgis
    except errors.HypervisorError, err:
704 105a40f2 Dimitris Aragiorgis
      logging.info("Passing fd %s via SCM_RIGHTS failed: %s", fd, err)
705 105a40f2 Dimitris Aragiorgis
      raise
706 655188a2 Dimitris Aragiorgis
707 655188a2 Dimitris Aragiorgis
    return fdset
708 655188a2 Dimitris Aragiorgis
709 d9982f38 Dimitris Aragiorgis
  def _RemoveFdset(self, fdset):
710 105a40f2 Dimitris Aragiorgis
    """Wrapper around remove-fd qmp command
711 655188a2 Dimitris Aragiorgis

712 105a40f2 Dimitris Aragiorgis
    Remove the file descriptor previously passed. After qemu has dup'd the fd
713 105a40f2 Dimitris Aragiorgis
    (e.g. during disk hotplug), it can be safely removed.
714 655188a2 Dimitris Aragiorgis

715 655188a2 Dimitris Aragiorgis
    """
716 655188a2 Dimitris Aragiorgis
    self._check_connection()
717 105a40f2 Dimitris Aragiorgis
    # Omit the fd to cleanup all fds in the fdset (see qemu/qmp-commands.hx)
718 655188a2 Dimitris Aragiorgis
    try:
719 105a40f2 Dimitris Aragiorgis
      self.Execute("remove-fd", {"fdset-id": fdset})
720 655188a2 Dimitris Aragiorgis
    except errors.HypervisorError, err:
721 105a40f2 Dimitris Aragiorgis
      # There is no big deal if we cannot remove an fdset. This cleanup here is
722 105a40f2 Dimitris Aragiorgis
      # done on a best effort basis. Upon next hot-add a new fdset will be
723 105a40f2 Dimitris Aragiorgis
      # created. If we raise an exception here, that is after drive_add has
724 105a40f2 Dimitris Aragiorgis
      # succeeded, the whole hot-add action will fail and the runtime file will
725 105a40f2 Dimitris Aragiorgis
      # not be updated which will make the instance non migrate-able
726 105a40f2 Dimitris Aragiorgis
      logging.info("Removing fdset with id %s failed: %s", fdset, err)