Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 95a74ef3

History | View | Annotate | Download (80.8 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
    def _BuildNetworkEnv(name, network, gateway, network6, gateway6,
784
                         network_type, mac_prefix, tags, env):
785
      if name:
786
        env["NETWORK_NAME"] = name
787
      if network:
788
        env["NETWORK_SUBNET"] = network
789
      if gateway:
790
        env["NETWORK_GATEWAY"] = gateway
791
      if network6:
792
        env["NETWORK_SUBNET6"] = network6
793
      if gateway6:
794
        env["NETWORK_GATEWAY6"] = gateway6
795
      if mac_prefix:
796
        env["NETWORK_MAC_PREFIX"] = mac_prefix
797
      if network_type:
798
        env["NETWORK_TYPE"] = network_type
799
      if tags:
800
        env["NETWORK_TAGS"] = " ".join(tags)
801

    
802
      return env
803

    
804

    
805
    if nic.network:
806
      n = objects.Network.FromDict(nic.netinfo)
807
      _BuildNetworkEnv(nic.network, n.network, n.gateway,
808
                       n.network6, n.gateway6, n.network_type,
809
                       n.mac_prefix, n.tags, env)
810

    
811
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
812
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
813

    
814
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
815
    if result.failed:
816
      raise errors.HypervisorError("Failed to configure interface %s: %s."
817
                                   " Network configuration script output: %s" %
818
                                   (tap, result.fail_reason, result.output))
819

    
820
  @staticmethod
821
  def _VerifyAffinityPackage():
822
    if affinity is None:
823
      raise errors.HypervisorError("affinity Python package not"
824
        " found; cannot use CPU pinning under KVM")
825

    
826
  @staticmethod
827
  def _BuildAffinityCpuMask(cpu_list):
828
    """Create a CPU mask suitable for sched_setaffinity from a list of
829
    CPUs.
830

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

834
    @type cpu_list: list of int
835
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
836
    @rtype: int
837
    @return: a bit mask of CPU affinities
838

839
    """
840
    if cpu_list == constants.CPU_PINNING_OFF:
841
      return constants.CPU_PINNING_ALL_KVM
842
    else:
843
      return sum(2 ** cpu for cpu in cpu_list)
844

    
845
  @classmethod
846
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
847
    """Change CPU affinity for running VM according to given CPU mask.
848

849
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
850
    @type cpu_mask: string
851
    @param process_id: process ID of KVM process. Used to pin entire VM
852
                       to physical CPUs.
853
    @type process_id: int
854
    @param thread_dict: map of virtual CPUs to KVM thread IDs
855
    @type thread_dict: dict int:int
856

857
    """
858
    # Convert the string CPU mask to a list of list of int's
859
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
860

    
861
    if len(cpu_list) == 1:
862
      all_cpu_mapping = cpu_list[0]
863
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
864
        # If CPU pinning has 1 entry that's "all", then do nothing
865
        pass
866
      else:
867
        # If CPU pinning has one non-all entry, map the entire VM to
868
        # one set of physical CPUs
869
        cls._VerifyAffinityPackage()
870
        affinity.set_process_affinity_mask(process_id,
871
          cls._BuildAffinityCpuMask(all_cpu_mapping))
872
    else:
873
      # The number of vCPUs mapped should match the number of vCPUs
874
      # reported by KVM. This was already verified earlier, so
875
      # here only as a sanity check.
876
      assert len(thread_dict) == len(cpu_list)
877
      cls._VerifyAffinityPackage()
878

    
879
      # For each vCPU, map it to the proper list of physical CPUs
880
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
881
        affinity.set_process_affinity_mask(thread_dict[i],
882
          cls._BuildAffinityCpuMask(vcpu))
883

    
884
  def _GetVcpuThreadIds(self, instance_name):
885
    """Get a mapping of vCPU no. to thread IDs for the instance
886

887
    @type instance_name: string
888
    @param instance_name: instance in question
889
    @rtype: dictionary of int:int
890
    @return: a dictionary mapping vCPU numbers to thread IDs
891

892
    """
893
    result = {}
894
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
895
    for line in output.stdout.splitlines():
896
      match = self._CPU_INFO_RE.search(line)
897
      if not match:
898
        continue
899
      grp = map(int, match.groups())
900
      result[grp[0]] = grp[1]
901

    
902
    return result
903

    
904
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
905
    """Complete CPU pinning.
906

907
    @type instance_name: string
908
    @param instance_name: name of instance
909
    @type cpu_mask: string
910
    @param cpu_mask: CPU pinning mask as entered by user
911

912
    """
913
    # Get KVM process ID, to be used if need to pin entire VM
914
    _, pid, _ = self._InstancePidAlive(instance_name)
915
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
916
    thread_dict = self._GetVcpuThreadIds(instance_name)
917
    # Run CPU pinning, based on configured mask
918
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
919

    
920
  def ListInstances(self):
921
    """Get the list of running instances.
922

923
    We can do this by listing our live instances directory and
924
    checking whether the associated kvm process is still alive.
925

926
    """
927
    result = []
928
    for name in os.listdir(self._PIDS_DIR):
929
      if self._InstancePidAlive(name)[2]:
930
        result.append(name)
931
    return result
932

    
933
  def GetInstanceInfo(self, instance_name):
934
    """Get instance properties.
935

936
    @type instance_name: string
937
    @param instance_name: the instance name
938
    @rtype: tuple of strings
939
    @return: (name, id, memory, vcpus, stat, times)
940

941
    """
942
    _, pid, alive = self._InstancePidAlive(instance_name)
943
    if not alive:
944
      return None
945

    
946
    _, memory, vcpus = self._InstancePidInfo(pid)
947
    istat = "---b-"
948
    times = "0"
949

    
950
    try:
951
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
952
      qmp.connect()
953
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
954
      # Will fail if ballooning is not enabled, but we can then just resort to
955
      # the value above.
956
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
957
      memory = mem_bytes / 1048576
958
    except errors.HypervisorError:
959
      pass
960

    
961
    return (instance_name, pid, memory, vcpus, istat, times)
962

    
963
  def GetAllInstancesInfo(self):
964
    """Get properties of all instances.
965

966
    @return: list of tuples (name, id, memory, vcpus, stat, times)
967

968
    """
969
    data = []
970
    for name in os.listdir(self._PIDS_DIR):
971
      try:
972
        info = self.GetInstanceInfo(name)
973
      except errors.HypervisorError:
974
        # Ignore exceptions due to instances being shut down
975
        continue
976
      if info:
977
        data.append(info)
978
    return data
979

    
980
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_cmd, block_devices):
981

    
982
    hvp = instance.hvparams
983
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
984

    
985
    _, v_major, v_min, _ = self._GetKVMVersion()
986

    
987
    # whether this is an older KVM version that uses the boot=on flag
988
    # on devices
989
    needs_boot_flag = (v_major, v_min) < (0, 14)
990

    
991
    disk_type = hvp[constants.HV_DISK_TYPE]
992
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
993
      if_val = ",if=virtio"
994
      if (v_major, v_min) >= (0, 12):
995
        disk_model = "virtio-blk-pci"
996
      else:
997
        disk_model = "virtio"
998
    else:
999
      if_val = ",if=%s" % disk_type
1000
      disk_model = disk_type
1001
    # Cache mode
1002
    disk_cache = hvp[constants.HV_DISK_CACHE]
1003
    if instance.disk_template in constants.DTS_EXT_MIRROR:
1004
      if disk_cache != "none":
1005
        # TODO: make this a hard error, instead of a silent overwrite
1006
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1007
                        " to prevent shared storage corruption on migration",
1008
                        disk_cache)
1009
      cache_val = ",cache=none"
1010
    elif disk_cache != constants.HT_CACHE_DEFAULT:
1011
      cache_val = ",cache=%s" % disk_cache
1012
    else:
1013
      cache_val = ""
1014
    for cfdev, dev_path in block_devices:
1015
      if cfdev.mode != constants.DISK_RDWR:
1016
        raise errors.HypervisorError("Instance has read-only disks which"
1017
                                     " are not supported by KVM")
1018
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
1019
      boot_val = ""
1020
      if boot_disk:
1021
        kvm_cmd.extend(["-boot", "c"])
1022
        boot_disk = False
1023
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1024
          boot_val = ",boot=on"
1025
      drive_val = "file=%s,format=raw%s%s" % \
1026
                  (dev_path, boot_val, cache_val)
1027
      if cfdev.idx is not None:
1028
        #TODO: name id after model
1029
        drive_val += (",if=none,id=drive%d" % cfdev.idx)
1030
        if cfdev.pci is not None:
1031
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1032
      else:
1033
        drive_val += if_val
1034

    
1035
      kvm_cmd.extend(["-drive", drive_val])
1036

    
1037
      if cfdev.idx is not None:
1038
        dev_val = ("%s,drive=drive%d,id=virtio-blk-pci.%d" %
1039
                    (disk_model, cfdev.idx, cfdev.idx))
1040
        if cfdev.pci is not None:
1041
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1042
        kvm_cmd.extend(["-device", dev_val])
1043

    
1044
    return kvm_cmd
1045

    
1046
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
1047
    """Generate KVM information to start an instance.
1048

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

1056
    """
1057
    # pylint: disable=R0914,R0915
1058
    _, v_major, v_min, _ = self._GetKVMVersion()
1059

    
1060
    pidfile = self._InstancePidFile(instance.name)
1061
    kvm = constants.KVM_PATH
1062
    kvm_cmd = [kvm]
1063
    # used just by the vnc server, if enabled
1064
    kvm_cmd.extend(["-name", instance.name])
1065
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1066
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
1067
    kvm_cmd.extend(["-pidfile", pidfile])
1068
    kvm_cmd.extend(["-balloon", "virtio"])
1069
    kvm_cmd.extend(["-daemonize"])
1070
    if not instance.hvparams[constants.HV_ACPI]:
1071
      kvm_cmd.extend(["-no-acpi"])
1072
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1073
        constants.INSTANCE_REBOOT_EXIT:
1074
      kvm_cmd.extend(["-no-reboot"])
1075

    
1076
    hvp = instance.hvparams
1077
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1078
    if kernel_path:
1079
      boot_cdrom = boot_floppy = boot_network = False
1080
    else:
1081
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1082
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1083
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1084

    
1085
    self.ValidateParameters(hvp)
1086

    
1087
    if startup_paused:
1088
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1089

    
1090
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1091
      kvm_cmd.extend(["-enable-kvm"])
1092
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1093
      kvm_cmd.extend(["-disable-kvm"])
1094

    
1095
    if boot_network:
1096
      kvm_cmd.extend(["-boot", "n"])
1097

    
1098
    # whether this is an older KVM version that uses the boot=on flag
1099
    # on devices
1100
    needs_boot_flag = (v_major, v_min) < (0, 14)
1101

    
1102
    disk_type = hvp[constants.HV_DISK_TYPE]
1103
    if not instance.hotplug_info:
1104
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1105
                                                     block_devices)
1106

    
1107
    #Now we can specify a different device type for CDROM devices.
1108
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1109
    if not cdrom_disk_type:
1110
      cdrom_disk_type = disk_type
1111

    
1112
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1113
    if iso_image:
1114
      options = ",format=raw,media=cdrom"
1115
      # set cdrom 'if' type
1116
      if boot_cdrom:
1117
        actual_cdrom_type = constants.HT_DISK_IDE
1118
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1119
        actual_cdrom_type = "virtio"
1120
      else:
1121
        actual_cdrom_type = cdrom_disk_type
1122
      if_val = ",if=%s" % actual_cdrom_type
1123
      # set boot flag, if needed
1124
      boot_val = ""
1125
      if boot_cdrom:
1126
        kvm_cmd.extend(["-boot", "d"])
1127
        if needs_boot_flag:
1128
          boot_val = ",boot=on"
1129
      # and finally build the entire '-drive' value
1130
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1131
      kvm_cmd.extend(["-drive", drive_val])
1132

    
1133
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1134
    if iso_image2:
1135
      options = ",format=raw,media=cdrom"
1136
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1137
        if_val = ",if=virtio"
1138
      else:
1139
        if_val = ",if=%s" % cdrom_disk_type
1140
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1141
      kvm_cmd.extend(["-drive", drive_val])
1142

    
1143
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1144
    if floppy_image:
1145
      options = ",format=raw,media=disk"
1146
      if boot_floppy:
1147
        kvm_cmd.extend(["-boot", "a"])
1148
        options = "%s,boot=on" % options
1149
      if_val = ",if=floppy"
1150
      options = "%s%s" % (options, if_val)
1151
      drive_val = "file=%s%s" % (floppy_image, options)
1152
      kvm_cmd.extend(["-drive", drive_val])
1153

    
1154
    if kernel_path:
1155
      kvm_cmd.extend(["-kernel", kernel_path])
1156
      initrd_path = hvp[constants.HV_INITRD_PATH]
1157
      if initrd_path:
1158
        kvm_cmd.extend(["-initrd", initrd_path])
1159
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1160
                     hvp[constants.HV_KERNEL_ARGS]]
1161
      if hvp[constants.HV_SERIAL_CONSOLE]:
1162
        root_append.append("console=ttyS0,38400")
1163
      kvm_cmd.extend(["-append", " ".join(root_append)])
1164

    
1165
    mem_path = hvp[constants.HV_MEM_PATH]
1166
    if mem_path:
1167
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1168

    
1169
    monitor_dev = ("unix:%s,server,nowait" %
1170
                   self._InstanceMonitor(instance.name))
1171
    kvm_cmd.extend(["-monitor", monitor_dev])
1172
    if hvp[constants.HV_SERIAL_CONSOLE]:
1173
      serial_dev = ("unix:%s,server,nowait" %
1174
                    self._InstanceSerial(instance.name))
1175
      kvm_cmd.extend(["-serial", serial_dev])
1176
    else:
1177
      kvm_cmd.extend(["-serial", "none"])
1178

    
1179
    mouse_type = hvp[constants.HV_USB_MOUSE]
1180
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1181
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1182
    spice_ip_version = None
1183

    
1184
    if mouse_type:
1185
      kvm_cmd.extend(["-usb"])
1186
      kvm_cmd.extend(["-usbdevice", mouse_type])
1187
    elif vnc_bind_address:
1188
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1189

    
1190
    if vnc_bind_address:
1191
      if netutils.IP4Address.IsValid(vnc_bind_address):
1192
        if instance.network_port > constants.VNC_BASE_PORT:
1193
          display = instance.network_port - constants.VNC_BASE_PORT
1194
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1195
            vnc_arg = ":%d" % (display)
1196
          else:
1197
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1198
        else:
1199
          logging.error("Network port is not a valid VNC display (%d < %d)."
1200
                        " Not starting VNC", instance.network_port,
1201
                        constants.VNC_BASE_PORT)
1202
          vnc_arg = "none"
1203

    
1204
        # Only allow tls and other option when not binding to a file, for now.
1205
        # kvm/qemu gets confused otherwise about the filename to use.
1206
        vnc_append = ""
1207
        if hvp[constants.HV_VNC_TLS]:
1208
          vnc_append = "%s,tls" % vnc_append
1209
          if hvp[constants.HV_VNC_X509_VERIFY]:
1210
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1211
                                               hvp[constants.HV_VNC_X509])
1212
          elif hvp[constants.HV_VNC_X509]:
1213
            vnc_append = "%s,x509=%s" % (vnc_append,
1214
                                         hvp[constants.HV_VNC_X509])
1215
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1216
          vnc_append = "%s,password" % vnc_append
1217

    
1218
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1219

    
1220
      else:
1221
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1222

    
1223
      kvm_cmd.extend(["-vnc", vnc_arg])
1224
    elif spice_bind:
1225
      # FIXME: this is wrong here; the iface ip address differs
1226
      # between systems, so it should be done in _ExecuteKVMRuntime
1227
      if netutils.IsValidInterface(spice_bind):
1228
        # The user specified a network interface, we have to figure out the IP
1229
        # address.
1230
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1231
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1232

    
1233
        # if the user specified an IP version and the interface does not
1234
        # have that kind of IP addresses, throw an exception
1235
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1236
          if not addresses[spice_ip_version]:
1237
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
1238
                                         " for %s" % (spice_ip_version,
1239
                                                      spice_bind))
1240

    
1241
        # the user did not specify an IP version, we have to figure it out
1242
        elif (addresses[constants.IP4_VERSION] and
1243
              addresses[constants.IP6_VERSION]):
1244
          # we have both ipv4 and ipv6, let's use the cluster default IP
1245
          # version
1246
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1247
          spice_ip_version = \
1248
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1249
        elif addresses[constants.IP4_VERSION]:
1250
          spice_ip_version = constants.IP4_VERSION
1251
        elif addresses[constants.IP6_VERSION]:
1252
          spice_ip_version = constants.IP6_VERSION
1253
        else:
1254
          raise errors.HypervisorError("spice: unable to get an IP address"
1255
                                       " for %s" % (spice_bind))
1256

    
1257
        spice_address = addresses[spice_ip_version][0]
1258

    
1259
      else:
1260
        # spice_bind is known to be a valid IP address, because
1261
        # ValidateParameters checked it.
1262
        spice_address = spice_bind
1263

    
1264
      spice_arg = "addr=%s" % spice_address
1265
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1266
        spice_arg = "%s,tls-port=%s,x509-cacert-file=%s" % (spice_arg,
1267
            instance.network_port, constants.SPICE_CACERT_FILE)
1268
        spice_arg = "%s,x509-key-file=%s,x509-cert-file=%s" % (spice_arg,
1269
            constants.SPICE_CERT_FILE, constants.SPICE_CERT_FILE)
1270
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1271
        if tls_ciphers:
1272
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1273
      else:
1274
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1275

    
1276
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1277
        spice_arg = "%s,disable-ticketing" % spice_arg
1278

    
1279
      if spice_ip_version:
1280
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1281

    
1282
      # Image compression options
1283
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1284
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1285
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1286
      if img_lossless:
1287
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1288
      if img_jpeg:
1289
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1290
      if img_zlib_glz:
1291
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1292

    
1293
      # Video stream detection
1294
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1295
      if video_streaming:
1296
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1297

    
1298
      # Audio compression, by default in qemu-kvm it is on
1299
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1300
        spice_arg = "%s,playback-compression=off" % spice_arg
1301
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1302
        spice_arg = "%s,agent-mouse=off" % spice_arg
1303
      else:
1304
        # Enable the spice agent communication channel between the host and the
1305
        # agent.
1306
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1307
        kvm_cmd.extend(["-device", "virtserialport,chardev=spicechannel0,"
1308
                                                   "name=com.redhat.spice.0"])
1309
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1310

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

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

    
1317
    else:
1318
      kvm_cmd.extend(["-nographic"])
1319

    
1320
    if hvp[constants.HV_USE_LOCALTIME]:
1321
      kvm_cmd.extend(["-localtime"])
1322

    
1323
    if hvp[constants.HV_KVM_USE_CHROOT]:
1324
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1325

    
1326
    # Save the current instance nics, but defer their expansion as parameters,
1327
    # as we'll need to generate executable temp files for them.
1328
    kvm_nics = instance.nics
1329
    hvparams = hvp
1330

    
1331
    if instance.hotplug_info:
1332
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1333
    else:
1334
      return (kvm_cmd, kvm_nics, hvparams)
1335

    
1336
  def _WriteKVMRuntime(self, instance_name, data):
1337
    """Write an instance's KVM runtime
1338

1339
    """
1340
    try:
1341
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1342
                      data=data)
1343
    except EnvironmentError, err:
1344
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1345

    
1346
  def _ReadKVMRuntime(self, instance_name):
1347
    """Read an instance's KVM runtime
1348

1349
    """
1350
    try:
1351
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1352
    except EnvironmentError, err:
1353
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1354
    return file_content
1355

    
1356
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1357
    """Save an instance's KVM runtime
1358

1359
    """
1360
    if instance.hotplug_info:
1361
      kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime
1362
      serialized_blockdevs = [(blk.ToDict(), link)
1363
                              for blk,link in block_devices]
1364
    else:
1365
      kvm_cmd, kvm_nics, hvparams = kvm_runtime
1366

    
1367
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1368

    
1369
    if instance.hotplug_info:
1370
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics,
1371
                                        hvparams, serialized_blockdevs))
1372
    else:
1373
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1374

    
1375
    self._WriteKVMRuntime(instance.name, serialized_form)
1376

    
1377
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1378
    """Load an instance's KVM runtime
1379

1380
    """
1381
    if not serialized_runtime:
1382
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1383
    loaded_runtime = serializer.Load(serialized_runtime)
1384
    if instance.hotplug_info:
1385
      kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime
1386
      block_devices = [(objects.Disk.FromDict(sdisk), link)
1387
                       for sdisk, link in serialized_blockdevs]
1388
    else:
1389
      kvm_cmd, serialized_nics, hvparams = loaded_runtime
1390

    
1391
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1392

    
1393
    if instance.hotplug_info:
1394
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1395
    else:
1396
      return (kvm_cmd, kvm_nics, hvparams)
1397

    
1398
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1399
    """Run the KVM cmd and check for errors
1400

1401
    @type name: string
1402
    @param name: instance name
1403
    @type kvm_cmd: list of strings
1404
    @param kvm_cmd: runcmd input for kvm
1405
    @type tap_fds: list of int
1406
    @param tap_fds: fds of tap devices opened by Ganeti
1407

1408
    """
1409
    try:
1410
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1411
    finally:
1412
      for fd in tap_fds:
1413
        utils_wrapper.CloseFdNoError(fd)
1414

    
1415
    if result.failed:
1416
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1417
                                   (name, result.fail_reason, result.output))
1418
    if not self._InstancePidAlive(name)[2]:
1419
      raise errors.HypervisorError("Failed to start instance %s" % name)
1420

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

1424
    @type incoming: tuple of strings
1425
    @param incoming: (target_host_ip, port)
1426

1427
    """
1428
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1429
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1430
    #    have changed since the instance started; only use them if the change
1431
    #    won't affect the inside of the instance (which hasn't been rebooted).
1432
    #  - up_hvp contains the parameters as they were when the instance was
1433
    #    started, plus any new parameter which has been added between ganeti
1434
    #    versions: it is paramount that those default to a value which won't
1435
    #    affect the inside of the instance as well.
1436
    conf_hvp = instance.hvparams
1437
    name = instance.name
1438
    self._CheckDown(name)
1439

    
1440
    temp_files = []
1441

    
1442
    if instance.hotplug_info:
1443
      kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
1444
    else:
1445
      kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1446

    
1447
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1448

    
1449
    _, v_major, v_min, _ = self._GetKVMVersion()
1450

    
1451
    # We know it's safe to run as a different user upon migration, so we'll use
1452
    # the latest conf, from conf_hvp.
1453
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1454
    if security_model == constants.HT_SM_USER:
1455
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1456

    
1457
    keymap = conf_hvp[constants.HV_KEYMAP]
1458
    if keymap:
1459
      keymap_path = self._InstanceKeymapFile(name)
1460
      # If a keymap file is specified, KVM won't use its internal defaults. By
1461
      # first including the "en-us" layout, an error on loading the actual
1462
      # layout (e.g. because it can't be found) won't lead to a non-functional
1463
      # keyboard. A keyboard with incorrect keys is still better than none.
1464
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1465
      kvm_cmd.extend(["-k", keymap_path])
1466

    
1467
    if instance.hotplug_info:
1468
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1469
                                                     block_devices)
1470

    
1471
    # We have reasons to believe changing something like the nic driver/type
1472
    # upon migration won't exactly fly with the instance kernel, so for nic
1473
    # related parameters we'll use up_hvp
1474
    tapfds = []
1475
    taps = []
1476
    if not kvm_nics:
1477
      kvm_cmd.extend(["-net", "none"])
1478
    else:
1479
      vnet_hdr = False
1480
      tap_extra = ""
1481
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1482
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1483
        # From version 0.12.0, kvm uses a new sintax for network configuration.
1484
        if (v_major, v_min) >= (0, 12):
1485
          nic_model = "virtio-net-pci"
1486
          vnet_hdr = True
1487
        else:
1488
          nic_model = "virtio"
1489

    
1490
        if up_hvp[constants.HV_VHOST_NET]:
1491
          # vhost_net is only available from version 0.13.0 or newer
1492
          if (v_major, v_min) >= (0, 13):
1493
            tap_extra = ",vhost=on"
1494
          else:
1495
            raise errors.HypervisorError("vhost_net is configured"
1496
                                        " but it is not available")
1497
      else:
1498
        nic_model = nic_type
1499

    
1500
      for nic_seq, nic in enumerate(kvm_nics):
1501
        tapname, tapfd = _OpenTap(vnet_hdr)
1502
        tapfds.append(tapfd)
1503
        taps.append(tapname)
1504
        if (v_major, v_min) >= (0, 12):
1505
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1506
          if nic.idx is not None:
1507
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1508
                        (nic.idx, nic.idx))
1509
            if nic.pci is not None:
1510
              nic_val += (",bus=pci.0,addr=%s" % hex(nic.pci))
1511
          else:
1512
            nic_val += (",netdev=netdev%d,id=virtio-net-pci.%d" %
1513
                        (nic_seq, nic_seq))
1514
          tap_val = ("type=tap,id=netdev%d,fd=%d%s" %
1515
                     (nic.idx or nic_seq, tapfd, tap_extra))
1516
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1517
        else:
1518
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1519
                                                         nic.mac, nic_model)
1520
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1521
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1522

    
1523
    if incoming:
1524
      target, port = incoming
1525
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1526

    
1527
    # Changing the vnc password doesn't bother the guest that much. At most it
1528
    # will surprise people who connect to it. Whether positively or negatively
1529
    # it's debatable.
1530
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1531
    vnc_pwd = None
1532
    if vnc_pwd_file:
1533
      try:
1534
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1535
      except EnvironmentError, err:
1536
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1537
                                     % (vnc_pwd_file, err))
1538

    
1539
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1540
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1541
                         constants.SECURE_DIR_MODE)])
1542

    
1543
    # Automatically enable QMP if version is >= 0.14
1544
    if (v_major, v_min) >= (0, 14):
1545
      logging.debug("Enabling QMP")
1546
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1547
                    self._InstanceQmpMonitor(instance.name)])
1548

    
1549
    # Configure the network now for starting instances and bridged interfaces,
1550
    # during FinalizeMigration for incoming instances' routed interfaces
1551
    for nic_seq, nic in enumerate(kvm_nics):
1552
      if (incoming and
1553
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1554
        continue
1555
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1556

    
1557
    # CPU affinity requires kvm to start paused, so we set this flag if the
1558
    # instance is not already paused and if we are not going to accept a
1559
    # migrating instance. In the latter case, pausing is not needed.
1560
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1561
    if start_kvm_paused:
1562
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1563

    
1564
    # Note: CPU pinning is using up_hvp since changes take effect
1565
    # during instance startup anyway, and to avoid problems when soft
1566
    # rebooting the instance.
1567
    cpu_pinning = False
1568
    if up_hvp.get(constants.HV_CPU_MASK, None):
1569
      cpu_pinning = True
1570

    
1571
    if security_model == constants.HT_SM_POOL:
1572
      ss = ssconf.SimpleStore()
1573
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1574
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1575
      uid = uidpool.RequestUnusedUid(all_uids)
1576
      try:
1577
        username = pwd.getpwuid(uid.GetUid()).pw_name
1578
        kvm_cmd.extend(["-runas", username])
1579
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1580
      except:
1581
        uidpool.ReleaseUid(uid)
1582
        raise
1583
      else:
1584
        uid.Unlock()
1585
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1586
    else:
1587
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1588

    
1589
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1590
                     constants.RUN_DIRS_MODE)])
1591
    for nic_seq, tap in enumerate(taps):
1592
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1593
                      data=tap)
1594

    
1595
    if vnc_pwd:
1596
      change_cmd = "change vnc password %s" % vnc_pwd
1597
      self._CallMonitorCommand(instance.name, change_cmd)
1598

    
1599
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1600
    # connection attempts because SPICE by default does not allow connections
1601
    # if neither a password nor the "disable_ticketing" options are specified.
1602
    # As soon as we send the password via QMP, that password is a valid ticket
1603
    # for connection.
1604
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1605
    if spice_password_file:
1606
      spice_pwd = ""
1607
      try:
1608
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1609
      except EnvironmentError, err:
1610
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1611
                                     % (spice_password_file, err))
1612

    
1613
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1614
      qmp.connect()
1615
      arguments = {
1616
          "protocol": "spice",
1617
          "password": spice_pwd,
1618
      }
1619
      qmp.Execute("set_password", arguments)
1620

    
1621
    for filename in temp_files:
1622
      utils.RemoveFile(filename)
1623

    
1624
    # If requested, set CPU affinity and resume instance execution
1625
    if cpu_pinning:
1626
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1627

    
1628
    start_memory = self._InstanceStartupMemory(instance)
1629
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1630
      self.BalloonInstanceMemory(instance, start_memory)
1631

    
1632
    if start_kvm_paused:
1633
      # To control CPU pinning, ballooning, and vnc/spice passwords
1634
      # the VM was started in a frozen state. If freezing was not
1635
      # explicitly requested resume the vm status.
1636
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1637

    
1638
  def StartInstance(self, instance, block_devices, startup_paused):
1639
    """Start an instance.
1640

1641
    """
1642
    self._CheckDown(instance.name)
1643
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1644
                                           startup_paused)
1645
    self._SaveKVMRuntime(instance, kvm_runtime)
1646
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1647

    
1648
  def _CallMonitorCommand(self, instance_name, command):
1649
    """Invoke a command on the instance monitor.
1650

1651
    """
1652
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1653
             (utils.ShellQuote(command),
1654
              constants.SOCAT_PATH,
1655
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1656
    result = utils.RunCmd(socat)
1657
    if result.failed:
1658
      msg = ("Failed to send command '%s' to instance %s."
1659
             " output: %s, error: %s, fail_reason: %s" %
1660
             (command, instance_name,
1661
              result.stdout, result.stderr, result.fail_reason))
1662
      raise errors.HypervisorError(msg)
1663

    
1664
    return result
1665

    
1666
  def _FindFreePCISlot(self, instance_name):
1667
    slots = bitarray(32)
1668
    slots.setall(False)
1669
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1670
    for line in output.stdout.splitlines():
1671
      match = self._INFO_PCI_RE.search(line)
1672
      if match:
1673
        slot = int(match.group(1))
1674
        slots[slot] = True
1675

    
1676
    free = slots.search(FREE, 1)
1677
    if not free:
1678
      raise errors.HypervisorError("All PCI slots occupied")
1679

    
1680
    return int(free[0])
1681

    
1682
  def _HotplugEnabled(self, instance_name):
1683
    if not self._InstancePidAlive(instance_name)[2]:
1684
      logging.info("Cannot hotplug. Instance %s not alive", instance_name)
1685
      return False
1686

    
1687
    _, v_major, v_min, _ = self._GetKVMVersion()
1688
    return (v_major, v_min) >= (1, 0)
1689

    
1690
  def HotAddDisk(self, instance, disk, dev_path, _):
1691
    """Hotadd new disk to the VM
1692

1693
    """
1694
    if self._HotplugEnabled(instance.name):
1695
      disk.pci = self._FindFreePCISlot(instance.name)
1696
      idx = disk.idx
1697
      command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" %
1698
                 (dev_path, idx))
1699

    
1700
      logging.info("Run cmd %s", command)
1701
      output = self._CallMonitorCommand(instance.name, command)
1702

    
1703
      command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s,"
1704
                 "drive=drive%d,id=virtio-blk-pci.%d"
1705
                 % (hex(disk.pci), idx, idx))
1706
      logging.info("Run cmd %s", command)
1707
      output = self._CallMonitorCommand(instance.name, command)
1708
      for line in output.stdout.splitlines():
1709
        logging.info("%s", line)
1710

    
1711
      (kvm_cmd, kvm_nics,
1712
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1713
      block_devices.append((disk, dev_path))
1714
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1715
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1716

    
1717
    return disk.pci
1718

    
1719
  def HotDelDisk(self, instance, disk, _):
1720
    """Hotdel disk to the VM
1721

1722
    """
1723
    if self._HotplugEnabled(instance.name):
1724
      idx = disk.idx
1725

    
1726
      command = "device_del virtio-blk-pci.%d" % idx
1727
      logging.info("Run cmd %s", command)
1728
      output = self._CallMonitorCommand(instance.name, command)
1729
      for line in output.stdout.splitlines():
1730
        logging.info("%s", line)
1731

    
1732
      command = "drive_del drive%d" % idx
1733
      logging.info("Run cmd %s", command)
1734
      #output = self._CallMonitorCommand(instance.name, command)
1735
      #for line in output.stdout.splitlines():
1736
      #  logging.info("%s" % line)
1737

    
1738
      (kvm_cmd, kvm_nics,
1739
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1740
      rem  = [(d, p) for d, p in block_devices
1741
                     if d.idx is not None and d.idx == idx]
1742
      try:
1743
        block_devices.remove(rem[0])
1744
      except (ValueError, IndexError):
1745
        logging.info("Disk with %d idx disappeared from runtime file", idx)
1746
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1747
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1748

    
1749
  def HotAddNic(self, instance, nic, seq):
1750
    """Hotadd new nic to the VM
1751

1752
    """
1753
    if self._HotplugEnabled(instance.name):
1754
      nic.pci = self._FindFreePCISlot(instance.name)
1755
      mac = nic.mac
1756
      idx = nic.idx
1757

    
1758
      (tap, fd) = _OpenTap()
1759
      logging.info("%s %d", tap, fd)
1760

    
1761
      self._PassTapFd(instance, fd, nic)
1762

    
1763
      command = ("netdev_add tap,id=netdev%d,fd=netdev%d"
1764
                 % (idx, idx))
1765
      logging.info("Run cmd %s", command)
1766
      output = self._CallMonitorCommand(instance.name, command)
1767
      for line in output.stdout.splitlines():
1768
        logging.info("%s", line)
1769

    
1770
      command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s,"
1771
                 "netdev=netdev%d,id=virtio-net-pci.%d"
1772
                 % (hex(nic.pci), mac, idx, idx))
1773
      logging.info("Run cmd %s", command)
1774
      output = self._CallMonitorCommand(instance.name, command)
1775
      for line in output.stdout.splitlines():
1776
        logging.info("%s", line)
1777

    
1778
      self._ConfigureNIC(instance, seq, nic, tap)
1779

    
1780
      (kvm_cmd, kvm_nics,
1781
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1782
      kvm_nics.append(nic)
1783
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1784
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1785

    
1786
    return nic.pci
1787

    
1788
  def HotDelNic(self, instance, nic, _):
1789
    """Hotadd new nic to the VM
1790

1791
    """
1792
    if self._HotplugEnabled(instance.name):
1793
      idx = nic.idx
1794

    
1795
      command = "device_del virtio-net-pci.%d" % idx
1796
      logging.info("Run cmd %s", command)
1797
      output = self._CallMonitorCommand(instance.name, command)
1798
      for line in output.stdout.splitlines():
1799
        logging.info("%s", line)
1800

    
1801
      command = "netdev_del netdev%d" % idx
1802
      logging.info("Run cmd %s", command)
1803
      output = self._CallMonitorCommand(instance.name, command)
1804
      for line in output.stdout.splitlines():
1805
        logging.info("%s", line)
1806

    
1807
      (kvm_cmd, kvm_nics,
1808
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1809
      rem  = [n for n in kvm_nics if n.idx is not None and n.idx == nic.idx]
1810
      try:
1811
        kvm_nics.remove(rem[0])
1812
      except (ValueError, IndexError):
1813
        logging.info("NIC with %d idx disappeared from runtime file", nic.idx)
1814
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1815
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1816

    
1817

    
1818
  def _PassTapFd(self, instance, fd, nic):
1819
    monsock = utils.ShellQuote(self._InstanceMonitor(instance.name))
1820
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1821
    s.connect(monsock)
1822
    idx = nic.idx
1823
    command = "getfd netdev%d\n" % idx
1824
    fds = [fd]
1825
    logging.info("%s", fds)
1826
    fdsend.sendfds(s, command, fds = fds)
1827
    s.close()
1828

    
1829
  @classmethod
1830
  def _ParseKVMVersion(cls, text):
1831
    """Parse the KVM version from the --help output.
1832

1833
    @type text: string
1834
    @param text: output of kvm --help
1835
    @return: (version, v_maj, v_min, v_rev)
1836
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1837

1838
    """
1839
    match = cls._VERSION_RE.search(text.splitlines()[0])
1840
    if not match:
1841
      raise errors.HypervisorError("Unable to get KVM version")
1842

    
1843
    v_all = match.group(0)
1844
    v_maj = int(match.group(1))
1845
    v_min = int(match.group(2))
1846
    if match.group(4):
1847
      v_rev = int(match.group(4))
1848
    else:
1849
      v_rev = 0
1850
    return (v_all, v_maj, v_min, v_rev)
1851

    
1852
  @classmethod
1853
  def _GetKVMVersion(cls):
1854
    """Return the installed KVM version.
1855

1856
    @return: (version, v_maj, v_min, v_rev)
1857
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1858

1859
    """
1860
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1861
    if result.failed:
1862
      raise errors.HypervisorError("Unable to get KVM version")
1863
    return cls._ParseKVMVersion(result.output)
1864

    
1865
  def StopInstance(self, instance, force=False, retry=False, name=None):
1866
    """Stop an instance.
1867

1868
    """
1869
    if name is not None and not force:
1870
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1871
    if name is None:
1872
      name = instance.name
1873
      acpi = instance.hvparams[constants.HV_ACPI]
1874
    else:
1875
      acpi = False
1876
    _, pid, alive = self._InstancePidAlive(name)
1877
    if pid > 0 and alive:
1878
      if force or not acpi:
1879
        utils.KillProcess(pid)
1880
      else:
1881
        self._CallMonitorCommand(name, "system_powerdown")
1882

    
1883
  def CleanupInstance(self, instance_name):
1884
    """Cleanup after a stopped instance
1885

1886
    """
1887
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1888
    if pid > 0 and alive:
1889
      raise errors.HypervisorError("Cannot cleanup a live instance")
1890
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1891

    
1892
  def RebootInstance(self, instance):
1893
    """Reboot an instance.
1894

1895
    """
1896
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1897
    # socket the instance will stop, but now power up again. So we'll resort
1898
    # to shutdown and restart.
1899
    _, _, alive = self._InstancePidAlive(instance.name)
1900
    if not alive:
1901
      raise errors.HypervisorError("Failed to reboot instance %s:"
1902
                                   " not running" % instance.name)
1903
    # StopInstance will delete the saved KVM runtime so:
1904
    # ...first load it...
1905
    kvm_runtime = self._LoadKVMRuntime(instance)
1906
    # ...now we can safely call StopInstance...
1907
    if not self.StopInstance(instance):
1908
      self.StopInstance(instance, force=True)
1909
    # ...and finally we can save it again, and execute it...
1910
    self._SaveKVMRuntime(instance, kvm_runtime)
1911
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1912

    
1913
  def MigrationInfo(self, instance):
1914
    """Get instance information to perform a migration.
1915

1916
    @type instance: L{objects.Instance}
1917
    @param instance: instance to be migrated
1918
    @rtype: string
1919
    @return: content of the KVM runtime file
1920

1921
    """
1922
    return self._ReadKVMRuntime(instance.name)
1923

    
1924
  def AcceptInstance(self, instance, info, target):
1925
    """Prepare to accept an instance.
1926

1927
    @type instance: L{objects.Instance}
1928
    @param instance: instance to be accepted
1929
    @type info: string
1930
    @param info: content of the KVM runtime file on the source node
1931
    @type target: string
1932
    @param target: target host (usually ip), on this node
1933

1934
    """
1935
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1936
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1937
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1938

    
1939
  def FinalizeMigrationDst(self, instance, info, success):
1940
    """Finalize the instance migration on the target node.
1941

1942
    Stop the incoming mode KVM.
1943

1944
    @type instance: L{objects.Instance}
1945
    @param instance: instance whose migration is being finalized
1946

1947
    """
1948
    if success:
1949
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1950
      kvm_nics = kvm_runtime[1]
1951

    
1952
      for nic_seq, nic in enumerate(kvm_nics):
1953
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1954
          # Bridged interfaces have already been configured
1955
          continue
1956
        try:
1957
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1958
        except EnvironmentError, err:
1959
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1960
                          instance.name, nic_seq, str(err))
1961
          continue
1962
        try:
1963
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1964
        except errors.HypervisorError, err:
1965
          logging.warning(str(err))
1966

    
1967
      self._WriteKVMRuntime(instance.name, info)
1968
    else:
1969
      self.StopInstance(instance, force=True)
1970

    
1971
  def MigrateInstance(self, instance, target, live):
1972
    """Migrate an instance to a target node.
1973

1974
    The migration will not be attempted if the instance is not
1975
    currently running.
1976

1977
    @type instance: L{objects.Instance}
1978
    @param instance: the instance to be migrated
1979
    @type target: string
1980
    @param target: ip address of the target node
1981
    @type live: boolean
1982
    @param live: perform a live migration
1983

1984
    """
1985
    instance_name = instance.name
1986
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1987
    _, _, alive = self._InstancePidAlive(instance_name)
1988
    if not alive:
1989
      raise errors.HypervisorError("Instance not running, cannot migrate")
1990

    
1991
    if not live:
1992
      self._CallMonitorCommand(instance_name, "stop")
1993

    
1994
    migrate_command = ("migrate_set_speed %dm" %
1995
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1996
    self._CallMonitorCommand(instance_name, migrate_command)
1997

    
1998
    migrate_command = ("migrate_set_downtime %dms" %
1999
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2000
    self._CallMonitorCommand(instance_name, migrate_command)
2001

    
2002
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2003
    self._CallMonitorCommand(instance_name, migrate_command)
2004

    
2005
  def FinalizeMigrationSource(self, instance, success, live):
2006
    """Finalize the instance migration on the source node.
2007

2008
    @type instance: L{objects.Instance}
2009
    @param instance: the instance that was migrated
2010
    @type success: bool
2011
    @param success: whether the migration succeeded or not
2012
    @type live: bool
2013
    @param live: whether the user requested a live migration or not
2014

2015
    """
2016
    if success:
2017
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2018
      utils.KillProcess(pid)
2019
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2020
    elif live:
2021
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2022

    
2023
  def GetMigrationStatus(self, instance):
2024
    """Get the migration status
2025

2026
    @type instance: L{objects.Instance}
2027
    @param instance: the instance that is being migrated
2028
    @rtype: L{objects.MigrationStatus}
2029
    @return: the status of the current migration (one of
2030
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2031
             progress info that can be retrieved from the hypervisor
2032

2033
    """
2034
    info_command = "info migrate"
2035
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2036
      result = self._CallMonitorCommand(instance.name, info_command)
2037
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2038
      if not match:
2039
        if not result.stdout:
2040
          logging.info("KVM: empty 'info migrate' result")
2041
        else:
2042
          logging.warning("KVM: unknown 'info migrate' result: %s",
2043
                          result.stdout)
2044
      else:
2045
        status = match.group(1)
2046
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2047
          migration_status = objects.MigrationStatus(status=status)
2048
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2049
          if match:
2050
            migration_status.transferred_ram = match.group("transferred")
2051
            migration_status.total_ram = match.group("total")
2052

    
2053
          return migration_status
2054

    
2055
        logging.warning("KVM: unknown migration status '%s'", status)
2056

    
2057
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2058

    
2059
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED,
2060
                                  info="Too many 'info migrate' broken answers")
2061

    
2062
  def BalloonInstanceMemory(self, instance, mem):
2063
    """Balloon an instance memory to a certain value.
2064

2065
    @type instance: L{objects.Instance}
2066
    @param instance: instance to be accepted
2067
    @type mem: int
2068
    @param mem: actual memory size to use for instance runtime
2069

2070
    """
2071
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2072

    
2073
  def GetNodeInfo(self):
2074
    """Return information about the node.
2075

2076
    @return: a dict with the following keys (values in MiB):
2077
          - memory_total: the total memory size on the node
2078
          - memory_free: the available memory on the node for instances
2079
          - memory_dom0: the memory used by the node itself, if available
2080
          - hv_version: the hypervisor version in the form (major, minor,
2081
                        revision)
2082

2083
    """
2084
    result = self.GetLinuxNodeInfo()
2085
    _, v_major, v_min, v_rev = self._GetKVMVersion()
2086
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2087
    return result
2088

    
2089
  @classmethod
2090
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2091
    """Return a command for connecting to the console of an instance.
2092

2093
    """
2094
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2095
      cmd = [constants.KVM_CONSOLE_WRAPPER,
2096
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2097
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2098
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2099
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2100
      return objects.InstanceConsole(instance=instance.name,
2101
                                     kind=constants.CONS_SSH,
2102
                                     host=instance.primary_node,
2103
                                     user=constants.GANETI_RUNAS,
2104
                                     command=cmd)
2105

    
2106
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2107
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2108
      display = instance.network_port - constants.VNC_BASE_PORT
2109
      return objects.InstanceConsole(instance=instance.name,
2110
                                     kind=constants.CONS_VNC,
2111
                                     host=vnc_bind_address,
2112
                                     port=instance.network_port,
2113
                                     display=display)
2114

    
2115
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2116
    if spice_bind:
2117
      return objects.InstanceConsole(instance=instance.name,
2118
                                     kind=constants.CONS_SPICE,
2119
                                     host=spice_bind,
2120
                                     port=instance.network_port)
2121

    
2122
    return objects.InstanceConsole(instance=instance.name,
2123
                                   kind=constants.CONS_MESSAGE,
2124
                                   message=("No serial shell for instance %s" %
2125
                                            instance.name))
2126

    
2127
  def Verify(self):
2128
    """Verify the hypervisor.
2129

2130
    Check that the binary exists.
2131

2132
    """
2133
    if not os.path.exists(constants.KVM_PATH):
2134
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
2135
    if not os.path.exists(constants.SOCAT_PATH):
2136
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
2137

    
2138
  @classmethod
2139
  def CheckParameterSyntax(cls, hvparams):
2140
    """Check the given parameters for validity.
2141

2142
    @type hvparams:  dict
2143
    @param hvparams: dictionary with parameter names/value
2144
    @raise errors.HypervisorError: when a parameter is not valid
2145

2146
    """
2147
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2148

    
2149
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2150
    if kernel_path:
2151
      if not hvparams[constants.HV_ROOT_PATH]:
2152
        raise errors.HypervisorError("Need a root partition for the instance,"
2153
                                     " if a kernel is defined")
2154

    
2155
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2156
        not hvparams[constants.HV_VNC_X509]):
2157
      raise errors.HypervisorError("%s must be defined, if %s is" %
2158
                                   (constants.HV_VNC_X509,
2159
                                    constants.HV_VNC_X509_VERIFY))
2160

    
2161
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2162
    if (boot_order == constants.HT_BO_CDROM and
2163
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2164
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2165
                                   " ISO path")
2166

    
2167
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2168
    if security_model == constants.HT_SM_USER:
2169
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2170
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2171
                                     " must be specified")
2172
    elif (security_model == constants.HT_SM_NONE or
2173
          security_model == constants.HT_SM_POOL):
2174
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2175
        raise errors.HypervisorError("Cannot have a security domain when the"
2176
                                     " security model is 'none' or 'pool'")
2177

    
2178
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2179
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2180
    if spice_bind:
2181
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2182
        # if an IP version is specified, the spice_bind parameter must be an
2183
        # IP of that family
2184
        if (netutils.IP4Address.IsValid(spice_bind) and
2185
            spice_ip_version != constants.IP4_VERSION):
2186
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2187
                                       " the specified IP version is %s" %
2188
                                       (spice_bind, spice_ip_version))
2189

    
2190
        if (netutils.IP6Address.IsValid(spice_bind) and
2191
            spice_ip_version != constants.IP6_VERSION):
2192
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2193
                                       " the specified IP version is %s" %
2194
                                       (spice_bind, spice_ip_version))
2195
    else:
2196
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2197
      # error if any of them is set without it.
2198
      spice_additional_params = frozenset([
2199
        constants.HV_KVM_SPICE_IP_VERSION,
2200
        constants.HV_KVM_SPICE_PASSWORD_FILE,
2201
        constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
2202
        constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
2203
        constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
2204
        constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
2205
        constants.HV_KVM_SPICE_USE_TLS,
2206
        ])
2207
      for param in spice_additional_params:
2208
        if hvparams[param]:
2209
          raise errors.HypervisorError("spice: %s requires %s to be set" %
2210
                                       (param, constants.HV_KVM_SPICE_BIND))
2211

    
2212
  @classmethod
2213
  def ValidateParameters(cls, hvparams):
2214
    """Check the given parameters for validity.
2215

2216
    @type hvparams:  dict
2217
    @param hvparams: dictionary with parameter names/value
2218
    @raise errors.HypervisorError: when a parameter is not valid
2219

2220
    """
2221
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2222

    
2223
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2224
    if security_model == constants.HT_SM_USER:
2225
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2226
      try:
2227
        pwd.getpwnam(username)
2228
      except KeyError:
2229
        raise errors.HypervisorError("Unknown security domain user %s"
2230
                                     % username)
2231

    
2232
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2233
    if spice_bind:
2234
      # only one of VNC and SPICE can be used currently.
2235
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2236
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
2237
                                     " only one of them can be used at a"
2238
                                     " given time.")
2239

    
2240
      # KVM version should be >= 0.14.0
2241
      _, v_major, v_min, _ = cls._GetKVMVersion()
2242
      if (v_major, v_min) < (0, 14):
2243
        raise errors.HypervisorError("spice is configured, but it is not"
2244
                                     " available in versions of KVM < 0.14")
2245

    
2246
      # if spice_bind is not an IP address, it must be a valid interface
2247
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2248
                       or netutils.IP6Address.IsValid(spice_bind))
2249
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2250
        raise errors.HypervisorError("spice: the %s parameter must be either"
2251
                                     " a valid IP address or interface name" %
2252
                                     constants.HV_KVM_SPICE_BIND)
2253

    
2254
  @classmethod
2255
  def PowercycleNode(cls):
2256
    """KVM powercycle, just a wrapper over Linux powercycle.
2257

2258
    """
2259
    cls.LinuxPowercycle()