Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 57304dad

History | View | Annotate | Download (100.2 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 _GetFreeSlot(slots, slot=None, reserve=False):
147
  """Helper method to get first available slot in a bitarray
148

149
  @type slots: bitarray
150
  @param slots: the bitarray to operate on
151
  @type slot: integer
152
  @param slot: if given we check whether the slot is free
153
  @type reserve: boolean
154
  @param reserve: whether to reserve the first available slot or not
155
  @return: the idx of the (first) available slot
156
  @raise errors.HotplugError: If all slots in a bitarray are occupied
157
    or the given slot is not free.
158

159
  """
160
  if slot is not None:
161
    assert slot < len(slots)
162
    if slots[slot]:
163
      raise errors.HypervisorError("Slots %d occupied" % slot)
164

    
165
  else:
166
    avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
167
    if not avail:
168
      raise errors.HypervisorError("All slots occupied")
169

    
170
    slot = int(avail[0])
171

    
172
  if reserve:
173
    slots[slot] = True
174

    
175
  return slot
176

    
177

    
178
def _GetExistingDeviceInfo(dev_type, device, runtime):
179
  """Helper function to get an existing device inside the runtime file
180

181
  Used when an instance is running. Load kvm runtime file and search
182
  for a device based on its type and uuid.
183

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

194
  """
195
  index = _DEVICE_RUNTIME_INDEX[dev_type]
196
  found = _FIND_RUNTIME_ENTRY[dev_type](device, runtime[index])
197
  if not found:
198
    raise errors.HotplugError("Cannot find runtime info for %s with UUID %s" %
199
                              (dev_type, device.uuid))
200

    
201
  return found[0]
202

    
203

    
204
def _UpgradeSerializedRuntime(serialized_runtime):
205
  """Upgrade runtime data
206

207
  Remove any deprecated fields or change the format of the data.
208
  The runtime files are not upgraded when Ganeti is upgraded, so the required
209
  modification have to be performed here.
210

211
  @type serialized_runtime: string
212
  @param serialized_runtime: raw text data read from actual runtime file
213
  @return: (cmd, nic dicts, hvparams, bdev dicts)
214
  @rtype: tuple
215

216
  """
217
  loaded_runtime = serializer.Load(serialized_runtime)
218
  kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
219
  if len(loaded_runtime) >= 4:
220
    serialized_disks = loaded_runtime[3]
221
  else:
222
    serialized_disks = []
223

    
224
  for nic in serialized_nics:
225
    # Add a dummy uuid slot if an pre-2.8 NIC is found
226
    if "uuid" not in nic:
227
      nic["uuid"] = utils.NewUUID()
228

    
229
  return kvm_cmd, serialized_nics, hvparams, serialized_disks
230

    
231

    
232
def _AnalyzeSerializedRuntime(serialized_runtime):
233
  """Return runtime entries for a serialized runtime file
234

235
  @type serialized_runtime: string
236
  @param serialized_runtime: raw text data read from actual runtime file
237
  @return: (cmd, nics, hvparams, bdevs)
238
  @rtype: tuple
239

240
  """
241
  kvm_cmd, serialized_nics, hvparams, serialized_disks = \
242
    _UpgradeSerializedRuntime(serialized_runtime)
243
  kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
244
  kvm_disks = [(objects.Disk.FromDict(sdisk), link)
245
               for sdisk, link in serialized_disks]
246

    
247
  return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
248

    
249

    
250
def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
251
  """Retrieves supported TUN features from file descriptor.
252

253
  @see: L{_ProbeTapVnetHdr}
254

255
  """
256
  req = struct.pack("I", 0)
257
  try:
258
    buf = _ioctl(fd, TUNGETFEATURES, req)
259
  except EnvironmentError, err:
260
    logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
261
    return None
262
  else:
263
    (flags, ) = struct.unpack("I", buf)
264
    return flags
265

    
266

    
267
def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
268
  """Check whether to enable the IFF_VNET_HDR flag.
269

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

280
   @type fd: int
281
   @param fd: the file descriptor of /dev/net/tun
282

283
  """
284
  flags = _features_fn(fd)
285

    
286
  if flags is None:
287
    # Not supported
288
    return False
289

    
290
  result = bool(flags & IFF_VNET_HDR)
291

    
292
  if not result:
293
    logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
294

    
295
  return result
296

    
297

    
298
def _OpenTap(vnet_hdr=True):
299
  """Open a new tap device and return its file descriptor.
300

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

304
  @type vnet_hdr: boolean
305
  @param vnet_hdr: Enable the VNET Header
306
  @return: (ifname, tapfd)
307
  @rtype: tuple
308

309
  """
310
  try:
311
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
312
  except EnvironmentError:
313
    raise errors.HypervisorError("Failed to open /dev/net/tun")
314

    
315
  flags = IFF_TAP | IFF_NO_PI
316

    
317
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
318
    flags |= IFF_VNET_HDR
319

    
320
  # The struct ifreq ioctl request (see netdevice(7))
321
  ifr = struct.pack("16sh", "", flags)
322

    
323
  try:
324
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
325
  except EnvironmentError, err:
326
    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
327
                                 err)
328

    
329
  # Get the interface name from the ioctl
330
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
331
  return (ifname, tapfd)
332

    
333

    
334
class QmpMessage:
335
  """QEMU Messaging Protocol (QMP) message.
336

337
  """
338
  def __init__(self, data):
339
    """Creates a new QMP message based on the passed data.
340

341
    """
342
    if not isinstance(data, dict):
343
      raise TypeError("QmpMessage must be initialized with a dict")
344

    
345
    self.data = data
346

    
347
  def __getitem__(self, field_name):
348
    """Get the value of the required field if present, or None.
349

350
    Overrides the [] operator to provide access to the message data,
351
    returning None if the required item is not in the message
352
    @return: the value of the field_name field, or None if field_name
353
             is not contained in the message
354

355
    """
356
    return self.data.get(field_name, None)
357

    
358
  def __setitem__(self, field_name, field_value):
359
    """Set the value of the required field_name to field_value.
360

361
    """
362
    self.data[field_name] = field_value
363

    
364
  @staticmethod
365
  def BuildFromJsonString(json_string):
366
    """Build a QmpMessage from a JSON encoded string.
367

368
    @type json_string: str
369
    @param json_string: JSON string representing the message
370
    @rtype: L{QmpMessage}
371
    @return: a L{QmpMessage} built from json_string
372

373
    """
374
    # Parse the string
375
    data = serializer.LoadJson(json_string)
376
    return QmpMessage(data)
377

    
378
  def __str__(self):
379
    # The protocol expects the JSON object to be sent as a single line.
380
    return serializer.DumpJson(self.data)
381

    
382
  def __eq__(self, other):
383
    # When comparing two QmpMessages, we are interested in comparing
384
    # their internal representation of the message data
385
    return self.data == other.data
386

    
387

    
388
class MonitorSocket(object):
389
  _SOCKET_TIMEOUT = 5
390

    
391
  def __init__(self, monitor_filename):
392
    """Instantiates the MonitorSocket object.
393

394
    @type monitor_filename: string
395
    @param monitor_filename: the filename of the UNIX raw socket on which the
396
                             monitor (QMP or simple one) is listening
397

398
    """
399
    self.monitor_filename = monitor_filename
400
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
401
    # We want to fail if the server doesn't send a complete message
402
    # in a reasonable amount of time
403
    self.sock.settimeout(self._SOCKET_TIMEOUT)
404
    self._connected = False
405

    
406
  def _check_socket(self):
407
    sock_stat = None
408
    try:
409
      sock_stat = os.stat(self.monitor_filename)
410
    except EnvironmentError, err:
411
      if err.errno == errno.ENOENT:
412
        raise errors.HypervisorError("No monitor socket found")
413
      else:
414
        raise errors.HypervisorError("Error checking monitor socket: %s",
415
                                     utils.ErrnoOrStr(err))
416
    if not stat.S_ISSOCK(sock_stat.st_mode):
417
      raise errors.HypervisorError("Monitor socket is not a socket")
418

    
419
  def _check_connection(self):
420
    """Make sure that the connection is established.
421

422
    """
423
    if not self._connected:
424
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
425
                                   " invoke connect() on it")
426

    
427
  def connect(self):
428
    """Connects to the monitor.
429

430
    Connects to the UNIX socket
431

432
    @raise errors.HypervisorError: when there are communication errors
433

434
    """
435
    if self._connected:
436
      raise errors.ProgrammerError("Cannot connect twice")
437

    
438
    self._check_socket()
439

    
440
    # Check file existance/stuff
441
    try:
442
      self.sock.connect(self.monitor_filename)
443
    except EnvironmentError:
444
      raise errors.HypervisorError("Can't connect to qmp socket")
445
    self._connected = True
446

    
447
  def close(self):
448
    """Closes the socket
449

450
    It cannot be used after this call.
451

452
    """
453
    self.sock.close()
454

    
455

    
456
class QmpConnection(MonitorSocket):
457
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
458

459
  """
460
  _FIRST_MESSAGE_KEY = "QMP"
461
  _EVENT_KEY = "event"
462
  _ERROR_KEY = "error"
463
  _RETURN_KEY = RETURN_KEY = "return"
464
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
465
  _ERROR_CLASS_KEY = "class"
466
  _ERROR_DESC_KEY = "desc"
467
  _EXECUTE_KEY = "execute"
468
  _ARGUMENTS_KEY = "arguments"
469
  _CAPABILITIES_COMMAND = "qmp_capabilities"
470
  _MESSAGE_END_TOKEN = "\r\n"
471

    
472
  def __init__(self, monitor_filename):
473
    super(QmpConnection, self).__init__(monitor_filename)
474
    self._buf = ""
475

    
476
  def connect(self):
477
    """Connects to the QMP monitor.
478

479
    Connects to the UNIX socket and makes sure that we can actually send and
480
    receive data to the kvm instance via QMP.
481

482
    @raise errors.HypervisorError: when there are communication errors
483
    @raise errors.ProgrammerError: when there are data serialization errors
484

485
    """
486
    super(QmpConnection, self).connect()
487
    # Check if we receive a correct greeting message from the server
488
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
489
    greeting = self._Recv()
490
    if not greeting[self._FIRST_MESSAGE_KEY]:
491
      self._connected = False
492
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
493
                                   " server greeting")
494

    
495
    # This is needed because QMP can return more than one greetings
496
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
497
    self._buf = ""
498

    
499
    # Let's put the monitor in command mode using the qmp_capabilities
500
    # command, or else no command will be executable.
501
    # (As per the QEMU Protocol Specification 0.1 - section 4)
502
    self.Execute(self._CAPABILITIES_COMMAND)
503

    
504
  def _ParseMessage(self, buf):
505
    """Extract and parse a QMP message from the given buffer.
506

507
    Seeks for a QMP message in the given buf. If found, it parses it and
508
    returns it together with the rest of the characters in the buf.
509
    If no message is found, returns None and the whole buffer.
510

511
    @raise errors.ProgrammerError: when there are data serialization errors
512

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

    
525
    return (message, buf)
526

    
527
  def _Recv(self):
528
    """Receives a message from QMP and decodes the received JSON object.
529

530
    @rtype: QmpMessage
531
    @return: the received message
532
    @raise errors.HypervisorError: when there are communication errors
533
    @raise errors.ProgrammerError: when there are data serialization errors
534

535
    """
536
    self._check_connection()
537

    
538
    # Check if there is already a message in the buffer
539
    (message, self._buf) = self._ParseMessage(self._buf)
540
    if message:
541
      return message
542

    
543
    recv_buffer = StringIO.StringIO(self._buf)
544
    recv_buffer.seek(len(self._buf))
545
    try:
546
      while True:
547
        data = self.sock.recv(4096)
548
        if not data:
549
          break
550
        recv_buffer.write(data)
551

    
552
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
553
        if message:
554
          return message
555

    
556
    except socket.timeout, err:
557
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
558
                                   "%s" % (err))
559
    except socket.error, err:
560
      raise errors.HypervisorError("Unable to receive data from KVM using the"
561
                                   " QMP protocol: %s" % err)
562

    
563
  def _Send(self, message):
564
    """Encodes and sends a message to KVM using QMP.
565

566
    @type message: QmpMessage
567
    @param message: message to send to KVM
568
    @raise errors.HypervisorError: when there are communication errors
569
    @raise errors.ProgrammerError: when there are data serialization errors
570

571
    """
572
    self._check_connection()
573
    try:
574
      message_str = str(message)
575
    except Exception, err:
576
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
577

    
578
    try:
579
      self.sock.sendall(message_str)
580
    except socket.timeout, err:
581
      raise errors.HypervisorError("Timeout while sending a QMP message: "
582
                                   "%s (%s)" % (err.string, err.errno))
583
    except socket.error, err:
584
      raise errors.HypervisorError("Unable to send data from KVM using the"
585
                                   " QMP protocol: %s" % err)
586

    
587
  def Execute(self, command, arguments=None):
588
    """Executes a QMP command and returns the response of the server.
589

590
    @type command: str
591
    @param command: the command to execute
592
    @type arguments: dict
593
    @param arguments: dictionary of arguments to be passed to the command
594
    @rtype: dict
595
    @return: dictionary representing the received JSON object
596
    @raise errors.HypervisorError: when there are communication errors
597
    @raise errors.ProgrammerError: when there are data serialization errors
598

599
    """
600
    self._check_connection()
601
    message = QmpMessage({self._EXECUTE_KEY: command})
602
    if arguments:
603
      message[self._ARGUMENTS_KEY] = arguments
604
    self._Send(message)
605

    
606
    # Events can occur between the sending of the command and the reception
607
    # of the response, so we need to filter out messages with the event key.
608
    while True:
609
      response = self._Recv()
610
      err = response[self._ERROR_KEY]
611
      if err:
612
        raise errors.HypervisorError("kvm: error executing the %s"
613
                                     " command: %s (%s):" %
614
                                     (command,
615
                                      err[self._ERROR_DESC_KEY],
616
                                      err[self._ERROR_CLASS_KEY]))
617

    
618
      elif not response[self._EVENT_KEY]:
619
        return response
620

    
621

    
622
class KVMHypervisor(hv_base.BaseHypervisor):
623
  """KVM hypervisor interface
624

625
  """
626
  CAN_MIGRATE = True
627

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

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

    
731
  _VIRTIO = "virtio"
732
  _VIRTIO_NET_PCI = "virtio-net-pci"
733
  _VIRTIO_BLK_PCI = "virtio-blk-pci"
734

    
735
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
736
                                    re.M | re.I)
737
  _MIGRATION_PROGRESS_RE = \
738
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
739
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
740
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
741

    
742
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
743
  _MIGRATION_INFO_RETRY_DELAY = 2
744

    
745
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
746

    
747
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
748
  _CPU_INFO_CMD = "info cpus"
749
  _CONT_CMD = "cont"
750

    
751
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
752
  _CHECK_MACHINE_VERSION_RE = \
753
    staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
754

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

    
770
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
771
  _INFO_PCI_CMD = "info pci"
772
  _FIND_PCI_DEVICE_RE = \
773
    staticmethod(lambda pci, devid:
774
      re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' % (pci, devid),
775
                 re.M))
776

    
777
  _INFO_VERSION_RE = \
778
    re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
779
  _INFO_VERSION_CMD = "info version"
780

    
781
  _DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000"
782

    
783
  ANCILLARY_FILES = [
784
    _KVM_NETWORK_SCRIPT,
785
    ]
786
  ANCILLARY_FILES_OPT = [
787
    _KVM_NETWORK_SCRIPT,
788
    ]
789

    
790
  # Supported kvm options to get output from
791
  _KVMOPT_HELP = "help"
792
  _KVMOPT_MLIST = "mlist"
793
  _KVMOPT_DEVICELIST = "devicelist"
794

    
795
  # Command to execute to get the output from kvm, and whether to
796
  # accept the output even on failure.
797
  _KVMOPTS_CMDS = {
798
    _KVMOPT_HELP: (["--help"], False),
799
    _KVMOPT_MLIST: (["-M", "?"], False),
800
    _KVMOPT_DEVICELIST: (["-device", "?"], True),
801
  }
802

    
803
  def __init__(self):
804
    hv_base.BaseHypervisor.__init__(self)
805
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
806
    # in a tmpfs filesystem or has been otherwise wiped out.
807
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
808
    utils.EnsureDirs(dirs)
809

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

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

    
817
  @classmethod
818
  def _InstanceUidFile(cls, instance_name):
819
    """Returns the instance uidfile.
820

821
    """
822
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
823

    
824
  @classmethod
825
  def _InstancePidInfo(cls, pid):
826
    """Check pid file for instance information.
827

828
    Check that a pid file is associated with an instance, and retrieve
829
    information from its command line.
830

831
    @type pid: string or int
832
    @param pid: process id of the instance to check
833
    @rtype: tuple
834
    @return: (instance_name, memory, vcpus)
835
    @raise errors.HypervisorError: when an instance cannot be found
836

837
    """
838
    alive = utils.IsProcessAlive(pid)
839
    if not alive:
840
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
841

    
842
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
843
    try:
844
      cmdline = utils.ReadFile(cmdline_file)
845
    except EnvironmentError, err:
846
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
847
                                   (pid, err))
848

    
849
    instance = None
850
    memory = 0
851
    vcpus = 0
852

    
853
    arg_list = cmdline.split("\x00")
854
    while arg_list:
855
      arg = arg_list.pop(0)
856
      if arg == "-name":
857
        instance = arg_list.pop(0)
858
      elif arg == "-m":
859
        memory = int(arg_list.pop(0))
860
      elif arg == "-smp":
861
        vcpus = int(arg_list.pop(0).split(",")[0])
862

    
863
    if instance is None:
864
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
865
                                   " instance" % pid)
866

    
867
    return (instance, memory, vcpus)
868

    
869
  def _InstancePidAlive(self, instance_name):
870
    """Returns the instance pidfile, pid, and liveness.
871

872
    @type instance_name: string
873
    @param instance_name: instance name
874
    @rtype: tuple
875
    @return: (pid file name, pid, liveness)
876

877
    """
878
    pidfile = self._InstancePidFile(instance_name)
879
    pid = utils.ReadPidFile(pidfile)
880

    
881
    alive = False
882
    try:
883
      cmd_instance = self._InstancePidInfo(pid)[0]
884
      alive = (cmd_instance == instance_name)
885
    except errors.HypervisorError:
886
      pass
887

    
888
    return (pidfile, pid, alive)
889

    
890
  def _CheckDown(self, instance_name):
891
    """Raises an error unless the given instance is down.
892

893
    """
894
    alive = self._InstancePidAlive(instance_name)[2]
895
    if alive:
896
      raise errors.HypervisorError("Failed to start instance %s: %s" %
897
                                   (instance_name, "already running"))
898

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

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

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

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

    
913
  @classmethod
914
  def _InstanceQmpMonitor(cls, instance_name):
915
    """Returns the instance serial QMP socket name
916

917
    """
918
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
919

    
920
  @staticmethod
921
  def _SocatUnixConsoleParams():
922
    """Returns the correct parameters for socat
923

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

926
    """
927
    if constants.SOCAT_USE_ESCAPE:
928
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
929
    else:
930
      return "echo=0,icanon=0"
931

    
932
  @classmethod
933
  def _InstanceKVMRuntime(cls, instance_name):
934
    """Returns the instance KVM runtime filename
935

936
    """
937
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
938

    
939
  @classmethod
940
  def _InstanceChrootDir(cls, instance_name):
941
    """Returns the name of the KVM chroot dir of the instance
942

943
    """
944
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
945

    
946
  @classmethod
947
  def _InstanceNICDir(cls, instance_name):
948
    """Returns the name of the directory holding the tap device files for a
949
    given instance.
950

951
    """
952
    return utils.PathJoin(cls._NICS_DIR, instance_name)
953

    
954
  @classmethod
955
  def _InstanceNICFile(cls, instance_name, seq_or_uuid):
956
    """Returns the name of the file containing the tap device for a given NIC
957

958
    """
959
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq_or_uuid))
960

    
961
  @classmethod
962
  def _GetInstanceNICTap(cls, instance_name, nic):
963
    """Returns the tap for the corresponding nic
964

965
    Search for tap file named after NIC's uuid.
966
    For old instances without uuid indexed tap files returns nothing.
967

968
    """
969
    try:
970
      return utils.ReadFile(cls._InstanceNICFile(instance_name, nic.uuid))
971
    except EnvironmentError:
972
      pass
973

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

978
    """
979
    for ident in [seq, nic.uuid]:
980
      utils.WriteFile(cls._InstanceNICFile(instance_name, ident), data=tap)
981

    
982
  @classmethod
983
  def _RemoveInstanceNICFiles(cls, instance_name, seq, nic):
984
    """Write tap name to both instance NIC files
985

986
    """
987
    for ident in [seq, nic.uuid]:
988
      utils.RemoveFile(cls._InstanceNICFile(instance_name, ident))
989

    
990
  @classmethod
991
  def _InstanceKeymapFile(cls, instance_name):
992
    """Returns the name of the file containing the keymap for a given instance
993

994
    """
995
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
996

    
997
  @classmethod
998
  def _TryReadUidFile(cls, uid_file):
999
    """Try to read a uid file
1000

1001
    """
1002
    if os.path.exists(uid_file):
1003
      try:
1004
        uid = int(utils.ReadOneLineFile(uid_file))
1005
        return uid
1006
      except EnvironmentError:
1007
        logging.warning("Can't read uid file", exc_info=True)
1008
      except (TypeError, ValueError):
1009
        logging.warning("Can't parse uid file contents", exc_info=True)
1010
    return None
1011

    
1012
  @classmethod
1013
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
1014
    """Removes an instance's rutime sockets/files/dirs.
1015

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

    
1054
  @staticmethod
1055
  def _CreateNICEnv(instance_name, nic, tap, seq=None, instance_tags=None):
1056
    """Create environment variables for a specific NIC
1057

1058
    This is needed during NIC ifup/ifdown scripts.
1059
    Since instance tags may change during NIC creation and removal
1060
    and because during cleanup instance object is not available we
1061
    pass them only upon NIC creation (instance startup/NIC hot-plugging).
1062

1063
    """
1064
    env = {
1065
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1066
      "INSTANCE": instance_name,
1067
      "MAC": nic.mac,
1068
      "MODE": nic.nicparams[constants.NIC_MODE],
1069
      "INTERFACE_UUID": nic.uuid,
1070
    }
1071

    
1072
    if instance_tags:
1073
      env["TAGS"] = " ".join(instance_tags)
1074

    
1075
    # This should always be available except for old instances in the
1076
    # cluster without uuid indexed tap files.
1077
    if tap:
1078
      env["INTERFACE"] = tap
1079

    
1080
    if seq:
1081
      env["INTERFACE_INDEX"] = str(seq)
1082

    
1083
    if nic.ip:
1084
      env["IP"] = nic.ip
1085

    
1086
    if nic.name:
1087
      env["INTERFACE_NAME"] = nic.name
1088

    
1089
    if nic.nicparams[constants.NIC_LINK]:
1090
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1091

    
1092
    if nic.network:
1093
      n = objects.Network.FromDict(nic.netinfo)
1094
      env.update(n.HooksDict())
1095

    
1096
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1097
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1098

    
1099
    return env
1100

    
1101
  @classmethod
1102
  def _ConfigureNIC(cls, instance, seq, nic, tap):
1103
    """Run the network configuration script for a specified NIC
1104

1105
    @param instance: instance we're acting on
1106
    @type instance: instance object
1107
    @param seq: nic sequence number
1108
    @type seq: int
1109
    @param nic: nic we're acting on
1110
    @type nic: nic object
1111
    @param tap: the host's tap interface this NIC corresponds to
1112
    @type tap: str
1113

1114
    """
1115
    env = cls._CreateNICEnv(instance.name, nic, tap, seq, instance.GetTags())
1116
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1117
    if result.failed:
1118
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1119
                                   " network configuration script output: %s" %
1120
                                   (tap, result.fail_reason, result.output))
1121

    
1122
  @classmethod
1123
  def _UnconfigureNic(cls, instance_name, nic, only_local=True):
1124
    """Run ifdown script for a specific NIC
1125

1126
    This is executed during instance cleanup and NIC hot-unplug
1127

1128
    @param instance: instance we're acting on
1129
    @type instance: instance object
1130
    @param nic: nic we're acting on
1131
    @type nic: nic object
1132
    @param localy: whether ifdown script should reset global conf (dns) or not
1133
    @type localy: boolean
1134

1135
    """
1136
    tap = cls._GetInstanceNICTap(instance_name, nic)
1137
    env = cls._CreateNICEnv(instance_name, nic, tap)
1138
    arg2 = str(only_local).lower()
1139
    result = utils.RunCmd([pathutils.KVM_IFDOWN, tap, arg2], env=env)
1140
    if result.failed:
1141
      raise errors.HypervisorError("Failed to unconfigure interface %s: %s;"
1142
                                   " network configuration script output: %s" %
1143
                                   (tap, result.fail_reason, result.output))
1144

    
1145
  @staticmethod
1146
  def _VerifyAffinityPackage():
1147
    if affinity is None:
1148
      raise errors.HypervisorError("affinity Python package not"
1149
                                   " found; cannot use CPU pinning under KVM")
1150

    
1151
  @staticmethod
1152
  def _BuildAffinityCpuMask(cpu_list):
1153
    """Create a CPU mask suitable for sched_setaffinity from a list of
1154
    CPUs.
1155

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

1159
    @type cpu_list: list of int
1160
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1161
    @rtype: int
1162
    @return: a bit mask of CPU affinities
1163

1164
    """
1165
    if cpu_list == constants.CPU_PINNING_OFF:
1166
      return constants.CPU_PINNING_ALL_KVM
1167
    else:
1168
      return sum(2 ** cpu for cpu in cpu_list)
1169

    
1170
  @classmethod
1171
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1172
    """Change CPU affinity for running VM according to given CPU mask.
1173

1174
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1175
    @type cpu_mask: string
1176
    @param process_id: process ID of KVM process. Used to pin entire VM
1177
                       to physical CPUs.
1178
    @type process_id: int
1179
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1180
    @type thread_dict: dict int:int
1181

1182
    """
1183
    # Convert the string CPU mask to a list of list of int's
1184
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1185

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

    
1204
      # For each vCPU, map it to the proper list of physical CPUs
1205
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1206
        affinity.set_process_affinity_mask(thread_dict[i],
1207
                                           cls._BuildAffinityCpuMask(vcpu))
1208

    
1209
  def _GetVcpuThreadIds(self, instance_name):
1210
    """Get a mapping of vCPU no. to thread IDs for the instance
1211

1212
    @type instance_name: string
1213
    @param instance_name: instance in question
1214
    @rtype: dictionary of int:int
1215
    @return: a dictionary mapping vCPU numbers to thread IDs
1216

1217
    """
1218
    result = {}
1219
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1220
    for line in output.stdout.splitlines():
1221
      match = self._CPU_INFO_RE.search(line)
1222
      if not match:
1223
        continue
1224
      grp = map(int, match.groups())
1225
      result[grp[0]] = grp[1]
1226

    
1227
    return result
1228

    
1229
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1230
    """Complete CPU pinning.
1231

1232
    @type instance_name: string
1233
    @param instance_name: name of instance
1234
    @type cpu_mask: string
1235
    @param cpu_mask: CPU pinning mask as entered by user
1236

1237
    """
1238
    # Get KVM process ID, to be used if need to pin entire VM
1239
    _, pid, _ = self._InstancePidAlive(instance_name)
1240
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1241
    thread_dict = self._GetVcpuThreadIds(instance_name)
1242
    # Run CPU pinning, based on configured mask
1243
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1244

    
1245
  def ListInstances(self):
1246
    """Get the list of running instances.
1247

1248
    We can do this by listing our live instances directory and
1249
    checking whether the associated kvm process is still alive.
1250

1251
    """
1252
    result = []
1253
    for name in os.listdir(self._PIDS_DIR):
1254
      if self._InstancePidAlive(name)[2]:
1255
        result.append(name)
1256
    return result
1257

    
1258
  def GetInstanceInfo(self, instance_name):
1259
    """Get instance properties.
1260

1261
    @type instance_name: string
1262
    @param instance_name: the instance name
1263
    @rtype: tuple of strings
1264
    @return: (name, id, memory, vcpus, stat, times)
1265

1266
    """
1267
    _, pid, alive = self._InstancePidAlive(instance_name)
1268
    if not alive:
1269
      return None
1270

    
1271
    _, memory, vcpus = self._InstancePidInfo(pid)
1272
    istat = "---b-"
1273
    times = "0"
1274

    
1275
    try:
1276
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1277
      qmp.connect()
1278
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1279
      # Will fail if ballooning is not enabled, but we can then just resort to
1280
      # the value above.
1281
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1282
      memory = mem_bytes / 1048576
1283
    except errors.HypervisorError:
1284
      pass
1285

    
1286
    return (instance_name, pid, memory, vcpus, istat, times)
1287

    
1288
  def GetAllInstancesInfo(self):
1289
    """Get properties of all instances.
1290

1291
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1292

1293
    """
1294
    data = []
1295
    for name in os.listdir(self._PIDS_DIR):
1296
      try:
1297
        info = self.GetInstanceInfo(name)
1298
      except errors.HypervisorError:
1299
        # Ignore exceptions due to instances being shut down
1300
        continue
1301
      if info:
1302
        data.append(info)
1303
    return data
1304

    
1305
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1306
                                      kvmhelp, devlist):
1307
    """Generate KVM options regarding instance's block devices.
1308

1309
    @type instance: L{objects.Instance}
1310
    @param instance: the instance object
1311
    @type kvm_disks: list of tuples
1312
    @param kvm_disks: list of tuples [(disk, link_name)..]
1313
    @type kvmhelp: string
1314
    @param kvmhelp: output of kvm --help
1315
    @type devlist: string
1316
    @param devlist: output of kvm -device ?
1317
    @rtype: list
1318
    @return: list of command line options eventually used by kvm executable
1319

1320
    """
1321
    hvp = instance.hvparams
1322
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1323
    if kernel_path:
1324
      boot_disk = False
1325
    else:
1326
      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1327

    
1328
    # whether this is an older KVM version that uses the boot=on flag
1329
    # on devices
1330
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1331

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

    
1371
      # For ext we allow overriding disk_cache hypervisor params per disk
1372
      disk_cache = cfdev.params.get("cache", None)
1373
      if disk_cache:
1374
        cache_val = ",cache=%s" % disk_cache
1375
      drive_val = "file=%s,format=raw%s%s%s" % \
1376
                  (link_name, if_val, boot_val, cache_val)
1377

    
1378
      if device_driver:
1379
        # kvm_disks are the 4th entry of runtime file that did not exist in
1380
        # the past. That means that cfdev should always have pci slot and
1381
        # _GenerateDeviceKVMId() will not raise a exception.
1382
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK,
1383
                                         cfdev, idx)
1384
        drive_val += (",id=%s" % kvm_devid)
1385
        if cfdev.pci:
1386
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1387
        dev_val = ("%s,drive=%s,id=%s" %
1388
                   (device_driver, kvm_devid, kvm_devid))
1389
        if cfdev.pci:
1390
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1391
        dev_opts.extend(["-device", dev_val])
1392

    
1393
      # TODO: export disk geometry in IDISK_PARAMS
1394
      heads = cfdev.params.get('heads', None)
1395
      secs = cfdev.params.get('secs', None)
1396
      if heads and secs:
1397
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1398
        cyls = nr_sectors / (int(heads) * int(secs))
1399
        if cyls > 16383:
1400
          cyls = 16383
1401
        elif cyls < 2:
1402
          cyls = 2
1403
        if cyls and heads and secs:
1404
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1405
                        (cyls, int(heads), int(secs)))
1406

    
1407
      dev_opts.extend(["-drive", drive_val])
1408

    
1409
    return dev_opts
1410

    
1411
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1412
                          kvmhelp):
1413
    """Generate KVM information to start an instance.
1414

1415
    @type kvmhelp: string
1416
    @param kvmhelp: output of kvm --help
1417
    @attention: this function must not have any side-effects; for
1418
        example, it must not write to the filesystem, or read values
1419
        from the current system the are expected to differ between
1420
        nodes, since it is only run once at instance startup;
1421
        actions/kvm arguments that can vary between systems should be
1422
        done in L{_ExecuteKVMRuntime}
1423

1424
    """
1425
    # pylint: disable=R0912,R0914,R0915
1426
    hvp = instance.hvparams
1427
    self.ValidateParameters(hvp)
1428

    
1429
    pidfile = self._InstancePidFile(instance.name)
1430
    kvm = hvp[constants.HV_KVM_PATH]
1431
    kvm_cmd = [kvm]
1432
    # used just by the vnc server, if enabled
1433
    kvm_cmd.extend(["-name", instance.name])
1434
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1435

    
1436
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1437
    if hvp[constants.HV_CPU_CORES]:
1438
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1439
    if hvp[constants.HV_CPU_THREADS]:
1440
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1441
    if hvp[constants.HV_CPU_SOCKETS]:
1442
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1443

    
1444
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1445

    
1446
    kvm_cmd.extend(["-pidfile", pidfile])
1447
    kvm_cmd.extend(["-balloon", "virtio"])
1448
    kvm_cmd.extend(["-daemonize"])
1449
    if not instance.hvparams[constants.HV_ACPI]:
1450
      kvm_cmd.extend(["-no-acpi"])
1451
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1452
        constants.INSTANCE_REBOOT_EXIT:
1453
      kvm_cmd.extend(["-no-reboot"])
1454

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

    
1477
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1478
    if kernel_path:
1479
      boot_cdrom = boot_floppy = boot_network = False
1480
    else:
1481
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1482
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1483
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1484

    
1485
    if startup_paused:
1486
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1487

    
1488
    if boot_network:
1489
      kvm_cmd.extend(["-boot", "n"])
1490

    
1491
    # whether this is an older KVM version that uses the boot=on flag
1492
    # on devices
1493
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1494

    
1495
    disk_type = hvp[constants.HV_DISK_TYPE]
1496

    
1497
    #Now we can specify a different device type for CDROM devices.
1498
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1499
    if not cdrom_disk_type:
1500
      cdrom_disk_type = disk_type
1501

    
1502
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1503
    if iso_image:
1504
      options = ",format=raw,media=cdrom"
1505
      # set cdrom 'if' type
1506
      if boot_cdrom:
1507
        actual_cdrom_type = constants.HT_DISK_IDE
1508
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1509
        actual_cdrom_type = "virtio"
1510
      else:
1511
        actual_cdrom_type = cdrom_disk_type
1512
      if_val = ",if=%s" % actual_cdrom_type
1513
      # set boot flag, if needed
1514
      boot_val = ""
1515
      if boot_cdrom:
1516
        kvm_cmd.extend(["-boot", "d"])
1517
        if needs_boot_flag:
1518
          boot_val = ",boot=on"
1519
      # and finally build the entire '-drive' value
1520
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1521
      kvm_cmd.extend(["-drive", drive_val])
1522

    
1523
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1524
    if iso_image2:
1525
      options = ",format=raw,media=cdrom"
1526
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1527
        if_val = ",if=virtio"
1528
      else:
1529
        if_val = ",if=%s" % cdrom_disk_type
1530
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1531
      kvm_cmd.extend(["-drive", drive_val])
1532

    
1533
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1534
    if floppy_image:
1535
      options = ",format=raw,media=disk"
1536
      if boot_floppy:
1537
        kvm_cmd.extend(["-boot", "a"])
1538
        options = "%s,boot=on" % options
1539
      if_val = ",if=floppy"
1540
      options = "%s%s" % (options, if_val)
1541
      drive_val = "file=%s%s" % (floppy_image, options)
1542
      kvm_cmd.extend(["-drive", drive_val])
1543

    
1544
    if kernel_path:
1545
      kvm_cmd.extend(["-kernel", kernel_path])
1546
      initrd_path = hvp[constants.HV_INITRD_PATH]
1547
      if initrd_path:
1548
        kvm_cmd.extend(["-initrd", initrd_path])
1549
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1550
                     hvp[constants.HV_KERNEL_ARGS]]
1551
      if hvp[constants.HV_SERIAL_CONSOLE]:
1552
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1553
        root_append.append("console=ttyS0,%s" % serial_speed)
1554
      kvm_cmd.extend(["-append", " ".join(root_append)])
1555

    
1556
    mem_path = hvp[constants.HV_MEM_PATH]
1557
    if mem_path:
1558
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1559

    
1560
    monitor_dev = ("unix:%s,server,nowait" %
1561
                   self._InstanceMonitor(instance.name))
1562
    kvm_cmd.extend(["-monitor", monitor_dev])
1563
    if hvp[constants.HV_SERIAL_CONSOLE]:
1564
      serial_dev = ("unix:%s,server,nowait" %
1565
                    self._InstanceSerial(instance.name))
1566
      kvm_cmd.extend(["-serial", serial_dev])
1567
    else:
1568
      kvm_cmd.extend(["-serial", "none"])
1569

    
1570
    mouse_type = hvp[constants.HV_USB_MOUSE]
1571
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1572
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1573
    spice_ip_version = None
1574

    
1575
    kvm_cmd.extend(["-usb"])
1576

    
1577
    if mouse_type:
1578
      kvm_cmd.extend(["-usbdevice", mouse_type])
1579
    elif vnc_bind_address:
1580
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1581

    
1582
    if vnc_bind_address:
1583
      if netutils.IP4Address.IsValid(vnc_bind_address):
1584
        if instance.network_port > constants.VNC_BASE_PORT:
1585
          display = instance.network_port - constants.VNC_BASE_PORT
1586
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1587
            vnc_arg = ":%d" % (display)
1588
          else:
1589
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1590
        else:
1591
          logging.error("Network port is not a valid VNC display (%d < %d),"
1592
                        " not starting VNC",
1593
                        instance.network_port, constants.VNC_BASE_PORT)
1594
          vnc_arg = "none"
1595

    
1596
        # Only allow tls and other option when not binding to a file, for now.
1597
        # kvm/qemu gets confused otherwise about the filename to use.
1598
        vnc_append = ""
1599
        if hvp[constants.HV_VNC_TLS]:
1600
          vnc_append = "%s,tls" % vnc_append
1601
          if hvp[constants.HV_VNC_X509_VERIFY]:
1602
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1603
                                               hvp[constants.HV_VNC_X509])
1604
          elif hvp[constants.HV_VNC_X509]:
1605
            vnc_append = "%s,x509=%s" % (vnc_append,
1606
                                         hvp[constants.HV_VNC_X509])
1607
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1608
          vnc_append = "%s,password" % vnc_append
1609

    
1610
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1611

    
1612
      else:
1613
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1614

    
1615
      kvm_cmd.extend(["-vnc", vnc_arg])
1616
    elif spice_bind:
1617
      # FIXME: this is wrong here; the iface ip address differs
1618
      # between systems, so it should be done in _ExecuteKVMRuntime
1619
      if netutils.IsValidInterface(spice_bind):
1620
        # The user specified a network interface, we have to figure out the IP
1621
        # address.
1622
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1623
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1624

    
1625
        # if the user specified an IP version and the interface does not
1626
        # have that kind of IP addresses, throw an exception
1627
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1628
          if not addresses[spice_ip_version]:
1629
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1630
                                         " for %s" % (spice_ip_version,
1631
                                                      spice_bind))
1632

    
1633
        # the user did not specify an IP version, we have to figure it out
1634
        elif (addresses[constants.IP4_VERSION] and
1635
              addresses[constants.IP6_VERSION]):
1636
          # we have both ipv4 and ipv6, let's use the cluster default IP
1637
          # version
1638
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1639
          spice_ip_version = \
1640
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1641
        elif addresses[constants.IP4_VERSION]:
1642
          spice_ip_version = constants.IP4_VERSION
1643
        elif addresses[constants.IP6_VERSION]:
1644
          spice_ip_version = constants.IP6_VERSION
1645
        else:
1646
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1647
                                       " for %s" % (spice_bind))
1648

    
1649
        spice_address = addresses[spice_ip_version][0]
1650

    
1651
      else:
1652
        # spice_bind is known to be a valid IP address, because
1653
        # ValidateParameters checked it.
1654
        spice_address = spice_bind
1655

    
1656
      spice_arg = "addr=%s" % spice_address
1657
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1658
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1659
                     (spice_arg, instance.network_port,
1660
                      pathutils.SPICE_CACERT_FILE))
1661
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1662
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1663
                      pathutils.SPICE_CERT_FILE))
1664
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1665
        if tls_ciphers:
1666
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1667
      else:
1668
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1669

    
1670
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1671
        spice_arg = "%s,disable-ticketing" % spice_arg
1672

    
1673
      if spice_ip_version:
1674
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1675

    
1676
      # Image compression options
1677
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1678
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1679
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1680
      if img_lossless:
1681
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1682
      if img_jpeg:
1683
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1684
      if img_zlib_glz:
1685
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1686

    
1687
      # Video stream detection
1688
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1689
      if video_streaming:
1690
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1691

    
1692
      # Audio compression, by default in qemu-kvm it is on
1693
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1694
        spice_arg = "%s,playback-compression=off" % spice_arg
1695
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1696
        spice_arg = "%s,agent-mouse=off" % spice_arg
1697
      else:
1698
        # Enable the spice agent communication channel between the host and the
1699
        # agent.
1700
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1701
        kvm_cmd.extend([
1702
          "-device",
1703
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1704
          ])
1705
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1706

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

    
1710
    else:
1711
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1712
      # also works in earlier versions though (tested with 1.1 and 1.3)
1713
      if self._DISPLAY_RE.search(kvmhelp):
1714
        kvm_cmd.extend(["-display", "none"])
1715
      else:
1716
        kvm_cmd.extend(["-nographic"])
1717

    
1718
    if hvp[constants.HV_USE_LOCALTIME]:
1719
      kvm_cmd.extend(["-localtime"])
1720

    
1721
    if hvp[constants.HV_KVM_USE_CHROOT]:
1722
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1723

    
1724
    # Add qemu-KVM -cpu param
1725
    if hvp[constants.HV_CPU_TYPE]:
1726
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1727

    
1728
    # As requested by music lovers
1729
    if hvp[constants.HV_SOUNDHW]:
1730
      kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1731

    
1732
    # Pass a -vga option if requested, or if spice is used, for backwards
1733
    # compatibility.
1734
    if hvp[constants.HV_VGA]:
1735
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1736
    elif spice_bind:
1737
      kvm_cmd.extend(["-vga", "qxl"])
1738

    
1739
    # Various types of usb devices, comma separated
1740
    if hvp[constants.HV_USB_DEVICES]:
1741
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1742
        kvm_cmd.extend(["-usbdevice", dev])
1743

    
1744
    if hvp[constants.HV_KVM_EXTRA]:
1745
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1746

    
1747
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1748
    kvm_disks = []
1749
    for disk, link_name in block_devices:
1750
      disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1751
      kvm_disks.append((disk, link_name))
1752

    
1753
    kvm_nics = []
1754
    for nic in instance.nics:
1755
      nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1756
      kvm_nics.append(nic)
1757

    
1758
    hvparams = hvp
1759

    
1760
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1761

    
1762
  def _WriteKVMRuntime(self, instance_name, data):
1763
    """Write an instance's KVM runtime
1764

1765
    """
1766
    try:
1767
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1768
                      data=data)
1769
    except EnvironmentError, err:
1770
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1771

    
1772
  @classmethod
1773
  def _ReadKVMRuntime(cls, instance_name):
1774
    """Read an instance's KVM runtime
1775

1776
    """
1777
    try:
1778
      file_content = utils.ReadFile(cls._InstanceKVMRuntime(instance_name))
1779
    except EnvironmentError, err:
1780
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1781
    return file_content
1782

    
1783
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1784
    """Save an instance's KVM runtime
1785

1786
    """
1787
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1788

    
1789
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1790
    serialized_disks = [(blk.ToDict(), link)
1791
                            for blk, link in kvm_disks]
1792
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1793
                                      serialized_disks))
1794

    
1795
    self._WriteKVMRuntime(instance.name, serialized_form)
1796

    
1797
  @classmethod
1798
  def _LoadKVMRuntime(cls, instance_name, serialized_runtime=None):
1799
    """Load an instance's KVM runtime
1800

1801
    """
1802
    if not serialized_runtime:
1803
      serialized_runtime = cls._ReadKVMRuntime(instance_name)
1804

    
1805
    return _AnalyzeSerializedRuntime(serialized_runtime)
1806

    
1807
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1808
    """Run the KVM cmd and check for errors
1809

1810
    @type name: string
1811
    @param name: instance name
1812
    @type kvm_cmd: list of strings
1813
    @param kvm_cmd: runcmd input for kvm
1814
    @type tap_fds: list of int
1815
    @param tap_fds: fds of tap devices opened by Ganeti
1816

1817
    """
1818
    try:
1819
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1820
    finally:
1821
      for fd in tap_fds:
1822
        utils_wrapper.CloseFdNoError(fd)
1823

    
1824
    if result.failed:
1825
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1826
                                   (name, result.fail_reason, result.output))
1827
    if not self._InstancePidAlive(name)[2]:
1828
      raise errors.HypervisorError("Failed to start instance %s" % name)
1829

    
1830
  # too many local variables
1831
  # pylint: disable=R0914
1832
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1833
    """Execute a KVM cmd, after completing it with some last minute data.
1834

1835
    @type incoming: tuple of strings
1836
    @param incoming: (target_host_ip, port)
1837
    @type kvmhelp: string
1838
    @param kvmhelp: output of kvm --help
1839

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

    
1853
    temp_files = []
1854

    
1855
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1856
    # the first element of kvm_cmd is always the path to the kvm binary
1857
    kvm_path = kvm_cmd[0]
1858
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1859

    
1860
    # We know it's safe to run as a different user upon migration, so we'll use
1861
    # the latest conf, from conf_hvp.
1862
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1863
    if security_model == constants.HT_SM_USER:
1864
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1865

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

    
1876
    # We have reasons to believe changing something like the nic driver/type
1877
    # upon migration won't exactly fly with the instance kernel, so for nic
1878
    # related parameters we'll use up_hvp
1879
    tapfds = []
1880
    taps = []
1881
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1882

    
1883
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1884
                                                     kvm_disks,
1885
                                                     kvmhelp,
1886
                                                     devlist)
1887
    kvm_cmd.extend(bdev_opts)
1888

    
1889
    if not kvm_nics:
1890
      kvm_cmd.extend(["-net", "none"])
1891
    else:
1892
      vnet_hdr = False
1893
      tap_extra = ""
1894
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1895
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1896
        nic_model = self._VIRTIO
1897
        try:
1898
          if self._VIRTIO_NET_RE.search(devlist):
1899
            nic_model = self._VIRTIO_NET_PCI
1900
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1901
        except errors.HypervisorError, _:
1902
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1903
          # have new virtio syntax either.
1904
          pass
1905

    
1906
        if up_hvp[constants.HV_VHOST_NET]:
1907
          # check for vhost_net support
1908
          if self._VHOST_RE.search(kvmhelp):
1909
            tap_extra = ",vhost=on"
1910
          else:
1911
            raise errors.HypervisorError("vhost_net is configured"
1912
                                         " but it is not available")
1913
      else:
1914
        nic_model = nic_type
1915

    
1916
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1917

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

    
1942
    if incoming:
1943
      target, port = incoming
1944
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1945

    
1946
    # Changing the vnc password doesn't bother the guest that much. At most it
1947
    # will surprise people who connect to it. Whether positively or negatively
1948
    # it's debatable.
1949
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1950
    vnc_pwd = None
1951
    if vnc_pwd_file:
1952
      try:
1953
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1954
      except EnvironmentError, err:
1955
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1956
                                     % (vnc_pwd_file, err))
1957

    
1958
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1959
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1960
                         constants.SECURE_DIR_MODE)])
1961

    
1962
    # Automatically enable QMP if version is >= 0.14
1963
    if self._QMP_RE.search(kvmhelp):
1964
      logging.debug("Enabling QMP")
1965
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1966
                      self._InstanceQmpMonitor(instance.name)])
1967

    
1968
    # Configure the network now for starting instances and bridged interfaces,
1969
    # during FinalizeMigration for incoming instances' routed interfaces
1970
    for nic_seq, nic in enumerate(kvm_nics):
1971
      if (incoming and
1972
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1973
        continue
1974
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1975

    
1976
    # CPU affinity requires kvm to start paused, so we set this flag if the
1977
    # instance is not already paused and if we are not going to accept a
1978
    # migrating instance. In the latter case, pausing is not needed.
1979
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1980
    if start_kvm_paused:
1981
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1982

    
1983
    # Note: CPU pinning is using up_hvp since changes take effect
1984
    # during instance startup anyway, and to avoid problems when soft
1985
    # rebooting the instance.
1986
    cpu_pinning = False
1987
    if up_hvp.get(constants.HV_CPU_MASK, None):
1988
      cpu_pinning = True
1989

    
1990
    if security_model == constants.HT_SM_POOL:
1991
      ss = ssconf.SimpleStore()
1992
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1993
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1994
      uid = uidpool.RequestUnusedUid(all_uids)
1995
      try:
1996
        username = pwd.getpwuid(uid.GetUid()).pw_name
1997
        kvm_cmd.extend(["-runas", username])
1998
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1999
      except:
2000
        uidpool.ReleaseUid(uid)
2001
        raise
2002
      else:
2003
        uid.Unlock()
2004
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
2005
    else:
2006
      self._RunKVMCmd(name, kvm_cmd, tapfds)
2007

    
2008
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
2009
                     constants.RUN_DIRS_MODE)])
2010
    for nic_seq, tap in enumerate(taps):
2011
      nic = kvm_nics[nic_seq]
2012
      self._WriteInstanceNICFiles(instance.name, nic_seq, nic, tap)
2013

    
2014
    if vnc_pwd:
2015
      change_cmd = "change vnc password %s" % vnc_pwd
2016
      self._CallMonitorCommand(instance.name, change_cmd)
2017

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

    
2032
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2033
      qmp.connect()
2034
      arguments = {
2035
          "protocol": "spice",
2036
          "password": spice_pwd,
2037
      }
2038
      qmp.Execute("set_password", arguments)
2039

    
2040
    for filename in temp_files:
2041
      utils.RemoveFile(filename)
2042

    
2043
    # If requested, set CPU affinity and resume instance execution
2044
    if cpu_pinning:
2045
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2046

    
2047
    start_memory = self._InstanceStartupMemory(instance)
2048
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
2049
      self.BalloonInstanceMemory(instance, start_memory)
2050

    
2051
    if start_kvm_paused:
2052
      # To control CPU pinning, ballooning, and vnc/spice passwords
2053
      # the VM was started in a frozen state. If freezing was not
2054
      # explicitly requested resume the vm status.
2055
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2056

    
2057
  def StartInstance(self, instance, block_devices, startup_paused):
2058
    """Start an instance.
2059

2060
    """
2061
    self._CheckDown(instance.name)
2062
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2063
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2064
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2065
                                           startup_paused, kvmhelp)
2066
    self._SaveKVMRuntime(instance, kvm_runtime)
2067
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2068

    
2069
  def _CallMonitorCommand(self, instance_name, command, timeout=None):
2070
    """Invoke a command on the instance monitor.
2071

2072
    """
2073
    if timeout is not None:
2074
      timeout_cmd = "timeout %s" % (timeout, )
2075
    else:
2076
      timeout_cmd = ""
2077

    
2078
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2079
    # version. The monitor protocol is designed for human consumption, whereas
2080
    # QMP is made for programmatic usage. In the worst case QMP can also
2081
    # execute monitor commands. As it is, all calls to socat take at least
2082
    # 500ms and likely more: socat can't detect the end of the reply and waits
2083
    # for 500ms of no data received before exiting (500 ms is the default for
2084
    # the "-t" parameter).
2085
    socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2086
             (utils.ShellQuote(command),
2087
              timeout_cmd,
2088
              constants.SOCAT_PATH,
2089
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2090

    
2091
    result = utils.RunCmd(socat)
2092
    if result.failed:
2093
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2094
             " output: %s" %
2095
             (command, instance_name, result.fail_reason, result.output))
2096
      raise errors.HypervisorError(msg)
2097

    
2098
    return result
2099

    
2100
  def _GetFreePCISlot(self, instance, dev):
2101
    """Get the first available pci slot of a runnung instance.
2102

2103
    """
2104
    slots = bitarray(32)
2105
    slots.setall(False) # pylint: disable=E1101
2106
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2107
    for line in output.stdout.splitlines():
2108
      match = self._INFO_PCI_RE.search(line)
2109
      if match:
2110
        slot = int(match.group(1))
2111
        slots[slot] = True
2112

    
2113
    dev.pci = _GetFreeSlot(slots)
2114

    
2115
  def VerifyHotplugSupport(self, instance, action, dev_type):
2116
    """Verifies that hotplug is supported.
2117

2118
    Hotplug is *not* supported in case of:
2119
     - security models and chroot (disk hotplug)
2120
     - fdsend module is missing (nic hot-add)
2121

2122
    @raise errors.HypervisorError: in one of the previous cases
2123

2124
    """
2125
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2126
      hvp = instance.hvparams
2127
      security_model = hvp[constants.HV_SECURITY_MODEL]
2128
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2129
      if use_chroot:
2130
        raise errors.HotplugError("Disk hotplug is not supported"
2131
                                  " in case of chroot.")
2132
      if security_model != constants.HT_SM_NONE:
2133
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2134
                                  " security models are used.")
2135

    
2136
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2137
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2138
      raise errors.HotplugError("Cannot hot-add NIC."
2139
                                " fdsend python module is missing.")
2140

    
2141
  def HotplugSupported(self, instance):
2142
    """Checks if hotplug is generally supported.
2143

2144
    Hotplug is *not* supported in case of:
2145
     - qemu versions < 1.0
2146
     - for stopped instances
2147

2148
    @raise errors.HypervisorError: in one of the previous cases
2149

2150
    """
2151
    try:
2152
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2153
    except errors.HypervisorError:
2154
      raise errors.HotplugError("Instance is probably down")
2155

    
2156
    # TODO: search for netdev_add, drive_add, device_add.....
2157
    match = self._INFO_VERSION_RE.search(output.stdout)
2158
    if not match:
2159
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2160

    
2161
    v_major, v_min, _, _ = match.groups()
2162
    if (int(v_major), int(v_min)) < (1, 0):
2163
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2164

    
2165
  def _CallHotplugCommands(self, name, cmds):
2166
    for c in cmds:
2167
      self._CallMonitorCommand(name, c)
2168
      time.sleep(1)
2169

    
2170
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2171
                            should_exist):
2172
    """Checks if a previous hotplug command has succeeded.
2173

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

2177
    @raise errors.HypervisorError: if result is not the expected one
2178

2179
    """
2180
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2181
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2182
    match = \
2183
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2184
    if match and not should_exist:
2185
      msg = "Device %s should have been removed but is still there" % kvm_devid
2186
      raise errors.HypervisorError(msg)
2187

    
2188
    if not match and should_exist:
2189
      msg = "Device %s should have been added but is missing" % kvm_devid
2190
      raise errors.HypervisorError(msg)
2191

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

    
2194
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2195
    """ Helper method to hot-add a new device
2196

2197
    It gets free pci slot generates the device name and invokes the
2198
    device specific method.
2199

2200
    """
2201
    # in case of hot-mod this is given
2202
    if device.pci is None:
2203
      self._GetFreePCISlot(instance, device)
2204
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2205
    runtime = self._LoadKVMRuntime(instance.name)
2206
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2207
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2208
                (extra, kvm_devid)]
2209
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2210
                (hex(device.pci), kvm_devid, kvm_devid)]
2211
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2212
      (tap, fd) = _OpenTap()
2213
      self._ConfigureNIC(instance, seq, device, tap)
2214
      self._PassTapFd(instance, fd, device)
2215
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2216
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2217
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2218
      cmds += ["device_add %s" % args]
2219
      self._WriteInstanceNICFiles(instance.name, seq, device, tap)
2220

    
2221
    self._CallHotplugCommands(instance.name, cmds)
2222
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2223
    # update relevant entries in runtime file
2224
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2225
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2226
    runtime[index].append(entry)
2227
    self._SaveKVMRuntime(instance, runtime)
2228

    
2229
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2230
    """ Helper method for hot-del device
2231

2232
    It gets device info from runtime file, generates the device name and
2233
    invokes the device specific method.
2234

2235
    """
2236
    runtime = self._LoadKVMRuntime(instance.name)
2237
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2238
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2239
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2240
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2241
      cmds = ["device_del %s" % kvm_devid]
2242
      cmds += ["drive_del %s" % kvm_devid]
2243
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2244
      cmds = ["device_del %s" % kvm_devid]
2245
      cmds += ["netdev_del %s" % kvm_devid]
2246
      self._UnconfigureNic(instance.name, kvm_device, False)
2247
      self._RemoveInstanceNICFiles(instance.name, seq, device)
2248
    self._CallHotplugCommands(instance.name, cmds)
2249
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2250
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2251
    runtime[index].remove(entry)
2252
    self._SaveKVMRuntime(instance, runtime)
2253

    
2254
    return kvm_device.pci
2255

    
2256
  def HotModDevice(self, instance, dev_type, device, _, seq):
2257
    """ Helper method for hot-mod device
2258

2259
    It gets device info from runtime file, generates the device name and
2260
    invokes the device specific method. Currently only NICs support hot-mod
2261

2262
    """
2263
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2264
      # putting it back in the same pci slot
2265
      device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2266
      # TODO: remove sleep when socat gets removed
2267
      self.HotAddDevice(instance, dev_type, device, _, seq)
2268

    
2269
  def _PassTapFd(self, instance, fd, nic):
2270
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2271

2272
    """
2273
    # TODO: factor out code related to unix sockets.
2274
    #       squash common parts between monitor and qmp
2275
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2276
    command = "getfd %s\n" % kvm_devid
2277
    fds = [fd]
2278
    logging.info("%s", fds)
2279
    try:
2280
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2281
      monsock.connect()
2282
      fdsend.sendfds(monsock.sock, command, fds=fds)
2283
    finally:
2284
      monsock.close()
2285

    
2286
  @classmethod
2287
  def _ParseKVMVersion(cls, text):
2288
    """Parse the KVM version from the --help output.
2289

2290
    @type text: string
2291
    @param text: output of kvm --help
2292
    @return: (version, v_maj, v_min, v_rev)
2293
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2294

2295
    """
2296
    match = cls._VERSION_RE.search(text.splitlines()[0])
2297
    if not match:
2298
      raise errors.HypervisorError("Unable to get KVM version")
2299

    
2300
    v_all = match.group(0)
2301
    v_maj = int(match.group(1))
2302
    v_min = int(match.group(2))
2303
    if match.group(4):
2304
      v_rev = int(match.group(4))
2305
    else:
2306
      v_rev = 0
2307
    return (v_all, v_maj, v_min, v_rev)
2308

    
2309
  @classmethod
2310
  def _GetKVMOutput(cls, kvm_path, option):
2311
    """Return the output of a kvm invocation
2312

2313
    @type kvm_path: string
2314
    @param kvm_path: path to the kvm executable
2315
    @type option: a key of _KVMOPTS_CMDS
2316
    @param option: kvm option to fetch the output from
2317
    @return: output a supported kvm invocation
2318
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2319

2320
    """
2321
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2322

    
2323
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2324

    
2325
    result = utils.RunCmd([kvm_path] + optlist)
2326
    if result.failed and not can_fail:
2327
      raise errors.HypervisorError("Unable to get KVM %s output" %
2328
                                    " ".join(optlist))
2329
    return result.output
2330

    
2331
  @classmethod
2332
  def _GetKVMVersion(cls, kvm_path):
2333
    """Return the installed KVM version.
2334

2335
    @return: (version, v_maj, v_min, v_rev)
2336
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2337

2338
    """
2339
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2340

    
2341
  @classmethod
2342
  def _GetDefaultMachineVersion(cls, kvm_path):
2343
    """Return the default hardware revision (e.g. pc-1.1)
2344

2345
    """
2346
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2347
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2348
    if match:
2349
      return match.group(1)
2350
    else:
2351
      return "pc"
2352

    
2353
  def StopInstance(self, instance, force=False, retry=False, name=None,
2354
                   timeout=None):
2355
    """Stop an instance.
2356

2357
    """
2358
    assert(timeout is None or force is not None)
2359

    
2360
    if name is not None and not force:
2361
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2362
    if name is None:
2363
      name = instance.name
2364
      acpi = instance.hvparams[constants.HV_ACPI]
2365
    else:
2366
      acpi = False
2367
    _, pid, alive = self._InstancePidAlive(name)
2368
    if pid > 0 and alive:
2369
      if force or not acpi:
2370
        utils.KillProcess(pid)
2371
      else:
2372
        self._CallMonitorCommand(name, "system_powerdown", timeout)
2373

    
2374
  @classmethod
2375
  def _UnconfigureInstanceNICs(cls, instance_name, info=None):
2376
    """Get runtime NICs of an instance and unconfigure them
2377

2378
    """
2379
    _, kvm_nics, __, ___ = cls._LoadKVMRuntime(instance_name, info)
2380
    for nic in kvm_nics:
2381
      cls._UnconfigureNic(instance_name, nic)
2382

    
2383
  def CleanupInstance(self, instance_name):
2384
    """Cleanup after a stopped instance
2385

2386
    """
2387
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2388
    if pid > 0 and alive:
2389
      raise errors.HypervisorError("Cannot cleanup a live instance")
2390
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2391

    
2392
  def RebootInstance(self, instance):
2393
    """Reboot an instance.
2394

2395
    """
2396
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2397
    # socket the instance will stop, but now power up again. So we'll resort
2398
    # to shutdown and restart.
2399
    _, _, alive = self._InstancePidAlive(instance.name)
2400
    if not alive:
2401
      raise errors.HypervisorError("Failed to reboot instance %s:"
2402
                                   " not running" % instance.name)
2403
    # StopInstance will delete the saved KVM runtime so:
2404
    # ...first load it...
2405
    kvm_runtime = self._LoadKVMRuntime(instance.name)
2406
    # ...now we can safely call StopInstance...
2407
    if not self.StopInstance(instance):
2408
      self.StopInstance(instance, force=True)
2409
    # ...and finally we can save it again, and execute it...
2410
    self._SaveKVMRuntime(instance, kvm_runtime)
2411
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2412
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2413
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2414

    
2415
  def MigrationInfo(self, instance):
2416
    """Get instance information to perform a migration.
2417

2418
    @type instance: L{objects.Instance}
2419
    @param instance: instance to be migrated
2420
    @rtype: string
2421
    @return: content of the KVM runtime file
2422

2423
    """
2424
    return self._ReadKVMRuntime(instance.name)
2425

    
2426
  def AcceptInstance(self, instance, info, target):
2427
    """Prepare to accept an instance.
2428

2429
    @type instance: L{objects.Instance}
2430
    @param instance: instance to be accepted
2431
    @type info: string
2432
    @param info: content of the KVM runtime file on the source node
2433
    @type target: string
2434
    @param target: target host (usually ip), on this node
2435

2436
    """
2437
    kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2438
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2439
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2440
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2441
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2442
                            incoming=incoming_address)
2443

    
2444
  def FinalizeMigrationDst(self, instance, info, success):
2445
    """Finalize the instance migration on the target node.
2446

2447
    Stop the incoming mode KVM.
2448

2449
    @type instance: L{objects.Instance}
2450
    @param instance: instance whose migration is being finalized
2451

2452
    """
2453
    if success:
2454
      kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2455
      kvm_nics = kvm_runtime[1]
2456

    
2457
      for nic_seq, nic in enumerate(kvm_nics):
2458
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2459
          # Bridged interfaces have already been configured
2460
          continue
2461
        try:
2462
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2463
        except EnvironmentError, err:
2464
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2465
                          instance.name, nic_seq, str(err))
2466
          continue
2467
        try:
2468
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2469
        except errors.HypervisorError, err:
2470
          logging.warning(str(err))
2471

    
2472
      self._WriteKVMRuntime(instance.name, info)
2473
    else:
2474
      self._UnconfigureInstanceNICs(instance.name, info)
2475
      self.StopInstance(instance, force=True)
2476

    
2477
  def MigrateInstance(self, instance, target, live):
2478
    """Migrate an instance to a target node.
2479

2480
    The migration will not be attempted if the instance is not
2481
    currently running.
2482

2483
    @type instance: L{objects.Instance}
2484
    @param instance: the instance to be migrated
2485
    @type target: string
2486
    @param target: ip address of the target node
2487
    @type live: boolean
2488
    @param live: perform a live migration
2489

2490
    """
2491
    instance_name = instance.name
2492
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2493
    _, _, alive = self._InstancePidAlive(instance_name)
2494
    if not alive:
2495
      raise errors.HypervisorError("Instance not running, cannot migrate")
2496

    
2497
    if not live:
2498
      self._CallMonitorCommand(instance_name, "stop")
2499

    
2500
    migrate_command = ("migrate_set_speed %dm" %
2501
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2502
    self._CallMonitorCommand(instance_name, migrate_command)
2503

    
2504
    migrate_command = ("migrate_set_downtime %dms" %
2505
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2506
    self._CallMonitorCommand(instance_name, migrate_command)
2507

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

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

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

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

    
2529
  def GetMigrationStatus(self, instance):
2530
    """Get the migration status
2531

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

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

    
2559
          return migration_status
2560

    
2561
        logging.warning("KVM: unknown migration status '%s'", status)
2562

    
2563
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2564

    
2565
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2566

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

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

2575
    """
2576
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2577

    
2578
  def GetNodeInfo(self):
2579
    """Return information about the node.
2580

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

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

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

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

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

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

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

    
2635
  def Verify(self):
2636
    """Verify the hypervisor.
2637

2638
    Check that the required binaries exist.
2639

2640
    @return: Problem description if something is wrong, C{None} otherwise
2641

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

    
2653
    return self._FormatVerifyResults(msgs)
2654

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

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

2663
    """
2664
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2665

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

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

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

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

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

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

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

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

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

2736
    """
2737
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2738

    
2739
    kvm_path = hvparams[constants.HV_KVM_PATH]
2740

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

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

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

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

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

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

2783
    """
2784
    cls.LinuxPowercycle()