Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ ab0c6a39

History | View | Annotate | Download (99.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""KVM hypervisor
23

24
"""
25

    
26
import errno
27
import os
28
import os.path
29
import re
30
import tempfile
31
import time
32
import logging
33
import pwd
34
import struct
35
import fcntl
36
import shutil
37
import socket
38
import stat
39
import StringIO
40
from bitarray import bitarray
41
try:
42
  import affinity   # pylint: disable=F0401
43
except ImportError:
44
  affinity = None
45
try:
46
  import fdsend   # pylint: disable=F0401
47
except ImportError:
48
  fdsend = None
49

    
50
from ganeti import utils
51
from ganeti import constants
52
from ganeti import errors
53
from ganeti import serializer
54
from ganeti import objects
55
from ganeti import uidpool
56
from ganeti import ssconf
57
from ganeti import netutils
58
from ganeti import pathutils
59
from ganeti.hypervisor import hv_base
60
from ganeti.utils import wrapper as utils_wrapper
61

    
62

    
63
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
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):
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))
969

    
970
  @classmethod
971
  def _InstanceKeymapFile(cls, instance_name):
972
    """Returns the name of the file containing the keymap for a given instance
973

974
    """
975
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
976

    
977
  @classmethod
978
  def _TryReadUidFile(cls, uid_file):
979
    """Try to read a uid file
980

981
    """
982
    if os.path.exists(uid_file):
983
      try:
984
        uid = int(utils.ReadOneLineFile(uid_file))
985
        return uid
986
      except EnvironmentError:
987
        logging.warning("Can't read uid file", exc_info=True)
988
      except (TypeError, ValueError):
989
        logging.warning("Can't parse uid file contents", exc_info=True)
990
    return None
991

    
992
  @classmethod
993
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
994
    """Removes an instance's rutime sockets/files/dirs.
995

996
    """
997
    utils.RemoveFile(pidfile)
998
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
999
    utils.RemoveFile(cls._InstanceSerial(instance_name))
1000
    utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
1001
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
1002
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
1003
    uid_file = cls._InstanceUidFile(instance_name)
1004
    uid = cls._TryReadUidFile(uid_file)
1005
    utils.RemoveFile(uid_file)
1006
    if uid is not None:
1007
      uidpool.ReleaseUid(uid)
1008
    try:
1009
      shutil.rmtree(cls._InstanceNICDir(instance_name))
1010
    except OSError, err:
1011
      if err.errno != errno.ENOENT:
1012
        raise
1013
    try:
1014
      chroot_dir = cls._InstanceChrootDir(instance_name)
1015
      utils.RemoveDir(chroot_dir)
1016
    except OSError, err:
1017
      if err.errno == errno.ENOTEMPTY:
1018
        # The chroot directory is expected to be empty, but it isn't.
1019
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
1020
                                          prefix="%s-%s-" %
1021
                                          (instance_name,
1022
                                           utils.TimestampForFilename()))
1023
        logging.warning("The chroot directory of instance %s can not be"
1024
                        " removed as it is not empty. Moving it to the"
1025
                        " quarantine instead. Please investigate the"
1026
                        " contents (%s) and clean up manually",
1027
                        instance_name, new_chroot_dir)
1028
        utils.RenameFile(chroot_dir, new_chroot_dir)
1029
      else:
1030
        raise
1031

    
1032
  @staticmethod
1033
  def _ConfigureNIC(instance, seq, nic, tap):
1034
    """Run the network configuration script for a specified NIC
1035

1036
    @param instance: instance we're acting on
1037
    @type instance: instance object
1038
    @param seq: nic sequence number
1039
    @type seq: int
1040
    @param nic: nic we're acting on
1041
    @type nic: nic object
1042
    @param tap: the host's tap interface this NIC corresponds to
1043
    @type tap: str
1044

1045
    """
1046
    env = {
1047
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1048
      "INSTANCE": instance.name,
1049
      "MAC": nic.mac,
1050
      "MODE": nic.nicparams[constants.NIC_MODE],
1051
      "INTERFACE": tap,
1052
      "INTERFACE_INDEX": str(seq),
1053
      "INTERFACE_UUID": nic.uuid,
1054
      "TAGS": " ".join(instance.GetTags()),
1055
    }
1056

    
1057
    if nic.ip:
1058
      env["IP"] = nic.ip
1059

    
1060
    if nic.name:
1061
      env["INTERFACE_NAME"] = nic.name
1062

    
1063
    if nic.nicparams[constants.NIC_LINK]:
1064
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1065

    
1066
    if nic.network:
1067
      n = objects.Network.FromDict(nic.netinfo)
1068
      env.update(n.HooksDict())
1069

    
1070
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1071
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1072

    
1073
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1074
    if result.failed:
1075
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1076
                                   " network configuration script output: %s" %
1077
                                   (tap, result.fail_reason, result.output))
1078

    
1079
  @staticmethod
1080
  def _VerifyAffinityPackage():
1081
    if affinity is None:
1082
      raise errors.HypervisorError("affinity Python package not"
1083
                                   " found; cannot use CPU pinning under KVM")
1084

    
1085
  @staticmethod
1086
  def _BuildAffinityCpuMask(cpu_list):
1087
    """Create a CPU mask suitable for sched_setaffinity from a list of
1088
    CPUs.
1089

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

1093
    @type cpu_list: list of int
1094
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1095
    @rtype: int
1096
    @return: a bit mask of CPU affinities
1097

1098
    """
1099
    if cpu_list == constants.CPU_PINNING_OFF:
1100
      return constants.CPU_PINNING_ALL_KVM
1101
    else:
1102
      return sum(2 ** cpu for cpu in cpu_list)
1103

    
1104
  @classmethod
1105
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1106
    """Change CPU affinity for running VM according to given CPU mask.
1107

1108
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1109
    @type cpu_mask: string
1110
    @param process_id: process ID of KVM process. Used to pin entire VM
1111
                       to physical CPUs.
1112
    @type process_id: int
1113
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1114
    @type thread_dict: dict int:int
1115

1116
    """
1117
    # Convert the string CPU mask to a list of list of int's
1118
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1119

    
1120
    if len(cpu_list) == 1:
1121
      all_cpu_mapping = cpu_list[0]
1122
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
1123
        # If CPU pinning has 1 entry that's "all", then do nothing
1124
        pass
1125
      else:
1126
        # If CPU pinning has one non-all entry, map the entire VM to
1127
        # one set of physical CPUs
1128
        cls._VerifyAffinityPackage()
1129
        affinity.set_process_affinity_mask(
1130
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1131
    else:
1132
      # The number of vCPUs mapped should match the number of vCPUs
1133
      # reported by KVM. This was already verified earlier, so
1134
      # here only as a sanity check.
1135
      assert len(thread_dict) == len(cpu_list)
1136
      cls._VerifyAffinityPackage()
1137

    
1138
      # For each vCPU, map it to the proper list of physical CPUs
1139
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1140
        affinity.set_process_affinity_mask(thread_dict[i],
1141
                                           cls._BuildAffinityCpuMask(vcpu))
1142

    
1143
  def _GetVcpuThreadIds(self, instance_name):
1144
    """Get a mapping of vCPU no. to thread IDs for the instance
1145

1146
    @type instance_name: string
1147
    @param instance_name: instance in question
1148
    @rtype: dictionary of int:int
1149
    @return: a dictionary mapping vCPU numbers to thread IDs
1150

1151
    """
1152
    result = {}
1153
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1154
    for line in output.stdout.splitlines():
1155
      match = self._CPU_INFO_RE.search(line)
1156
      if not match:
1157
        continue
1158
      grp = map(int, match.groups())
1159
      result[grp[0]] = grp[1]
1160

    
1161
    return result
1162

    
1163
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1164
    """Complete CPU pinning.
1165

1166
    @type instance_name: string
1167
    @param instance_name: name of instance
1168
    @type cpu_mask: string
1169
    @param cpu_mask: CPU pinning mask as entered by user
1170

1171
    """
1172
    # Get KVM process ID, to be used if need to pin entire VM
1173
    _, pid, _ = self._InstancePidAlive(instance_name)
1174
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1175
    thread_dict = self._GetVcpuThreadIds(instance_name)
1176
    # Run CPU pinning, based on configured mask
1177
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1178

    
1179
  def ListInstances(self, hvparams=None):
1180
    """Get the list of running instances.
1181

1182
    We can do this by listing our live instances directory and
1183
    checking whether the associated kvm process is still alive.
1184

1185
    """
1186
    result = []
1187
    for name in os.listdir(self._PIDS_DIR):
1188
      if self._InstancePidAlive(name)[2]:
1189
        result.append(name)
1190
    return result
1191

    
1192
  def GetInstanceInfo(self, instance_name, hvparams=None):
1193
    """Get instance properties.
1194

1195
    @type instance_name: string
1196
    @param instance_name: the instance name
1197
    @type hvparams: dict of strings
1198
    @param hvparams: hvparams to be used with this instance
1199
    @rtype: tuple of strings
1200
    @return: (name, id, memory, vcpus, stat, times)
1201

1202
    """
1203
    _, pid, alive = self._InstancePidAlive(instance_name)
1204
    if not alive:
1205
      return None
1206

    
1207
    _, memory, vcpus = self._InstancePidInfo(pid)
1208
    istat = "---b-"
1209
    times = "0"
1210

    
1211
    try:
1212
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1213
      qmp.connect()
1214
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1215
      # Will fail if ballooning is not enabled, but we can then just resort to
1216
      # the value above.
1217
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1218
      memory = mem_bytes / 1048576
1219
    except errors.HypervisorError:
1220
      pass
1221

    
1222
    return (instance_name, pid, memory, vcpus, istat, times)
1223

    
1224
  def GetAllInstancesInfo(self, hvparams=None):
1225
    """Get properties of all instances.
1226

1227
    @type hvparams: dict of strings
1228
    @param hvparams: hypervisor parameter
1229
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1230

1231
    """
1232
    data = []
1233
    for name in os.listdir(self._PIDS_DIR):
1234
      try:
1235
        info = self.GetInstanceInfo(name)
1236
      except errors.HypervisorError:
1237
        # Ignore exceptions due to instances being shut down
1238
        continue
1239
      if info:
1240
        data.append(info)
1241
    return data
1242

    
1243
  def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
1244
                                      kvmhelp, devlist):
1245
    """Generate KVM options regarding instance's block devices.
1246

1247
    @type instance: L{objects.Instance}
1248
    @param instance: the instance object
1249
    @type up_hvp: dict
1250
    @param up_hvp: the instance's runtime hypervisor parameters
1251
    @type kvm_disks: list of tuples
1252
    @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1253
    @type kvmhelp: string
1254
    @param kvmhelp: output of kvm --help
1255
    @type devlist: string
1256
    @param devlist: output of kvm -device ?
1257
    @rtype: list
1258
    @return: list of command line options eventually used by kvm executable
1259

1260
    """
1261
    kernel_path = up_hvp[constants.HV_KERNEL_PATH]
1262
    if kernel_path:
1263
      boot_disk = False
1264
    else:
1265
      boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1266

    
1267
    # whether this is an older KVM version that uses the boot=on flag
1268
    # on devices
1269
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1270

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

    
1310
      access_mode = cfdev.params.get(constants.LDP_ACCESS,
1311
                                     constants.DISK_KERNELSPACE)
1312
      if (uri and access_mode == constants.DISK_USERSPACE):
1313
        drive_uri = uri
1314
      else:
1315
        drive_uri = link_name
1316

    
1317
      drive_val = "file=%s,format=raw%s%s%s" % \
1318
                  (drive_uri, if_val, boot_val, cache_val)
1319

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

    
1332
      dev_opts.extend(["-drive", drive_val])
1333

    
1334
    return dev_opts
1335

    
1336
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1337
                          kvmhelp):
1338
    """Generate KVM information to start an instance.
1339

1340
    @type kvmhelp: string
1341
    @param kvmhelp: output of kvm --help
1342
    @attention: this function must not have any side-effects; for
1343
        example, it must not write to the filesystem, or read values
1344
        from the current system the are expected to differ between
1345
        nodes, since it is only run once at instance startup;
1346
        actions/kvm arguments that can vary between systems should be
1347
        done in L{_ExecuteKVMRuntime}
1348

1349
    """
1350
    # pylint: disable=R0912,R0914,R0915
1351
    hvp = instance.hvparams
1352
    self.ValidateParameters(hvp)
1353

    
1354
    pidfile = self._InstancePidFile(instance.name)
1355
    kvm = hvp[constants.HV_KVM_PATH]
1356
    kvm_cmd = [kvm]
1357
    # used just by the vnc server, if enabled
1358
    kvm_cmd.extend(["-name", instance.name])
1359
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1360

    
1361
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1362
    if hvp[constants.HV_CPU_CORES]:
1363
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1364
    if hvp[constants.HV_CPU_THREADS]:
1365
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1366
    if hvp[constants.HV_CPU_SOCKETS]:
1367
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1368

    
1369
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1370

    
1371
    kvm_cmd.extend(["-pidfile", pidfile])
1372

    
1373
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1374

    
1375
    # As requested by music lovers
1376
    if hvp[constants.HV_SOUNDHW]:
1377
      soundhw = hvp[constants.HV_SOUNDHW]
1378
      # For some reason only few sound devices require a PCI slot
1379
      # while the Audio controller *must* be in slot 3.
1380
      # That's why we bridge this option early in command line
1381
      if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1382
        _ = _GetFreeSlot(pci_reservations, reserve=True)
1383
      kvm_cmd.extend(["-soundhw", soundhw])
1384

    
1385
    if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1386
      # The SCSI controller requires another PCI slot.
1387
      _ = _GetFreeSlot(pci_reservations, reserve=True)
1388

    
1389
    # Add id to ballon and place to the first available slot (3 or 4)
1390
    addr = _GetFreeSlot(pci_reservations, reserve=True)
1391
    pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1392
    kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1393
    kvm_cmd.extend(["-daemonize"])
1394
    if not instance.hvparams[constants.HV_ACPI]:
1395
      kvm_cmd.extend(["-no-acpi"])
1396
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1397
        constants.INSTANCE_REBOOT_EXIT:
1398
      kvm_cmd.extend(["-no-reboot"])
1399

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

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

    
1430
    if startup_paused:
1431
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1432

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

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

    
1440
    disk_type = hvp[constants.HV_DISK_TYPE]
1441

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

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

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

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

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

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

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

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

    
1520
    kvm_cmd.extend(["-usb"])
1521

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

    
1527
    if vnc_bind_address:
1528
      if netutils.IsValidInterface(vnc_bind_address):
1529
        if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1530
        if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1531
        if len(if_ip4_addresses) < 1:
1532
          logging.error("Could not determine IPv4 address of interface %s",
1533
                        vnc_bind_address)
1534
        else:
1535
          vnc_bind_address = if_ip4_addresses[0]
1536
      if netutils.IP4Address.IsValid(vnc_bind_address):
1537
        if instance.network_port > constants.VNC_BASE_PORT:
1538
          display = instance.network_port - constants.VNC_BASE_PORT
1539
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1540
            vnc_arg = ":%d" % (display)
1541
          else:
1542
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1543
        else:
1544
          logging.error("Network port is not a valid VNC display (%d < %d),"
1545
                        " not starting VNC",
1546
                        instance.network_port, constants.VNC_BASE_PORT)
1547
          vnc_arg = "none"
1548

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

    
1563
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1564

    
1565
      else:
1566
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1567

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

    
1578
        # if the user specified an IP version and the interface does not
1579
        # have that kind of IP addresses, throw an exception
1580
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1581
          if not addresses[spice_ip_version]:
1582
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1583
                                         " for %s" % (spice_ip_version,
1584
                                                      spice_bind))
1585

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

    
1602
        spice_address = addresses[spice_ip_version][0]
1603

    
1604
      else:
1605
        # spice_bind is known to be a valid IP address, because
1606
        # ValidateParameters checked it.
1607
        spice_address = spice_bind
1608

    
1609
      spice_arg = "addr=%s" % spice_address
1610
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1611
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1612
                     (spice_arg, instance.network_port,
1613
                      pathutils.SPICE_CACERT_FILE))
1614
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1615
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1616
                      pathutils.SPICE_CERT_FILE))
1617
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1618
        if tls_ciphers:
1619
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1620
      else:
1621
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1622

    
1623
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1624
        spice_arg = "%s,disable-ticketing" % spice_arg
1625

    
1626
      if spice_ip_version:
1627
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1628

    
1629
      # Image compression options
1630
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1631
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1632
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1633
      if img_lossless:
1634
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1635
      if img_jpeg:
1636
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1637
      if img_zlib_glz:
1638
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1639

    
1640
      # Video stream detection
1641
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1642
      if video_streaming:
1643
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1644

    
1645
      # Audio compression, by default in qemu-kvm it is on
1646
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1647
        spice_arg = "%s,playback-compression=off" % spice_arg
1648
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1649
        spice_arg = "%s,agent-mouse=off" % spice_arg
1650
      else:
1651
        # Enable the spice agent communication channel between the host and the
1652
        # agent.
1653
        addr = _GetFreeSlot(pci_reservations, reserve=True)
1654
        pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1655
        kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1656
        kvm_cmd.extend([
1657
          "-device",
1658
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1659
          ])
1660
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1661

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

    
1665
    else:
1666
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1667
      # also works in earlier versions though (tested with 1.1 and 1.3)
1668
      if self._DISPLAY_RE.search(kvmhelp):
1669
        kvm_cmd.extend(["-display", "none"])
1670
      else:
1671
        kvm_cmd.extend(["-nographic"])
1672

    
1673
    if hvp[constants.HV_USE_LOCALTIME]:
1674
      kvm_cmd.extend(["-localtime"])
1675

    
1676
    if hvp[constants.HV_KVM_USE_CHROOT]:
1677
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1678

    
1679
    # Add qemu-KVM -cpu param
1680
    if hvp[constants.HV_CPU_TYPE]:
1681
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1682

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

    
1690
    # Various types of usb devices, comma separated
1691
    if hvp[constants.HV_USB_DEVICES]:
1692
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1693
        kvm_cmd.extend(["-usbdevice", dev])
1694

    
1695
    # Set system UUID to instance UUID
1696
    if self._UUID_RE.search(kvmhelp):
1697
      kvm_cmd.extend(["-uuid", instance.uuid])
1698

    
1699
    if hvp[constants.HV_KVM_EXTRA]:
1700
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1701

    
1702
    kvm_disks = []
1703
    for disk, link_name, uri in block_devices:
1704
      disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1705
      kvm_disks.append((disk, link_name, uri))
1706

    
1707
    kvm_nics = []
1708
    for nic in instance.nics:
1709
      nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1710
      kvm_nics.append(nic)
1711

    
1712
    hvparams = hvp
1713

    
1714
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1715

    
1716
  def _WriteKVMRuntime(self, instance_name, data):
1717
    """Write an instance's KVM runtime
1718

1719
    """
1720
    try:
1721
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1722
                      data=data)
1723
    except EnvironmentError, err:
1724
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1725

    
1726
  def _ReadKVMRuntime(self, instance_name):
1727
    """Read an instance's KVM runtime
1728

1729
    """
1730
    try:
1731
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1732
    except EnvironmentError, err:
1733
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1734
    return file_content
1735

    
1736
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1737
    """Save an instance's KVM runtime
1738

1739
    """
1740
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1741

    
1742
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1743
    serialized_disks = [(blk.ToDict(), link, uri)
1744
                        for blk, link, uri in kvm_disks]
1745
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1746
                                      serialized_disks))
1747

    
1748
    self._WriteKVMRuntime(instance.name, serialized_form)
1749

    
1750
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1751
    """Load an instance's KVM runtime
1752

1753
    """
1754
    if not serialized_runtime:
1755
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1756

    
1757
    return _AnalyzeSerializedRuntime(serialized_runtime)
1758

    
1759
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1760
    """Run the KVM cmd and check for errors
1761

1762
    @type name: string
1763
    @param name: instance name
1764
    @type kvm_cmd: list of strings
1765
    @param kvm_cmd: runcmd input for kvm
1766
    @type tap_fds: list of int
1767
    @param tap_fds: fds of tap devices opened by Ganeti
1768

1769
    """
1770
    try:
1771
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1772
    finally:
1773
      for fd in tap_fds:
1774
        utils_wrapper.CloseFdNoError(fd)
1775

    
1776
    if result.failed:
1777
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1778
                                   (name, result.fail_reason, result.output))
1779
    if not self._InstancePidAlive(name)[2]:
1780
      raise errors.HypervisorError("Failed to start instance %s" % name)
1781

    
1782
  # too many local variables
1783
  # pylint: disable=R0914
1784
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1785
    """Execute a KVM cmd, after completing it with some last minute data.
1786

1787
    @type incoming: tuple of strings
1788
    @param incoming: (target_host_ip, port)
1789
    @type kvmhelp: string
1790
    @param kvmhelp: output of kvm --help
1791

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

    
1805
    temp_files = []
1806

    
1807
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1808
    # the first element of kvm_cmd is always the path to the kvm binary
1809
    kvm_path = kvm_cmd[0]
1810
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1811

    
1812
    # We know it's safe to run as a different user upon migration, so we'll use
1813
    # the latest conf, from conf_hvp.
1814
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1815
    if security_model == constants.HT_SM_USER:
1816
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1817

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

    
1828
    # We have reasons to believe changing something like the nic driver/type
1829
    # upon migration won't exactly fly with the instance kernel, so for nic
1830
    # related parameters we'll use up_hvp
1831
    tapfds = []
1832
    taps = []
1833
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1834

    
1835
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1836
                                                     up_hvp,
1837
                                                     kvm_disks,
1838
                                                     kvmhelp,
1839
                                                     devlist)
1840
    kvm_cmd.extend(bdev_opts)
1841

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

    
1859
        if up_hvp[constants.HV_VHOST_NET]:
1860
          # check for vhost_net support
1861
          if self._VHOST_RE.search(kvmhelp):
1862
            tap_extra = ",vhost=on"
1863
          else:
1864
            raise errors.HypervisorError("vhost_net is configured"
1865
                                         " but it is not available")
1866
      else:
1867
        nic_model = nic_type
1868

    
1869
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1870

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

    
1895
    if incoming:
1896
      target, port = incoming
1897
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1898

    
1899
    # Changing the vnc password doesn't bother the guest that much. At most it
1900
    # will surprise people who connect to it. Whether positively or negatively
1901
    # it's debatable.
1902
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1903
    vnc_pwd = None
1904
    if vnc_pwd_file:
1905
      try:
1906
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1907
      except EnvironmentError, err:
1908
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1909
                                     % (vnc_pwd_file, err))
1910

    
1911
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1912
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1913
                         constants.SECURE_DIR_MODE)])
1914

    
1915
    # Automatically enable QMP if version is >= 0.14
1916
    if self._QMP_RE.search(kvmhelp):
1917
      logging.debug("Enabling QMP")
1918
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1919
                      self._InstanceQmpMonitor(instance.name)])
1920

    
1921
    # Configure the network now for starting instances and bridged interfaces,
1922
    # during FinalizeMigration for incoming instances' routed interfaces
1923
    for nic_seq, nic in enumerate(kvm_nics):
1924
      if (incoming and
1925
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1926
        continue
1927
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1928

    
1929
    # CPU affinity requires kvm to start paused, so we set this flag if the
1930
    # instance is not already paused and if we are not going to accept a
1931
    # migrating instance. In the latter case, pausing is not needed.
1932
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1933
    if start_kvm_paused:
1934
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1935

    
1936
    # Note: CPU pinning is using up_hvp since changes take effect
1937
    # during instance startup anyway, and to avoid problems when soft
1938
    # rebooting the instance.
1939
    cpu_pinning = False
1940
    if up_hvp.get(constants.HV_CPU_MASK, None):
1941
      cpu_pinning = True
1942

    
1943
    if security_model == constants.HT_SM_POOL:
1944
      ss = ssconf.SimpleStore()
1945
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1946
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1947
      uid = uidpool.RequestUnusedUid(all_uids)
1948
      try:
1949
        username = pwd.getpwuid(uid.GetUid()).pw_name
1950
        kvm_cmd.extend(["-runas", username])
1951
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1952
      except:
1953
        uidpool.ReleaseUid(uid)
1954
        raise
1955
      else:
1956
        uid.Unlock()
1957
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1958
    else:
1959
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1960

    
1961
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1962
                     constants.RUN_DIRS_MODE)])
1963
    for nic_seq, tap in enumerate(taps):
1964
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1965
                      data=tap)
1966

    
1967
    if vnc_pwd:
1968
      change_cmd = "change vnc password %s" % vnc_pwd
1969
      self._CallMonitorCommand(instance.name, change_cmd)
1970

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

    
1985
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1986
      qmp.connect()
1987
      arguments = {
1988
          "protocol": "spice",
1989
          "password": spice_pwd,
1990
      }
1991
      qmp.Execute("set_password", arguments)
1992

    
1993
    for filename in temp_files:
1994
      utils.RemoveFile(filename)
1995

    
1996
    # If requested, set CPU affinity and resume instance execution
1997
    if cpu_pinning:
1998
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1999

    
2000
    start_memory = self._InstanceStartupMemory(instance)
2001
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
2002
      self.BalloonInstanceMemory(instance, start_memory)
2003

    
2004
    if start_kvm_paused:
2005
      # To control CPU pinning, ballooning, and vnc/spice passwords
2006
      # the VM was started in a frozen state. If freezing was not
2007
      # explicitly requested resume the vm status.
2008
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2009

    
2010
  def StartInstance(self, instance, block_devices, startup_paused):
2011
    """Start an instance.
2012

2013
    """
2014
    self._CheckDown(instance.name)
2015
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2016
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2017
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2018
                                           startup_paused, kvmhelp)
2019
    self._SaveKVMRuntime(instance, kvm_runtime)
2020
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2021

    
2022
  def _CallMonitorCommand(self, instance_name, command, timeout=None):
2023
    """Invoke a command on the instance monitor.
2024

2025
    """
2026
    if timeout is not None:
2027
      timeout_cmd = "timeout %s" % (timeout, )
2028
    else:
2029
      timeout_cmd = ""
2030

    
2031
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2032
    # version. The monitor protocol is designed for human consumption, whereas
2033
    # QMP is made for programmatic usage. In the worst case QMP can also
2034
    # execute monitor commands. As it is, all calls to socat take at least
2035
    # 500ms and likely more: socat can't detect the end of the reply and waits
2036
    # for 500ms of no data received before exiting (500 ms is the default for
2037
    # the "-t" parameter).
2038
    socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2039
             (utils.ShellQuote(command),
2040
              timeout_cmd,
2041
              constants.SOCAT_PATH,
2042
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2043

    
2044
    result = utils.RunCmd(socat)
2045
    if result.failed:
2046
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2047
             " output: %s" %
2048
             (command, instance_name, result.fail_reason, result.output))
2049
      raise errors.HypervisorError(msg)
2050

    
2051
    return result
2052

    
2053
  def _GetFreePCISlot(self, instance, dev):
2054
    """Get the first available pci slot of a runnung instance.
2055

2056
    """
2057
    slots = bitarray(32)
2058
    slots.setall(False) # pylint: disable=E1101
2059
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2060
    for line in output.stdout.splitlines():
2061
      match = self._INFO_PCI_RE.search(line)
2062
      if match:
2063
        slot = int(match.group(1))
2064
        slots[slot] = True
2065

    
2066
    dev.pci = _GetFreeSlot(slots)
2067

    
2068
  def VerifyHotplugSupport(self, instance, action, dev_type):
2069
    """Verifies that hotplug is supported.
2070

2071
    Hotplug is *not* supported in case of:
2072
     - security models and chroot (disk hotplug)
2073
     - fdsend module is missing (nic hot-add)
2074

2075
    @raise errors.HypervisorError: in one of the previous cases
2076

2077
    """
2078
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2079
      hvp = instance.hvparams
2080
      security_model = hvp[constants.HV_SECURITY_MODEL]
2081
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2082
      if use_chroot:
2083
        raise errors.HotplugError("Disk hotplug is not supported"
2084
                                  " in case of chroot.")
2085
      if security_model != constants.HT_SM_NONE:
2086
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2087
                                  " security models are used.")
2088

    
2089
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2090
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2091
      raise errors.HotplugError("Cannot hot-add NIC."
2092
                                " fdsend python module is missing.")
2093

    
2094
  def HotplugSupported(self, instance):
2095
    """Checks if hotplug is generally supported.
2096

2097
    Hotplug is *not* supported in case of:
2098
     - qemu versions < 1.0
2099
     - for stopped instances
2100

2101
    @raise errors.HypervisorError: in one of the previous cases
2102

2103
    """
2104
    try:
2105
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2106
    except errors.HypervisorError:
2107
      raise errors.HotplugError("Instance is probably down")
2108

    
2109
    # TODO: search for netdev_add, drive_add, device_add.....
2110
    match = self._INFO_VERSION_RE.search(output.stdout)
2111
    if not match:
2112
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2113

    
2114
    v_major, v_min, _, _ = match.groups()
2115
    if (int(v_major), int(v_min)) < (1, 0):
2116
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2117

    
2118
  def _CallHotplugCommands(self, name, cmds):
2119
    for c in cmds:
2120
      self._CallMonitorCommand(name, c)
2121
      time.sleep(1)
2122

    
2123
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2124
                            should_exist):
2125
    """Checks if a previous hotplug command has succeeded.
2126

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

2130
    @raise errors.HypervisorError: if result is not the expected one
2131

2132
    """
2133
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2134
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2135
    match = \
2136
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2137
    if match and not should_exist:
2138
      msg = "Device %s should have been removed but is still there" % kvm_devid
2139
      raise errors.HypervisorError(msg)
2140

    
2141
    if not match and should_exist:
2142
      msg = "Device %s should have been added but is missing" % kvm_devid
2143
      raise errors.HypervisorError(msg)
2144

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

    
2147
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2148
    """ Helper method to hot-add a new device
2149

2150
    It gets free pci slot generates the device name and invokes the
2151
    device specific method.
2152

2153
    """
2154
    # in case of hot-mod this is given
2155
    if device.pci is None:
2156
      self._GetFreePCISlot(instance, device)
2157
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2158
    runtime = self._LoadKVMRuntime(instance)
2159
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2160
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2161
                (extra, kvm_devid)]
2162
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2163
                (hex(device.pci), kvm_devid, kvm_devid)]
2164
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2165
      (tap, fd) = _OpenTap()
2166
      self._ConfigureNIC(instance, seq, device, tap)
2167
      self._PassTapFd(instance, fd, device)
2168
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2169
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2170
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2171
      cmds += ["device_add %s" % args]
2172
      utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2173

    
2174
    self._CallHotplugCommands(instance.name, cmds)
2175
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2176
    # update relevant entries in runtime file
2177
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2178
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2179
    runtime[index].append(entry)
2180
    self._SaveKVMRuntime(instance, runtime)
2181

    
2182
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2183
    """ Helper method for hot-del device
2184

2185
    It gets device info from runtime file, generates the device name and
2186
    invokes the device specific method.
2187

2188
    """
2189
    runtime = self._LoadKVMRuntime(instance)
2190
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2191
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2192
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2193
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2194
      cmds = ["device_del %s" % kvm_devid]
2195
      cmds += ["drive_del %s" % kvm_devid]
2196
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2197
      cmds = ["device_del %s" % kvm_devid]
2198
      cmds += ["netdev_del %s" % kvm_devid]
2199
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2200
    self._CallHotplugCommands(instance.name, cmds)
2201
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2202
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2203
    runtime[index].remove(entry)
2204
    self._SaveKVMRuntime(instance, runtime)
2205

    
2206
    return kvm_device.pci
2207

    
2208
  def HotModDevice(self, instance, dev_type, device, _, seq):
2209
    """ Helper method for hot-mod device
2210

2211
    It gets device info from runtime file, generates the device name and
2212
    invokes the device specific method. Currently only NICs support hot-mod
2213

2214
    """
2215
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2216
      # putting it back in the same pci slot
2217
      device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2218
      self.HotAddDevice(instance, dev_type, device, _, seq)
2219

    
2220
  def _PassTapFd(self, instance, fd, nic):
2221
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2222

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

    
2237
  @classmethod
2238
  def _ParseKVMVersion(cls, text):
2239
    """Parse the KVM version from the --help output.
2240

2241
    @type text: string
2242
    @param text: output of kvm --help
2243
    @return: (version, v_maj, v_min, v_rev)
2244
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2245

2246
    """
2247
    match = cls._VERSION_RE.search(text.splitlines()[0])
2248
    if not match:
2249
      raise errors.HypervisorError("Unable to get KVM version")
2250

    
2251
    v_all = match.group(0)
2252
    v_maj = int(match.group(1))
2253
    v_min = int(match.group(2))
2254
    if match.group(4):
2255
      v_rev = int(match.group(4))
2256
    else:
2257
      v_rev = 0
2258
    return (v_all, v_maj, v_min, v_rev)
2259

    
2260
  @classmethod
2261
  def _GetKVMOutput(cls, kvm_path, option):
2262
    """Return the output of a kvm invocation
2263

2264
    @type kvm_path: string
2265
    @param kvm_path: path to the kvm executable
2266
    @type option: a key of _KVMOPTS_CMDS
2267
    @param option: kvm option to fetch the output from
2268
    @return: output a supported kvm invocation
2269
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2270

2271
    """
2272
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2273

    
2274
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2275

    
2276
    result = utils.RunCmd([kvm_path] + optlist)
2277
    if result.failed and not can_fail:
2278
      raise errors.HypervisorError("Unable to get KVM %s output" %
2279
                                    " ".join(optlist))
2280
    return result.output
2281

    
2282
  @classmethod
2283
  def _GetKVMVersion(cls, kvm_path):
2284
    """Return the installed KVM version.
2285

2286
    @return: (version, v_maj, v_min, v_rev)
2287
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2288

2289
    """
2290
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2291

    
2292
  @classmethod
2293
  def _GetDefaultMachineVersion(cls, kvm_path):
2294
    """Return the default hardware revision (e.g. pc-1.1)
2295

2296
    """
2297
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2298
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2299
    if match:
2300
      return match.group(1)
2301
    else:
2302
      return "pc"
2303

    
2304
  def StopInstance(self, instance, force=False, retry=False, name=None,
2305
                   timeout=None):
2306
    """Stop an instance.
2307

2308
    """
2309
    assert(timeout is None or force is not None)
2310

    
2311
    if name is not None and not force:
2312
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2313
    if name is None:
2314
      name = instance.name
2315
      acpi = instance.hvparams[constants.HV_ACPI]
2316
    else:
2317
      acpi = False
2318
    _, pid, alive = self._InstancePidAlive(name)
2319
    if pid > 0 and alive:
2320
      if force or not acpi:
2321
        utils.KillProcess(pid)
2322
      else:
2323
        self._CallMonitorCommand(name, "system_powerdown", timeout)
2324

    
2325
  def CleanupInstance(self, instance_name):
2326
    """Cleanup after a stopped instance
2327

2328
    """
2329
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2330
    if pid > 0 and alive:
2331
      raise errors.HypervisorError("Cannot cleanup a live instance")
2332
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2333

    
2334
  def RebootInstance(self, instance):
2335
    """Reboot an instance.
2336

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

    
2357
  def MigrationInfo(self, instance):
2358
    """Get instance information to perform a migration.
2359

2360
    @type instance: L{objects.Instance}
2361
    @param instance: instance to be migrated
2362
    @rtype: string
2363
    @return: content of the KVM runtime file
2364

2365
    """
2366
    return self._ReadKVMRuntime(instance.name)
2367

    
2368
  def AcceptInstance(self, instance, info, target):
2369
    """Prepare to accept an instance.
2370

2371
    @type instance: L{objects.Instance}
2372
    @param instance: instance to be accepted
2373
    @type info: string
2374
    @param info: content of the KVM runtime file on the source node
2375
    @type target: string
2376
    @param target: target host (usually ip), on this node
2377

2378
    """
2379
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2380
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2381
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2382
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2383
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2384
                            incoming=incoming_address)
2385

    
2386
  def FinalizeMigrationDst(self, instance, info, success):
2387
    """Finalize the instance migration on the target node.
2388

2389
    Stop the incoming mode KVM.
2390

2391
    @type instance: L{objects.Instance}
2392
    @param instance: instance whose migration is being finalized
2393

2394
    """
2395
    if success:
2396
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2397
      kvm_nics = kvm_runtime[1]
2398

    
2399
      for nic_seq, nic in enumerate(kvm_nics):
2400
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2401
          # Bridged interfaces have already been configured
2402
          continue
2403
        try:
2404
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2405
        except EnvironmentError, err:
2406
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2407
                          instance.name, nic_seq, str(err))
2408
          continue
2409
        try:
2410
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2411
        except errors.HypervisorError, err:
2412
          logging.warning(str(err))
2413

    
2414
      self._WriteKVMRuntime(instance.name, info)
2415
    else:
2416
      self.StopInstance(instance, force=True)
2417

    
2418
  def MigrateInstance(self, cluster_name, instance, target, live):
2419
    """Migrate an instance to a target node.
2420

2421
    The migration will not be attempted if the instance is not
2422
    currently running.
2423

2424
    @type cluster_name: string
2425
    @param cluster_name: name of the cluster
2426
    @type instance: L{objects.Instance}
2427
    @param instance: the instance to be migrated
2428
    @type target: string
2429
    @param target: ip address of the target node
2430
    @type live: boolean
2431
    @param live: perform a live migration
2432

2433
    """
2434
    instance_name = instance.name
2435
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2436
    _, _, alive = self._InstancePidAlive(instance_name)
2437
    if not alive:
2438
      raise errors.HypervisorError("Instance not running, cannot migrate")
2439

    
2440
    if not live:
2441
      self._CallMonitorCommand(instance_name, "stop")
2442

    
2443
    migrate_command = ("migrate_set_speed %dm" %
2444
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2445
    self._CallMonitorCommand(instance_name, migrate_command)
2446

    
2447
    migrate_command = ("migrate_set_downtime %dms" %
2448
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2449
    self._CallMonitorCommand(instance_name, migrate_command)
2450

    
2451
    migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2452
    if migration_caps:
2453
      for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2454
        migrate_command = ("migrate_set_capability %s on" % c)
2455
        self._CallMonitorCommand(instance_name, migrate_command)
2456

    
2457
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2458
    self._CallMonitorCommand(instance_name, migrate_command)
2459

    
2460
  def FinalizeMigrationSource(self, instance, success, live):
2461
    """Finalize the instance migration on the source node.
2462

2463
    @type instance: L{objects.Instance}
2464
    @param instance: the instance that was migrated
2465
    @type success: bool
2466
    @param success: whether the migration succeeded or not
2467
    @type live: bool
2468
    @param live: whether the user requested a live migration or not
2469

2470
    """
2471
    if success:
2472
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2473
      utils.KillProcess(pid)
2474
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2475
    elif live:
2476
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2477

    
2478
  def GetMigrationStatus(self, instance):
2479
    """Get the migration status
2480

2481
    @type instance: L{objects.Instance}
2482
    @param instance: the instance that is being migrated
2483
    @rtype: L{objects.MigrationStatus}
2484
    @return: the status of the current migration (one of
2485
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2486
             progress info that can be retrieved from the hypervisor
2487

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

    
2508
          return migration_status
2509

    
2510
        logging.warning("KVM: unknown migration status '%s'", status)
2511

    
2512
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2513

    
2514
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2515

    
2516
  def BalloonInstanceMemory(self, instance, mem):
2517
    """Balloon an instance memory to a certain value.
2518

2519
    @type instance: L{objects.Instance}
2520
    @param instance: instance to be accepted
2521
    @type mem: int
2522
    @param mem: actual memory size to use for instance runtime
2523

2524
    """
2525
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2526

    
2527
  def GetNodeInfo(self, hvparams=None):
2528
    """Return information about the node.
2529

2530
    @type hvparams: dict of strings
2531
    @param hvparams: hypervisor parameters, not used in this class
2532

2533
    @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2534
        the following keys:
2535
          - hv_version: the hypervisor version in the form (major, minor,
2536
                        revision)
2537

2538
    """
2539
    result = self.GetLinuxNodeInfo()
2540
    kvmpath = constants.KVM_PATH
2541
    if hvparams is not None:
2542
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2543
    _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2544
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2545
    return result
2546

    
2547
  @classmethod
2548
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
2549
    """Return a command for connecting to the console of an instance.
2550

2551
    """
2552
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2553
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2554
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2555
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2556
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2557
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2558
      return objects.InstanceConsole(instance=instance.name,
2559
                                     kind=constants.CONS_SSH,
2560
                                     host=primary_node.name,
2561
                                     user=constants.SSH_CONSOLE_USER,
2562
                                     command=cmd)
2563

    
2564
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2565
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2566
      display = instance.network_port - constants.VNC_BASE_PORT
2567
      return objects.InstanceConsole(instance=instance.name,
2568
                                     kind=constants.CONS_VNC,
2569
                                     host=vnc_bind_address,
2570
                                     port=instance.network_port,
2571
                                     display=display)
2572

    
2573
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2574
    if spice_bind:
2575
      return objects.InstanceConsole(instance=instance.name,
2576
                                     kind=constants.CONS_SPICE,
2577
                                     host=spice_bind,
2578
                                     port=instance.network_port)
2579

    
2580
    return objects.InstanceConsole(instance=instance.name,
2581
                                   kind=constants.CONS_MESSAGE,
2582
                                   message=("No serial shell for instance %s" %
2583
                                            instance.name))
2584

    
2585
  def Verify(self, hvparams=None):
2586
    """Verify the hypervisor.
2587

2588
    Check that the required binaries exist.
2589

2590
    @type hvparams: dict of strings
2591
    @param hvparams: hypervisor parameters to be verified against, not used here
2592

2593
    @return: Problem description if something is wrong, C{None} otherwise
2594

2595
    """
2596
    msgs = []
2597
    kvmpath = constants.KVM_PATH
2598
    if hvparams is not None:
2599
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2600
    if not os.path.exists(kvmpath):
2601
      msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2602
    if not os.path.exists(constants.SOCAT_PATH):
2603
      msgs.append("The socat binary ('%s') does not exist" %
2604
                  constants.SOCAT_PATH)
2605

    
2606
    return self._FormatVerifyResults(msgs)
2607

    
2608
  @classmethod
2609
  def CheckParameterSyntax(cls, hvparams):
2610
    """Check the given parameters for validity.
2611

2612
    @type hvparams:  dict
2613
    @param hvparams: dictionary with parameter names/value
2614
    @raise errors.HypervisorError: when a parameter is not valid
2615

2616
    """
2617
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2618

    
2619
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2620
    if kernel_path:
2621
      if not hvparams[constants.HV_ROOT_PATH]:
2622
        raise errors.HypervisorError("Need a root partition for the instance,"
2623
                                     " if a kernel is defined")
2624

    
2625
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2626
        not hvparams[constants.HV_VNC_X509]):
2627
      raise errors.HypervisorError("%s must be defined, if %s is" %
2628
                                   (constants.HV_VNC_X509,
2629
                                    constants.HV_VNC_X509_VERIFY))
2630

    
2631
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2632
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2633
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2634
      if not serial_speed or serial_speed not in valid_speeds:
2635
        raise errors.HypervisorError("Invalid serial console speed, must be"
2636
                                     " one of: %s" %
2637
                                     utils.CommaJoin(valid_speeds))
2638

    
2639
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2640
    if (boot_order == constants.HT_BO_CDROM and
2641
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2642
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2643
                                   " ISO path")
2644

    
2645
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2646
    if security_model == constants.HT_SM_USER:
2647
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2648
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2649
                                     " must be specified")
2650
    elif (security_model == constants.HT_SM_NONE or
2651
          security_model == constants.HT_SM_POOL):
2652
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2653
        raise errors.HypervisorError("Cannot have a security domain when the"
2654
                                     " security model is 'none' or 'pool'")
2655

    
2656
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2657
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2658
    if spice_bind:
2659
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2660
        # if an IP version is specified, the spice_bind parameter must be an
2661
        # IP of that family
2662
        if (netutils.IP4Address.IsValid(spice_bind) and
2663
            spice_ip_version != constants.IP4_VERSION):
2664
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2665
                                       " the specified IP version is %s" %
2666
                                       (spice_bind, spice_ip_version))
2667

    
2668
        if (netutils.IP6Address.IsValid(spice_bind) and
2669
            spice_ip_version != constants.IP6_VERSION):
2670
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2671
                                       " the specified IP version is %s" %
2672
                                       (spice_bind, spice_ip_version))
2673
    else:
2674
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2675
      # error if any of them is set without it.
2676
      for param in _SPICE_ADDITIONAL_PARAMS:
2677
        if hvparams[param]:
2678
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2679
                                       (param, constants.HV_KVM_SPICE_BIND))
2680

    
2681
  @classmethod
2682
  def ValidateParameters(cls, hvparams):
2683
    """Check the given parameters for validity.
2684

2685
    @type hvparams:  dict
2686
    @param hvparams: dictionary with parameter names/value
2687
    @raise errors.HypervisorError: when a parameter is not valid
2688

2689
    """
2690
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2691

    
2692
    kvm_path = hvparams[constants.HV_KVM_PATH]
2693

    
2694
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2695
    if security_model == constants.HT_SM_USER:
2696
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2697
      try:
2698
        pwd.getpwnam(username)
2699
      except KeyError:
2700
        raise errors.HypervisorError("Unknown security domain user %s"
2701
                                     % username)
2702
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2703
    if vnc_bind_address:
2704
      bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2705
      is_interface = netutils.IsValidInterface(vnc_bind_address)
2706
      is_path = utils.IsNormAbsPath(vnc_bind_address)
2707
      if not bound_to_addr and not is_interface and not is_path:
2708
        raise errors.HypervisorError("VNC: The %s parameter must be either"
2709
                                     " a valid IP address, an interface name,"
2710
                                     " or an absolute path" %
2711
                                     constants.HV_KVM_SPICE_BIND)
2712

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

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

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

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

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

2746
    @type hvparams: dict of strings
2747
    @param hvparams: hypervisor params to be used on this node
2748

2749
    """
2750
    cls.LinuxPowercycle()