Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ aec254f5

History | View | Annotate | Download (101.1 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_ONE_QUEUE = 0x2000
75
IFF_VNET_HDR = 0x4000
76

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

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

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

    
118
_MIGRATION_CAPS_DELIM = ":"
119

    
120

    
121
def _GenerateDeviceKVMId(dev_type, dev):
122
  """Helper function to generate a unique device name used by KVM
123

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

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

134
  """
135

    
136
  if not dev.pci:
137
    raise errors.HotplugError("Hotplug is not supported for %s with UUID %s" %
138
                              (dev_type, dev.uuid))
139

    
140
  return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
141

    
142

    
143
def _GetFreeSlot(slots, slot=None, reserve=False):
144
  """Helper method to get first available slot in a bitarray
145

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

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

    
162
  else:
163
    avail = slots.search(_AVAILABLE_PCI_SLOT, 1)
164
    if not avail:
165
      raise errors.HypervisorError("All slots occupied")
166

    
167
    slot = int(avail[0])
168

    
169
  if reserve:
170
    slots[slot] = True
171

    
172
  return slot
173

    
174

    
175
def _GetExistingDeviceInfo(dev_type, device, runtime):
176
  """Helper function to get an existing device inside the runtime file
177

178
  Used when an instance is running. Load kvm runtime file and search
179
  for a device based on its type and uuid.
180

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

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

    
198
  return found[0]
199

    
200

    
201
def _UpgradeSerializedRuntime(serialized_runtime):
202
  """Upgrade runtime data
203

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

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

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

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

    
226
  return kvm_cmd, serialized_nics, hvparams, serialized_disks
227

    
228

    
229
def _AnalyzeSerializedRuntime(serialized_runtime):
230
  """Return runtime entries for a serialized runtime file
231

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

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

    
244
  return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
245

    
246

    
247
def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
248
  """Retrieves supported TUN features from file descriptor.
249

250
  @see: L{_ProbeTapVnetHdr}
251

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

    
263

    
264
def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
265
  """Check whether to enable the IFF_VNET_HDR flag.
266

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

277
   @type fd: int
278
   @param fd: the file descriptor of /dev/net/tun
279

280
  """
281
  flags = _features_fn(fd)
282

    
283
  if flags is None:
284
    # Not supported
285
    return False
286

    
287
  result = bool(flags & IFF_VNET_HDR)
288

    
289
  if not result:
290
    logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
291

    
292
  return result
293

    
294

    
295
def _OpenTap(vnet_hdr=True):
296
  """Open a new tap device and return its file descriptor.
297

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

301
  @type vnet_hdr: boolean
302
  @param vnet_hdr: Enable the VNET Header
303
  @return: (ifname, tapfd)
304
  @rtype: tuple
305

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

    
312
  flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE
313

    
314
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
315
    flags |= IFF_VNET_HDR
316

    
317
  # The struct ifreq ioctl request (see netdevice(7))
318
  ifr = struct.pack("16sh", "", flags)
319

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

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

    
330

    
331
class QmpMessage:
332
  """QEMU Messaging Protocol (QMP) message.
333

334
  """
335
  def __init__(self, data):
336
    """Creates a new QMP message based on the passed data.
337

338
    """
339
    if not isinstance(data, dict):
340
      raise TypeError("QmpMessage must be initialized with a dict")
341

    
342
    self.data = data
343

    
344
  def __getitem__(self, field_name):
345
    """Get the value of the required field if present, or None.
346

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

352
    """
353
    return self.data.get(field_name, None)
354

    
355
  def __setitem__(self, field_name, field_value):
356
    """Set the value of the required field_name to field_value.
357

358
    """
359
    self.data[field_name] = field_value
360

    
361
  def __len__(self):
362
    """Return the number of fields stored in this QmpMessage.
363

364
    """
365
    return len(self.data)
366

    
367
  def __delitem__(self, key):
368
    """Delete the specified element from the QmpMessage.
369

370
    """
371
    del(self.data[key])
372

    
373
  @staticmethod
374
  def BuildFromJsonString(json_string):
375
    """Build a QmpMessage from a JSON encoded string.
376

377
    @type json_string: str
378
    @param json_string: JSON string representing the message
379
    @rtype: L{QmpMessage}
380
    @return: a L{QmpMessage} built from json_string
381

382
    """
383
    # Parse the string
384
    data = serializer.LoadJson(json_string)
385
    return QmpMessage(data)
386

    
387
  def __str__(self):
388
    # The protocol expects the JSON object to be sent as a single line.
389
    return serializer.DumpJson(self.data)
390

    
391
  def __eq__(self, other):
392
    # When comparing two QmpMessages, we are interested in comparing
393
    # their internal representation of the message data
394
    return self.data == other.data
395

    
396

    
397
class MonitorSocket(object):
398
  _SOCKET_TIMEOUT = 5
399

    
400
  def __init__(self, monitor_filename):
401
    """Instantiates the MonitorSocket object.
402

403
    @type monitor_filename: string
404
    @param monitor_filename: the filename of the UNIX raw socket on which the
405
                             monitor (QMP or simple one) is listening
406

407
    """
408
    self.monitor_filename = monitor_filename
409
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
410
    # We want to fail if the server doesn't send a complete message
411
    # in a reasonable amount of time
412
    self.sock.settimeout(self._SOCKET_TIMEOUT)
413
    self._connected = False
414

    
415
  def _check_socket(self):
416
    sock_stat = None
417
    try:
418
      sock_stat = os.stat(self.monitor_filename)
419
    except EnvironmentError, err:
420
      if err.errno == errno.ENOENT:
421
        raise errors.HypervisorError("No monitor socket found")
422
      else:
423
        raise errors.HypervisorError("Error checking monitor socket: %s",
424
                                     utils.ErrnoOrStr(err))
425
    if not stat.S_ISSOCK(sock_stat.st_mode):
426
      raise errors.HypervisorError("Monitor socket is not a socket")
427

    
428
  def _check_connection(self):
429
    """Make sure that the connection is established.
430

431
    """
432
    if not self._connected:
433
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
434
                                   " invoke connect() on it")
435

    
436
  def connect(self):
437
    """Connects to the monitor.
438

439
    Connects to the UNIX socket
440

441
    @raise errors.HypervisorError: when there are communication errors
442

443
    """
444
    if self._connected:
445
      raise errors.ProgrammerError("Cannot connect twice")
446

    
447
    self._check_socket()
448

    
449
    # Check file existance/stuff
450
    try:
451
      self.sock.connect(self.monitor_filename)
452
    except EnvironmentError:
453
      raise errors.HypervisorError("Can't connect to qmp socket")
454
    self._connected = True
455

    
456
  def close(self):
457
    """Closes the socket
458

459
    It cannot be used after this call.
460

461
    """
462
    self.sock.close()
463

    
464

    
465
class QmpConnection(MonitorSocket):
466
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
467

468
  """
469
  _FIRST_MESSAGE_KEY = "QMP"
470
  _EVENT_KEY = "event"
471
  _ERROR_KEY = "error"
472
  _RETURN_KEY = RETURN_KEY = "return"
473
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
474
  _ERROR_CLASS_KEY = "class"
475
  _ERROR_DESC_KEY = "desc"
476
  _EXECUTE_KEY = "execute"
477
  _ARGUMENTS_KEY = "arguments"
478
  _CAPABILITIES_COMMAND = "qmp_capabilities"
479
  _MESSAGE_END_TOKEN = "\r\n"
480

    
481
  def __init__(self, monitor_filename):
482
    super(QmpConnection, self).__init__(monitor_filename)
483
    self._buf = ""
484

    
485
  def connect(self):
486
    """Connects to the QMP monitor.
487

488
    Connects to the UNIX socket and makes sure that we can actually send and
489
    receive data to the kvm instance via QMP.
490

491
    @raise errors.HypervisorError: when there are communication errors
492
    @raise errors.ProgrammerError: when there are data serialization errors
493

494
    """
495
    super(QmpConnection, self).connect()
496
    # Check if we receive a correct greeting message from the server
497
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
498
    greeting = self._Recv()
499
    if not greeting[self._FIRST_MESSAGE_KEY]:
500
      self._connected = False
501
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
502
                                   " server greeting")
503

    
504
    # This is needed because QMP can return more than one greetings
505
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
506
    self._buf = ""
507

    
508
    # Let's put the monitor in command mode using the qmp_capabilities
509
    # command, or else no command will be executable.
510
    # (As per the QEMU Protocol Specification 0.1 - section 4)
511
    self.Execute(self._CAPABILITIES_COMMAND)
512

    
513
  def _ParseMessage(self, buf):
514
    """Extract and parse a QMP message from the given buffer.
515

516
    Seeks for a QMP message in the given buf. If found, it parses it and
517
    returns it together with the rest of the characters in the buf.
518
    If no message is found, returns None and the whole buffer.
519

520
    @raise errors.ProgrammerError: when there are data serialization errors
521

522
    """
523
    message = None
524
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
525
    # Specification 0.1 - Section 2.1.1)
526
    pos = buf.find(self._MESSAGE_END_TOKEN)
527
    if pos >= 0:
528
      try:
529
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
530
      except Exception, err:
531
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
532
      buf = buf[pos + 1:]
533

    
534
    return (message, buf)
535

    
536
  def _Recv(self):
537
    """Receives a message from QMP and decodes the received JSON object.
538

539
    @rtype: QmpMessage
540
    @return: the received message
541
    @raise errors.HypervisorError: when there are communication errors
542
    @raise errors.ProgrammerError: when there are data serialization errors
543

544
    """
545
    self._check_connection()
546

    
547
    # Check if there is already a message in the buffer
548
    (message, self._buf) = self._ParseMessage(self._buf)
549
    if message:
550
      return message
551

    
552
    recv_buffer = StringIO.StringIO(self._buf)
553
    recv_buffer.seek(len(self._buf))
554
    try:
555
      while True:
556
        data = self.sock.recv(4096)
557
        if not data:
558
          break
559
        recv_buffer.write(data)
560

    
561
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
562
        if message:
563
          return message
564

    
565
    except socket.timeout, err:
566
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
567
                                   "%s" % (err))
568
    except socket.error, err:
569
      raise errors.HypervisorError("Unable to receive data from KVM using the"
570
                                   " QMP protocol: %s" % err)
571

    
572
  def _Send(self, message):
573
    """Encodes and sends a message to KVM using QMP.
574

575
    @type message: QmpMessage
576
    @param message: message to send to KVM
577
    @raise errors.HypervisorError: when there are communication errors
578
    @raise errors.ProgrammerError: when there are data serialization errors
579

580
    """
581
    self._check_connection()
582
    try:
583
      message_str = str(message)
584
    except Exception, err:
585
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
586

    
587
    try:
588
      self.sock.sendall(message_str)
589
    except socket.timeout, err:
590
      raise errors.HypervisorError("Timeout while sending a QMP message: "
591
                                   "%s (%s)" % (err.string, err.errno))
592
    except socket.error, err:
593
      raise errors.HypervisorError("Unable to send data from KVM using the"
594
                                   " QMP protocol: %s" % err)
595

    
596
  def Execute(self, command, arguments=None):
597
    """Executes a QMP command and returns the response of the server.
598

599
    @type command: str
600
    @param command: the command to execute
601
    @type arguments: dict
602
    @param arguments: dictionary of arguments to be passed to the command
603
    @rtype: dict
604
    @return: dictionary representing the received JSON object
605
    @raise errors.HypervisorError: when there are communication errors
606
    @raise errors.ProgrammerError: when there are data serialization errors
607

608
    """
609
    self._check_connection()
610
    message = QmpMessage({self._EXECUTE_KEY: command})
611
    if arguments:
612
      message[self._ARGUMENTS_KEY] = arguments
613
    self._Send(message)
614

    
615
    # Events can occur between the sending of the command and the reception
616
    # of the response, so we need to filter out messages with the event key.
617
    while True:
618
      response = self._Recv()
619
      err = response[self._ERROR_KEY]
620
      if err:
621
        raise errors.HypervisorError("kvm: error executing the %s"
622
                                     " command: %s (%s):" %
623
                                     (command,
624
                                      err[self._ERROR_DESC_KEY],
625
                                      err[self._ERROR_CLASS_KEY]))
626

    
627
      elif not response[self._EVENT_KEY]:
628
        return response
629

    
630

    
631
class KVMHypervisor(hv_base.BaseHypervisor):
632
  """KVM hypervisor interface
633

634
  """
635
  CAN_MIGRATE = True
636

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

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

    
737
  _VIRTIO = "virtio"
738
  _VIRTIO_NET_PCI = "virtio-net-pci"
739
  _VIRTIO_BLK_PCI = "virtio-blk-pci"
740

    
741
  _MIGRATION_STATUS_RE = re.compile(r"Migration\s+status:\s+(\w+)",
742
                                    re.M | re.I)
743
  _MIGRATION_PROGRESS_RE = \
744
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
745
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
746
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
747

    
748
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
749
  _MIGRATION_INFO_RETRY_DELAY = 2
750

    
751
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
752

    
753
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
754
  _CPU_INFO_CMD = "info cpus"
755
  _CONT_CMD = "cont"
756

    
757
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
758
  _CHECK_MACHINE_VERSION_RE = \
759
    staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
760

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

    
777
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
778
  _INFO_PCI_CMD = "info pci"
779
  _FIND_PCI_DEVICE_RE = \
780
    staticmethod(
781
      lambda pci, devid: re.compile(r'Bus.*device[ ]*%d,(.*\n){5,6}.*id "%s"' %
782
                                    (pci, devid), re.M))
783

    
784
  _INFO_VERSION_RE = \
785
    re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
786
  _INFO_VERSION_CMD = "info version"
787

    
788
  # Slot 0 for Host bridge, Slot 1 for ISA bridge, Slot 2 for VGA controller
789
  _DEFAULT_PCI_RESERVATIONS = "11100000000000000000000000000000"
790
  _SOUNDHW_WITH_PCI_SLOT = ["ac97", "es1370", "hda"]
791

    
792
  ANCILLARY_FILES = [
793
    _KVM_NETWORK_SCRIPT,
794
    ]
795
  ANCILLARY_FILES_OPT = [
796
    _KVM_NETWORK_SCRIPT,
797
    ]
798

    
799
  # Supported kvm options to get output from
800
  _KVMOPT_HELP = "help"
801
  _KVMOPT_MLIST = "mlist"
802
  _KVMOPT_DEVICELIST = "devicelist"
803

    
804
  # Command to execute to get the output from kvm, and whether to
805
  # accept the output even on failure.
806
  _KVMOPTS_CMDS = {
807
    _KVMOPT_HELP: (["--help"], False),
808
    _KVMOPT_MLIST: (["-M", "?"], False),
809
    _KVMOPT_DEVICELIST: (["-device", "?"], True),
810
  }
811

    
812
  def __init__(self):
813
    hv_base.BaseHypervisor.__init__(self)
814
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
815
    # in a tmpfs filesystem or has been otherwise wiped out.
816
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
817
    utils.EnsureDirs(dirs)
818

    
819
  @classmethod
820
  def _InstancePidFile(cls, instance_name):
821
    """Returns the instance pidfile.
822

823
    """
824
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
825

    
826
  @classmethod
827
  def _InstanceUidFile(cls, instance_name):
828
    """Returns the instance uidfile.
829

830
    """
831
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
832

    
833
  @classmethod
834
  def _InstancePidInfo(cls, pid):
835
    """Check pid file for instance information.
836

837
    Check that a pid file is associated with an instance, and retrieve
838
    information from its command line.
839

840
    @type pid: string or int
841
    @param pid: process id of the instance to check
842
    @rtype: tuple
843
    @return: (instance_name, memory, vcpus)
844
    @raise errors.HypervisorError: when an instance cannot be found
845

846
    """
847
    alive = utils.IsProcessAlive(pid)
848
    if not alive:
849
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
850

    
851
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
852
    try:
853
      cmdline = utils.ReadFile(cmdline_file)
854
    except EnvironmentError, err:
855
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
856
                                   (pid, err))
857

    
858
    instance = None
859
    memory = 0
860
    vcpus = 0
861

    
862
    arg_list = cmdline.split("\x00")
863
    while arg_list:
864
      arg = arg_list.pop(0)
865
      if arg == "-name":
866
        instance = arg_list.pop(0)
867
      elif arg == "-m":
868
        memory = int(arg_list.pop(0))
869
      elif arg == "-smp":
870
        vcpus = int(arg_list.pop(0).split(",")[0])
871

    
872
    if instance is None:
873
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
874
                                   " instance" % pid)
875

    
876
    return (instance, memory, vcpus)
877

    
878
  def _InstancePidAlive(self, instance_name):
879
    """Returns the instance pidfile, pid, and liveness.
880

881
    @type instance_name: string
882
    @param instance_name: instance name
883
    @rtype: tuple
884
    @return: (pid file name, pid, liveness)
885

886
    """
887
    pidfile = self._InstancePidFile(instance_name)
888
    pid = utils.ReadPidFile(pidfile)
889

    
890
    alive = False
891
    try:
892
      cmd_instance = self._InstancePidInfo(pid)[0]
893
      alive = (cmd_instance == instance_name)
894
    except errors.HypervisorError:
895
      pass
896

    
897
    return (pidfile, pid, alive)
898

    
899
  def _CheckDown(self, instance_name):
900
    """Raises an error unless the given instance is down.
901

902
    """
903
    alive = self._InstancePidAlive(instance_name)[2]
904
    if alive:
905
      raise errors.HypervisorError("Failed to start instance %s: %s" %
906
                                   (instance_name, "already running"))
907

    
908
  @classmethod
909
  def _InstanceMonitor(cls, instance_name):
910
    """Returns the instance monitor socket name
911

912
    """
913
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
914

    
915
  @classmethod
916
  def _InstanceSerial(cls, instance_name):
917
    """Returns the instance serial socket name
918

919
    """
920
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
921

    
922
  @classmethod
923
  def _InstanceQmpMonitor(cls, instance_name):
924
    """Returns the instance serial QMP socket name
925

926
    """
927
    return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
928

    
929
  @staticmethod
930
  def _SocatUnixConsoleParams():
931
    """Returns the correct parameters for socat
932

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

935
    """
936
    if constants.SOCAT_USE_ESCAPE:
937
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
938
    else:
939
      return "echo=0,icanon=0"
940

    
941
  @classmethod
942
  def _InstanceKVMRuntime(cls, instance_name):
943
    """Returns the instance KVM runtime filename
944

945
    """
946
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
947

    
948
  @classmethod
949
  def _InstanceChrootDir(cls, instance_name):
950
    """Returns the name of the KVM chroot dir of the instance
951

952
    """
953
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
954

    
955
  @classmethod
956
  def _InstanceNICDir(cls, instance_name):
957
    """Returns the name of the directory holding the tap device files for a
958
    given instance.
959

960
    """
961
    return utils.PathJoin(cls._NICS_DIR, instance_name)
962

    
963
  @classmethod
964
  def _InstanceNICFile(cls, instance_name, seq_or_uuid):
965
    """Returns the name of the file containing the tap device for a given NIC
966

967
    """
968
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq_or_uuid))
969

    
970
  @classmethod
971
  def _GetInstanceNICTap(cls, instance_name, nic):
972
    """Returns the tap for the corresponding nic
973

974
    Search for tap file named after NIC's uuid.
975
    For old instances without uuid indexed tap files returns nothing.
976

977
    """
978
    try:
979
      return utils.ReadFile(cls._InstanceNICFile(instance_name, nic.uuid))
980
    except EnvironmentError:
981
      pass
982

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

987
    """
988
    for ident in [seq, nic.uuid]:
989
      utils.WriteFile(cls._InstanceNICFile(instance_name, ident), data=tap)
990

    
991
  @classmethod
992
  def _RemoveInstanceNICFiles(cls, instance_name, seq, nic):
993
    """Write tap name to both instance NIC files
994

995
    """
996
    for ident in [seq, nic.uuid]:
997
      utils.RemoveFile(cls._InstanceNICFile(instance_name, ident))
998

    
999
  @classmethod
1000
  def _InstanceKeymapFile(cls, instance_name):
1001
    """Returns the name of the file containing the keymap for a given instance
1002

1003
    """
1004
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
1005

    
1006
  @classmethod
1007
  def _TryReadUidFile(cls, uid_file):
1008
    """Try to read a uid file
1009

1010
    """
1011
    if os.path.exists(uid_file):
1012
      try:
1013
        uid = int(utils.ReadOneLineFile(uid_file))
1014
        return uid
1015
      except EnvironmentError:
1016
        logging.warning("Can't read uid file", exc_info=True)
1017
      except (TypeError, ValueError):
1018
        logging.warning("Can't parse uid file contents", exc_info=True)
1019
    return None
1020

    
1021
  @classmethod
1022
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
1023
    """Removes an instance's rutime sockets/files/dirs.
1024

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

    
1061
  @staticmethod
1062
  def _ConfigureNIC(instance, seq, nic, tap):
1063
    """Run the network configuration script for a specified NIC
1064

1065
    @param instance: instance we're acting on
1066
    @type instance: instance object
1067
    @param seq: nic sequence number
1068
    @type seq: int
1069
    @param nic: nic we're acting on
1070
    @type nic: nic object
1071
    @param tap: the host's tap interface this NIC corresponds to
1072
    @type tap: str
1073

1074
    """
1075
    env = {
1076
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1077
      "INSTANCE": instance.name,
1078
      "MAC": nic.mac,
1079
      "MODE": nic.nicparams[constants.NIC_MODE],
1080
      "INTERFACE": tap,
1081
      "INTERFACE_INDEX": str(seq),
1082
      "INTERFACE_UUID": nic.uuid,
1083
      "TAGS": " ".join(instance.GetTags()),
1084
    }
1085

    
1086
    if nic.ip:
1087
      env["IP"] = nic.ip
1088

    
1089
    if nic.name:
1090
      env["INTERFACE_NAME"] = nic.name
1091

    
1092
    if nic.nicparams[constants.NIC_LINK]:
1093
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1094

    
1095
    if nic.network:
1096
      n = objects.Network.FromDict(nic.netinfo)
1097
      env.update(n.HooksDict())
1098

    
1099
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1100
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1101

    
1102
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1103
    if result.failed:
1104
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1105
                                   " network configuration script output: %s" %
1106
                                   (tap, result.fail_reason, result.output))
1107

    
1108
  @staticmethod
1109
  def _VerifyAffinityPackage():
1110
    if affinity is None:
1111
      raise errors.HypervisorError("affinity Python package not"
1112
                                   " found; cannot use CPU pinning under KVM")
1113

    
1114
  @staticmethod
1115
  def _BuildAffinityCpuMask(cpu_list):
1116
    """Create a CPU mask suitable for sched_setaffinity from a list of
1117
    CPUs.
1118

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

1122
    @type cpu_list: list of int
1123
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1124
    @rtype: int
1125
    @return: a bit mask of CPU affinities
1126

1127
    """
1128
    if cpu_list == constants.CPU_PINNING_OFF:
1129
      return constants.CPU_PINNING_ALL_KVM
1130
    else:
1131
      return sum(2 ** cpu for cpu in cpu_list)
1132

    
1133
  @classmethod
1134
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1135
    """Change CPU affinity for running VM according to given CPU mask.
1136

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

1145
    """
1146
    # Convert the string CPU mask to a list of list of int's
1147
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1148

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

    
1167
      # For each vCPU, map it to the proper list of physical CPUs
1168
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1169
        affinity.set_process_affinity_mask(thread_dict[i],
1170
                                           cls._BuildAffinityCpuMask(vcpu))
1171

    
1172
  def _GetVcpuThreadIds(self, instance_name):
1173
    """Get a mapping of vCPU no. to thread IDs for the instance
1174

1175
    @type instance_name: string
1176
    @param instance_name: instance in question
1177
    @rtype: dictionary of int:int
1178
    @return: a dictionary mapping vCPU numbers to thread IDs
1179

1180
    """
1181
    result = {}
1182
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1183
    for line in output.stdout.splitlines():
1184
      match = self._CPU_INFO_RE.search(line)
1185
      if not match:
1186
        continue
1187
      grp = map(int, match.groups())
1188
      result[grp[0]] = grp[1]
1189

    
1190
    return result
1191

    
1192
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1193
    """Complete CPU pinning.
1194

1195
    @type instance_name: string
1196
    @param instance_name: name of instance
1197
    @type cpu_mask: string
1198
    @param cpu_mask: CPU pinning mask as entered by user
1199

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

    
1208
  def ListInstances(self, hvparams=None):
1209
    """Get the list of running instances.
1210

1211
    We can do this by listing our live instances directory and
1212
    checking whether the associated kvm process is still alive.
1213

1214
    """
1215
    result = []
1216
    for name in os.listdir(self._PIDS_DIR):
1217
      if self._InstancePidAlive(name)[2]:
1218
        result.append(name)
1219
    return result
1220

    
1221
  def GetInstanceInfo(self, instance_name, hvparams=None):
1222
    """Get instance properties.
1223

1224
    @type instance_name: string
1225
    @param instance_name: the instance name
1226
    @type hvparams: dict of strings
1227
    @param hvparams: hvparams to be used with this instance
1228
    @rtype: tuple of strings
1229
    @return: (name, id, memory, vcpus, stat, times)
1230

1231
    """
1232
    _, pid, alive = self._InstancePidAlive(instance_name)
1233
    if not alive:
1234
      return None
1235

    
1236
    _, memory, vcpus = self._InstancePidInfo(pid)
1237
    istat = "---b-"
1238
    times = "0"
1239

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

    
1251
    return (instance_name, pid, memory, vcpus, istat, times)
1252

    
1253
  def GetAllInstancesInfo(self, hvparams=None):
1254
    """Get properties of all instances.
1255

1256
    @type hvparams: dict of strings
1257
    @param hvparams: hypervisor parameter
1258
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1259

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

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

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

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

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

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

    
1339
      access_mode = cfdev.params.get(constants.LDP_ACCESS,
1340
                                     constants.DISK_KERNELSPACE)
1341
      if (uri and access_mode == constants.DISK_USERSPACE):
1342
        drive_uri = uri
1343
      else:
1344
        drive_uri = link_name
1345

    
1346
      # For ext we allow overriding disk_cache hypervisor params per disk
1347
      disk_cache = cfdev.params.get("cache", None)
1348
      if disk_cache:
1349
        cache_val = ",cache=%s" % disk_cache
1350

    
1351
      drive_val = "file=%s,format=raw%s%s%s" % \
1352
                  (drive_uri, if_val, boot_val, cache_val)
1353

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

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

    
1380
      dev_opts.extend(["-drive", drive_val])
1381

    
1382
    return dev_opts
1383

    
1384
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1385
                          kvmhelp):
1386
    """Generate KVM information to start an instance.
1387

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

1397
    """
1398
    # pylint: disable=R0912,R0914,R0915
1399
    hvp = instance.hvparams
1400
    self.ValidateParameters(hvp)
1401

    
1402
    pidfile = self._InstancePidFile(instance.name)
1403
    kvm = hvp[constants.HV_KVM_PATH]
1404
    kvm_cmd = [kvm]
1405
    # used just by the vnc server, if enabled
1406
    kvm_cmd.extend(["-name", instance.name])
1407
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1408

    
1409
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1410
    if hvp[constants.HV_CPU_CORES]:
1411
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1412
    if hvp[constants.HV_CPU_THREADS]:
1413
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1414
    if hvp[constants.HV_CPU_SOCKETS]:
1415
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1416

    
1417
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1418

    
1419
    kvm_cmd.extend(["-pidfile", pidfile])
1420

    
1421
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1422

    
1423
    # As requested by music lovers
1424
    if hvp[constants.HV_SOUNDHW]:
1425
      soundhw = hvp[constants.HV_SOUNDHW]
1426
      # For some reason only few sound devices require a PCI slot
1427
      # while the Audio controller *must* be in slot 3.
1428
      # That's why we bridge this option early in command line
1429
      if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1430
        _ = _GetFreeSlot(pci_reservations, reserve=True)
1431
      kvm_cmd.extend(["-soundhw", soundhw])
1432

    
1433
    if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1434
      # The SCSI controller requires another PCI slot.
1435
      _ = _GetFreeSlot(pci_reservations, reserve=True)
1436

    
1437
    # Add id to ballon and place to the first available slot (3 or 4)
1438
    addr = _GetFreeSlot(pci_reservations, reserve=True)
1439
    pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1440
    kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1441
    kvm_cmd.extend(["-daemonize"])
1442
    if not instance.hvparams[constants.HV_ACPI]:
1443
      kvm_cmd.extend(["-no-acpi"])
1444
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1445
        constants.INSTANCE_REBOOT_EXIT:
1446
      kvm_cmd.extend(["-no-reboot"])
1447

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

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

    
1478
    if startup_paused:
1479
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1480

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

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

    
1488
    disk_type = hvp[constants.HV_DISK_TYPE]
1489

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

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

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

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

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

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

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

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

    
1568
    kvm_cmd.extend(["-usb"])
1569

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

    
1575
    if vnc_bind_address:
1576
      if netutils.IsValidInterface(vnc_bind_address):
1577
        if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1578
        if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1579
        if len(if_ip4_addresses) < 1:
1580
          logging.error("Could not determine IPv4 address of interface %s",
1581
                        vnc_bind_address)
1582
        else:
1583
          vnc_bind_address = if_ip4_addresses[0]
1584
      if netutils.IP4Address.IsValid(vnc_bind_address):
1585
        if instance.network_port > constants.VNC_BASE_PORT:
1586
          display = instance.network_port - constants.VNC_BASE_PORT
1587
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1588
            vnc_arg = ":%d" % (display)
1589
          else:
1590
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1591
        else:
1592
          logging.error("Network port is not a valid VNC display (%d < %d),"
1593
                        " not starting VNC",
1594
                        instance.network_port, constants.VNC_BASE_PORT)
1595
          vnc_arg = "none"
1596

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

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

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

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

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

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

    
1650
        spice_address = addresses[spice_ip_version][0]
1651

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

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

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

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

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

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

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

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

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

    
1721
    if hvp[constants.HV_USE_LOCALTIME]:
1722
      kvm_cmd.extend(["-localtime"])
1723

    
1724
    if hvp[constants.HV_KVM_USE_CHROOT]:
1725
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1726

    
1727
    # Add qemu-KVM -cpu param
1728
    if hvp[constants.HV_CPU_TYPE]:
1729
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1730

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

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

    
1743
    # Set system UUID to instance UUID
1744
    if self._UUID_RE.search(kvmhelp):
1745
      kvm_cmd.extend(["-uuid", instance.uuid])
1746

    
1747
    if hvp[constants.HV_KVM_EXTRA]:
1748
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1749

    
1750
    kvm_disks = []
1751
    for disk, link_name, uri in block_devices:
1752
      disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1753
      kvm_disks.append((disk, link_name, uri))
1754

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

    
1760
    hvparams = hvp
1761

    
1762
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1763

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

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

    
1774
  def _ReadKVMRuntime(self, instance_name):
1775
    """Read an instance's KVM runtime
1776

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

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

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

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

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

    
1798
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1799
    """Load an instance's KVM runtime
1800

1801
    """
1802
    if not serialized_runtime:
1803
      serialized_runtime = self._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
                                                     up_hvp,
1885
                                                     kvm_disks,
1886
                                                     kvmhelp,
1887
                                                     devlist)
1888
    kvm_cmd.extend(bdev_opts)
1889

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

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

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

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

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

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

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

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

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

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

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

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

    
2009
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
2010
                     constants.RUN_DIRS_MODE)])
2011
    for nic_seq, tap in enumerate(taps):
2012
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
2013
                      data=tap)
2014

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2099
    return result
2100

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

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

    
2114
    dev.pci = _GetFreeSlot(slots)
2115

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2236
    """
2237
    runtime = self._LoadKVMRuntime(instance)
2238
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2239
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2240
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2241
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2242
      cmds = ["device_del %s" % kvm_devid]
2243
      cmds += ["drive_del %s" % kvm_devid]
2244
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2245
      cmds = ["device_del %s" % kvm_devid]
2246
      cmds += ["netdev_del %s" % kvm_devid]
2247
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
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
      self.HotAddDevice(instance, dev_type, device, _, seq)
2267

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2373
  def CleanupInstance(self, instance_name):
2374
    """Cleanup after a stopped instance
2375

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

    
2382
  def RebootInstance(self, instance):
2383
    """Reboot an instance.
2384

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

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

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

2413
    """
2414
    return self._ReadKVMRuntime(instance.name)
2415

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

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

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

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

2437
    Stop the incoming mode KVM.
2438

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

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

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

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

    
2466
  def MigrateInstance(self, cluster_name, instance, target, live):
2467
    """Migrate an instance to a target node.
2468

2469
    The migration will not be attempted if the instance is not
2470
    currently running.
2471

2472
    @type cluster_name: string
2473
    @param cluster_name: name of the cluster
2474
    @type instance: L{objects.Instance}
2475
    @param instance: the instance to be migrated
2476
    @type target: string
2477
    @param target: ip address of the target node
2478
    @type live: boolean
2479
    @param live: perform a live migration
2480

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

    
2488
    if not live:
2489
      self._CallMonitorCommand(instance_name, "stop")
2490

    
2491
    migrate_command = ("migrate_set_speed %dm" %
2492
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2493
    self._CallMonitorCommand(instance_name, migrate_command)
2494

    
2495
    migrate_command = ("migrate_set_downtime %dms" %
2496
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2497
    self._CallMonitorCommand(instance_name, migrate_command)
2498

    
2499
    migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2500
    if migration_caps:
2501
      for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2502
        migrate_command = ("migrate_set_capability %s on" % c)
2503
        self._CallMonitorCommand(instance_name, migrate_command)
2504

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

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

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

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

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

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

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

    
2556
          return migration_status
2557

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

    
2560
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2561

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

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

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

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

    
2575
  def GetNodeInfo(self, hvparams=None):
2576
    """Return information about the node.
2577

2578
    @type hvparams: dict of strings
2579
    @param hvparams: hypervisor parameters, not used in this class
2580

2581
    @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2582
        the following keys:
2583
          - hv_version: the hypervisor version in the form (major, minor,
2584
                        revision)
2585

2586
    """
2587
    result = self.GetLinuxNodeInfo()
2588
    kvmpath = constants.KVM_PATH
2589
    if hvparams is not None:
2590
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2591
    _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2592
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2593
    return result
2594

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

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

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

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

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

    
2633
  def Verify(self, hvparams=None):
2634
    """Verify the hypervisor.
2635

2636
    Check that the required binaries exist.
2637

2638
    @type hvparams: dict of strings
2639
    @param hvparams: hypervisor parameters to be verified against, not used here
2640

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

2643
    """
2644
    msgs = []
2645
    kvmpath = constants.KVM_PATH
2646
    if hvparams is not None:
2647
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2648
    if not os.path.exists(kvmpath):
2649
      msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2650
    if not os.path.exists(constants.SOCAT_PATH):
2651
      msgs.append("The socat binary ('%s') does not exist" %
2652
                  constants.SOCAT_PATH)
2653

    
2654
    return self._FormatVerifyResults(msgs)
2655

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2740
    kvm_path = hvparams[constants.HV_KVM_PATH]
2741

    
2742
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2743
    if security_model == constants.HT_SM_USER:
2744
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2745
      try:
2746
        pwd.getpwnam(username)
2747
      except KeyError:
2748
        raise errors.HypervisorError("Unknown security domain user %s"
2749
                                     % username)
2750
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2751
    if vnc_bind_address:
2752
      bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2753
      is_interface = netutils.IsValidInterface(vnc_bind_address)
2754
      is_path = utils.IsNormAbsPath(vnc_bind_address)
2755
      if not bound_to_addr and not is_interface and not is_path:
2756
        raise errors.HypervisorError("VNC: The %s parameter must be either"
2757
                                     " a valid IP address, an interface name,"
2758
                                     " or an absolute path" %
2759
                                     constants.HV_KVM_SPICE_BIND)
2760

    
2761
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2762
    if spice_bind:
2763
      # only one of VNC and SPICE can be used currently.
2764
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2765
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2766
                                     " only one of them can be used at a"
2767
                                     " given time")
2768

    
2769
      # check that KVM supports SPICE
2770
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2771
      if not cls._SPICE_RE.search(kvmhelp):
2772
        raise errors.HypervisorError("SPICE is configured, but it is not"
2773
                                     " supported according to 'kvm --help'")
2774

    
2775
      # if spice_bind is not an IP address, it must be a valid interface
2776
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2777
                       netutils.IP6Address.IsValid(spice_bind))
2778
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2779
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2780
                                     " a valid IP address or interface name" %
2781
                                     constants.HV_KVM_SPICE_BIND)
2782

    
2783
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2784
    if machine_version:
2785
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2786
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2787
        raise errors.HypervisorError("Unsupported machine version: %s" %
2788
                                     machine_version)
2789

    
2790
  @classmethod
2791
  def PowercycleNode(cls, hvparams=None):
2792
    """KVM powercycle, just a wrapper over Linux powercycle.
2793

2794
    @type hvparams: dict of strings
2795
    @param hvparams: hypervisor params to be used on this node
2796

2797
    """
2798
    cls.LinuxPowercycle()