Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ cc3a2cd9

History | View | Annotate | Download (99.4 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
  def _ReadKVMRuntime(self, instance_name):
1741
    """Read an instance's KVM runtime
1742

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

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

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

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

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

    
1764
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1765
    """Load an instance's KVM runtime
1766

1767
    """
1768
    if not serialized_runtime:
1769
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1770

    
1771
    return _AnalyzeSerializedRuntime(serialized_runtime)
1772

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

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

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

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

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

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

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

    
1819
    temp_files = []
1820

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

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

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

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

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

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

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

    
1882
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1883

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2006
    for filename in temp_files:
2007
      utils.RemoveFile(filename)
2008

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

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

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

    
2023
  def StartInstance(self, instance, block_devices, startup_paused):
2024
    """Start an instance.
2025

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

    
2035
  def _CallMonitorCommand(self, instance_name, command):
2036
    """Invoke a command on the instance monitor.
2037

2038
    """
2039
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2040
    # version. The monitor protocol is designed for human consumption, whereas
2041
    # QMP is made for programmatic usage. In the worst case QMP can also
2042
    # execute monitor commands. As it is, all calls to socat take at least
2043
    # 500ms and likely more: socat can't detect the end of the reply and waits
2044
    # for 500ms of no data received before exiting (500 ms is the default for
2045
    # the "-t" parameter).
2046
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
2047
             (utils.ShellQuote(command),
2048
              constants.SOCAT_PATH,
2049
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2050
    result = utils.RunCmd(socat)
2051
    if result.failed:
2052
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2053
             " output: %s" %
2054
             (command, instance_name, result.fail_reason, result.output))
2055
      raise errors.HypervisorError(msg)
2056

    
2057
    return result
2058

    
2059
  def _GetFreePCISlot(self, instance, dev):
2060
    """Get the first available pci slot of a runnung instance.
2061

2062
    """
2063
    slots = bitarray(32)
2064
    slots.setall(False) # pylint: disable=E1101
2065
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2066
    for line in output.stdout.splitlines():
2067
      match = self._INFO_PCI_RE.search(line)
2068
      if match:
2069
        slot = int(match.group(1))
2070
        slots[slot] = True
2071

    
2072
    [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
2073
    if not free:
2074
      raise errors.HypervisorError("All PCI slots occupied")
2075

    
2076
    dev.pci = int(free)
2077

    
2078
  def VerifyHotplugSupport(self, instance, action, dev_type):
2079
    """Verifies that hotplug is supported.
2080

2081
    Hotplug is *not* supported in case of:
2082
     - security models and chroot (disk hotplug)
2083
     - fdsend module is missing (nic hot-add)
2084

2085
    @raise errors.HypervisorError: in one of the previous cases
2086

2087
    """
2088
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2089
      hvp = instance.hvparams
2090
      security_model = hvp[constants.HV_SECURITY_MODEL]
2091
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2092
      if use_chroot:
2093
        raise errors.HotplugError("Disk hotplug is not supported"
2094
                                  " in case of chroot.")
2095
      if security_model != constants.HT_SM_NONE:
2096
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2097
                                  " security models are used.")
2098

    
2099
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2100
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2101
      raise errors.HotplugError("Cannot hot-add NIC."
2102
                                " fdsend python module is missing.")
2103

    
2104
  def HotplugSupported(self, instance):
2105
    """Checks if hotplug is generally supported.
2106

2107
    Hotplug is *not* supported in case of:
2108
     - qemu versions < 1.0
2109
     - for stopped instances
2110

2111
    @raise errors.HypervisorError: in one of the previous cases
2112

2113
    """
2114
    try:
2115
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2116
    except errors.HypervisorError:
2117
      raise errors.HotplugError("Instance is probably down")
2118

    
2119
    # TODO: search for netdev_add, drive_add, device_add.....
2120
    match = self._INFO_VERSION_RE.search(output.stdout)
2121
    if not match:
2122
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2123

    
2124
    v_major, v_min, _, _ = match.groups()
2125
    if (int(v_major), int(v_min)) < (1, 0):
2126
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2127

    
2128
  def _CallHotplugCommands(self, name, cmds):
2129
    for c in cmds:
2130
      self._CallMonitorCommand(name, c)
2131
      time.sleep(1)
2132

    
2133
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2134
                            should_exist):
2135
    """Checks if a previous hotplug command has succeeded.
2136

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

2140
    @raise errors.HypervisorError: if result is not the expected one
2141

2142
    """
2143
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2144
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2145
    match = \
2146
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2147
    if match and not should_exist:
2148
      msg = "Device %s should have been removed but is still there" % kvm_devid
2149
      raise errors.HypervisorError(msg)
2150

    
2151
    if not match and should_exist:
2152
      msg = "Device %s should have been added but is missing" % kvm_devid
2153
      raise errors.HypervisorError(msg)
2154

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

    
2157
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2158
    """ Helper method to hot-add a new device
2159

2160
    It gets free pci slot generates the device name and invokes the
2161
    device specific method.
2162

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

    
2184
    self._CallHotplugCommands(instance.name, cmds)
2185
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2186
    # update relevant entries in runtime file
2187
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2188
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2189
    runtime[index].append(entry)
2190
    self._SaveKVMRuntime(instance, runtime)
2191

    
2192
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2193
    """ Helper method for hot-del device
2194

2195
    It gets device info from runtime file, generates the device name and
2196
    invokes the device specific method.
2197

2198
    """
2199
    runtime = self._LoadKVMRuntime(instance)
2200
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2201
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2202
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2203
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2204
      cmds = ["device_del %s" % kvm_devid]
2205
      cmds += ["drive_del %s" % kvm_devid]
2206
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2207
      cmds = ["device_del %s" % kvm_devid]
2208
      cmds += ["netdev_del %s" % kvm_devid]
2209
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2210
    self._CallHotplugCommands(instance.name, cmds)
2211
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2212
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2213
    runtime[index].remove(entry)
2214
    self._SaveKVMRuntime(instance, runtime)
2215

    
2216
    return kvm_device.pci
2217

    
2218
  def HotModDevice(self, instance, dev_type, device, _, seq):
2219
    """ Helper method for hot-mod device
2220

2221
    It gets device info from runtime file, generates the device name and
2222
    invokes the device specific method. Currently only NICs support hot-mod
2223

2224
    """
2225
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2226
      # putting it back in the same pci slot
2227
      try:
2228
        device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2229
      except errors.HotplugError:
2230
        logging.info("Device not found in runtime file. Assuming it was"
2231
                     " previously added without --hotplug option.")
2232
      # TODO: remove sleep when socat gets removed
2233
      self.HotAddDevice(instance, dev_type, device, _, seq)
2234

    
2235
  def _PassTapFd(self, instance, fd, nic):
2236
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2237

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

    
2252
  @classmethod
2253
  def _ParseKVMVersion(cls, text):
2254
    """Parse the KVM version from the --help output.
2255

2256
    @type text: string
2257
    @param text: output of kvm --help
2258
    @return: (version, v_maj, v_min, v_rev)
2259
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2260

2261
    """
2262
    match = cls._VERSION_RE.search(text.splitlines()[0])
2263
    if not match:
2264
      raise errors.HypervisorError("Unable to get KVM version")
2265

    
2266
    v_all = match.group(0)
2267
    v_maj = int(match.group(1))
2268
    v_min = int(match.group(2))
2269
    if match.group(4):
2270
      v_rev = int(match.group(4))
2271
    else:
2272
      v_rev = 0
2273
    return (v_all, v_maj, v_min, v_rev)
2274

    
2275
  @classmethod
2276
  def _GetKVMOutput(cls, kvm_path, option):
2277
    """Return the output of a kvm invocation
2278

2279
    @type kvm_path: string
2280
    @param kvm_path: path to the kvm executable
2281
    @type option: a key of _KVMOPTS_CMDS
2282
    @param option: kvm option to fetch the output from
2283
    @return: output a supported kvm invocation
2284
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2285

2286
    """
2287
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2288

    
2289
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2290

    
2291
    result = utils.RunCmd([kvm_path] + optlist)
2292
    if result.failed and not can_fail:
2293
      raise errors.HypervisorError("Unable to get KVM %s output" %
2294
                                    " ".join(optlist))
2295
    return result.output
2296

    
2297
  @classmethod
2298
  def _GetKVMVersion(cls, kvm_path):
2299
    """Return the installed KVM version.
2300

2301
    @return: (version, v_maj, v_min, v_rev)
2302
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2303

2304
    """
2305
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2306

    
2307
  @classmethod
2308
  def _GetDefaultMachineVersion(cls, kvm_path):
2309
    """Return the default hardware revision (e.g. pc-1.1)
2310

2311
    """
2312
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2313
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2314
    if match:
2315
      return match.group(1)
2316
    else:
2317
      return "pc"
2318

    
2319
  def StopInstance(self, instance, force=False, retry=False, name=None):
2320
    """Stop an instance.
2321

2322
    """
2323
    if name is not None and not force:
2324
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2325
    if name is None:
2326
      name = instance.name
2327
      acpi = instance.hvparams[constants.HV_ACPI]
2328
    else:
2329
      acpi = False
2330
    _, pid, alive = self._InstancePidAlive(name)
2331
    if pid > 0 and alive:
2332
      if force or not acpi:
2333
        utils.KillProcess(pid)
2334
      else:
2335
        self._CallMonitorCommand(name, "system_powerdown")
2336

    
2337
  def CleanupInstance(self, instance_name):
2338
    """Cleanup after a stopped instance
2339

2340
    """
2341
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2342
    if pid > 0 and alive:
2343
      raise errors.HypervisorError("Cannot cleanup a live instance")
2344
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2345

    
2346
  def RebootInstance(self, instance):
2347
    """Reboot an instance.
2348

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

    
2369
  def MigrationInfo(self, instance):
2370
    """Get instance information to perform a migration.
2371

2372
    @type instance: L{objects.Instance}
2373
    @param instance: instance to be migrated
2374
    @rtype: string
2375
    @return: content of the KVM runtime file
2376

2377
    """
2378
    return self._ReadKVMRuntime(instance.name)
2379

    
2380
  def AcceptInstance(self, instance, info, target):
2381
    """Prepare to accept an instance.
2382

2383
    @type instance: L{objects.Instance}
2384
    @param instance: instance to be accepted
2385
    @type info: string
2386
    @param info: content of the KVM runtime file on the source node
2387
    @type target: string
2388
    @param target: target host (usually ip), on this node
2389

2390
    """
2391
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2392
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2393
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2394
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2395
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2396
                            incoming=incoming_address)
2397

    
2398
  def FinalizeMigrationDst(self, instance, info, success):
2399
    """Finalize the instance migration on the target node.
2400

2401
    Stop the incoming mode KVM.
2402

2403
    @type instance: L{objects.Instance}
2404
    @param instance: instance whose migration is being finalized
2405

2406
    """
2407
    if success:
2408
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2409
      kvm_nics = kvm_runtime[1]
2410

    
2411
      for nic_seq, nic in enumerate(kvm_nics):
2412
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2413
          # Bridged interfaces have already been configured
2414
          continue
2415
        try:
2416
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2417
        except EnvironmentError, err:
2418
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2419
                          instance.name, nic_seq, str(err))
2420
          continue
2421
        try:
2422
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2423
        except errors.HypervisorError, err:
2424
          logging.warning(str(err))
2425

    
2426
      self._WriteKVMRuntime(instance.name, info)
2427
    else:
2428
      self.StopInstance(instance, force=True)
2429

    
2430
  def MigrateInstance(self, instance, target, live):
2431
    """Migrate an instance to a target node.
2432

2433
    The migration will not be attempted if the instance is not
2434
    currently running.
2435

2436
    @type instance: L{objects.Instance}
2437
    @param instance: the instance to be migrated
2438
    @type target: string
2439
    @param target: ip address of the target node
2440
    @type live: boolean
2441
    @param live: perform a live migration
2442

2443
    """
2444
    instance_name = instance.name
2445
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2446
    _, _, alive = self._InstancePidAlive(instance_name)
2447
    if not alive:
2448
      raise errors.HypervisorError("Instance not running, cannot migrate")
2449

    
2450
    if not live:
2451
      self._CallMonitorCommand(instance_name, "stop")
2452

    
2453
    migrate_command = ("migrate_set_speed %dm" %
2454
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2455
    self._CallMonitorCommand(instance_name, migrate_command)
2456

    
2457
    migrate_command = ("migrate_set_downtime %dms" %
2458
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2459
    self._CallMonitorCommand(instance_name, migrate_command)
2460

    
2461
    # These commands are supported in latest qemu versions.
2462
    # Since _CallMonitorCommand does not catch monitor errors
2463
    # this does not raise an exception in case command is not supported
2464
    # TODO: either parse output of command or see if the command supported
2465
    # via info help (see hotplug)
2466
    migrate_command = ("migrate_set_capability xbzrle on")
2467
    self._CallMonitorCommand(instance_name, migrate_command)
2468

    
2469
    migrate_command = ("migrate_set_capability auto-converge on")
2470
    self._CallMonitorCommand(instance_name, migrate_command)
2471

    
2472
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2473
    self._CallMonitorCommand(instance_name, migrate_command)
2474

    
2475
  def FinalizeMigrationSource(self, instance, success, live):
2476
    """Finalize the instance migration on the source node.
2477

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

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

    
2493
  def GetMigrationStatus(self, instance):
2494
    """Get the migration status
2495

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

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

    
2523
          return migration_status
2524

    
2525
        logging.warning("KVM: unknown migration status '%s'", status)
2526

    
2527
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2528

    
2529
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2530

    
2531
  def BalloonInstanceMemory(self, instance, mem):
2532
    """Balloon an instance memory to a certain value.
2533

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

2539
    """
2540
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2541

    
2542
  def GetNodeInfo(self):
2543
    """Return information about the node.
2544

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

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

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

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

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

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

    
2594
    return objects.InstanceConsole(instance=instance.name,
2595
                                   kind=constants.CONS_MESSAGE,
2596
                                   message=("No serial shell for instance %s" %
2597
                                            instance.name))
2598

    
2599
  def Verify(self):
2600
    """Verify the hypervisor.
2601

2602
    Check that the required binaries exist.
2603

2604
    @return: Problem description if something is wrong, C{None} otherwise
2605

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

    
2617
    return self._FormatVerifyResults(msgs)
2618

    
2619
  @classmethod
2620
  def CheckParameterSyntax(cls, hvparams):
2621
    """Check the given parameters for validity.
2622

2623
    @type hvparams:  dict
2624
    @param hvparams: dictionary with parameter names/value
2625
    @raise errors.HypervisorError: when a parameter is not valid
2626

2627
    """
2628
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2629

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

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

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

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

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

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

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

    
2692
  @classmethod
2693
  def ValidateParameters(cls, hvparams):
2694
    """Check the given parameters for validity.
2695

2696
    @type hvparams:  dict
2697
    @param hvparams: dictionary with parameter names/value
2698
    @raise errors.HypervisorError: when a parameter is not valid
2699

2700
    """
2701
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2702

    
2703
    kvm_path = hvparams[constants.HV_KVM_PATH]
2704

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

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

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

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

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

    
2743
  @classmethod
2744
  def PowercycleNode(cls):
2745
    """KVM powercycle, just a wrapper over Linux powercycle.
2746

2747
    """
2748
    cls.LinuxPowercycle()