Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ c9ff267c

History | View | Annotate | Download (81.4 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
    constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
500
    }
501

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

    
509
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
510
  _MIGRATION_INFO_RETRY_DELAY = 2
511

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

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

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

    
521
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"(\S+).*\(default\)")
522

    
523
  ANCILLARY_FILES = [
524
    _KVM_NETWORK_SCRIPT,
525
    ]
526
  ANCILLARY_FILES_OPT = [
527
    _KVM_NETWORK_SCRIPT,
528
    ]
529

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

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

541
    """
542
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
543

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

548
    """
549
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
550

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

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

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

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

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

    
576
    instance = None
577
    memory = 0
578
    vcpus = 0
579

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

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

    
594
    return (instance, memory, vcpus)
595

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

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

604
    """
605
    pidfile = self._InstancePidFile(instance_name)
606
    pid = utils.ReadPidFile(pidfile)
607

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

    
615
    return (pidfile, pid, alive)
616

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

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

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

630
    """
631
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
632

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

637
    """
638
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
639

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

644
    """
645
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
646

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

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

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

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

663
    """
664
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
665

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

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

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

678
    """
679
    return utils.PathJoin(cls._NICS_DIR, instance_name)
680

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

685
    """
686
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
687

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

692
    """
693
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
694

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

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

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

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

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

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

763
    """
764
    if instance.tags:
765
      tags = " ".join(instance.tags)
766
    else:
767
      tags = ""
768

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

    
779
    if nic.ip:
780
      env["IP"] = nic.ip
781

    
782
    if nic.nicparams[constants.NIC_LINK]:
783
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
784

    
785
    def _BuildNetworkEnv(name, network, gateway, network6, gateway6,
786
                         network_type, mac_prefix, tags, env):
787
      if name:
788
        env["NETWORK_NAME"] = name
789
      if network:
790
        env["NETWORK_SUBNET"] = network
791
      if gateway:
792
        env["NETWORK_GATEWAY"] = gateway
793
      if network6:
794
        env["NETWORK_SUBNET6"] = network6
795
      if gateway6:
796
        env["NETWORK_GATEWAY6"] = gateway6
797
      if mac_prefix:
798
        env["NETWORK_MAC_PREFIX"] = mac_prefix
799
      if network_type:
800
        env["NETWORK_TYPE"] = network_type
801
      if tags:
802
        env["NETWORK_TAGS"] = " ".join(tags)
803

    
804
      return env
805

    
806

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
904
    return result
905

    
906
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
907
    """Complete CPU pinning.
908

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

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

    
922
  def ListInstances(self):
923
    """Get the list of running instances.
924

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

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

    
935
  def GetInstanceInfo(self, instance_name):
936
    """Get instance properties.
937

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

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

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

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

    
963
    return (instance_name, pid, memory, vcpus, istat, times)
964

    
965
  def GetAllInstancesInfo(self):
966
    """Get properties of all instances.
967

968
    @return: list of tuples (name, id, memory, vcpus, stat, times)
969

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

    
982
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_cmd, block_devices):
983

    
984
    hvp = instance.hvparams
985
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
986

    
987
    _, v_major, v_min, _ = self._GetKVMVersion()
988

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

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

    
1037
      kvm_cmd.extend(["-drive", drive_val])
1038

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

    
1046
    return kvm_cmd
1047

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

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

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

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

    
1078
    hvp = instance.hvparams
1079
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1080
    if not mversion:
1081
      mversion = self._GetDefaultMachineVersion()
1082
    kvm_cmd.extend(["-M", mversion])
1083
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1084
    if kernel_path:
1085
      boot_cdrom = boot_floppy = boot_network = False
1086
    else:
1087
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1088
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1089
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1090

    
1091
    self.ValidateParameters(hvp)
1092

    
1093
    if startup_paused:
1094
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1095

    
1096
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
1097
      kvm_cmd.extend(["-enable-kvm"])
1098
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
1099
      kvm_cmd.extend(["-disable-kvm"])
1100

    
1101
    if boot_network:
1102
      kvm_cmd.extend(["-boot", "n"])
1103

    
1104
    # whether this is an older KVM version that uses the boot=on flag
1105
    # on devices
1106
    needs_boot_flag = (v_major, v_min) < (0, 14)
1107

    
1108
    disk_type = hvp[constants.HV_DISK_TYPE]
1109
    if not instance.hotplug_info:
1110
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1111
                                                     block_devices)
1112

    
1113
    #Now we can specify a different device type for CDROM devices.
1114
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1115
    if not cdrom_disk_type:
1116
      cdrom_disk_type = disk_type
1117

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

    
1139
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1140
    if iso_image2:
1141
      options = ",format=raw,media=cdrom"
1142
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1143
        if_val = ",if=virtio"
1144
      else:
1145
        if_val = ",if=%s" % cdrom_disk_type
1146
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1147
      kvm_cmd.extend(["-drive", drive_val])
1148

    
1149
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1150
    if floppy_image:
1151
      options = ",format=raw,media=disk"
1152
      if boot_floppy:
1153
        kvm_cmd.extend(["-boot", "a"])
1154
        options = "%s,boot=on" % options
1155
      if_val = ",if=floppy"
1156
      options = "%s%s" % (options, if_val)
1157
      drive_val = "file=%s%s" % (floppy_image, options)
1158
      kvm_cmd.extend(["-drive", drive_val])
1159

    
1160
    if kernel_path:
1161
      kvm_cmd.extend(["-kernel", kernel_path])
1162
      initrd_path = hvp[constants.HV_INITRD_PATH]
1163
      if initrd_path:
1164
        kvm_cmd.extend(["-initrd", initrd_path])
1165
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1166
                     hvp[constants.HV_KERNEL_ARGS]]
1167
      if hvp[constants.HV_SERIAL_CONSOLE]:
1168
        root_append.append("console=ttyS0,38400")
1169
      kvm_cmd.extend(["-append", " ".join(root_append)])
1170

    
1171
    mem_path = hvp[constants.HV_MEM_PATH]
1172
    if mem_path:
1173
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1174

    
1175
    monitor_dev = ("unix:%s,server,nowait" %
1176
                   self._InstanceMonitor(instance.name))
1177
    kvm_cmd.extend(["-monitor", monitor_dev])
1178
    if hvp[constants.HV_SERIAL_CONSOLE]:
1179
      serial_dev = ("unix:%s,server,nowait" %
1180
                    self._InstanceSerial(instance.name))
1181
      kvm_cmd.extend(["-serial", serial_dev])
1182
    else:
1183
      kvm_cmd.extend(["-serial", "none"])
1184

    
1185
    mouse_type = hvp[constants.HV_USB_MOUSE]
1186
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1187
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1188
    spice_ip_version = None
1189

    
1190
    if mouse_type:
1191
      kvm_cmd.extend(["-usb"])
1192
      kvm_cmd.extend(["-usbdevice", mouse_type])
1193
    elif vnc_bind_address:
1194
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1195

    
1196
    if vnc_bind_address:
1197
      if netutils.IP4Address.IsValid(vnc_bind_address):
1198
        if instance.network_port > constants.VNC_BASE_PORT:
1199
          display = instance.network_port - constants.VNC_BASE_PORT
1200
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1201
            vnc_arg = ":%d" % (display)
1202
          else:
1203
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1204
        else:
1205
          logging.error("Network port is not a valid VNC display (%d < %d)."
1206
                        " Not starting VNC", instance.network_port,
1207
                        constants.VNC_BASE_PORT)
1208
          vnc_arg = "none"
1209

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

    
1224
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1225

    
1226
      else:
1227
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1228

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

    
1239
        # if the user specified an IP version and the interface does not
1240
        # have that kind of IP addresses, throw an exception
1241
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1242
          if not addresses[spice_ip_version]:
1243
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
1244
                                         " for %s" % (spice_ip_version,
1245
                                                      spice_bind))
1246

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

    
1263
        spice_address = addresses[spice_ip_version][0]
1264

    
1265
      else:
1266
        # spice_bind is known to be a valid IP address, because
1267
        # ValidateParameters checked it.
1268
        spice_address = spice_bind
1269

    
1270
      spice_arg = "addr=%s" % spice_address
1271
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1272
        spice_arg = "%s,tls-port=%s,x509-cacert-file=%s" % (spice_arg,
1273
            instance.network_port, constants.SPICE_CACERT_FILE)
1274
        spice_arg = "%s,x509-key-file=%s,x509-cert-file=%s" % (spice_arg,
1275
            constants.SPICE_CERT_FILE, constants.SPICE_CERT_FILE)
1276
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1277
        if tls_ciphers:
1278
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1279
      else:
1280
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1281

    
1282
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1283
        spice_arg = "%s,disable-ticketing" % spice_arg
1284

    
1285
      if spice_ip_version:
1286
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1287

    
1288
      # Image compression options
1289
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1290
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1291
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1292
      if img_lossless:
1293
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1294
      if img_jpeg:
1295
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1296
      if img_zlib_glz:
1297
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1298

    
1299
      # Video stream detection
1300
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1301
      if video_streaming:
1302
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1303

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

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

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

    
1323
    else:
1324
      kvm_cmd.extend(["-nographic"])
1325

    
1326
    if hvp[constants.HV_USE_LOCALTIME]:
1327
      kvm_cmd.extend(["-localtime"])
1328

    
1329
    if hvp[constants.HV_KVM_USE_CHROOT]:
1330
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1331

    
1332
    # Save the current instance nics, but defer their expansion as parameters,
1333
    # as we'll need to generate executable temp files for them.
1334
    kvm_nics = instance.nics
1335
    hvparams = hvp
1336

    
1337
    if instance.hotplug_info:
1338
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1339
    else:
1340
      return (kvm_cmd, kvm_nics, hvparams)
1341

    
1342
  def _WriteKVMRuntime(self, instance_name, data):
1343
    """Write an instance's KVM runtime
1344

1345
    """
1346
    try:
1347
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1348
                      data=data)
1349
    except EnvironmentError, err:
1350
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1351

    
1352
  def _ReadKVMRuntime(self, instance_name):
1353
    """Read an instance's KVM runtime
1354

1355
    """
1356
    try:
1357
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1358
    except EnvironmentError, err:
1359
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1360
    return file_content
1361

    
1362
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1363
    """Save an instance's KVM runtime
1364

1365
    """
1366
    if instance.hotplug_info:
1367
      kvm_cmd, kvm_nics, hvparams, block_devices = kvm_runtime
1368
      serialized_blockdevs = [(blk.ToDict(), link)
1369
                              for blk,link in block_devices]
1370
    else:
1371
      kvm_cmd, kvm_nics, hvparams = kvm_runtime
1372

    
1373
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1374

    
1375
    if instance.hotplug_info:
1376
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics,
1377
                                        hvparams, serialized_blockdevs))
1378
    else:
1379
      serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
1380

    
1381
    self._WriteKVMRuntime(instance.name, serialized_form)
1382

    
1383
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1384
    """Load an instance's KVM runtime
1385

1386
    """
1387
    if not serialized_runtime:
1388
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1389
    loaded_runtime = serializer.Load(serialized_runtime)
1390
    if instance.hotplug_info:
1391
      kvm_cmd, serialized_nics, hvparams, serialized_blockdevs = loaded_runtime
1392
      block_devices = [(objects.Disk.FromDict(sdisk), link)
1393
                       for sdisk, link in serialized_blockdevs]
1394
    else:
1395
      kvm_cmd, serialized_nics, hvparams = loaded_runtime
1396

    
1397
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
1398

    
1399
    if instance.hotplug_info:
1400
      return (kvm_cmd, kvm_nics, hvparams, block_devices)
1401
    else:
1402
      return (kvm_cmd, kvm_nics, hvparams)
1403

    
1404
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1405
    """Run the KVM cmd and check for errors
1406

1407
    @type name: string
1408
    @param name: instance name
1409
    @type kvm_cmd: list of strings
1410
    @param kvm_cmd: runcmd input for kvm
1411
    @type tap_fds: list of int
1412
    @param tap_fds: fds of tap devices opened by Ganeti
1413

1414
    """
1415
    try:
1416
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1417
    finally:
1418
      for fd in tap_fds:
1419
        utils_wrapper.CloseFdNoError(fd)
1420

    
1421
    if result.failed:
1422
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1423
                                   (name, result.fail_reason, result.output))
1424
    if not self._InstancePidAlive(name)[2]:
1425
      raise errors.HypervisorError("Failed to start instance %s" % name)
1426

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

1430
    @type incoming: tuple of strings
1431
    @param incoming: (target_host_ip, port)
1432

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

    
1446
    temp_files = []
1447

    
1448
    if instance.hotplug_info:
1449
      kvm_cmd, kvm_nics, up_hvp, block_devices = kvm_runtime
1450
    else:
1451
      kvm_cmd, kvm_nics, up_hvp = kvm_runtime
1452

    
1453
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1454

    
1455
    _, v_major, v_min, _ = self._GetKVMVersion()
1456

    
1457
    # We know it's safe to run as a different user upon migration, so we'll use
1458
    # the latest conf, from conf_hvp.
1459
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1460
    if security_model == constants.HT_SM_USER:
1461
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1462

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

    
1473
    if instance.hotplug_info:
1474
      kvm_cmd = self._GenerateKVMBlockDevicesOptions(instance, kvm_cmd,
1475
                                                     block_devices)
1476

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

    
1496
        if up_hvp[constants.HV_VHOST_NET]:
1497
          # vhost_net is only available from version 0.13.0 or newer
1498
          if (v_major, v_min) >= (0, 13):
1499
            tap_extra = ",vhost=on"
1500
          else:
1501
            raise errors.HypervisorError("vhost_net is configured"
1502
                                        " but it is not available")
1503
      else:
1504
        nic_model = nic_type
1505

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

    
1529
    if incoming:
1530
      target, port = incoming
1531
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1532

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

    
1545
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1546
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1547
                         constants.SECURE_DIR_MODE)])
1548

    
1549
    # Automatically enable QMP if version is >= 0.14
1550
    if (v_major, v_min) >= (0, 14):
1551
      logging.debug("Enabling QMP")
1552
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1553
                    self._InstanceQmpMonitor(instance.name)])
1554

    
1555
    # Configure the network now for starting instances and bridged interfaces,
1556
    # during FinalizeMigration for incoming instances' routed interfaces
1557
    for nic_seq, nic in enumerate(kvm_nics):
1558
      if (incoming and
1559
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1560
        continue
1561
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1562

    
1563
    # CPU affinity requires kvm to start paused, so we set this flag if the
1564
    # instance is not already paused and if we are not going to accept a
1565
    # migrating instance. In the latter case, pausing is not needed.
1566
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1567
    if start_kvm_paused:
1568
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1569

    
1570
    # Note: CPU pinning is using up_hvp since changes take effect
1571
    # during instance startup anyway, and to avoid problems when soft
1572
    # rebooting the instance.
1573
    cpu_pinning = False
1574
    if up_hvp.get(constants.HV_CPU_MASK, None):
1575
      cpu_pinning = True
1576

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

    
1595
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1596
                     constants.RUN_DIRS_MODE)])
1597
    for nic_seq, tap in enumerate(taps):
1598
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1599
                      data=tap)
1600

    
1601
    if vnc_pwd:
1602
      change_cmd = "change vnc password %s" % vnc_pwd
1603
      self._CallMonitorCommand(instance.name, change_cmd)
1604

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

    
1619
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1620
      qmp.connect()
1621
      arguments = {
1622
          "protocol": "spice",
1623
          "password": spice_pwd,
1624
      }
1625
      qmp.Execute("set_password", arguments)
1626

    
1627
    for filename in temp_files:
1628
      utils.RemoveFile(filename)
1629

    
1630
    # If requested, set CPU affinity and resume instance execution
1631
    if cpu_pinning:
1632
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1633

    
1634
    start_memory = self._InstanceStartupMemory(instance)
1635
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1636
      self.BalloonInstanceMemory(instance, start_memory)
1637

    
1638
    if start_kvm_paused:
1639
      # To control CPU pinning, ballooning, and vnc/spice passwords
1640
      # the VM was started in a frozen state. If freezing was not
1641
      # explicitly requested resume the vm status.
1642
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1643

    
1644
  def StartInstance(self, instance, block_devices, startup_paused):
1645
    """Start an instance.
1646

1647
    """
1648
    self._CheckDown(instance.name)
1649
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1650
                                           startup_paused)
1651
    self._SaveKVMRuntime(instance, kvm_runtime)
1652
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1653

    
1654
  def _CallMonitorCommand(self, instance_name, command):
1655
    """Invoke a command on the instance monitor.
1656

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

    
1670
    return result
1671

    
1672
  def _FindFreePCISlot(self, instance_name):
1673
    slots = bitarray(32)
1674
    slots.setall(False)
1675
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
1676
    for line in output.stdout.splitlines():
1677
      match = self._INFO_PCI_RE.search(line)
1678
      if match:
1679
        slot = int(match.group(1))
1680
        slots[slot] = True
1681

    
1682
    free = slots.search(FREE, 1)
1683
    if not free:
1684
      raise errors.HypervisorError("All PCI slots occupied")
1685

    
1686
    return int(free[0])
1687

    
1688
  def _HotplugEnabled(self, instance_name):
1689
    if not self._InstancePidAlive(instance_name)[2]:
1690
      logging.info("Cannot hotplug. Instance %s not alive", instance_name)
1691
      return False
1692

    
1693
    _, v_major, v_min, _ = self._GetKVMVersion()
1694
    return (v_major, v_min) >= (1, 0)
1695

    
1696
  def HotAddDisk(self, instance, disk, dev_path, _):
1697
    """Hotadd new disk to the VM
1698

1699
    """
1700
    if self._HotplugEnabled(instance.name):
1701
      disk.pci = self._FindFreePCISlot(instance.name)
1702
      idx = disk.idx
1703
      command = ("drive_add dummy file=%s,if=none,id=drive%d,format=raw" %
1704
                 (dev_path, idx))
1705

    
1706
      logging.info("Run cmd %s", command)
1707
      output = self._CallMonitorCommand(instance.name, command)
1708

    
1709
      command = ("device_add virtio-blk-pci,bus=pci.0,addr=%s,"
1710
                 "drive=drive%d,id=virtio-blk-pci.%d"
1711
                 % (hex(disk.pci), idx, idx))
1712
      logging.info("Run cmd %s", command)
1713
      output = self._CallMonitorCommand(instance.name, command)
1714
      for line in output.stdout.splitlines():
1715
        logging.info("%s", line)
1716

    
1717
      (kvm_cmd, kvm_nics,
1718
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1719
      block_devices.append((disk, dev_path))
1720
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1721
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1722

    
1723
    return disk.pci
1724

    
1725
  def HotDelDisk(self, instance, disk, _):
1726
    """Hotdel disk to the VM
1727

1728
    """
1729
    if self._HotplugEnabled(instance.name):
1730
      idx = disk.idx
1731

    
1732
      command = "device_del virtio-blk-pci.%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
      command = "drive_del drive%d" % idx
1739
      logging.info("Run cmd %s", command)
1740
      #output = self._CallMonitorCommand(instance.name, command)
1741
      #for line in output.stdout.splitlines():
1742
      #  logging.info("%s" % line)
1743

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

    
1755
  def HotAddNic(self, instance, nic, seq):
1756
    """Hotadd new nic to the VM
1757

1758
    """
1759
    if self._HotplugEnabled(instance.name):
1760
      nic.pci = self._FindFreePCISlot(instance.name)
1761
      mac = nic.mac
1762
      idx = nic.idx
1763

    
1764
      (tap, fd) = _OpenTap()
1765
      logging.info("%s %d", tap, fd)
1766

    
1767
      self._PassTapFd(instance, fd, nic)
1768

    
1769
      command = ("netdev_add tap,id=netdev%d,fd=netdev%d"
1770
                 % (idx, idx))
1771
      logging.info("Run cmd %s", command)
1772
      output = self._CallMonitorCommand(instance.name, command)
1773
      for line in output.stdout.splitlines():
1774
        logging.info("%s", line)
1775

    
1776
      command = ("device_add virtio-net-pci,bus=pci.0,addr=%s,mac=%s,"
1777
                 "netdev=netdev%d,id=virtio-net-pci.%d"
1778
                 % (hex(nic.pci), mac, idx, idx))
1779
      logging.info("Run cmd %s", command)
1780
      output = self._CallMonitorCommand(instance.name, command)
1781
      for line in output.stdout.splitlines():
1782
        logging.info("%s", line)
1783

    
1784
      self._ConfigureNIC(instance, seq, nic, tap)
1785

    
1786
      (kvm_cmd, kvm_nics,
1787
       hvparams, block_devices) = self._LoadKVMRuntime(instance)
1788
      kvm_nics.append(nic)
1789
      new_kvm_runtime = (kvm_cmd, kvm_nics, hvparams, block_devices)
1790
      self._SaveKVMRuntime(instance, new_kvm_runtime)
1791

    
1792
    return nic.pci
1793

    
1794
  def HotDelNic(self, instance, nic, _):
1795
    """Hotadd new nic to the VM
1796

1797
    """
1798
    if self._HotplugEnabled(instance.name):
1799
      idx = nic.idx
1800

    
1801
      command = "device_del virtio-net-pci.%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
      command = "netdev_del netdev%d" % idx
1808
      logging.info("Run cmd %s", command)
1809
      output = self._CallMonitorCommand(instance.name, command)
1810
      for line in output.stdout.splitlines():
1811
        logging.info("%s", line)
1812

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

    
1823

    
1824
  def _PassTapFd(self, instance, fd, nic):
1825
    monsock = utils.ShellQuote(self._InstanceMonitor(instance.name))
1826
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1827
    s.connect(monsock)
1828
    idx = nic.idx
1829
    command = "getfd netdev%d\n" % idx
1830
    fds = [fd]
1831
    logging.info("%s", fds)
1832
    fdsend.sendfds(s, command, fds = fds)
1833
    s.close()
1834

    
1835
  @classmethod
1836
  def _ParseKVMVersion(cls, text):
1837
    """Parse the KVM version from the --help output.
1838

1839
    @type text: string
1840
    @param text: output of kvm --help
1841
    @return: (version, v_maj, v_min, v_rev)
1842
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1843

1844
    """
1845
    match = cls._VERSION_RE.search(text.splitlines()[0])
1846
    if not match:
1847
      raise errors.HypervisorError("Unable to get KVM version")
1848

    
1849
    v_all = match.group(0)
1850
    v_maj = int(match.group(1))
1851
    v_min = int(match.group(2))
1852
    if match.group(4):
1853
      v_rev = int(match.group(4))
1854
    else:
1855
      v_rev = 0
1856
    return (v_all, v_maj, v_min, v_rev)
1857

    
1858
  @classmethod
1859
  def _GetKVMVersion(cls):
1860
    """Return the installed KVM version.
1861

1862
    @return: (version, v_maj, v_min, v_rev)
1863
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
1864

1865
    """
1866
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1867
    if result.failed:
1868
      raise errors.HypervisorError("Unable to get KVM version")
1869
    return cls._ParseKVMVersion(result.output)
1870

    
1871
  def StopInstance(self, instance, force=False, retry=False, name=None):
1872
    """Stop an instance.
1873

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

    
1889
  @classmethod
1890
  def _GetDefaultMachineVersion(cls):
1891
    """Return the default hardware revision (e.g. pc-1.1)
1892

1893
    """
1894
    result = utils.RunCmd([constants.KVM_PATH, "-M", "?"])
1895
    if result.failed:
1896
      raise errors.HypervisorError("Unable to get default hardware revision")
1897
    for line in result.output.splitlines():
1898
      match = cls._DEFAULT_MACHINE_VERSION_RE.match(line)
1899
      if match:
1900
        return match.group(1)
1901

    
1902
    return "pc"
1903

    
1904
  def CleanupInstance(self, instance_name):
1905
    """Cleanup after a stopped instance
1906

1907
    """
1908
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1909
    if pid > 0 and alive:
1910
      raise errors.HypervisorError("Cannot cleanup a live instance")
1911
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1912

    
1913
  def RebootInstance(self, instance):
1914
    """Reboot an instance.
1915

1916
    """
1917
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1918
    # socket the instance will stop, but now power up again. So we'll resort
1919
    # to shutdown and restart.
1920
    _, _, alive = self._InstancePidAlive(instance.name)
1921
    if not alive:
1922
      raise errors.HypervisorError("Failed to reboot instance %s:"
1923
                                   " not running" % instance.name)
1924
    # StopInstance will delete the saved KVM runtime so:
1925
    # ...first load it...
1926
    kvm_runtime = self._LoadKVMRuntime(instance)
1927
    # ...now we can safely call StopInstance...
1928
    if not self.StopInstance(instance):
1929
      self.StopInstance(instance, force=True)
1930
    # ...and finally we can save it again, and execute it...
1931
    self._SaveKVMRuntime(instance, kvm_runtime)
1932
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1933

    
1934
  def MigrationInfo(self, instance):
1935
    """Get instance information to perform a migration.
1936

1937
    @type instance: L{objects.Instance}
1938
    @param instance: instance to be migrated
1939
    @rtype: string
1940
    @return: content of the KVM runtime file
1941

1942
    """
1943
    return self._ReadKVMRuntime(instance.name)
1944

    
1945
  def AcceptInstance(self, instance, info, target):
1946
    """Prepare to accept an instance.
1947

1948
    @type instance: L{objects.Instance}
1949
    @param instance: instance to be accepted
1950
    @type info: string
1951
    @param info: content of the KVM runtime file on the source node
1952
    @type target: string
1953
    @param target: target host (usually ip), on this node
1954

1955
    """
1956
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1957
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1958
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1959

    
1960
  def FinalizeMigrationDst(self, instance, info, success):
1961
    """Finalize the instance migration on the target node.
1962

1963
    Stop the incoming mode KVM.
1964

1965
    @type instance: L{objects.Instance}
1966
    @param instance: instance whose migration is being finalized
1967

1968
    """
1969
    if success:
1970
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1971
      kvm_nics = kvm_runtime[1]
1972

    
1973
      for nic_seq, nic in enumerate(kvm_nics):
1974
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1975
          # Bridged interfaces have already been configured
1976
          continue
1977
        try:
1978
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1979
        except EnvironmentError, err:
1980
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1981
                          instance.name, nic_seq, str(err))
1982
          continue
1983
        try:
1984
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1985
        except errors.HypervisorError, err:
1986
          logging.warning(str(err))
1987

    
1988
      self._WriteKVMRuntime(instance.name, info)
1989
    else:
1990
      self.StopInstance(instance, force=True)
1991

    
1992
  def MigrateInstance(self, instance, target, live):
1993
    """Migrate an instance to a target node.
1994

1995
    The migration will not be attempted if the instance is not
1996
    currently running.
1997

1998
    @type instance: L{objects.Instance}
1999
    @param instance: the instance to be migrated
2000
    @type target: string
2001
    @param target: ip address of the target node
2002
    @type live: boolean
2003
    @param live: perform a live migration
2004

2005
    """
2006
    instance_name = instance.name
2007
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2008
    _, _, alive = self._InstancePidAlive(instance_name)
2009
    if not alive:
2010
      raise errors.HypervisorError("Instance not running, cannot migrate")
2011

    
2012
    if not live:
2013
      self._CallMonitorCommand(instance_name, "stop")
2014

    
2015
    migrate_command = ("migrate_set_speed %dm" %
2016
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2017
    self._CallMonitorCommand(instance_name, migrate_command)
2018

    
2019
    migrate_command = ("migrate_set_downtime %dms" %
2020
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2021
    self._CallMonitorCommand(instance_name, migrate_command)
2022

    
2023
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2024
    self._CallMonitorCommand(instance_name, migrate_command)
2025

    
2026
  def FinalizeMigrationSource(self, instance, success, live):
2027
    """Finalize the instance migration on the source node.
2028

2029
    @type instance: L{objects.Instance}
2030
    @param instance: the instance that was migrated
2031
    @type success: bool
2032
    @param success: whether the migration succeeded or not
2033
    @type live: bool
2034
    @param live: whether the user requested a live migration or not
2035

2036
    """
2037
    if success:
2038
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2039
      utils.KillProcess(pid)
2040
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2041
    elif live:
2042
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2043

    
2044
  def GetMigrationStatus(self, instance):
2045
    """Get the migration status
2046

2047
    @type instance: L{objects.Instance}
2048
    @param instance: the instance that is being migrated
2049
    @rtype: L{objects.MigrationStatus}
2050
    @return: the status of the current migration (one of
2051
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2052
             progress info that can be retrieved from the hypervisor
2053

2054
    """
2055
    info_command = "info migrate"
2056
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2057
      result = self._CallMonitorCommand(instance.name, info_command)
2058
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2059
      if not match:
2060
        if not result.stdout:
2061
          logging.info("KVM: empty 'info migrate' result")
2062
        else:
2063
          logging.warning("KVM: unknown 'info migrate' result: %s",
2064
                          result.stdout)
2065
      else:
2066
        status = match.group(1)
2067
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2068
          migration_status = objects.MigrationStatus(status=status)
2069
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2070
          if match:
2071
            migration_status.transferred_ram = match.group("transferred")
2072
            migration_status.total_ram = match.group("total")
2073

    
2074
          return migration_status
2075

    
2076
        logging.warning("KVM: unknown migration status '%s'", status)
2077

    
2078
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2079

    
2080
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2081

    
2082
  def BalloonInstanceMemory(self, instance, mem):
2083
    """Balloon an instance memory to a certain value.
2084

2085
    @type instance: L{objects.Instance}
2086
    @param instance: instance to be accepted
2087
    @type mem: int
2088
    @param mem: actual memory size to use for instance runtime
2089

2090
    """
2091
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2092

    
2093
  def GetNodeInfo(self):
2094
    """Return information about the node.
2095

2096
    @return: a dict with the following keys (values in MiB):
2097
          - memory_total: the total memory size on the node
2098
          - memory_free: the available memory on the node for instances
2099
          - memory_dom0: the memory used by the node itself, if available
2100
          - hv_version: the hypervisor version in the form (major, minor,
2101
                        revision)
2102

2103
    """
2104
    result = self.GetLinuxNodeInfo()
2105
    _, v_major, v_min, v_rev = self._GetKVMVersion()
2106
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2107
    return result
2108

    
2109
  @classmethod
2110
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2111
    """Return a command for connecting to the console of an instance.
2112

2113
    """
2114
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2115
      cmd = [constants.KVM_CONSOLE_WRAPPER,
2116
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2117
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2118
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2119
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2120
      return objects.InstanceConsole(instance=instance.name,
2121
                                     kind=constants.CONS_SSH,
2122
                                     host=instance.primary_node,
2123
                                     user=constants.GANETI_RUNAS,
2124
                                     command=cmd)
2125

    
2126
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2127
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2128
      display = instance.network_port - constants.VNC_BASE_PORT
2129
      return objects.InstanceConsole(instance=instance.name,
2130
                                     kind=constants.CONS_VNC,
2131
                                     host=vnc_bind_address,
2132
                                     port=instance.network_port,
2133
                                     display=display)
2134

    
2135
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2136
    if spice_bind:
2137
      return objects.InstanceConsole(instance=instance.name,
2138
                                     kind=constants.CONS_SPICE,
2139
                                     host=spice_bind,
2140
                                     port=instance.network_port)
2141

    
2142
    return objects.InstanceConsole(instance=instance.name,
2143
                                   kind=constants.CONS_MESSAGE,
2144
                                   message=("No serial shell for instance %s" %
2145
                                            instance.name))
2146

    
2147
  def Verify(self):
2148
    """Verify the hypervisor.
2149

2150
    Check that the binary exists.
2151

2152
    """
2153
    if not os.path.exists(constants.KVM_PATH):
2154
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
2155
    if not os.path.exists(constants.SOCAT_PATH):
2156
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
2157

    
2158
  @classmethod
2159
  def CheckParameterSyntax(cls, hvparams):
2160
    """Check the given parameters for validity.
2161

2162
    @type hvparams:  dict
2163
    @param hvparams: dictionary with parameter names/value
2164
    @raise errors.HypervisorError: when a parameter is not valid
2165

2166
    """
2167
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2168

    
2169
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2170
    if kernel_path:
2171
      if not hvparams[constants.HV_ROOT_PATH]:
2172
        raise errors.HypervisorError("Need a root partition for the instance,"
2173
                                     " if a kernel is defined")
2174

    
2175
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2176
        not hvparams[constants.HV_VNC_X509]):
2177
      raise errors.HypervisorError("%s must be defined, if %s is" %
2178
                                   (constants.HV_VNC_X509,
2179
                                    constants.HV_VNC_X509_VERIFY))
2180

    
2181
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2182
    if (boot_order == constants.HT_BO_CDROM and
2183
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2184
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2185
                                   " ISO path")
2186

    
2187
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2188
    if security_model == constants.HT_SM_USER:
2189
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2190
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2191
                                     " must be specified")
2192
    elif (security_model == constants.HT_SM_NONE or
2193
          security_model == constants.HT_SM_POOL):
2194
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2195
        raise errors.HypervisorError("Cannot have a security domain when the"
2196
                                     " security model is 'none' or 'pool'")
2197

    
2198
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2199
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2200
    if spice_bind:
2201
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2202
        # if an IP version is specified, the spice_bind parameter must be an
2203
        # IP of that family
2204
        if (netutils.IP4Address.IsValid(spice_bind) and
2205
            spice_ip_version != constants.IP4_VERSION):
2206
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
2207
                                       " the specified IP version is %s" %
2208
                                       (spice_bind, spice_ip_version))
2209

    
2210
        if (netutils.IP6Address.IsValid(spice_bind) and
2211
            spice_ip_version != constants.IP6_VERSION):
2212
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
2213
                                       " the specified IP version is %s" %
2214
                                       (spice_bind, spice_ip_version))
2215
    else:
2216
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2217
      # error if any of them is set without it.
2218
      spice_additional_params = frozenset([
2219
        constants.HV_KVM_SPICE_IP_VERSION,
2220
        constants.HV_KVM_SPICE_PASSWORD_FILE,
2221
        constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
2222
        constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
2223
        constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
2224
        constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
2225
        constants.HV_KVM_SPICE_USE_TLS,
2226
        ])
2227
      for param in spice_additional_params:
2228
        if hvparams[param]:
2229
          raise errors.HypervisorError("spice: %s requires %s to be set" %
2230
                                       (param, constants.HV_KVM_SPICE_BIND))
2231

    
2232
  @classmethod
2233
  def ValidateParameters(cls, hvparams):
2234
    """Check the given parameters for validity.
2235

2236
    @type hvparams:  dict
2237
    @param hvparams: dictionary with parameter names/value
2238
    @raise errors.HypervisorError: when a parameter is not valid
2239

2240
    """
2241
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2242

    
2243
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2244
    if security_model == constants.HT_SM_USER:
2245
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2246
      try:
2247
        pwd.getpwnam(username)
2248
      except KeyError:
2249
        raise errors.HypervisorError("Unknown security domain user %s"
2250
                                     % username)
2251

    
2252
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2253
    if spice_bind:
2254
      # only one of VNC and SPICE can be used currently.
2255
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2256
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
2257
                                     " only one of them can be used at a"
2258
                                     " given time.")
2259

    
2260
      # KVM version should be >= 0.14.0
2261
      _, v_major, v_min, _ = cls._GetKVMVersion()
2262
      if (v_major, v_min) < (0, 14):
2263
        raise errors.HypervisorError("spice is configured, but it is not"
2264
                                     " available in versions of KVM < 0.14")
2265

    
2266
      # if spice_bind is not an IP address, it must be a valid interface
2267
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
2268
                       or netutils.IP6Address.IsValid(spice_bind))
2269
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2270
        raise errors.HypervisorError("spice: the %s parameter must be either"
2271
                                     " a valid IP address or interface name" %
2272
                                     constants.HV_KVM_SPICE_BIND)
2273

    
2274
  @classmethod
2275
  def PowercycleNode(cls):
2276
    """KVM powercycle, just a wrapper over Linux powercycle.
2277

2278
    """
2279
    cls.LinuxPowercycle()