Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 2435f63b

History | View | Annotate | Download (79.9 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
from bitarray import bitarray
42
try:
43
  import affinity   # pylint: disable=F0401
44
except ImportError:
45
  affinity = None
46

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

    
58

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

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

    
72
FREE = bitarray("0")
73

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

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

87
   @type fd: int
88
   @param fd: the file descriptor of /dev/net/tun
89

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

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

    
105

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

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

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

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

    
123
  flags = IFF_TAP | IFF_NO_PI
124

    
125
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
126
    flags |= IFF_VNET_HDR
127

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

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

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

    
140

    
141
class QmpMessage:
142
  """QEMU Messaging Protocol (QMP) message.
143

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

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

    
152
    self.data = data
153

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

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

162
    """
163
    return self.data.get(field_name, None)
164

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

168
    """
169
    self.data[field_name] = field_value
170

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

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

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

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

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

    
194

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

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

    
213
  def __init__(self, monitor_filename):
214
    """Instantiates the QmpConnection object.
215

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

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

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

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

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

    
250
  def connect(self):
251
    """Connects to the QMP monitor.
252

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

256
    @raise errors.HypervisorError: when there are communication errors
257
    @raise errors.ProgrammerError: when there are data serialization errors
258

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

    
263
    self._check_socket()
264

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

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

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

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

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

292
    @raise errors.ProgrammerError: when there are data serialization errors
293

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

    
306
    return (message, buf)
307

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

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

316
    """
317
    self._check_connection()
318

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

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

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

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

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

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

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

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

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

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

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

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

    
400
      elif not response[self._EVENT_KEY]:
401
        return response
402

    
403

    
404
class KVMHypervisor(hv_base.BaseHypervisor):
405
  """KVM hypervisor interface
406

407
  """
408
  CAN_MIGRATE = True
409

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

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

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

    
508
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
509
  _MIGRATION_INFO_RETRY_DELAY = 2
510

    
511
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
512

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

    
517
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
518
  _INFO_PCI_CMD = "info pci"
519

    
520

    
521
  ANCILLARY_FILES = [
522
    _KVM_NETWORK_SCRIPT,
523
    ]
524
  ANCILLARY_FILES_OPT = [
525
    _KVM_NETWORK_SCRIPT,
526
    ]
527

    
528
  def __init__(self):
529
    hv_base.BaseHypervisor.__init__(self)
530
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
531
    # in a tmpfs filesystem or has been otherwise wiped out.
532
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
533
    utils.EnsureDirs(dirs)
534

    
535
  @classmethod
536
  def _InstancePidFile(cls, instance_name):
537
    """Returns the instance pidfile.
538

539
    """
540
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
541

    
542
  @classmethod
543
  def _InstanceUidFile(cls, instance_name):
544
    """Returns the instance uidfile.
545

546
    """
547
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
548

    
549
  @classmethod
550
  def _InstancePidInfo(cls, pid):
551
    """Check pid file for instance information.
552

553
    Check that a pid file is associated with an instance, and retrieve
554
    information from its command line.
555

556
    @type pid: string or int
557
    @param pid: process id of the instance to check
558
    @rtype: tuple
559
    @return: (instance_name, memory, vcpus)
560
    @raise errors.HypervisorError: when an instance cannot be found
561

562
    """
563
    alive = utils.IsProcessAlive(pid)
564
    if not alive:
565
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
566

    
567
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
568
    try:
569
      cmdline = utils.ReadFile(cmdline_file)
570
    except EnvironmentError, err:
571
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
572
                                   (pid, err))
573

    
574
    instance = None
575
    memory = 0
576
    vcpus = 0
577

    
578
    arg_list = cmdline.split("\x00")
579
    while arg_list:
580
      arg = arg_list.pop(0)
581
      if arg == "-name":
582
        instance = arg_list.pop(0)
583
      elif arg == "-m":
584
        memory = int(arg_list.pop(0))
585
      elif arg == "-smp":
586
        vcpus = int(arg_list.pop(0))
587

    
588
    if instance is None:
589
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
590
                                   " instance" % pid)
591

    
592
    return (instance, memory, vcpus)
593

    
594
  def _InstancePidAlive(self, instance_name):
595
    """Returns the instance pidfile, pid, and liveness.
596

597
    @type instance_name: string
598
    @param instance_name: instance name
599
    @rtype: tuple
600
    @return: (pid file name, pid, liveness)
601

602
    """
603
    pidfile = self._InstancePidFile(instance_name)
604
    pid = utils.ReadPidFile(pidfile)
605

    
606
    alive = False
607
    try:
608
      cmd_instance = self._InstancePidInfo(pid)[0]
609
      alive = (cmd_instance == instance_name)
610
    except errors.HypervisorError:
611
      pass
612

    
613
    return (pidfile, pid, alive)
614

    
615
  def _CheckDown(self, instance_name):
616
    """Raises an error unless the given instance is down.
617

618
    """
619
    alive = self._InstancePidAlive(instance_name)[2]
620
    if alive:
621
      raise errors.HypervisorError("Failed to start instance %s: %s" %
622
                                   (instance_name, "already running"))
623

    
624
  @classmethod
625
  def _InstanceMonitor(cls, instance_name):
626
    """Returns the instance monitor socket name
627

628
    """
629
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
630

    
631
  @classmethod
632
  def _InstanceSerial(cls, instance_name):
633
    """Returns the instance serial socket name
634

635
    """
636
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
637

    
638
  @classmethod
639
  def _InstanceQmpMonitor(cls, instance_name):
640
    """Returns the instance serial QMP socket name
641

642
    """
643
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
644

    
645
  @staticmethod
646
  def _SocatUnixConsoleParams():
647
    """Returns the correct parameters for socat
648

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

651
    """
652
    if constants.SOCAT_USE_ESCAPE:
653
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
654
    else:
655
      return "echo=0,icanon=0"
656

    
657
  @classmethod
658
  def _InstanceKVMRuntime(cls, instance_name):
659
    """Returns the instance KVM runtime filename
660

661
    """
662
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
663

    
664
  @classmethod
665
  def _InstanceChrootDir(cls, instance_name):
666
    """Returns the name of the KVM chroot dir of the instance
667

668
    """
669
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
670

    
671
  @classmethod
672
  def _InstanceNICDir(cls, instance_name):
673
    """Returns the name of the directory holding the tap device files for a
674
    given instance.
675

676
    """
677
    return utils.PathJoin(cls._NICS_DIR, instance_name)
678

    
679
  @classmethod
680
  def _InstanceNICFile(cls, instance_name, seq):
681
    """Returns the name of the file containing the tap device for a given NIC
682

683
    """
684
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
685

    
686
  @classmethod
687
  def _InstanceKeymapFile(cls, instance_name):
688
    """Returns the name of the file containing the keymap for a given instance
689

690
    """
691
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
692

    
693
  @classmethod
694
  def _TryReadUidFile(cls, uid_file):
695
    """Try to read a uid file
696

697
    """
698
    if os.path.exists(uid_file):
699
      try:
700
        uid = int(utils.ReadOneLineFile(uid_file))
701
        return uid
702
      except EnvironmentError:
703
        logging.warning("Can't read uid file", exc_info=True)
704
      except (TypeError, ValueError):
705
        logging.warning("Can't parse uid file contents", exc_info=True)
706
    return None
707

    
708
  @classmethod
709
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
710
    """Removes an instance's rutime sockets/files/dirs.
711

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

    
748
  @staticmethod
749
  def _ConfigureNIC(instance, seq, nic, tap):
750
    """Run the network configuration script for a specified NIC
751

752
    @param instance: instance we're acting on
753
    @type instance: instance object
754
    @param seq: nic sequence number
755
    @type seq: int
756
    @param nic: nic we're acting on
757
    @type nic: nic object
758
    @param tap: the host's tap interface this NIC corresponds to
759
    @type tap: str
760

761
    """
762
    if instance.tags:
763
      tags = " ".join(instance.tags)
764
    else:
765
      tags = ""
766

    
767
    env = {
768
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
769
      "INSTANCE": instance.name,
770
      "MAC": nic.mac,
771
      "MODE": nic.nicparams[constants.NIC_MODE],
772
      "INTERFACE": tap,
773
      "INTERFACE_INDEX": str(seq),
774
      "TAGS": tags,
775
    }
776

    
777
    if nic.ip:
778
      env["IP"] = nic.ip
779

    
780
    if nic.nicparams[constants.NIC_LINK]:
781
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
782

    
783
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
784
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
785

    
786
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
787
    if result.failed:
788
      raise errors.HypervisorError("Failed to configure interface %s: %s."
789
                                   " Network configuration script output: %s" %
790
                                   (tap, result.fail_reason, result.output))
791

    
792
  @staticmethod
793
  def _VerifyAffinityPackage():
794
    if affinity is None:
795
      raise errors.HypervisorError("affinity Python package not"
796
        " found; cannot use CPU pinning under KVM")
797

    
798
  @staticmethod
799
  def _BuildAffinityCpuMask(cpu_list):
800
    """Create a CPU mask suitable for sched_setaffinity from a list of
801
    CPUs.
802

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

806
    @type cpu_list: list of int
807
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
808
    @rtype: int
809
    @return: a bit mask of CPU affinities
810

811
    """
812
    if cpu_list == constants.CPU_PINNING_OFF:
813
      return constants.CPU_PINNING_ALL_KVM
814
    else:
815
      return sum(2 ** cpu for cpu in cpu_list)
816

    
817
  @classmethod
818
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
819
    """Change CPU affinity for running VM according to given CPU mask.
820

821
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
822
    @type cpu_mask: string
823
    @param process_id: process ID of KVM process. Used to pin entire VM
824
                       to physical CPUs.
825
    @type process_id: int
826
    @param thread_dict: map of virtual CPUs to KVM thread IDs
827
    @type thread_dict: dict int:int
828

829
    """
830
    # Convert the string CPU mask to a list of list of int's
831
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
832

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

    
851
      # For each vCPU, map it to the proper list of physical CPUs
852
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
853
        affinity.set_process_affinity_mask(thread_dict[i],
854
          cls._BuildAffinityCpuMask(vcpu))
855

    
856
  def _GetVcpuThreadIds(self, instance_name):
857
    """Get a mapping of vCPU no. to thread IDs for the instance
858

859
    @type instance_name: string
860
    @param instance_name: instance in question
861
    @rtype: dictionary of int:int
862
    @return: a dictionary mapping vCPU numbers to thread IDs
863

864
    """
865
    result = {}
866
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
867
    for line in output.stdout.splitlines():
868
      match = self._CPU_INFO_RE.search(line)
869
      if not match:
870
        continue
871
      grp = map(int, match.groups())
872
      result[grp[0]] = grp[1]
873

    
874
    return result
875

    
876
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
877
    """Complete CPU pinning.
878

879
    @type instance_name: string
880
    @param instance_name: name of instance
881
    @type cpu_mask: string
882
    @param cpu_mask: CPU pinning mask as entered by user
883

884
    """
885
    # Get KVM process ID, to be used if need to pin entire VM
886
    _, pid, _ = self._InstancePidAlive(instance_name)
887
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
888
    thread_dict = self._GetVcpuThreadIds(instance_name)
889
    # Run CPU pinning, based on configured mask
890
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
891

    
892
  def ListInstances(self):
893
    """Get the list of running instances.
894

895
    We can do this by listing our live instances directory and
896
    checking whether the associated kvm process is still alive.
897

898
    """
899
    result = []
900
    for name in os.listdir(self._PIDS_DIR):
901
      if self._InstancePidAlive(name)[2]:
902
        result.append(name)
903
    return result
904

    
905
  def GetInstanceInfo(self, instance_name):
906
    """Get instance properties.
907

908
    @type instance_name: string
909
    @param instance_name: the instance name
910
    @rtype: tuple of strings
911
    @return: (name, id, memory, vcpus, stat, times)
912

913
    """
914
    _, pid, alive = self._InstancePidAlive(instance_name)
915
    if not alive:
916
      return None
917

    
918
    _, memory, vcpus = self._InstancePidInfo(pid)
919
    istat = "---b-"
920
    times = "0"
921

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

    
933
    return (instance_name, pid, memory, vcpus, istat, times)
934

    
935
  def GetAllInstancesInfo(self):
936
    """Get properties of all instances.
937

938
    @return: list of tuples (name, id, memory, vcpus, stat, times)
939

940
    """
941
    data = []
942
    for name in os.listdir(self._PIDS_DIR):
943
      try:
944
        info = self.GetInstanceInfo(name)
945
      except errors.HypervisorError:
946
        # Ignore exceptions due to instances being shut down
947
        continue
948
      if info:
949
        data.append(info)
950
    return data
951

    
952
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_cmd, block_devices):
953

    
954
    hvp = instance.hvparams
955
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
956

    
957
    _, v_major, v_min, _ = self._GetKVMVersion()
958

    
959
    # whether this is an older KVM version that uses the boot=on flag
960
    # on devices
961
    needs_boot_flag = (v_major, v_min) < (0, 14)
962

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

    
1007
      kvm_cmd.extend(["-drive", drive_val])
1008

    
1009
      if cfdev.idx is not None:
1010
        dev_val = ("%s,drive=drive%d,id=virtio-blk-pci.%d" %
1011
                    (disk_model, cfdev.idx, cfdev.idx))
1012
        if cfdev.pci is not None:
1013
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1014
        kvm_cmd.extend(["-device", dev_val])
1015

    
1016
    return kvm_cmd
1017

    
1018
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
1019
    """Generate KVM information to start an instance.
1020

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

1028
    """
1029
    # pylint: disable=R0914,R0915
1030
    _, v_major, v_min, _ = self._GetKVMVersion()
1031

    
1032
    pidfile = self._InstancePidFile(instance.name)
1033
    kvm = constants.KVM_PATH
1034
    kvm_cmd = [kvm]
1035
    # used just by the vnc server, if enabled
1036
    kvm_cmd.extend(["-name", instance.name])
1037
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1038
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
1039
    kvm_cmd.extend(["-pidfile", pidfile])
1040
    kvm_cmd.extend(["-balloon", "virtio"])
1041
    kvm_cmd.extend(["-daemonize"])
1042
    if not instance.hvparams[constants.HV_ACPI]:
1043
      kvm_cmd.extend(["-no-acpi"])
1044
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1045
        constants.INSTANCE_REBOOT_EXIT:
1046
      kvm_cmd.extend(["-no-reboot"])
1047

    
1048
    hvp = instance.hvparams
1049
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1050
    if kernel_path:
1051
      boot_cdrom = boot_floppy = boot_network = False
1052
    else:
1053
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1054
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1055
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1056

    
1057
    self.ValidateParameters(hvp)
1058

    
1059
    if startup_paused:
1060
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1061

    
1062
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1063
      kvm_cmd.extend(["-enable-kvm"])
1064
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1065
      kvm_cmd.extend(["-disable-kvm"])
1066

    
1067
    if boot_network:
1068
      kvm_cmd.extend(["-boot", "n"])
1069

    
1070
    # whether this is an older KVM version that uses the boot=on flag
1071
    # on devices
1072
    needs_boot_flag = (v_major, v_min) < (0, 14)
1073

    
1074
    disk_type = hvp[constants.HV_DISK_TYPE]
1075
    if not instance.hotplug_info:
1076
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1077
                                                     block_devices)
1078

    
1079
    #Now we can specify a different device type for CDROM devices.
1080
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1081
    if not cdrom_disk_type:
1082
      cdrom_disk_type = disk_type
1083

    
1084
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1085
    if iso_image:
1086
      options = ",format=raw,media=cdrom"
1087
      # set cdrom 'if' type
1088
      if boot_cdrom:
1089
        actual_cdrom_type = constants.HT_DISK_IDE
1090
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1091
        actual_cdrom_type = "virtio"
1092
      else:
1093
        actual_cdrom_type = cdrom_disk_type
1094
      if_val = ",if=%s" % actual_cdrom_type
1095
      # set boot flag, if needed
1096
      boot_val = ""
1097
      if boot_cdrom:
1098
        kvm_cmd.extend(["-boot", "d"])
1099
        if needs_boot_flag:
1100
          boot_val = ",boot=on"
1101
      # and finally build the entire '-drive' value
1102
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1103
      kvm_cmd.extend(["-drive", drive_val])
1104

    
1105
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1106
    if iso_image2:
1107
      options = ",format=raw,media=cdrom"
1108
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1109
        if_val = ",if=virtio"
1110
      else:
1111
        if_val = ",if=%s" % cdrom_disk_type
1112
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1113
      kvm_cmd.extend(["-drive", drive_val])
1114

    
1115
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1116
    if floppy_image:
1117
      options = ",format=raw,media=disk"
1118
      if boot_floppy:
1119
        kvm_cmd.extend(["-boot", "a"])
1120
        options = "%s,boot=on" % options
1121
      if_val = ",if=floppy"
1122
      options = "%s%s" % (options, if_val)
1123
      drive_val = "file=%s%s" % (floppy_image, options)
1124
      kvm_cmd.extend(["-drive", drive_val])
1125

    
1126
    if kernel_path:
1127
      kvm_cmd.extend(["-kernel", kernel_path])
1128
      initrd_path = hvp[constants.HV_INITRD_PATH]
1129
      if initrd_path:
1130
        kvm_cmd.extend(["-initrd", initrd_path])
1131
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1132
                     hvp[constants.HV_KERNEL_ARGS]]
1133
      if hvp[constants.HV_SERIAL_CONSOLE]:
1134
        root_append.append("console=ttyS0,38400")
1135
      kvm_cmd.extend(["-append", " ".join(root_append)])
1136

    
1137
    mem_path = hvp[constants.HV_MEM_PATH]
1138
    if mem_path:
1139
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1140

    
1141
    monitor_dev = ("unix:%s,server,nowait" %
1142
                   self._InstanceMonitor(instance.name))
1143
    kvm_cmd.extend(["-monitor", monitor_dev])
1144
    if hvp[constants.HV_SERIAL_CONSOLE]:
1145
      serial_dev = ("unix:%s,server,nowait" %
1146
                    self._InstanceSerial(instance.name))
1147
      kvm_cmd.extend(["-serial", serial_dev])
1148
    else:
1149
      kvm_cmd.extend(["-serial", "none"])
1150

    
1151
    mouse_type = hvp[constants.HV_USB_MOUSE]
1152
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1153
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1154
    spice_ip_version = None
1155

    
1156
    if mouse_type:
1157
      kvm_cmd.extend(["-usb"])
1158
      kvm_cmd.extend(["-usbdevice", mouse_type])
1159
    elif vnc_bind_address:
1160
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1161

    
1162
    if vnc_bind_address:
1163
      if netutils.IP4Address.IsValid(vnc_bind_address):
1164
        if instance.network_port > constants.VNC_BASE_PORT:
1165
          display = instance.network_port - constants.VNC_BASE_PORT
1166
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1167
            vnc_arg = ":%d" % (display)
1168
          else:
1169
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1170
        else:
1171
          logging.error("Network port is not a valid VNC display (%d < %d)."
1172
                        " Not starting VNC", instance.network_port,
1173
                        constants.VNC_BASE_PORT)
1174
          vnc_arg = "none"
1175

    
1176
        # Only allow tls and other option when not binding to a file, for now.
1177
        # kvm/qemu gets confused otherwise about the filename to use.
1178
        vnc_append = ""
1179
        if hvp[constants.HV_VNC_TLS]:
1180
          vnc_append = "%s,tls" % vnc_append
1181
          if hvp[constants.HV_VNC_X509_VERIFY]:
1182
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1183
                                               hvp[constants.HV_VNC_X509])
1184
          elif hvp[constants.HV_VNC_X509]:
1185
            vnc_append = "%s,x509=%s" % (vnc_append,
1186
                                         hvp[constants.HV_VNC_X509])
1187
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1188
          vnc_append = "%s,password" % vnc_append
1189

    
1190
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1191

    
1192
      else:
1193
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1194

    
1195
      kvm_cmd.extend(["-vnc", vnc_arg])
1196
    elif spice_bind:
1197
      # FIXME: this is wrong here; the iface ip address differs
1198
      # between systems, so it should be done in _ExecuteKVMRuntime
1199
      if netutils.IsValidInterface(spice_bind):
1200
        # The user specified a network interface, we have to figure out the IP
1201
        # address.
1202
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1203
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1204

    
1205
        # if the user specified an IP version and the interface does not
1206
        # have that kind of IP addresses, throw an exception
1207
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1208
          if not addresses[spice_ip_version]:
1209
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
1210
                                         " for %s" % (spice_ip_version,
1211
                                                      spice_bind))
1212

    
1213
        # the user did not specify an IP version, we have to figure it out
1214
        elif (addresses[constants.IP4_VERSION] and
1215
              addresses[constants.IP6_VERSION]):
1216
          # we have both ipv4 and ipv6, let's use the cluster default IP
1217
          # version
1218
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1219
          spice_ip_version = \
1220
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1221
        elif addresses[constants.IP4_VERSION]:
1222
          spice_ip_version = constants.IP4_VERSION
1223
        elif addresses[constants.IP6_VERSION]:
1224
          spice_ip_version = constants.IP6_VERSION
1225
        else:
1226
          raise errors.HypervisorError("spice: unable to get an IP address"
1227
                                       " for %s" % (spice_bind))
1228

    
1229
        spice_address = addresses[spice_ip_version][0]
1230

    
1231
      else:
1232
        # spice_bind is known to be a valid IP address, because
1233
        # ValidateParameters checked it.
1234
        spice_address = spice_bind
1235

    
1236
      spice_arg = "addr=%s" % spice_address
1237
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1238
        spice_arg = "%s,tls-port=%s,x509-cacert-file=%s" % (spice_arg,
1239
            instance.network_port, constants.SPICE_CACERT_FILE)
1240
        spice_arg = "%s,x509-key-file=%s,x509-cert-file=%s" % (spice_arg,
1241
            constants.SPICE_CERT_FILE, constants.SPICE_CERT_FILE)
1242
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1243
        if tls_ciphers:
1244
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1245
      else:
1246
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1247

    
1248
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1249
        spice_arg = "%s,disable-ticketing" % spice_arg
1250

    
1251
      if spice_ip_version:
1252
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1253

    
1254
      # Image compression options
1255
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1256
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1257
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1258
      if img_lossless:
1259
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1260
      if img_jpeg:
1261
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1262
      if img_zlib_glz:
1263
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1264

    
1265
      # Video stream detection
1266
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1267
      if video_streaming:
1268
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1269

    
1270
      # Audio compression, by default in qemu-kvm it is on
1271
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1272
        spice_arg = "%s,playback-compression=off" % spice_arg
1273
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1274
        spice_arg = "%s,agent-mouse=off" % spice_arg
1275
      else:
1276
        # Enable the spice agent communication channel between the host and the
1277
        # agent.
1278
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1279
        kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1280
                                                   "name=com.redhat.spice.0"])
1281
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1282

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

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

    
1289
    else:
1290
      kvm_cmd.extend(["-nographic"])
1291

    
1292
    if hvp[constants.HV_USE_LOCALTIME]:
1293
      kvm_cmd.extend(["-localtime"])
1294

    
1295
    if hvp[constants.HV_KVM_USE_CHROOT]:
1296
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1297

    
1298
    # Save the current instance nics, but defer their expansion as parameters,
1299
    # as we'll need to generate executable temp files for them.
1300
    kvm_nics = instance.nics
1301
    hvparams = hvp
1302

    
1303
    if instance.hotplug_info:
1304
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1305
    else:
1306
      return (kvm_cmd, kvm_nics, hvparams)
1307

    
1308
  def _WriteKVMRuntime(self, instance_name, data):
1309
    """Write an instance's KVM runtime
1310

1311
    """
1312
    try:
1313
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1314
                      data=data)
1315
    except EnvironmentError, err:
1316
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1317

    
1318
  def _ReadKVMRuntime(self, instance_name):
1319
    """Read an instance's KVM runtime
1320

1321
    """
1322
    try:
1323
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1324
    except EnvironmentError, err:
1325
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1326
    return file_content
1327

    
1328
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1329
    """Save an instance's KVM runtime
1330

1331
    """
1332
    if instance.hotplug_info:
1333
      kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime
1334
      serialized_blockdevs = [(blk.ToDict(), link)
1335
                              for blk,link in block_devices]
1336
    else:
1337
      kvm_cmd, kvm_nics, hvparams = kvm_runtime
1338

    
1339
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1340

    
1341
    if instance.hotplug_info:
1342
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics,
1343
                                        hvparams, serialized_blockdevs))
1344
    else:
1345
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1346

    
1347
    self._WriteKVMRuntime(instance.name, serialized_form)
1348

    
1349
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1350
    """Load an instance's KVM runtime
1351

1352
    """
1353
    if not serialized_runtime:
1354
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1355
    loaded_runtime = serializer.Load(serialized_runtime)
1356
    if instance.hotplug_info:
1357
      kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime
1358
      block_devices = [(objects.Disk.FromDict(sdisk), link)
1359
                       for sdisk, link in serialized_blockdevs]
1360
    else:
1361
      kvm_cmd, serialized_nics, hvparams = loaded_runtime
1362

    
1363
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1364

    
1365
    if instance.hotplug_info:
1366
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1367
    else:
1368
      return (kvm_cmd, kvm_nics, hvparams)
1369

    
1370
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1371
    """Run the KVM cmd and check for errors
1372

1373
    @type name: string
1374
    @param name: instance name
1375
    @type kvm_cmd: list of strings
1376
    @param kvm_cmd: runcmd input for kvm
1377
    @type tap_fds: list of int
1378
    @param tap_fds: fds of tap devices opened by Ganeti
1379

1380
    """
1381
    try:
1382
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1383
    finally:
1384
      for fd in tap_fds:
1385
        utils_wrapper.CloseFdNoError(fd)
1386

    
1387
    if result.failed:
1388
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1389
                                   (name, result.fail_reason, result.output))
1390
    if not self._InstancePidAlive(name)[2]:
1391
      raise errors.HypervisorError("Failed to start instance %s" % name)
1392

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

1396
    @type incoming: tuple of strings
1397
    @param incoming: (target_host_ip, port)
1398

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

    
1412
    temp_files = []
1413

    
1414
    if instance.hotplug_info:
1415
      kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
1416
    else:
1417
      kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1418

    
1419
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1420

    
1421
    _, v_major, v_min, _ = self._GetKVMVersion()
1422

    
1423
    # We know it's safe to run as a different user upon migration, so we'll use
1424
    # the latest conf, from conf_hvp.
1425
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1426
    if security_model == constants.HT_SM_USER:
1427
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1428

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

    
1439
    if instance.hotplug_info:
1440
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1441
                                                     block_devices)
1442

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

    
1462
        if up_hvp[constants.HV_VHOST_NET]:
1463
          # vhost_net is only available from version 0.13.0 or newer
1464
          if (v_major, v_min) >= (0, 13):
1465
            tap_extra = ",vhost=on"
1466
          else:
1467
            raise errors.HypervisorError("vhost_net is configured"
1468
                                        " but it is not available")
1469
      else:
1470
        nic_model = nic_type
1471

    
1472
      for nic_seq, nic in enumerate(kvm_nics):
1473
        tapname, tapfd = _OpenTap(vnet_hdr)
1474
        tapfds.append(tapfd)
1475
        taps.append(tapname)
1476
        if (v_major, v_min) >= (0, 12):
1477
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1478
          if nic.idx:
1479
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1480
                        (nic.idx, nic.idx))
1481
            if nic.pci is not None:
1482
              nic_val += (",bus=pci.0,addr=%s" % hex(nic.pci))
1483
          else:
1484
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1485
                        (nic_seq, nic_seq))
1486
          tap_val = ("type=tap,id=netdev%d,fd=%d%s" %
1487
                     (nic.idx or nic_seq, tapfd, tap_extra))
1488
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1489
        else:
1490
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1491
                                                         nic.mac, nic_model)
1492
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1493
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1494

    
1495
    if incoming:
1496
      target, port = incoming
1497
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1498

    
1499
    # Changing the vnc password doesn't bother the guest that much. At most it
1500
    # will surprise people who connect to it. Whether positively or negatively
1501
    # it's debatable.
1502
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1503
    vnc_pwd = None
1504
    if vnc_pwd_file:
1505
      try:
1506
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1507
      except EnvironmentError, err:
1508
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1509
                                     % (vnc_pwd_file, err))
1510

    
1511
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1512
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1513
                         constants.SECURE_DIR_MODE)])
1514

    
1515
    # Automatically enable QMP if version is >= 0.14
1516
    if (v_major, v_min) >= (0, 14):
1517
      logging.debug("Enabling QMP")
1518
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1519
                    self._InstanceQmpMonitor(instance.name)])
1520

    
1521
    # Configure the network now for starting instances and bridged interfaces,
1522
    # during FinalizeMigration for incoming instances' routed interfaces
1523
    for nic_seq, nic in enumerate(kvm_nics):
1524
      if (incoming and
1525
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1526
        continue
1527
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1528

    
1529
    # CPU affinity requires kvm to start paused, so we set this flag if the
1530
    # instance is not already paused and if we are not going to accept a
1531
    # migrating instance. In the latter case, pausing is not needed.
1532
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1533
    if start_kvm_paused:
1534
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1535

    
1536
    # Note: CPU pinning is using up_hvp since changes take effect
1537
    # during instance startup anyway, and to avoid problems when soft
1538
    # rebooting the instance.
1539
    cpu_pinning = False
1540
    if up_hvp.get(constants.HV_CPU_MASK, None):
1541
      cpu_pinning = True
1542

    
1543
    if security_model == constants.HT_SM_POOL:
1544
      ss = ssconf.SimpleStore()
1545
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1546
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1547
      uid = uidpool.RequestUnusedUid(all_uids)
1548
      try:
1549
        username = pwd.getpwuid(uid.GetUid()).pw_name
1550
        kvm_cmd.extend(["-runas", username])
1551
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1552
      except:
1553
        uidpool.ReleaseUid(uid)
1554
        raise
1555
      else:
1556
        uid.Unlock()
1557
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1558
    else:
1559
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1560

    
1561
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1562
                     constants.RUN_DIRS_MODE)])
1563
    for nic_seq, tap in enumerate(taps):
1564
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1565
                      data=tap)
1566

    
1567
    if vnc_pwd:
1568
      change_cmd = "change vnc password %s" % vnc_pwd
1569
      self._CallMonitorCommand(instance.name, change_cmd)
1570

    
1571
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1572
    # connection attempts because SPICE by default does not allow connections
1573
    # if neither a password nor the "disable_ticketing" options are specified.
1574
    # As soon as we send the password via QMP, that password is a valid ticket
1575
    # for connection.
1576
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1577
    if spice_password_file:
1578
      spice_pwd = ""
1579
      try:
1580
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1581
      except EnvironmentError, err:
1582
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1583
                                     % (spice_password_file, err))
1584

    
1585
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1586
      qmp.connect()
1587
      arguments = {
1588
          "protocol": "spice",
1589
          "password": spice_pwd,
1590
      }
1591
      qmp.Execute("set_password", arguments)
1592

    
1593
    for filename in temp_files:
1594
      utils.RemoveFile(filename)
1595

    
1596
    # If requested, set CPU affinity and resume instance execution
1597
    if cpu_pinning:
1598
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1599

    
1600
    start_memory = self._InstanceStartupMemory(instance)
1601
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1602
      self.BalloonInstanceMemory(instance, start_memory)
1603

    
1604
    if start_kvm_paused:
1605
      # To control CPU pinning, ballooning, and vnc/spice passwords
1606
      # the VM was started in a frozen state. If freezing was not
1607
      # explicitly requested resume the vm status.
1608
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1609

    
1610
  def StartInstance(self, instance, block_devices, startup_paused):
1611
    """Start an instance.
1612

1613
    """
1614
    self._CheckDown(instance.name)
1615
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1616
                                           startup_paused)
1617
    self._SaveKVMRuntime(instance, kvm_runtime)
1618
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1619

    
1620
  def _CallMonitorCommand(self, instance_name, command):
1621
    """Invoke a command on the instance monitor.
1622

1623
    """
1624
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1625
             (utils.ShellQuote(command),
1626
              constants.SOCAT_PATH,
1627
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1628
    result = utils.RunCmd(socat)
1629
    if result.failed:
1630
      msg = ("Failed to send command '%s' to instance %s."
1631
             " output: %s, error: %s, fail_reason: %s" %
1632
             (command, instance_name,
1633
              result.stdout, result.stderr, result.fail_reason))
1634
      raise errors.HypervisorError(msg)
1635

    
1636
    return result
1637

    
1638
  def _FindFreePCISlot(self, instance_name):
1639
    slots = bitarray(32)
1640
    slots.setall(False)
1641
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1642
    for line in output.stdout.splitlines():
1643
      match = self._INFO_PCI_RE.search(line)
1644
      if match:
1645
        slot = int(match.group(1))
1646
        slots[slot] = True
1647

    
1648
    free = slots.search(FREE, 1)
1649
    if not free:
1650
      raise errors.HypervisorError("All PCI slots occupied")
1651

    
1652
    return int(free[0])
1653

    
1654
  def _HotplugEnabled(self, instance_name):
1655
    if not self._InstancePidAlive(instance_name)[2]:
1656
      logging.info("Cannot hotplug. Instance %s not alive", instance_name)
1657
      return False
1658

    
1659
    _, v_major, v_min, _ = self._GetKVMVersion()
1660
    return (v_major, v_min) >= (1, 0)
1661

    
1662
  def HotAddDisk(self, instance, disk, dev_path, _):
1663
    """Hotadd new disk to the VM
1664

1665
    """
1666
    if self._HotplugEnabled(instance.name):
1667
      disk.pci = self._FindFreePCISlot(instance.name)
1668
      idx = disk.idx
1669
      command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" %
1670
                 (dev_path, idx))
1671

    
1672
      logging.info("Run cmd %s", command)
1673
      output = self._CallMonitorCommand(instance.name, command)
1674

    
1675
      command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s,"
1676
                 "drive=drive%d,id=virtio-blk-pci.%d"
1677
                 % (hex(disk.pci), idx, idx))
1678
      logging.info("Run cmd %s", command)
1679
      output = self._CallMonitorCommand(instance.name, command)
1680
      for line in output.stdout.splitlines():
1681
        logging.info("%s", line)
1682

    
1683
      (kvm_cmd, kvm_nics,
1684
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1685
      block_devices.append((disk, dev_path))
1686
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1687
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1688

    
1689
    return disk.pci
1690

    
1691
  def HotDelDisk(self, instance, disk, _):
1692
    """Hotdel disk to the VM
1693

1694
    """
1695
    if self._HotplugEnabled(instance.name):
1696
      idx = disk.idx
1697

    
1698
      command = "device_del virtio-blk-pci.%d" % idx
1699
      logging.info("Run cmd %s", command)
1700
      output = self._CallMonitorCommand(instance.name, command)
1701
      for line in output.stdout.splitlines():
1702
        logging.info("%s", line)
1703

    
1704
      command = "drive_del drive%d" % idx
1705
      logging.info("Run cmd %s", command)
1706
      #output = self._CallMonitorCommand(instance.name, command)
1707
      #for line in output.stdout.splitlines():
1708
      #  logging.info("%s" % line)
1709

    
1710
      (kvm_cmd, kvm_nics,
1711
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1712
      rem  = [(d, p) for d, p in block_devices
1713
                     if d.idx is not None and d.idx == idx]
1714
      try:
1715
        block_devices.remove(rem[0])
1716
      except (ValueError, IndexError):
1717
        logging.info("Disk with %d idx disappeared from runtime file", idx)
1718
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1719
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1720

    
1721
  def HotAddNic(self, instance, nic, seq):
1722
    """Hotadd new nic to the VM
1723

1724
    """
1725
    if self._HotplugEnabled(instance.name):
1726
      nic.pci = self._FindFreePCISlot(instance.name)
1727
      mac = nic.mac
1728
      idx = nic.idx
1729

    
1730
      (tap, fd) = _OpenTap()
1731
      logging.info("%s %d", tap, fd)
1732

    
1733
      self._PassTapFd(instance, fd, nic)
1734

    
1735
      command = ("netdev_add tap,id=netdev%d,fd=netdev%d"
1736
                 % (idx, idx))
1737
      logging.info("Run cmd %s", command)
1738
      output = self._CallMonitorCommand(instance.name, command)
1739
      for line in output.stdout.splitlines():
1740
        logging.info("%s", line)
1741

    
1742
      command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s,"
1743
                 "netdev=netdev%d,id=virtio-net-pci.%d"
1744
                 % (hex(nic.pci), mac, idx, idx))
1745
      logging.info("Run cmd %s", command)
1746
      output = self._CallMonitorCommand(instance.name, command)
1747
      for line in output.stdout.splitlines():
1748
        logging.info("%s", line)
1749

    
1750
      self._ConfigureNIC(instance, seq, nic, tap)
1751

    
1752
      (kvm_cmd, kvm_nics,
1753
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1754
      kvm_nics.append(nic)
1755
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1756
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1757

    
1758
    return nic.pci
1759

    
1760
  def HotDelNic(self, instance, nic, _):
1761
    """Hotadd new nic to the VM
1762

1763
    """
1764
    if self._HotplugEnabled(instance.name):
1765
      idx = nic.idx
1766

    
1767
      command = "device_del virtio-net-pci.%d" % idx
1768
      logging.info("Run cmd %s", command)
1769
      output = self._CallMonitorCommand(instance.name, command)
1770
      for line in output.stdout.splitlines():
1771
        logging.info("%s", line)
1772

    
1773
      command = "netdev_del netdev%d" % idx
1774
      logging.info("Run cmd %s", command)
1775
      output = self._CallMonitorCommand(instance.name, command)
1776
      for line in output.stdout.splitlines():
1777
        logging.info("%s", line)
1778

    
1779
      (kvm_cmd, kvm_nics,
1780
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1781
      rem  = [n for n in kvm_nics if n.idx is not None and n.idx == nic.idx]
1782
      try:
1783
        kvm_nics.remove(rem[0])
1784
      except (ValueError, IndexError):
1785
        logging.info("NIC with %d idx disappeared from runtime file", nic.idx)
1786
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1787
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1788

    
1789

    
1790
  def _PassTapFd(self, instance, fd, nic):
1791
    monsock = utils.ShellQuote(self._InstanceMonitor(instance.name))
1792
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1793
    s.connect(monsock)
1794
    idx = nic.idx
1795
    command = "getfd netdev%d\n" % idx
1796
    fds = [fd]
1797
    logging.info("%s", fds)
1798
    fdsend.sendfds(s, command, fds = fds)
1799
    s.close()
1800

    
1801
  @classmethod
1802
  def _ParseKVMVersion(cls, text):
1803
    """Parse the KVM version from the --help output.
1804

1805
    @type text: string
1806
    @param text: output of kvm --help
1807
    @return: (version, v_maj, v_min, v_rev)
1808
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1809

1810
    """
1811
    match = cls._VERSION_RE.search(text.splitlines()[0])
1812
    if not match:
1813
      raise errors.HypervisorError("Unable to get KVM version")
1814

    
1815
    v_all = match.group(0)
1816
    v_maj = int(match.group(1))
1817
    v_min = int(match.group(2))
1818
    if match.group(4):
1819
      v_rev = int(match.group(4))
1820
    else:
1821
      v_rev = 0
1822
    return (v_all, v_maj, v_min, v_rev)
1823

    
1824
  @classmethod
1825
  def _GetKVMVersion(cls):
1826
    """Return the installed KVM version.
1827

1828
    @return: (version, v_maj, v_min, v_rev)
1829
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1830

1831
    """
1832
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1833
    if result.failed:
1834
      raise errors.HypervisorError("Unable to get KVM version")
1835
    return cls._ParseKVMVersion(result.output)
1836

    
1837
  def StopInstance(self, instance, force=False, retry=False, name=None):
1838
    """Stop an instance.
1839

1840
    """
1841
    if name is not None and not force:
1842
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1843
    if name is None:
1844
      name = instance.name
1845
      acpi = instance.hvparams[constants.HV_ACPI]
1846
    else:
1847
      acpi = False
1848
    _, pid, alive = self._InstancePidAlive(name)
1849
    if pid > 0 and alive:
1850
      if force or not acpi:
1851
        utils.KillProcess(pid)
1852
      else:
1853
        self._CallMonitorCommand(name, "system_powerdown")
1854

    
1855
  def CleanupInstance(self, instance_name):
1856
    """Cleanup after a stopped instance
1857

1858
    """
1859
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1860
    if pid > 0 and alive:
1861
      raise errors.HypervisorError("Cannot cleanup a live instance")
1862
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1863

    
1864
  def RebootInstance(self, instance):
1865
    """Reboot an instance.
1866

1867
    """
1868
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1869
    # socket the instance will stop, but now power up again. So we'll resort
1870
    # to shutdown and restart.
1871
    _, _, alive = self._InstancePidAlive(instance.name)
1872
    if not alive:
1873
      raise errors.HypervisorError("Failed to reboot instance %s:"
1874
                                   " not running" % instance.name)
1875
    # StopInstance will delete the saved KVM runtime so:
1876
    # ...first load it...
1877
    kvm_runtime = self._LoadKVMRuntime(instance)
1878
    # ...now we can safely call StopInstance...
1879
    if not self.StopInstance(instance):
1880
      self.StopInstance(instance, force=True)
1881
    # ...and finally we can save it again, and execute it...
1882
    self._SaveKVMRuntime(instance, kvm_runtime)
1883
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1884

    
1885
  def MigrationInfo(self, instance):
1886
    """Get instance information to perform a migration.
1887

1888
    @type instance: L{objects.Instance}
1889
    @param instance: instance to be migrated
1890
    @rtype: string
1891
    @return: content of the KVM runtime file
1892

1893
    """
1894
    return self._ReadKVMRuntime(instance.name)
1895

    
1896
  def AcceptInstance(self, instance, info, target):
1897
    """Prepare to accept an instance.
1898

1899
    @type instance: L{objects.Instance}
1900
    @param instance: instance to be accepted
1901
    @type info: string
1902
    @param info: content of the KVM runtime file on the source node
1903
    @type target: string
1904
    @param target: target host (usually ip), on this node
1905

1906
    """
1907
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1908
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1909
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1910

    
1911
  def FinalizeMigrationDst(self, instance, info, success):
1912
    """Finalize the instance migration on the target node.
1913

1914
    Stop the incoming mode KVM.
1915

1916
    @type instance: L{objects.Instance}
1917
    @param instance: instance whose migration is being finalized
1918

1919
    """
1920
    if success:
1921
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1922
      kvm_nics = kvm_runtime[1]
1923

    
1924
      for nic_seq, nic in enumerate(kvm_nics):
1925
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1926
          # Bridged interfaces have already been configured
1927
          continue
1928
        try:
1929
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1930
        except EnvironmentError, err:
1931
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1932
                          instance.name, nic_seq, str(err))
1933
          continue
1934
        try:
1935
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1936
        except errors.HypervisorError, err:
1937
          logging.warning(str(err))
1938

    
1939
      self._WriteKVMRuntime(instance.name, info)
1940
    else:
1941
      self.StopInstance(instance, force=True)
1942

    
1943
  def MigrateInstance(self, instance, target, live):
1944
    """Migrate an instance to a target node.
1945

1946
    The migration will not be attempted if the instance is not
1947
    currently running.
1948

1949
    @type instance: L{objects.Instance}
1950
    @param instance: the instance to be migrated
1951
    @type target: string
1952
    @param target: ip address of the target node
1953
    @type live: boolean
1954
    @param live: perform a live migration
1955

1956
    """
1957
    instance_name = instance.name
1958
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1959
    _, _, alive = self._InstancePidAlive(instance_name)
1960
    if not alive:
1961
      raise errors.HypervisorError("Instance not running, cannot migrate")
1962

    
1963
    if not live:
1964
      self._CallMonitorCommand(instance_name, "stop")
1965

    
1966
    migrate_command = ("migrate_set_speed %dm" %
1967
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1968
    self._CallMonitorCommand(instance_name, migrate_command)
1969

    
1970
    migrate_command = ("migrate_set_downtime %dms" %
1971
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1972
    self._CallMonitorCommand(instance_name, migrate_command)
1973

    
1974
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1975
    self._CallMonitorCommand(instance_name, migrate_command)
1976

    
1977
  def FinalizeMigrationSource(self, instance, success, live):
1978
    """Finalize the instance migration on the source node.
1979

1980
    @type instance: L{objects.Instance}
1981
    @param instance: the instance that was migrated
1982
    @type success: bool
1983
    @param success: whether the migration succeeded or not
1984
    @type live: bool
1985
    @param live: whether the user requested a live migration or not
1986

1987
    """
1988
    if success:
1989
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
1990
      utils.KillProcess(pid)
1991
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
1992
    elif live:
1993
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1994

    
1995
  def GetMigrationStatus(self, instance):
1996
    """Get the migration status
1997

1998
    @type instance: L{objects.Instance}
1999
    @param instance: the instance that is being migrated
2000
    @rtype: L{objects.MigrationStatus}
2001
    @return: the status of the current migration (one of
2002
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2003
             progress info that can be retrieved from the hypervisor
2004

2005
    """
2006
    info_command = "info migrate"
2007
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2008
      result = self._CallMonitorCommand(instance.name, info_command)
2009
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2010
      if not match:
2011
        if not result.stdout:
2012
          logging.info("KVM: empty 'info migrate' result")
2013
        else:
2014
          logging.warning("KVM: unknown 'info migrate' result: %s",
2015
                          result.stdout)
2016
      else:
2017
        status = match.group(1)
2018
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2019
          migration_status = objects.MigrationStatus(status=status)
2020
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2021
          if match:
2022
            migration_status.transferred_ram = match.group("transferred")
2023
            migration_status.total_ram = match.group("total")
2024

    
2025
          return migration_status
2026

    
2027
        logging.warning("KVM: unknown migration status '%s'", status)
2028

    
2029
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2030

    
2031
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED,
2032
                                  info="Too many 'info migrate' broken answers")
2033

    
2034
  def BalloonInstanceMemory(self, instance, mem):
2035
    """Balloon an instance memory to a certain value.
2036

2037
    @type instance: L{objects.Instance}
2038
    @param instance: instance to be accepted
2039
    @type mem: int
2040
    @param mem: actual memory size to use for instance runtime
2041

2042
    """
2043
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2044

    
2045
  def GetNodeInfo(self):
2046
    """Return information about the node.
2047

2048
    @return: a dict with the following keys (values in MiB):
2049
          - memory_total: the total memory size on the node
2050
          - memory_free: the available memory on the node for instances
2051
          - memory_dom0: the memory used by the node itself, if available
2052
          - hv_version: the hypervisor version in the form (major, minor,
2053
                        revision)
2054

2055
    """
2056
    result = self.GetLinuxNodeInfo()
2057
    _, v_major, v_min, v_rev = self._GetKVMVersion()
2058
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2059
    return result
2060

    
2061
  @classmethod
2062
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2063
    """Return a command for connecting to the console of an instance.
2064

2065
    """
2066
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2067
      cmd = [constants.KVM_CONSOLE_WRAPPER,
2068
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2069
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2070
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2071
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2072
      return objects.InstanceConsole(instance=instance.name,
2073
                                     kind=constants.CONS_SSH,
2074
                                     host=instance.primary_node,
2075
                                     user=constants.GANETI_RUNAS,
2076
                                     command=cmd)
2077

    
2078
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2079
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2080
      display = instance.network_port - constants.VNC_BASE_PORT
2081
      return objects.InstanceConsole(instance=instance.name,
2082
                                     kind=constants.CONS_VNC,
2083
                                     host=vnc_bind_address,
2084
                                     port=instance.network_port,
2085
                                     display=display)
2086

    
2087
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2088
    if spice_bind:
2089
      return objects.InstanceConsole(instance=instance.name,
2090
                                     kind=constants.CONS_SPICE,
2091
                                     host=spice_bind,
2092
                                     port=instance.network_port)
2093

    
2094
    return objects.InstanceConsole(instance=instance.name,
2095
                                   kind=constants.CONS_MESSAGE,
2096
                                   message=("No serial shell for instance %s" %
2097
                                            instance.name))
2098

    
2099
  def Verify(self):
2100
    """Verify the hypervisor.
2101

2102
    Check that the binary exists.
2103

2104
    """
2105
    if not os.path.exists(constants.KVM_PATH):
2106
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
2107
    if not os.path.exists(constants.SOCAT_PATH):
2108
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
2109

    
2110
  @classmethod
2111
  def CheckParameterSyntax(cls, hvparams):
2112
    """Check the given parameters for validity.
2113

2114
    @type hvparams:  dict
2115
    @param hvparams: dictionary with parameter names/value
2116
    @raise errors.HypervisorError: when a parameter is not valid
2117

2118
    """
2119
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2120

    
2121
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2122
    if kernel_path:
2123
      if not hvparams[constants.HV_ROOT_PATH]:
2124
        raise errors.HypervisorError("Need a root partition for the instance,"
2125
                                     " if a kernel is defined")
2126

    
2127
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2128
        not hvparams[constants.HV_VNC_X509]):
2129
      raise errors.HypervisorError("%s must be defined, if %s is" %
2130
                                   (constants.HV_VNC_X509,
2131
                                    constants.HV_VNC_X509_VERIFY))
2132

    
2133
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2134
    if (boot_order == constants.HT_BO_CDROM and
2135
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2136
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2137
                                   " ISO path")
2138

    
2139
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2140
    if security_model == constants.HT_SM_USER:
2141
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2142
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2143
                                     " must be specified")
2144
    elif (security_model == constants.HT_SM_NONE or
2145
          security_model == constants.HT_SM_POOL):
2146
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2147
        raise errors.HypervisorError("Cannot have a security domain when the"
2148
                                     " security model is 'none' or 'pool'")
2149

    
2150
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2151
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2152
    if spice_bind:
2153
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2154
        # if an IP version is specified, the spice_bind parameter must be an
2155
        # IP of that family
2156
        if (netutils.IP4Address.IsValid(spice_bind) and
2157
            spice_ip_version != constants.IP4_VERSION):
2158
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2159
                                       " the specified IP version is %s" %
2160
                                       (spice_bind, spice_ip_version))
2161

    
2162
        if (netutils.IP6Address.IsValid(spice_bind) and
2163
            spice_ip_version != constants.IP6_VERSION):
2164
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2165
                                       " the specified IP version is %s" %
2166
                                       (spice_bind, spice_ip_version))
2167
    else:
2168
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2169
      # error if any of them is set without it.
2170
      spice_additional_params = frozenset([
2171
        constants.HV_KVM_SPICE_IP_VERSION,
2172
        constants.HV_KVM_SPICE_PASSWORD_FILE,
2173
        constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
2174
        constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
2175
        constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
2176
        constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
2177
        constants.HV_KVM_SPICE_USE_TLS,
2178
        ])
2179
      for param in spice_additional_params:
2180
        if hvparams[param]:
2181
          raise errors.HypervisorError("spice: %s requires %s to be set" %
2182
                                       (param, constants.HV_KVM_SPICE_BIND))
2183

    
2184
  @classmethod
2185
  def ValidateParameters(cls, hvparams):
2186
    """Check the given parameters for validity.
2187

2188
    @type hvparams:  dict
2189
    @param hvparams: dictionary with parameter names/value
2190
    @raise errors.HypervisorError: when a parameter is not valid
2191

2192
    """
2193
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2194

    
2195
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2196
    if security_model == constants.HT_SM_USER:
2197
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2198
      try:
2199
        pwd.getpwnam(username)
2200
      except KeyError:
2201
        raise errors.HypervisorError("Unknown security domain user %s"
2202
                                     % username)
2203

    
2204
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2205
    if spice_bind:
2206
      # only one of VNC and SPICE can be used currently.
2207
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2208
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
2209
                                     " only one of them can be used at a"
2210
                                     " given time.")
2211

    
2212
      # KVM version should be >= 0.14.0
2213
      _, v_major, v_min, _ = cls._GetKVMVersion()
2214
      if (v_major, v_min) < (0, 14):
2215
        raise errors.HypervisorError("spice is configured, but it is not"
2216
                                     " available in versions of KVM < 0.14")
2217

    
2218
      # if spice_bind is not an IP address, it must be a valid interface
2219
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2220
                       or netutils.IP6Address.IsValid(spice_bind))
2221
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2222
        raise errors.HypervisorError("spice: the %s parameter must be either"
2223
                                     " a valid IP address or interface name" %
2224
                                     constants.HV_KVM_SPICE_BIND)
2225

    
2226
  @classmethod
2227
  def PowercycleNode(cls):
2228
    """KVM powercycle, just a wrapper over Linux powercycle.
2229

2230
    """
2231
    cls.LinuxPowercycle()