Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 59a5de9e

History | View | Annotate | Download (79.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010, 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""KVM hypervisor
23

24
"""
25

    
26
import errno
27
import os
28
import os.path
29
import re
30
import tempfile
31
import time
32
import logging
33
import pwd
34
import struct
35
import fcntl
36
import shutil
37
import socket
38
import stat
39
import StringIO
40
import fdsend
41
try:
42
  import affinity   # pylint: disable=F0401
43
except ImportError:
44
  affinity = None
45

    
46
from ganeti import utils
47
from ganeti import constants
48
from ganeti import errors
49
from ganeti import serializer
50
from ganeti import objects
51
from ganeti import uidpool
52
from ganeti import ssconf
53
from ganeti.hypervisor import hv_base
54
from ganeti import netutils
55
from ganeti.utils import wrapper as utils_wrapper
56

    
57

    
58
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
59
_KVM_START_PAUSED_FLAG = "-S"
60

    
61
# TUN/TAP driver constants, taken from <linux/if_tun.h>
62
# They are architecture-independent and already hardcoded in qemu-kvm source,
63
# so we can safely include them here.
64
TUNSETIFF = 0x400454ca
65
TUNGETIFF = 0x800454d2
66
TUNGETFEATURES = 0x800454cf
67
IFF_TAP = 0x0002
68
IFF_NO_PI = 0x1000
69
IFF_VNET_HDR = 0x4000
70

    
71

    
72
def _ProbeTapVnetHdr(fd):
73
  """Check whether to enable the IFF_VNET_HDR flag.
74

75
  To do this, _all_ of the following conditions must be met:
76
   1. TUNGETFEATURES ioctl() *must* be implemented
77
   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
78
   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
79
      drivers/net/tun.c there is no way to test this until after the tap device
80
      has been created using TUNSETIFF, and there is no way to change the
81
      IFF_VNET_HDR flag after creating the interface, catch-22! However both
82
      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
83
      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
84

85
   @type fd: int
86
   @param fd: the file descriptor of /dev/net/tun
87

88
  """
89
  req = struct.pack("I", 0)
90
  try:
91
    res = fcntl.ioctl(fd, TUNGETFEATURES, req)
92
  except EnvironmentError:
93
    logging.warning("TUNGETFEATURES ioctl() not implemented")
94
    return False
95

    
96
  tunflags = struct.unpack("I", res)[0]
97
  if tunflags & IFF_VNET_HDR:
98
    return True
99
  else:
100
    logging.warning("Host does not support IFF_VNET_HDR, not enabling")
101
    return False
102

    
103

    
104
def _OpenTap(vnet_hdr=True):
105
  """Open a new tap device and return its file descriptor.
106

107
  This is intended to be used by a qemu-type hypervisor together with the -net
108
  tap,fd=<fd> command line parameter.
109

110
  @type vnet_hdr: boolean
111
  @param vnet_hdr: Enable the VNET Header
112
  @return: (ifname, tapfd)
113
  @rtype: tuple
114

115
  """
116
  try:
117
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
118
  except EnvironmentError:
119
    raise errors.HypervisorError("Failed to open /dev/net/tun")
120

    
121
  flags = IFF_TAP | IFF_NO_PI
122

    
123
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
124
    flags |= IFF_VNET_HDR
125

    
126
  # The struct ifreq ioctl request (see netdevice(7))
127
  ifr = struct.pack("16sh", "", flags)
128

    
129
  try:
130
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
131
  except EnvironmentError:
132
    raise errors.HypervisorError("Failed to allocate a new TAP device")
133

    
134
  # Get the interface name from the ioctl
135
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
136
  return (ifname, tapfd)
137

    
138

    
139
class QmpMessage:
140
  """QEMU Messaging Protocol (QMP) message.
141

142
  """
143
  def __init__(self, data):
144
    """Creates a new QMP message based on the passed data.
145

146
    """
147
    if not isinstance(data, dict):
148
      raise TypeError("QmpMessage must be initialized with a dict")
149

    
150
    self.data = data
151

    
152
  def __getitem__(self, field_name):
153
    """Get the value of the required field if present, or None.
154

155
    Overrides the [] operator to provide access to the message data,
156
    returning None if the required item is not in the message
157
    @return: the value of the field_name field, or None if field_name
158
             is not contained in the message
159

160
    """
161
    return self.data.get(field_name, None)
162

    
163
  def __setitem__(self, field_name, field_value):
164
    """Set the value of the required field_name to field_value.
165

166
    """
167
    self.data[field_name] = field_value
168

    
169
  @staticmethod
170
  def BuildFromJsonString(json_string):
171
    """Build a QmpMessage from a JSON encoded string.
172

173
    @type json_string: str
174
    @param json_string: JSON string representing the message
175
    @rtype: L{QmpMessage}
176
    @return: a L{QmpMessage} built from json_string
177

178
    """
179
    # Parse the string
180
    data = serializer.LoadJson(json_string)
181
    return QmpMessage(data)
182

    
183
  def __str__(self):
184
    # The protocol expects the JSON object to be sent as a single line.
185
    return serializer.DumpJson(self.data)
186

    
187
  def __eq__(self, other):
188
    # When comparing two QmpMessages, we are interested in comparing
189
    # their internal representation of the message data
190
    return self.data == other.data
191

    
192

    
193
class QmpConnection:
194
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
195

196
  """
197
  _FIRST_MESSAGE_KEY = "QMP"
198
  _EVENT_KEY = "event"
199
  _ERROR_KEY = "error"
200
  _RETURN_KEY = RETURN_KEY = "return"
201
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
202
  _ERROR_CLASS_KEY = "class"
203
  _ERROR_DATA_KEY = "data"
204
  _ERROR_DESC_KEY = "desc"
205
  _EXECUTE_KEY = "execute"
206
  _ARGUMENTS_KEY = "arguments"
207
  _CAPABILITIES_COMMAND = "qmp_capabilities"
208
  _MESSAGE_END_TOKEN = "\r\n"
209
  _SOCKET_TIMEOUT = 5
210

    
211
  def __init__(self, monitor_filename):
212
    """Instantiates the QmpConnection object.
213

214
    @type monitor_filename: string
215
    @param monitor_filename: the filename of the UNIX raw socket on which the
216
                             QMP monitor is listening
217

218
    """
219
    self.monitor_filename = monitor_filename
220
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
221
    # We want to fail if the server doesn't send a complete message
222
    # in a reasonable amount of time
223
    self.sock.settimeout(self._SOCKET_TIMEOUT)
224
    self._connected = False
225
    self._buf = ""
226

    
227
  def _check_socket(self):
228
    sock_stat = None
229
    try:
230
      sock_stat = os.stat(self.monitor_filename)
231
    except EnvironmentError, err:
232
      if err.errno == errno.ENOENT:
233
        raise errors.HypervisorError("No qmp socket found")
234
      else:
235
        raise errors.HypervisorError("Error checking qmp socket: %s",
236
                                     utils.ErrnoOrStr(err))
237
    if not stat.S_ISSOCK(sock_stat.st_mode):
238
      raise errors.HypervisorError("Qmp socket is not a socket")
239

    
240
  def _check_connection(self):
241
    """Make sure that the connection is established.
242

243
    """
244
    if not self._connected:
245
      raise errors.ProgrammerError("To use a QmpConnection you need to first"
246
                                   " invoke connect() on it")
247

    
248
  def connect(self):
249
    """Connects to the QMP monitor.
250

251
    Connects to the UNIX socket and makes sure that we can actually send and
252
    receive data to the kvm instance via QMP.
253

254
    @raise errors.HypervisorError: when there are communication errors
255
    @raise errors.ProgrammerError: when there are data serialization errors
256

257
    """
258
    if self._connected:
259
      raise errors.ProgrammerError("Cannot connect twice")
260

    
261
    self._check_socket()
262

    
263
    # Check file existance/stuff
264
    try:
265
      self.sock.connect(self.monitor_filename)
266
    except EnvironmentError:
267
      raise errors.HypervisorError("Can't connect to qmp socket")
268
    self._connected = True
269

    
270
    # Check if we receive a correct greeting message from the server
271
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
272
    greeting = self._Recv()
273
    if not greeting[self._FIRST_MESSAGE_KEY]:
274
      self._connected = False
275
      raise errors.HypervisorError("kvm: qmp communication error (wrong"
276
                                   " server greeting")
277

    
278
    # Let's put the monitor in command mode using the qmp_capabilities
279
    # command, or else no command will be executable.
280
    # (As per the QEMU Protocol Specification 0.1 - section 4)
281
    self.Execute(self._CAPABILITIES_COMMAND)
282

    
283
  def _ParseMessage(self, buf):
284
    """Extract and parse a QMP message from the given buffer.
285

286
    Seeks for a QMP message in the given buf. If found, it parses it and
287
    returns it together with the rest of the characters in the buf.
288
    If no message is found, returns None and the whole buffer.
289

290
    @raise errors.ProgrammerError: when there are data serialization errors
291

292
    """
293
    message = None
294
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
295
    # Specification 0.1 - Section 2.1.1)
296
    pos = buf.find(self._MESSAGE_END_TOKEN)
297
    if pos >= 0:
298
      try:
299
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
300
      except Exception, err:
301
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
302
      buf = buf[pos + 1:]
303

    
304
    return (message, buf)
305

    
306
  def _Recv(self):
307
    """Receives a message from QMP and decodes the received JSON object.
308

309
    @rtype: QmpMessage
310
    @return: the received message
311
    @raise errors.HypervisorError: when there are communication errors
312
    @raise errors.ProgrammerError: when there are data serialization errors
313

314
    """
315
    self._check_connection()
316

    
317
    # Check if there is already a message in the buffer
318
    (message, self._buf) = self._ParseMessage(self._buf)
319
    if message:
320
      return message
321

    
322
    recv_buffer = StringIO.StringIO(self._buf)
323
    recv_buffer.seek(len(self._buf))
324
    try:
325
      while True:
326
        data = self.sock.recv(4096)
327
        if not data:
328
          break
329
        recv_buffer.write(data)
330

    
331
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
332
        if message:
333
          return message
334

    
335
    except socket.timeout, err:
336
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
337
                                   "%s" % (err))
338
    except socket.error, err:
339
      raise errors.HypervisorError("Unable to receive data from KVM using the"
340
                                   " QMP protocol: %s" % err)
341

    
342
  def _Send(self, message):
343
    """Encodes and sends a message to KVM using QMP.
344

345
    @type message: QmpMessage
346
    @param message: message to send to KVM
347
    @raise errors.HypervisorError: when there are communication errors
348
    @raise errors.ProgrammerError: when there are data serialization errors
349

350
    """
351
    self._check_connection()
352
    try:
353
      message_str = str(message)
354
    except Exception, err:
355
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
356

    
357
    try:
358
      self.sock.sendall(message_str)
359
    except socket.timeout, err:
360
      raise errors.HypervisorError("Timeout while sending a QMP message: "
361
                                   "%s (%s)" % (err.string, err.errno))
362
    except socket.error, err:
363
      raise errors.HypervisorError("Unable to send data from KVM using the"
364
                                   " QMP protocol: %s" % err)
365

    
366
  def Execute(self, command, arguments=None):
367
    """Executes a QMP command and returns the response of the server.
368

369
    @type command: str
370
    @param command: the command to execute
371
    @type arguments: dict
372
    @param arguments: dictionary of arguments to be passed to the command
373
    @rtype: dict
374
    @return: dictionary representing the received JSON object
375
    @raise errors.HypervisorError: when there are communication errors
376
    @raise errors.ProgrammerError: when there are data serialization errors
377

378
    """
379
    self._check_connection()
380
    message = QmpMessage({self._EXECUTE_KEY: command})
381
    if arguments:
382
      message[self._ARGUMENTS_KEY] = arguments
383
    self._Send(message)
384

    
385
    # Events can occur between the sending of the command and the reception
386
    # of the response, so we need to filter out messages with the event key.
387
    while True:
388
      response = self._Recv()
389
      err = response[self._ERROR_KEY]
390
      if err:
391
        raise errors.HypervisorError("kvm: error executing the %s"
392
                                     " command: %s (%s, %s):" %
393
                                     (command,
394
                                      err[self._ERROR_DESC_KEY],
395
                                      err[self._ERROR_CLASS_KEY],
396
                                      err[self._ERROR_DATA_KEY]))
397

    
398
      elif not response[self._EVENT_KEY]:
399
        return response
400

    
401

    
402
class KVMHypervisor(hv_base.BaseHypervisor):
403
  """KVM hypervisor interface
404

405
  """
406
  CAN_MIGRATE = True
407

    
408
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
409
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
410
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
411
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
412
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
413
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
414
  _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
415
  # KVM instances with chroot enabled are started in empty chroot directories.
416
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
417
  # After an instance is stopped, its chroot directory is removed.
418
  # If the chroot directory is not empty, it can't be removed.
419
  # A non-empty chroot directory indicates a possible security incident.
420
  # To support forensics, the non-empty chroot directory is quarantined in
421
  # a separate directory, called 'chroot-quarantine'.
422
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
423
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
424
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
425

    
426
  PARAMETERS = {
427
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
428
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
429
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
430
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
431
    constants.HV_ACPI: hv_base.NO_CHECK,
432
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
433
    constants.HV_VNC_BIND_ADDRESS:
434
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
435
                         utils.IsNormAbsPath(x)),
436
       "the VNC bind address must be either a valid IP address or an absolute"
437
       " pathname", None, None),
438
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
439
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
440
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
441
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
442
    constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
443
    constants.HV_KVM_SPICE_IP_VERSION:
444
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
445
                         x in constants.VALID_IP_VERSIONS),
446
       "the SPICE IP version should be 4 or 6",
447
       None, None),
448
    constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
449
    constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
450
      hv_base.ParamInSet(False,
451
        constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
452
    constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
453
      hv_base.ParamInSet(False,
454
        constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
455
    constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
456
      hv_base.ParamInSet(False,
457
        constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
458
    constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
459
      hv_base.ParamInSet(False,
460
        constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
461
    constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
462
    constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
463
    constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
464
    constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
465
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
466
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
467
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
468
    constants.HV_BOOT_ORDER:
469
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
470
    constants.HV_NIC_TYPE:
471
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
472
    constants.HV_DISK_TYPE:
473
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
474
    constants.HV_KVM_CDROM_DISK_TYPE:
475
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
476
    constants.HV_USB_MOUSE:
477
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
478
    constants.HV_KEYMAP: hv_base.NO_CHECK,
479
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
480
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
481
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
482
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
483
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
484
    constants.HV_DISK_CACHE:
485
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
486
    constants.HV_SECURITY_MODEL:
487
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
488
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
489
    constants.HV_KVM_FLAG:
490
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
491
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
492
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
493
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
494
    constants.HV_REBOOT_BEHAVIOR:
495
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
496
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
497
    }
498

    
499
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
500
                                    re.M | re.I)
501
  _MIGRATION_PROGRESS_RE = \
502
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
503
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
504
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
505

    
506
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
507
  _MIGRATION_INFO_RETRY_DELAY = 2
508

    
509
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
510

    
511
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
512
  _CPU_INFO_CMD = "info cpus"
513
  _CONT_CMD = "cont"
514

    
515
  ANCILLARY_FILES = [
516
    _KVM_NETWORK_SCRIPT,
517
    ]
518
  ANCILLARY_FILES_OPT = [
519
    _KVM_NETWORK_SCRIPT,
520
    ]
521

    
522
  def __init__(self):
523
    hv_base.BaseHypervisor.__init__(self)
524
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
525
    # in a tmpfs filesystem or has been otherwise wiped out.
526
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
527
    utils.EnsureDirs(dirs)
528

    
529
  @classmethod
530
  def _InstancePidFile(cls, instance_name):
531
    """Returns the instance pidfile.
532

533
    """
534
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
535

    
536
  @classmethod
537
  def _InstanceUidFile(cls, instance_name):
538
    """Returns the instance uidfile.
539

540
    """
541
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
542

    
543
  @classmethod
544
  def _InstancePidInfo(cls, pid):
545
    """Check pid file for instance information.
546

547
    Check that a pid file is associated with an instance, and retrieve
548
    information from its command line.
549

550
    @type pid: string or int
551
    @param pid: process id of the instance to check
552
    @rtype: tuple
553
    @return: (instance_name, memory, vcpus)
554
    @raise errors.HypervisorError: when an instance cannot be found
555

556
    """
557
    alive = utils.IsProcessAlive(pid)
558
    if not alive:
559
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
560

    
561
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
562
    try:
563
      cmdline = utils.ReadFile(cmdline_file)
564
    except EnvironmentError, err:
565
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
566
                                   (pid, err))
567

    
568
    instance = None
569
    memory = 0
570
    vcpus = 0
571

    
572
    arg_list = cmdline.split("\x00")
573
    while arg_list:
574
      arg = arg_list.pop(0)
575
      if arg == "-name":
576
        instance = arg_list.pop(0)
577
      elif arg == "-m":
578
        memory = int(arg_list.pop(0))
579
      elif arg == "-smp":
580
        vcpus = int(arg_list.pop(0))
581

    
582
    if instance is None:
583
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
584
                                   " instance" % pid)
585

    
586
    return (instance, memory, vcpus)
587

    
588
  def _InstancePidAlive(self, instance_name):
589
    """Returns the instance pidfile, pid, and liveness.
590

591
    @type instance_name: string
592
    @param instance_name: instance name
593
    @rtype: tuple
594
    @return: (pid file name, pid, liveness)
595

596
    """
597
    pidfile = self._InstancePidFile(instance_name)
598
    pid = utils.ReadPidFile(pidfile)
599

    
600
    alive = False
601
    try:
602
      cmd_instance = self._InstancePidInfo(pid)[0]
603
      alive = (cmd_instance == instance_name)
604
    except errors.HypervisorError:
605
      pass
606

    
607
    return (pidfile, pid, alive)
608

    
609
  def _CheckDown(self, instance_name):
610
    """Raises an error unless the given instance is down.
611

612
    """
613
    alive = self._InstancePidAlive(instance_name)[2]
614
    if alive:
615
      raise errors.HypervisorError("Failed to start instance %s: %s" %
616
                                   (instance_name, "already running"))
617

    
618
  @classmethod
619
  def _InstanceMonitor(cls, instance_name):
620
    """Returns the instance monitor socket name
621

622
    """
623
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
624

    
625
  @classmethod
626
  def _InstanceSerial(cls, instance_name):
627
    """Returns the instance serial socket name
628

629
    """
630
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
631

    
632
  @classmethod
633
  def _InstanceQmpMonitor(cls, instance_name):
634
    """Returns the instance serial QMP socket name
635

636
    """
637
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
638

    
639
  @staticmethod
640
  def _SocatUnixConsoleParams():
641
    """Returns the correct parameters for socat
642

643
    If we have a new-enough socat we can use raw mode with an escape character.
644

645
    """
646
    if constants.SOCAT_USE_ESCAPE:
647
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
648
    else:
649
      return "echo=0,icanon=0"
650

    
651
  @classmethod
652
  def _InstanceKVMRuntime(cls, instance_name):
653
    """Returns the instance KVM runtime filename
654

655
    """
656
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
657

    
658
  @classmethod
659
  def _InstanceChrootDir(cls, instance_name):
660
    """Returns the name of the KVM chroot dir of the instance
661

662
    """
663
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
664

    
665
  @classmethod
666
  def _InstanceNICDir(cls, instance_name):
667
    """Returns the name of the directory holding the tap device files for a
668
    given instance.
669

670
    """
671
    return utils.PathJoin(cls._NICS_DIR, instance_name)
672

    
673
  @classmethod
674
  def _InstanceNICFile(cls, instance_name, seq):
675
    """Returns the name of the file containing the tap device for a given NIC
676

677
    """
678
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
679

    
680
  @classmethod
681
  def _InstanceKeymapFile(cls, instance_name):
682
    """Returns the name of the file containing the keymap for a given instance
683

684
    """
685
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
686

    
687
  @classmethod
688
  def _TryReadUidFile(cls, uid_file):
689
    """Try to read a uid file
690

691
    """
692
    if os.path.exists(uid_file):
693
      try:
694
        uid = int(utils.ReadOneLineFile(uid_file))
695
        return uid
696
      except EnvironmentError:
697
        logging.warning("Can't read uid file", exc_info=True)
698
      except (TypeError, ValueError):
699
        logging.warning("Can't parse uid file contents", exc_info=True)
700
    return None
701

    
702
  @classmethod
703
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
704
    """Removes an instance's rutime sockets/files/dirs.
705

706
    """
707
    utils.RemoveFile(pidfile)
708
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
709
    utils.RemoveFile(cls._InstanceSerial(instance_name))
710
    utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
711
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
712
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
713
    uid_file = cls._InstanceUidFile(instance_name)
714
    uid = cls._TryReadUidFile(uid_file)
715
    utils.RemoveFile(uid_file)
716
    if uid is not None:
717
      uidpool.ReleaseUid(uid)
718
    try:
719
      shutil.rmtree(cls._InstanceNICDir(instance_name))
720
    except OSError, err:
721
      if err.errno != errno.ENOENT:
722
        raise
723
    try:
724
      chroot_dir = cls._InstanceChrootDir(instance_name)
725
      utils.RemoveDir(chroot_dir)
726
    except OSError, err:
727
      if err.errno == errno.ENOTEMPTY:
728
        # The chroot directory is expected to be empty, but it isn't.
729
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
730
                                          prefix="%s-%s-" %
731
                                          (instance_name,
732
                                           utils.TimestampForFilename()))
733
        logging.warning("The chroot directory of instance %s can not be"
734
                        " removed as it is not empty. Moving it to the"
735
                        " quarantine instead. Please investigate the"
736
                        " contents (%s) and clean up manually",
737
                        instance_name, new_chroot_dir)
738
        utils.RenameFile(chroot_dir, new_chroot_dir)
739
      else:
740
        raise
741

    
742
  @staticmethod
743
  def _ConfigureNIC(instance, seq, nic, tap):
744
    """Run the network configuration script for a specified NIC
745

746
    @param instance: instance we're acting on
747
    @type instance: instance object
748
    @param seq: nic sequence number
749
    @type seq: int
750
    @param nic: nic we're acting on
751
    @type nic: nic object
752
    @param tap: the host's tap interface this NIC corresponds to
753
    @type tap: str
754

755
    """
756
    if instance.tags:
757
      tags = " ".join(instance.tags)
758
    else:
759
      tags = ""
760

    
761
    env = {
762
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
763
      "INSTANCE": instance.name,
764
      "MAC": nic.mac,
765
      "MODE": nic.nicparams[constants.NIC_MODE],
766
      "INTERFACE": tap,
767
      "INTERFACE_INDEX": str(seq),
768
      "TAGS": tags,
769
    }
770

    
771
    if nic.ip:
772
      env["IP"] = nic.ip
773

    
774
    if nic.nicparams[constants.NIC_LINK]:
775
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
776

    
777
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
778
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
779

    
780
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
781
    if result.failed:
782
      raise errors.HypervisorError("Failed to configure interface %s: %s."
783
                                   " Network configuration script output: %s" %
784
                                   (tap, result.fail_reason, result.output))
785

    
786
  @staticmethod
787
  def _VerifyAffinityPackage():
788
    if affinity is None:
789
      raise errors.HypervisorError("affinity Python package not"
790
        " found; cannot use CPU pinning under KVM")
791

    
792
  @staticmethod
793
  def _BuildAffinityCpuMask(cpu_list):
794
    """Create a CPU mask suitable for sched_setaffinity from a list of
795
    CPUs.
796

797
    See man taskset for more info on sched_setaffinity masks.
798
    For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
799

800
    @type cpu_list: list of int
801
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
802
    @rtype: int
803
    @return: a bit mask of CPU affinities
804

805
    """
806
    if cpu_list == constants.CPU_PINNING_OFF:
807
      return constants.CPU_PINNING_ALL_KVM
808
    else:
809
      return sum(2 ** cpu for cpu in cpu_list)
810

    
811
  @classmethod
812
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
813
    """Change CPU affinity for running VM according to given CPU mask.
814

815
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
816
    @type cpu_mask: string
817
    @param process_id: process ID of KVM process. Used to pin entire VM
818
                       to physical CPUs.
819
    @type process_id: int
820
    @param thread_dict: map of virtual CPUs to KVM thread IDs
821
    @type thread_dict: dict int:int
822

823
    """
824
    # Convert the string CPU mask to a list of list of int's
825
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
826

    
827
    if len(cpu_list) == 1:
828
      all_cpu_mapping = cpu_list[0]
829
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
830
        # If CPU pinning has 1 entry that's "all", then do nothing
831
        pass
832
      else:
833
        # If CPU pinning has one non-all entry, map the entire VM to
834
        # one set of physical CPUs
835
        cls._VerifyAffinityPackage()
836
        affinity.set_process_affinity_mask(process_id,
837
          cls._BuildAffinityCpuMask(all_cpu_mapping))
838
    else:
839
      # The number of vCPUs mapped should match the number of vCPUs
840
      # reported by KVM. This was already verified earlier, so
841
      # here only as a sanity check.
842
      assert len(thread_dict) == len(cpu_list)
843
      cls._VerifyAffinityPackage()
844

    
845
      # For each vCPU, map it to the proper list of physical CPUs
846
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
847
        affinity.set_process_affinity_mask(thread_dict[i],
848
          cls._BuildAffinityCpuMask(vcpu))
849

    
850
  def _GetVcpuThreadIds(self, instance_name):
851
    """Get a mapping of vCPU no. to thread IDs for the instance
852

853
    @type instance_name: string
854
    @param instance_name: instance in question
855
    @rtype: dictionary of int:int
856
    @return: a dictionary mapping vCPU numbers to thread IDs
857

858
    """
859
    result = {}
860
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
861
    for line in output.stdout.splitlines():
862
      match = self._CPU_INFO_RE.search(line)
863
      if not match:
864
        continue
865
      grp = map(int, match.groups())
866
      result[grp[0]] = grp[1]
867

    
868
    return result
869

    
870
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
871
    """Complete CPU pinning.
872

873
    @type instance_name: string
874
    @param instance_name: name of instance
875
    @type cpu_mask: string
876
    @param cpu_mask: CPU pinning mask as entered by user
877

878
    """
879
    # Get KVM process ID, to be used if need to pin entire VM
880
    _, pid, _ = self._InstancePidAlive(instance_name)
881
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
882
    thread_dict = self._GetVcpuThreadIds(instance_name)
883
    # Run CPU pinning, based on configured mask
884
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
885

    
886
  def ListInstances(self):
887
    """Get the list of running instances.
888

889
    We can do this by listing our live instances directory and
890
    checking whether the associated kvm process is still alive.
891

892
    """
893
    result = []
894
    for name in os.listdir(self._PIDS_DIR):
895
      if self._InstancePidAlive(name)[2]:
896
        result.append(name)
897
    return result
898

    
899
  def GetInstanceInfo(self, instance_name):
900
    """Get instance properties.
901

902
    @type instance_name: string
903
    @param instance_name: the instance name
904
    @rtype: tuple of strings
905
    @return: (name, id, memory, vcpus, stat, times)
906

907
    """
908
    _, pid, alive = self._InstancePidAlive(instance_name)
909
    if not alive:
910
      return None
911

    
912
    _, memory, vcpus = self._InstancePidInfo(pid)
913
    istat = "---b-"
914
    times = "0"
915

    
916
    try:
917
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
918
      qmp.connect()
919
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
920
      # Will fail if ballooning is not enabled, but we can then just resort to
921
      # the value above.
922
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
923
      memory = mem_bytes / 1048576
924
    except errors.HypervisorError:
925
      pass
926

    
927
    return (instance_name, pid, memory, vcpus, istat, times)
928

    
929
  def GetAllInstancesInfo(self):
930
    """Get properties of all instances.
931

932
    @return: list of tuples (name, id, memory, vcpus, stat, times)
933

934
    """
935
    data = []
936
    for name in os.listdir(self._PIDS_DIR):
937
      try:
938
        info = self.GetInstanceInfo(name)
939
      except errors.HypervisorError:
940
        # Ignore exceptions due to instances being shut down
941
        continue
942
      if info:
943
        data.append(info)
944
    return data
945

    
946
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_cmd, block_devices):
947

    
948
    hvp = instance.hvparams
949
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
950

    
951
    _, v_major, v_min, _ = self._GetKVMVersion()
952

    
953
    # whether this is an older KVM version that uses the boot=on flag
954
    # on devices
955
    needs_boot_flag = (v_major, v_min) < (0, 14)
956

    
957
    disk_type = hvp[constants.HV_DISK_TYPE]
958
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
959
      if_val = ",if=virtio"
960
      if (v_major, v_min) >= (0, 12):
961
        disk_model = "virtio-blk-pci"
962
      else:
963
        disk_model = "virtio"
964
    else:
965
      if_val = ",if=%s" % disk_type
966
      disk_model = disk_type
967
    # Cache mode
968
    disk_cache = hvp[constants.HV_DISK_CACHE]
969
    if instance.disk_template in constants.DTS_EXT_MIRROR:
970
      if disk_cache != "none":
971
        # TODO: make this a hard error, instead of a silent overwrite
972
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
973
                        " to prevent shared storage corruption on migration",
974
                        disk_cache)
975
      cache_val = ",cache=none"
976
    elif disk_cache != constants.HT_CACHE_DEFAULT:
977
      cache_val = ",cache=%s" % disk_cache
978
    else:
979
      cache_val = ""
980
    for cfdev, dev_path in block_devices:
981
      if cfdev.mode != constants.DISK_RDWR:
982
        raise errors.HypervisorError("Instance has read-only disks which"
983
                                     " are not supported by KVM")
984
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
985
      boot_val = ""
986
      if boot_disk:
987
        kvm_cmd.extend(["-boot", "c"])
988
        boot_disk = False
989
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
990
          boot_val = ",boot=on"
991
      drive_val = "file=%s,format=raw%s%s" % \
992
                  (dev_path, boot_val, cache_val)
993
      if cfdev.pci:
994
        #TODO: name id after model
995
        drive_val += (",bus=0,unit=%d,if=none,id=drive%d" %
996
                      (cfdev.pci, cfdev.idx))
997
      else:
998
        drive_val += if_val
999

    
1000
      kvm_cmd.extend(["-drive", drive_val])
1001

    
1002
      if cfdev.pci:
1003
        dev_val = ("%s,bus=pci.0,addr=%s,drive=drive%d,id=virtio-blk-pci.%d" %
1004
                   (disk_model, hex(cfdev.pci), cfdev.idx, cfdev.idx))
1005
        kvm_cmd.extend(["-device", dev_val])
1006

    
1007
    return kvm_cmd
1008

    
1009
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
1010
    """Generate KVM information to start an instance.
1011

1012
    @attention: this function must not have any side-effects; for
1013
        example, it must not write to the filesystem, or read values
1014
        from the current system the are expected to differ between
1015
        nodes, since it is only run once at instance startup;
1016
        actions/kvm arguments that can vary between systems should be
1017
        done in L{_ExecuteKVMRuntime}
1018

1019
    """
1020
    # pylint: disable=R0914,R0915
1021
    _, v_major, v_min, _ = self._GetKVMVersion()
1022

    
1023
    pidfile = self._InstancePidFile(instance.name)
1024
    kvm = constants.KVM_PATH
1025
    kvm_cmd = [kvm]
1026
    # used just by the vnc server, if enabled
1027
    kvm_cmd.extend(["-name", instance.name])
1028
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1029
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
1030
    kvm_cmd.extend(["-pidfile", pidfile])
1031
    kvm_cmd.extend(["-balloon", "virtio"])
1032
    kvm_cmd.extend(["-daemonize"])
1033
    if not instance.hvparams[constants.HV_ACPI]:
1034
      kvm_cmd.extend(["-no-acpi"])
1035
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1036
        constants.INSTANCE_REBOOT_EXIT:
1037
      kvm_cmd.extend(["-no-reboot"])
1038

    
1039
    hvp = instance.hvparams
1040
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1041
    if kernel_path:
1042
      boot_cdrom = boot_floppy = boot_network = False
1043
    else:
1044
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1045
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1046
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1047

    
1048
    self.ValidateParameters(hvp)
1049

    
1050
    if startup_paused:
1051
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1052

    
1053
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1054
      kvm_cmd.extend(["-enable-kvm"])
1055
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1056
      kvm_cmd.extend(["-disable-kvm"])
1057

    
1058
    if boot_network:
1059
      kvm_cmd.extend(["-boot", "n"])
1060

    
1061
    # whether this is an older KVM version that uses the boot=on flag
1062
    # on devices
1063
    needs_boot_flag = (v_major, v_min) < (0, 14)
1064

    
1065
    disk_type = hvp[constants.HV_DISK_TYPE]
1066
    if not instance.hotplug_info:
1067
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1068
                                                     block_devices)
1069

    
1070
    #Now we can specify a different device type for CDROM devices.
1071
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1072
    if not cdrom_disk_type:
1073
      cdrom_disk_type = disk_type
1074

    
1075
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1076
    if iso_image:
1077
      options = ",format=raw,media=cdrom"
1078
      # set cdrom 'if' type
1079
      if boot_cdrom:
1080
        actual_cdrom_type = constants.HT_DISK_IDE
1081
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1082
        actual_cdrom_type = "virtio"
1083
      else:
1084
        actual_cdrom_type = cdrom_disk_type
1085
      if_val = ",if=%s" % actual_cdrom_type
1086
      # set boot flag, if needed
1087
      boot_val = ""
1088
      if boot_cdrom:
1089
        kvm_cmd.extend(["-boot", "d"])
1090
        if needs_boot_flag:
1091
          boot_val = ",boot=on"
1092
      # and finally build the entire '-drive' value
1093
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1094
      kvm_cmd.extend(["-drive", drive_val])
1095

    
1096
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1097
    if iso_image2:
1098
      options = ",format=raw,media=cdrom"
1099
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1100
        if_val = ",if=virtio"
1101
      else:
1102
        if_val = ",if=%s" % cdrom_disk_type
1103
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1104
      kvm_cmd.extend(["-drive", drive_val])
1105

    
1106
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1107
    if floppy_image:
1108
      options = ",format=raw,media=disk"
1109
      if boot_floppy:
1110
        kvm_cmd.extend(["-boot", "a"])
1111
        options = "%s,boot=on" % options
1112
      if_val = ",if=floppy"
1113
      options = "%s%s" % (options, if_val)
1114
      drive_val = "file=%s%s" % (floppy_image, options)
1115
      kvm_cmd.extend(["-drive", drive_val])
1116

    
1117
    if kernel_path:
1118
      kvm_cmd.extend(["-kernel", kernel_path])
1119
      initrd_path = hvp[constants.HV_INITRD_PATH]
1120
      if initrd_path:
1121
        kvm_cmd.extend(["-initrd", initrd_path])
1122
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1123
                     hvp[constants.HV_KERNEL_ARGS]]
1124
      if hvp[constants.HV_SERIAL_CONSOLE]:
1125
        root_append.append("console=ttyS0,38400")
1126
      kvm_cmd.extend(["-append", " ".join(root_append)])
1127

    
1128
    mem_path = hvp[constants.HV_MEM_PATH]
1129
    if mem_path:
1130
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1131

    
1132
    monitor_dev = ("unix:%s,server,nowait" %
1133
                   self._InstanceMonitor(instance.name))
1134
    kvm_cmd.extend(["-monitor", monitor_dev])
1135
    if hvp[constants.HV_SERIAL_CONSOLE]:
1136
      serial_dev = ("unix:%s,server,nowait" %
1137
                    self._InstanceSerial(instance.name))
1138
      kvm_cmd.extend(["-serial", serial_dev])
1139
    else:
1140
      kvm_cmd.extend(["-serial", "none"])
1141

    
1142
    mouse_type = hvp[constants.HV_USB_MOUSE]
1143
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1144
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1145
    spice_ip_version = None
1146

    
1147
    if mouse_type:
1148
      kvm_cmd.extend(["-usb"])
1149
      kvm_cmd.extend(["-usbdevice", mouse_type])
1150
    elif vnc_bind_address:
1151
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1152

    
1153
    if vnc_bind_address:
1154
      if netutils.IP4Address.IsValid(vnc_bind_address):
1155
        if instance.network_port > constants.VNC_BASE_PORT:
1156
          display = instance.network_port - constants.VNC_BASE_PORT
1157
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1158
            vnc_arg = ":%d" % (display)
1159
          else:
1160
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1161
        else:
1162
          logging.error("Network port is not a valid VNC display (%d < %d)."
1163
                        " Not starting VNC", instance.network_port,
1164
                        constants.VNC_BASE_PORT)
1165
          vnc_arg = "none"
1166

    
1167
        # Only allow tls and other option when not binding to a file, for now.
1168
        # kvm/qemu gets confused otherwise about the filename to use.
1169
        vnc_append = ""
1170
        if hvp[constants.HV_VNC_TLS]:
1171
          vnc_append = "%s,tls" % vnc_append
1172
          if hvp[constants.HV_VNC_X509_VERIFY]:
1173
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1174
                                               hvp[constants.HV_VNC_X509])
1175
          elif hvp[constants.HV_VNC_X509]:
1176
            vnc_append = "%s,x509=%s" % (vnc_append,
1177
                                         hvp[constants.HV_VNC_X509])
1178
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1179
          vnc_append = "%s,password" % vnc_append
1180

    
1181
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1182

    
1183
      else:
1184
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1185

    
1186
      kvm_cmd.extend(["-vnc", vnc_arg])
1187
    elif spice_bind:
1188
      # FIXME: this is wrong here; the iface ip address differs
1189
      # between systems, so it should be done in _ExecuteKVMRuntime
1190
      if netutils.IsValidInterface(spice_bind):
1191
        # The user specified a network interface, we have to figure out the IP
1192
        # address.
1193
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1194
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1195

    
1196
        # if the user specified an IP version and the interface does not
1197
        # have that kind of IP addresses, throw an exception
1198
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1199
          if not addresses[spice_ip_version]:
1200
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
1201
                                         " for %s" % (spice_ip_version,
1202
                                                      spice_bind))
1203

    
1204
        # the user did not specify an IP version, we have to figure it out
1205
        elif (addresses[constants.IP4_VERSION] and
1206
              addresses[constants.IP6_VERSION]):
1207
          # we have both ipv4 and ipv6, let's use the cluster default IP
1208
          # version
1209
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1210
          spice_ip_version = \
1211
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1212
        elif addresses[constants.IP4_VERSION]:
1213
          spice_ip_version = constants.IP4_VERSION
1214
        elif addresses[constants.IP6_VERSION]:
1215
          spice_ip_version = constants.IP6_VERSION
1216
        else:
1217
          raise errors.HypervisorError("spice: unable to get an IP address"
1218
                                       " for %s" % (spice_bind))
1219

    
1220
        spice_address = addresses[spice_ip_version][0]
1221

    
1222
      else:
1223
        # spice_bind is known to be a valid IP address, because
1224
        # ValidateParameters checked it.
1225
        spice_address = spice_bind
1226

    
1227
      spice_arg = "addr=%s" % spice_address
1228
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1229
        spice_arg = "%s,tls-port=%s,x509-cacert-file=%s" % (spice_arg,
1230
            instance.network_port, constants.SPICE_CACERT_FILE)
1231
        spice_arg = "%s,x509-key-file=%s,x509-cert-file=%s" % (spice_arg,
1232
            constants.SPICE_CERT_FILE, constants.SPICE_CERT_FILE)
1233
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1234
        if tls_ciphers:
1235
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1236
      else:
1237
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1238

    
1239
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1240
        spice_arg = "%s,disable-ticketing" % spice_arg
1241

    
1242
      if spice_ip_version:
1243
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1244

    
1245
      # Image compression options
1246
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1247
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1248
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1249
      if img_lossless:
1250
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1251
      if img_jpeg:
1252
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1253
      if img_zlib_glz:
1254
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1255

    
1256
      # Video stream detection
1257
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1258
      if video_streaming:
1259
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1260

    
1261
      # Audio compression, by default in qemu-kvm it is on
1262
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1263
        spice_arg = "%s,playback-compression=off" % spice_arg
1264
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1265
        spice_arg = "%s,agent-mouse=off" % spice_arg
1266
      else:
1267
        # Enable the spice agent communication channel between the host and the
1268
        # agent.
1269
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1270
        kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1271
                                                   "name=com.redhat.spice.0"])
1272
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1273

    
1274
      logging.info("KVM: SPICE will listen on port %s", instance.network_port)
1275
      kvm_cmd.extend(["-spice", spice_arg])
1276

    
1277
      # Tell kvm to use the paravirtualized graphic card, optimized for SPICE
1278
      kvm_cmd.extend(["-vga", "qxl"])
1279

    
1280
    else:
1281
      kvm_cmd.extend(["-nographic"])
1282

    
1283
    if hvp[constants.HV_USE_LOCALTIME]:
1284
      kvm_cmd.extend(["-localtime"])
1285

    
1286
    if hvp[constants.HV_KVM_USE_CHROOT]:
1287
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1288

    
1289
    # Save the current instance nics, but defer their expansion as parameters,
1290
    # as we'll need to generate executable temp files for them.
1291
    kvm_nics = instance.nics
1292
    hvparams = hvp
1293

    
1294
    if instance.hotplug_info:
1295
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1296
    else:
1297
      return (kvm_cmd, kvm_nics, hvparams)
1298

    
1299
  def _WriteKVMRuntime(self, instance_name, data):
1300
    """Write an instance's KVM runtime
1301

1302
    """
1303
    try:
1304
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1305
                      data=data)
1306
    except EnvironmentError, err:
1307
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1308

    
1309
  def _ReadKVMRuntime(self, instance_name):
1310
    """Read an instance's KVM runtime
1311

1312
    """
1313
    try:
1314
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1315
    except EnvironmentError, err:
1316
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1317
    return file_content
1318

    
1319
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1320
    """Save an instance's KVM runtime
1321

1322
    """
1323
    if instance.hotplug_info:
1324
      kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime
1325
      serialized_blockdevs = [(blk.ToDict(), link)
1326
                              for blk,link in block_devices]
1327
    else:
1328
      kvm_cmd, kvm_nics, hvparams = kvm_runtime
1329

    
1330
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1331

    
1332
    if instance.hotplug_info:
1333
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics,
1334
                                        hvparams, serialized_blockdevs))
1335
    else:
1336
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1337

    
1338
    self._WriteKVMRuntime(instance.name, serialized_form)
1339

    
1340
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1341
    """Load an instance's KVM runtime
1342

1343
    """
1344
    if not serialized_runtime:
1345
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1346
    loaded_runtime = serializer.Load(serialized_runtime)
1347
    if instance.hotplug_info:
1348
      kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime
1349
      block_devices = [(objects.Disk.FromDict(sdisk), link)
1350
                       for sdisk, link in serialized_blockdevs]
1351
    else:
1352
      kvm_cmd, serialized_nics, hvparams = loaded_runtime
1353

    
1354
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1355

    
1356
    if instance.hotplug_info:
1357
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1358
    else:
1359
      return (kvm_cmd, kvm_nics, hvparams)
1360

    
1361
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1362
    """Run the KVM cmd and check for errors
1363

1364
    @type name: string
1365
    @param name: instance name
1366
    @type kvm_cmd: list of strings
1367
    @param kvm_cmd: runcmd input for kvm
1368
    @type tap_fds: list of int
1369
    @param tap_fds: fds of tap devices opened by Ganeti
1370

1371
    """
1372
    try:
1373
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1374
    finally:
1375
      for fd in tap_fds:
1376
        utils_wrapper.CloseFdNoError(fd)
1377

    
1378
    if result.failed:
1379
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1380
                                   (name, result.fail_reason, result.output))
1381
    if not self._InstancePidAlive(name)[2]:
1382
      raise errors.HypervisorError("Failed to start instance %s" % name)
1383

    
1384
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
1385
    """Execute a KVM cmd, after completing it with some last minute data.
1386

1387
    @type incoming: tuple of strings
1388
    @param incoming: (target_host_ip, port)
1389

1390
    """
1391
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1392
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1393
    #    have changed since the instance started; only use them if the change
1394
    #    won't affect the inside of the instance (which hasn't been rebooted).
1395
    #  - up_hvp contains the parameters as they were when the instance was
1396
    #    started, plus any new parameter which has been added between ganeti
1397
    #    versions: it is paramount that those default to a value which won't
1398
    #    affect the inside of the instance as well.
1399
    conf_hvp = instance.hvparams
1400
    name = instance.name
1401
    self._CheckDown(name)
1402

    
1403
    temp_files = []
1404

    
1405
    if instance.hotplug_info:
1406
      kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
1407
    else:
1408
      kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1409

    
1410
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1411

    
1412
    _, v_major, v_min, _ = self._GetKVMVersion()
1413

    
1414
    # We know it's safe to run as a different user upon migration, so we'll use
1415
    # the latest conf, from conf_hvp.
1416
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1417
    if security_model == constants.HT_SM_USER:
1418
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1419

    
1420
    keymap = conf_hvp[constants.HV_KEYMAP]
1421
    if keymap:
1422
      keymap_path = self._InstanceKeymapFile(name)
1423
      # If a keymap file is specified, KVM won't use its internal defaults. By
1424
      # first including the "en-us" layout, an error on loading the actual
1425
      # layout (e.g. because it can't be found) won't lead to a non-functional
1426
      # keyboard. A keyboard with incorrect keys is still better than none.
1427
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1428
      kvm_cmd.extend(["-k", keymap_path])
1429

    
1430
    if instance.hotplug_info:
1431
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1432
                                                     block_devices)
1433

    
1434
    # We have reasons to believe changing something like the nic driver/type
1435
    # upon migration won't exactly fly with the instance kernel, so for nic
1436
    # related parameters we'll use up_hvp
1437
    tapfds = []
1438
    taps = []
1439
    if not kvm_nics:
1440
      kvm_cmd.extend(["-net", "none"])
1441
    else:
1442
      vnet_hdr = False
1443
      tap_extra = ""
1444
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1445
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1446
        # From version 0.12.0, kvm uses a new sintax for network configuration.
1447
        if (v_major, v_min) >= (0, 12):
1448
          nic_model = "virtio-net-pci"
1449
          vnet_hdr = True
1450
        else:
1451
          nic_model = "virtio"
1452

    
1453
        if up_hvp[constants.HV_VHOST_NET]:
1454
          # vhost_net is only available from version 0.13.0 or newer
1455
          if (v_major, v_min) >= (0, 13):
1456
            tap_extra = ",vhost=on"
1457
          else:
1458
            raise errors.HypervisorError("vhost_net is configured"
1459
                                        " but it is not available")
1460
      else:
1461
        nic_model = nic_type
1462

    
1463
      for nic_seq, nic in enumerate(kvm_nics):
1464
        tapname, tapfd = _OpenTap(vnet_hdr)
1465
        tapfds.append(tapfd)
1466
        taps.append(tapname)
1467
        if (v_major, v_min) >= (0, 12):
1468
          if nic.pci:
1469
            nic_idx = nic.idx
1470
          else:
1471
            nic_idx = nic_seq
1472
          nic_val = ("%s,mac=%s,netdev=netdev%d" %
1473
                     (nic_model, nic.mac, nic_idx))
1474
          if nic.pci:
1475
            nic_val += (",bus=pci.0,addr=%s,id=virtio-net-pci.%d" %
1476
                        (hex(nic.pci), nic_idx))
1477
          tap_val = "type=tap,id=netdev%d,fd=%d%s" % (nic_idx, tapfd, tap_extra)
1478
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1479
        else:
1480
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1481
                                                         nic.mac, nic_model)
1482
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1483
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1484

    
1485
    if incoming:
1486
      target, port = incoming
1487
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1488

    
1489
    # Changing the vnc password doesn't bother the guest that much. At most it
1490
    # will surprise people who connect to it. Whether positively or negatively
1491
    # it's debatable.
1492
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1493
    vnc_pwd = None
1494
    if vnc_pwd_file:
1495
      try:
1496
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1497
      except EnvironmentError, err:
1498
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1499
                                     % (vnc_pwd_file, err))
1500

    
1501
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1502
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1503
                         constants.SECURE_DIR_MODE)])
1504

    
1505
    # Automatically enable QMP if version is >= 0.14
1506
    if (v_major, v_min) >= (0, 14):
1507
      logging.debug("Enabling QMP")
1508
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1509
                    self._InstanceQmpMonitor(instance.name)])
1510

    
1511
    # Configure the network now for starting instances and bridged interfaces,
1512
    # during FinalizeMigration for incoming instances' routed interfaces
1513
    for nic_seq, nic in enumerate(kvm_nics):
1514
      if (incoming and
1515
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1516
        continue
1517
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1518

    
1519
    # CPU affinity requires kvm to start paused, so we set this flag if the
1520
    # instance is not already paused and if we are not going to accept a
1521
    # migrating instance. In the latter case, pausing is not needed.
1522
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1523
    if start_kvm_paused:
1524
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1525

    
1526
    # Note: CPU pinning is using up_hvp since changes take effect
1527
    # during instance startup anyway, and to avoid problems when soft
1528
    # rebooting the instance.
1529
    cpu_pinning = False
1530
    if up_hvp.get(constants.HV_CPU_MASK, None):
1531
      cpu_pinning = True
1532

    
1533
    if security_model == constants.HT_SM_POOL:
1534
      ss = ssconf.SimpleStore()
1535
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1536
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1537
      uid = uidpool.RequestUnusedUid(all_uids)
1538
      try:
1539
        username = pwd.getpwuid(uid.GetUid()).pw_name
1540
        kvm_cmd.extend(["-runas", username])
1541
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1542
      except:
1543
        uidpool.ReleaseUid(uid)
1544
        raise
1545
      else:
1546
        uid.Unlock()
1547
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1548
    else:
1549
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1550

    
1551
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1552
                     constants.RUN_DIRS_MODE)])
1553
    for nic_seq, tap in enumerate(taps):
1554
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1555
                      data=tap)
1556

    
1557
    if vnc_pwd:
1558
      change_cmd = "change vnc password %s" % vnc_pwd
1559
      self._CallMonitorCommand(instance.name, change_cmd)
1560

    
1561
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1562
    # connection attempts because SPICE by default does not allow connections
1563
    # if neither a password nor the "disable_ticketing" options are specified.
1564
    # As soon as we send the password via QMP, that password is a valid ticket
1565
    # for connection.
1566
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1567
    if spice_password_file:
1568
      spice_pwd = ""
1569
      try:
1570
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1571
      except EnvironmentError, err:
1572
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1573
                                     % (spice_password_file, err))
1574

    
1575
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1576
      qmp.connect()
1577
      arguments = {
1578
          "protocol": "spice",
1579
          "password": spice_pwd,
1580
      }
1581
      qmp.Execute("set_password", arguments)
1582

    
1583
    for filename in temp_files:
1584
      utils.RemoveFile(filename)
1585

    
1586
    # If requested, set CPU affinity and resume instance execution
1587
    if cpu_pinning:
1588
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1589

    
1590
    start_memory = self._InstanceStartupMemory(instance)
1591
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1592
      self.BalloonInstanceMemory(instance, start_memory)
1593

    
1594
    if start_kvm_paused:
1595
      # To control CPU pinning, ballooning, and vnc/spice passwords
1596
      # the VM was started in a frozen state. If freezing was not
1597
      # explicitly requested resume the vm status.
1598
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1599

    
1600
  def StartInstance(self, instance, block_devices, startup_paused):
1601
    """Start an instance.
1602

1603
    """
1604
    self._CheckDown(instance.name)
1605
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1606
                                           startup_paused)
1607
    self._SaveKVMRuntime(instance, kvm_runtime)
1608
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1609

    
1610
  def _CallMonitorCommand(self, instance_name, command):
1611
    """Invoke a command on the instance monitor.
1612

1613
    """
1614
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1615
             (utils.ShellQuote(command),
1616
              constants.SOCAT_PATH,
1617
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1618
    result = utils.RunCmd(socat)
1619
    if result.failed:
1620
      msg = ("Failed to send command '%s' to instance %s."
1621
             " output: %s, error: %s, fail_reason: %s" %
1622
             (command, instance_name,
1623
              result.stdout, result.stderr, result.fail_reason))
1624
      raise errors.HypervisorError(msg)
1625

    
1626
    return result
1627

    
1628
  def HotAddDisk(self, instance, disk, dev_path, _):
1629
    """Hotadd new disk to the VM
1630

1631
    """
1632
    if not self._InstancePidAlive(instance.name)[2]:
1633
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1634
      return disk.ToDict()
1635

    
1636
    _, v_major, v_min, _ = self._GetKVMVersion()
1637
    if (v_major, v_min) >= (1, 0) and disk.pci:
1638
      idx = disk.idx
1639
      command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" %
1640
                 (dev_path, idx))
1641

    
1642
      logging.info("Run cmd %s", command)
1643
      output = self._CallMonitorCommand(instance.name, command)
1644

    
1645
      command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s,"
1646
                 "drive=drive%d,id=virtio-blk-pci.%d"
1647
                 % (hex(disk.pci), idx, idx))
1648
      logging.info("Run cmd %s", command)
1649
      output = self._CallMonitorCommand(instance.name, command)
1650
      for line in output.stdout.splitlines():
1651
        logging.info("%s", line)
1652

    
1653
      (kvm_cmd, kvm_nics,
1654
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1655
      block_devices.append((disk, dev_path))
1656
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1657
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1658

    
1659
    return disk.ToDict()
1660

    
1661
  def HotDelDisk(self, instance, disk, _):
1662
    """Hotdel disk to the VM
1663

1664
    """
1665
    if not self._InstancePidAlive(instance.name)[2]:
1666
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1667
      return disk.ToDict()
1668

    
1669
    _, v_major, v_min, _ = self._GetKVMVersion()
1670
    if (v_major, v_min) >= (1, 0) and disk.pci:
1671
      idx = disk.idx
1672

    
1673
      command = "device_del virtio-blk-pci.%d" % idx
1674
      logging.info("Run cmd %s", command)
1675
      output = self._CallMonitorCommand(instance.name, command)
1676
      for line in output.stdout.splitlines():
1677
        logging.info("%s", line)
1678

    
1679
      command = "drive_del drive%d" % idx
1680
      logging.info("Run cmd %s", command)
1681
      #output = self._CallMonitorCommand(instance.name, command)
1682
      #for line in output.stdout.splitlines():
1683
      #  logging.info("%s" % line)
1684

    
1685
      (kvm_cmd, kvm_nics,
1686
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1687
      rem  = [(d, p) for d, p in block_devices
1688
                     if d.idx is not None and d.idx == idx]
1689
      try:
1690
        block_devices.remove(rem[0])
1691
      except (ValueError, IndexError):
1692
        logging.info("Disk with %d idx disappeared from runtime file", idx)
1693
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1694
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1695

    
1696
    return disk.ToDict()
1697

    
1698
  def HotAddNic(self, instance, nic, seq):
1699
    """Hotadd new nic to the VM
1700

1701
    """
1702
    if not self._InstancePidAlive(instance.name)[2]:
1703
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1704
      return nic.ToDict()
1705

    
1706
    _, v_major, v_min, _ = self._GetKVMVersion()
1707
    if (v_major, v_min) >= (1, 0) and nic.pci:
1708
      mac = nic.mac
1709
      idx = nic.idx
1710

    
1711
      (tap, fd) = _OpenTap()
1712
      logging.info("%s %d", tap, fd)
1713

    
1714
      self._PassTapFd(instance, fd, nic)
1715

    
1716
      command = ("netdev_add tap,id=netdev%d,fd=netdev%d"
1717
                 % (idx, idx))
1718
      logging.info("Run cmd %s", command)
1719
      output = self._CallMonitorCommand(instance.name, command)
1720
      for line in output.stdout.splitlines():
1721
        logging.info("%s", line)
1722

    
1723
      command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s,"
1724
                 "netdev=netdev%d,id=virtio-net-pci.%d"
1725
                 % (hex(nic.pci), mac, idx, idx))
1726
      logging.info("Run cmd %s", command)
1727
      output = self._CallMonitorCommand(instance.name, command)
1728
      for line in output.stdout.splitlines():
1729
        logging.info("%s", line)
1730

    
1731
      self._ConfigureNIC(instance, seq, nic, tap)
1732

    
1733
      (kvm_cmd, kvm_nics,
1734
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1735
      kvm_nics.append(nic)
1736
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1737
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1738

    
1739
    return nic.ToDict()
1740

    
1741
  def HotDelNic(self, instance, nic, _):
1742
    """Hotadd new nic to the VM
1743

1744
    """
1745
    if not self._InstancePidAlive(instance.name)[2]:
1746
      logging.info("Cannot hotplug. Instance %s not alive", instance.name)
1747
      return nic.ToDict()
1748

    
1749
    _, v_major, v_min, _ = self._GetKVMVersion()
1750
    if (v_major, v_min) >= (1, 0) and nic.pci:
1751
      idx = nic.idx
1752

    
1753
      command = "device_del virtio-net-pci.%d" % idx
1754
      logging.info("Run cmd %s", command)
1755
      output = self._CallMonitorCommand(instance.name, command)
1756
      for line in output.stdout.splitlines():
1757
        logging.info("%s", line)
1758

    
1759
      command = "netdev_del netdev%d" % idx
1760
      logging.info("Run cmd %s", command)
1761
      output = self._CallMonitorCommand(instance.name, command)
1762
      for line in output.stdout.splitlines():
1763
        logging.info("%s", line)
1764

    
1765
      (kvm_cmd, kvm_nics,
1766
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1767
      rem  = [n for n in kvm_nics if n.idx is not None and n.idx == nic.idx]
1768
      try:
1769
        kvm_nics.remove(rem[0])
1770
      except (ValueError, IndexError):
1771
        logging.info("NIC with %d idx disappeared from runtime file", nic.idx)
1772
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1773
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1774

    
1775
    return nic.ToDict()
1776

    
1777
  def _PassTapFd(self, instance, fd, nic):
1778
    monsock = utils.ShellQuote(self._InstanceMonitor(instance.name))
1779
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1780
    s.connect(monsock)
1781
    idx = nic.idx
1782
    command = "getfd netdev%d\n" % idx
1783
    fds = [fd]
1784
    logging.info("%s", fds)
1785
    fdsend.sendfds(s, command, fds = fds)
1786
    s.close()
1787

    
1788
  @classmethod
1789
  def _ParseKVMVersion(cls, text):
1790
    """Parse the KVM version from the --help output.
1791

1792
    @type text: string
1793
    @param text: output of kvm --help
1794
    @return: (version, v_maj, v_min, v_rev)
1795
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1796

1797
    """
1798
    match = cls._VERSION_RE.search(text.splitlines()[0])
1799
    if not match:
1800
      raise errors.HypervisorError("Unable to get KVM version")
1801

    
1802
    v_all = match.group(0)
1803
    v_maj = int(match.group(1))
1804
    v_min = int(match.group(2))
1805
    if match.group(4):
1806
      v_rev = int(match.group(4))
1807
    else:
1808
      v_rev = 0
1809
    return (v_all, v_maj, v_min, v_rev)
1810

    
1811
  @classmethod
1812
  def _GetKVMVersion(cls):
1813
    """Return the installed KVM version.
1814

1815
    @return: (version, v_maj, v_min, v_rev)
1816
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1817

1818
    """
1819
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1820
    if result.failed:
1821
      raise errors.HypervisorError("Unable to get KVM version")
1822
    return cls._ParseKVMVersion(result.output)
1823

    
1824
  def StopInstance(self, instance, force=False, retry=False, name=None):
1825
    """Stop an instance.
1826

1827
    """
1828
    if name is not None and not force:
1829
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1830
    if name is None:
1831
      name = instance.name
1832
      acpi = instance.hvparams[constants.HV_ACPI]
1833
    else:
1834
      acpi = False
1835
    _, pid, alive = self._InstancePidAlive(name)
1836
    if pid > 0 and alive:
1837
      if force or not acpi:
1838
        utils.KillProcess(pid)
1839
      else:
1840
        self._CallMonitorCommand(name, "system_powerdown")
1841

    
1842
  def CleanupInstance(self, instance_name):
1843
    """Cleanup after a stopped instance
1844

1845
    """
1846
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1847
    if pid > 0 and alive:
1848
      raise errors.HypervisorError("Cannot cleanup a live instance")
1849
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1850

    
1851
  def RebootInstance(self, instance):
1852
    """Reboot an instance.
1853

1854
    """
1855
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1856
    # socket the instance will stop, but now power up again. So we'll resort
1857
    # to shutdown and restart.
1858
    _, _, alive = self._InstancePidAlive(instance.name)
1859
    if not alive:
1860
      raise errors.HypervisorError("Failed to reboot instance %s:"
1861
                                   " not running" % instance.name)
1862
    # StopInstance will delete the saved KVM runtime so:
1863
    # ...first load it...
1864
    kvm_runtime = self._LoadKVMRuntime(instance)
1865
    # ...now we can safely call StopInstance...
1866
    if not self.StopInstance(instance):
1867
      self.StopInstance(instance, force=True)
1868
    # ...and finally we can save it again, and execute it...
1869
    self._SaveKVMRuntime(instance, kvm_runtime)
1870
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1871

    
1872
  def MigrationInfo(self, instance):
1873
    """Get instance information to perform a migration.
1874

1875
    @type instance: L{objects.Instance}
1876
    @param instance: instance to be migrated
1877
    @rtype: string
1878
    @return: content of the KVM runtime file
1879

1880
    """
1881
    return self._ReadKVMRuntime(instance.name)
1882

    
1883
  def AcceptInstance(self, instance, info, target):
1884
    """Prepare to accept an instance.
1885

1886
    @type instance: L{objects.Instance}
1887
    @param instance: instance to be accepted
1888
    @type info: string
1889
    @param info: content of the KVM runtime file on the source node
1890
    @type target: string
1891
    @param target: target host (usually ip), on this node
1892

1893
    """
1894
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1895
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1896
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1897

    
1898
  def FinalizeMigrationDst(self, instance, info, success):
1899
    """Finalize the instance migration on the target node.
1900

1901
    Stop the incoming mode KVM.
1902

1903
    @type instance: L{objects.Instance}
1904
    @param instance: instance whose migration is being finalized
1905

1906
    """
1907
    if success:
1908
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1909
      kvm_nics = kvm_runtime[1]
1910

    
1911
      for nic_seq, nic in enumerate(kvm_nics):
1912
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1913
          # Bridged interfaces have already been configured
1914
          continue
1915
        try:
1916
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1917
        except EnvironmentError, err:
1918
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1919
                          instance.name, nic_seq, str(err))
1920
          continue
1921
        try:
1922
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1923
        except errors.HypervisorError, err:
1924
          logging.warning(str(err))
1925

    
1926
      self._WriteKVMRuntime(instance.name, info)
1927
    else:
1928
      self.StopInstance(instance, force=True)
1929

    
1930
  def MigrateInstance(self, instance, target, live):
1931
    """Migrate an instance to a target node.
1932

1933
    The migration will not be attempted if the instance is not
1934
    currently running.
1935

1936
    @type instance: L{objects.Instance}
1937
    @param instance: the instance to be migrated
1938
    @type target: string
1939
    @param target: ip address of the target node
1940
    @type live: boolean
1941
    @param live: perform a live migration
1942

1943
    """
1944
    instance_name = instance.name
1945
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1946
    _, _, alive = self._InstancePidAlive(instance_name)
1947
    if not alive:
1948
      raise errors.HypervisorError("Instance not running, cannot migrate")
1949

    
1950
    if not live:
1951
      self._CallMonitorCommand(instance_name, "stop")
1952

    
1953
    migrate_command = ("migrate_set_speed %dm" %
1954
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1955
    self._CallMonitorCommand(instance_name, migrate_command)
1956

    
1957
    migrate_command = ("migrate_set_downtime %dms" %
1958
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1959
    self._CallMonitorCommand(instance_name, migrate_command)
1960

    
1961
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1962
    self._CallMonitorCommand(instance_name, migrate_command)
1963

    
1964
  def FinalizeMigrationSource(self, instance, success, live):
1965
    """Finalize the instance migration on the source node.
1966

1967
    @type instance: L{objects.Instance}
1968
    @param instance: the instance that was migrated
1969
    @type success: bool
1970
    @param success: whether the migration succeeded or not
1971
    @type live: bool
1972
    @param live: whether the user requested a live migration or not
1973

1974
    """
1975
    if success:
1976
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
1977
      utils.KillProcess(pid)
1978
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
1979
    elif live:
1980
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1981

    
1982
  def GetMigrationStatus(self, instance):
1983
    """Get the migration status
1984

1985
    @type instance: L{objects.Instance}
1986
    @param instance: the instance that is being migrated
1987
    @rtype: L{objects.MigrationStatus}
1988
    @return: the status of the current migration (one of
1989
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
1990
             progress info that can be retrieved from the hypervisor
1991

1992
    """
1993
    info_command = "info migrate"
1994
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
1995
      result = self._CallMonitorCommand(instance.name, info_command)
1996
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1997
      if not match:
1998
        if not result.stdout:
1999
          logging.info("KVM: empty 'info migrate' result")
2000
        else:
2001
          logging.warning("KVM: unknown 'info migrate' result: %s",
2002
                          result.stdout)
2003
      else:
2004
        status = match.group(1)
2005
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2006
          migration_status = objects.MigrationStatus(status=status)
2007
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2008
          if match:
2009
            migration_status.transferred_ram = match.group("transferred")
2010
            migration_status.total_ram = match.group("total")
2011

    
2012
          return migration_status
2013

    
2014
        logging.warning("KVM: unknown migration status '%s'", status)
2015

    
2016
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2017

    
2018
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED,
2019
                                  info="Too many 'info migrate' broken answers")
2020

    
2021
  def BalloonInstanceMemory(self, instance, mem):
2022
    """Balloon an instance memory to a certain value.
2023

2024
    @type instance: L{objects.Instance}
2025
    @param instance: instance to be accepted
2026
    @type mem: int
2027
    @param mem: actual memory size to use for instance runtime
2028

2029
    """
2030
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2031

    
2032
  def GetNodeInfo(self):
2033
    """Return information about the node.
2034

2035
    @return: a dict with the following keys (values in MiB):
2036
          - memory_total: the total memory size on the node
2037
          - memory_free: the available memory on the node for instances
2038
          - memory_dom0: the memory used by the node itself, if available
2039
          - hv_version: the hypervisor version in the form (major, minor,
2040
                        revision)
2041

2042
    """
2043
    result = self.GetLinuxNodeInfo()
2044
    _, v_major, v_min, v_rev = self._GetKVMVersion()
2045
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2046
    return result
2047

    
2048
  @classmethod
2049
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2050
    """Return a command for connecting to the console of an instance.
2051

2052
    """
2053
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2054
      cmd = [constants.KVM_CONSOLE_WRAPPER,
2055
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2056
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2057
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2058
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2059
      return objects.InstanceConsole(instance=instance.name,
2060
                                     kind=constants.CONS_SSH,
2061
                                     host=instance.primary_node,
2062
                                     user=constants.GANETI_RUNAS,
2063
                                     command=cmd)
2064

    
2065
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2066
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2067
      display = instance.network_port - constants.VNC_BASE_PORT
2068
      return objects.InstanceConsole(instance=instance.name,
2069
                                     kind=constants.CONS_VNC,
2070
                                     host=vnc_bind_address,
2071
                                     port=instance.network_port,
2072
                                     display=display)
2073

    
2074
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2075
    if spice_bind:
2076
      return objects.InstanceConsole(instance=instance.name,
2077
                                     kind=constants.CONS_SPICE,
2078
                                     host=spice_bind,
2079
                                     port=instance.network_port)
2080

    
2081
    return objects.InstanceConsole(instance=instance.name,
2082
                                   kind=constants.CONS_MESSAGE,
2083
                                   message=("No serial shell for instance %s" %
2084
                                            instance.name))
2085

    
2086
  def Verify(self):
2087
    """Verify the hypervisor.
2088

2089
    Check that the binary exists.
2090

2091
    """
2092
    if not os.path.exists(constants.KVM_PATH):
2093
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
2094
    if not os.path.exists(constants.SOCAT_PATH):
2095
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
2096

    
2097
  @classmethod
2098
  def CheckParameterSyntax(cls, hvparams):
2099
    """Check the given parameters for validity.
2100

2101
    @type hvparams:  dict
2102
    @param hvparams: dictionary with parameter names/value
2103
    @raise errors.HypervisorError: when a parameter is not valid
2104

2105
    """
2106
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2107

    
2108
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2109
    if kernel_path:
2110
      if not hvparams[constants.HV_ROOT_PATH]:
2111
        raise errors.HypervisorError("Need a root partition for the instance,"
2112
                                     " if a kernel is defined")
2113

    
2114
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2115
        not hvparams[constants.HV_VNC_X509]):
2116
      raise errors.HypervisorError("%s must be defined, if %s is" %
2117
                                   (constants.HV_VNC_X509,
2118
                                    constants.HV_VNC_X509_VERIFY))
2119

    
2120
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2121
    if (boot_order == constants.HT_BO_CDROM and
2122
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2123
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2124
                                   " ISO path")
2125

    
2126
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2127
    if security_model == constants.HT_SM_USER:
2128
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2129
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2130
                                     " must be specified")
2131
    elif (security_model == constants.HT_SM_NONE or
2132
          security_model == constants.HT_SM_POOL):
2133
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2134
        raise errors.HypervisorError("Cannot have a security domain when the"
2135
                                     " security model is 'none' or 'pool'")
2136

    
2137
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2138
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2139
    if spice_bind:
2140
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2141
        # if an IP version is specified, the spice_bind parameter must be an
2142
        # IP of that family
2143
        if (netutils.IP4Address.IsValid(spice_bind) and
2144
            spice_ip_version != constants.IP4_VERSION):
2145
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2146
                                       " the specified IP version is %s" %
2147
                                       (spice_bind, spice_ip_version))
2148

    
2149
        if (netutils.IP6Address.IsValid(spice_bind) and
2150
            spice_ip_version != constants.IP6_VERSION):
2151
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2152
                                       " the specified IP version is %s" %
2153
                                       (spice_bind, spice_ip_version))
2154
    else:
2155
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2156
      # error if any of them is set without it.
2157
      spice_additional_params = frozenset([
2158
        constants.HV_KVM_SPICE_IP_VERSION,
2159
        constants.HV_KVM_SPICE_PASSWORD_FILE,
2160
        constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
2161
        constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
2162
        constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
2163
        constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
2164
        constants.HV_KVM_SPICE_USE_TLS,
2165
        ])
2166
      for param in spice_additional_params:
2167
        if hvparams[param]:
2168
          raise errors.HypervisorError("spice: %s requires %s to be set" %
2169
                                       (param, constants.HV_KVM_SPICE_BIND))
2170

    
2171
  @classmethod
2172
  def ValidateParameters(cls, hvparams):
2173
    """Check the given parameters for validity.
2174

2175
    @type hvparams:  dict
2176
    @param hvparams: dictionary with parameter names/value
2177
    @raise errors.HypervisorError: when a parameter is not valid
2178

2179
    """
2180
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2181

    
2182
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2183
    if security_model == constants.HT_SM_USER:
2184
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2185
      try:
2186
        pwd.getpwnam(username)
2187
      except KeyError:
2188
        raise errors.HypervisorError("Unknown security domain user %s"
2189
                                     % username)
2190

    
2191
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2192
    if spice_bind:
2193
      # only one of VNC and SPICE can be used currently.
2194
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2195
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
2196
                                     " only one of them can be used at a"
2197
                                     " given time.")
2198

    
2199
      # KVM version should be >= 0.14.0
2200
      _, v_major, v_min, _ = cls._GetKVMVersion()
2201
      if (v_major, v_min) < (0, 14):
2202
        raise errors.HypervisorError("spice is configured, but it is not"
2203
                                     " available in versions of KVM < 0.14")
2204

    
2205
      # if spice_bind is not an IP address, it must be a valid interface
2206
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2207
                       or netutils.IP6Address.IsValid(spice_bind))
2208
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2209
        raise errors.HypervisorError("spice: the %s parameter must be either"
2210
                                     " a valid IP address or interface name" %
2211
                                     constants.HV_KVM_SPICE_BIND)
2212

    
2213
  @classmethod
2214
  def PowercycleNode(cls):
2215
    """KVM powercycle, just a wrapper over Linux powercycle.
2216

2217
    """
2218
    cls.LinuxPowercycle()