Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 02985c42

History | View | Annotate | Download (99 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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
from bitarray import bitarray
41
try:
42
  import affinity   # pylint: disable=F0401
43
except ImportError:
44
  affinity = None
45
try:
46
  import fdsend   # pylint: disable=F0401
47
except ImportError:
48
  fdsend = None
49

    
50
from ganeti import utils
51
from ganeti import constants
52
from ganeti import errors
53
from ganeti import serializer
54
from ganeti import objects
55
from ganeti import uidpool
56
from ganeti import ssconf
57
from ganeti import netutils
58
from ganeti import pathutils
59
from ganeti.hypervisor import hv_base
60
from ganeti.utils import wrapper as utils_wrapper
61

    
62

    
63
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-ifup-custom"
64
_KVM_START_PAUSED_FLAG = "-S"
65

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

    
76
#: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
77
_SPICE_ADDITIONAL_PARAMS = frozenset([
78
  constants.HV_KVM_SPICE_IP_VERSION,
79
  constants.HV_KVM_SPICE_PASSWORD_FILE,
80
  constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR,
81
  constants.HV_KVM_SPICE_JPEG_IMG_COMPR,
82
  constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR,
83
  constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION,
84
  constants.HV_KVM_SPICE_USE_TLS,
85
  ])
86

    
87
# Constant bitarray that reflects to a free pci slot
88
# Use it with bitarray.search()
89
_AVAILABLE_PCI_SLOT = bitarray("0")
90

    
91
# below constants show the format of runtime file
92
# the nics are in second possition, while the disks in 4th (last)
93
# moreover disk entries are stored as a list of in tuples
94
# (L{objects.Disk}, link_name)
95
_KVM_NICS_RUNTIME_INDEX = 1
96
_KVM_DISKS_RUNTIME_INDEX = 3
97
_DEVICE_RUNTIME_INDEX = {
98
  constants.HOTPLUG_TARGET_DISK: _KVM_DISKS_RUNTIME_INDEX,
99
  constants.HOTPLUG_TARGET_NIC: _KVM_NICS_RUNTIME_INDEX
100
  }
101
_FIND_RUNTIME_ENTRY = {
102
  constants.HOTPLUG_TARGET_NIC:
103
    lambda nic, kvm_nics: [n for n in kvm_nics if n.uuid == nic.uuid],
104
  constants.HOTPLUG_TARGET_DISK:
105
    lambda disk, kvm_disks: [(d, l) for (d, l) in kvm_disks
106
                             if d.uuid == disk.uuid]
107
  }
108
_RUNTIME_DEVICE = {
109
  constants.HOTPLUG_TARGET_NIC: lambda d: d,
110
  constants.HOTPLUG_TARGET_DISK: lambda (d, e): d
111
  }
112
_RUNTIME_ENTRY = {
113
  constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
114
  constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e)
115
  }
116

    
117

    
118
def _GenerateDeviceKVMId(dev_type, dev, idx=None):
119
  """Helper function to generate a unique device name used by KVM
120

121
  QEMU monitor commands use names to identify devices. Here we use their pci
122
  slot and a part of their UUID to name them. dev.pci might be None for old
123
  devices in the cluster.
124

125
  @type dev_type: sting
126
  @param dev_type: device type of param dev
127
  @type dev: L{objects.Disk} or L{objects.NIC}
128
  @param dev: the device object for which we generate a kvm name
129
  @raise errors.HotplugError: in case a device has no pci slot (old devices)
130

131
  """
132

    
133
  # proper device id - available in latest Ganeti versions
134
  if dev.pci and dev.uuid:
135
    return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
136

    
137
  # dummy device id - returned only to _GenerateKVMBlockDevicesOptions
138
  # This enables -device option for paravirtual disk_type
139
  if idx is not None:
140
    return "%s-%d" % (dev_type.lower(), idx)
141

    
142
  raise errors.HotplugError("Hotplug is not supported for devices"
143
                            " without UUID or PCI info")
144

    
145

    
146
def _UpdatePCISlots(dev, pci_reservations):
147
  """Update pci configuration for a stopped instance
148

149
  If dev has a pci slot then reserve it, else find first available
150
  in pci_reservations bitarray. It acts on the same objects passed
151
  as params so there is no need to return anything.
152

153
  @type dev: L{objects.Disk} or L{objects.NIC}
154
  @param dev: the device object for which we update its pci slot
155
  @type pci_reservations: bitarray
156
  @param pci_reservations: existing pci reservations for an instance
157
  @raise errors.HotplugError: in case an instance has all its slot occupied
158

159
  """
160
  if dev.pci:
161
    free = dev.pci
162
  else: # pylint: disable=E1103
163
    [free] = pci_reservations.search(_AVAILABLE_PCI_SLOT, 1)
164
    if not free:
165
      raise errors.HypervisorError("All PCI slots occupied")
166
    dev.pci = int(free)
167

    
168
  pci_reservations[free] = True
169

    
170

    
171
def _GetExistingDeviceInfo(dev_type, device, runtime):
172
  """Helper function to get an existing device inside the runtime file
173

174
  Used when an instance is running. Load kvm runtime file and search
175
  for a device based on its type and uuid.
176

177
  @type dev_type: sting
178
  @param dev_type: device type of param dev
179
  @type device: L{objects.Disk} or L{objects.NIC}
180
  @param device: the device object for which we generate a kvm name
181
  @type runtime: tuple (cmd, nics, hvparams, disks)
182
  @param runtime: the runtime data to search for the device
183
  @raise errors.HotplugError: in case the requested device does not
184
    exist (e.g. device has been added without --hotplug option) or
185
    device info has not pci slot (e.g. old devices in the cluster)
186

187
  """
188
  index = _DEVICE_RUNTIME_INDEX[dev_type]
189
  found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
190
  if not found:
191
    raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
192
                              (dev_type, device.uuid))
193

    
194
  return found[0]
195

    
196

    
197
def _UpgradeSerializedRuntime(serialized_runtime):
198
  """Upgrade runtime data
199

200
  Remove any deprecated fields or change the format of the data.
201
  The runtime files are not upgraded when Ganeti is upgraded, so the required
202
  modification have to be performed here.
203

204
  @type serialized_runtime: string
205
  @param serialized_runtime: raw text data read from actual runtime file
206
  @return: (cmd, nic dicts, hvparams, bdev dicts)
207
  @rtype: tuple
208

209
  """
210
  loaded_runtime = serializer.Load(serialized_runtime)
211
  kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
212
  if len(loaded_runtime) >= 4:
213
    serialized_disks = loaded_runtime[3]
214
  else:
215
    serialized_disks = []
216

    
217
  for nic in serialized_nics:
218
    # Add a dummy uuid slot if an pre-2.8 NIC is found
219
    if "uuid" not in nic:
220
      nic["uuid"] = utils.NewUUID()
221

    
222
  return kvm_cmd, serialized_nics, hvparams, serialized_disks
223

    
224

    
225
def _AnalyzeSerializedRuntime(serialized_runtime):
226
  """Return runtime entries for a serialized runtime file
227

228
  @type serialized_runtime: string
229
  @param serialized_runtime: raw text data read from actual runtime file
230
  @return: (cmd, nics, hvparams, bdevs)
231
  @rtype: tuple
232

233
  """
234
  kvm_cmd, serialized_nics, hvparams, serialized_disks = \
235
    _UpgradeSerializedRuntime(serialized_runtime)
236
  kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
237
  kvm_disks = [(objects.Disk.FromDict(sdisk), link)
238
               for sdisk, link in serialized_disks]
239

    
240
  return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
241

    
242

    
243
def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
244
  """Retrieves supported TUN features from file descriptor.
245

246
  @see: L{_ProbeTapVnetHdr}
247

248
  """
249
  req = struct.pack("I", 0)
250
  try:
251
    buf = _ioctl(fd, TUNGETFEATURES, req)
252
  except EnvironmentError, err:
253
    logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
254
    return None
255
  else:
256
    (flags, ) = struct.unpack("I", buf)
257
    return flags
258

    
259

    
260
def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
261
  """Check whether to enable the IFF_VNET_HDR flag.
262

263
  To do this, _all_ of the following conditions must be met:
264
   1. TUNGETFEATURES ioctl() *must* be implemented
265
   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
266
   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
267
      drivers/net/tun.c there is no way to test this until after the tap device
268
      has been created using TUNSETIFF, and there is no way to change the
269
      IFF_VNET_HDR flag after creating the interface, catch-22! However both
270
      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
271
      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
272

273
   @type fd: int
274
   @param fd: the file descriptor of /dev/net/tun
275

276
  """
277
  flags = _features_fn(fd)
278

    
279
  if flags is None:
280
    # Not supported
281
    return False
282

    
283
  result = bool(flags & IFF_VNET_HDR)
284

    
285
  if not result:
286
    logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
287

    
288
  return result
289

    
290

    
291
def _OpenTap(vnet_hdr=True):
292
  """Open a new tap device and return its file descriptor.
293

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

297
  @type vnet_hdr: boolean
298
  @param vnet_hdr: Enable the VNET Header
299
  @return: (ifname, tapfd)
300
  @rtype: tuple
301

302
  """
303
  try:
304
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
305
  except EnvironmentError:
306
    raise errors.HypervisorError("Failed to open /dev/net/tun")
307

    
308
  flags = IFF_TAP | IFF_NO_PI
309

    
310
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
311
    flags |= IFF_VNET_HDR
312

    
313
  # The struct ifreq ioctl request (see netdevice(7))
314
  ifr = struct.pack("16sh", "", flags)
315

    
316
  try:
317
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
318
  except EnvironmentError, err:
319
    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
320
                                 err)
321

    
322
  # Get the interface name from the ioctl
323
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
324
  return (ifname, tapfd)
325

    
326

    
327
class QmpMessage:
328
  """QEMU Messaging Protocol (QMP) message.
329

330
  """
331
  def __init__(self, data):
332
    """Creates a new QMP message based on the passed data.
333

334
    """
335
    if not isinstance(data, dict):
336
      raise TypeError("QmpMessage must be initialized with a dict")
337

    
338
    self.data = data
339

    
340
  def __getitem__(self, field_name):
341
    """Get the value of the required field if present, or None.
342

343
    Overrides the [] operator to provide access to the message data,
344
    returning None if the required item is not in the message
345
    @return: the value of the field_name field, or None if field_name
346
             is not contained in the message
347

348
    """
349
    return self.data.get(field_name, None)
350

    
351
  def __setitem__(self, field_name, field_value):
352
    """Set the value of the required field_name to field_value.
353

354
    """
355
    self.data[field_name] = field_value
356

    
357
  @staticmethod
358
  def BuildFromJsonString(json_string):
359
    """Build a QmpMessage from a JSON encoded string.
360

361
    @type json_string: str
362
    @param json_string: JSON string representing the message
363
    @rtype: L{QmpMessage}
364
    @return: a L{QmpMessage} built from json_string
365

366
    """
367
    # Parse the string
368
    data = serializer.LoadJson(json_string)
369
    return QmpMessage(data)
370

    
371
  def __str__(self):
372
    # The protocol expects the JSON object to be sent as a single line.
373
    return serializer.DumpJson(self.data)
374

    
375
  def __eq__(self, other):
376
    # When comparing two QmpMessages, we are interested in comparing
377
    # their internal representation of the message data
378
    return self.data == other.data
379

    
380

    
381
class MonitorSocket(object):
382
  _SOCKET_TIMEOUT = 5
383

    
384
  def __init__(self, monitor_filename):
385
    """Instantiates the MonitorSocket object.
386

387
    @type monitor_filename: string
388
    @param monitor_filename: the filename of the UNIX raw socket on which the
389
                             monitor (QMP or simple one) is listening
390

391
    """
392
    self.monitor_filename = monitor_filename
393
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
394
    # We want to fail if the server doesn't send a complete message
395
    # in a reasonable amount of time
396
    self.sock.settimeout(self._SOCKET_TIMEOUT)
397
    self._connected = False
398

    
399
  def _check_socket(self):
400
    sock_stat = None
401
    try:
402
      sock_stat = os.stat(self.monitor_filename)
403
    except EnvironmentError, err:
404
      if err.errno == errno.ENOENT:
405
        raise errors.HypervisorError("No monitor socket found")
406
      else:
407
        raise errors.HypervisorError("Error checking monitor socket: %s",
408
                                     utils.ErrnoOrStr(err))
409
    if not stat.S_ISSOCK(sock_stat.st_mode):
410
      raise errors.HypervisorError("Monitor socket is not a socket")
411

    
412
  def _check_connection(self):
413
    """Make sure that the connection is established.
414

415
    """
416
    if not self._connected:
417
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
418
                                   " invoke connect() on it")
419

    
420
  def connect(self):
421
    """Connects to the monitor.
422

423
    Connects to the UNIX socket
424

425
    @raise errors.HypervisorError: when there are communication errors
426

427
    """
428
    if self._connected:
429
      raise errors.ProgrammerError("Cannot connect twice")
430

    
431
    self._check_socket()
432

    
433
    # Check file existance/stuff
434
    try:
435
      self.sock.connect(self.monitor_filename)
436
    except EnvironmentError:
437
      raise errors.HypervisorError("Can't connect to qmp socket")
438
    self._connected = True
439

    
440
  def close(self):
441
    """Closes the socket
442

443
    It cannot be used after this call.
444

445
    """
446
    self.sock.close()
447

    
448

    
449
class QmpConnection(MonitorSocket):
450
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
451

452
  """
453
  _FIRST_MESSAGE_KEY = "QMP"
454
  _EVENT_KEY = "event"
455
  _ERROR_KEY = "error"
456
  _RETURN_KEY = RETURN_KEY = "return"
457
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
458
  _ERROR_CLASS_KEY = "class"
459
  _ERROR_DESC_KEY = "desc"
460
  _EXECUTE_KEY = "execute"
461
  _ARGUMENTS_KEY = "arguments"
462
  _CAPABILITIES_COMMAND = "qmp_capabilities"
463
  _MESSAGE_END_TOKEN = "\r\n"
464

    
465
  def __init__(self, monitor_filename):
466
    super(QmpConnection, self).__init__(monitor_filename)
467
    self._buf = ""
468

    
469
  def connect(self):
470
    """Connects to the QMP monitor.
471

472
    Connects to the UNIX socket and makes sure that we can actually send and
473
    receive data to the kvm instance via QMP.
474

475
    @raise errors.HypervisorError: when there are communication errors
476
    @raise errors.ProgrammerError: when there are data serialization errors
477

478
    """
479
    super(QmpConnection, self).connect()
480
    # Check if we receive a correct greeting message from the server
481
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
482
    greeting = self._Recv()
483
    if not greeting[self._FIRST_MESSAGE_KEY]:
484
      self._connected = False
485
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
486
                                   " server greeting")
487

    
488
    # This is needed because QMP can return more than one greetings
489
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
490
    self._buf = ""
491

    
492
    # Let's put the monitor in command mode using the qmp_capabilities
493
    # command, or else no command will be executable.
494
    # (As per the QEMU Protocol Specification 0.1 - section 4)
495
    self.Execute(self._CAPABILITIES_COMMAND)
496

    
497
  def _ParseMessage(self, buf):
498
    """Extract and parse a QMP message from the given buffer.
499

500
    Seeks for a QMP message in the given buf. If found, it parses it and
501
    returns it together with the rest of the characters in the buf.
502
    If no message is found, returns None and the whole buffer.
503

504
    @raise errors.ProgrammerError: when there are data serialization errors
505

506
    """
507
    message = None
508
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
509
    # Specification 0.1 - Section 2.1.1)
510
    pos = buf.find(self._MESSAGE_END_TOKEN)
511
    if pos >= 0:
512
      try:
513
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
514
      except Exception, err:
515
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
516
      buf = buf[pos + 1:]
517

    
518
    return (message, buf)
519

    
520
  def _Recv(self):
521
    """Receives a message from QMP and decodes the received JSON object.
522

523
    @rtype: QmpMessage
524
    @return: the received message
525
    @raise errors.HypervisorError: when there are communication errors
526
    @raise errors.ProgrammerError: when there are data serialization errors
527

528
    """
529
    self._check_connection()
530

    
531
    # Check if there is already a message in the buffer
532
    (message, self._buf) = self._ParseMessage(self._buf)
533
    if message:
534
      return message
535

    
536
    recv_buffer = StringIO.StringIO(self._buf)
537
    recv_buffer.seek(len(self._buf))
538
    try:
539
      while True:
540
        data = self.sock.recv(4096)
541
        if not data:
542
          break
543
        recv_buffer.write(data)
544

    
545
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
546
        if message:
547
          return message
548

    
549
    except socket.timeout, err:
550
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
551
                                   "%s" % (err))
552
    except socket.error, err:
553
      raise errors.HypervisorError("Unable to receive data from KVM using the"
554
                                   " QMP protocol: %s" % err)
555

    
556
  def _Send(self, message):
557
    """Encodes and sends a message to KVM using QMP.
558

559
    @type message: QmpMessage
560
    @param message: message to send to KVM
561
    @raise errors.HypervisorError: when there are communication errors
562
    @raise errors.ProgrammerError: when there are data serialization errors
563

564
    """
565
    self._check_connection()
566
    try:
567
      message_str = str(message)
568
    except Exception, err:
569
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
570

    
571
    try:
572
      self.sock.sendall(message_str)
573
    except socket.timeout, err:
574
      raise errors.HypervisorError("Timeout while sending a QMP message: "
575
                                   "%s (%s)" % (err.string, err.errno))
576
    except socket.error, err:
577
      raise errors.HypervisorError("Unable to send data from KVM using the"
578
                                   " QMP protocol: %s" % err)
579

    
580
  def Execute(self, command, arguments=None):
581
    """Executes a QMP command and returns the response of the server.
582

583
    @type command: str
584
    @param command: the command to execute
585
    @type arguments: dict
586
    @param arguments: dictionary of arguments to be passed to the command
587
    @rtype: dict
588
    @return: dictionary representing the received JSON object
589
    @raise errors.HypervisorError: when there are communication errors
590
    @raise errors.ProgrammerError: when there are data serialization errors
591

592
    """
593
    self._check_connection()
594
    message = QmpMessage({self._EXECUTE_KEY: command})
595
    if arguments:
596
      message[self._ARGUMENTS_KEY] = arguments
597
    self._Send(message)
598

    
599
    # Events can occur between the sending of the command and the reception
600
    # of the response, so we need to filter out messages with the event key.
601
    while True:
602
      response = self._Recv()
603
      err = response[self._ERROR_KEY]
604
      if err:
605
        raise errors.HypervisorError("kvm: error executing the %s"
606
                                     " command: %s (%s):" %
607
                                     (command,
608
                                      err[self._ERROR_DESC_KEY],
609
                                      err[self._ERROR_CLASS_KEY]))
610

    
611
      elif not response[self._EVENT_KEY]:
612
        return response
613

    
614

    
615
class KVMHypervisor(hv_base.BaseHypervisor):
616
  """KVM hypervisor interface
617

618
  """
619
  CAN_MIGRATE = True
620

    
621
  _ROOT_DIR = pathutils.RUN_DIR + "/kvm-hypervisor"
622
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
623
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
624
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
625
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
626
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
627
  _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
628
  # KVM instances with chroot enabled are started in empty chroot directories.
629
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
630
  # After an instance is stopped, its chroot directory is removed.
631
  # If the chroot directory is not empty, it can't be removed.
632
  # A non-empty chroot directory indicates a possible security incident.
633
  # To support forensics, the non-empty chroot directory is quarantined in
634
  # a separate directory, called 'chroot-quarantine'.
635
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
636
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
637
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
638

    
639
  PARAMETERS = {
640
    constants.HV_KVM_PATH: hv_base.REQ_FILE_CHECK,
641
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
642
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
643
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
644
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
645
    constants.HV_ACPI: hv_base.NO_CHECK,
646
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
647
    constants.HV_SERIAL_SPEED: hv_base.NO_CHECK,
648
    constants.HV_VNC_BIND_ADDRESS:
649
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
650
                         utils.IsNormAbsPath(x)),
651
       "The VNC bind address must be either a valid IP address or an absolute"
652
       " pathname", None, None),
653
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
654
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
655
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
656
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
657
    constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
658
    constants.HV_KVM_SPICE_IP_VERSION:
659
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
660
                         x in constants.VALID_IP_VERSIONS),
661
       "The SPICE IP version should be 4 or 6",
662
       None, None),
663
    constants.HV_KVM_SPICE_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
664
    constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR:
665
      hv_base.ParamInSet(
666
        False, constants.HT_KVM_SPICE_VALID_LOSSLESS_IMG_COMPR_OPTIONS),
667
    constants.HV_KVM_SPICE_JPEG_IMG_COMPR:
668
      hv_base.ParamInSet(
669
        False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
670
    constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR:
671
      hv_base.ParamInSet(
672
        False, constants.HT_KVM_SPICE_VALID_LOSSY_IMG_COMPR_OPTIONS),
673
    constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION:
674
      hv_base.ParamInSet(
675
        False, constants.HT_KVM_SPICE_VALID_VIDEO_STREAM_DETECTION_OPTIONS),
676
    constants.HV_KVM_SPICE_AUDIO_COMPR: hv_base.NO_CHECK,
677
    constants.HV_KVM_SPICE_USE_TLS: hv_base.NO_CHECK,
678
    constants.HV_KVM_SPICE_TLS_CIPHERS: hv_base.NO_CHECK,
679
    constants.HV_KVM_SPICE_USE_VDAGENT: hv_base.NO_CHECK,
680
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
681
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
682
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
683
    constants.HV_BOOT_ORDER:
684
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
685
    constants.HV_NIC_TYPE:
686
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
687
    constants.HV_DISK_TYPE:
688
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
689
    constants.HV_KVM_CDROM_DISK_TYPE:
690
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
691
    constants.HV_USB_MOUSE:
692
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
693
    constants.HV_KEYMAP: hv_base.NO_CHECK,
694
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
695
    constants.HV_MIGRATION_BANDWIDTH: hv_base.REQ_NONNEGATIVE_INT_CHECK,
696
    constants.HV_MIGRATION_DOWNTIME: hv_base.REQ_NONNEGATIVE_INT_CHECK,
697
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
698
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
699
    constants.HV_DISK_CACHE:
700
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
701
    constants.HV_SECURITY_MODEL:
702
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
703
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
704
    constants.HV_KVM_FLAG:
705
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
706
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
707
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
708
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
709
    constants.HV_REBOOT_BEHAVIOR:
710
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
711
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
712
    constants.HV_CPU_TYPE: hv_base.NO_CHECK,
713
    constants.HV_CPU_CORES: hv_base.OPT_NONNEGATIVE_INT_CHECK,
714
    constants.HV_CPU_THREADS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
715
    constants.HV_CPU_SOCKETS: hv_base.OPT_NONNEGATIVE_INT_CHECK,
716
    constants.HV_SOUNDHW: hv_base.NO_CHECK,
717
    constants.HV_USB_DEVICES: hv_base.NO_CHECK,
718
    constants.HV_VGA: hv_base.NO_CHECK,
719
    constants.HV_KVM_EXTRA: hv_base.NO_CHECK,
720
    constants.HV_KVM_MACHINE_VERSION: hv_base.NO_CHECK,
721
    constants.HV_VNET_HDR: hv_base.NO_CHECK,
722
    }
723

    
724
  _VIRTIO = "virtio"
725
  _VIRTIO_NET_PCI = "virtio-net-pci"
726
  _VIRTIO_BLK_PCI = "virtio-blk-pci"
727

    
728
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
729
                                    re.M | re.I)
730
  _MIGRATION_PROGRESS_RE = \
731
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
732
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
733
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
734

    
735
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
736
  _MIGRATION_INFO_RETRY_DELAY = 2
737

    
738
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
739

    
740
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
741
  _CPU_INFO_CMD = "info cpus"
742
  _CONT_CMD = "cont"
743

    
744
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
745
  _CHECK_MACHINE_VERSION_RE = \
746
    staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
747

    
748
  _QMP_RE = re.compile(r"^-qmp\s", re.M)
749
  _SPICE_RE = re.compile(r"^-spice\s", re.M)
750
  _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
751
  _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
752
  _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
753
  _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
754
  _DISPLAY_RE = re.compile(r"^-display\s", re.M)
755
  _MACHINE_RE = re.compile(r"^-machine\s", re.M)
756
  _VIRTIO_NET_RE = re.compile(r"^name \"%s\"" % _VIRTIO_NET_PCI, re.M)
757
  _VIRTIO_BLK_RE = re.compile(r"^name \"%s\"" % _VIRTIO_BLK_PCI, re.M)
758
  # match  -drive.*boot=on|off on different lines, but in between accept only
759
  # dashes not preceeded by a new line (which would mean another option
760
  # different than -drive is starting)
761
  _BOOT_RE = re.compile(r"^-drive\s([^-]|(?<!^)-)*,boot=on\|off", re.M | re.S)
762

    
763
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
764
  _INFO_PCI_CMD = "info pci"
765
  _FIND_PCI_DEVICE_RE = \
766
    staticmethod(lambda pci, devid:
767
      re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' % (pci, devid),
768
                 re.M))
769

    
770
  _INFO_VERSION_RE = \
771
    re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
772
  _INFO_VERSION_CMD = "info version"
773

    
774
  _DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000"
775

    
776
  ANCILLARY_FILES = [
777
    _KVM_NETWORK_SCRIPT,
778
    ]
779
  ANCILLARY_FILES_OPT = [
780
    _KVM_NETWORK_SCRIPT,
781
    ]
782

    
783
  # Supported kvm options to get output from
784
  _KVMOPT_HELP = "help"
785
  _KVMOPT_MLIST = "mlist"
786
  _KVMOPT_DEVICELIST = "devicelist"
787

    
788
  # Command to execute to get the output from kvm, and whether to
789
  # accept the output even on failure.
790
  _KVMOPTS_CMDS = {
791
    _KVMOPT_HELP: (["--help"], False),
792
    _KVMOPT_MLIST: (["-M", "?"], False),
793
    _KVMOPT_DEVICELIST: (["-device", "?"], True),
794
  }
795

    
796
  def __init__(self):
797
    hv_base.BaseHypervisor.__init__(self)
798
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
799
    # in a tmpfs filesystem or has been otherwise wiped out.
800
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
801
    utils.EnsureDirs(dirs)
802

    
803
  @classmethod
804
  def _InstancePidFile(cls, instance_name):
805
    """Returns the instance pidfile.
806

807
    """
808
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
809

    
810
  @classmethod
811
  def _InstanceUidFile(cls, instance_name):
812
    """Returns the instance uidfile.
813

814
    """
815
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
816

    
817
  @classmethod
818
  def _InstancePidInfo(cls, pid):
819
    """Check pid file for instance information.
820

821
    Check that a pid file is associated with an instance, and retrieve
822
    information from its command line.
823

824
    @type pid: string or int
825
    @param pid: process id of the instance to check
826
    @rtype: tuple
827
    @return: (instance_name, memory, vcpus)
828
    @raise errors.HypervisorError: when an instance cannot be found
829

830
    """
831
    alive = utils.IsProcessAlive(pid)
832
    if not alive:
833
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
834

    
835
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
836
    try:
837
      cmdline = utils.ReadFile(cmdline_file)
838
    except EnvironmentError, err:
839
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
840
                                   (pid, err))
841

    
842
    instance = None
843
    memory = 0
844
    vcpus = 0
845

    
846
    arg_list = cmdline.split("\x00")
847
    while arg_list:
848
      arg = arg_list.pop(0)
849
      if arg == "-name":
850
        instance = arg_list.pop(0)
851
      elif arg == "-m":
852
        memory = int(arg_list.pop(0))
853
      elif arg == "-smp":
854
        vcpus = int(arg_list.pop(0).split(",")[0])
855

    
856
    if instance is None:
857
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
858
                                   " instance" % pid)
859

    
860
    return (instance, memory, vcpus)
861

    
862
  def _InstancePidAlive(self, instance_name):
863
    """Returns the instance pidfile, pid, and liveness.
864

865
    @type instance_name: string
866
    @param instance_name: instance name
867
    @rtype: tuple
868
    @return: (pid file name, pid, liveness)
869

870
    """
871
    pidfile = self._InstancePidFile(instance_name)
872
    pid = utils.ReadPidFile(pidfile)
873

    
874
    alive = False
875
    try:
876
      cmd_instance = self._InstancePidInfo(pid)[0]
877
      alive = (cmd_instance == instance_name)
878
    except errors.HypervisorError:
879
      pass
880

    
881
    return (pidfile, pid, alive)
882

    
883
  def _CheckDown(self, instance_name):
884
    """Raises an error unless the given instance is down.
885

886
    """
887
    alive = self._InstancePidAlive(instance_name)[2]
888
    if alive:
889
      raise errors.HypervisorError("Failed to start instance %s: %s" %
890
                                   (instance_name, "already running"))
891

    
892
  @classmethod
893
  def _InstanceMonitor(cls, instance_name):
894
    """Returns the instance monitor socket name
895

896
    """
897
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
898

    
899
  @classmethod
900
  def _InstanceSerial(cls, instance_name):
901
    """Returns the instance serial socket name
902

903
    """
904
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
905

    
906
  @classmethod
907
  def _InstanceQmpMonitor(cls, instance_name):
908
    """Returns the instance serial QMP socket name
909

910
    """
911
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
912

    
913
  @staticmethod
914
  def _SocatUnixConsoleParams():
915
    """Returns the correct parameters for socat
916

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

919
    """
920
    if constants.SOCAT_USE_ESCAPE:
921
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
922
    else:
923
      return "echo=0,icanon=0"
924

    
925
  @classmethod
926
  def _InstanceKVMRuntime(cls, instance_name):
927
    """Returns the instance KVM runtime filename
928

929
    """
930
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
931

    
932
  @classmethod
933
  def _InstanceChrootDir(cls, instance_name):
934
    """Returns the name of the KVM chroot dir of the instance
935

936
    """
937
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
938

    
939
  @classmethod
940
  def _InstanceNICDir(cls, instance_name):
941
    """Returns the name of the directory holding the tap device files for a
942
    given instance.
943

944
    """
945
    return utils.PathJoin(cls._NICS_DIR, instance_name)
946

    
947
  @classmethod
948
  def _InstanceNICFile(cls, instance_name, seq_or_uuid):
949
    """Returns the name of the file containing the tap device for a given NIC
950

951
    """
952
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq_or_uuid))
953

    
954
  @classmethod
955
  def _GetInstanceNICTap(cls, instance_name, nic):
956
    """Returns the tap for the corresponding nic
957

958
    Search for tap file named after NIC's uuid.
959
    For old instances without uuid indexed tap files returns nothing.
960

961
    """
962
    try:
963
      return utils.ReadFile(cls._InstanceNICFile(instance_name, nic.uuid))
964
    except EnvironmentError:
965
      pass
966

    
967
  @classmethod
968
  def _WriteInstanceNICFiles(cls, instance_name, seq, nic, tap):
969
    """Write tap name to both instance NIC files
970

971
    """
972
    for ident in [seq, nic.uuid]:
973
      utils.WriteFile(cls._InstanceNICFile(instance_name, ident), data=tap)
974

    
975
  @classmethod
976
  def _RemoveInstanceNICFiles(cls, instance_name, seq, nic):
977
    """Write tap name to both instance NIC files
978

979
    """
980
    for ident in [seq, nic.uuid]:
981
      utils.RemoveFile(cls._InstanceNICFile(instance_name, ident))
982

    
983
  @classmethod
984
  def _InstanceKeymapFile(cls, instance_name):
985
    """Returns the name of the file containing the keymap for a given instance
986

987
    """
988
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
989

    
990
  @classmethod
991
  def _TryReadUidFile(cls, uid_file):
992
    """Try to read a uid file
993

994
    """
995
    if os.path.exists(uid_file):
996
      try:
997
        uid = int(utils.ReadOneLineFile(uid_file))
998
        return uid
999
      except EnvironmentError:
1000
        logging.warning("Can't read uid file", exc_info=True)
1001
      except (TypeError, ValueError):
1002
        logging.warning("Can't parse uid file contents", exc_info=True)
1003
    return None
1004

    
1005
  @classmethod
1006
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
1007
    """Removes an instance's rutime sockets/files/dirs.
1008

1009
    """
1010
    utils.RemoveFile(pidfile)
1011
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
1012
    utils.RemoveFile(cls._InstanceSerial(instance_name))
1013
    utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
1014
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
1015
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
1016
    uid_file = cls._InstanceUidFile(instance_name)
1017
    uid = cls._TryReadUidFile(uid_file)
1018
    utils.RemoveFile(uid_file)
1019
    if uid is not None:
1020
      uidpool.ReleaseUid(uid)
1021
    try:
1022
      shutil.rmtree(cls._InstanceNICDir(instance_name))
1023
    except OSError, err:
1024
      if err.errno != errno.ENOENT:
1025
        raise
1026
    try:
1027
      chroot_dir = cls._InstanceChrootDir(instance_name)
1028
      utils.RemoveDir(chroot_dir)
1029
    except OSError, err:
1030
      if err.errno == errno.ENOTEMPTY:
1031
        # The chroot directory is expected to be empty, but it isn't.
1032
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
1033
                                          prefix="%s-%s-" %
1034
                                          (instance_name,
1035
                                           utils.TimestampForFilename()))
1036
        logging.warning("The chroot directory of instance %s can not be"
1037
                        " removed as it is not empty. Moving it to the"
1038
                        " quarantine instead. Please investigate the"
1039
                        " contents (%s) and clean up manually",
1040
                        instance_name, new_chroot_dir)
1041
        utils.RenameFile(chroot_dir, new_chroot_dir)
1042
      else:
1043
        raise
1044

    
1045
  @staticmethod
1046
  def _CreateNICEnv(instance_name, nic, tap, seq=None, instance_tags=None):
1047
    """Create environment variables for a specific NIC
1048

1049
    This is needed during NIC ifup/ifdown scripts.
1050
    Since instance tags may change during NIC creation and removal
1051
    and because during cleanup instance object is not available we
1052
    pass them only upon NIC creation (instance startup/NIC hot-plugging).
1053

1054
    """
1055
    env = {
1056
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1057
      "INSTANCE": instance_name,
1058
      "MAC": nic.mac,
1059
      "MODE": nic.nicparams[constants.NIC_MODE],
1060
      "INTERFACE_UUID": nic.uuid,
1061
    }
1062

    
1063
    if instance_tags:
1064
      env["TAGS"] = " ".join(instance_tags)
1065

    
1066
    # This should always be available except for old instances in the
1067
    # cluster without uuid indexed tap files.
1068
    if tap:
1069
      env["INTERFACE"] = tap
1070

    
1071
    if seq:
1072
      env["INTERFACE_INDEX"] = str(seq)
1073

    
1074
    if nic.ip:
1075
      env["IP"] = nic.ip
1076

    
1077
    if nic.name:
1078
      env["INTERFACE_NAME"] = nic.name
1079

    
1080
    if nic.nicparams[constants.NIC_LINK]:
1081
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1082

    
1083
    if nic.network:
1084
      n = objects.Network.FromDict(nic.netinfo)
1085
      env.update(n.HooksDict())
1086

    
1087
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1088
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1089

    
1090
    return env
1091

    
1092
  @classmethod
1093
  def _ConfigureNIC(cls, instance, seq, nic, tap):
1094
    """Run the network configuration script for a specified NIC
1095

1096
    @param instance: instance we're acting on
1097
    @type instance: instance object
1098
    @param seq: nic sequence number
1099
    @type seq: int
1100
    @param nic: nic we're acting on
1101
    @type nic: nic object
1102
    @param tap: the host's tap interface this NIC corresponds to
1103
    @type tap: str
1104

1105
    """
1106
    env = cls._CreateNICEnv(instance.name, nic, tap, seq, instance.GetTags())
1107
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1108
    if result.failed:
1109
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1110
                                   " network configuration script output: %s" %
1111
                                   (tap, result.fail_reason, result.output))
1112

    
1113
  @staticmethod
1114
  def _VerifyAffinityPackage():
1115
    if affinity is None:
1116
      raise errors.HypervisorError("affinity Python package not"
1117
                                   " found; cannot use CPU pinning under KVM")
1118

    
1119
  @staticmethod
1120
  def _BuildAffinityCpuMask(cpu_list):
1121
    """Create a CPU mask suitable for sched_setaffinity from a list of
1122
    CPUs.
1123

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

1127
    @type cpu_list: list of int
1128
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1129
    @rtype: int
1130
    @return: a bit mask of CPU affinities
1131

1132
    """
1133
    if cpu_list == constants.CPU_PINNING_OFF:
1134
      return constants.CPU_PINNING_ALL_KVM
1135
    else:
1136
      return sum(2 ** cpu for cpu in cpu_list)
1137

    
1138
  @classmethod
1139
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1140
    """Change CPU affinity for running VM according to given CPU mask.
1141

1142
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1143
    @type cpu_mask: string
1144
    @param process_id: process ID of KVM process. Used to pin entire VM
1145
                       to physical CPUs.
1146
    @type process_id: int
1147
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1148
    @type thread_dict: dict int:int
1149

1150
    """
1151
    # Convert the string CPU mask to a list of list of int's
1152
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1153

    
1154
    if len(cpu_list) == 1:
1155
      all_cpu_mapping = cpu_list[0]
1156
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
1157
        # If CPU pinning has 1 entry that's "all", then do nothing
1158
        pass
1159
      else:
1160
        # If CPU pinning has one non-all entry, map the entire VM to
1161
        # one set of physical CPUs
1162
        cls._VerifyAffinityPackage()
1163
        affinity.set_process_affinity_mask(
1164
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1165
    else:
1166
      # The number of vCPUs mapped should match the number of vCPUs
1167
      # reported by KVM. This was already verified earlier, so
1168
      # here only as a sanity check.
1169
      assert len(thread_dict) == len(cpu_list)
1170
      cls._VerifyAffinityPackage()
1171

    
1172
      # For each vCPU, map it to the proper list of physical CPUs
1173
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1174
        affinity.set_process_affinity_mask(thread_dict[i],
1175
                                           cls._BuildAffinityCpuMask(vcpu))
1176

    
1177
  def _GetVcpuThreadIds(self, instance_name):
1178
    """Get a mapping of vCPU no. to thread IDs for the instance
1179

1180
    @type instance_name: string
1181
    @param instance_name: instance in question
1182
    @rtype: dictionary of int:int
1183
    @return: a dictionary mapping vCPU numbers to thread IDs
1184

1185
    """
1186
    result = {}
1187
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1188
    for line in output.stdout.splitlines():
1189
      match = self._CPU_INFO_RE.search(line)
1190
      if not match:
1191
        continue
1192
      grp = map(int, match.groups())
1193
      result[grp[0]] = grp[1]
1194

    
1195
    return result
1196

    
1197
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1198
    """Complete CPU pinning.
1199

1200
    @type instance_name: string
1201
    @param instance_name: name of instance
1202
    @type cpu_mask: string
1203
    @param cpu_mask: CPU pinning mask as entered by user
1204

1205
    """
1206
    # Get KVM process ID, to be used if need to pin entire VM
1207
    _, pid, _ = self._InstancePidAlive(instance_name)
1208
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1209
    thread_dict = self._GetVcpuThreadIds(instance_name)
1210
    # Run CPU pinning, based on configured mask
1211
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1212

    
1213
  def ListInstances(self):
1214
    """Get the list of running instances.
1215

1216
    We can do this by listing our live instances directory and
1217
    checking whether the associated kvm process is still alive.
1218

1219
    """
1220
    result = []
1221
    for name in os.listdir(self._PIDS_DIR):
1222
      if self._InstancePidAlive(name)[2]:
1223
        result.append(name)
1224
    return result
1225

    
1226
  def GetInstanceInfo(self, instance_name):
1227
    """Get instance properties.
1228

1229
    @type instance_name: string
1230
    @param instance_name: the instance name
1231
    @rtype: tuple of strings
1232
    @return: (name, id, memory, vcpus, stat, times)
1233

1234
    """
1235
    _, pid, alive = self._InstancePidAlive(instance_name)
1236
    if not alive:
1237
      return None
1238

    
1239
    _, memory, vcpus = self._InstancePidInfo(pid)
1240
    istat = "---b-"
1241
    times = "0"
1242

    
1243
    try:
1244
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1245
      qmp.connect()
1246
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1247
      # Will fail if ballooning is not enabled, but we can then just resort to
1248
      # the value above.
1249
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1250
      memory = mem_bytes / 1048576
1251
    except errors.HypervisorError:
1252
      pass
1253

    
1254
    return (instance_name, pid, memory, vcpus, istat, times)
1255

    
1256
  def GetAllInstancesInfo(self):
1257
    """Get properties of all instances.
1258

1259
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1260

1261
    """
1262
    data = []
1263
    for name in os.listdir(self._PIDS_DIR):
1264
      try:
1265
        info = self.GetInstanceInfo(name)
1266
      except errors.HypervisorError:
1267
        # Ignore exceptions due to instances being shut down
1268
        continue
1269
      if info:
1270
        data.append(info)
1271
    return data
1272

    
1273
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1274
                                      kvmhelp, devlist):
1275
    """Generate KVM options regarding instance's block devices.
1276

1277
    @type instance: L{objects.Instance}
1278
    @param instance: the instance object
1279
    @type kvm_disks: list of tuples
1280
    @param kvm_disks: list of tuples [(disk, link_name)..]
1281
    @type kvmhelp: string
1282
    @param kvmhelp: output of kvm --help
1283
    @type devlist: string
1284
    @param devlist: output of kvm -device ?
1285
    @rtype: list
1286
    @return: list of command line options eventually used by kvm executable
1287

1288
    """
1289
    hvp = instance.hvparams
1290
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1291
    if kernel_path:
1292
      boot_disk = False
1293
    else:
1294
      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1295

    
1296
    # whether this is an older KVM version that uses the boot=on flag
1297
    # on devices
1298
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1299

    
1300
    dev_opts = []
1301
    device_driver = None
1302
    disk_type = hvp[constants.HV_DISK_TYPE]
1303
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
1304
      if_val = ",if=%s" % self._VIRTIO
1305
      try:
1306
        if self._VIRTIO_BLK_RE.search(devlist):
1307
          if_val = ",if=none"
1308
          # will be passed in -device option as driver
1309
          device_driver = self._VIRTIO_BLK_PCI
1310
      except errors.HypervisorError, _:
1311
        pass
1312
    else:
1313
      if_val = ",if=%s" % disk_type
1314
    # Cache mode
1315
    disk_cache = hvp[constants.HV_DISK_CACHE]
1316
    if instance.disk_template in constants.DTS_EXT_MIRROR:
1317
      if disk_cache != "none":
1318
        # TODO: make this a hard error, instead of a silent overwrite
1319
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1320
                        " to prevent shared storage corruption on migration",
1321
                        disk_cache)
1322
      cache_val = ",cache=none"
1323
    elif disk_cache != constants.HT_CACHE_DEFAULT:
1324
      cache_val = ",cache=%s" % disk_cache
1325
    else:
1326
      cache_val = ""
1327
    for idx, (cfdev, link_name) in enumerate(kvm_disks):
1328
      if cfdev.mode != constants.DISK_RDWR:
1329
        raise errors.HypervisorError("Instance has read-only disks which"
1330
                                     " are not supported by KVM")
1331
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
1332
      boot_val = ""
1333
      if boot_disk:
1334
        dev_opts.extend(["-boot", "c"])
1335
        boot_disk = False
1336
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1337
          boot_val = ",boot=on"
1338

    
1339
      # For ext we allow overriding disk_cache hypervisor params per disk
1340
      disk_cache = cfdev.params.get("cache", None)
1341
      if disk_cache:
1342
        cache_val = ",cache=%s" % disk_cache
1343
      drive_val = "file=%s,format=raw%s%s%s" % \
1344
                  (link_name, if_val, boot_val, cache_val)
1345

    
1346
      if device_driver:
1347
        # kvm_disks are the 4th entry of runtime file that did not exist in
1348
        # the past. That means that cfdev should always have pci slot and
1349
        # _GenerateDeviceKVMId() will not raise a exception.
1350
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK,
1351
                                         cfdev, idx)
1352
        drive_val += (",id=%s" % kvm_devid)
1353
        if cfdev.pci:
1354
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1355
        dev_val = ("%s,drive=%s,id=%s" %
1356
                   (device_driver, kvm_devid, kvm_devid))
1357
        if cfdev.pci:
1358
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1359
        dev_opts.extend(["-device", dev_val])
1360

    
1361
      # TODO: export disk geometry in IDISK_PARAMS
1362
      heads = cfdev.params.get('heads', None)
1363
      secs = cfdev.params.get('secs', None)
1364
      if heads and secs:
1365
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1366
        cyls = nr_sectors / (int(heads) * int(secs))
1367
        if cyls > 16383:
1368
          cyls = 16383
1369
        elif cyls < 2:
1370
          cyls = 2
1371
        if cyls and heads and secs:
1372
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1373
                        (cyls, int(heads), int(secs)))
1374

    
1375
      dev_opts.extend(["-drive", drive_val])
1376

    
1377
    return dev_opts
1378

    
1379
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1380
                          kvmhelp):
1381
    """Generate KVM information to start an instance.
1382

1383
    @type kvmhelp: string
1384
    @param kvmhelp: output of kvm --help
1385
    @attention: this function must not have any side-effects; for
1386
        example, it must not write to the filesystem, or read values
1387
        from the current system the are expected to differ between
1388
        nodes, since it is only run once at instance startup;
1389
        actions/kvm arguments that can vary between systems should be
1390
        done in L{_ExecuteKVMRuntime}
1391

1392
    """
1393
    # pylint: disable=R0912,R0914,R0915
1394
    hvp = instance.hvparams
1395
    self.ValidateParameters(hvp)
1396

    
1397
    pidfile = self._InstancePidFile(instance.name)
1398
    kvm = hvp[constants.HV_KVM_PATH]
1399
    kvm_cmd = [kvm]
1400
    # used just by the vnc server, if enabled
1401
    kvm_cmd.extend(["-name", instance.name])
1402
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1403

    
1404
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1405
    if hvp[constants.HV_CPU_CORES]:
1406
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1407
    if hvp[constants.HV_CPU_THREADS]:
1408
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1409
    if hvp[constants.HV_CPU_SOCKETS]:
1410
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1411

    
1412
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1413

    
1414
    kvm_cmd.extend(["-pidfile", pidfile])
1415
    kvm_cmd.extend(["-balloon", "virtio"])
1416
    kvm_cmd.extend(["-daemonize"])
1417
    if not instance.hvparams[constants.HV_ACPI]:
1418
      kvm_cmd.extend(["-no-acpi"])
1419
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1420
        constants.INSTANCE_REBOOT_EXIT:
1421
      kvm_cmd.extend(["-no-reboot"])
1422

    
1423
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1424
    if not mversion:
1425
      mversion = self._GetDefaultMachineVersion(kvm)
1426
    if self._MACHINE_RE.search(kvmhelp):
1427
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1428
      # extra hypervisor parameters. We should also investigate whether and how
1429
      # shadow_mem should be considered for the resource model.
1430
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1431
        specprop = ",accel=kvm"
1432
      else:
1433
        specprop = ""
1434
      machinespec = "%s%s" % (mversion, specprop)
1435
      kvm_cmd.extend(["-machine", machinespec])
1436
    else:
1437
      kvm_cmd.extend(["-M", mversion])
1438
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1439
          self._ENABLE_KVM_RE.search(kvmhelp)):
1440
        kvm_cmd.extend(["-enable-kvm"])
1441
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1442
            self._DISABLE_KVM_RE.search(kvmhelp)):
1443
        kvm_cmd.extend(["-disable-kvm"])
1444

    
1445
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1446
    if kernel_path:
1447
      boot_cdrom = boot_floppy = boot_network = False
1448
    else:
1449
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1450
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1451
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1452

    
1453
    if startup_paused:
1454
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1455

    
1456
    if boot_network:
1457
      kvm_cmd.extend(["-boot", "n"])
1458

    
1459
    # whether this is an older KVM version that uses the boot=on flag
1460
    # on devices
1461
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1462

    
1463
    disk_type = hvp[constants.HV_DISK_TYPE]
1464

    
1465
    #Now we can specify a different device type for CDROM devices.
1466
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1467
    if not cdrom_disk_type:
1468
      cdrom_disk_type = disk_type
1469

    
1470
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1471
    if iso_image:
1472
      options = ",format=raw,media=cdrom"
1473
      # set cdrom 'if' type
1474
      if boot_cdrom:
1475
        actual_cdrom_type = constants.HT_DISK_IDE
1476
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1477
        actual_cdrom_type = "virtio"
1478
      else:
1479
        actual_cdrom_type = cdrom_disk_type
1480
      if_val = ",if=%s" % actual_cdrom_type
1481
      # set boot flag, if needed
1482
      boot_val = ""
1483
      if boot_cdrom:
1484
        kvm_cmd.extend(["-boot", "d"])
1485
        if needs_boot_flag:
1486
          boot_val = ",boot=on"
1487
      # and finally build the entire '-drive' value
1488
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1489
      kvm_cmd.extend(["-drive", drive_val])
1490

    
1491
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1492
    if iso_image2:
1493
      options = ",format=raw,media=cdrom"
1494
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1495
        if_val = ",if=virtio"
1496
      else:
1497
        if_val = ",if=%s" % cdrom_disk_type
1498
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1499
      kvm_cmd.extend(["-drive", drive_val])
1500

    
1501
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1502
    if floppy_image:
1503
      options = ",format=raw,media=disk"
1504
      if boot_floppy:
1505
        kvm_cmd.extend(["-boot", "a"])
1506
        options = "%s,boot=on" % options
1507
      if_val = ",if=floppy"
1508
      options = "%s%s" % (options, if_val)
1509
      drive_val = "file=%s%s" % (floppy_image, options)
1510
      kvm_cmd.extend(["-drive", drive_val])
1511

    
1512
    if kernel_path:
1513
      kvm_cmd.extend(["-kernel", kernel_path])
1514
      initrd_path = hvp[constants.HV_INITRD_PATH]
1515
      if initrd_path:
1516
        kvm_cmd.extend(["-initrd", initrd_path])
1517
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1518
                     hvp[constants.HV_KERNEL_ARGS]]
1519
      if hvp[constants.HV_SERIAL_CONSOLE]:
1520
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1521
        root_append.append("console=ttyS0,%s" % serial_speed)
1522
      kvm_cmd.extend(["-append", " ".join(root_append)])
1523

    
1524
    mem_path = hvp[constants.HV_MEM_PATH]
1525
    if mem_path:
1526
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1527

    
1528
    monitor_dev = ("unix:%s,server,nowait" %
1529
                   self._InstanceMonitor(instance.name))
1530
    kvm_cmd.extend(["-monitor", monitor_dev])
1531
    if hvp[constants.HV_SERIAL_CONSOLE]:
1532
      serial_dev = ("unix:%s,server,nowait" %
1533
                    self._InstanceSerial(instance.name))
1534
      kvm_cmd.extend(["-serial", serial_dev])
1535
    else:
1536
      kvm_cmd.extend(["-serial", "none"])
1537

    
1538
    mouse_type = hvp[constants.HV_USB_MOUSE]
1539
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1540
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1541
    spice_ip_version = None
1542

    
1543
    kvm_cmd.extend(["-usb"])
1544

    
1545
    if mouse_type:
1546
      kvm_cmd.extend(["-usbdevice", mouse_type])
1547
    elif vnc_bind_address:
1548
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1549

    
1550
    if vnc_bind_address:
1551
      if netutils.IP4Address.IsValid(vnc_bind_address):
1552
        if instance.network_port > constants.VNC_BASE_PORT:
1553
          display = instance.network_port - constants.VNC_BASE_PORT
1554
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1555
            vnc_arg = ":%d" % (display)
1556
          else:
1557
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1558
        else:
1559
          logging.error("Network port is not a valid VNC display (%d < %d),"
1560
                        " not starting VNC",
1561
                        instance.network_port, constants.VNC_BASE_PORT)
1562
          vnc_arg = "none"
1563

    
1564
        # Only allow tls and other option when not binding to a file, for now.
1565
        # kvm/qemu gets confused otherwise about the filename to use.
1566
        vnc_append = ""
1567
        if hvp[constants.HV_VNC_TLS]:
1568
          vnc_append = "%s,tls" % vnc_append
1569
          if hvp[constants.HV_VNC_X509_VERIFY]:
1570
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1571
                                               hvp[constants.HV_VNC_X509])
1572
          elif hvp[constants.HV_VNC_X509]:
1573
            vnc_append = "%s,x509=%s" % (vnc_append,
1574
                                         hvp[constants.HV_VNC_X509])
1575
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1576
          vnc_append = "%s,password" % vnc_append
1577

    
1578
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1579

    
1580
      else:
1581
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1582

    
1583
      kvm_cmd.extend(["-vnc", vnc_arg])
1584
    elif spice_bind:
1585
      # FIXME: this is wrong here; the iface ip address differs
1586
      # between systems, so it should be done in _ExecuteKVMRuntime
1587
      if netutils.IsValidInterface(spice_bind):
1588
        # The user specified a network interface, we have to figure out the IP
1589
        # address.
1590
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1591
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1592

    
1593
        # if the user specified an IP version and the interface does not
1594
        # have that kind of IP addresses, throw an exception
1595
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1596
          if not addresses[spice_ip_version]:
1597
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1598
                                         " for %s" % (spice_ip_version,
1599
                                                      spice_bind))
1600

    
1601
        # the user did not specify an IP version, we have to figure it out
1602
        elif (addresses[constants.IP4_VERSION] and
1603
              addresses[constants.IP6_VERSION]):
1604
          # we have both ipv4 and ipv6, let's use the cluster default IP
1605
          # version
1606
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1607
          spice_ip_version = \
1608
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1609
        elif addresses[constants.IP4_VERSION]:
1610
          spice_ip_version = constants.IP4_VERSION
1611
        elif addresses[constants.IP6_VERSION]:
1612
          spice_ip_version = constants.IP6_VERSION
1613
        else:
1614
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1615
                                       " for %s" % (spice_bind))
1616

    
1617
        spice_address = addresses[spice_ip_version][0]
1618

    
1619
      else:
1620
        # spice_bind is known to be a valid IP address, because
1621
        # ValidateParameters checked it.
1622
        spice_address = spice_bind
1623

    
1624
      spice_arg = "addr=%s" % spice_address
1625
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1626
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1627
                     (spice_arg, instance.network_port,
1628
                      pathutils.SPICE_CACERT_FILE))
1629
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1630
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1631
                      pathutils.SPICE_CERT_FILE))
1632
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1633
        if tls_ciphers:
1634
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1635
      else:
1636
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1637

    
1638
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1639
        spice_arg = "%s,disable-ticketing" % spice_arg
1640

    
1641
      if spice_ip_version:
1642
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1643

    
1644
      # Image compression options
1645
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1646
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1647
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1648
      if img_lossless:
1649
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1650
      if img_jpeg:
1651
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1652
      if img_zlib_glz:
1653
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1654

    
1655
      # Video stream detection
1656
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1657
      if video_streaming:
1658
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1659

    
1660
      # Audio compression, by default in qemu-kvm it is on
1661
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1662
        spice_arg = "%s,playback-compression=off" % spice_arg
1663
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1664
        spice_arg = "%s,agent-mouse=off" % spice_arg
1665
      else:
1666
        # Enable the spice agent communication channel between the host and the
1667
        # agent.
1668
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1669
        kvm_cmd.extend([
1670
          "-device",
1671
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1672
          ])
1673
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1674

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

    
1678
    else:
1679
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1680
      # also works in earlier versions though (tested with 1.1 and 1.3)
1681
      if self._DISPLAY_RE.search(kvmhelp):
1682
        kvm_cmd.extend(["-display", "none"])
1683
      else:
1684
        kvm_cmd.extend(["-nographic"])
1685

    
1686
    if hvp[constants.HV_USE_LOCALTIME]:
1687
      kvm_cmd.extend(["-localtime"])
1688

    
1689
    if hvp[constants.HV_KVM_USE_CHROOT]:
1690
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1691

    
1692
    # Add qemu-KVM -cpu param
1693
    if hvp[constants.HV_CPU_TYPE]:
1694
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1695

    
1696
    # As requested by music lovers
1697
    if hvp[constants.HV_SOUNDHW]:
1698
      kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1699

    
1700
    # Pass a -vga option if requested, or if spice is used, for backwards
1701
    # compatibility.
1702
    if hvp[constants.HV_VGA]:
1703
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1704
    elif spice_bind:
1705
      kvm_cmd.extend(["-vga", "qxl"])
1706

    
1707
    # Various types of usb devices, comma separated
1708
    if hvp[constants.HV_USB_DEVICES]:
1709
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1710
        kvm_cmd.extend(["-usbdevice", dev])
1711

    
1712
    if hvp[constants.HV_KVM_EXTRA]:
1713
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1714

    
1715
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1716
    kvm_disks = []
1717
    for disk, link_name in block_devices:
1718
      _UpdatePCISlots(disk, pci_reservations)
1719
      kvm_disks.append((disk, link_name))
1720

    
1721
    kvm_nics = []
1722
    for nic in instance.nics:
1723
      _UpdatePCISlots(nic, pci_reservations)
1724
      kvm_nics.append(nic)
1725

    
1726
    hvparams = hvp
1727

    
1728
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1729

    
1730
  def _WriteKVMRuntime(self, instance_name, data):
1731
    """Write an instance's KVM runtime
1732

1733
    """
1734
    try:
1735
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1736
                      data=data)
1737
    except EnvironmentError, err:
1738
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1739

    
1740
  @classmethod
1741
  def _ReadKVMRuntime(cls, instance_name):
1742
    """Read an instance's KVM runtime
1743

1744
    """
1745
    try:
1746
      file_content = utils.ReadFile(cls._InstanceKVMRuntime(instance_name))
1747
    except EnvironmentError, err:
1748
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1749
    return file_content
1750

    
1751
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1752
    """Save an instance's KVM runtime
1753

1754
    """
1755
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1756

    
1757
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1758
    serialized_disks = [(blk.ToDict(), link)
1759
                            for blk, link in kvm_disks]
1760
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1761
                                      serialized_disks))
1762

    
1763
    self._WriteKVMRuntime(instance.name, serialized_form)
1764

    
1765
  @classmethod
1766
  def _LoadKVMRuntime(cls, instance_name, serialized_runtime=None):
1767
    """Load an instance's KVM runtime
1768

1769
    """
1770
    if not serialized_runtime:
1771
      serialized_runtime = cls._ReadKVMRuntime(instance_name)
1772

    
1773
    return _AnalyzeSerializedRuntime(serialized_runtime)
1774

    
1775
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1776
    """Run the KVM cmd and check for errors
1777

1778
    @type name: string
1779
    @param name: instance name
1780
    @type kvm_cmd: list of strings
1781
    @param kvm_cmd: runcmd input for kvm
1782
    @type tap_fds: list of int
1783
    @param tap_fds: fds of tap devices opened by Ganeti
1784

1785
    """
1786
    try:
1787
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1788
    finally:
1789
      for fd in tap_fds:
1790
        utils_wrapper.CloseFdNoError(fd)
1791

    
1792
    if result.failed:
1793
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1794
                                   (name, result.fail_reason, result.output))
1795
    if not self._InstancePidAlive(name)[2]:
1796
      raise errors.HypervisorError("Failed to start instance %s" % name)
1797

    
1798
  # too many local variables
1799
  # pylint: disable=R0914
1800
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1801
    """Execute a KVM cmd, after completing it with some last minute data.
1802

1803
    @type incoming: tuple of strings
1804
    @param incoming: (target_host_ip, port)
1805
    @type kvmhelp: string
1806
    @param kvmhelp: output of kvm --help
1807

1808
    """
1809
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1810
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1811
    #    have changed since the instance started; only use them if the change
1812
    #    won't affect the inside of the instance (which hasn't been rebooted).
1813
    #  - up_hvp contains the parameters as they were when the instance was
1814
    #    started, plus any new parameter which has been added between ganeti
1815
    #    versions: it is paramount that those default to a value which won't
1816
    #    affect the inside of the instance as well.
1817
    conf_hvp = instance.hvparams
1818
    name = instance.name
1819
    self._CheckDown(name)
1820

    
1821
    temp_files = []
1822

    
1823
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1824
    # the first element of kvm_cmd is always the path to the kvm binary
1825
    kvm_path = kvm_cmd[0]
1826
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1827

    
1828
    # We know it's safe to run as a different user upon migration, so we'll use
1829
    # the latest conf, from conf_hvp.
1830
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1831
    if security_model == constants.HT_SM_USER:
1832
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1833

    
1834
    keymap = conf_hvp[constants.HV_KEYMAP]
1835
    if keymap:
1836
      keymap_path = self._InstanceKeymapFile(name)
1837
      # If a keymap file is specified, KVM won't use its internal defaults. By
1838
      # first including the "en-us" layout, an error on loading the actual
1839
      # layout (e.g. because it can't be found) won't lead to a non-functional
1840
      # keyboard. A keyboard with incorrect keys is still better than none.
1841
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1842
      kvm_cmd.extend(["-k", keymap_path])
1843

    
1844
    # We have reasons to believe changing something like the nic driver/type
1845
    # upon migration won't exactly fly with the instance kernel, so for nic
1846
    # related parameters we'll use up_hvp
1847
    tapfds = []
1848
    taps = []
1849
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1850

    
1851
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1852
                                                     kvm_disks,
1853
                                                     kvmhelp,
1854
                                                     devlist)
1855
    kvm_cmd.extend(bdev_opts)
1856

    
1857
    if not kvm_nics:
1858
      kvm_cmd.extend(["-net", "none"])
1859
    else:
1860
      vnet_hdr = False
1861
      tap_extra = ""
1862
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1863
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1864
        nic_model = self._VIRTIO
1865
        try:
1866
          if self._VIRTIO_NET_RE.search(devlist):
1867
            nic_model = self._VIRTIO_NET_PCI
1868
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1869
        except errors.HypervisorError, _:
1870
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1871
          # have new virtio syntax either.
1872
          pass
1873

    
1874
        if up_hvp[constants.HV_VHOST_NET]:
1875
          # check for vhost_net support
1876
          if self._VHOST_RE.search(kvmhelp):
1877
            tap_extra = ",vhost=on"
1878
          else:
1879
            raise errors.HypervisorError("vhost_net is configured"
1880
                                         " but it is not available")
1881
      else:
1882
        nic_model = nic_type
1883

    
1884
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1885

    
1886
      for nic_seq, nic in enumerate(kvm_nics):
1887
        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1888
        tapfds.append(tapfd)
1889
        taps.append(tapname)
1890
        if kvm_supports_netdev:
1891
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1892
          try:
1893
            # kvm_nics already exist in old runtime files and thus there might
1894
            # be some entries without pci slot (therefore try: except:)
1895
            kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1896
            netdev = kvm_devid
1897
            nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1898
          except errors.HotplugError:
1899
            netdev = "netdev%d" % nic_seq
1900
          nic_val += (",netdev=%s" % netdev)
1901
          tap_val = ("type=tap,id=%s,fd=%d%s" %
1902
                     (netdev, tapfd, tap_extra))
1903
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1904
        else:
1905
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1906
                                                         nic.mac, nic_model)
1907
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1908
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1909

    
1910
    if incoming:
1911
      target, port = incoming
1912
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1913

    
1914
    # Changing the vnc password doesn't bother the guest that much. At most it
1915
    # will surprise people who connect to it. Whether positively or negatively
1916
    # it's debatable.
1917
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1918
    vnc_pwd = None
1919
    if vnc_pwd_file:
1920
      try:
1921
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1922
      except EnvironmentError, err:
1923
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1924
                                     % (vnc_pwd_file, err))
1925

    
1926
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1927
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1928
                         constants.SECURE_DIR_MODE)])
1929

    
1930
    # Automatically enable QMP if version is >= 0.14
1931
    if self._QMP_RE.search(kvmhelp):
1932
      logging.debug("Enabling QMP")
1933
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1934
                      self._InstanceQmpMonitor(instance.name)])
1935

    
1936
    # Configure the network now for starting instances and bridged interfaces,
1937
    # during FinalizeMigration for incoming instances' routed interfaces
1938
    for nic_seq, nic in enumerate(kvm_nics):
1939
      if (incoming and
1940
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1941
        continue
1942
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1943

    
1944
    # CPU affinity requires kvm to start paused, so we set this flag if the
1945
    # instance is not already paused and if we are not going to accept a
1946
    # migrating instance. In the latter case, pausing is not needed.
1947
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1948
    if start_kvm_paused:
1949
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1950

    
1951
    # Note: CPU pinning is using up_hvp since changes take effect
1952
    # during instance startup anyway, and to avoid problems when soft
1953
    # rebooting the instance.
1954
    cpu_pinning = False
1955
    if up_hvp.get(constants.HV_CPU_MASK, None):
1956
      cpu_pinning = True
1957

    
1958
    if security_model == constants.HT_SM_POOL:
1959
      ss = ssconf.SimpleStore()
1960
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1961
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1962
      uid = uidpool.RequestUnusedUid(all_uids)
1963
      try:
1964
        username = pwd.getpwuid(uid.GetUid()).pw_name
1965
        kvm_cmd.extend(["-runas", username])
1966
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1967
      except:
1968
        uidpool.ReleaseUid(uid)
1969
        raise
1970
      else:
1971
        uid.Unlock()
1972
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1973
    else:
1974
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1975

    
1976
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1977
                     constants.RUN_DIRS_MODE)])
1978
    for nic_seq, tap in enumerate(taps):
1979
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1980
                      data=tap)
1981

    
1982
    if vnc_pwd:
1983
      change_cmd = "change vnc password %s" % vnc_pwd
1984
      self._CallMonitorCommand(instance.name, change_cmd)
1985

    
1986
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1987
    # connection attempts because SPICE by default does not allow connections
1988
    # if neither a password nor the "disable_ticketing" options are specified.
1989
    # As soon as we send the password via QMP, that password is a valid ticket
1990
    # for connection.
1991
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1992
    if spice_password_file:
1993
      spice_pwd = ""
1994
      try:
1995
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1996
      except EnvironmentError, err:
1997
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1998
                                     % (spice_password_file, err))
1999

    
2000
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2001
      qmp.connect()
2002
      arguments = {
2003
          "protocol": "spice",
2004
          "password": spice_pwd,
2005
      }
2006
      qmp.Execute("set_password", arguments)
2007

    
2008
    for filename in temp_files:
2009
      utils.RemoveFile(filename)
2010

    
2011
    # If requested, set CPU affinity and resume instance execution
2012
    if cpu_pinning:
2013
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2014

    
2015
    start_memory = self._InstanceStartupMemory(instance)
2016
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
2017
      self.BalloonInstanceMemory(instance, start_memory)
2018

    
2019
    if start_kvm_paused:
2020
      # To control CPU pinning, ballooning, and vnc/spice passwords
2021
      # the VM was started in a frozen state. If freezing was not
2022
      # explicitly requested resume the vm status.
2023
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2024

    
2025
  def StartInstance(self, instance, block_devices, startup_paused):
2026
    """Start an instance.
2027

2028
    """
2029
    self._CheckDown(instance.name)
2030
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2031
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2032
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2033
                                           startup_paused, kvmhelp)
2034
    self._SaveKVMRuntime(instance, kvm_runtime)
2035
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2036

    
2037
  def _CallMonitorCommand(self, instance_name, command, timeout=None):
2038
    """Invoke a command on the instance monitor.
2039

2040
    """
2041
    if timeout is not None:
2042
      timeout_cmd = "timeout %s" % (timeout, )
2043
    else:
2044
      timeout_cmd = ""
2045

    
2046
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2047
    # version. The monitor protocol is designed for human consumption, whereas
2048
    # QMP is made for programmatic usage. In the worst case QMP can also
2049
    # execute monitor commands. As it is, all calls to socat take at least
2050
    # 500ms and likely more: socat can't detect the end of the reply and waits
2051
    # for 500ms of no data received before exiting (500 ms is the default for
2052
    # the "-t" parameter).
2053
    socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2054
             (utils.ShellQuote(command),
2055
              timeout_cmd,
2056
              constants.SOCAT_PATH,
2057
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2058

    
2059
    result = utils.RunCmd(socat)
2060
    if result.failed:
2061
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2062
             " output: %s" %
2063
             (command, instance_name, result.fail_reason, result.output))
2064
      raise errors.HypervisorError(msg)
2065

    
2066
    return result
2067

    
2068
  def _GetFreePCISlot(self, instance, dev):
2069
    """Get the first available pci slot of a runnung instance.
2070

2071
    """
2072
    slots = bitarray(32)
2073
    slots.setall(False) # pylint: disable=E1101
2074
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2075
    for line in output.stdout.splitlines():
2076
      match = self._INFO_PCI_RE.search(line)
2077
      if match:
2078
        slot = int(match.group(1))
2079
        slots[slot] = True
2080

    
2081
    [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
2082
    if not free:
2083
      raise errors.HypervisorError("All PCI slots occupied")
2084

    
2085
    dev.pci = int(free)
2086

    
2087
  def VerifyHotplugSupport(self, instance, action, dev_type):
2088
    """Verifies that hotplug is supported.
2089

2090
    Hotplug is *not* supported in case of:
2091
     - security models and chroot (disk hotplug)
2092
     - fdsend module is missing (nic hot-add)
2093

2094
    @raise errors.HypervisorError: in one of the previous cases
2095

2096
    """
2097
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2098
      hvp = instance.hvparams
2099
      security_model = hvp[constants.HV_SECURITY_MODEL]
2100
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2101
      if use_chroot:
2102
        raise errors.HotplugError("Disk hotplug is not supported"
2103
                                  " in case of chroot.")
2104
      if security_model != constants.HT_SM_NONE:
2105
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2106
                                  " security models are used.")
2107

    
2108
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2109
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2110
      raise errors.HotplugError("Cannot hot-add NIC."
2111
                                " fdsend python module is missing.")
2112

    
2113
  def HotplugSupported(self, instance):
2114
    """Checks if hotplug is generally supported.
2115

2116
    Hotplug is *not* supported in case of:
2117
     - qemu versions < 1.0
2118
     - for stopped instances
2119

2120
    @raise errors.HypervisorError: in one of the previous cases
2121

2122
    """
2123
    try:
2124
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2125
    except errors.HypervisorError:
2126
      raise errors.HotplugError("Instance is probably down")
2127

    
2128
    # TODO: search for netdev_add, drive_add, device_add.....
2129
    match = self._INFO_VERSION_RE.search(output.stdout)
2130
    if not match:
2131
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2132

    
2133
    v_major, v_min, _, _ = match.groups()
2134
    if (int(v_major), int(v_min)) < (1, 0):
2135
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2136

    
2137
  def _CallHotplugCommands(self, name, cmds):
2138
    for c in cmds:
2139
      self._CallMonitorCommand(name, c)
2140
      time.sleep(1)
2141

    
2142
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2143
                            should_exist):
2144
    """Checks if a previous hotplug command has succeeded.
2145

2146
    It issues info pci monitor command and checks depending on should_exist
2147
    value if an entry with PCI slot and device ID is found or not.
2148

2149
    @raise errors.HypervisorError: if result is not the expected one
2150

2151
    """
2152
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2153
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2154
    match = \
2155
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2156
    if match and not should_exist:
2157
      msg = "Device %s should have been removed but is still there" % kvm_devid
2158
      raise errors.HypervisorError(msg)
2159

    
2160
    if not match and should_exist:
2161
      msg = "Device %s should have been added but is missing" % kvm_devid
2162
      raise errors.HypervisorError(msg)
2163

    
2164
    logging.info("Device %s has been correctly hot-plugged", kvm_devid)
2165

    
2166
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2167
    """ Helper method to hot-add a new device
2168

2169
    It gets free pci slot generates the device name and invokes the
2170
    device specific method.
2171

2172
    """
2173
    # in case of hot-mod this is given
2174
    if device.pci is None:
2175
      self._GetFreePCISlot(instance, device)
2176
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2177
    runtime = self._LoadKVMRuntime(instance.name)
2178
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2179
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2180
                (extra, kvm_devid)]
2181
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2182
                (hex(device.pci), kvm_devid, kvm_devid)]
2183
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2184
      (tap, fd) = _OpenTap()
2185
      self._ConfigureNIC(instance, seq, device, tap)
2186
      self._PassTapFd(instance, fd, device)
2187
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2188
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2189
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2190
      cmds += ["device_add %s" % args]
2191
      utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2192

    
2193
    self._CallHotplugCommands(instance.name, cmds)
2194
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2195
    # update relevant entries in runtime file
2196
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2197
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2198
    runtime[index].append(entry)
2199
    self._SaveKVMRuntime(instance, runtime)
2200

    
2201
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2202
    """ Helper method for hot-del device
2203

2204
    It gets device info from runtime file, generates the device name and
2205
    invokes the device specific method.
2206

2207
    """
2208
    runtime = self._LoadKVMRuntime(instance.name)
2209
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2210
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2211
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2212
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2213
      cmds = ["device_del %s" % kvm_devid]
2214
      cmds += ["drive_del %s" % kvm_devid]
2215
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2216
      cmds = ["device_del %s" % kvm_devid]
2217
      cmds += ["netdev_del %s" % kvm_devid]
2218
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2219
    self._CallHotplugCommands(instance.name, cmds)
2220
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2221
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2222
    runtime[index].remove(entry)
2223
    self._SaveKVMRuntime(instance, runtime)
2224

    
2225
    return kvm_device.pci
2226

    
2227
  def HotModDevice(self, instance, dev_type, device, _, seq):
2228
    """ Helper method for hot-mod device
2229

2230
    It gets device info from runtime file, generates the device name and
2231
    invokes the device specific method. Currently only NICs support hot-mod
2232

2233
    """
2234
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2235
      # putting it back in the same pci slot
2236
      device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2237
      # TODO: remove sleep when socat gets removed
2238
      self.HotAddDevice(instance, dev_type, device, _, seq)
2239

    
2240
  def _PassTapFd(self, instance, fd, nic):
2241
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2242

2243
    """
2244
    # TODO: factor out code related to unix sockets.
2245
    #       squash common parts between monitor and qmp
2246
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2247
    command = "getfd %s\n" % kvm_devid
2248
    fds = [fd]
2249
    logging.info("%s", fds)
2250
    try:
2251
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2252
      monsock.connect()
2253
      fdsend.sendfds(monsock.sock, command, fds=fds)
2254
    finally:
2255
      monsock.close()
2256

    
2257
  @classmethod
2258
  def _ParseKVMVersion(cls, text):
2259
    """Parse the KVM version from the --help output.
2260

2261
    @type text: string
2262
    @param text: output of kvm --help
2263
    @return: (version, v_maj, v_min, v_rev)
2264
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2265

2266
    """
2267
    match = cls._VERSION_RE.search(text.splitlines()[0])
2268
    if not match:
2269
      raise errors.HypervisorError("Unable to get KVM version")
2270

    
2271
    v_all = match.group(0)
2272
    v_maj = int(match.group(1))
2273
    v_min = int(match.group(2))
2274
    if match.group(4):
2275
      v_rev = int(match.group(4))
2276
    else:
2277
      v_rev = 0
2278
    return (v_all, v_maj, v_min, v_rev)
2279

    
2280
  @classmethod
2281
  def _GetKVMOutput(cls, kvm_path, option):
2282
    """Return the output of a kvm invocation
2283

2284
    @type kvm_path: string
2285
    @param kvm_path: path to the kvm executable
2286
    @type option: a key of _KVMOPTS_CMDS
2287
    @param option: kvm option to fetch the output from
2288
    @return: output a supported kvm invocation
2289
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2290

2291
    """
2292
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2293

    
2294
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2295

    
2296
    result = utils.RunCmd([kvm_path] + optlist)
2297
    if result.failed and not can_fail:
2298
      raise errors.HypervisorError("Unable to get KVM %s output" %
2299
                                    " ".join(optlist))
2300
    return result.output
2301

    
2302
  @classmethod
2303
  def _GetKVMVersion(cls, kvm_path):
2304
    """Return the installed KVM version.
2305

2306
    @return: (version, v_maj, v_min, v_rev)
2307
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2308

2309
    """
2310
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2311

    
2312
  @classmethod
2313
  def _GetDefaultMachineVersion(cls, kvm_path):
2314
    """Return the default hardware revision (e.g. pc-1.1)
2315

2316
    """
2317
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2318
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2319
    if match:
2320
      return match.group(1)
2321
    else:
2322
      return "pc"
2323

    
2324
  def StopInstance(self, instance, force=False, retry=False, name=None,
2325
                   timeout=None):
2326
    """Stop an instance.
2327

2328
    """
2329
    assert(timeout is None or force is not None)
2330

    
2331
    if name is not None and not force:
2332
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2333
    if name is None:
2334
      name = instance.name
2335
      acpi = instance.hvparams[constants.HV_ACPI]
2336
    else:
2337
      acpi = False
2338
    _, pid, alive = self._InstancePidAlive(name)
2339
    if pid > 0 and alive:
2340
      if force or not acpi:
2341
        utils.KillProcess(pid)
2342
      else:
2343
        self._CallMonitorCommand(name, "system_powerdown", timeout)
2344

    
2345
  def CleanupInstance(self, instance_name):
2346
    """Cleanup after a stopped instance
2347

2348
    """
2349
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2350
    if pid > 0 and alive:
2351
      raise errors.HypervisorError("Cannot cleanup a live instance")
2352
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2353

    
2354
  def RebootInstance(self, instance):
2355
    """Reboot an instance.
2356

2357
    """
2358
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2359
    # socket the instance will stop, but now power up again. So we'll resort
2360
    # to shutdown and restart.
2361
    _, _, alive = self._InstancePidAlive(instance.name)
2362
    if not alive:
2363
      raise errors.HypervisorError("Failed to reboot instance %s:"
2364
                                   " not running" % instance.name)
2365
    # StopInstance will delete the saved KVM runtime so:
2366
    # ...first load it...
2367
    kvm_runtime = self._LoadKVMRuntime(instance.name)
2368
    # ...now we can safely call StopInstance...
2369
    if not self.StopInstance(instance):
2370
      self.StopInstance(instance, force=True)
2371
    # ...and finally we can save it again, and execute it...
2372
    self._SaveKVMRuntime(instance, kvm_runtime)
2373
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2374
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2375
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2376

    
2377
  def MigrationInfo(self, instance):
2378
    """Get instance information to perform a migration.
2379

2380
    @type instance: L{objects.Instance}
2381
    @param instance: instance to be migrated
2382
    @rtype: string
2383
    @return: content of the KVM runtime file
2384

2385
    """
2386
    return self._ReadKVMRuntime(instance.name)
2387

    
2388
  def AcceptInstance(self, instance, info, target):
2389
    """Prepare to accept an instance.
2390

2391
    @type instance: L{objects.Instance}
2392
    @param instance: instance to be accepted
2393
    @type info: string
2394
    @param info: content of the KVM runtime file on the source node
2395
    @type target: string
2396
    @param target: target host (usually ip), on this node
2397

2398
    """
2399
    kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2400
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2401
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2402
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2403
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2404
                            incoming=incoming_address)
2405

    
2406
  def FinalizeMigrationDst(self, instance, info, success):
2407
    """Finalize the instance migration on the target node.
2408

2409
    Stop the incoming mode KVM.
2410

2411
    @type instance: L{objects.Instance}
2412
    @param instance: instance whose migration is being finalized
2413

2414
    """
2415
    if success:
2416
      kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2417
      kvm_nics = kvm_runtime[1]
2418

    
2419
      for nic_seq, nic in enumerate(kvm_nics):
2420
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2421
          # Bridged interfaces have already been configured
2422
          continue
2423
        try:
2424
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2425
        except EnvironmentError, err:
2426
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2427
                          instance.name, nic_seq, str(err))
2428
          continue
2429
        try:
2430
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2431
        except errors.HypervisorError, err:
2432
          logging.warning(str(err))
2433

    
2434
      self._WriteKVMRuntime(instance.name, info)
2435
    else:
2436
      self.StopInstance(instance, force=True)
2437

    
2438
  def MigrateInstance(self, instance, target, live):
2439
    """Migrate an instance to a target node.
2440

2441
    The migration will not be attempted if the instance is not
2442
    currently running.
2443

2444
    @type instance: L{objects.Instance}
2445
    @param instance: the instance to be migrated
2446
    @type target: string
2447
    @param target: ip address of the target node
2448
    @type live: boolean
2449
    @param live: perform a live migration
2450

2451
    """
2452
    instance_name = instance.name
2453
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2454
    _, _, alive = self._InstancePidAlive(instance_name)
2455
    if not alive:
2456
      raise errors.HypervisorError("Instance not running, cannot migrate")
2457

    
2458
    if not live:
2459
      self._CallMonitorCommand(instance_name, "stop")
2460

    
2461
    migrate_command = ("migrate_set_speed %dm" %
2462
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2463
    self._CallMonitorCommand(instance_name, migrate_command)
2464

    
2465
    migrate_command = ("migrate_set_downtime %dms" %
2466
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2467
    self._CallMonitorCommand(instance_name, migrate_command)
2468

    
2469
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2470
    self._CallMonitorCommand(instance_name, migrate_command)
2471

    
2472
  def FinalizeMigrationSource(self, instance, success, live):
2473
    """Finalize the instance migration on the source node.
2474

2475
    @type instance: L{objects.Instance}
2476
    @param instance: the instance that was migrated
2477
    @type success: bool
2478
    @param success: whether the migration succeeded or not
2479
    @type live: bool
2480
    @param live: whether the user requested a live migration or not
2481

2482
    """
2483
    if success:
2484
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2485
      utils.KillProcess(pid)
2486
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2487
    elif live:
2488
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2489

    
2490
  def GetMigrationStatus(self, instance):
2491
    """Get the migration status
2492

2493
    @type instance: L{objects.Instance}
2494
    @param instance: the instance that is being migrated
2495
    @rtype: L{objects.MigrationStatus}
2496
    @return: the status of the current migration (one of
2497
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2498
             progress info that can be retrieved from the hypervisor
2499

2500
    """
2501
    info_command = "info migrate"
2502
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2503
      result = self._CallMonitorCommand(instance.name, info_command)
2504
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2505
      if not match:
2506
        if not result.stdout:
2507
          logging.info("KVM: empty 'info migrate' result")
2508
        else:
2509
          logging.warning("KVM: unknown 'info migrate' result: %s",
2510
                          result.stdout)
2511
      else:
2512
        status = match.group(1)
2513
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2514
          migration_status = objects.MigrationStatus(status=status)
2515
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2516
          if match:
2517
            migration_status.transferred_ram = match.group("transferred")
2518
            migration_status.total_ram = match.group("total")
2519

    
2520
          return migration_status
2521

    
2522
        logging.warning("KVM: unknown migration status '%s'", status)
2523

    
2524
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2525

    
2526
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2527

    
2528
  def BalloonInstanceMemory(self, instance, mem):
2529
    """Balloon an instance memory to a certain value.
2530

2531
    @type instance: L{objects.Instance}
2532
    @param instance: instance to be accepted
2533
    @type mem: int
2534
    @param mem: actual memory size to use for instance runtime
2535

2536
    """
2537
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2538

    
2539
  def GetNodeInfo(self):
2540
    """Return information about the node.
2541

2542
    @return: a dict with the following keys (values in MiB):
2543
          - memory_total: the total memory size on the node
2544
          - memory_free: the available memory on the node for instances
2545
          - memory_dom0: the memory used by the node itself, if available
2546
          - hv_version: the hypervisor version in the form (major, minor,
2547
                        revision)
2548

2549
    """
2550
    result = self.GetLinuxNodeInfo()
2551
    # FIXME: this is the global kvm version, but the actual version can be
2552
    # customized as an hv parameter. we should use the nodegroup's default kvm
2553
    # path parameter here.
2554
    _, v_major, v_min, v_rev = self._GetKVMVersion(constants.KVM_PATH)
2555
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2556
    return result
2557

    
2558
  @classmethod
2559
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2560
    """Return a command for connecting to the console of an instance.
2561

2562
    """
2563
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2564
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2565
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2566
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2567
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2568
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2569
      return objects.InstanceConsole(instance=instance.name,
2570
                                     kind=constants.CONS_SSH,
2571
                                     host=instance.primary_node,
2572
                                     user=constants.SSH_CONSOLE_USER,
2573
                                     command=cmd)
2574

    
2575
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2576
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2577
      display = instance.network_port - constants.VNC_BASE_PORT
2578
      return objects.InstanceConsole(instance=instance.name,
2579
                                     kind=constants.CONS_VNC,
2580
                                     host=vnc_bind_address,
2581
                                     port=instance.network_port,
2582
                                     display=display)
2583

    
2584
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2585
    if spice_bind:
2586
      return objects.InstanceConsole(instance=instance.name,
2587
                                     kind=constants.CONS_SPICE,
2588
                                     host=spice_bind,
2589
                                     port=instance.network_port)
2590

    
2591
    return objects.InstanceConsole(instance=instance.name,
2592
                                   kind=constants.CONS_MESSAGE,
2593
                                   message=("No serial shell for instance %s" %
2594
                                            instance.name))
2595

    
2596
  def Verify(self):
2597
    """Verify the hypervisor.
2598

2599
    Check that the required binaries exist.
2600

2601
    @return: Problem description if something is wrong, C{None} otherwise
2602

2603
    """
2604
    msgs = []
2605
    # FIXME: this is the global kvm binary, but the actual path can be
2606
    # customized as an hv parameter; we should use the nodegroup's
2607
    # default kvm path parameter here.
2608
    if not os.path.exists(constants.KVM_PATH):
2609
      msgs.append("The KVM binary ('%s') does not exist" % constants.KVM_PATH)
2610
    if not os.path.exists(constants.SOCAT_PATH):
2611
      msgs.append("The socat binary ('%s') does not exist" %
2612
                  constants.SOCAT_PATH)
2613

    
2614
    return self._FormatVerifyResults(msgs)
2615

    
2616
  @classmethod
2617
  def CheckParameterSyntax(cls, hvparams):
2618
    """Check the given parameters for validity.
2619

2620
    @type hvparams:  dict
2621
    @param hvparams: dictionary with parameter names/value
2622
    @raise errors.HypervisorError: when a parameter is not valid
2623

2624
    """
2625
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2626

    
2627
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2628
    if kernel_path:
2629
      if not hvparams[constants.HV_ROOT_PATH]:
2630
        raise errors.HypervisorError("Need a root partition for the instance,"
2631
                                     " if a kernel is defined")
2632

    
2633
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2634
        not hvparams[constants.HV_VNC_X509]):
2635
      raise errors.HypervisorError("%s must be defined, if %s is" %
2636
                                   (constants.HV_VNC_X509,
2637
                                    constants.HV_VNC_X509_VERIFY))
2638

    
2639
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2640
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2641
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2642
      if not serial_speed or serial_speed not in valid_speeds:
2643
        raise errors.HypervisorError("Invalid serial console speed, must be"
2644
                                     " one of: %s" %
2645
                                     utils.CommaJoin(valid_speeds))
2646

    
2647
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2648
    if (boot_order == constants.HT_BO_CDROM and
2649
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2650
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2651
                                   " ISO path")
2652

    
2653
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2654
    if security_model == constants.HT_SM_USER:
2655
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2656
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2657
                                     " must be specified")
2658
    elif (security_model == constants.HT_SM_NONE or
2659
          security_model == constants.HT_SM_POOL):
2660
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2661
        raise errors.HypervisorError("Cannot have a security domain when the"
2662
                                     " security model is 'none' or 'pool'")
2663

    
2664
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2665
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2666
    if spice_bind:
2667
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2668
        # if an IP version is specified, the spice_bind parameter must be an
2669
        # IP of that family
2670
        if (netutils.IP4Address.IsValid(spice_bind) and
2671
            spice_ip_version != constants.IP4_VERSION):
2672
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2673
                                       " the specified IP version is %s" %
2674
                                       (spice_bind, spice_ip_version))
2675

    
2676
        if (netutils.IP6Address.IsValid(spice_bind) and
2677
            spice_ip_version != constants.IP6_VERSION):
2678
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2679
                                       " the specified IP version is %s" %
2680
                                       (spice_bind, spice_ip_version))
2681
    else:
2682
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2683
      # error if any of them is set without it.
2684
      for param in _SPICE_ADDITIONAL_PARAMS:
2685
        if hvparams[param]:
2686
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2687
                                       (param, constants.HV_KVM_SPICE_BIND))
2688

    
2689
  @classmethod
2690
  def ValidateParameters(cls, hvparams):
2691
    """Check the given parameters for validity.
2692

2693
    @type hvparams:  dict
2694
    @param hvparams: dictionary with parameter names/value
2695
    @raise errors.HypervisorError: when a parameter is not valid
2696

2697
    """
2698
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2699

    
2700
    kvm_path = hvparams[constants.HV_KVM_PATH]
2701

    
2702
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2703
    if security_model == constants.HT_SM_USER:
2704
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2705
      try:
2706
        pwd.getpwnam(username)
2707
      except KeyError:
2708
        raise errors.HypervisorError("Unknown security domain user %s"
2709
                                     % username)
2710

    
2711
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2712
    if spice_bind:
2713
      # only one of VNC and SPICE can be used currently.
2714
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2715
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2716
                                     " only one of them can be used at a"
2717
                                     " given time")
2718

    
2719
      # check that KVM supports SPICE
2720
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2721
      if not cls._SPICE_RE.search(kvmhelp):
2722
        raise errors.HypervisorError("SPICE is configured, but it is not"
2723
                                     " supported according to 'kvm --help'")
2724

    
2725
      # if spice_bind is not an IP address, it must be a valid interface
2726
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2727
                       netutils.IP6Address.IsValid(spice_bind))
2728
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2729
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2730
                                     " a valid IP address or interface name" %
2731
                                     constants.HV_KVM_SPICE_BIND)
2732

    
2733
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2734
    if machine_version:
2735
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2736
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2737
        raise errors.HypervisorError("Unsupported machine version: %s" %
2738
                                     machine_version)
2739

    
2740
  @classmethod
2741
  def PowercycleNode(cls):
2742
    """KVM powercycle, just a wrapper over Linux powercycle.
2743

2744
    """
2745
    cls.LinuxPowercycle()