Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 9dcc20b4

History | View | Annotate | Download (98.7 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 _ConfigureNIC(instance, seq, nic, tap):
1047
    """Run the network configuration script for a specified NIC
1048

1049
    @param instance: instance we're acting on
1050
    @type instance: instance object
1051
    @param seq: nic sequence number
1052
    @type seq: int
1053
    @param nic: nic we're acting on
1054
    @type nic: nic object
1055
    @param tap: the host's tap interface this NIC corresponds to
1056
    @type tap: str
1057

1058
    """
1059
    env = {
1060
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1061
      "INSTANCE": instance.name,
1062
      "MAC": nic.mac,
1063
      "MODE": nic.nicparams[constants.NIC_MODE],
1064
      "INTERFACE": tap,
1065
      "INTERFACE_INDEX": str(seq),
1066
      "INTERFACE_UUID": nic.uuid,
1067
      "TAGS": " ".join(instance.GetTags()),
1068
    }
1069

    
1070
    if nic.ip:
1071
      env["IP"] = nic.ip
1072

    
1073
    if nic.name:
1074
      env["INTERFACE_NAME"] = nic.name
1075

    
1076
    if nic.nicparams[constants.NIC_LINK]:
1077
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1078

    
1079
    if nic.network:
1080
      n = objects.Network.FromDict(nic.netinfo)
1081
      env.update(n.HooksDict())
1082

    
1083
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1084
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1085

    
1086
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1087
    if result.failed:
1088
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1089
                                   " network configuration script output: %s" %
1090
                                   (tap, result.fail_reason, result.output))
1091

    
1092
  @staticmethod
1093
  def _VerifyAffinityPackage():
1094
    if affinity is None:
1095
      raise errors.HypervisorError("affinity Python package not"
1096
                                   " found; cannot use CPU pinning under KVM")
1097

    
1098
  @staticmethod
1099
  def _BuildAffinityCpuMask(cpu_list):
1100
    """Create a CPU mask suitable for sched_setaffinity from a list of
1101
    CPUs.
1102

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

1106
    @type cpu_list: list of int
1107
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1108
    @rtype: int
1109
    @return: a bit mask of CPU affinities
1110

1111
    """
1112
    if cpu_list == constants.CPU_PINNING_OFF:
1113
      return constants.CPU_PINNING_ALL_KVM
1114
    else:
1115
      return sum(2 ** cpu for cpu in cpu_list)
1116

    
1117
  @classmethod
1118
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1119
    """Change CPU affinity for running VM according to given CPU mask.
1120

1121
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1122
    @type cpu_mask: string
1123
    @param process_id: process ID of KVM process. Used to pin entire VM
1124
                       to physical CPUs.
1125
    @type process_id: int
1126
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1127
    @type thread_dict: dict int:int
1128

1129
    """
1130
    # Convert the string CPU mask to a list of list of int's
1131
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1132

    
1133
    if len(cpu_list) == 1:
1134
      all_cpu_mapping = cpu_list[0]
1135
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
1136
        # If CPU pinning has 1 entry that's "all", then do nothing
1137
        pass
1138
      else:
1139
        # If CPU pinning has one non-all entry, map the entire VM to
1140
        # one set of physical CPUs
1141
        cls._VerifyAffinityPackage()
1142
        affinity.set_process_affinity_mask(
1143
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1144
    else:
1145
      # The number of vCPUs mapped should match the number of vCPUs
1146
      # reported by KVM. This was already verified earlier, so
1147
      # here only as a sanity check.
1148
      assert len(thread_dict) == len(cpu_list)
1149
      cls._VerifyAffinityPackage()
1150

    
1151
      # For each vCPU, map it to the proper list of physical CPUs
1152
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1153
        affinity.set_process_affinity_mask(thread_dict[i],
1154
                                           cls._BuildAffinityCpuMask(vcpu))
1155

    
1156
  def _GetVcpuThreadIds(self, instance_name):
1157
    """Get a mapping of vCPU no. to thread IDs for the instance
1158

1159
    @type instance_name: string
1160
    @param instance_name: instance in question
1161
    @rtype: dictionary of int:int
1162
    @return: a dictionary mapping vCPU numbers to thread IDs
1163

1164
    """
1165
    result = {}
1166
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1167
    for line in output.stdout.splitlines():
1168
      match = self._CPU_INFO_RE.search(line)
1169
      if not match:
1170
        continue
1171
      grp = map(int, match.groups())
1172
      result[grp[0]] = grp[1]
1173

    
1174
    return result
1175

    
1176
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1177
    """Complete CPU pinning.
1178

1179
    @type instance_name: string
1180
    @param instance_name: name of instance
1181
    @type cpu_mask: string
1182
    @param cpu_mask: CPU pinning mask as entered by user
1183

1184
    """
1185
    # Get KVM process ID, to be used if need to pin entire VM
1186
    _, pid, _ = self._InstancePidAlive(instance_name)
1187
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1188
    thread_dict = self._GetVcpuThreadIds(instance_name)
1189
    # Run CPU pinning, based on configured mask
1190
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1191

    
1192
  def ListInstances(self):
1193
    """Get the list of running instances.
1194

1195
    We can do this by listing our live instances directory and
1196
    checking whether the associated kvm process is still alive.
1197

1198
    """
1199
    result = []
1200
    for name in os.listdir(self._PIDS_DIR):
1201
      if self._InstancePidAlive(name)[2]:
1202
        result.append(name)
1203
    return result
1204

    
1205
  def GetInstanceInfo(self, instance_name):
1206
    """Get instance properties.
1207

1208
    @type instance_name: string
1209
    @param instance_name: the instance name
1210
    @rtype: tuple of strings
1211
    @return: (name, id, memory, vcpus, stat, times)
1212

1213
    """
1214
    _, pid, alive = self._InstancePidAlive(instance_name)
1215
    if not alive:
1216
      return None
1217

    
1218
    _, memory, vcpus = self._InstancePidInfo(pid)
1219
    istat = "---b-"
1220
    times = "0"
1221

    
1222
    try:
1223
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1224
      qmp.connect()
1225
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1226
      # Will fail if ballooning is not enabled, but we can then just resort to
1227
      # the value above.
1228
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1229
      memory = mem_bytes / 1048576
1230
    except errors.HypervisorError:
1231
      pass
1232

    
1233
    return (instance_name, pid, memory, vcpus, istat, times)
1234

    
1235
  def GetAllInstancesInfo(self):
1236
    """Get properties of all instances.
1237

1238
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1239

1240
    """
1241
    data = []
1242
    for name in os.listdir(self._PIDS_DIR):
1243
      try:
1244
        info = self.GetInstanceInfo(name)
1245
      except errors.HypervisorError:
1246
        # Ignore exceptions due to instances being shut down
1247
        continue
1248
      if info:
1249
        data.append(info)
1250
    return data
1251

    
1252
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1253
                                      kvmhelp, devlist):
1254
    """Generate KVM options regarding instance's block devices.
1255

1256
    @type instance: L{objects.Instance}
1257
    @param instance: the instance object
1258
    @type kvm_disks: list of tuples
1259
    @param kvm_disks: list of tuples [(disk, link_name)..]
1260
    @type kvmhelp: string
1261
    @param kvmhelp: output of kvm --help
1262
    @type devlist: string
1263
    @param devlist: output of kvm -device ?
1264
    @rtype: list
1265
    @return: list of command line options eventually used by kvm executable
1266

1267
    """
1268
    hvp = instance.hvparams
1269
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1270
    if kernel_path:
1271
      boot_disk = False
1272
    else:
1273
      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1274

    
1275
    # whether this is an older KVM version that uses the boot=on flag
1276
    # on devices
1277
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1278

    
1279
    dev_opts = []
1280
    device_driver = None
1281
    disk_type = hvp[constants.HV_DISK_TYPE]
1282
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
1283
      if_val = ",if=%s" % self._VIRTIO
1284
      try:
1285
        if self._VIRTIO_BLK_RE.search(devlist):
1286
          if_val = ",if=none"
1287
          # will be passed in -device option as driver
1288
          device_driver = self._VIRTIO_BLK_PCI
1289
      except errors.HypervisorError, _:
1290
        pass
1291
    else:
1292
      if_val = ",if=%s" % disk_type
1293
    # Cache mode
1294
    disk_cache = hvp[constants.HV_DISK_CACHE]
1295
    if instance.disk_template in constants.DTS_EXT_MIRROR:
1296
      if disk_cache != "none":
1297
        # TODO: make this a hard error, instead of a silent overwrite
1298
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1299
                        " to prevent shared storage corruption on migration",
1300
                        disk_cache)
1301
      cache_val = ",cache=none"
1302
    elif disk_cache != constants.HT_CACHE_DEFAULT:
1303
      cache_val = ",cache=%s" % disk_cache
1304
    else:
1305
      cache_val = ""
1306
    for idx, (cfdev, link_name) in enumerate(kvm_disks):
1307
      if cfdev.mode != constants.DISK_RDWR:
1308
        raise errors.HypervisorError("Instance has read-only disks which"
1309
                                     " are not supported by KVM")
1310
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
1311
      boot_val = ""
1312
      if boot_disk:
1313
        dev_opts.extend(["-boot", "c"])
1314
        boot_disk = False
1315
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1316
          boot_val = ",boot=on"
1317

    
1318
      # For ext we allow overriding disk_cache hypervisor params per disk
1319
      disk_cache = cfdev.params.get("cache", None)
1320
      if disk_cache:
1321
        cache_val = ",cache=%s" % disk_cache
1322
      drive_val = "file=%s,format=raw%s%s%s" % \
1323
                  (link_name, if_val, boot_val, cache_val)
1324

    
1325
      if device_driver:
1326
        # kvm_disks are the 4th entry of runtime file that did not exist in
1327
        # the past. That means that cfdev should always have pci slot and
1328
        # _GenerateDeviceKVMId() will not raise a exception.
1329
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK,
1330
                                         cfdev, idx)
1331
        drive_val += (",id=%s" % kvm_devid)
1332
        if cfdev.pci:
1333
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1334
        dev_val = ("%s,drive=%s,id=%s" %
1335
                   (device_driver, kvm_devid, kvm_devid))
1336
        if cfdev.pci:
1337
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1338
        dev_opts.extend(["-device", dev_val])
1339

    
1340
      # TODO: export disk geometry in IDISK_PARAMS
1341
      heads = cfdev.params.get('heads', None)
1342
      secs = cfdev.params.get('secs', None)
1343
      if heads and secs:
1344
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1345
        cyls = nr_sectors / (int(heads) * int(secs))
1346
        if cyls > 16383:
1347
          cyls = 16383
1348
        elif cyls < 2:
1349
          cyls = 2
1350
        if cyls and heads and secs:
1351
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1352
                        (cyls, int(heads), int(secs)))
1353

    
1354
      dev_opts.extend(["-drive", drive_val])
1355

    
1356
    return dev_opts
1357

    
1358
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1359
                          kvmhelp):
1360
    """Generate KVM information to start an instance.
1361

1362
    @type kvmhelp: string
1363
    @param kvmhelp: output of kvm --help
1364
    @attention: this function must not have any side-effects; for
1365
        example, it must not write to the filesystem, or read values
1366
        from the current system the are expected to differ between
1367
        nodes, since it is only run once at instance startup;
1368
        actions/kvm arguments that can vary between systems should be
1369
        done in L{_ExecuteKVMRuntime}
1370

1371
    """
1372
    # pylint: disable=R0912,R0914,R0915
1373
    hvp = instance.hvparams
1374
    self.ValidateParameters(hvp)
1375

    
1376
    pidfile = self._InstancePidFile(instance.name)
1377
    kvm = hvp[constants.HV_KVM_PATH]
1378
    kvm_cmd = [kvm]
1379
    # used just by the vnc server, if enabled
1380
    kvm_cmd.extend(["-name", instance.name])
1381
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1382

    
1383
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1384
    if hvp[constants.HV_CPU_CORES]:
1385
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1386
    if hvp[constants.HV_CPU_THREADS]:
1387
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1388
    if hvp[constants.HV_CPU_SOCKETS]:
1389
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1390

    
1391
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1392

    
1393
    kvm_cmd.extend(["-pidfile", pidfile])
1394
    kvm_cmd.extend(["-balloon", "virtio"])
1395
    kvm_cmd.extend(["-daemonize"])
1396
    if not instance.hvparams[constants.HV_ACPI]:
1397
      kvm_cmd.extend(["-no-acpi"])
1398
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1399
        constants.INSTANCE_REBOOT_EXIT:
1400
      kvm_cmd.extend(["-no-reboot"])
1401

    
1402
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1403
    if not mversion:
1404
      mversion = self._GetDefaultMachineVersion(kvm)
1405
    if self._MACHINE_RE.search(kvmhelp):
1406
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1407
      # extra hypervisor parameters. We should also investigate whether and how
1408
      # shadow_mem should be considered for the resource model.
1409
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1410
        specprop = ",accel=kvm"
1411
      else:
1412
        specprop = ""
1413
      machinespec = "%s%s" % (mversion, specprop)
1414
      kvm_cmd.extend(["-machine", machinespec])
1415
    else:
1416
      kvm_cmd.extend(["-M", mversion])
1417
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1418
          self._ENABLE_KVM_RE.search(kvmhelp)):
1419
        kvm_cmd.extend(["-enable-kvm"])
1420
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1421
            self._DISABLE_KVM_RE.search(kvmhelp)):
1422
        kvm_cmd.extend(["-disable-kvm"])
1423

    
1424
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1425
    if kernel_path:
1426
      boot_cdrom = boot_floppy = boot_network = False
1427
    else:
1428
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1429
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1430
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1431

    
1432
    if startup_paused:
1433
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1434

    
1435
    if boot_network:
1436
      kvm_cmd.extend(["-boot", "n"])
1437

    
1438
    # whether this is an older KVM version that uses the boot=on flag
1439
    # on devices
1440
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1441

    
1442
    disk_type = hvp[constants.HV_DISK_TYPE]
1443

    
1444
    #Now we can specify a different device type for CDROM devices.
1445
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1446
    if not cdrom_disk_type:
1447
      cdrom_disk_type = disk_type
1448

    
1449
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1450
    if iso_image:
1451
      options = ",format=raw,media=cdrom"
1452
      # set cdrom 'if' type
1453
      if boot_cdrom:
1454
        actual_cdrom_type = constants.HT_DISK_IDE
1455
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1456
        actual_cdrom_type = "virtio"
1457
      else:
1458
        actual_cdrom_type = cdrom_disk_type
1459
      if_val = ",if=%s" % actual_cdrom_type
1460
      # set boot flag, if needed
1461
      boot_val = ""
1462
      if boot_cdrom:
1463
        kvm_cmd.extend(["-boot", "d"])
1464
        if needs_boot_flag:
1465
          boot_val = ",boot=on"
1466
      # and finally build the entire '-drive' value
1467
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1468
      kvm_cmd.extend(["-drive", drive_val])
1469

    
1470
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1471
    if iso_image2:
1472
      options = ",format=raw,media=cdrom"
1473
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1474
        if_val = ",if=virtio"
1475
      else:
1476
        if_val = ",if=%s" % cdrom_disk_type
1477
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1478
      kvm_cmd.extend(["-drive", drive_val])
1479

    
1480
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1481
    if floppy_image:
1482
      options = ",format=raw,media=disk"
1483
      if boot_floppy:
1484
        kvm_cmd.extend(["-boot", "a"])
1485
        options = "%s,boot=on" % options
1486
      if_val = ",if=floppy"
1487
      options = "%s%s" % (options, if_val)
1488
      drive_val = "file=%s%s" % (floppy_image, options)
1489
      kvm_cmd.extend(["-drive", drive_val])
1490

    
1491
    if kernel_path:
1492
      kvm_cmd.extend(["-kernel", kernel_path])
1493
      initrd_path = hvp[constants.HV_INITRD_PATH]
1494
      if initrd_path:
1495
        kvm_cmd.extend(["-initrd", initrd_path])
1496
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1497
                     hvp[constants.HV_KERNEL_ARGS]]
1498
      if hvp[constants.HV_SERIAL_CONSOLE]:
1499
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1500
        root_append.append("console=ttyS0,%s" % serial_speed)
1501
      kvm_cmd.extend(["-append", " ".join(root_append)])
1502

    
1503
    mem_path = hvp[constants.HV_MEM_PATH]
1504
    if mem_path:
1505
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1506

    
1507
    monitor_dev = ("unix:%s,server,nowait" %
1508
                   self._InstanceMonitor(instance.name))
1509
    kvm_cmd.extend(["-monitor", monitor_dev])
1510
    if hvp[constants.HV_SERIAL_CONSOLE]:
1511
      serial_dev = ("unix:%s,server,nowait" %
1512
                    self._InstanceSerial(instance.name))
1513
      kvm_cmd.extend(["-serial", serial_dev])
1514
    else:
1515
      kvm_cmd.extend(["-serial", "none"])
1516

    
1517
    mouse_type = hvp[constants.HV_USB_MOUSE]
1518
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1519
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1520
    spice_ip_version = None
1521

    
1522
    kvm_cmd.extend(["-usb"])
1523

    
1524
    if mouse_type:
1525
      kvm_cmd.extend(["-usbdevice", mouse_type])
1526
    elif vnc_bind_address:
1527
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1528

    
1529
    if vnc_bind_address:
1530
      if netutils.IP4Address.IsValid(vnc_bind_address):
1531
        if instance.network_port > constants.VNC_BASE_PORT:
1532
          display = instance.network_port - constants.VNC_BASE_PORT
1533
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1534
            vnc_arg = ":%d" % (display)
1535
          else:
1536
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1537
        else:
1538
          logging.error("Network port is not a valid VNC display (%d < %d),"
1539
                        " not starting VNC",
1540
                        instance.network_port, constants.VNC_BASE_PORT)
1541
          vnc_arg = "none"
1542

    
1543
        # Only allow tls and other option when not binding to a file, for now.
1544
        # kvm/qemu gets confused otherwise about the filename to use.
1545
        vnc_append = ""
1546
        if hvp[constants.HV_VNC_TLS]:
1547
          vnc_append = "%s,tls" % vnc_append
1548
          if hvp[constants.HV_VNC_X509_VERIFY]:
1549
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1550
                                               hvp[constants.HV_VNC_X509])
1551
          elif hvp[constants.HV_VNC_X509]:
1552
            vnc_append = "%s,x509=%s" % (vnc_append,
1553
                                         hvp[constants.HV_VNC_X509])
1554
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1555
          vnc_append = "%s,password" % vnc_append
1556

    
1557
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1558

    
1559
      else:
1560
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1561

    
1562
      kvm_cmd.extend(["-vnc", vnc_arg])
1563
    elif spice_bind:
1564
      # FIXME: this is wrong here; the iface ip address differs
1565
      # between systems, so it should be done in _ExecuteKVMRuntime
1566
      if netutils.IsValidInterface(spice_bind):
1567
        # The user specified a network interface, we have to figure out the IP
1568
        # address.
1569
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1570
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1571

    
1572
        # if the user specified an IP version and the interface does not
1573
        # have that kind of IP addresses, throw an exception
1574
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1575
          if not addresses[spice_ip_version]:
1576
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1577
                                         " for %s" % (spice_ip_version,
1578
                                                      spice_bind))
1579

    
1580
        # the user did not specify an IP version, we have to figure it out
1581
        elif (addresses[constants.IP4_VERSION] and
1582
              addresses[constants.IP6_VERSION]):
1583
          # we have both ipv4 and ipv6, let's use the cluster default IP
1584
          # version
1585
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1586
          spice_ip_version = \
1587
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1588
        elif addresses[constants.IP4_VERSION]:
1589
          spice_ip_version = constants.IP4_VERSION
1590
        elif addresses[constants.IP6_VERSION]:
1591
          spice_ip_version = constants.IP6_VERSION
1592
        else:
1593
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1594
                                       " for %s" % (spice_bind))
1595

    
1596
        spice_address = addresses[spice_ip_version][0]
1597

    
1598
      else:
1599
        # spice_bind is known to be a valid IP address, because
1600
        # ValidateParameters checked it.
1601
        spice_address = spice_bind
1602

    
1603
      spice_arg = "addr=%s" % spice_address
1604
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1605
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1606
                     (spice_arg, instance.network_port,
1607
                      pathutils.SPICE_CACERT_FILE))
1608
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1609
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1610
                      pathutils.SPICE_CERT_FILE))
1611
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1612
        if tls_ciphers:
1613
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1614
      else:
1615
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1616

    
1617
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1618
        spice_arg = "%s,disable-ticketing" % spice_arg
1619

    
1620
      if spice_ip_version:
1621
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1622

    
1623
      # Image compression options
1624
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1625
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1626
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1627
      if img_lossless:
1628
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1629
      if img_jpeg:
1630
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1631
      if img_zlib_glz:
1632
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1633

    
1634
      # Video stream detection
1635
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1636
      if video_streaming:
1637
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1638

    
1639
      # Audio compression, by default in qemu-kvm it is on
1640
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1641
        spice_arg = "%s,playback-compression=off" % spice_arg
1642
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1643
        spice_arg = "%s,agent-mouse=off" % spice_arg
1644
      else:
1645
        # Enable the spice agent communication channel between the host and the
1646
        # agent.
1647
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1648
        kvm_cmd.extend([
1649
          "-device",
1650
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1651
          ])
1652
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1653

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

    
1657
    else:
1658
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1659
      # also works in earlier versions though (tested with 1.1 and 1.3)
1660
      if self._DISPLAY_RE.search(kvmhelp):
1661
        kvm_cmd.extend(["-display", "none"])
1662
      else:
1663
        kvm_cmd.extend(["-nographic"])
1664

    
1665
    if hvp[constants.HV_USE_LOCALTIME]:
1666
      kvm_cmd.extend(["-localtime"])
1667

    
1668
    if hvp[constants.HV_KVM_USE_CHROOT]:
1669
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1670

    
1671
    # Add qemu-KVM -cpu param
1672
    if hvp[constants.HV_CPU_TYPE]:
1673
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1674

    
1675
    # As requested by music lovers
1676
    if hvp[constants.HV_SOUNDHW]:
1677
      kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1678

    
1679
    # Pass a -vga option if requested, or if spice is used, for backwards
1680
    # compatibility.
1681
    if hvp[constants.HV_VGA]:
1682
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1683
    elif spice_bind:
1684
      kvm_cmd.extend(["-vga", "qxl"])
1685

    
1686
    # Various types of usb devices, comma separated
1687
    if hvp[constants.HV_USB_DEVICES]:
1688
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1689
        kvm_cmd.extend(["-usbdevice", dev])
1690

    
1691
    if hvp[constants.HV_KVM_EXTRA]:
1692
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1693

    
1694
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1695
    kvm_disks = []
1696
    for disk, link_name in block_devices:
1697
      _UpdatePCISlots(disk, pci_reservations)
1698
      kvm_disks.append((disk, link_name))
1699

    
1700
    kvm_nics = []
1701
    for nic in instance.nics:
1702
      _UpdatePCISlots(nic, pci_reservations)
1703
      kvm_nics.append(nic)
1704

    
1705
    hvparams = hvp
1706

    
1707
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1708

    
1709
  def _WriteKVMRuntime(self, instance_name, data):
1710
    """Write an instance's KVM runtime
1711

1712
    """
1713
    try:
1714
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1715
                      data=data)
1716
    except EnvironmentError, err:
1717
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1718

    
1719
  def _ReadKVMRuntime(self, instance_name):
1720
    """Read an instance's KVM runtime
1721

1722
    """
1723
    try:
1724
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1725
    except EnvironmentError, err:
1726
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1727
    return file_content
1728

    
1729
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1730
    """Save an instance's KVM runtime
1731

1732
    """
1733
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1734

    
1735
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1736
    serialized_disks = [(blk.ToDict(), link)
1737
                            for blk, link in kvm_disks]
1738
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1739
                                      serialized_disks))
1740

    
1741
    self._WriteKVMRuntime(instance.name, serialized_form)
1742

    
1743
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1744
    """Load an instance's KVM runtime
1745

1746
    """
1747
    if not serialized_runtime:
1748
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1749

    
1750
    return _AnalyzeSerializedRuntime(serialized_runtime)
1751

    
1752
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1753
    """Run the KVM cmd and check for errors
1754

1755
    @type name: string
1756
    @param name: instance name
1757
    @type kvm_cmd: list of strings
1758
    @param kvm_cmd: runcmd input for kvm
1759
    @type tap_fds: list of int
1760
    @param tap_fds: fds of tap devices opened by Ganeti
1761

1762
    """
1763
    try:
1764
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1765
    finally:
1766
      for fd in tap_fds:
1767
        utils_wrapper.CloseFdNoError(fd)
1768

    
1769
    if result.failed:
1770
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1771
                                   (name, result.fail_reason, result.output))
1772
    if not self._InstancePidAlive(name)[2]:
1773
      raise errors.HypervisorError("Failed to start instance %s" % name)
1774

    
1775
  # too many local variables
1776
  # pylint: disable=R0914
1777
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1778
    """Execute a KVM cmd, after completing it with some last minute data.
1779

1780
    @type incoming: tuple of strings
1781
    @param incoming: (target_host_ip, port)
1782
    @type kvmhelp: string
1783
    @param kvmhelp: output of kvm --help
1784

1785
    """
1786
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1787
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1788
    #    have changed since the instance started; only use them if the change
1789
    #    won't affect the inside of the instance (which hasn't been rebooted).
1790
    #  - up_hvp contains the parameters as they were when the instance was
1791
    #    started, plus any new parameter which has been added between ganeti
1792
    #    versions: it is paramount that those default to a value which won't
1793
    #    affect the inside of the instance as well.
1794
    conf_hvp = instance.hvparams
1795
    name = instance.name
1796
    self._CheckDown(name)
1797

    
1798
    temp_files = []
1799

    
1800
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1801
    # the first element of kvm_cmd is always the path to the kvm binary
1802
    kvm_path = kvm_cmd[0]
1803
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1804

    
1805
    # We know it's safe to run as a different user upon migration, so we'll use
1806
    # the latest conf, from conf_hvp.
1807
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1808
    if security_model == constants.HT_SM_USER:
1809
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1810

    
1811
    keymap = conf_hvp[constants.HV_KEYMAP]
1812
    if keymap:
1813
      keymap_path = self._InstanceKeymapFile(name)
1814
      # If a keymap file is specified, KVM won't use its internal defaults. By
1815
      # first including the "en-us" layout, an error on loading the actual
1816
      # layout (e.g. because it can't be found) won't lead to a non-functional
1817
      # keyboard. A keyboard with incorrect keys is still better than none.
1818
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1819
      kvm_cmd.extend(["-k", keymap_path])
1820

    
1821
    # We have reasons to believe changing something like the nic driver/type
1822
    # upon migration won't exactly fly with the instance kernel, so for nic
1823
    # related parameters we'll use up_hvp
1824
    tapfds = []
1825
    taps = []
1826
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1827

    
1828
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1829
                                                     kvm_disks,
1830
                                                     kvmhelp,
1831
                                                     devlist)
1832
    kvm_cmd.extend(bdev_opts)
1833

    
1834
    if not kvm_nics:
1835
      kvm_cmd.extend(["-net", "none"])
1836
    else:
1837
      vnet_hdr = False
1838
      tap_extra = ""
1839
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1840
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1841
        nic_model = self._VIRTIO
1842
        try:
1843
          if self._VIRTIO_NET_RE.search(devlist):
1844
            nic_model = self._VIRTIO_NET_PCI
1845
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1846
        except errors.HypervisorError, _:
1847
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1848
          # have new virtio syntax either.
1849
          pass
1850

    
1851
        if up_hvp[constants.HV_VHOST_NET]:
1852
          # check for vhost_net support
1853
          if self._VHOST_RE.search(kvmhelp):
1854
            tap_extra = ",vhost=on"
1855
          else:
1856
            raise errors.HypervisorError("vhost_net is configured"
1857
                                         " but it is not available")
1858
      else:
1859
        nic_model = nic_type
1860

    
1861
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1862

    
1863
      for nic_seq, nic in enumerate(kvm_nics):
1864
        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1865
        tapfds.append(tapfd)
1866
        taps.append(tapname)
1867
        if kvm_supports_netdev:
1868
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1869
          try:
1870
            # kvm_nics already exist in old runtime files and thus there might
1871
            # be some entries without pci slot (therefore try: except:)
1872
            kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1873
            netdev = kvm_devid
1874
            nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1875
          except errors.HotplugError:
1876
            netdev = "netdev%d" % nic_seq
1877
          nic_val += (",netdev=%s" % netdev)
1878
          tap_val = ("type=tap,id=%s,fd=%d%s" %
1879
                     (netdev, tapfd, tap_extra))
1880
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1881
        else:
1882
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1883
                                                         nic.mac, nic_model)
1884
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1885
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1886

    
1887
    if incoming:
1888
      target, port = incoming
1889
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1890

    
1891
    # Changing the vnc password doesn't bother the guest that much. At most it
1892
    # will surprise people who connect to it. Whether positively or negatively
1893
    # it's debatable.
1894
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1895
    vnc_pwd = None
1896
    if vnc_pwd_file:
1897
      try:
1898
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1899
      except EnvironmentError, err:
1900
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1901
                                     % (vnc_pwd_file, err))
1902

    
1903
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1904
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1905
                         constants.SECURE_DIR_MODE)])
1906

    
1907
    # Automatically enable QMP if version is >= 0.14
1908
    if self._QMP_RE.search(kvmhelp):
1909
      logging.debug("Enabling QMP")
1910
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1911
                      self._InstanceQmpMonitor(instance.name)])
1912

    
1913
    # Configure the network now for starting instances and bridged interfaces,
1914
    # during FinalizeMigration for incoming instances' routed interfaces
1915
    for nic_seq, nic in enumerate(kvm_nics):
1916
      if (incoming and
1917
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1918
        continue
1919
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1920

    
1921
    # CPU affinity requires kvm to start paused, so we set this flag if the
1922
    # instance is not already paused and if we are not going to accept a
1923
    # migrating instance. In the latter case, pausing is not needed.
1924
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1925
    if start_kvm_paused:
1926
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1927

    
1928
    # Note: CPU pinning is using up_hvp since changes take effect
1929
    # during instance startup anyway, and to avoid problems when soft
1930
    # rebooting the instance.
1931
    cpu_pinning = False
1932
    if up_hvp.get(constants.HV_CPU_MASK, None):
1933
      cpu_pinning = True
1934

    
1935
    if security_model == constants.HT_SM_POOL:
1936
      ss = ssconf.SimpleStore()
1937
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1938
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1939
      uid = uidpool.RequestUnusedUid(all_uids)
1940
      try:
1941
        username = pwd.getpwuid(uid.GetUid()).pw_name
1942
        kvm_cmd.extend(["-runas", username])
1943
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1944
      except:
1945
        uidpool.ReleaseUid(uid)
1946
        raise
1947
      else:
1948
        uid.Unlock()
1949
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1950
    else:
1951
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1952

    
1953
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1954
                     constants.RUN_DIRS_MODE)])
1955
    for nic_seq, tap in enumerate(taps):
1956
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1957
                      data=tap)
1958

    
1959
    if vnc_pwd:
1960
      change_cmd = "change vnc password %s" % vnc_pwd
1961
      self._CallMonitorCommand(instance.name, change_cmd)
1962

    
1963
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1964
    # connection attempts because SPICE by default does not allow connections
1965
    # if neither a password nor the "disable_ticketing" options are specified.
1966
    # As soon as we send the password via QMP, that password is a valid ticket
1967
    # for connection.
1968
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1969
    if spice_password_file:
1970
      spice_pwd = ""
1971
      try:
1972
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1973
      except EnvironmentError, err:
1974
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1975
                                     % (spice_password_file, err))
1976

    
1977
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1978
      qmp.connect()
1979
      arguments = {
1980
          "protocol": "spice",
1981
          "password": spice_pwd,
1982
      }
1983
      qmp.Execute("set_password", arguments)
1984

    
1985
    for filename in temp_files:
1986
      utils.RemoveFile(filename)
1987

    
1988
    # If requested, set CPU affinity and resume instance execution
1989
    if cpu_pinning:
1990
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1991

    
1992
    start_memory = self._InstanceStartupMemory(instance)
1993
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1994
      self.BalloonInstanceMemory(instance, start_memory)
1995

    
1996
    if start_kvm_paused:
1997
      # To control CPU pinning, ballooning, and vnc/spice passwords
1998
      # the VM was started in a frozen state. If freezing was not
1999
      # explicitly requested resume the vm status.
2000
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2001

    
2002
  def StartInstance(self, instance, block_devices, startup_paused):
2003
    """Start an instance.
2004

2005
    """
2006
    self._CheckDown(instance.name)
2007
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2008
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2009
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2010
                                           startup_paused, kvmhelp)
2011
    self._SaveKVMRuntime(instance, kvm_runtime)
2012
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2013

    
2014
  def _CallMonitorCommand(self, instance_name, command):
2015
    """Invoke a command on the instance monitor.
2016

2017
    """
2018
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2019
    # version. The monitor protocol is designed for human consumption, whereas
2020
    # QMP is made for programmatic usage. In the worst case QMP can also
2021
    # execute monitor commands. As it is, all calls to socat take at least
2022
    # 500ms and likely more: socat can't detect the end of the reply and waits
2023
    # for 500ms of no data received before exiting (500 ms is the default for
2024
    # the "-t" parameter).
2025
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
2026
             (utils.ShellQuote(command),
2027
              constants.SOCAT_PATH,
2028
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2029
    result = utils.RunCmd(socat)
2030
    if result.failed:
2031
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2032
             " output: %s" %
2033
             (command, instance_name, result.fail_reason, result.output))
2034
      raise errors.HypervisorError(msg)
2035

    
2036
    return result
2037

    
2038
  def _GetFreePCISlot(self, instance, dev):
2039
    """Get the first available pci slot of a runnung instance.
2040

2041
    """
2042
    slots = bitarray(32)
2043
    slots.setall(False) # pylint: disable=E1101
2044
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2045
    for line in output.stdout.splitlines():
2046
      match = self._INFO_PCI_RE.search(line)
2047
      if match:
2048
        slot = int(match.group(1))
2049
        slots[slot] = True
2050

    
2051
    [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
2052
    if not free:
2053
      raise errors.HypervisorError("All PCI slots occupied")
2054

    
2055
    dev.pci = int(free)
2056

    
2057
  def VerifyHotplugSupport(self, instance, action, dev_type):
2058
    """Verifies that hotplug is supported.
2059

2060
    Hotplug is *not* supported in case of:
2061
     - security models and chroot (disk hotplug)
2062
     - fdsend module is missing (nic hot-add)
2063

2064
    @raise errors.HypervisorError: in one of the previous cases
2065

2066
    """
2067
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2068
      hvp = instance.hvparams
2069
      security_model = hvp[constants.HV_SECURITY_MODEL]
2070
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2071
      if use_chroot:
2072
        raise errors.HotplugError("Disk hotplug is not supported"
2073
                                  " in case of chroot.")
2074
      if security_model != constants.HT_SM_NONE:
2075
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2076
                                  " security models are used.")
2077

    
2078
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2079
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2080
      raise errors.HotplugError("Cannot hot-add NIC."
2081
                                " fdsend python module is missing.")
2082

    
2083
  def HotplugSupported(self, instance):
2084
    """Checks if hotplug is generally supported.
2085

2086
    Hotplug is *not* supported in case of:
2087
     - qemu versions < 1.0
2088
     - for stopped instances
2089

2090
    @raise errors.HypervisorError: in one of the previous cases
2091

2092
    """
2093
    try:
2094
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2095
    except errors.HypervisorError:
2096
      raise errors.HotplugError("Instance is probably down")
2097

    
2098
    # TODO: search for netdev_add, drive_add, device_add.....
2099
    match = self._INFO_VERSION_RE.search(output.stdout)
2100
    if not match:
2101
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2102

    
2103
    v_major, v_min, _, _ = match.groups()
2104
    if (int(v_major), int(v_min)) < (1, 0):
2105
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2106

    
2107
  def _CallHotplugCommands(self, name, cmds):
2108
    for c in cmds:
2109
      self._CallMonitorCommand(name, c)
2110
      time.sleep(1)
2111

    
2112
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2113
                            should_exist):
2114
    """Checks if a previous hotplug command has succeeded.
2115

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

2119
    @raise errors.HypervisorError: if result is not the expected one
2120

2121
    """
2122
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2123
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2124
    match = \
2125
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2126
    if match and not should_exist:
2127
      msg = "Device %s should have been removed but is still there" % kvm_devid
2128
      raise errors.HypervisorError(msg)
2129

    
2130
    if not match and should_exist:
2131
      msg = "Device %s should have been added but is missing" % kvm_devid
2132
      raise errors.HypervisorError(msg)
2133

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

    
2136
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2137
    """ Helper method to hot-add a new device
2138

2139
    It gets free pci slot generates the device name and invokes the
2140
    device specific method.
2141

2142
    """
2143
    # in case of hot-mod if the device already exists this is given
2144
    if device.pci is None:
2145
      self._GetFreePCISlot(instance, device)
2146
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2147
    runtime = self._LoadKVMRuntime(instance)
2148
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2149
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2150
                (extra, kvm_devid)]
2151
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2152
                (hex(device.pci), kvm_devid, kvm_devid)]
2153
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2154
      (tap, fd) = _OpenTap()
2155
      self._ConfigureNIC(instance, seq, device, tap)
2156
      self._PassTapFd(instance, fd, device)
2157
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2158
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2159
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2160
      cmds += ["device_add %s" % args]
2161
      utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2162

    
2163
    self._CallHotplugCommands(instance.name, cmds)
2164
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2165
    # update relevant entries in runtime file
2166
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2167
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2168
    runtime[index].append(entry)
2169
    self._SaveKVMRuntime(instance, runtime)
2170

    
2171
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2172
    """ Helper method for hot-del device
2173

2174
    It gets device info from runtime file, generates the device name and
2175
    invokes the device specific method.
2176

2177
    """
2178
    runtime = self._LoadKVMRuntime(instance)
2179
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2180
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2181
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2182
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2183
      cmds = ["device_del %s" % kvm_devid]
2184
      cmds += ["drive_del %s" % kvm_devid]
2185
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2186
      cmds = ["device_del %s" % kvm_devid]
2187
      cmds += ["netdev_del %s" % kvm_devid]
2188
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2189
    self._CallHotplugCommands(instance.name, cmds)
2190
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2191
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2192
    runtime[index].remove(entry)
2193
    self._SaveKVMRuntime(instance, runtime)
2194

    
2195
    return kvm_device.pci
2196

    
2197
  def HotModDevice(self, instance, dev_type, device, _, seq):
2198
    """ Helper method for hot-mod device
2199

2200
    It gets device info from runtime file, generates the device name and
2201
    invokes the device specific method. Currently only NICs support hot-mod
2202

2203
    """
2204
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2205
      # putting it back in the same pci slot
2206
      try:
2207
        device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2208
      except errors.HotplugError:
2209
        logging.info("Device not found in runtime file. Assuming it was"
2210
                     " previously added without --hotplug option.")
2211
      # TODO: remove sleep when socat gets removed
2212
      self.HotAddDevice(instance, dev_type, device, _, seq)
2213

    
2214
  def _PassTapFd(self, instance, fd, nic):
2215
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2216

2217
    """
2218
    # TODO: factor out code related to unix sockets.
2219
    #       squash common parts between monitor and qmp
2220
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2221
    command = "getfd %s\n" % kvm_devid
2222
    fds = [fd]
2223
    logging.info("%s", fds)
2224
    try:
2225
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2226
      monsock.connect()
2227
      fdsend.sendfds(monsock.sock, command, fds=fds)
2228
    finally:
2229
      monsock.close()
2230

    
2231
  @classmethod
2232
  def _ParseKVMVersion(cls, text):
2233
    """Parse the KVM version from the --help output.
2234

2235
    @type text: string
2236
    @param text: output of kvm --help
2237
    @return: (version, v_maj, v_min, v_rev)
2238
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2239

2240
    """
2241
    match = cls._VERSION_RE.search(text.splitlines()[0])
2242
    if not match:
2243
      raise errors.HypervisorError("Unable to get KVM version")
2244

    
2245
    v_all = match.group(0)
2246
    v_maj = int(match.group(1))
2247
    v_min = int(match.group(2))
2248
    if match.group(4):
2249
      v_rev = int(match.group(4))
2250
    else:
2251
      v_rev = 0
2252
    return (v_all, v_maj, v_min, v_rev)
2253

    
2254
  @classmethod
2255
  def _GetKVMOutput(cls, kvm_path, option):
2256
    """Return the output of a kvm invocation
2257

2258
    @type kvm_path: string
2259
    @param kvm_path: path to the kvm executable
2260
    @type option: a key of _KVMOPTS_CMDS
2261
    @param option: kvm option to fetch the output from
2262
    @return: output a supported kvm invocation
2263
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2264

2265
    """
2266
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2267

    
2268
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2269

    
2270
    result = utils.RunCmd([kvm_path] + optlist)
2271
    if result.failed and not can_fail:
2272
      raise errors.HypervisorError("Unable to get KVM %s output" %
2273
                                    " ".join(optlist))
2274
    return result.output
2275

    
2276
  @classmethod
2277
  def _GetKVMVersion(cls, kvm_path):
2278
    """Return the installed KVM version.
2279

2280
    @return: (version, v_maj, v_min, v_rev)
2281
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2282

2283
    """
2284
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2285

    
2286
  @classmethod
2287
  def _GetDefaultMachineVersion(cls, kvm_path):
2288
    """Return the default hardware revision (e.g. pc-1.1)
2289

2290
    """
2291
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2292
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2293
    if match:
2294
      return match.group(1)
2295
    else:
2296
      return "pc"
2297

    
2298
  def StopInstance(self, instance, force=False, retry=False, name=None):
2299
    """Stop an instance.
2300

2301
    """
2302
    if name is not None and not force:
2303
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2304
    if name is None:
2305
      name = instance.name
2306
      acpi = instance.hvparams[constants.HV_ACPI]
2307
    else:
2308
      acpi = False
2309
    _, pid, alive = self._InstancePidAlive(name)
2310
    if pid > 0 and alive:
2311
      if force or not acpi:
2312
        utils.KillProcess(pid)
2313
      else:
2314
        self._CallMonitorCommand(name, "system_powerdown")
2315

    
2316
  def CleanupInstance(self, instance_name):
2317
    """Cleanup after a stopped instance
2318

2319
    """
2320
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2321
    if pid > 0 and alive:
2322
      raise errors.HypervisorError("Cannot cleanup a live instance")
2323
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2324

    
2325
  def RebootInstance(self, instance):
2326
    """Reboot an instance.
2327

2328
    """
2329
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2330
    # socket the instance will stop, but now power up again. So we'll resort
2331
    # to shutdown and restart.
2332
    _, _, alive = self._InstancePidAlive(instance.name)
2333
    if not alive:
2334
      raise errors.HypervisorError("Failed to reboot instance %s:"
2335
                                   " not running" % instance.name)
2336
    # StopInstance will delete the saved KVM runtime so:
2337
    # ...first load it...
2338
    kvm_runtime = self._LoadKVMRuntime(instance)
2339
    # ...now we can safely call StopInstance...
2340
    if not self.StopInstance(instance):
2341
      self.StopInstance(instance, force=True)
2342
    # ...and finally we can save it again, and execute it...
2343
    self._SaveKVMRuntime(instance, kvm_runtime)
2344
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2345
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2346
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2347

    
2348
  def MigrationInfo(self, instance):
2349
    """Get instance information to perform a migration.
2350

2351
    @type instance: L{objects.Instance}
2352
    @param instance: instance to be migrated
2353
    @rtype: string
2354
    @return: content of the KVM runtime file
2355

2356
    """
2357
    return self._ReadKVMRuntime(instance.name)
2358

    
2359
  def AcceptInstance(self, instance, info, target):
2360
    """Prepare to accept an instance.
2361

2362
    @type instance: L{objects.Instance}
2363
    @param instance: instance to be accepted
2364
    @type info: string
2365
    @param info: content of the KVM runtime file on the source node
2366
    @type target: string
2367
    @param target: target host (usually ip), on this node
2368

2369
    """
2370
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2371
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2372
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2373
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2374
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2375
                            incoming=incoming_address)
2376

    
2377
  def FinalizeMigrationDst(self, instance, info, success):
2378
    """Finalize the instance migration on the target node.
2379

2380
    Stop the incoming mode KVM.
2381

2382
    @type instance: L{objects.Instance}
2383
    @param instance: instance whose migration is being finalized
2384

2385
    """
2386
    if success:
2387
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2388
      kvm_nics = kvm_runtime[1]
2389

    
2390
      for nic_seq, nic in enumerate(kvm_nics):
2391
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2392
          # Bridged interfaces have already been configured
2393
          continue
2394
        try:
2395
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2396
        except EnvironmentError, err:
2397
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2398
                          instance.name, nic_seq, str(err))
2399
          continue
2400
        try:
2401
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2402
        except errors.HypervisorError, err:
2403
          logging.warning(str(err))
2404

    
2405
      self._WriteKVMRuntime(instance.name, info)
2406
    else:
2407
      self.StopInstance(instance, force=True)
2408

    
2409
  def MigrateInstance(self, instance, target, live):
2410
    """Migrate an instance to a target node.
2411

2412
    The migration will not be attempted if the instance is not
2413
    currently running.
2414

2415
    @type instance: L{objects.Instance}
2416
    @param instance: the instance to be migrated
2417
    @type target: string
2418
    @param target: ip address of the target node
2419
    @type live: boolean
2420
    @param live: perform a live migration
2421

2422
    """
2423
    instance_name = instance.name
2424
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2425
    _, _, alive = self._InstancePidAlive(instance_name)
2426
    if not alive:
2427
      raise errors.HypervisorError("Instance not running, cannot migrate")
2428

    
2429
    if not live:
2430
      self._CallMonitorCommand(instance_name, "stop")
2431

    
2432
    migrate_command = ("migrate_set_speed %dm" %
2433
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2434
    self._CallMonitorCommand(instance_name, migrate_command)
2435

    
2436
    migrate_command = ("migrate_set_downtime %dms" %
2437
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2438
    self._CallMonitorCommand(instance_name, migrate_command)
2439

    
2440
    # These commands are supported in latest qemu versions.
2441
    # Since _CallMonitorCommand does not catch monitor errors
2442
    # this does not raise an exception in case command is not supported
2443
    # TODO: either parse output of command or see if the command supported
2444
    # via info help (see hotplug)
2445
    migrate_command = ("migrate_set_capability xbzrle on")
2446
    self._CallMonitorCommand(instance_name, migrate_command)
2447

    
2448
    migrate_command = ("migrate_set_capability auto-converge on")
2449
    self._CallMonitorCommand(instance_name, migrate_command)
2450

    
2451
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2452
    self._CallMonitorCommand(instance_name, migrate_command)
2453

    
2454
  def FinalizeMigrationSource(self, instance, success, live):
2455
    """Finalize the instance migration on the source node.
2456

2457
    @type instance: L{objects.Instance}
2458
    @param instance: the instance that was migrated
2459
    @type success: bool
2460
    @param success: whether the migration succeeded or not
2461
    @type live: bool
2462
    @param live: whether the user requested a live migration or not
2463

2464
    """
2465
    if success:
2466
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2467
      utils.KillProcess(pid)
2468
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2469
    elif live:
2470
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2471

    
2472
  def GetMigrationStatus(self, instance):
2473
    """Get the migration status
2474

2475
    @type instance: L{objects.Instance}
2476
    @param instance: the instance that is being migrated
2477
    @rtype: L{objects.MigrationStatus}
2478
    @return: the status of the current migration (one of
2479
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2480
             progress info that can be retrieved from the hypervisor
2481

2482
    """
2483
    info_command = "info migrate"
2484
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2485
      result = self._CallMonitorCommand(instance.name, info_command)
2486
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2487
      if not match:
2488
        if not result.stdout:
2489
          logging.info("KVM: empty 'info migrate' result")
2490
        else:
2491
          logging.warning("KVM: unknown 'info migrate' result: %s",
2492
                          result.stdout)
2493
      else:
2494
        status = match.group(1)
2495
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2496
          migration_status = objects.MigrationStatus(status=status)
2497
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2498
          if match:
2499
            migration_status.transferred_ram = match.group("transferred")
2500
            migration_status.total_ram = match.group("total")
2501

    
2502
          return migration_status
2503

    
2504
        logging.warning("KVM: unknown migration status '%s'", status)
2505

    
2506
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2507

    
2508
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2509

    
2510
  def BalloonInstanceMemory(self, instance, mem):
2511
    """Balloon an instance memory to a certain value.
2512

2513
    @type instance: L{objects.Instance}
2514
    @param instance: instance to be accepted
2515
    @type mem: int
2516
    @param mem: actual memory size to use for instance runtime
2517

2518
    """
2519
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2520

    
2521
  def GetNodeInfo(self):
2522
    """Return information about the node.
2523

2524
    @return: a dict with the following keys (values in MiB):
2525
          - memory_total: the total memory size on the node
2526
          - memory_free: the available memory on the node for instances
2527
          - memory_dom0: the memory used by the node itself, if available
2528
          - hv_version: the hypervisor version in the form (major, minor,
2529
                        revision)
2530

2531
    """
2532
    result = self.GetLinuxNodeInfo()
2533
    # FIXME: this is the global kvm version, but the actual version can be
2534
    # customized as an hv parameter. we should use the nodegroup's default kvm
2535
    # path parameter here.
2536
    _, v_major, v_min, v_rev = self._GetKVMVersion(constants.KVM_PATH)
2537
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2538
    return result
2539

    
2540
  @classmethod
2541
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2542
    """Return a command for connecting to the console of an instance.
2543

2544
    """
2545
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2546
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2547
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2548
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2549
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2550
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2551
      return objects.InstanceConsole(instance=instance.name,
2552
                                     kind=constants.CONS_SSH,
2553
                                     host=instance.primary_node,
2554
                                     user=constants.SSH_CONSOLE_USER,
2555
                                     command=cmd)
2556

    
2557
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2558
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2559
      display = instance.network_port - constants.VNC_BASE_PORT
2560
      return objects.InstanceConsole(instance=instance.name,
2561
                                     kind=constants.CONS_VNC,
2562
                                     host=vnc_bind_address,
2563
                                     port=instance.network_port,
2564
                                     display=display)
2565

    
2566
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2567
    if spice_bind:
2568
      return objects.InstanceConsole(instance=instance.name,
2569
                                     kind=constants.CONS_SPICE,
2570
                                     host=spice_bind,
2571
                                     port=instance.network_port)
2572

    
2573
    return objects.InstanceConsole(instance=instance.name,
2574
                                   kind=constants.CONS_MESSAGE,
2575
                                   message=("No serial shell for instance %s" %
2576
                                            instance.name))
2577

    
2578
  def Verify(self):
2579
    """Verify the hypervisor.
2580

2581
    Check that the required binaries exist.
2582

2583
    @return: Problem description if something is wrong, C{None} otherwise
2584

2585
    """
2586
    msgs = []
2587
    # FIXME: this is the global kvm binary, but the actual path can be
2588
    # customized as an hv parameter; we should use the nodegroup's
2589
    # default kvm path parameter here.
2590
    if not os.path.exists(constants.KVM_PATH):
2591
      msgs.append("The KVM binary ('%s') does not exist" % constants.KVM_PATH)
2592
    if not os.path.exists(constants.SOCAT_PATH):
2593
      msgs.append("The socat binary ('%s') does not exist" %
2594
                  constants.SOCAT_PATH)
2595

    
2596
    return self._FormatVerifyResults(msgs)
2597

    
2598
  @classmethod
2599
  def CheckParameterSyntax(cls, hvparams):
2600
    """Check the given parameters for validity.
2601

2602
    @type hvparams:  dict
2603
    @param hvparams: dictionary with parameter names/value
2604
    @raise errors.HypervisorError: when a parameter is not valid
2605

2606
    """
2607
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2608

    
2609
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2610
    if kernel_path:
2611
      if not hvparams[constants.HV_ROOT_PATH]:
2612
        raise errors.HypervisorError("Need a root partition for the instance,"
2613
                                     " if a kernel is defined")
2614

    
2615
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2616
        not hvparams[constants.HV_VNC_X509]):
2617
      raise errors.HypervisorError("%s must be defined, if %s is" %
2618
                                   (constants.HV_VNC_X509,
2619
                                    constants.HV_VNC_X509_VERIFY))
2620

    
2621
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2622
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2623
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2624
      if not serial_speed or serial_speed not in valid_speeds:
2625
        raise errors.HypervisorError("Invalid serial console speed, must be"
2626
                                     " one of: %s" %
2627
                                     utils.CommaJoin(valid_speeds))
2628

    
2629
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2630
    if (boot_order == constants.HT_BO_CDROM and
2631
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2632
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2633
                                   " ISO path")
2634

    
2635
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2636
    if security_model == constants.HT_SM_USER:
2637
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2638
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2639
                                     " must be specified")
2640
    elif (security_model == constants.HT_SM_NONE or
2641
          security_model == constants.HT_SM_POOL):
2642
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2643
        raise errors.HypervisorError("Cannot have a security domain when the"
2644
                                     " security model is 'none' or 'pool'")
2645

    
2646
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2647
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2648
    if spice_bind:
2649
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2650
        # if an IP version is specified, the spice_bind parameter must be an
2651
        # IP of that family
2652
        if (netutils.IP4Address.IsValid(spice_bind) and
2653
            spice_ip_version != constants.IP4_VERSION):
2654
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2655
                                       " the specified IP version is %s" %
2656
                                       (spice_bind, spice_ip_version))
2657

    
2658
        if (netutils.IP6Address.IsValid(spice_bind) and
2659
            spice_ip_version != constants.IP6_VERSION):
2660
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2661
                                       " the specified IP version is %s" %
2662
                                       (spice_bind, spice_ip_version))
2663
    else:
2664
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2665
      # error if any of them is set without it.
2666
      for param in _SPICE_ADDITIONAL_PARAMS:
2667
        if hvparams[param]:
2668
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2669
                                       (param, constants.HV_KVM_SPICE_BIND))
2670

    
2671
  @classmethod
2672
  def ValidateParameters(cls, hvparams):
2673
    """Check the given parameters for validity.
2674

2675
    @type hvparams:  dict
2676
    @param hvparams: dictionary with parameter names/value
2677
    @raise errors.HypervisorError: when a parameter is not valid
2678

2679
    """
2680
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2681

    
2682
    kvm_path = hvparams[constants.HV_KVM_PATH]
2683

    
2684
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2685
    if security_model == constants.HT_SM_USER:
2686
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2687
      try:
2688
        pwd.getpwnam(username)
2689
      except KeyError:
2690
        raise errors.HypervisorError("Unknown security domain user %s"
2691
                                     % username)
2692

    
2693
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2694
    if spice_bind:
2695
      # only one of VNC and SPICE can be used currently.
2696
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2697
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2698
                                     " only one of them can be used at a"
2699
                                     " given time")
2700

    
2701
      # check that KVM supports SPICE
2702
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2703
      if not cls._SPICE_RE.search(kvmhelp):
2704
        raise errors.HypervisorError("SPICE is configured, but it is not"
2705
                                     " supported according to 'kvm --help'")
2706

    
2707
      # if spice_bind is not an IP address, it must be a valid interface
2708
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2709
                       netutils.IP6Address.IsValid(spice_bind))
2710
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2711
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2712
                                     " a valid IP address or interface name" %
2713
                                     constants.HV_KVM_SPICE_BIND)
2714

    
2715
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2716
    if machine_version:
2717
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2718
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2719
        raise errors.HypervisorError("Unsupported machine version: %s" %
2720
                                     machine_version)
2721

    
2722
  @classmethod
2723
  def PowercycleNode(cls):
2724
    """KVM powercycle, just a wrapper over Linux powercycle.
2725

2726
    """
2727
    cls.LinuxPowercycle()