Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ f14863bc

History | View | Annotate | Download (96.5 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_VNET_HDR = 0x4000
75

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

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

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

    
117

    
118
def _GenerateDeviceKVMId(dev_type, dev, idx=None):
119
  """Helper function to generate a unique device name used by KVM
120

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

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

131
  """
132

    
133
  # proper device id - available in latest Ganeti versions
134
  if dev.pci and dev.uuid:
135
    return "%s-%s-pci-%d" % (dev_type.lower(), dev.uuid.split("-")[0], dev.pci)
136

    
137
  # dummy device id - returned only to _GenerateKVMBlockDevicesOptions
138
  # This enables -device option for paravirtual disk_type
139
  if idx is not None:
140
    return "%s-%d" % (dev_type.lower(), idx)
141

    
142
  raise errors.HotplugError("Hotplug is not supported for devices"
143
                            " without UUID or PCI info")
144

    
145

    
146
def _UpdatePCISlots(dev, pci_reservations):
147
  """Update pci configuration for a stopped instance
148

149
  If dev has a pci slot then reserve it, else find first available
150
  in pci_reservations bitarray. It acts on the same objects passed
151
  as params so there is no need to return anything.
152

153
  @type dev: L{objects.Disk} or L{objects.NIC}
154
  @param dev: the device object for which we update its pci slot
155
  @type pci_reservations: bitarray
156
  @param pci_reservations: existing pci reservations for an instance
157
  @raise errors.HotplugError: in case an instance has all its slot occupied
158

159
  """
160
  if dev.pci:
161
    free = dev.pci
162
  else: # pylint: disable=E1103
163
    [free] = pci_reservations.search(_AVAILABLE_PCI_SLOT, 1)
164
    if not free:
165
      raise errors.HypervisorError("All PCI slots occupied")
166
    dev.pci = int(free)
167

    
168
  pci_reservations[free] = True
169

    
170

    
171
def _GetExistingDeviceInfo(dev_type, device, runtime):
172
  """Helper function to get an existing device inside the runtime file
173

174
  Used when an instance is running. Load kvm runtime file and search
175
  for a device based on its type and uuid.
176

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

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

    
194
  return found[0]
195

    
196

    
197
def _UpgradeSerializedRuntime(serialized_runtime):
198
  """Upgrade runtime data
199

200
  Remove any deprecated fields or change the format of the data.
201
  The runtime files are not upgraded when Ganeti is upgraded, so the required
202
  modification have to be performed here.
203

204
  @type serialized_runtime: string
205
  @param serialized_runtime: raw text data read from actual runtime file
206
  @return: (cmd, nic dicts, hvparams, bdev dicts)
207
  @rtype: tuple
208

209
  """
210
  loaded_runtime = serializer.Load(serialized_runtime)
211
  kvm_cmd, serialized_nics, hvparams = loaded_runtime[:3]
212
  if len(loaded_runtime) >= 4:
213
    serialized_disks = loaded_runtime[3]
214
  else:
215
    serialized_disks = []
216

    
217
  for nic in serialized_nics:
218
    # Add a dummy uuid slot if an pre-2.8 NIC is found
219
    if "uuid" not in nic:
220
      nic["uuid"] = utils.NewUUID()
221

    
222
  return kvm_cmd, serialized_nics, hvparams, serialized_disks
223

    
224

    
225
def _AnalyzeSerializedRuntime(serialized_runtime):
226
  """Return runtime entries for a serialized runtime file
227

228
  @type serialized_runtime: string
229
  @param serialized_runtime: raw text data read from actual runtime file
230
  @return: (cmd, nics, hvparams, bdevs)
231
  @rtype: tuple
232

233
  """
234
  kvm_cmd, serialized_nics, hvparams, serialized_disks = \
235
    _UpgradeSerializedRuntime(serialized_runtime)
236
  kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
237
  kvm_disks = [(objects.Disk.FromDict(sdisk), link)
238
               for sdisk, link in serialized_disks]
239

    
240
  return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
241

    
242

    
243
def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
244
  """Retrieves supported TUN features from file descriptor.
245

246
  @see: L{_ProbeTapVnetHdr}
247

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

    
259

    
260
def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
261
  """Check whether to enable the IFF_VNET_HDR flag.
262

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

273
   @type fd: int
274
   @param fd: the file descriptor of /dev/net/tun
275

276
  """
277
  flags = _features_fn(fd)
278

    
279
  if flags is None:
280
    # Not supported
281
    return False
282

    
283
  result = bool(flags & IFF_VNET_HDR)
284

    
285
  if not result:
286
    logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
287

    
288
  return result
289

    
290

    
291
def _OpenTap(vnet_hdr=True):
292
  """Open a new tap device and return its file descriptor.
293

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

297
  @type vnet_hdr: boolean
298
  @param vnet_hdr: Enable the VNET Header
299
  @return: (ifname, tapfd)
300
  @rtype: tuple
301

302
  """
303
  try:
304
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
305
  except EnvironmentError:
306
    raise errors.HypervisorError("Failed to open /dev/net/tun")
307

    
308
  flags = IFF_TAP | IFF_NO_PI
309

    
310
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
311
    flags |= IFF_VNET_HDR
312

    
313
  # The struct ifreq ioctl request (see netdevice(7))
314
  ifr = struct.pack("16sh", "", flags)
315

    
316
  try:
317
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
318
  except EnvironmentError, err:
319
    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
320
                                 err)
321

    
322
  # Get the interface name from the ioctl
323
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
324
  return (ifname, tapfd)
325

    
326

    
327
class QmpMessage:
328
  """QEMU Messaging Protocol (QMP) message.
329

330
  """
331
  def __init__(self, data):
332
    """Creates a new QMP message based on the passed data.
333

334
    """
335
    if not isinstance(data, dict):
336
      raise TypeError("QmpMessage must be initialized with a dict")
337

    
338
    self.data = data
339

    
340
  def __getitem__(self, field_name):
341
    """Get the value of the required field if present, or None.
342

343
    Overrides the [] operator to provide access to the message data,
344
    returning None if the required item is not in the message
345
    @return: the value of the field_name field, or None if field_name
346
             is not contained in the message
347

348
    """
349
    return self.data.get(field_name, None)
350

    
351
  def __setitem__(self, field_name, field_value):
352
    """Set the value of the required field_name to field_value.
353

354
    """
355
    self.data[field_name] = field_value
356

    
357
  @staticmethod
358
  def BuildFromJsonString(json_string):
359
    """Build a QmpMessage from a JSON encoded string.
360

361
    @type json_string: str
362
    @param json_string: JSON string representing the message
363
    @rtype: L{QmpMessage}
364
    @return: a L{QmpMessage} built from json_string
365

366
    """
367
    # Parse the string
368
    data = serializer.LoadJson(json_string)
369
    return QmpMessage(data)
370

    
371
  def __str__(self):
372
    # The protocol expects the JSON object to be sent as a single line.
373
    return serializer.DumpJson(self.data)
374

    
375
  def __eq__(self, other):
376
    # When comparing two QmpMessages, we are interested in comparing
377
    # their internal representation of the message data
378
    return self.data == other.data
379

    
380

    
381
class MonitorSocket(object):
382
  _SOCKET_TIMEOUT = 5
383

    
384
  def __init__(self, monitor_filename):
385
    """Instantiates the MonitorSocket object.
386

387
    @type monitor_filename: string
388
    @param monitor_filename: the filename of the UNIX raw socket on which the
389
                             monitor (QMP or simple one) is listening
390

391
    """
392
    self.monitor_filename = monitor_filename
393
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
394
    # We want to fail if the server doesn't send a complete message
395
    # in a reasonable amount of time
396
    self.sock.settimeout(self._SOCKET_TIMEOUT)
397
    self._connected = False
398

    
399
  def _check_socket(self):
400
    sock_stat = None
401
    try:
402
      sock_stat = os.stat(self.monitor_filename)
403
    except EnvironmentError, err:
404
      if err.errno == errno.ENOENT:
405
        raise errors.HypervisorError("No monitor socket found")
406
      else:
407
        raise errors.HypervisorError("Error checking monitor socket: %s",
408
                                     utils.ErrnoOrStr(err))
409
    if not stat.S_ISSOCK(sock_stat.st_mode):
410
      raise errors.HypervisorError("Monitor socket is not a socket")
411

    
412
  def _check_connection(self):
413
    """Make sure that the connection is established.
414

415
    """
416
    if not self._connected:
417
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
418
                                   " invoke connect() on it")
419

    
420
  def connect(self):
421
    """Connects to the monitor.
422

423
    Connects to the UNIX socket
424

425
    @raise errors.HypervisorError: when there are communication errors
426

427
    """
428
    if self._connected:
429
      raise errors.ProgrammerError("Cannot connect twice")
430

    
431
    self._check_socket()
432

    
433
    # Check file existance/stuff
434
    try:
435
      self.sock.connect(self.monitor_filename)
436
    except EnvironmentError:
437
      raise errors.HypervisorError("Can't connect to qmp socket")
438
    self._connected = True
439

    
440
  def close(self):
441
    """Closes the socket
442

443
    It cannot be used after this call.
444

445
    """
446
    self.sock.close()
447

    
448

    
449
class QmpConnection(MonitorSocket):
450
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
451

452
  """
453
  _FIRST_MESSAGE_KEY = "QMP"
454
  _EVENT_KEY = "event"
455
  _ERROR_KEY = "error"
456
  _RETURN_KEY = RETURN_KEY = "return"
457
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
458
  _ERROR_CLASS_KEY = "class"
459
  _ERROR_DATA_KEY = "data"
460
  _ERROR_DESC_KEY = "desc"
461
  _EXECUTE_KEY = "execute"
462
  _ARGUMENTS_KEY = "arguments"
463
  _CAPABILITIES_COMMAND = "qmp_capabilities"
464
  _MESSAGE_END_TOKEN = "\r\n"
465

    
466
  def __init__(self, monitor_filename):
467
    super(QmpConnection, self).__init__(monitor_filename)
468
    self._buf = ""
469

    
470
  def connect(self):
471
    """Connects to the QMP monitor.
472

473
    Connects to the UNIX socket and makes sure that we can actually send and
474
    receive data to the kvm instance via QMP.
475

476
    @raise errors.HypervisorError: when there are communication errors
477
    @raise errors.ProgrammerError: when there are data serialization errors
478

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

    
489
    # Let's put the monitor in command mode using the qmp_capabilities
490
    # command, or else no command will be executable.
491
    # (As per the QEMU Protocol Specification 0.1 - section 4)
492
    self.Execute(self._CAPABILITIES_COMMAND)
493

    
494
  def _ParseMessage(self, buf):
495
    """Extract and parse a QMP message from the given buffer.
496

497
    Seeks for a QMP message in the given buf. If found, it parses it and
498
    returns it together with the rest of the characters in the buf.
499
    If no message is found, returns None and the whole buffer.
500

501
    @raise errors.ProgrammerError: when there are data serialization errors
502

503
    """
504
    message = None
505
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
506
    # Specification 0.1 - Section 2.1.1)
507
    pos = buf.find(self._MESSAGE_END_TOKEN)
508
    if pos >= 0:
509
      try:
510
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
511
      except Exception, err:
512
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
513
      buf = buf[pos + 1:]
514

    
515
    return (message, buf)
516

    
517
  def _Recv(self):
518
    """Receives a message from QMP and decodes the received JSON object.
519

520
    @rtype: QmpMessage
521
    @return: the received message
522
    @raise errors.HypervisorError: when there are communication errors
523
    @raise errors.ProgrammerError: when there are data serialization errors
524

525
    """
526
    self._check_connection()
527

    
528
    # Check if there is already a message in the buffer
529
    (message, self._buf) = self._ParseMessage(self._buf)
530
    if message:
531
      return message
532

    
533
    recv_buffer = StringIO.StringIO(self._buf)
534
    recv_buffer.seek(len(self._buf))
535
    try:
536
      while True:
537
        data = self.sock.recv(4096)
538
        if not data:
539
          break
540
        recv_buffer.write(data)
541

    
542
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
543
        if message:
544
          return message
545

    
546
    except socket.timeout, err:
547
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
548
                                   "%s" % (err))
549
    except socket.error, err:
550
      raise errors.HypervisorError("Unable to receive data from KVM using the"
551
                                   " QMP protocol: %s" % err)
552

    
553
  def _Send(self, message):
554
    """Encodes and sends a message to KVM using QMP.
555

556
    @type message: QmpMessage
557
    @param message: message to send to KVM
558
    @raise errors.HypervisorError: when there are communication errors
559
    @raise errors.ProgrammerError: when there are data serialization errors
560

561
    """
562
    self._check_connection()
563
    try:
564
      message_str = str(message)
565
    except Exception, err:
566
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
567

    
568
    try:
569
      self.sock.sendall(message_str)
570
    except socket.timeout, err:
571
      raise errors.HypervisorError("Timeout while sending a QMP message: "
572
                                   "%s (%s)" % (err.string, err.errno))
573
    except socket.error, err:
574
      raise errors.HypervisorError("Unable to send data from KVM using the"
575
                                   " QMP protocol: %s" % err)
576

    
577
  def Execute(self, command, arguments=None):
578
    """Executes a QMP command and returns the response of the server.
579

580
    @type command: str
581
    @param command: the command to execute
582
    @type arguments: dict
583
    @param arguments: dictionary of arguments to be passed to the command
584
    @rtype: dict
585
    @return: dictionary representing the received JSON object
586
    @raise errors.HypervisorError: when there are communication errors
587
    @raise errors.ProgrammerError: when there are data serialization errors
588

589
    """
590
    self._check_connection()
591
    message = QmpMessage({self._EXECUTE_KEY: command})
592
    if arguments:
593
      message[self._ARGUMENTS_KEY] = arguments
594
    self._Send(message)
595

    
596
    # Events can occur between the sending of the command and the reception
597
    # of the response, so we need to filter out messages with the event key.
598
    while True:
599
      response = self._Recv()
600
      err = response[self._ERROR_KEY]
601
      if err:
602
        raise errors.HypervisorError("kvm: error executing the %s"
603
                                     " command: %s (%s, %s):" %
604
                                     (command,
605
                                      err[self._ERROR_DESC_KEY],
606
                                      err[self._ERROR_CLASS_KEY],
607
                                      err[self._ERROR_DATA_KEY]))
608

    
609
      elif not response[self._EVENT_KEY]:
610
        return response
611

    
612

    
613
class KVMHypervisor(hv_base.BaseHypervisor):
614
  """KVM hypervisor interface
615

616
  """
617
  CAN_MIGRATE = True
618

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

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

    
722
  _VIRTIO = "virtio"
723
  _VIRTIO_NET_PCI = "virtio-net-pci"
724
  _VIRTIO_BLK_PCI = "virtio-blk-pci"
725

    
726
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
727
                                    re.M | re.I)
728
  _MIGRATION_PROGRESS_RE = \
729
    re.compile(r"\s*transferred\s+ram:\s+(?P<transferred>\d+)\s+kbytes\s*\n"
730
               r"\s*remaining\s+ram:\s+(?P<remaining>\d+)\s+kbytes\s*\n"
731
               r"\s*total\s+ram:\s+(?P<total>\d+)\s+kbytes\s*\n", re.I)
732

    
733
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
734
  _MIGRATION_INFO_RETRY_DELAY = 2
735

    
736
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
737

    
738
  _CPU_INFO_RE = re.compile(r"cpu\s+\#(\d+).*thread_id\s*=\s*(\d+)", re.I)
739
  _CPU_INFO_CMD = "info cpus"
740
  _CONT_CMD = "cont"
741

    
742
  _DEFAULT_MACHINE_VERSION_RE = re.compile(r"^(\S+).*\(default\)", re.M)
743
  _CHECK_MACHINE_VERSION_RE = \
744
    staticmethod(lambda x: re.compile(r"^(%s)[ ]+.*PC" % x, re.M))
745

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

    
761
  _INFO_PCI_RE = re.compile(r'Bus.*device[ ]*(\d+).*')
762
  _INFO_PCI_CMD = "info pci"
763
  _INFO_VERSION_RE = \
764
    re.compile(r'^QEMU (\d+)\.(\d+)(\.(\d+))?.*monitor.*', re.M)
765
  _INFO_VERSION_CMD = "info version"
766

    
767
  _DEFAULT_PCI_RESERVATIONS = "11110000000000000000000000000000"
768

    
769
  ANCILLARY_FILES = [
770
    _KVM_NETWORK_SCRIPT,
771
    ]
772
  ANCILLARY_FILES_OPT = [
773
    _KVM_NETWORK_SCRIPT,
774
    ]
775

    
776
  # Supported kvm options to get output from
777
  _KVMOPT_HELP = "help"
778
  _KVMOPT_MLIST = "mlist"
779
  _KVMOPT_DEVICELIST = "devicelist"
780

    
781
  # Command to execute to get the output from kvm, and whether to
782
  # accept the output even on failure.
783
  _KVMOPTS_CMDS = {
784
    _KVMOPT_HELP: (["--help"], False),
785
    _KVMOPT_MLIST: (["-M", "?"], False),
786
    _KVMOPT_DEVICELIST: (["-device", "?"], True),
787
  }
788

    
789
  def __init__(self):
790
    hv_base.BaseHypervisor.__init__(self)
791
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
792
    # in a tmpfs filesystem or has been otherwise wiped out.
793
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
794
    utils.EnsureDirs(dirs)
795

    
796
  @classmethod
797
  def _InstancePidFile(cls, instance_name):
798
    """Returns the instance pidfile.
799

800
    """
801
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
802

    
803
  @classmethod
804
  def _InstanceUidFile(cls, instance_name):
805
    """Returns the instance uidfile.
806

807
    """
808
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
809

    
810
  @classmethod
811
  def _InstancePidInfo(cls, pid):
812
    """Check pid file for instance information.
813

814
    Check that a pid file is associated with an instance, and retrieve
815
    information from its command line.
816

817
    @type pid: string or int
818
    @param pid: process id of the instance to check
819
    @rtype: tuple
820
    @return: (instance_name, memory, vcpus)
821
    @raise errors.HypervisorError: when an instance cannot be found
822

823
    """
824
    alive = utils.IsProcessAlive(pid)
825
    if not alive:
826
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
827

    
828
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
829
    try:
830
      cmdline = utils.ReadFile(cmdline_file)
831
    except EnvironmentError, err:
832
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
833
                                   (pid, err))
834

    
835
    instance = None
836
    memory = 0
837
    vcpus = 0
838

    
839
    arg_list = cmdline.split("\x00")
840
    while arg_list:
841
      arg = arg_list.pop(0)
842
      if arg == "-name":
843
        instance = arg_list.pop(0)
844
      elif arg == "-m":
845
        memory = int(arg_list.pop(0))
846
      elif arg == "-smp":
847
        vcpus = int(arg_list.pop(0).split(",")[0])
848

    
849
    if instance is None:
850
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
851
                                   " instance" % pid)
852

    
853
    return (instance, memory, vcpus)
854

    
855
  def _InstancePidAlive(self, instance_name):
856
    """Returns the instance pidfile, pid, and liveness.
857

858
    @type instance_name: string
859
    @param instance_name: instance name
860
    @rtype: tuple
861
    @return: (pid file name, pid, liveness)
862

863
    """
864
    pidfile = self._InstancePidFile(instance_name)
865
    pid = utils.ReadPidFile(pidfile)
866

    
867
    alive = False
868
    try:
869
      cmd_instance = self._InstancePidInfo(pid)[0]
870
      alive = (cmd_instance == instance_name)
871
    except errors.HypervisorError:
872
      pass
873

    
874
    return (pidfile, pid, alive)
875

    
876
  def _CheckDown(self, instance_name):
877
    """Raises an error unless the given instance is down.
878

879
    """
880
    alive = self._InstancePidAlive(instance_name)[2]
881
    if alive:
882
      raise errors.HypervisorError("Failed to start instance %s: %s" %
883
                                   (instance_name, "already running"))
884

    
885
  @classmethod
886
  def _InstanceMonitor(cls, instance_name):
887
    """Returns the instance monitor socket name
888

889
    """
890
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
891

    
892
  @classmethod
893
  def _InstanceSerial(cls, instance_name):
894
    """Returns the instance serial socket name
895

896
    """
897
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
898

    
899
  @classmethod
900
  def _InstanceQmpMonitor(cls, instance_name):
901
    """Returns the instance serial QMP socket name
902

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

    
906
  @staticmethod
907
  def _SocatUnixConsoleParams():
908
    """Returns the correct parameters for socat
909

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

912
    """
913
    if constants.SOCAT_USE_ESCAPE:
914
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
915
    else:
916
      return "echo=0,icanon=0"
917

    
918
  @classmethod
919
  def _InstanceKVMRuntime(cls, instance_name):
920
    """Returns the instance KVM runtime filename
921

922
    """
923
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
924

    
925
  @classmethod
926
  def _InstanceChrootDir(cls, instance_name):
927
    """Returns the name of the KVM chroot dir of the instance
928

929
    """
930
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
931

    
932
  @classmethod
933
  def _InstanceNICDir(cls, instance_name):
934
    """Returns the name of the directory holding the tap device files for a
935
    given instance.
936

937
    """
938
    return utils.PathJoin(cls._NICS_DIR, instance_name)
939

    
940
  @classmethod
941
  def _InstanceNICFile(cls, instance_name, seq):
942
    """Returns the name of the file containing the tap device for a given NIC
943

944
    """
945
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
946

    
947
  @classmethod
948
  def _InstanceKeymapFile(cls, instance_name):
949
    """Returns the name of the file containing the keymap for a given instance
950

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

    
954
  @classmethod
955
  def _TryReadUidFile(cls, uid_file):
956
    """Try to read a uid file
957

958
    """
959
    if os.path.exists(uid_file):
960
      try:
961
        uid = int(utils.ReadOneLineFile(uid_file))
962
        return uid
963
      except EnvironmentError:
964
        logging.warning("Can't read uid file", exc_info=True)
965
      except (TypeError, ValueError):
966
        logging.warning("Can't parse uid file contents", exc_info=True)
967
    return None
968

    
969
  @classmethod
970
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
971
    """Removes an instance's rutime sockets/files/dirs.
972

973
    """
974
    utils.RemoveFile(pidfile)
975
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
976
    utils.RemoveFile(cls._InstanceSerial(instance_name))
977
    utils.RemoveFile(cls._InstanceQmpMonitor(instance_name))
978
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
979
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
980
    uid_file = cls._InstanceUidFile(instance_name)
981
    uid = cls._TryReadUidFile(uid_file)
982
    utils.RemoveFile(uid_file)
983
    if uid is not None:
984
      uidpool.ReleaseUid(uid)
985
    try:
986
      shutil.rmtree(cls._InstanceNICDir(instance_name))
987
    except OSError, err:
988
      if err.errno != errno.ENOENT:
989
        raise
990
    try:
991
      chroot_dir = cls._InstanceChrootDir(instance_name)
992
      utils.RemoveDir(chroot_dir)
993
    except OSError, err:
994
      if err.errno == errno.ENOTEMPTY:
995
        # The chroot directory is expected to be empty, but it isn't.
996
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
997
                                          prefix="%s-%s-" %
998
                                          (instance_name,
999
                                           utils.TimestampForFilename()))
1000
        logging.warning("The chroot directory of instance %s can not be"
1001
                        " removed as it is not empty. Moving it to the"
1002
                        " quarantine instead. Please investigate the"
1003
                        " contents (%s) and clean up manually",
1004
                        instance_name, new_chroot_dir)
1005
        utils.RenameFile(chroot_dir, new_chroot_dir)
1006
      else:
1007
        raise
1008

    
1009
  @staticmethod
1010
  def _ConfigureNIC(instance, seq, nic, tap):
1011
    """Run the network configuration script for a specified NIC
1012

1013
    @param instance: instance we're acting on
1014
    @type instance: instance object
1015
    @param seq: nic sequence number
1016
    @type seq: int
1017
    @param nic: nic we're acting on
1018
    @type nic: nic object
1019
    @param tap: the host's tap interface this NIC corresponds to
1020
    @type tap: str
1021

1022
    """
1023
    env = {
1024
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1025
      "INSTANCE": instance.name,
1026
      "MAC": nic.mac,
1027
      "MODE": nic.nicparams[constants.NIC_MODE],
1028
      "INTERFACE": tap,
1029
      "INTERFACE_INDEX": str(seq),
1030
      "INTERFACE_UUID": nic.uuid,
1031
      "TAGS": " ".join(instance.GetTags()),
1032
    }
1033

    
1034
    if nic.ip:
1035
      env["IP"] = nic.ip
1036

    
1037
    if nic.name:
1038
      env["INTERFACE_NAME"] = nic.name
1039

    
1040
    if nic.nicparams[constants.NIC_LINK]:
1041
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1042

    
1043
    if nic.network:
1044
      n = objects.Network.FromDict(nic.netinfo)
1045
      env.update(n.HooksDict())
1046

    
1047
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1048
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1049

    
1050
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1051
    if result.failed:
1052
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1053
                                   " network configuration script output: %s" %
1054
                                   (tap, result.fail_reason, result.output))
1055

    
1056
  @staticmethod
1057
  def _VerifyAffinityPackage():
1058
    if affinity is None:
1059
      raise errors.HypervisorError("affinity Python package not"
1060
                                   " found; cannot use CPU pinning under KVM")
1061

    
1062
  @staticmethod
1063
  def _BuildAffinityCpuMask(cpu_list):
1064
    """Create a CPU mask suitable for sched_setaffinity from a list of
1065
    CPUs.
1066

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

1070
    @type cpu_list: list of int
1071
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1072
    @rtype: int
1073
    @return: a bit mask of CPU affinities
1074

1075
    """
1076
    if cpu_list == constants.CPU_PINNING_OFF:
1077
      return constants.CPU_PINNING_ALL_KVM
1078
    else:
1079
      return sum(2 ** cpu for cpu in cpu_list)
1080

    
1081
  @classmethod
1082
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1083
    """Change CPU affinity for running VM according to given CPU mask.
1084

1085
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1086
    @type cpu_mask: string
1087
    @param process_id: process ID of KVM process. Used to pin entire VM
1088
                       to physical CPUs.
1089
    @type process_id: int
1090
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1091
    @type thread_dict: dict int:int
1092

1093
    """
1094
    # Convert the string CPU mask to a list of list of int's
1095
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1096

    
1097
    if len(cpu_list) == 1:
1098
      all_cpu_mapping = cpu_list[0]
1099
      if all_cpu_mapping == constants.CPU_PINNING_OFF:
1100
        # If CPU pinning has 1 entry that's "all", then do nothing
1101
        pass
1102
      else:
1103
        # If CPU pinning has one non-all entry, map the entire VM to
1104
        # one set of physical CPUs
1105
        cls._VerifyAffinityPackage()
1106
        affinity.set_process_affinity_mask(
1107
          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
1108
    else:
1109
      # The number of vCPUs mapped should match the number of vCPUs
1110
      # reported by KVM. This was already verified earlier, so
1111
      # here only as a sanity check.
1112
      assert len(thread_dict) == len(cpu_list)
1113
      cls._VerifyAffinityPackage()
1114

    
1115
      # For each vCPU, map it to the proper list of physical CPUs
1116
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1117
        affinity.set_process_affinity_mask(thread_dict[i],
1118
                                           cls._BuildAffinityCpuMask(vcpu))
1119

    
1120
  def _GetVcpuThreadIds(self, instance_name):
1121
    """Get a mapping of vCPU no. to thread IDs for the instance
1122

1123
    @type instance_name: string
1124
    @param instance_name: instance in question
1125
    @rtype: dictionary of int:int
1126
    @return: a dictionary mapping vCPU numbers to thread IDs
1127

1128
    """
1129
    result = {}
1130
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1131
    for line in output.stdout.splitlines():
1132
      match = self._CPU_INFO_RE.search(line)
1133
      if not match:
1134
        continue
1135
      grp = map(int, match.groups())
1136
      result[grp[0]] = grp[1]
1137

    
1138
    return result
1139

    
1140
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1141
    """Complete CPU pinning.
1142

1143
    @type instance_name: string
1144
    @param instance_name: name of instance
1145
    @type cpu_mask: string
1146
    @param cpu_mask: CPU pinning mask as entered by user
1147

1148
    """
1149
    # Get KVM process ID, to be used if need to pin entire VM
1150
    _, pid, _ = self._InstancePidAlive(instance_name)
1151
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1152
    thread_dict = self._GetVcpuThreadIds(instance_name)
1153
    # Run CPU pinning, based on configured mask
1154
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1155

    
1156
  def ListInstances(self):
1157
    """Get the list of running instances.
1158

1159
    We can do this by listing our live instances directory and
1160
    checking whether the associated kvm process is still alive.
1161

1162
    """
1163
    result = []
1164
    for name in os.listdir(self._PIDS_DIR):
1165
      if self._InstancePidAlive(name)[2]:
1166
        result.append(name)
1167
    return result
1168

    
1169
  def GetInstanceInfo(self, instance_name):
1170
    """Get instance properties.
1171

1172
    @type instance_name: string
1173
    @param instance_name: the instance name
1174
    @rtype: tuple of strings
1175
    @return: (name, id, memory, vcpus, stat, times)
1176

1177
    """
1178
    _, pid, alive = self._InstancePidAlive(instance_name)
1179
    if not alive:
1180
      return None
1181

    
1182
    _, memory, vcpus = self._InstancePidInfo(pid)
1183
    istat = "---b-"
1184
    times = "0"
1185

    
1186
    try:
1187
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1188
      qmp.connect()
1189
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1190
      # Will fail if ballooning is not enabled, but we can then just resort to
1191
      # the value above.
1192
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1193
      memory = mem_bytes / 1048576
1194
    except errors.HypervisorError:
1195
      pass
1196

    
1197
    return (instance_name, pid, memory, vcpus, istat, times)
1198

    
1199
  def GetAllInstancesInfo(self):
1200
    """Get properties of all instances.
1201

1202
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1203

1204
    """
1205
    data = []
1206
    for name in os.listdir(self._PIDS_DIR):
1207
      try:
1208
        info = self.GetInstanceInfo(name)
1209
      except errors.HypervisorError:
1210
        # Ignore exceptions due to instances being shut down
1211
        continue
1212
      if info:
1213
        data.append(info)
1214
    return data
1215

    
1216
  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
1217
                                      kvmhelp, devlist):
1218
    """Generate KVM options regarding instance's block devices.
1219

1220
    @type instance: L{objects.Instance}
1221
    @param instance: the instance object
1222
    @type kvm_disks: list of tuples
1223
    @param kvm_disks: list of tuples [(disk, link_name)..]
1224
    @type kvmhelp: string
1225
    @param kvmhelp: output of kvm --help
1226
    @type devlist: string
1227
    @param devlist: output of kvm -device ?
1228
    @rtype: list
1229
    @return: list of command line options eventually used by kvm executable
1230

1231
    """
1232
    hvp = instance.hvparams
1233
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1234
    if kernel_path:
1235
      boot_disk = False
1236
    else:
1237
      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1238

    
1239
    # whether this is an older KVM version that uses the boot=on flag
1240
    # on devices
1241
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1242

    
1243
    dev_opts = []
1244
    device_driver = None
1245
    disk_type = hvp[constants.HV_DISK_TYPE]
1246
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
1247
      if_val = ",if=%s" % self._VIRTIO
1248
      try:
1249
        if self._VIRTIO_BLK_RE.search(devlist):
1250
          if_val = ",if=none"
1251
          # will be passed in -device option as driver
1252
          device_driver = self._VIRTIO_BLK_PCI
1253
      except errors.HypervisorError, _:
1254
        pass
1255
    else:
1256
      if_val = ",if=%s" % disk_type
1257
    # Cache mode
1258
    disk_cache = hvp[constants.HV_DISK_CACHE]
1259
    if instance.disk_template in constants.DTS_EXT_MIRROR:
1260
      if disk_cache != "none":
1261
        # TODO: make this a hard error, instead of a silent overwrite
1262
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
1263
                        " to prevent shared storage corruption on migration",
1264
                        disk_cache)
1265
      cache_val = ",cache=none"
1266
    elif disk_cache != constants.HT_CACHE_DEFAULT:
1267
      cache_val = ",cache=%s" % disk_cache
1268
    else:
1269
      cache_val = ""
1270
    for idx, (cfdev, link_name) in enumerate(kvm_disks):
1271
      if cfdev.mode != constants.DISK_RDWR:
1272
        raise errors.HypervisorError("Instance has read-only disks which"
1273
                                     " are not supported by KVM")
1274
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
1275
      boot_val = ""
1276
      if boot_disk:
1277
        dev_opts.extend(["-boot", "c"])
1278
        boot_disk = False
1279
        if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
1280
          boot_val = ",boot=on"
1281

    
1282
      # For ext we allow overriding disk_cache hypervisor params per disk
1283
      disk_cache = cfdev.params.get("cache", None)
1284
      if disk_cache:
1285
        cache_val = ",cache=%s" % disk_cache
1286
      drive_val = "file=%s,format=raw%s%s%s" % \
1287
                  (link_name, if_val, boot_val, cache_val)
1288

    
1289
      if device_driver:
1290
        # kvm_disks are the 4th entry of runtime file that did not exist in
1291
        # the past. That means that cfdev should always have pci slot and
1292
        # _GenerateDeviceKVMId() will not raise a exception.
1293
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK,
1294
                                         cfdev, idx)
1295
        drive_val += (",id=%s" % kvm_devid)
1296
        if cfdev.pci:
1297
          drive_val += (",bus=0,unit=%d" % cfdev.pci)
1298
        dev_val = ("%s,drive=%s,id=%s" %
1299
                   (device_driver, kvm_devid, kvm_devid))
1300
        if cfdev.pci:
1301
          dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1302
        dev_opts.extend(["-device", dev_val])
1303

    
1304
      # TODO: export disk geometry in IDISK_PARAMS
1305
      heads = cfdev.params.get('heads', None)
1306
      secs = cfdev.params.get('secs', None)
1307
      if heads and secs:
1308
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1309
        cyls = nr_sectors / (int(heads) * int(secs))
1310
        if cyls > 16383:
1311
          cyls = 16383
1312
        elif cyls < 2:
1313
          cyls = 2
1314
        if cyls and heads and secs:
1315
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1316
                        (cyls, int(heads), int(secs)))
1317

    
1318
      dev_opts.extend(["-drive", drive_val])
1319

    
1320
    return dev_opts
1321

    
1322
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1323
                          kvmhelp):
1324
    """Generate KVM information to start an instance.
1325

1326
    @type kvmhelp: string
1327
    @param kvmhelp: output of kvm --help
1328
    @attention: this function must not have any side-effects; for
1329
        example, it must not write to the filesystem, or read values
1330
        from the current system the are expected to differ between
1331
        nodes, since it is only run once at instance startup;
1332
        actions/kvm arguments that can vary between systems should be
1333
        done in L{_ExecuteKVMRuntime}
1334

1335
    """
1336
    # pylint: disable=R0912,R0914,R0915
1337
    hvp = instance.hvparams
1338
    self.ValidateParameters(hvp)
1339

    
1340
    pidfile = self._InstancePidFile(instance.name)
1341
    kvm = hvp[constants.HV_KVM_PATH]
1342
    kvm_cmd = [kvm]
1343
    # used just by the vnc server, if enabled
1344
    kvm_cmd.extend(["-name", instance.name])
1345
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1346

    
1347
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1348
    if hvp[constants.HV_CPU_CORES]:
1349
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1350
    if hvp[constants.HV_CPU_THREADS]:
1351
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1352
    if hvp[constants.HV_CPU_SOCKETS]:
1353
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1354

    
1355
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1356

    
1357
    kvm_cmd.extend(["-pidfile", pidfile])
1358
    kvm_cmd.extend(["-balloon", "virtio"])
1359
    kvm_cmd.extend(["-daemonize"])
1360
    if not instance.hvparams[constants.HV_ACPI]:
1361
      kvm_cmd.extend(["-no-acpi"])
1362
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1363
        constants.INSTANCE_REBOOT_EXIT:
1364
      kvm_cmd.extend(["-no-reboot"])
1365

    
1366
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1367
    if not mversion:
1368
      mversion = self._GetDefaultMachineVersion(kvm)
1369
    if self._MACHINE_RE.search(kvmhelp):
1370
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1371
      # extra hypervisor parameters. We should also investigate whether and how
1372
      # shadow_mem should be considered for the resource model.
1373
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1374
        specprop = ",accel=kvm"
1375
      else:
1376
        specprop = ""
1377
      machinespec = "%s%s" % (mversion, specprop)
1378
      kvm_cmd.extend(["-machine", machinespec])
1379
    else:
1380
      kvm_cmd.extend(["-M", mversion])
1381
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1382
          self._ENABLE_KVM_RE.search(kvmhelp)):
1383
        kvm_cmd.extend(["-enable-kvm"])
1384
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1385
            self._DISABLE_KVM_RE.search(kvmhelp)):
1386
        kvm_cmd.extend(["-disable-kvm"])
1387

    
1388
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1389
    if kernel_path:
1390
      boot_cdrom = boot_floppy = boot_network = False
1391
    else:
1392
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1393
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1394
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1395

    
1396
    if startup_paused:
1397
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1398

    
1399
    if boot_network:
1400
      kvm_cmd.extend(["-boot", "n"])
1401

    
1402
    # whether this is an older KVM version that uses the boot=on flag
1403
    # on devices
1404
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1405

    
1406
    disk_type = hvp[constants.HV_DISK_TYPE]
1407

    
1408
    #Now we can specify a different device type for CDROM devices.
1409
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1410
    if not cdrom_disk_type:
1411
      cdrom_disk_type = disk_type
1412

    
1413
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1414
    if iso_image:
1415
      options = ",format=raw,media=cdrom"
1416
      # set cdrom 'if' type
1417
      if boot_cdrom:
1418
        actual_cdrom_type = constants.HT_DISK_IDE
1419
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1420
        actual_cdrom_type = "virtio"
1421
      else:
1422
        actual_cdrom_type = cdrom_disk_type
1423
      if_val = ",if=%s" % actual_cdrom_type
1424
      # set boot flag, if needed
1425
      boot_val = ""
1426
      if boot_cdrom:
1427
        kvm_cmd.extend(["-boot", "d"])
1428
        if needs_boot_flag:
1429
          boot_val = ",boot=on"
1430
      # and finally build the entire '-drive' value
1431
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1432
      kvm_cmd.extend(["-drive", drive_val])
1433

    
1434
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1435
    if iso_image2:
1436
      options = ",format=raw,media=cdrom"
1437
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1438
        if_val = ",if=virtio"
1439
      else:
1440
        if_val = ",if=%s" % cdrom_disk_type
1441
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1442
      kvm_cmd.extend(["-drive", drive_val])
1443

    
1444
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1445
    if floppy_image:
1446
      options = ",format=raw,media=disk"
1447
      if boot_floppy:
1448
        kvm_cmd.extend(["-boot", "a"])
1449
        options = "%s,boot=on" % options
1450
      if_val = ",if=floppy"
1451
      options = "%s%s" % (options, if_val)
1452
      drive_val = "file=%s%s" % (floppy_image, options)
1453
      kvm_cmd.extend(["-drive", drive_val])
1454

    
1455
    if kernel_path:
1456
      kvm_cmd.extend(["-kernel", kernel_path])
1457
      initrd_path = hvp[constants.HV_INITRD_PATH]
1458
      if initrd_path:
1459
        kvm_cmd.extend(["-initrd", initrd_path])
1460
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1461
                     hvp[constants.HV_KERNEL_ARGS]]
1462
      if hvp[constants.HV_SERIAL_CONSOLE]:
1463
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1464
        root_append.append("console=ttyS0,%s" % serial_speed)
1465
      kvm_cmd.extend(["-append", " ".join(root_append)])
1466

    
1467
    mem_path = hvp[constants.HV_MEM_PATH]
1468
    if mem_path:
1469
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1470

    
1471
    monitor_dev = ("unix:%s,server,nowait" %
1472
                   self._InstanceMonitor(instance.name))
1473
    kvm_cmd.extend(["-monitor", monitor_dev])
1474
    if hvp[constants.HV_SERIAL_CONSOLE]:
1475
      serial_dev = ("unix:%s,server,nowait" %
1476
                    self._InstanceSerial(instance.name))
1477
      kvm_cmd.extend(["-serial", serial_dev])
1478
    else:
1479
      kvm_cmd.extend(["-serial", "none"])
1480

    
1481
    mouse_type = hvp[constants.HV_USB_MOUSE]
1482
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1483
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1484
    spice_ip_version = None
1485

    
1486
    kvm_cmd.extend(["-usb"])
1487

    
1488
    if mouse_type:
1489
      kvm_cmd.extend(["-usbdevice", mouse_type])
1490
    elif vnc_bind_address:
1491
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1492

    
1493
    if vnc_bind_address:
1494
      if netutils.IP4Address.IsValid(vnc_bind_address):
1495
        if instance.network_port > constants.VNC_BASE_PORT:
1496
          display = instance.network_port - constants.VNC_BASE_PORT
1497
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1498
            vnc_arg = ":%d" % (display)
1499
          else:
1500
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1501
        else:
1502
          logging.error("Network port is not a valid VNC display (%d < %d),"
1503
                        " not starting VNC",
1504
                        instance.network_port, constants.VNC_BASE_PORT)
1505
          vnc_arg = "none"
1506

    
1507
        # Only allow tls and other option when not binding to a file, for now.
1508
        # kvm/qemu gets confused otherwise about the filename to use.
1509
        vnc_append = ""
1510
        if hvp[constants.HV_VNC_TLS]:
1511
          vnc_append = "%s,tls" % vnc_append
1512
          if hvp[constants.HV_VNC_X509_VERIFY]:
1513
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1514
                                               hvp[constants.HV_VNC_X509])
1515
          elif hvp[constants.HV_VNC_X509]:
1516
            vnc_append = "%s,x509=%s" % (vnc_append,
1517
                                         hvp[constants.HV_VNC_X509])
1518
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1519
          vnc_append = "%s,password" % vnc_append
1520

    
1521
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1522

    
1523
      else:
1524
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1525

    
1526
      kvm_cmd.extend(["-vnc", vnc_arg])
1527
    elif spice_bind:
1528
      # FIXME: this is wrong here; the iface ip address differs
1529
      # between systems, so it should be done in _ExecuteKVMRuntime
1530
      if netutils.IsValidInterface(spice_bind):
1531
        # The user specified a network interface, we have to figure out the IP
1532
        # address.
1533
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1534
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1535

    
1536
        # if the user specified an IP version and the interface does not
1537
        # have that kind of IP addresses, throw an exception
1538
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1539
          if not addresses[spice_ip_version]:
1540
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1541
                                         " for %s" % (spice_ip_version,
1542
                                                      spice_bind))
1543

    
1544
        # the user did not specify an IP version, we have to figure it out
1545
        elif (addresses[constants.IP4_VERSION] and
1546
              addresses[constants.IP6_VERSION]):
1547
          # we have both ipv4 and ipv6, let's use the cluster default IP
1548
          # version
1549
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1550
          spice_ip_version = \
1551
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1552
        elif addresses[constants.IP4_VERSION]:
1553
          spice_ip_version = constants.IP4_VERSION
1554
        elif addresses[constants.IP6_VERSION]:
1555
          spice_ip_version = constants.IP6_VERSION
1556
        else:
1557
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1558
                                       " for %s" % (spice_bind))
1559

    
1560
        spice_address = addresses[spice_ip_version][0]
1561

    
1562
      else:
1563
        # spice_bind is known to be a valid IP address, because
1564
        # ValidateParameters checked it.
1565
        spice_address = spice_bind
1566

    
1567
      spice_arg = "addr=%s" % spice_address
1568
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1569
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1570
                     (spice_arg, instance.network_port,
1571
                      pathutils.SPICE_CACERT_FILE))
1572
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1573
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1574
                      pathutils.SPICE_CERT_FILE))
1575
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1576
        if tls_ciphers:
1577
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1578
      else:
1579
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1580

    
1581
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1582
        spice_arg = "%s,disable-ticketing" % spice_arg
1583

    
1584
      if spice_ip_version:
1585
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1586

    
1587
      # Image compression options
1588
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1589
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1590
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1591
      if img_lossless:
1592
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1593
      if img_jpeg:
1594
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1595
      if img_zlib_glz:
1596
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1597

    
1598
      # Video stream detection
1599
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1600
      if video_streaming:
1601
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1602

    
1603
      # Audio compression, by default in qemu-kvm it is on
1604
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1605
        spice_arg = "%s,playback-compression=off" % spice_arg
1606
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1607
        spice_arg = "%s,agent-mouse=off" % spice_arg
1608
      else:
1609
        # Enable the spice agent communication channel between the host and the
1610
        # agent.
1611
        kvm_cmd.extend(["-device", "virtio-serial-pci"])
1612
        kvm_cmd.extend([
1613
          "-device",
1614
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1615
          ])
1616
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1617

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

    
1621
    else:
1622
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1623
      # also works in earlier versions though (tested with 1.1 and 1.3)
1624
      if self._DISPLAY_RE.search(kvmhelp):
1625
        kvm_cmd.extend(["-display", "none"])
1626
      else:
1627
        kvm_cmd.extend(["-nographic"])
1628

    
1629
    if hvp[constants.HV_USE_LOCALTIME]:
1630
      kvm_cmd.extend(["-localtime"])
1631

    
1632
    if hvp[constants.HV_KVM_USE_CHROOT]:
1633
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1634

    
1635
    # Add qemu-KVM -cpu param
1636
    if hvp[constants.HV_CPU_TYPE]:
1637
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1638

    
1639
    # As requested by music lovers
1640
    if hvp[constants.HV_SOUNDHW]:
1641
      kvm_cmd.extend(["-soundhw", hvp[constants.HV_SOUNDHW]])
1642

    
1643
    # Pass a -vga option if requested, or if spice is used, for backwards
1644
    # compatibility.
1645
    if hvp[constants.HV_VGA]:
1646
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1647
    elif spice_bind:
1648
      kvm_cmd.extend(["-vga", "qxl"])
1649

    
1650
    # Various types of usb devices, comma separated
1651
    if hvp[constants.HV_USB_DEVICES]:
1652
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1653
        kvm_cmd.extend(["-usbdevice", dev])
1654

    
1655
    if hvp[constants.HV_KVM_EXTRA]:
1656
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1657

    
1658
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1659
    kvm_disks = []
1660
    for disk, link_name in block_devices:
1661
      _UpdatePCISlots(disk, pci_reservations)
1662
      kvm_disks.append((disk, link_name))
1663

    
1664
    kvm_nics = []
1665
    for nic in instance.nics:
1666
      _UpdatePCISlots(nic, pci_reservations)
1667
      kvm_nics.append(nic)
1668

    
1669
    hvparams = hvp
1670

    
1671
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1672

    
1673
  def _WriteKVMRuntime(self, instance_name, data):
1674
    """Write an instance's KVM runtime
1675

1676
    """
1677
    try:
1678
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1679
                      data=data)
1680
    except EnvironmentError, err:
1681
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1682

    
1683
  def _ReadKVMRuntime(self, instance_name):
1684
    """Read an instance's KVM runtime
1685

1686
    """
1687
    try:
1688
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
1689
    except EnvironmentError, err:
1690
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1691
    return file_content
1692

    
1693
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1694
    """Save an instance's KVM runtime
1695

1696
    """
1697
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1698

    
1699
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1700
    serialized_disks = [(blk.ToDict(), link)
1701
                            for blk, link in kvm_disks]
1702
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1703
                                      serialized_disks))
1704

    
1705
    self._WriteKVMRuntime(instance.name, serialized_form)
1706

    
1707
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
1708
    """Load an instance's KVM runtime
1709

1710
    """
1711
    if not serialized_runtime:
1712
      serialized_runtime = self._ReadKVMRuntime(instance.name)
1713

    
1714
    return _AnalyzeSerializedRuntime(serialized_runtime)
1715

    
1716
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1717
    """Run the KVM cmd and check for errors
1718

1719
    @type name: string
1720
    @param name: instance name
1721
    @type kvm_cmd: list of strings
1722
    @param kvm_cmd: runcmd input for kvm
1723
    @type tap_fds: list of int
1724
    @param tap_fds: fds of tap devices opened by Ganeti
1725

1726
    """
1727
    try:
1728
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1729
    finally:
1730
      for fd in tap_fds:
1731
        utils_wrapper.CloseFdNoError(fd)
1732

    
1733
    if result.failed:
1734
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1735
                                   (name, result.fail_reason, result.output))
1736
    if not self._InstancePidAlive(name)[2]:
1737
      raise errors.HypervisorError("Failed to start instance %s" % name)
1738

    
1739
  # too many local variables
1740
  # pylint: disable=R0914
1741
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1742
    """Execute a KVM cmd, after completing it with some last minute data.
1743

1744
    @type incoming: tuple of strings
1745
    @param incoming: (target_host_ip, port)
1746
    @type kvmhelp: string
1747
    @param kvmhelp: output of kvm --help
1748

1749
    """
1750
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1751
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1752
    #    have changed since the instance started; only use them if the change
1753
    #    won't affect the inside of the instance (which hasn't been rebooted).
1754
    #  - up_hvp contains the parameters as they were when the instance was
1755
    #    started, plus any new parameter which has been added between ganeti
1756
    #    versions: it is paramount that those default to a value which won't
1757
    #    affect the inside of the instance as well.
1758
    conf_hvp = instance.hvparams
1759
    name = instance.name
1760
    self._CheckDown(name)
1761

    
1762
    temp_files = []
1763

    
1764
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1765
    # the first element of kvm_cmd is always the path to the kvm binary
1766
    kvm_path = kvm_cmd[0]
1767
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1768

    
1769
    # We know it's safe to run as a different user upon migration, so we'll use
1770
    # the latest conf, from conf_hvp.
1771
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1772
    if security_model == constants.HT_SM_USER:
1773
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1774

    
1775
    keymap = conf_hvp[constants.HV_KEYMAP]
1776
    if keymap:
1777
      keymap_path = self._InstanceKeymapFile(name)
1778
      # If a keymap file is specified, KVM won't use its internal defaults. By
1779
      # first including the "en-us" layout, an error on loading the actual
1780
      # layout (e.g. because it can't be found) won't lead to a non-functional
1781
      # keyboard. A keyboard with incorrect keys is still better than none.
1782
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1783
      kvm_cmd.extend(["-k", keymap_path])
1784

    
1785
    # We have reasons to believe changing something like the nic driver/type
1786
    # upon migration won't exactly fly with the instance kernel, so for nic
1787
    # related parameters we'll use up_hvp
1788
    tapfds = []
1789
    taps = []
1790
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1791

    
1792
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1793
                                                     kvm_disks,
1794
                                                     kvmhelp,
1795
                                                     devlist)
1796
    kvm_cmd.extend(bdev_opts)
1797

    
1798
    if not kvm_nics:
1799
      kvm_cmd.extend(["-net", "none"])
1800
    else:
1801
      vnet_hdr = False
1802
      tap_extra = ""
1803
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1804
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1805
        nic_model = self._VIRTIO
1806
        try:
1807
          if self._VIRTIO_NET_RE.search(devlist):
1808
            nic_model = self._VIRTIO_NET_PCI
1809
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1810
        except errors.HypervisorError, _:
1811
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1812
          # have new virtio syntax either.
1813
          pass
1814

    
1815
        if up_hvp[constants.HV_VHOST_NET]:
1816
          # check for vhost_net support
1817
          if self._VHOST_RE.search(kvmhelp):
1818
            tap_extra = ",vhost=on"
1819
          else:
1820
            raise errors.HypervisorError("vhost_net is configured"
1821
                                         " but it is not available")
1822
      else:
1823
        nic_model = nic_type
1824

    
1825
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1826

    
1827
      for nic_seq, nic in enumerate(kvm_nics):
1828
        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1829
        tapfds.append(tapfd)
1830
        taps.append(tapname)
1831
        if kvm_supports_netdev:
1832
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1833
          try:
1834
            # kvm_nics already exist in old runtime files and thus there might
1835
            # be some entries without pci slot (therefore try: except:)
1836
            kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1837
            netdev = kvm_devid
1838
            nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1839
          except errors.HotplugError:
1840
            netdev = "netdev%d" % nic_seq
1841
          nic_val += (",netdev=%s" % netdev)
1842
          tap_val = ("type=tap,id=%s,fd=%d%s" %
1843
                     (netdev, tapfd, tap_extra))
1844
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1845
        else:
1846
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1847
                                                         nic.mac, nic_model)
1848
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1849
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1850

    
1851
    if incoming:
1852
      target, port = incoming
1853
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1854

    
1855
    # Changing the vnc password doesn't bother the guest that much. At most it
1856
    # will surprise people who connect to it. Whether positively or negatively
1857
    # it's debatable.
1858
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1859
    vnc_pwd = None
1860
    if vnc_pwd_file:
1861
      try:
1862
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
1863
      except EnvironmentError, err:
1864
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
1865
                                     % (vnc_pwd_file, err))
1866

    
1867
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
1868
      utils.EnsureDirs([(self._InstanceChrootDir(name),
1869
                         constants.SECURE_DIR_MODE)])
1870

    
1871
    # Automatically enable QMP if version is >= 0.14
1872
    if self._QMP_RE.search(kvmhelp):
1873
      logging.debug("Enabling QMP")
1874
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
1875
                      self._InstanceQmpMonitor(instance.name)])
1876

    
1877
    # Configure the network now for starting instances and bridged interfaces,
1878
    # during FinalizeMigration for incoming instances' routed interfaces
1879
    for nic_seq, nic in enumerate(kvm_nics):
1880
      if (incoming and
1881
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
1882
        continue
1883
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
1884

    
1885
    # CPU affinity requires kvm to start paused, so we set this flag if the
1886
    # instance is not already paused and if we are not going to accept a
1887
    # migrating instance. In the latter case, pausing is not needed.
1888
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
1889
    if start_kvm_paused:
1890
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1891

    
1892
    # Note: CPU pinning is using up_hvp since changes take effect
1893
    # during instance startup anyway, and to avoid problems when soft
1894
    # rebooting the instance.
1895
    cpu_pinning = False
1896
    if up_hvp.get(constants.HV_CPU_MASK, None):
1897
      cpu_pinning = True
1898

    
1899
    if security_model == constants.HT_SM_POOL:
1900
      ss = ssconf.SimpleStore()
1901
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
1902
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
1903
      uid = uidpool.RequestUnusedUid(all_uids)
1904
      try:
1905
        username = pwd.getpwuid(uid.GetUid()).pw_name
1906
        kvm_cmd.extend(["-runas", username])
1907
        self._RunKVMCmd(name, kvm_cmd, tapfds)
1908
      except:
1909
        uidpool.ReleaseUid(uid)
1910
        raise
1911
      else:
1912
        uid.Unlock()
1913
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
1914
    else:
1915
      self._RunKVMCmd(name, kvm_cmd, tapfds)
1916

    
1917
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
1918
                     constants.RUN_DIRS_MODE)])
1919
    for nic_seq, tap in enumerate(taps):
1920
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
1921
                      data=tap)
1922

    
1923
    if vnc_pwd:
1924
      change_cmd = "change vnc password %s" % vnc_pwd
1925
      self._CallMonitorCommand(instance.name, change_cmd)
1926

    
1927
    # Setting SPICE password. We are not vulnerable to malicious passwordless
1928
    # connection attempts because SPICE by default does not allow connections
1929
    # if neither a password nor the "disable_ticketing" options are specified.
1930
    # As soon as we send the password via QMP, that password is a valid ticket
1931
    # for connection.
1932
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
1933
    if spice_password_file:
1934
      spice_pwd = ""
1935
      try:
1936
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
1937
      except EnvironmentError, err:
1938
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
1939
                                     % (spice_password_file, err))
1940

    
1941
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
1942
      qmp.connect()
1943
      arguments = {
1944
          "protocol": "spice",
1945
          "password": spice_pwd,
1946
      }
1947
      qmp.Execute("set_password", arguments)
1948

    
1949
    for filename in temp_files:
1950
      utils.RemoveFile(filename)
1951

    
1952
    # If requested, set CPU affinity and resume instance execution
1953
    if cpu_pinning:
1954
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
1955

    
1956
    start_memory = self._InstanceStartupMemory(instance)
1957
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
1958
      self.BalloonInstanceMemory(instance, start_memory)
1959

    
1960
    if start_kvm_paused:
1961
      # To control CPU pinning, ballooning, and vnc/spice passwords
1962
      # the VM was started in a frozen state. If freezing was not
1963
      # explicitly requested resume the vm status.
1964
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
1965

    
1966
  def StartInstance(self, instance, block_devices, startup_paused):
1967
    """Start an instance.
1968

1969
    """
1970
    self._CheckDown(instance.name)
1971
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
1972
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
1973
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
1974
                                           startup_paused, kvmhelp)
1975
    self._SaveKVMRuntime(instance, kvm_runtime)
1976
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
1977

    
1978
  def _CallMonitorCommand(self, instance_name, command):
1979
    """Invoke a command on the instance monitor.
1980

1981
    """
1982
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
1983
    # version. The monitor protocol is designed for human consumption, whereas
1984
    # QMP is made for programmatic usage. In the worst case QMP can also
1985
    # execute monitor commands. As it is, all calls to socat take at least
1986
    # 500ms and likely more: socat can't detect the end of the reply and waits
1987
    # for 500ms of no data received before exiting (500 ms is the default for
1988
    # the "-t" parameter).
1989
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1990
             (utils.ShellQuote(command),
1991
              constants.SOCAT_PATH,
1992
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1993
    result = utils.RunCmd(socat)
1994
    if result.failed:
1995
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
1996
             " output: %s" %
1997
             (command, instance_name, result.fail_reason, result.output))
1998
      raise errors.HypervisorError(msg)
1999

    
2000
    return result
2001

    
2002
  def _GetFreePCISlot(self, instance, dev):
2003
    """Get the first available pci slot of a runnung instance.
2004

2005
    """
2006
    slots = bitarray(32)
2007
    slots.setall(False) # pylint: disable=E1101
2008
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2009
    for line in output.stdout.splitlines():
2010
      match = self._INFO_PCI_RE.search(line)
2011
      if match:
2012
        slot = int(match.group(1))
2013
        slots[slot] = True
2014

    
2015
    [free] = slots.search(_AVAILABLE_PCI_SLOT, 1) # pylint: disable=E1101
2016
    if not free:
2017
      raise errors.HypervisorError("All PCI slots occupied")
2018

    
2019
    dev.pci = int(free)
2020

    
2021
  def VerifyHotplugSupport(self, instance, action, dev_type):
2022
    """Verifies that hotplug is supported.
2023

2024
    Hotplug is *not* supported in case of:
2025
     - security models and chroot (disk hotplug)
2026
     - fdsend module is missing (nic hot-add)
2027

2028
    @raise errors.HypervisorError: in one of the previous cases
2029

2030
    """
2031
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2032
      hvp = instance.hvparams
2033
      security_model = hvp[constants.HV_SECURITY_MODEL]
2034
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2035
      if use_chroot:
2036
        raise errors.HotplugError("Disk hotplug is not supported"
2037
                                  " in case of chroot.")
2038
      if security_model != constants.HT_SM_NONE:
2039
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2040
                                  " security models are used.")
2041

    
2042
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2043
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2044
      raise errors.HotplugError("Cannot hot-add NIC."
2045
                                " fdsend python module is missing.")
2046

    
2047
  def HotplugSupported(self, instance):
2048
    """Checks if hotplug is generally supported.
2049

2050
    Hotplug is *not* supported in case of:
2051
     - qemu versions < 1.0
2052
     - for stopped instances
2053

2054
    @raise errors.HypervisorError: in one of the previous cases
2055

2056
    """
2057
    try:
2058
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2059
    except errors.HypervisorError:
2060
      raise errors.HotplugError("Instance is probably down")
2061

    
2062
    # TODO: search for netdev_add, drive_add, device_add.....
2063
    match = self._INFO_VERSION_RE.search(output.stdout)
2064
    if not match:
2065
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2066

    
2067
    v_major, v_min, _, _ = match.groups()
2068
    if (int(v_major), int(v_min)) < (1, 0):
2069
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2070

    
2071
  def _CallHotplugCommand(self, name, cmd):
2072
    output = self._CallMonitorCommand(name, cmd)
2073
    # TODO: parse output and check if succeeded
2074
    for line in output.stdout.splitlines():
2075
      logging.info("%s", line)
2076

    
2077
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2078
    """ Helper method to hot-add a new device
2079

2080
    It gets free pci slot generates the device name and invokes the
2081
    device specific method.
2082

2083
    """
2084
    # in case of hot-mod this is given
2085
    if device.pci is None:
2086
      self._GetFreePCISlot(instance, device)
2087
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2088
    runtime = self._LoadKVMRuntime(instance)
2089
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2090
      command = "drive_add dummy file=%s,if=none,id=%s,format=raw\n" % \
2091
                 (extra, kvm_devid)
2092
      command += ("device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2093
                  (hex(device.pci), kvm_devid, kvm_devid))
2094
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2095
      (tap, fd) = _OpenTap()
2096
      self._ConfigureNIC(instance, seq, device, tap)
2097
      self._PassTapFd(instance, fd, device)
2098
      command = "netdev_add tap,id=%s,fd=%s\n" % (kvm_devid, kvm_devid)
2099
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2100
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2101
      command += "device_add %s" % args
2102
      utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
2103

    
2104
    self._CallHotplugCommand(instance.name, command)
2105
    # update relevant entries in runtime file
2106
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2107
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2108
    runtime[index].append(entry)
2109
    self._SaveKVMRuntime(instance, runtime)
2110

    
2111
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2112
    """ Helper method for hot-del device
2113

2114
    It gets device info from runtime file, generates the device name and
2115
    invokes the device specific method.
2116

2117
    """
2118
    runtime = self._LoadKVMRuntime(instance)
2119
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2120
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2121
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2122
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2123
      command = "device_del %s\n" % kvm_devid
2124
      command += "drive_del %s" % kvm_devid
2125
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2126
      command = "device_del %s\n" % kvm_devid
2127
      command += "netdev_del %s" % kvm_devid
2128
      utils.RemoveFile(self._InstanceNICFile(instance.name, seq))
2129
    self._CallHotplugCommand(instance.name, command)
2130
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2131
    runtime[index].remove(entry)
2132
    self._SaveKVMRuntime(instance, runtime)
2133

    
2134
    return kvm_device.pci
2135

    
2136
  def HotModDevice(self, instance, dev_type, device, _, seq):
2137
    """ Helper method for hot-mod device
2138

2139
    It gets device info from runtime file, generates the device name and
2140
    invokes the device specific method. Currently only NICs support hot-mod
2141

2142
    """
2143
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2144
      # putting it back in the same pci slot
2145
      device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2146
      # TODO: remove sleep when socat gets removed
2147
      time.sleep(2)
2148
      self.HotAddDevice(instance, dev_type, device, _, seq)
2149

    
2150
  def _PassTapFd(self, instance, fd, nic):
2151
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2152

2153
    """
2154
    # TODO: factor out code related to unix sockets.
2155
    #       squash common parts between monitor and qmp
2156
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2157
    command = "getfd %s\n" % kvm_devid
2158
    fds = [fd]
2159
    logging.info("%s", fds)
2160
    try:
2161
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2162
      monsock.connect()
2163
      fdsend.sendfds(monsock.sock, command, fds=fds)
2164
    finally:
2165
      monsock.close()
2166

    
2167
  @classmethod
2168
  def _ParseKVMVersion(cls, text):
2169
    """Parse the KVM version from the --help output.
2170

2171
    @type text: string
2172
    @param text: output of kvm --help
2173
    @return: (version, v_maj, v_min, v_rev)
2174
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2175

2176
    """
2177
    match = cls._VERSION_RE.search(text.splitlines()[0])
2178
    if not match:
2179
      raise errors.HypervisorError("Unable to get KVM version")
2180

    
2181
    v_all = match.group(0)
2182
    v_maj = int(match.group(1))
2183
    v_min = int(match.group(2))
2184
    if match.group(4):
2185
      v_rev = int(match.group(4))
2186
    else:
2187
      v_rev = 0
2188
    return (v_all, v_maj, v_min, v_rev)
2189

    
2190
  @classmethod
2191
  def _GetKVMOutput(cls, kvm_path, option):
2192
    """Return the output of a kvm invocation
2193

2194
    @type kvm_path: string
2195
    @param kvm_path: path to the kvm executable
2196
    @type option: a key of _KVMOPTS_CMDS
2197
    @param option: kvm option to fetch the output from
2198
    @return: output a supported kvm invocation
2199
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2200

2201
    """
2202
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2203

    
2204
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2205

    
2206
    result = utils.RunCmd([kvm_path] + optlist)
2207
    if result.failed and not can_fail:
2208
      raise errors.HypervisorError("Unable to get KVM %s output" %
2209
                                    " ".join(optlist))
2210
    return result.output
2211

    
2212
  @classmethod
2213
  def _GetKVMVersion(cls, kvm_path):
2214
    """Return the installed KVM version.
2215

2216
    @return: (version, v_maj, v_min, v_rev)
2217
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2218

2219
    """
2220
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2221

    
2222
  @classmethod
2223
  def _GetDefaultMachineVersion(cls, kvm_path):
2224
    """Return the default hardware revision (e.g. pc-1.1)
2225

2226
    """
2227
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2228
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2229
    if match:
2230
      return match.group(1)
2231
    else:
2232
      return "pc"
2233

    
2234
  def StopInstance(self, instance, force=False, retry=False, name=None):
2235
    """Stop an instance.
2236

2237
    """
2238
    if name is not None and not force:
2239
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2240
    if name is None:
2241
      name = instance.name
2242
      acpi = instance.hvparams[constants.HV_ACPI]
2243
    else:
2244
      acpi = False
2245
    _, pid, alive = self._InstancePidAlive(name)
2246
    if pid > 0 and alive:
2247
      if force or not acpi:
2248
        utils.KillProcess(pid)
2249
      else:
2250
        self._CallMonitorCommand(name, "system_powerdown")
2251

    
2252
  def CleanupInstance(self, instance_name):
2253
    """Cleanup after a stopped instance
2254

2255
    """
2256
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2257
    if pid > 0 and alive:
2258
      raise errors.HypervisorError("Cannot cleanup a live instance")
2259
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2260

    
2261
  def RebootInstance(self, instance):
2262
    """Reboot an instance.
2263

2264
    """
2265
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2266
    # socket the instance will stop, but now power up again. So we'll resort
2267
    # to shutdown and restart.
2268
    _, _, alive = self._InstancePidAlive(instance.name)
2269
    if not alive:
2270
      raise errors.HypervisorError("Failed to reboot instance %s:"
2271
                                   " not running" % instance.name)
2272
    # StopInstance will delete the saved KVM runtime so:
2273
    # ...first load it...
2274
    kvm_runtime = self._LoadKVMRuntime(instance)
2275
    # ...now we can safely call StopInstance...
2276
    if not self.StopInstance(instance):
2277
      self.StopInstance(instance, force=True)
2278
    # ...and finally we can save it again, and execute it...
2279
    self._SaveKVMRuntime(instance, kvm_runtime)
2280
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2281
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2282
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2283

    
2284
  def MigrationInfo(self, instance):
2285
    """Get instance information to perform a migration.
2286

2287
    @type instance: L{objects.Instance}
2288
    @param instance: instance to be migrated
2289
    @rtype: string
2290
    @return: content of the KVM runtime file
2291

2292
    """
2293
    return self._ReadKVMRuntime(instance.name)
2294

    
2295
  def AcceptInstance(self, instance, info, target):
2296
    """Prepare to accept an instance.
2297

2298
    @type instance: L{objects.Instance}
2299
    @param instance: instance to be accepted
2300
    @type info: string
2301
    @param info: content of the KVM runtime file on the source node
2302
    @type target: string
2303
    @param target: target host (usually ip), on this node
2304

2305
    """
2306
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2307
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2308
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2309
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2310
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2311
                            incoming=incoming_address)
2312

    
2313
  def FinalizeMigrationDst(self, instance, info, success):
2314
    """Finalize the instance migration on the target node.
2315

2316
    Stop the incoming mode KVM.
2317

2318
    @type instance: L{objects.Instance}
2319
    @param instance: instance whose migration is being finalized
2320

2321
    """
2322
    if success:
2323
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
2324
      kvm_nics = kvm_runtime[1]
2325

    
2326
      for nic_seq, nic in enumerate(kvm_nics):
2327
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2328
          # Bridged interfaces have already been configured
2329
          continue
2330
        try:
2331
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2332
        except EnvironmentError, err:
2333
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2334
                          instance.name, nic_seq, str(err))
2335
          continue
2336
        try:
2337
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2338
        except errors.HypervisorError, err:
2339
          logging.warning(str(err))
2340

    
2341
      self._WriteKVMRuntime(instance.name, info)
2342
    else:
2343
      self.StopInstance(instance, force=True)
2344

    
2345
  def MigrateInstance(self, instance, target, live):
2346
    """Migrate an instance to a target node.
2347

2348
    The migration will not be attempted if the instance is not
2349
    currently running.
2350

2351
    @type instance: L{objects.Instance}
2352
    @param instance: the instance to be migrated
2353
    @type target: string
2354
    @param target: ip address of the target node
2355
    @type live: boolean
2356
    @param live: perform a live migration
2357

2358
    """
2359
    instance_name = instance.name
2360
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2361
    _, _, alive = self._InstancePidAlive(instance_name)
2362
    if not alive:
2363
      raise errors.HypervisorError("Instance not running, cannot migrate")
2364

    
2365
    if not live:
2366
      self._CallMonitorCommand(instance_name, "stop")
2367

    
2368
    migrate_command = ("migrate_set_speed %dm" %
2369
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2370
    self._CallMonitorCommand(instance_name, migrate_command)
2371

    
2372
    migrate_command = ("migrate_set_downtime %dms" %
2373
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2374
    self._CallMonitorCommand(instance_name, migrate_command)
2375

    
2376
    # These commands are supported in latest qemu versions.
2377
    # Since _CallMonitorCommand does not catch monitor errors
2378
    # this does not raise an exception in case command is not supported
2379
    # TODO: either parse output of command or see if the command supported
2380
    # via info help (see hotplug)
2381
    migrate_command = ("migrate_set_capability xbzrle on")
2382
    self._CallMonitorCommand(instance_name, migrate_command)
2383

    
2384
    migrate_command = ("migrate_set_capability auto-converge on")
2385
    self._CallMonitorCommand(instance_name, migrate_command)
2386

    
2387
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2388
    self._CallMonitorCommand(instance_name, migrate_command)
2389

    
2390
  def FinalizeMigrationSource(self, instance, success, live):
2391
    """Finalize the instance migration on the source node.
2392

2393
    @type instance: L{objects.Instance}
2394
    @param instance: the instance that was migrated
2395
    @type success: bool
2396
    @param success: whether the migration succeeded or not
2397
    @type live: bool
2398
    @param live: whether the user requested a live migration or not
2399

2400
    """
2401
    if success:
2402
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2403
      utils.KillProcess(pid)
2404
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2405
    elif live:
2406
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2407

    
2408
  def GetMigrationStatus(self, instance):
2409
    """Get the migration status
2410

2411
    @type instance: L{objects.Instance}
2412
    @param instance: the instance that is being migrated
2413
    @rtype: L{objects.MigrationStatus}
2414
    @return: the status of the current migration (one of
2415
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2416
             progress info that can be retrieved from the hypervisor
2417

2418
    """
2419
    info_command = "info migrate"
2420
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2421
      result = self._CallMonitorCommand(instance.name, info_command)
2422
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2423
      if not match:
2424
        if not result.stdout:
2425
          logging.info("KVM: empty 'info migrate' result")
2426
        else:
2427
          logging.warning("KVM: unknown 'info migrate' result: %s",
2428
                          result.stdout)
2429
      else:
2430
        status = match.group(1)
2431
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2432
          migration_status = objects.MigrationStatus(status=status)
2433
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2434
          if match:
2435
            migration_status.transferred_ram = match.group("transferred")
2436
            migration_status.total_ram = match.group("total")
2437

    
2438
          return migration_status
2439

    
2440
        logging.warning("KVM: unknown migration status '%s'", status)
2441

    
2442
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2443

    
2444
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2445

    
2446
  def BalloonInstanceMemory(self, instance, mem):
2447
    """Balloon an instance memory to a certain value.
2448

2449
    @type instance: L{objects.Instance}
2450
    @param instance: instance to be accepted
2451
    @type mem: int
2452
    @param mem: actual memory size to use for instance runtime
2453

2454
    """
2455
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2456

    
2457
  def GetNodeInfo(self):
2458
    """Return information about the node.
2459

2460
    @return: a dict with the following keys (values in MiB):
2461
          - memory_total: the total memory size on the node
2462
          - memory_free: the available memory on the node for instances
2463
          - memory_dom0: the memory used by the node itself, if available
2464
          - hv_version: the hypervisor version in the form (major, minor,
2465
                        revision)
2466

2467
    """
2468
    result = self.GetLinuxNodeInfo()
2469
    # FIXME: this is the global kvm version, but the actual version can be
2470
    # customized as an hv parameter. we should use the nodegroup's default kvm
2471
    # path parameter here.
2472
    _, v_major, v_min, v_rev = self._GetKVMVersion(constants.KVM_PATH)
2473
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2474
    return result
2475

    
2476
  @classmethod
2477
  def GetInstanceConsole(cls, instance, hvparams, beparams):
2478
    """Return a command for connecting to the console of an instance.
2479

2480
    """
2481
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2482
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2483
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2484
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2485
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2486
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2487
      return objects.InstanceConsole(instance=instance.name,
2488
                                     kind=constants.CONS_SSH,
2489
                                     host=instance.primary_node,
2490
                                     user=constants.SSH_CONSOLE_USER,
2491
                                     command=cmd)
2492

    
2493
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2494
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2495
      display = instance.network_port - constants.VNC_BASE_PORT
2496
      return objects.InstanceConsole(instance=instance.name,
2497
                                     kind=constants.CONS_VNC,
2498
                                     host=vnc_bind_address,
2499
                                     port=instance.network_port,
2500
                                     display=display)
2501

    
2502
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2503
    if spice_bind:
2504
      return objects.InstanceConsole(instance=instance.name,
2505
                                     kind=constants.CONS_SPICE,
2506
                                     host=spice_bind,
2507
                                     port=instance.network_port)
2508

    
2509
    return objects.InstanceConsole(instance=instance.name,
2510
                                   kind=constants.CONS_MESSAGE,
2511
                                   message=("No serial shell for instance %s" %
2512
                                            instance.name))
2513

    
2514
  def Verify(self):
2515
    """Verify the hypervisor.
2516

2517
    Check that the required binaries exist.
2518

2519
    @return: Problem description if something is wrong, C{None} otherwise
2520

2521
    """
2522
    msgs = []
2523
    # FIXME: this is the global kvm binary, but the actual path can be
2524
    # customized as an hv parameter; we should use the nodegroup's
2525
    # default kvm path parameter here.
2526
    if not os.path.exists(constants.KVM_PATH):
2527
      msgs.append("The KVM binary ('%s') does not exist" % constants.KVM_PATH)
2528
    if not os.path.exists(constants.SOCAT_PATH):
2529
      msgs.append("The socat binary ('%s') does not exist" %
2530
                  constants.SOCAT_PATH)
2531

    
2532
    return self._FormatVerifyResults(msgs)
2533

    
2534
  @classmethod
2535
  def CheckParameterSyntax(cls, hvparams):
2536
    """Check the given parameters for validity.
2537

2538
    @type hvparams:  dict
2539
    @param hvparams: dictionary with parameter names/value
2540
    @raise errors.HypervisorError: when a parameter is not valid
2541

2542
    """
2543
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2544

    
2545
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2546
    if kernel_path:
2547
      if not hvparams[constants.HV_ROOT_PATH]:
2548
        raise errors.HypervisorError("Need a root partition for the instance,"
2549
                                     " if a kernel is defined")
2550

    
2551
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2552
        not hvparams[constants.HV_VNC_X509]):
2553
      raise errors.HypervisorError("%s must be defined, if %s is" %
2554
                                   (constants.HV_VNC_X509,
2555
                                    constants.HV_VNC_X509_VERIFY))
2556

    
2557
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2558
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2559
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2560
      if not serial_speed or serial_speed not in valid_speeds:
2561
        raise errors.HypervisorError("Invalid serial console speed, must be"
2562
                                     " one of: %s" %
2563
                                     utils.CommaJoin(valid_speeds))
2564

    
2565
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2566
    if (boot_order == constants.HT_BO_CDROM and
2567
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2568
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2569
                                   " ISO path")
2570

    
2571
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2572
    if security_model == constants.HT_SM_USER:
2573
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2574
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2575
                                     " must be specified")
2576
    elif (security_model == constants.HT_SM_NONE or
2577
          security_model == constants.HT_SM_POOL):
2578
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2579
        raise errors.HypervisorError("Cannot have a security domain when the"
2580
                                     " security model is 'none' or 'pool'")
2581

    
2582
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2583
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2584
    if spice_bind:
2585
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2586
        # if an IP version is specified, the spice_bind parameter must be an
2587
        # IP of that family
2588
        if (netutils.IP4Address.IsValid(spice_bind) and
2589
            spice_ip_version != constants.IP4_VERSION):
2590
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2591
                                       " the specified IP version is %s" %
2592
                                       (spice_bind, spice_ip_version))
2593

    
2594
        if (netutils.IP6Address.IsValid(spice_bind) and
2595
            spice_ip_version != constants.IP6_VERSION):
2596
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2597
                                       " the specified IP version is %s" %
2598
                                       (spice_bind, spice_ip_version))
2599
    else:
2600
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2601
      # error if any of them is set without it.
2602
      for param in _SPICE_ADDITIONAL_PARAMS:
2603
        if hvparams[param]:
2604
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2605
                                       (param, constants.HV_KVM_SPICE_BIND))
2606

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

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

2615
    """
2616
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2617

    
2618
    kvm_path = hvparams[constants.HV_KVM_PATH]
2619

    
2620
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2621
    if security_model == constants.HT_SM_USER:
2622
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2623
      try:
2624
        pwd.getpwnam(username)
2625
      except KeyError:
2626
        raise errors.HypervisorError("Unknown security domain user %s"
2627
                                     % username)
2628

    
2629
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2630
    if spice_bind:
2631
      # only one of VNC and SPICE can be used currently.
2632
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2633
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2634
                                     " only one of them can be used at a"
2635
                                     " given time")
2636

    
2637
      # check that KVM supports SPICE
2638
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2639
      if not cls._SPICE_RE.search(kvmhelp):
2640
        raise errors.HypervisorError("SPICE is configured, but it is not"
2641
                                     " supported according to 'kvm --help'")
2642

    
2643
      # if spice_bind is not an IP address, it must be a valid interface
2644
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2645
                       netutils.IP6Address.IsValid(spice_bind))
2646
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2647
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2648
                                     " a valid IP address or interface name" %
2649
                                     constants.HV_KVM_SPICE_BIND)
2650

    
2651
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2652
    if machine_version:
2653
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2654
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2655
        raise errors.HypervisorError("Unsupported machine version: %s" %
2656
                                     machine_version)
2657

    
2658
  @classmethod
2659
  def PowercycleNode(cls):
2660
    """KVM powercycle, just a wrapper over Linux powercycle.
2661

2662
    """
2663
    cls.LinuxPowercycle()