Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 31a2b501

History | View | Annotate | Download (100.6 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
  @classmethod
1114
  def _UnconfigureNic(cls, instance_name, nic, only_local=True):
1115
    """Run ifdown script for a specific NIC
1116

1117
    This is executed during instance cleanup and NIC hot-unplug
1118

1119
    @param instance: instance we're acting on
1120
    @type instance: instance object
1121
    @param nic: nic we're acting on
1122
    @type nic: nic object
1123
    @param localy: whether ifdown script should reset global conf (dns) or not
1124
    @type localy: boolean
1125

1126
    """
1127
    tap = cls._GetInstanceNICTap(instance_name, nic)
1128
    env = cls._CreateNICEnv(instance_name, nic, tap)
1129
    arg2 = str(only_local).lower()
1130
    result = utils.RunCmd([pathutils.KVM_IFDOWN, tap, arg2], env=env)
1131
    if result.failed:
1132
      raise errors.HypervisorError("Failed to unconfigure interface %s: %s;"
1133
                                   " network configuration script output: %s" %
1134
                                   (tap, result.fail_reason, result.output))
1135

    
1136
  @staticmethod
1137
  def _VerifyAffinityPackage():
1138
    if affinity is None:
1139
      raise errors.HypervisorError("affinity Python package not"
1140
                                   " found; cannot use CPU pinning under KVM")
1141

    
1142
  @staticmethod
1143
  def _BuildAffinityCpuMask(cpu_list):
1144
    """Create a CPU mask suitable for sched_setaffinity from a list of
1145
    CPUs.
1146

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

1150
    @type cpu_list: list of int
1151
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1152
    @rtype: int
1153
    @return: a bit mask of CPU affinities
1154

1155
    """
1156
    if cpu_list == constants.CPU_PINNING_OFF:
1157
      return constants.CPU_PINNING_ALL_KVM
1158
    else:
1159
      return sum(2 ** cpu for cpu in cpu_list)
1160

    
1161
  @classmethod
1162
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1163
    """Change CPU affinity for running VM according to given CPU mask.
1164

1165
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1166
    @type cpu_mask: string
1167
    @param process_id: process ID of KVM process. Used to pin entire VM
1168
                       to physical CPUs.
1169
    @type process_id: int
1170
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1171
    @type thread_dict: dict int:int
1172

1173
    """
1174
    # Convert the string CPU mask to a list of list of int's
1175
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1176

    
1177
    if len(cpu_list) == 1:
1178
      all_cpu_mapping = cpu_list[0]
1179
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
1180
        # If CPU pinning has 1 entry that's "all", then do nothing
1181
        pass
1182
      else:
1183
        # If CPU pinning has one non-all entry, map the entire VM to
1184
        # one set of physical CPUs
1185
        cls._VerifyAffinityPackage()
1186
        affinity.set_process_affinity_mask(
1187
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1188
    else:
1189
      # The number of vCPUs mapped should match the number of vCPUs
1190
      # reported by KVM. This was already verified earlier, so
1191
      # here only as a sanity check.
1192
      assert len(thread_dict) == len(cpu_list)
1193
      cls._VerifyAffinityPackage()
1194

    
1195
      # For each vCPU, map it to the proper list of physical CPUs
1196
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1197
        affinity.set_process_affinity_mask(thread_dict[i],
1198
                                           cls._BuildAffinityCpuMask(vcpu))
1199

    
1200
  def _GetVcpuThreadIds(self, instance_name):
1201
    """Get a mapping of vCPU no. to thread IDs for the instance
1202

1203
    @type instance_name: string
1204
    @param instance_name: instance in question
1205
    @rtype: dictionary of int:int
1206
    @return: a dictionary mapping vCPU numbers to thread IDs
1207

1208
    """
1209
    result = {}
1210
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1211
    for line in output.stdout.splitlines():
1212
      match = self._CPU_INFO_RE.search(line)
1213
      if not match:
1214
        continue
1215
      grp = map(int, match.groups())
1216
      result[grp[0]] = grp[1]
1217

    
1218
    return result
1219

    
1220
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1221
    """Complete CPU pinning.
1222

1223
    @type instance_name: string
1224
    @param instance_name: name of instance
1225
    @type cpu_mask: string
1226
    @param cpu_mask: CPU pinning mask as entered by user
1227

1228
    """
1229
    # Get KVM process ID, to be used if need to pin entire VM
1230
    _, pid, _ = self._InstancePidAlive(instance_name)
1231
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1232
    thread_dict = self._GetVcpuThreadIds(instance_name)
1233
    # Run CPU pinning, based on configured mask
1234
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1235

    
1236
  def ListInstances(self):
1237
    """Get the list of running instances.
1238

1239
    We can do this by listing our live instances directory and
1240
    checking whether the associated kvm process is still alive.
1241

1242
    """
1243
    result = []
1244
    for name in os.listdir(self._PIDS_DIR):
1245
      if self._InstancePidAlive(name)[2]:
1246
        result.append(name)
1247
    return result
1248

    
1249
  def GetInstanceInfo(self, instance_name):
1250
    """Get instance properties.
1251

1252
    @type instance_name: string
1253
    @param instance_name: the instance name
1254
    @rtype: tuple of strings
1255
    @return: (name, id, memory, vcpus, stat, times)
1256

1257
    """
1258
    _, pid, alive = self._InstancePidAlive(instance_name)
1259
    if not alive:
1260
      return None
1261

    
1262
    _, memory, vcpus = self._InstancePidInfo(pid)
1263
    istat = "---b-"
1264
    times = "0"
1265

    
1266
    try:
1267
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1268
      qmp.connect()
1269
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1270
      # Will fail if ballooning is not enabled, but we can then just resort to
1271
      # the value above.
1272
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1273
      memory = mem_bytes / 1048576
1274
    except errors.HypervisorError:
1275
      pass
1276

    
1277
    return (instance_name, pid, memory, vcpus, istat, times)
1278

    
1279
  def GetAllInstancesInfo(self):
1280
    """Get properties of all instances.
1281

1282
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1283

1284
    """
1285
    data = []
1286
    for name in os.listdir(self._PIDS_DIR):
1287
      try:
1288
        info = self.GetInstanceInfo(name)
1289
      except errors.HypervisorError:
1290
        # Ignore exceptions due to instances being shut down
1291
        continue
1292
      if info:
1293
        data.append(info)
1294
    return data
1295

    
1296
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1297
                                      kvmhelp, devlist):
1298
    """Generate KVM options regarding instance's block devices.
1299

1300
    @type instance: L{objects.Instance}
1301
    @param instance: the instance object
1302
    @type kvm_disks: list of tuples
1303
    @param kvm_disks: list of tuples [(disk, link_name)..]
1304
    @type kvmhelp: string
1305
    @param kvmhelp: output of kvm --help
1306
    @type devlist: string
1307
    @param devlist: output of kvm -device ?
1308
    @rtype: list
1309
    @return: list of command line options eventually used by kvm executable
1310

1311
    """
1312
    hvp = instance.hvparams
1313
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1314
    if kernel_path:
1315
      boot_disk = False
1316
    else:
1317
      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1318

    
1319
    # whether this is an older KVM version that uses the boot=on flag
1320
    # on devices
1321
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1322

    
1323
    dev_opts = []
1324
    device_driver = None
1325
    disk_type = hvp[constants.HV_DISK_TYPE]
1326
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
1327
      if_val = ",if=%s" % self._VIRTIO
1328
      try:
1329
        if self._VIRTIO_BLK_RE.search(devlist):
1330
          if_val = ",if=none"
1331
          # will be passed in -device option as driver
1332
          device_driver = self._VIRTIO_BLK_PCI
1333
      except errors.HypervisorError, _:
1334
        pass
1335
    else:
1336
      if_val = ",if=%s" % disk_type
1337
    # Cache mode
1338
    disk_cache = hvp[constants.HV_DISK_CACHE]
1339
    if instance.disk_template in constants.DTS_EXT_MIRROR:
1340
      if disk_cache != "none":
1341
        # TODO: make this a hard error, instead of a silent overwrite
1342
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1343
                        " to prevent shared storage corruption on migration",
1344
                        disk_cache)
1345
      cache_val = ",cache=none"
1346
    elif disk_cache != constants.HT_CACHE_DEFAULT:
1347
      cache_val = ",cache=%s" % disk_cache
1348
    else:
1349
      cache_val = ""
1350
    for idx, (cfdev, link_name) in enumerate(kvm_disks):
1351
      if cfdev.mode != constants.DISK_RDWR:
1352
        raise errors.HypervisorError("Instance has read-only disks which"
1353
                                     " are not supported by KVM")
1354
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
1355
      boot_val = ""
1356
      if boot_disk:
1357
        dev_opts.extend(["-boot", "c"])
1358
        boot_disk = False
1359
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1360
          boot_val = ",boot=on"
1361

    
1362
      # For ext we allow overriding disk_cache hypervisor params per disk
1363
      disk_cache = cfdev.params.get("cache", None)
1364
      if disk_cache:
1365
        cache_val = ",cache=%s" % disk_cache
1366
      drive_val = "file=%s,format=raw%s%s%s" % \
1367
                  (link_name, if_val, boot_val, cache_val)
1368

    
1369
      if device_driver:
1370
        # kvm_disks are the 4th entry of runtime file that did not exist in
1371
        # the past. That means that cfdev should always have pci slot and
1372
        # _GenerateDeviceKVMId() will not raise a exception.
1373
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK,
1374
                                         cfdev, idx)
1375
        drive_val += (",id=%s" % kvm_devid)
1376
        if cfdev.pci:
1377
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1378
        dev_val = ("%s,drive=%s,id=%s" %
1379
                   (device_driver, kvm_devid, kvm_devid))
1380
        if cfdev.pci:
1381
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1382
        dev_opts.extend(["-device", dev_val])
1383

    
1384
      # TODO: export disk geometry in IDISK_PARAMS
1385
      heads = cfdev.params.get('heads', None)
1386
      secs = cfdev.params.get('secs', None)
1387
      if heads and secs:
1388
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1389
        cyls = nr_sectors / (int(heads) * int(secs))
1390
        if cyls > 16383:
1391
          cyls = 16383
1392
        elif cyls < 2:
1393
          cyls = 2
1394
        if cyls and heads and secs:
1395
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1396
                        (cyls, int(heads), int(secs)))
1397

    
1398
      dev_opts.extend(["-drive", drive_val])
1399

    
1400
    return dev_opts
1401

    
1402
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1403
                          kvmhelp):
1404
    """Generate KVM information to start an instance.
1405

1406
    @type kvmhelp: string
1407
    @param kvmhelp: output of kvm --help
1408
    @attention: this function must not have any side-effects; for
1409
        example, it must not write to the filesystem, or read values
1410
        from the current system the are expected to differ between
1411
        nodes, since it is only run once at instance startup;
1412
        actions/kvm arguments that can vary between systems should be
1413
        done in L{_ExecuteKVMRuntime}
1414

1415
    """
1416
    # pylint: disable=R0912,R0914,R0915
1417
    hvp = instance.hvparams
1418
    self.ValidateParameters(hvp)
1419

    
1420
    pidfile = self._InstancePidFile(instance.name)
1421
    kvm = hvp[constants.HV_KVM_PATH]
1422
    kvm_cmd = [kvm]
1423
    # used just by the vnc server, if enabled
1424
    kvm_cmd.extend(["-name", instance.name])
1425
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1426

    
1427
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1428
    if hvp[constants.HV_CPU_CORES]:
1429
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1430
    if hvp[constants.HV_CPU_THREADS]:
1431
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1432
    if hvp[constants.HV_CPU_SOCKETS]:
1433
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1434

    
1435
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1436

    
1437
    kvm_cmd.extend(["-pidfile", pidfile])
1438
    kvm_cmd.extend(["-balloon", "virtio"])
1439
    kvm_cmd.extend(["-daemonize"])
1440
    if not instance.hvparams[constants.HV_ACPI]:
1441
      kvm_cmd.extend(["-no-acpi"])
1442
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1443
        constants.INSTANCE_REBOOT_EXIT:
1444
      kvm_cmd.extend(["-no-reboot"])
1445

    
1446
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1447
    if not mversion:
1448
      mversion = self._GetDefaultMachineVersion(kvm)
1449
    if self._MACHINE_RE.search(kvmhelp):
1450
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1451
      # extra hypervisor parameters. We should also investigate whether and how
1452
      # shadow_mem should be considered for the resource model.
1453
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1454
        specprop = ",accel=kvm"
1455
      else:
1456
        specprop = ""
1457
      machinespec = "%s%s" % (mversion, specprop)
1458
      kvm_cmd.extend(["-machine", machinespec])
1459
    else:
1460
      kvm_cmd.extend(["-M", mversion])
1461
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1462
          self._ENABLE_KVM_RE.search(kvmhelp)):
1463
        kvm_cmd.extend(["-enable-kvm"])
1464
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1465
            self._DISABLE_KVM_RE.search(kvmhelp)):
1466
        kvm_cmd.extend(["-disable-kvm"])
1467

    
1468
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1469
    if kernel_path:
1470
      boot_cdrom = boot_floppy = boot_network = False
1471
    else:
1472
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1473
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1474
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1475

    
1476
    if startup_paused:
1477
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1478

    
1479
    if boot_network:
1480
      kvm_cmd.extend(["-boot", "n"])
1481

    
1482
    # whether this is an older KVM version that uses the boot=on flag
1483
    # on devices
1484
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1485

    
1486
    disk_type = hvp[constants.HV_DISK_TYPE]
1487

    
1488
    #Now we can specify a different device type for CDROM devices.
1489
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1490
    if not cdrom_disk_type:
1491
      cdrom_disk_type = disk_type
1492

    
1493
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1494
    if iso_image:
1495
      options = ",format=raw,media=cdrom"
1496
      # set cdrom 'if' type
1497
      if boot_cdrom:
1498
        actual_cdrom_type = constants.HT_DISK_IDE
1499
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1500
        actual_cdrom_type = "virtio"
1501
      else:
1502
        actual_cdrom_type = cdrom_disk_type
1503
      if_val = ",if=%s" % actual_cdrom_type
1504
      # set boot flag, if needed
1505
      boot_val = ""
1506
      if boot_cdrom:
1507
        kvm_cmd.extend(["-boot", "d"])
1508
        if needs_boot_flag:
1509
          boot_val = ",boot=on"
1510
      # and finally build the entire '-drive' value
1511
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1512
      kvm_cmd.extend(["-drive", drive_val])
1513

    
1514
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1515
    if iso_image2:
1516
      options = ",format=raw,media=cdrom"
1517
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1518
        if_val = ",if=virtio"
1519
      else:
1520
        if_val = ",if=%s" % cdrom_disk_type
1521
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1522
      kvm_cmd.extend(["-drive", drive_val])
1523

    
1524
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1525
    if floppy_image:
1526
      options = ",format=raw,media=disk"
1527
      if boot_floppy:
1528
        kvm_cmd.extend(["-boot", "a"])
1529
        options = "%s,boot=on" % options
1530
      if_val = ",if=floppy"
1531
      options = "%s%s" % (options, if_val)
1532
      drive_val = "file=%s%s" % (floppy_image, options)
1533
      kvm_cmd.extend(["-drive", drive_val])
1534

    
1535
    if kernel_path:
1536
      kvm_cmd.extend(["-kernel", kernel_path])
1537
      initrd_path = hvp[constants.HV_INITRD_PATH]
1538
      if initrd_path:
1539
        kvm_cmd.extend(["-initrd", initrd_path])
1540
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1541
                     hvp[constants.HV_KERNEL_ARGS]]
1542
      if hvp[constants.HV_SERIAL_CONSOLE]:
1543
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1544
        root_append.append("console=ttyS0,%s" % serial_speed)
1545
      kvm_cmd.extend(["-append", " ".join(root_append)])
1546

    
1547
    mem_path = hvp[constants.HV_MEM_PATH]
1548
    if mem_path:
1549
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1550

    
1551
    monitor_dev = ("unix:%s,server,nowait" %
1552
                   self._InstanceMonitor(instance.name))
1553
    kvm_cmd.extend(["-monitor", monitor_dev])
1554
    if hvp[constants.HV_SERIAL_CONSOLE]:
1555
      serial_dev = ("unix:%s,server,nowait" %
1556
                    self._InstanceSerial(instance.name))
1557
      kvm_cmd.extend(["-serial", serial_dev])
1558
    else:
1559
      kvm_cmd.extend(["-serial", "none"])
1560

    
1561
    mouse_type = hvp[constants.HV_USB_MOUSE]
1562
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1563
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1564
    spice_ip_version = None
1565

    
1566
    kvm_cmd.extend(["-usb"])
1567

    
1568
    if mouse_type:
1569
      kvm_cmd.extend(["-usbdevice", mouse_type])
1570
    elif vnc_bind_address:
1571
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1572

    
1573
    if vnc_bind_address:
1574
      if netutils.IP4Address.IsValid(vnc_bind_address):
1575
        if instance.network_port > constants.VNC_BASE_PORT:
1576
          display = instance.network_port - constants.VNC_BASE_PORT
1577
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1578
            vnc_arg = ":%d" % (display)
1579
          else:
1580
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1581
        else:
1582
          logging.error("Network port is not a valid VNC display (%d < %d),"
1583
                        " not starting VNC",
1584
                        instance.network_port, constants.VNC_BASE_PORT)
1585
          vnc_arg = "none"
1586

    
1587
        # Only allow tls and other option when not binding to a file, for now.
1588
        # kvm/qemu gets confused otherwise about the filename to use.
1589
        vnc_append = ""
1590
        if hvp[constants.HV_VNC_TLS]:
1591
          vnc_append = "%s,tls" % vnc_append
1592
          if hvp[constants.HV_VNC_X509_VERIFY]:
1593
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1594
                                               hvp[constants.HV_VNC_X509])
1595
          elif hvp[constants.HV_VNC_X509]:
1596
            vnc_append = "%s,x509=%s" % (vnc_append,
1597
                                         hvp[constants.HV_VNC_X509])
1598
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1599
          vnc_append = "%s,password" % vnc_append
1600

    
1601
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1602

    
1603
      else:
1604
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1605

    
1606
      kvm_cmd.extend(["-vnc", vnc_arg])
1607
    elif spice_bind:
1608
      # FIXME: this is wrong here; the iface ip address differs
1609
      # between systems, so it should be done in _ExecuteKVMRuntime
1610
      if netutils.IsValidInterface(spice_bind):
1611
        # The user specified a network interface, we have to figure out the IP
1612
        # address.
1613
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1614
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1615

    
1616
        # if the user specified an IP version and the interface does not
1617
        # have that kind of IP addresses, throw an exception
1618
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1619
          if not addresses[spice_ip_version]:
1620
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1621
                                         " for %s" % (spice_ip_version,
1622
                                                      spice_bind))
1623

    
1624
        # the user did not specify an IP version, we have to figure it out
1625
        elif (addresses[constants.IP4_VERSION] and
1626
              addresses[constants.IP6_VERSION]):
1627
          # we have both ipv4 and ipv6, let's use the cluster default IP
1628
          # version
1629
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1630
          spice_ip_version = \
1631
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1632
        elif addresses[constants.IP4_VERSION]:
1633
          spice_ip_version = constants.IP4_VERSION
1634
        elif addresses[constants.IP6_VERSION]:
1635
          spice_ip_version = constants.IP6_VERSION
1636
        else:
1637
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1638
                                       " for %s" % (spice_bind))
1639

    
1640
        spice_address = addresses[spice_ip_version][0]
1641

    
1642
      else:
1643
        # spice_bind is known to be a valid IP address, because
1644
        # ValidateParameters checked it.
1645
        spice_address = spice_bind
1646

    
1647
      spice_arg = "addr=%s" % spice_address
1648
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1649
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1650
                     (spice_arg, instance.network_port,
1651
                      pathutils.SPICE_CACERT_FILE))
1652
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1653
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1654
                      pathutils.SPICE_CERT_FILE))
1655
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1656
        if tls_ciphers:
1657
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1658
      else:
1659
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1660

    
1661
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1662
        spice_arg = "%s,disable-ticketing" % spice_arg
1663

    
1664
      if spice_ip_version:
1665
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1666

    
1667
      # Image compression options
1668
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1669
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1670
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1671
      if img_lossless:
1672
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1673
      if img_jpeg:
1674
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1675
      if img_zlib_glz:
1676
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1677

    
1678
      # Video stream detection
1679
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1680
      if video_streaming:
1681
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1682

    
1683
      # Audio compression, by default in qemu-kvm it is on
1684
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1685
        spice_arg = "%s,playback-compression=off" % spice_arg
1686
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1687
        spice_arg = "%s,agent-mouse=off" % spice_arg
1688
      else:
1689
        # Enable the spice agent communication channel between the host and the
1690
        # agent.
1691
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1692
        kvm_cmd.extend([
1693
          "-device",
1694
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1695
          ])
1696
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1697

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

    
1701
    else:
1702
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1703
      # also works in earlier versions though (tested with 1.1 and 1.3)
1704
      if self._DISPLAY_RE.search(kvmhelp):
1705
        kvm_cmd.extend(["-display", "none"])
1706
      else:
1707
        kvm_cmd.extend(["-nographic"])
1708

    
1709
    if hvp[constants.HV_USE_LOCALTIME]:
1710
      kvm_cmd.extend(["-localtime"])
1711

    
1712
    if hvp[constants.HV_KVM_USE_CHROOT]:
1713
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1714

    
1715
    # Add qemu-KVM -cpu param
1716
    if hvp[constants.HV_CPU_TYPE]:
1717
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1718

    
1719
    # As requested by music lovers
1720
    if hvp[constants.HV_SOUNDHW]:
1721
      kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1722

    
1723
    # Pass a -vga option if requested, or if spice is used, for backwards
1724
    # compatibility.
1725
    if hvp[constants.HV_VGA]:
1726
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1727
    elif spice_bind:
1728
      kvm_cmd.extend(["-vga", "qxl"])
1729

    
1730
    # Various types of usb devices, comma separated
1731
    if hvp[constants.HV_USB_DEVICES]:
1732
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1733
        kvm_cmd.extend(["-usbdevice", dev])
1734

    
1735
    if hvp[constants.HV_KVM_EXTRA]:
1736
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1737

    
1738
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1739
    kvm_disks = []
1740
    for disk, link_name in block_devices:
1741
      _UpdatePCISlots(disk, pci_reservations)
1742
      kvm_disks.append((disk, link_name))
1743

    
1744
    kvm_nics = []
1745
    for nic in instance.nics:
1746
      _UpdatePCISlots(nic, pci_reservations)
1747
      kvm_nics.append(nic)
1748

    
1749
    hvparams = hvp
1750

    
1751
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1752

    
1753
  def _WriteKVMRuntime(self, instance_name, data):
1754
    """Write an instance's KVM runtime
1755

1756
    """
1757
    try:
1758
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1759
                      data=data)
1760
    except EnvironmentError, err:
1761
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1762

    
1763
  @classmethod
1764
  def _ReadKVMRuntime(cls, instance_name):
1765
    """Read an instance's KVM runtime
1766

1767
    """
1768
    try:
1769
      file_content = utils.ReadFile(cls._InstanceKVMRuntime(instance_name))
1770
    except EnvironmentError, err:
1771
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1772
    return file_content
1773

    
1774
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1775
    """Save an instance's KVM runtime
1776

1777
    """
1778
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1779

    
1780
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1781
    serialized_disks = [(blk.ToDict(), link)
1782
                            for blk, link in kvm_disks]
1783
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1784
                                      serialized_disks))
1785

    
1786
    self._WriteKVMRuntime(instance.name, serialized_form)
1787

    
1788
  @classmethod
1789
  def _LoadKVMRuntime(cls, instance_name, serialized_runtime=None):
1790
    """Load an instance's KVM runtime
1791

1792
    """
1793
    if not serialized_runtime:
1794
      serialized_runtime = cls._ReadKVMRuntime(instance_name)
1795

    
1796
    return _AnalyzeSerializedRuntime(serialized_runtime)
1797

    
1798
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1799
    """Run the KVM cmd and check for errors
1800

1801
    @type name: string
1802
    @param name: instance name
1803
    @type kvm_cmd: list of strings
1804
    @param kvm_cmd: runcmd input for kvm
1805
    @type tap_fds: list of int
1806
    @param tap_fds: fds of tap devices opened by Ganeti
1807

1808
    """
1809
    try:
1810
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1811
    finally:
1812
      for fd in tap_fds:
1813
        utils_wrapper.CloseFdNoError(fd)
1814

    
1815
    if result.failed:
1816
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1817
                                   (name, result.fail_reason, result.output))
1818
    if not self._InstancePidAlive(name)[2]:
1819
      raise errors.HypervisorError("Failed to start instance %s" % name)
1820

    
1821
  # too many local variables
1822
  # pylint: disable=R0914
1823
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1824
    """Execute a KVM cmd, after completing it with some last minute data.
1825

1826
    @type incoming: tuple of strings
1827
    @param incoming: (target_host_ip, port)
1828
    @type kvmhelp: string
1829
    @param kvmhelp: output of kvm --help
1830

1831
    """
1832
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1833
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1834
    #    have changed since the instance started; only use them if the change
1835
    #    won't affect the inside of the instance (which hasn't been rebooted).
1836
    #  - up_hvp contains the parameters as they were when the instance was
1837
    #    started, plus any new parameter which has been added between ganeti
1838
    #    versions: it is paramount that those default to a value which won't
1839
    #    affect the inside of the instance as well.
1840
    conf_hvp = instance.hvparams
1841
    name = instance.name
1842
    self._CheckDown(name)
1843

    
1844
    temp_files = []
1845

    
1846
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1847
    # the first element of kvm_cmd is always the path to the kvm binary
1848
    kvm_path = kvm_cmd[0]
1849
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1850

    
1851
    # We know it's safe to run as a different user upon migration, so we'll use
1852
    # the latest conf, from conf_hvp.
1853
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1854
    if security_model == constants.HT_SM_USER:
1855
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1856

    
1857
    keymap = conf_hvp[constants.HV_KEYMAP]
1858
    if keymap:
1859
      keymap_path = self._InstanceKeymapFile(name)
1860
      # If a keymap file is specified, KVM won't use its internal defaults. By
1861
      # first including the "en-us" layout, an error on loading the actual
1862
      # layout (e.g. because it can't be found) won't lead to a non-functional
1863
      # keyboard. A keyboard with incorrect keys is still better than none.
1864
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1865
      kvm_cmd.extend(["-k", keymap_path])
1866

    
1867
    # We have reasons to believe changing something like the nic driver/type
1868
    # upon migration won't exactly fly with the instance kernel, so for nic
1869
    # related parameters we'll use up_hvp
1870
    tapfds = []
1871
    taps = []
1872
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1873

    
1874
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1875
                                                     kvm_disks,
1876
                                                     kvmhelp,
1877
                                                     devlist)
1878
    kvm_cmd.extend(bdev_opts)
1879

    
1880
    if not kvm_nics:
1881
      kvm_cmd.extend(["-net", "none"])
1882
    else:
1883
      vnet_hdr = False
1884
      tap_extra = ""
1885
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1886
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1887
        nic_model = self._VIRTIO
1888
        try:
1889
          if self._VIRTIO_NET_RE.search(devlist):
1890
            nic_model = self._VIRTIO_NET_PCI
1891
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1892
        except errors.HypervisorError, _:
1893
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1894
          # have new virtio syntax either.
1895
          pass
1896

    
1897
        if up_hvp[constants.HV_VHOST_NET]:
1898
          # check for vhost_net support
1899
          if self._VHOST_RE.search(kvmhelp):
1900
            tap_extra = ",vhost=on"
1901
          else:
1902
            raise errors.HypervisorError("vhost_net is configured"
1903
                                         " but it is not available")
1904
      else:
1905
        nic_model = nic_type
1906

    
1907
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1908

    
1909
      for nic_seq, nic in enumerate(kvm_nics):
1910
        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1911
        tapfds.append(tapfd)
1912
        taps.append(tapname)
1913
        if kvm_supports_netdev:
1914
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1915
          try:
1916
            # kvm_nics already exist in old runtime files and thus there might
1917
            # be some entries without pci slot (therefore try: except:)
1918
            kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1919
            netdev = kvm_devid
1920
            nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1921
          except errors.HotplugError:
1922
            netdev = "netdev%d" % nic_seq
1923
          nic_val += (",netdev=%s" % netdev)
1924
          tap_val = ("type=tap,id=%s,fd=%d%s" %
1925
                     (netdev, tapfd, tap_extra))
1926
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1927
        else:
1928
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1929
                                                         nic.mac, nic_model)
1930
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1931
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1932

    
1933
    if incoming:
1934
      target, port = incoming
1935
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1936

    
1937
    # Changing the vnc password doesn't bother the guest that much. At most it
1938
    # will surprise people who connect to it. Whether positively or negatively
1939
    # it's debatable.
1940
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1941
    vnc_pwd = None
1942
    if vnc_pwd_file:
1943
      try:
1944
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1945
      except EnvironmentError, err:
1946
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1947
                                     % (vnc_pwd_file, err))
1948

    
1949
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1950
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1951
                         constants.SECURE_DIR_MODE)])
1952

    
1953
    # Automatically enable QMP if version is >= 0.14
1954
    if self._QMP_RE.search(kvmhelp):
1955
      logging.debug("Enabling QMP")
1956
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1957
                      self._InstanceQmpMonitor(instance.name)])
1958

    
1959
    # Configure the network now for starting instances and bridged interfaces,
1960
    # during FinalizeMigration for incoming instances' routed interfaces
1961
    for nic_seq, nic in enumerate(kvm_nics):
1962
      if (incoming and
1963
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1964
        continue
1965
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1966

    
1967
    # CPU affinity requires kvm to start paused, so we set this flag if the
1968
    # instance is not already paused and if we are not going to accept a
1969
    # migrating instance. In the latter case, pausing is not needed.
1970
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1971
    if start_kvm_paused:
1972
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1973

    
1974
    # Note: CPU pinning is using up_hvp since changes take effect
1975
    # during instance startup anyway, and to avoid problems when soft
1976
    # rebooting the instance.
1977
    cpu_pinning = False
1978
    if up_hvp.get(constants.HV_CPU_MASK, None):
1979
      cpu_pinning = True
1980

    
1981
    if security_model == constants.HT_SM_POOL:
1982
      ss = ssconf.SimpleStore()
1983
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1984
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1985
      uid = uidpool.RequestUnusedUid(all_uids)
1986
      try:
1987
        username = pwd.getpwuid(uid.GetUid()).pw_name
1988
        kvm_cmd.extend(["-runas", username])
1989
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1990
      except:
1991
        uidpool.ReleaseUid(uid)
1992
        raise
1993
      else:
1994
        uid.Unlock()
1995
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1996
    else:
1997
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1998

    
1999
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
2000
                     constants.RUN_DIRS_MODE)])
2001
    for nic_seq, tap in enumerate(taps):
2002
      nic = kvm_nics[nic_seq]
2003
      self._WriteInstanceNICFiles(instance.name, nic_seq, nic, tap)
2004

    
2005
    if vnc_pwd:
2006
      change_cmd = "change vnc password %s" % vnc_pwd
2007
      self._CallMonitorCommand(instance.name, change_cmd)
2008

    
2009
    # Setting SPICE password. We are not vulnerable to malicious passwordless
2010
    # connection attempts because SPICE by default does not allow connections
2011
    # if neither a password nor the "disable_ticketing" options are specified.
2012
    # As soon as we send the password via QMP, that password is a valid ticket
2013
    # for connection.
2014
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
2015
    if spice_password_file:
2016
      spice_pwd = ""
2017
      try:
2018
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
2019
      except EnvironmentError, err:
2020
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
2021
                                     % (spice_password_file, err))
2022

    
2023
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2024
      qmp.connect()
2025
      arguments = {
2026
          "protocol": "spice",
2027
          "password": spice_pwd,
2028
      }
2029
      qmp.Execute("set_password", arguments)
2030

    
2031
    for filename in temp_files:
2032
      utils.RemoveFile(filename)
2033

    
2034
    # If requested, set CPU affinity and resume instance execution
2035
    if cpu_pinning:
2036
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2037

    
2038
    start_memory = self._InstanceStartupMemory(instance)
2039
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
2040
      self.BalloonInstanceMemory(instance, start_memory)
2041

    
2042
    if start_kvm_paused:
2043
      # To control CPU pinning, ballooning, and vnc/spice passwords
2044
      # the VM was started in a frozen state. If freezing was not
2045
      # explicitly requested resume the vm status.
2046
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2047

    
2048
  def StartInstance(self, instance, block_devices, startup_paused):
2049
    """Start an instance.
2050

2051
    """
2052
    self._CheckDown(instance.name)
2053
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2054
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2055
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2056
                                           startup_paused, kvmhelp)
2057
    self._SaveKVMRuntime(instance, kvm_runtime)
2058
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2059

    
2060
  def _CallMonitorCommand(self, instance_name, command):
2061
    """Invoke a command on the instance monitor.
2062

2063
    """
2064
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2065
    # version. The monitor protocol is designed for human consumption, whereas
2066
    # QMP is made for programmatic usage. In the worst case QMP can also
2067
    # execute monitor commands. As it is, all calls to socat take at least
2068
    # 500ms and likely more: socat can't detect the end of the reply and waits
2069
    # for 500ms of no data received before exiting (500 ms is the default for
2070
    # the "-t" parameter).
2071
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
2072
             (utils.ShellQuote(command),
2073
              constants.SOCAT_PATH,
2074
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2075
    result = utils.RunCmd(socat)
2076
    if result.failed:
2077
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2078
             " output: %s" %
2079
             (command, instance_name, result.fail_reason, result.output))
2080
      raise errors.HypervisorError(msg)
2081

    
2082
    return result
2083

    
2084
  def _GetFreePCISlot(self, instance, dev):
2085
    """Get the first available pci slot of a runnung instance.
2086

2087
    """
2088
    slots = bitarray(32)
2089
    slots.setall(False) # pylint: disable=E1101
2090
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2091
    for line in output.stdout.splitlines():
2092
      match = self._INFO_PCI_RE.search(line)
2093
      if match:
2094
        slot = int(match.group(1))
2095
        slots[slot] = True
2096

    
2097
    [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
2098
    if not free:
2099
      raise errors.HypervisorError("All PCI slots occupied")
2100

    
2101
    dev.pci = int(free)
2102

    
2103
  def VerifyHotplugSupport(self, instance, action, dev_type):
2104
    """Verifies that hotplug is supported.
2105

2106
    Hotplug is *not* supported in case of:
2107
     - security models and chroot (disk hotplug)
2108
     - fdsend module is missing (nic hot-add)
2109

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

2112
    """
2113
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2114
      hvp = instance.hvparams
2115
      security_model = hvp[constants.HV_SECURITY_MODEL]
2116
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2117
      if use_chroot:
2118
        raise errors.HotplugError("Disk hotplug is not supported"
2119
                                  " in case of chroot.")
2120
      if security_model != constants.HT_SM_NONE:
2121
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2122
                                  " security models are used.")
2123

    
2124
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2125
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2126
      raise errors.HotplugError("Cannot hot-add NIC."
2127
                                " fdsend python module is missing.")
2128

    
2129
  def HotplugSupported(self, instance):
2130
    """Checks if hotplug is generally supported.
2131

2132
    Hotplug is *not* supported in case of:
2133
     - qemu versions < 1.0
2134
     - for stopped instances
2135

2136
    @raise errors.HypervisorError: in one of the previous cases
2137

2138
    """
2139
    try:
2140
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2141
    except errors.HypervisorError:
2142
      raise errors.HotplugError("Instance is probably down")
2143

    
2144
    # TODO: search for netdev_add, drive_add, device_add.....
2145
    match = self._INFO_VERSION_RE.search(output.stdout)
2146
    if not match:
2147
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2148

    
2149
    v_major, v_min, _, _ = match.groups()
2150
    if (int(v_major), int(v_min)) < (1, 0):
2151
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2152

    
2153
  def _CallHotplugCommands(self, name, cmds):
2154
    for c in cmds:
2155
      self._CallMonitorCommand(name, c)
2156
      time.sleep(1)
2157

    
2158
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2159
                            should_exist):
2160
    """Checks if a previous hotplug command has succeeded.
2161

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

2165
    @raise errors.HypervisorError: if result is not the expected one
2166

2167
    """
2168
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2169
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2170
    match = \
2171
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2172
    if match and not should_exist:
2173
      msg = "Device %s should have been removed but is still there" % kvm_devid
2174
      raise errors.HypervisorError(msg)
2175

    
2176
    if not match and should_exist:
2177
      msg = "Device %s should have been added but is missing" % kvm_devid
2178
      raise errors.HypervisorError(msg)
2179

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

    
2182
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2183
    """ Helper method to hot-add a new device
2184

2185
    It gets free pci slot generates the device name and invokes the
2186
    device specific method.
2187

2188
    """
2189
    # in case of hot-mod if the device already exists this is given
2190
    if device.pci is None:
2191
      self._GetFreePCISlot(instance, device)
2192
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2193
    runtime = self._LoadKVMRuntime(instance.name)
2194
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2195
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2196
                (extra, kvm_devid)]
2197
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2198
                (hex(device.pci), kvm_devid, kvm_devid)]
2199
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2200
      (tap, fd) = _OpenTap()
2201
      self._ConfigureNIC(instance, seq, device, tap)
2202
      self._PassTapFd(instance, fd, device)
2203
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2204
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2205
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2206
      cmds += ["device_add %s" % args]
2207
      self._WriteInstanceNICFiles(instance.name, seq, device, tap)
2208

    
2209
    self._CallHotplugCommands(instance.name, cmds)
2210
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2211
    # update relevant entries in runtime file
2212
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2213
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2214
    runtime[index].append(entry)
2215
    self._SaveKVMRuntime(instance, runtime)
2216

    
2217
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2218
    """ Helper method for hot-del device
2219

2220
    It gets device info from runtime file, generates the device name and
2221
    invokes the device specific method.
2222

2223
    """
2224
    runtime = self._LoadKVMRuntime(instance.name)
2225
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2226
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2227
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2228
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2229
      cmds = ["device_del %s" % kvm_devid]
2230
      cmds += ["drive_del %s" % kvm_devid]
2231
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2232
      cmds = ["device_del %s" % kvm_devid]
2233
      cmds += ["netdev_del %s" % kvm_devid]
2234
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2235
    self._CallHotplugCommands(instance.name, cmds)
2236
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2237
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2238
    runtime[index].remove(entry)
2239
    self._SaveKVMRuntime(instance, runtime)
2240

    
2241
    return kvm_device.pci
2242

    
2243
  def HotModDevice(self, instance, dev_type, device, _, seq):
2244
    """ Helper method for hot-mod device
2245

2246
    It gets device info from runtime file, generates the device name and
2247
    invokes the device specific method. Currently only NICs support hot-mod
2248

2249
    """
2250
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2251
      # putting it back in the same pci slot
2252
      try:
2253
        device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2254
      except errors.HotplugError:
2255
        logging.info("Device not found in runtime file. Assuming it was"
2256
                     " previously added without --hotplug option.")
2257
      # TODO: remove sleep when socat gets removed
2258
      self.HotAddDevice(instance, dev_type, device, _, seq)
2259

    
2260
  def _PassTapFd(self, instance, fd, nic):
2261
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2262

2263
    """
2264
    # TODO: factor out code related to unix sockets.
2265
    #       squash common parts between monitor and qmp
2266
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2267
    command = "getfd %s\n" % kvm_devid
2268
    fds = [fd]
2269
    logging.info("%s", fds)
2270
    try:
2271
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2272
      monsock.connect()
2273
      fdsend.sendfds(monsock.sock, command, fds=fds)
2274
    finally:
2275
      monsock.close()
2276

    
2277
  @classmethod
2278
  def _ParseKVMVersion(cls, text):
2279
    """Parse the KVM version from the --help output.
2280

2281
    @type text: string
2282
    @param text: output of kvm --help
2283
    @return: (version, v_maj, v_min, v_rev)
2284
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2285

2286
    """
2287
    match = cls._VERSION_RE.search(text.splitlines()[0])
2288
    if not match:
2289
      raise errors.HypervisorError("Unable to get KVM version")
2290

    
2291
    v_all = match.group(0)
2292
    v_maj = int(match.group(1))
2293
    v_min = int(match.group(2))
2294
    if match.group(4):
2295
      v_rev = int(match.group(4))
2296
    else:
2297
      v_rev = 0
2298
    return (v_all, v_maj, v_min, v_rev)
2299

    
2300
  @classmethod
2301
  def _GetKVMOutput(cls, kvm_path, option):
2302
    """Return the output of a kvm invocation
2303

2304
    @type kvm_path: string
2305
    @param kvm_path: path to the kvm executable
2306
    @type option: a key of _KVMOPTS_CMDS
2307
    @param option: kvm option to fetch the output from
2308
    @return: output a supported kvm invocation
2309
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2310

2311
    """
2312
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2313

    
2314
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2315

    
2316
    result = utils.RunCmd([kvm_path] + optlist)
2317
    if result.failed and not can_fail:
2318
      raise errors.HypervisorError("Unable to get KVM %s output" %
2319
                                    " ".join(optlist))
2320
    return result.output
2321

    
2322
  @classmethod
2323
  def _GetKVMVersion(cls, kvm_path):
2324
    """Return the installed KVM version.
2325

2326
    @return: (version, v_maj, v_min, v_rev)
2327
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2328

2329
    """
2330
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2331

    
2332
  @classmethod
2333
  def _GetDefaultMachineVersion(cls, kvm_path):
2334
    """Return the default hardware revision (e.g. pc-1.1)
2335

2336
    """
2337
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2338
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2339
    if match:
2340
      return match.group(1)
2341
    else:
2342
      return "pc"
2343

    
2344
  def StopInstance(self, instance, force=False, retry=False, name=None):
2345
    """Stop an instance.
2346

2347
    """
2348
    if name is not None and not force:
2349
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2350
    if name is None:
2351
      name = instance.name
2352
      acpi = instance.hvparams[constants.HV_ACPI]
2353
    else:
2354
      acpi = False
2355
    _, pid, alive = self._InstancePidAlive(name)
2356
    if pid > 0 and alive:
2357
      if force or not acpi:
2358
        utils.KillProcess(pid)
2359
      else:
2360
        self._CallMonitorCommand(name, "system_powerdown")
2361

    
2362
  @classmethod
2363
  def _UnconfigureInstanceNICs(cls, instance_name, info=None):
2364
    """Get runtime NICs of an instance and unconfigure them
2365

2366
    """
2367
    _, kvm_nics, __, ___ = cls._LoadKVMRuntime(instance_name, info)
2368
    for nic in kvm_nics:
2369
      cls._UnconfigureNic(instance_name, nic)
2370

    
2371
  def CleanupInstance(self, instance_name):
2372
    """Cleanup after a stopped instance
2373

2374
    """
2375
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2376
    if pid > 0 and alive:
2377
      raise errors.HypervisorError("Cannot cleanup a live instance")
2378
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2379

    
2380
  def RebootInstance(self, instance):
2381
    """Reboot an instance.
2382

2383
    """
2384
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2385
    # socket the instance will stop, but now power up again. So we'll resort
2386
    # to shutdown and restart.
2387
    _, _, alive = self._InstancePidAlive(instance.name)
2388
    if not alive:
2389
      raise errors.HypervisorError("Failed to reboot instance %s:"
2390
                                   " not running" % instance.name)
2391
    # StopInstance will delete the saved KVM runtime so:
2392
    # ...first load it...
2393
    kvm_runtime = self._LoadKVMRuntime(instance.name)
2394
    # ...now we can safely call StopInstance...
2395
    if not self.StopInstance(instance):
2396
      self.StopInstance(instance, force=True)
2397
    # ...and finally we can save it again, and execute it...
2398
    self._SaveKVMRuntime(instance, kvm_runtime)
2399
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2400
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2401
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2402

    
2403
  def MigrationInfo(self, instance):
2404
    """Get instance information to perform a migration.
2405

2406
    @type instance: L{objects.Instance}
2407
    @param instance: instance to be migrated
2408
    @rtype: string
2409
    @return: content of the KVM runtime file
2410

2411
    """
2412
    return self._ReadKVMRuntime(instance.name)
2413

    
2414
  def AcceptInstance(self, instance, info, target):
2415
    """Prepare to accept an instance.
2416

2417
    @type instance: L{objects.Instance}
2418
    @param instance: instance to be accepted
2419
    @type info: string
2420
    @param info: content of the KVM runtime file on the source node
2421
    @type target: string
2422
    @param target: target host (usually ip), on this node
2423

2424
    """
2425
    kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2426
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2427
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2428
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2429
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2430
                            incoming=incoming_address)
2431

    
2432
  def FinalizeMigrationDst(self, instance, info, success):
2433
    """Finalize the instance migration on the target node.
2434

2435
    Stop the incoming mode KVM.
2436

2437
    @type instance: L{objects.Instance}
2438
    @param instance: instance whose migration is being finalized
2439

2440
    """
2441
    if success:
2442
      kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2443
      kvm_nics = kvm_runtime[1]
2444

    
2445
      for nic_seq, nic in enumerate(kvm_nics):
2446
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2447
          # Bridged interfaces have already been configured
2448
          continue
2449
        try:
2450
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2451
        except EnvironmentError, err:
2452
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2453
                          instance.name, nic_seq, str(err))
2454
          continue
2455
        try:
2456
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2457
        except errors.HypervisorError, err:
2458
          logging.warning(str(err))
2459

    
2460
      self._WriteKVMRuntime(instance.name, info)
2461
    else:
2462
      self.StopInstance(instance, force=True)
2463

    
2464
  def MigrateInstance(self, instance, target, live):
2465
    """Migrate an instance to a target node.
2466

2467
    The migration will not be attempted if the instance is not
2468
    currently running.
2469

2470
    @type instance: L{objects.Instance}
2471
    @param instance: the instance to be migrated
2472
    @type target: string
2473
    @param target: ip address of the target node
2474
    @type live: boolean
2475
    @param live: perform a live migration
2476

2477
    """
2478
    instance_name = instance.name
2479
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2480
    _, _, alive = self._InstancePidAlive(instance_name)
2481
    if not alive:
2482
      raise errors.HypervisorError("Instance not running, cannot migrate")
2483

    
2484
    if not live:
2485
      self._CallMonitorCommand(instance_name, "stop")
2486

    
2487
    migrate_command = ("migrate_set_speed %dm" %
2488
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2489
    self._CallMonitorCommand(instance_name, migrate_command)
2490

    
2491
    migrate_command = ("migrate_set_downtime %dms" %
2492
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2493
    self._CallMonitorCommand(instance_name, migrate_command)
2494

    
2495
    # These commands are supported in latest qemu versions.
2496
    # Since _CallMonitorCommand does not catch monitor errors
2497
    # this does not raise an exception in case command is not supported
2498
    # TODO: either parse output of command or see if the command supported
2499
    # via info help (see hotplug)
2500
    migrate_command = ("migrate_set_capability xbzrle on")
2501
    self._CallMonitorCommand(instance_name, migrate_command)
2502

    
2503
    migrate_command = ("migrate_set_capability auto-converge on")
2504
    self._CallMonitorCommand(instance_name, migrate_command)
2505

    
2506
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2507
    self._CallMonitorCommand(instance_name, migrate_command)
2508

    
2509
  def FinalizeMigrationSource(self, instance, success, live):
2510
    """Finalize the instance migration on the source node.
2511

2512
    @type instance: L{objects.Instance}
2513
    @param instance: the instance that was migrated
2514
    @type success: bool
2515
    @param success: whether the migration succeeded or not
2516
    @type live: bool
2517
    @param live: whether the user requested a live migration or not
2518

2519
    """
2520
    if success:
2521
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2522
      utils.KillProcess(pid)
2523
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2524
    elif live:
2525
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2526

    
2527
  def GetMigrationStatus(self, instance):
2528
    """Get the migration status
2529

2530
    @type instance: L{objects.Instance}
2531
    @param instance: the instance that is being migrated
2532
    @rtype: L{objects.MigrationStatus}
2533
    @return: the status of the current migration (one of
2534
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2535
             progress info that can be retrieved from the hypervisor
2536

2537
    """
2538
    info_command = "info migrate"
2539
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2540
      result = self._CallMonitorCommand(instance.name, info_command)
2541
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2542
      if not match:
2543
        if not result.stdout:
2544
          logging.info("KVM: empty 'info migrate' result")
2545
        else:
2546
          logging.warning("KVM: unknown 'info migrate' result: %s",
2547
                          result.stdout)
2548
      else:
2549
        status = match.group(1)
2550
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2551
          migration_status = objects.MigrationStatus(status=status)
2552
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2553
          if match:
2554
            migration_status.transferred_ram = match.group("transferred")
2555
            migration_status.total_ram = match.group("total")
2556

    
2557
          return migration_status
2558

    
2559
        logging.warning("KVM: unknown migration status '%s'", status)
2560

    
2561
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2562

    
2563
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2564

    
2565
  def BalloonInstanceMemory(self, instance, mem):
2566
    """Balloon an instance memory to a certain value.
2567

2568
    @type instance: L{objects.Instance}
2569
    @param instance: instance to be accepted
2570
    @type mem: int
2571
    @param mem: actual memory size to use for instance runtime
2572

2573
    """
2574
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2575

    
2576
  def GetNodeInfo(self):
2577
    """Return information about the node.
2578

2579
    @return: a dict with the following keys (values in MiB):
2580
          - memory_total: the total memory size on the node
2581
          - memory_free: the available memory on the node for instances
2582
          - memory_dom0: the memory used by the node itself, if available
2583
          - hv_version: the hypervisor version in the form (major, minor,
2584
                        revision)
2585

2586
    """
2587
    result = self.GetLinuxNodeInfo()
2588
    # FIXME: this is the global kvm version, but the actual version can be
2589
    # customized as an hv parameter. we should use the nodegroup's default kvm
2590
    # path parameter here.
2591
    _, v_major, v_min, v_rev = self._GetKVMVersion(constants.KVM_PATH)
2592
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2593
    return result
2594

    
2595
  @classmethod
2596
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2597
    """Return a command for connecting to the console of an instance.
2598

2599
    """
2600
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2601
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2602
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2603
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2604
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2605
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2606
      return objects.InstanceConsole(instance=instance.name,
2607
                                     kind=constants.CONS_SSH,
2608
                                     host=instance.primary_node,
2609
                                     user=constants.SSH_CONSOLE_USER,
2610
                                     command=cmd)
2611

    
2612
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2613
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2614
      display = instance.network_port - constants.VNC_BASE_PORT
2615
      return objects.InstanceConsole(instance=instance.name,
2616
                                     kind=constants.CONS_VNC,
2617
                                     host=vnc_bind_address,
2618
                                     port=instance.network_port,
2619
                                     display=display)
2620

    
2621
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2622
    if spice_bind:
2623
      return objects.InstanceConsole(instance=instance.name,
2624
                                     kind=constants.CONS_SPICE,
2625
                                     host=spice_bind,
2626
                                     port=instance.network_port)
2627

    
2628
    return objects.InstanceConsole(instance=instance.name,
2629
                                   kind=constants.CONS_MESSAGE,
2630
                                   message=("No serial shell for instance %s" %
2631
                                            instance.name))
2632

    
2633
  def Verify(self):
2634
    """Verify the hypervisor.
2635

2636
    Check that the required binaries exist.
2637

2638
    @return: Problem description if something is wrong, C{None} otherwise
2639

2640
    """
2641
    msgs = []
2642
    # FIXME: this is the global kvm binary, but the actual path can be
2643
    # customized as an hv parameter; we should use the nodegroup's
2644
    # default kvm path parameter here.
2645
    if not os.path.exists(constants.KVM_PATH):
2646
      msgs.append("The KVM binary ('%s') does not exist" % constants.KVM_PATH)
2647
    if not os.path.exists(constants.SOCAT_PATH):
2648
      msgs.append("The socat binary ('%s') does not exist" %
2649
                  constants.SOCAT_PATH)
2650

    
2651
    return self._FormatVerifyResults(msgs)
2652

    
2653
  @classmethod
2654
  def CheckParameterSyntax(cls, hvparams):
2655
    """Check the given parameters for validity.
2656

2657
    @type hvparams:  dict
2658
    @param hvparams: dictionary with parameter names/value
2659
    @raise errors.HypervisorError: when a parameter is not valid
2660

2661
    """
2662
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2663

    
2664
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2665
    if kernel_path:
2666
      if not hvparams[constants.HV_ROOT_PATH]:
2667
        raise errors.HypervisorError("Need a root partition for the instance,"
2668
                                     " if a kernel is defined")
2669

    
2670
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2671
        not hvparams[constants.HV_VNC_X509]):
2672
      raise errors.HypervisorError("%s must be defined, if %s is" %
2673
                                   (constants.HV_VNC_X509,
2674
                                    constants.HV_VNC_X509_VERIFY))
2675

    
2676
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2677
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2678
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2679
      if not serial_speed or serial_speed not in valid_speeds:
2680
        raise errors.HypervisorError("Invalid serial console speed, must be"
2681
                                     " one of: %s" %
2682
                                     utils.CommaJoin(valid_speeds))
2683

    
2684
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2685
    if (boot_order == constants.HT_BO_CDROM and
2686
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2687
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2688
                                   " ISO path")
2689

    
2690
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2691
    if security_model == constants.HT_SM_USER:
2692
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2693
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2694
                                     " must be specified")
2695
    elif (security_model == constants.HT_SM_NONE or
2696
          security_model == constants.HT_SM_POOL):
2697
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2698
        raise errors.HypervisorError("Cannot have a security domain when the"
2699
                                     " security model is 'none' or 'pool'")
2700

    
2701
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2702
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2703
    if spice_bind:
2704
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2705
        # if an IP version is specified, the spice_bind parameter must be an
2706
        # IP of that family
2707
        if (netutils.IP4Address.IsValid(spice_bind) and
2708
            spice_ip_version != constants.IP4_VERSION):
2709
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2710
                                       " the specified IP version is %s" %
2711
                                       (spice_bind, spice_ip_version))
2712

    
2713
        if (netutils.IP6Address.IsValid(spice_bind) and
2714
            spice_ip_version != constants.IP6_VERSION):
2715
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2716
                                       " the specified IP version is %s" %
2717
                                       (spice_bind, spice_ip_version))
2718
    else:
2719
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2720
      # error if any of them is set without it.
2721
      for param in _SPICE_ADDITIONAL_PARAMS:
2722
        if hvparams[param]:
2723
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2724
                                       (param, constants.HV_KVM_SPICE_BIND))
2725

    
2726
  @classmethod
2727
  def ValidateParameters(cls, hvparams):
2728
    """Check the given parameters for validity.
2729

2730
    @type hvparams:  dict
2731
    @param hvparams: dictionary with parameter names/value
2732
    @raise errors.HypervisorError: when a parameter is not valid
2733

2734
    """
2735
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2736

    
2737
    kvm_path = hvparams[constants.HV_KVM_PATH]
2738

    
2739
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2740
    if security_model == constants.HT_SM_USER:
2741
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2742
      try:
2743
        pwd.getpwnam(username)
2744
      except KeyError:
2745
        raise errors.HypervisorError("Unknown security domain user %s"
2746
                                     % username)
2747

    
2748
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2749
    if spice_bind:
2750
      # only one of VNC and SPICE can be used currently.
2751
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2752
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2753
                                     " only one of them can be used at a"
2754
                                     " given time")
2755

    
2756
      # check that KVM supports SPICE
2757
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2758
      if not cls._SPICE_RE.search(kvmhelp):
2759
        raise errors.HypervisorError("SPICE is configured, but it is not"
2760
                                     " supported according to 'kvm --help'")
2761

    
2762
      # if spice_bind is not an IP address, it must be a valid interface
2763
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2764
                       netutils.IP6Address.IsValid(spice_bind))
2765
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2766
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2767
                                     " a valid IP address or interface name" %
2768
                                     constants.HV_KVM_SPICE_BIND)
2769

    
2770
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2771
    if machine_version:
2772
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2773
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2774
        raise errors.HypervisorError("Unsupported machine version: %s" %
2775
                                     machine_version)
2776

    
2777
  @classmethod
2778
  def PowercycleNode(cls):
2779
    """KVM powercycle, just a wrapper over Linux powercycle.
2780

2781
    """
2782
    cls.LinuxPowercycle()