Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 08879a9d

History | View | Annotate | Download (103.2 kB)

1
#
2
#
3

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

    
21

    
22
"""KVM hypervisor
23

24
"""
25

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

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

    
62

    
63
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-ifup-custom"
64
_KVM_START_PAUSED_FLAG = "-S"
65

    
66
# TUN/TAP driver constants, taken from <linux/if_tun.h>
67
# They are architecture-independent and already hardcoded in qemu-kvm source,
68
# so we can safely include them here.
69
TUNSETIFF = 0x400454ca
70
TUNGETIFF = 0x800454d2
71
TUNGETFEATURES = 0x800454cf
72
IFF_TAP = 0x0002
73
IFF_NO_PI = 0x1000
74
IFF_ONE_QUEUE = 0x2000
75
IFF_VNET_HDR = 0x4000
76

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

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

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

    
118
_MIGRATION_CAPS_DELIM = ":"
119

    
120

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

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

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

134
  """
135

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

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

    
142

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

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

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

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

    
167
    slot = int(avail[0])
168

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

    
172
  return slot
173

    
174

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

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

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

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

    
198
  return found[0]
199

    
200

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

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

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

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

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

    
226
  return kvm_cmd, serialized_nics, hvparams, serialized_disks
227

    
228

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

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

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

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

    
246

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

250
  @see: L{_ProbeTapVnetHdr}
251

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

    
263

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

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

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

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

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

    
287
  result = bool(flags & IFF_VNET_HDR)
288

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

    
292
  return result
293

    
294

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

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

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

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

    
312
  flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE
313

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

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

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

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

    
330

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

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

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

    
342
    self.data = data
343

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
396

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

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

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

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

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

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

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

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

439
    Connects to the UNIX socket
440

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

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

    
447
    self._check_socket()
448

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

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

459
    It cannot be used after this call.
460

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

    
464

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

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

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

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

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

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

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

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

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

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

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

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

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

    
534
    return (message, buf)
535

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

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

544
    """
545
    self._check_connection()
546

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

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

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

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

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

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

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

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

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

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

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

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

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

    
630

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

634
  """
635
  CAN_MIGRATE = True
636

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

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

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

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

    
748
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
749
  _MIGRATION_INFO_RETRY_DELAY = 2
750

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
876
    return (instance, memory, vcpus)
877

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

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

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

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

    
897
    return (pidfile, pid, alive)
898

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1063
  @staticmethod
1064
  def _CreateNICEnv(instance_name, nic, tap, seq=None, instance_tags=None):
1065
    """Create environment variables for a specific NIC
1066

1067
    This is needed during NIC ifup/ifdown scripts.
1068
    Since instance tags may change during NIC creation and removal
1069
    and because during cleanup instance object is not available we
1070
    pass them only upon NIC creation (instance startup/NIC hot-plugging).
1071

1072
    """
1073
    env = {
1074
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
1075
      "INSTANCE": instance_name,
1076
      "MAC": nic.mac,
1077
      "MODE": nic.nicparams[constants.NIC_MODE],
1078
      "INTERFACE_UUID": nic.uuid,
1079
    }
1080

    
1081
    if instance_tags:
1082
      env["TAGS"] = " ".join(instance_tags)
1083

    
1084
    # This should always be available except for old instances in the
1085
    # cluster without uuid indexed tap files.
1086
    if tap:
1087
      env["INTERFACE"] = tap
1088

    
1089
    if seq:
1090
      env["INTERFACE_INDEX"] = str(seq)
1091

    
1092
    if nic.ip:
1093
      env["IP"] = nic.ip
1094

    
1095
    if nic.name:
1096
      env["INTERFACE_NAME"] = nic.name
1097

    
1098
    if nic.nicparams[constants.NIC_LINK]:
1099
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
1100

    
1101
    if nic.network:
1102
      n = objects.Network.FromDict(nic.netinfo)
1103
      env.update(n.HooksDict())
1104

    
1105
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1106
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
1107

    
1108
    return env
1109

    
1110
  @classmethod
1111
  def _ConfigureNIC(cls, instance, seq, nic, tap):
1112
    """Run the network configuration script for a specified NIC
1113

1114
    @param instance: instance we're acting on
1115
    @type instance: instance object
1116
    @param seq: nic sequence number
1117
    @type seq: int
1118
    @param nic: nic we're acting on
1119
    @type nic: nic object
1120
    @param tap: the host's tap interface this NIC corresponds to
1121
    @type tap: str
1122

1123
    """
1124
    env = cls._CreateNICEnv(instance.name, nic, tap, seq, instance.GetTags())
1125
    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
1126
    if result.failed:
1127
      raise errors.HypervisorError("Failed to configure interface %s: %s;"
1128
                                   " network configuration script output: %s" %
1129
                                   (tap, result.fail_reason, result.output))
1130

    
1131
  @classmethod
1132
  def _UnconfigureNic(cls, instance_name, nic, only_local=True):
1133
    """Run ifdown script for a specific NIC
1134

1135
    This is executed during instance cleanup and NIC hot-unplug
1136

1137
    @param instance_name: instance we're acting on
1138
    @type instance_name: string
1139
    @param nic: nic we're acting on
1140
    @type nic: nic object
1141
    @param only_local: whether ifdown script should reset global conf (dns)
1142
    @type only_local: boolean
1143

1144
    """
1145
    tap = cls._GetInstanceNICTap(instance_name, nic)
1146
    env = cls._CreateNICEnv(instance_name, nic, tap)
1147
    arg2 = str(only_local).lower()
1148
    result = utils.RunCmd([pathutils.KVM_IFDOWN, tap, arg2], env=env)
1149
    if result.failed:
1150
      raise errors.HypervisorError("Failed to unconfigure interface %s: %s;"
1151
                                   " network configuration script output: %s" %
1152
                                   (tap, result.fail_reason, result.output))
1153

    
1154
  @staticmethod
1155
  def _VerifyAffinityPackage():
1156
    if affinity is None:
1157
      raise errors.HypervisorError("affinity Python package not"
1158
                                   " found; cannot use CPU pinning under KVM")
1159

    
1160
  @staticmethod
1161
  def _BuildAffinityCpuMask(cpu_list):
1162
    """Create a CPU mask suitable for sched_setaffinity from a list of
1163
    CPUs.
1164

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

1168
    @type cpu_list: list of int
1169
    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
1170
    @rtype: int
1171
    @return: a bit mask of CPU affinities
1172

1173
    """
1174
    if cpu_list == constants.CPU_PINNING_OFF:
1175
      return constants.CPU_PINNING_ALL_KVM
1176
    else:
1177
      return sum(2 ** cpu for cpu in cpu_list)
1178

    
1179
  @classmethod
1180
  def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
1181
    """Change CPU affinity for running VM according to given CPU mask.
1182

1183
    @param cpu_mask: CPU mask as given by the user. e.g. "0-2,4:all:1,3"
1184
    @type cpu_mask: string
1185
    @param process_id: process ID of KVM process. Used to pin entire VM
1186
                       to physical CPUs.
1187
    @type process_id: int
1188
    @param thread_dict: map of virtual CPUs to KVM thread IDs
1189
    @type thread_dict: dict int:int
1190

1191
    """
1192
    # Convert the string CPU mask to a list of list of int's
1193
    cpu_list = utils.ParseMultiCpuMask(cpu_mask)
1194

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

    
1213
      # For each vCPU, map it to the proper list of physical CPUs
1214
      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
1215
        affinity.set_process_affinity_mask(thread_dict[i],
1216
                                           cls._BuildAffinityCpuMask(vcpu))
1217

    
1218
  def _GetVcpuThreadIds(self, instance_name):
1219
    """Get a mapping of vCPU no. to thread IDs for the instance
1220

1221
    @type instance_name: string
1222
    @param instance_name: instance in question
1223
    @rtype: dictionary of int:int
1224
    @return: a dictionary mapping vCPU numbers to thread IDs
1225

1226
    """
1227
    result = {}
1228
    output = self._CallMonitorCommand(instance_name, self._CPU_INFO_CMD)
1229
    for line in output.stdout.splitlines():
1230
      match = self._CPU_INFO_RE.search(line)
1231
      if not match:
1232
        continue
1233
      grp = map(int, match.groups())
1234
      result[grp[0]] = grp[1]
1235

    
1236
    return result
1237

    
1238
  def _ExecuteCpuAffinity(self, instance_name, cpu_mask):
1239
    """Complete CPU pinning.
1240

1241
    @type instance_name: string
1242
    @param instance_name: name of instance
1243
    @type cpu_mask: string
1244
    @param cpu_mask: CPU pinning mask as entered by user
1245

1246
    """
1247
    # Get KVM process ID, to be used if need to pin entire VM
1248
    _, pid, _ = self._InstancePidAlive(instance_name)
1249
    # Get vCPU thread IDs, to be used if need to pin vCPUs separately
1250
    thread_dict = self._GetVcpuThreadIds(instance_name)
1251
    # Run CPU pinning, based on configured mask
1252
    self._AssignCpuAffinity(cpu_mask, pid, thread_dict)
1253

    
1254
  def ListInstances(self, hvparams=None):
1255
    """Get the list of running instances.
1256

1257
    We can do this by listing our live instances directory and
1258
    checking whether the associated kvm process is still alive.
1259

1260
    """
1261
    result = []
1262
    for name in os.listdir(self._PIDS_DIR):
1263
      if self._InstancePidAlive(name)[2]:
1264
        result.append(name)
1265
    return result
1266

    
1267
  def GetInstanceInfo(self, instance_name, hvparams=None):
1268
    """Get instance properties.
1269

1270
    @type instance_name: string
1271
    @param instance_name: the instance name
1272
    @type hvparams: dict of strings
1273
    @param hvparams: hvparams to be used with this instance
1274
    @rtype: tuple of strings
1275
    @return: (name, id, memory, vcpus, stat, times)
1276

1277
    """
1278
    _, pid, alive = self._InstancePidAlive(instance_name)
1279
    if not alive:
1280
      return None
1281

    
1282
    _, memory, vcpus = self._InstancePidInfo(pid)
1283
    istat = "---b-"
1284
    times = "0"
1285

    
1286
    try:
1287
      qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
1288
      qmp.connect()
1289
      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
1290
      # Will fail if ballooning is not enabled, but we can then just resort to
1291
      # the value above.
1292
      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
1293
      memory = mem_bytes / 1048576
1294
    except errors.HypervisorError:
1295
      pass
1296

    
1297
    return (instance_name, pid, memory, vcpus, istat, times)
1298

    
1299
  def GetAllInstancesInfo(self, hvparams=None):
1300
    """Get properties of all instances.
1301

1302
    @type hvparams: dict of strings
1303
    @param hvparams: hypervisor parameter
1304
    @return: list of tuples (name, id, memory, vcpus, stat, times)
1305

1306
    """
1307
    data = []
1308
    for name in os.listdir(self._PIDS_DIR):
1309
      try:
1310
        info = self.GetInstanceInfo(name)
1311
      except errors.HypervisorError:
1312
        # Ignore exceptions due to instances being shut down
1313
        continue
1314
      if info:
1315
        data.append(info)
1316
    return data
1317

    
1318
  def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
1319
                                      kvmhelp, devlist):
1320
    """Generate KVM options regarding instance's block devices.
1321

1322
    @type instance: L{objects.Instance}
1323
    @param instance: the instance object
1324
    @type up_hvp: dict
1325
    @param up_hvp: the instance's runtime hypervisor parameters
1326
    @type kvm_disks: list of tuples
1327
    @param kvm_disks: list of tuples [(disk, link_name, uri)..]
1328
    @type kvmhelp: string
1329
    @param kvmhelp: output of kvm --help
1330
    @type devlist: string
1331
    @param devlist: output of kvm -device ?
1332
    @rtype: list
1333
    @return: list of command line options eventually used by kvm executable
1334

1335
    """
1336
    kernel_path = up_hvp[constants.HV_KERNEL_PATH]
1337
    if kernel_path:
1338
      boot_disk = False
1339
    else:
1340
      boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
1341

    
1342
    # whether this is an older KVM version that uses the boot=on flag
1343
    # on devices
1344
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1345

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

    
1385
      access_mode = cfdev.params.get(constants.LDP_ACCESS,
1386
                                     constants.DISK_KERNELSPACE)
1387
      if (uri and access_mode == constants.DISK_USERSPACE):
1388
        drive_uri = uri
1389
      else:
1390
        drive_uri = link_name
1391

    
1392
      # For ext we allow overriding disk_cache hypervisor params per disk
1393
      disk_cache = cfdev.params.get("cache", None)
1394
      if disk_cache:
1395
        cache_val = ",cache=%s" % disk_cache
1396

    
1397
      drive_val = "file=%s,format=raw%s%s%s" % \
1398
                  (drive_uri, if_val, boot_val, cache_val)
1399

    
1400
      if device_driver:
1401
        # kvm_disks are the 4th entry of runtime file that did not exist in
1402
        # the past. That means that cfdev should always have pci slot and
1403
        # _GenerateDeviceKVMId() will not raise a exception.
1404
        kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_DISK, cfdev)
1405
        drive_val += (",id=%s" % kvm_devid)
1406
        drive_val += (",bus=0,unit=%d" % cfdev.pci)
1407
        dev_val = ("%s,drive=%s,id=%s" %
1408
                   (device_driver, kvm_devid, kvm_devid))
1409
        dev_val += ",bus=pci.0,addr=%s" % hex(cfdev.pci)
1410
        dev_opts.extend(["-device", dev_val])
1411

    
1412
      # TODO: export disk geometry in IDISK_PARAMS
1413
      heads = cfdev.params.get('heads', None)
1414
      secs = cfdev.params.get('secs', None)
1415
      if heads and secs:
1416
        nr_sectors = cfdev.size * 1024 * 1024 / 512
1417
        cyls = nr_sectors / (int(heads) * int(secs))
1418
        if cyls > 16383:
1419
          cyls = 16383
1420
        elif cyls < 2:
1421
          cyls = 2
1422
        if cyls and heads and secs:
1423
          drive_val += (",cyls=%d,heads=%d,secs=%d" %
1424
                        (cyls, int(heads), int(secs)))
1425

    
1426
      dev_opts.extend(["-drive", drive_val])
1427

    
1428
    return dev_opts
1429

    
1430
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused,
1431
                          kvmhelp):
1432
    """Generate KVM information to start an instance.
1433

1434
    @type kvmhelp: string
1435
    @param kvmhelp: output of kvm --help
1436
    @attention: this function must not have any side-effects; for
1437
        example, it must not write to the filesystem, or read values
1438
        from the current system the are expected to differ between
1439
        nodes, since it is only run once at instance startup;
1440
        actions/kvm arguments that can vary between systems should be
1441
        done in L{_ExecuteKVMRuntime}
1442

1443
    """
1444
    # pylint: disable=R0912,R0914,R0915
1445
    hvp = instance.hvparams
1446
    self.ValidateParameters(hvp)
1447

    
1448
    pidfile = self._InstancePidFile(instance.name)
1449
    kvm = hvp[constants.HV_KVM_PATH]
1450
    kvm_cmd = [kvm]
1451
    # used just by the vnc server, if enabled
1452
    kvm_cmd.extend(["-name", instance.name])
1453
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MAXMEM]])
1454

    
1455
    smp_list = ["%s" % instance.beparams[constants.BE_VCPUS]]
1456
    if hvp[constants.HV_CPU_CORES]:
1457
      smp_list.append("cores=%s" % hvp[constants.HV_CPU_CORES])
1458
    if hvp[constants.HV_CPU_THREADS]:
1459
      smp_list.append("threads=%s" % hvp[constants.HV_CPU_THREADS])
1460
    if hvp[constants.HV_CPU_SOCKETS]:
1461
      smp_list.append("sockets=%s" % hvp[constants.HV_CPU_SOCKETS])
1462

    
1463
    kvm_cmd.extend(["-smp", ",".join(smp_list)])
1464

    
1465
    kvm_cmd.extend(["-pidfile", pidfile])
1466

    
1467
    pci_reservations = bitarray(self._DEFAULT_PCI_RESERVATIONS)
1468

    
1469
    # As requested by music lovers
1470
    if hvp[constants.HV_SOUNDHW]:
1471
      soundhw = hvp[constants.HV_SOUNDHW]
1472
      # For some reason only few sound devices require a PCI slot
1473
      # while the Audio controller *must* be in slot 3.
1474
      # That's why we bridge this option early in command line
1475
      if soundhw in self._SOUNDHW_WITH_PCI_SLOT:
1476
        _ = _GetFreeSlot(pci_reservations, reserve=True)
1477
      kvm_cmd.extend(["-soundhw", soundhw])
1478

    
1479
    if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
1480
      # The SCSI controller requires another PCI slot.
1481
      _ = _GetFreeSlot(pci_reservations, reserve=True)
1482

    
1483
    # Add id to ballon and place to the first available slot (3 or 4)
1484
    addr = _GetFreeSlot(pci_reservations, reserve=True)
1485
    pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1486
    kvm_cmd.extend(["-balloon", "virtio,id=balloon%s" % pci_info])
1487
    kvm_cmd.extend(["-daemonize"])
1488
    if not instance.hvparams[constants.HV_ACPI]:
1489
      kvm_cmd.extend(["-no-acpi"])
1490
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
1491
        constants.INSTANCE_REBOOT_EXIT:
1492
      kvm_cmd.extend(["-no-reboot"])
1493

    
1494
    mversion = hvp[constants.HV_KVM_MACHINE_VERSION]
1495
    if not mversion:
1496
      mversion = self._GetDefaultMachineVersion(kvm)
1497
    if self._MACHINE_RE.search(kvmhelp):
1498
      # TODO (2.8): kernel_irqchip and kvm_shadow_mem machine properties, as
1499
      # extra hypervisor parameters. We should also investigate whether and how
1500
      # shadow_mem should be considered for the resource model.
1501
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED):
1502
        specprop = ",accel=kvm"
1503
      else:
1504
        specprop = ""
1505
      machinespec = "%s%s" % (mversion, specprop)
1506
      kvm_cmd.extend(["-machine", machinespec])
1507
    else:
1508
      kvm_cmd.extend(["-M", mversion])
1509
      if (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED and
1510
          self._ENABLE_KVM_RE.search(kvmhelp)):
1511
        kvm_cmd.extend(["-enable-kvm"])
1512
      elif (hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED and
1513
            self._DISABLE_KVM_RE.search(kvmhelp)):
1514
        kvm_cmd.extend(["-disable-kvm"])
1515

    
1516
    kernel_path = hvp[constants.HV_KERNEL_PATH]
1517
    if kernel_path:
1518
      boot_cdrom = boot_floppy = boot_network = False
1519
    else:
1520
      boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
1521
      boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
1522
      boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
1523

    
1524
    if startup_paused:
1525
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
1526

    
1527
    if boot_network:
1528
      kvm_cmd.extend(["-boot", "n"])
1529

    
1530
    # whether this is an older KVM version that uses the boot=on flag
1531
    # on devices
1532
    needs_boot_flag = self._BOOT_RE.search(kvmhelp)
1533

    
1534
    disk_type = hvp[constants.HV_DISK_TYPE]
1535

    
1536
    #Now we can specify a different device type for CDROM devices.
1537
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
1538
    if not cdrom_disk_type:
1539
      cdrom_disk_type = disk_type
1540

    
1541
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
1542
    if iso_image:
1543
      options = ",format=raw,media=cdrom"
1544
      # set cdrom 'if' type
1545
      if boot_cdrom:
1546
        actual_cdrom_type = constants.HT_DISK_IDE
1547
      elif cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1548
        actual_cdrom_type = "virtio"
1549
      else:
1550
        actual_cdrom_type = cdrom_disk_type
1551
      if_val = ",if=%s" % actual_cdrom_type
1552
      # set boot flag, if needed
1553
      boot_val = ""
1554
      if boot_cdrom:
1555
        kvm_cmd.extend(["-boot", "d"])
1556
        if needs_boot_flag:
1557
          boot_val = ",boot=on"
1558
      # and finally build the entire '-drive' value
1559
      drive_val = "file=%s%s%s%s" % (iso_image, options, if_val, boot_val)
1560
      kvm_cmd.extend(["-drive", drive_val])
1561

    
1562
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
1563
    if iso_image2:
1564
      options = ",format=raw,media=cdrom"
1565
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
1566
        if_val = ",if=virtio"
1567
      else:
1568
        if_val = ",if=%s" % cdrom_disk_type
1569
      drive_val = "file=%s%s%s" % (iso_image2, options, if_val)
1570
      kvm_cmd.extend(["-drive", drive_val])
1571

    
1572
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
1573
    if floppy_image:
1574
      options = ",format=raw,media=disk"
1575
      if boot_floppy:
1576
        kvm_cmd.extend(["-boot", "a"])
1577
        options = "%s,boot=on" % options
1578
      if_val = ",if=floppy"
1579
      options = "%s%s" % (options, if_val)
1580
      drive_val = "file=%s%s" % (floppy_image, options)
1581
      kvm_cmd.extend(["-drive", drive_val])
1582

    
1583
    if kernel_path:
1584
      kvm_cmd.extend(["-kernel", kernel_path])
1585
      initrd_path = hvp[constants.HV_INITRD_PATH]
1586
      if initrd_path:
1587
        kvm_cmd.extend(["-initrd", initrd_path])
1588
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
1589
                     hvp[constants.HV_KERNEL_ARGS]]
1590
      if hvp[constants.HV_SERIAL_CONSOLE]:
1591
        serial_speed = hvp[constants.HV_SERIAL_SPEED]
1592
        root_append.append("console=ttyS0,%s" % serial_speed)
1593
      kvm_cmd.extend(["-append", " ".join(root_append)])
1594

    
1595
    mem_path = hvp[constants.HV_MEM_PATH]
1596
    if mem_path:
1597
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
1598

    
1599
    monitor_dev = ("unix:%s,server,nowait" %
1600
                   self._InstanceMonitor(instance.name))
1601
    kvm_cmd.extend(["-monitor", monitor_dev])
1602
    if hvp[constants.HV_SERIAL_CONSOLE]:
1603
      serial_dev = ("unix:%s,server,nowait" %
1604
                    self._InstanceSerial(instance.name))
1605
      kvm_cmd.extend(["-serial", serial_dev])
1606
    else:
1607
      kvm_cmd.extend(["-serial", "none"])
1608

    
1609
    mouse_type = hvp[constants.HV_USB_MOUSE]
1610
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
1611
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
1612
    spice_ip_version = None
1613

    
1614
    kvm_cmd.extend(["-usb"])
1615

    
1616
    if mouse_type:
1617
      kvm_cmd.extend(["-usbdevice", mouse_type])
1618
    elif vnc_bind_address:
1619
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
1620

    
1621
    if vnc_bind_address:
1622
      if netutils.IsValidInterface(vnc_bind_address):
1623
        if_addresses = netutils.GetInterfaceIpAddresses(vnc_bind_address)
1624
        if_ip4_addresses = if_addresses[constants.IP4_VERSION]
1625
        if len(if_ip4_addresses) < 1:
1626
          logging.error("Could not determine IPv4 address of interface %s",
1627
                        vnc_bind_address)
1628
        else:
1629
          vnc_bind_address = if_ip4_addresses[0]
1630
      if netutils.IP4Address.IsValid(vnc_bind_address):
1631
        if instance.network_port > constants.VNC_BASE_PORT:
1632
          display = instance.network_port - constants.VNC_BASE_PORT
1633
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
1634
            vnc_arg = ":%d" % (display)
1635
          else:
1636
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
1637
        else:
1638
          logging.error("Network port is not a valid VNC display (%d < %d),"
1639
                        " not starting VNC",
1640
                        instance.network_port, constants.VNC_BASE_PORT)
1641
          vnc_arg = "none"
1642

    
1643
        # Only allow tls and other option when not binding to a file, for now.
1644
        # kvm/qemu gets confused otherwise about the filename to use.
1645
        vnc_append = ""
1646
        if hvp[constants.HV_VNC_TLS]:
1647
          vnc_append = "%s,tls" % vnc_append
1648
          if hvp[constants.HV_VNC_X509_VERIFY]:
1649
            vnc_append = "%s,x509verify=%s" % (vnc_append,
1650
                                               hvp[constants.HV_VNC_X509])
1651
          elif hvp[constants.HV_VNC_X509]:
1652
            vnc_append = "%s,x509=%s" % (vnc_append,
1653
                                         hvp[constants.HV_VNC_X509])
1654
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
1655
          vnc_append = "%s,password" % vnc_append
1656

    
1657
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
1658

    
1659
      else:
1660
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
1661

    
1662
      kvm_cmd.extend(["-vnc", vnc_arg])
1663
    elif spice_bind:
1664
      # FIXME: this is wrong here; the iface ip address differs
1665
      # between systems, so it should be done in _ExecuteKVMRuntime
1666
      if netutils.IsValidInterface(spice_bind):
1667
        # The user specified a network interface, we have to figure out the IP
1668
        # address.
1669
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
1670
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
1671

    
1672
        # if the user specified an IP version and the interface does not
1673
        # have that kind of IP addresses, throw an exception
1674
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1675
          if not addresses[spice_ip_version]:
1676
            raise errors.HypervisorError("SPICE: Unable to get an IPv%s address"
1677
                                         " for %s" % (spice_ip_version,
1678
                                                      spice_bind))
1679

    
1680
        # the user did not specify an IP version, we have to figure it out
1681
        elif (addresses[constants.IP4_VERSION] and
1682
              addresses[constants.IP6_VERSION]):
1683
          # we have both ipv4 and ipv6, let's use the cluster default IP
1684
          # version
1685
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
1686
          spice_ip_version = \
1687
            netutils.IPAddress.GetVersionFromAddressFamily(cluster_family)
1688
        elif addresses[constants.IP4_VERSION]:
1689
          spice_ip_version = constants.IP4_VERSION
1690
        elif addresses[constants.IP6_VERSION]:
1691
          spice_ip_version = constants.IP6_VERSION
1692
        else:
1693
          raise errors.HypervisorError("SPICE: Unable to get an IP address"
1694
                                       " for %s" % (spice_bind))
1695

    
1696
        spice_address = addresses[spice_ip_version][0]
1697

    
1698
      else:
1699
        # spice_bind is known to be a valid IP address, because
1700
        # ValidateParameters checked it.
1701
        spice_address = spice_bind
1702

    
1703
      spice_arg = "addr=%s" % spice_address
1704
      if hvp[constants.HV_KVM_SPICE_USE_TLS]:
1705
        spice_arg = ("%s,tls-port=%s,x509-cacert-file=%s" %
1706
                     (spice_arg, instance.network_port,
1707
                      pathutils.SPICE_CACERT_FILE))
1708
        spice_arg = ("%s,x509-key-file=%s,x509-cert-file=%s" %
1709
                     (spice_arg, pathutils.SPICE_CERT_FILE,
1710
                      pathutils.SPICE_CERT_FILE))
1711
        tls_ciphers = hvp[constants.HV_KVM_SPICE_TLS_CIPHERS]
1712
        if tls_ciphers:
1713
          spice_arg = "%s,tls-ciphers=%s" % (spice_arg, tls_ciphers)
1714
      else:
1715
        spice_arg = "%s,port=%s" % (spice_arg, instance.network_port)
1716

    
1717
      if not hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]:
1718
        spice_arg = "%s,disable-ticketing" % spice_arg
1719

    
1720
      if spice_ip_version:
1721
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
1722

    
1723
      # Image compression options
1724
      img_lossless = hvp[constants.HV_KVM_SPICE_LOSSLESS_IMG_COMPR]
1725
      img_jpeg = hvp[constants.HV_KVM_SPICE_JPEG_IMG_COMPR]
1726
      img_zlib_glz = hvp[constants.HV_KVM_SPICE_ZLIB_GLZ_IMG_COMPR]
1727
      if img_lossless:
1728
        spice_arg = "%s,image-compression=%s" % (spice_arg, img_lossless)
1729
      if img_jpeg:
1730
        spice_arg = "%s,jpeg-wan-compression=%s" % (spice_arg, img_jpeg)
1731
      if img_zlib_glz:
1732
        spice_arg = "%s,zlib-glz-wan-compression=%s" % (spice_arg, img_zlib_glz)
1733

    
1734
      # Video stream detection
1735
      video_streaming = hvp[constants.HV_KVM_SPICE_STREAMING_VIDEO_DETECTION]
1736
      if video_streaming:
1737
        spice_arg = "%s,streaming-video=%s" % (spice_arg, video_streaming)
1738

    
1739
      # Audio compression, by default in qemu-kvm it is on
1740
      if not hvp[constants.HV_KVM_SPICE_AUDIO_COMPR]:
1741
        spice_arg = "%s,playback-compression=off" % spice_arg
1742
      if not hvp[constants.HV_KVM_SPICE_USE_VDAGENT]:
1743
        spice_arg = "%s,agent-mouse=off" % spice_arg
1744
      else:
1745
        # Enable the spice agent communication channel between the host and the
1746
        # agent.
1747
        addr = _GetFreeSlot(pci_reservations, reserve=True)
1748
        pci_info = ",bus=pci.0,addr=%s" % hex(addr)
1749
        kvm_cmd.extend(["-device", "virtio-serial-pci,id=spice%s" % pci_info])
1750
        kvm_cmd.extend([
1751
          "-device",
1752
          "virtserialport,chardev=spicechannel0,name=com.redhat.spice.0",
1753
          ])
1754
        kvm_cmd.extend(["-chardev", "spicevmc,id=spicechannel0,name=vdagent"])
1755

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

    
1759
    else:
1760
      # From qemu 1.4 -nographic is incompatible with -daemonize. The new way
1761
      # also works in earlier versions though (tested with 1.1 and 1.3)
1762
      if self._DISPLAY_RE.search(kvmhelp):
1763
        kvm_cmd.extend(["-display", "none"])
1764
      else:
1765
        kvm_cmd.extend(["-nographic"])
1766

    
1767
    if hvp[constants.HV_USE_LOCALTIME]:
1768
      kvm_cmd.extend(["-localtime"])
1769

    
1770
    if hvp[constants.HV_KVM_USE_CHROOT]:
1771
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
1772

    
1773
    # Add qemu-KVM -cpu param
1774
    if hvp[constants.HV_CPU_TYPE]:
1775
      kvm_cmd.extend(["-cpu", hvp[constants.HV_CPU_TYPE]])
1776

    
1777
    # Pass a -vga option if requested, or if spice is used, for backwards
1778
    # compatibility.
1779
    if hvp[constants.HV_VGA]:
1780
      kvm_cmd.extend(["-vga", hvp[constants.HV_VGA]])
1781
    elif spice_bind:
1782
      kvm_cmd.extend(["-vga", "qxl"])
1783

    
1784
    # Various types of usb devices, comma separated
1785
    if hvp[constants.HV_USB_DEVICES]:
1786
      for dev in hvp[constants.HV_USB_DEVICES].split(","):
1787
        kvm_cmd.extend(["-usbdevice", dev])
1788

    
1789
    # Set system UUID to instance UUID
1790
    if self._UUID_RE.search(kvmhelp):
1791
      kvm_cmd.extend(["-uuid", instance.uuid])
1792

    
1793
    if hvp[constants.HV_KVM_EXTRA]:
1794
      kvm_cmd.extend(hvp[constants.HV_KVM_EXTRA].split(" "))
1795

    
1796
    kvm_disks = []
1797
    for disk, link_name, uri in block_devices:
1798
      disk.pci = _GetFreeSlot(pci_reservations, disk.pci, True)
1799
      kvm_disks.append((disk, link_name, uri))
1800

    
1801
    kvm_nics = []
1802
    for nic in instance.nics:
1803
      nic.pci = _GetFreeSlot(pci_reservations, nic.pci, True)
1804
      kvm_nics.append(nic)
1805

    
1806
    hvparams = hvp
1807

    
1808
    return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
1809

    
1810
  def _WriteKVMRuntime(self, instance_name, data):
1811
    """Write an instance's KVM runtime
1812

1813
    """
1814
    try:
1815
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
1816
                      data=data)
1817
    except EnvironmentError, err:
1818
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
1819

    
1820
  @classmethod
1821
  def _ReadKVMRuntime(cls, instance_name):
1822
    """Read an instance's KVM runtime
1823

1824
    """
1825
    try:
1826
      file_content = utils.ReadFile(cls._InstanceKVMRuntime(instance_name))
1827
    except EnvironmentError, err:
1828
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
1829
    return file_content
1830

    
1831
  def _SaveKVMRuntime(self, instance, kvm_runtime):
1832
    """Save an instance's KVM runtime
1833

1834
    """
1835
    kvm_cmd, kvm_nics, hvparams, kvm_disks = kvm_runtime
1836

    
1837
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
1838
    serialized_disks = [(blk.ToDict(), link, uri)
1839
                        for blk, link, uri in kvm_disks]
1840
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams,
1841
                                      serialized_disks))
1842

    
1843
    self._WriteKVMRuntime(instance.name, serialized_form)
1844

    
1845
  @classmethod
1846
  def _LoadKVMRuntime(cls, instance_name, serialized_runtime=None):
1847
    """Load an instance's KVM runtime
1848

1849
    """
1850
    if not serialized_runtime:
1851
      serialized_runtime = cls._ReadKVMRuntime(instance_name)
1852

    
1853
    return _AnalyzeSerializedRuntime(serialized_runtime)
1854

    
1855
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
1856
    """Run the KVM cmd and check for errors
1857

1858
    @type name: string
1859
    @param name: instance name
1860
    @type kvm_cmd: list of strings
1861
    @param kvm_cmd: runcmd input for kvm
1862
    @type tap_fds: list of int
1863
    @param tap_fds: fds of tap devices opened by Ganeti
1864

1865
    """
1866
    try:
1867
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
1868
    finally:
1869
      for fd in tap_fds:
1870
        utils_wrapper.CloseFdNoError(fd)
1871

    
1872
    if result.failed:
1873
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
1874
                                   (name, result.fail_reason, result.output))
1875
    if not self._InstancePidAlive(name)[2]:
1876
      raise errors.HypervisorError("Failed to start instance %s" % name)
1877

    
1878
  # too many local variables
1879
  # pylint: disable=R0914
1880
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, kvmhelp, incoming=None):
1881
    """Execute a KVM cmd, after completing it with some last minute data.
1882

1883
    @type incoming: tuple of strings
1884
    @param incoming: (target_host_ip, port)
1885
    @type kvmhelp: string
1886
    @param kvmhelp: output of kvm --help
1887

1888
    """
1889
    # Small _ExecuteKVMRuntime hv parameters programming howto:
1890
    #  - conf_hvp contains the parameters as configured on ganeti. they might
1891
    #    have changed since the instance started; only use them if the change
1892
    #    won't affect the inside of the instance (which hasn't been rebooted).
1893
    #  - up_hvp contains the parameters as they were when the instance was
1894
    #    started, plus any new parameter which has been added between ganeti
1895
    #    versions: it is paramount that those default to a value which won't
1896
    #    affect the inside of the instance as well.
1897
    conf_hvp = instance.hvparams
1898
    name = instance.name
1899
    self._CheckDown(name)
1900

    
1901
    temp_files = []
1902

    
1903
    kvm_cmd, kvm_nics, up_hvp, kvm_disks = kvm_runtime
1904
    # the first element of kvm_cmd is always the path to the kvm binary
1905
    kvm_path = kvm_cmd[0]
1906
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
1907

    
1908
    # We know it's safe to run as a different user upon migration, so we'll use
1909
    # the latest conf, from conf_hvp.
1910
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
1911
    if security_model == constants.HT_SM_USER:
1912
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
1913

    
1914
    keymap = conf_hvp[constants.HV_KEYMAP]
1915
    if keymap:
1916
      keymap_path = self._InstanceKeymapFile(name)
1917
      # If a keymap file is specified, KVM won't use its internal defaults. By
1918
      # first including the "en-us" layout, an error on loading the actual
1919
      # layout (e.g. because it can't be found) won't lead to a non-functional
1920
      # keyboard. A keyboard with incorrect keys is still better than none.
1921
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
1922
      kvm_cmd.extend(["-k", keymap_path])
1923

    
1924
    # We have reasons to believe changing something like the nic driver/type
1925
    # upon migration won't exactly fly with the instance kernel, so for nic
1926
    # related parameters we'll use up_hvp
1927
    tapfds = []
1928
    taps = []
1929
    devlist = self._GetKVMOutput(kvm_path, self._KVMOPT_DEVICELIST)
1930

    
1931
    bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
1932
                                                     up_hvp,
1933
                                                     kvm_disks,
1934
                                                     kvmhelp,
1935
                                                     devlist)
1936
    kvm_cmd.extend(bdev_opts)
1937

    
1938
    if not kvm_nics:
1939
      kvm_cmd.extend(["-net", "none"])
1940
    else:
1941
      vnet_hdr = False
1942
      tap_extra = ""
1943
      nic_type = up_hvp[constants.HV_NIC_TYPE]
1944
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
1945
        nic_model = self._VIRTIO
1946
        try:
1947
          if self._VIRTIO_NET_RE.search(devlist):
1948
            nic_model = self._VIRTIO_NET_PCI
1949
            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
1950
        except errors.HypervisorError, _:
1951
          # Older versions of kvm don't support DEVICE_LIST, but they don't
1952
          # have new virtio syntax either.
1953
          pass
1954

    
1955
        if up_hvp[constants.HV_VHOST_NET]:
1956
          # check for vhost_net support
1957
          if self._VHOST_RE.search(kvmhelp):
1958
            tap_extra = ",vhost=on"
1959
          else:
1960
            raise errors.HypervisorError("vhost_net is configured"
1961
                                         " but it is not available")
1962
      else:
1963
        nic_model = nic_type
1964

    
1965
      kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
1966

    
1967
      for nic_seq, nic in enumerate(kvm_nics):
1968
        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr)
1969
        tapfds.append(tapfd)
1970
        taps.append(tapname)
1971
        if kvm_supports_netdev:
1972
          nic_val = "%s,mac=%s" % (nic_model, nic.mac)
1973
          try:
1974
            # kvm_nics already exist in old runtime files and thus there might
1975
            # be some entries without pci slot (therefore try: except:)
1976
            kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
1977
            netdev = kvm_devid
1978
            nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
1979
          except errors.HotplugError:
1980
            netdev = "netdev%d" % nic_seq
1981
          nic_val += (",netdev=%s" % netdev)
1982
          tap_val = ("type=tap,id=%s,fd=%d%s" %
1983
                     (netdev, tapfd, tap_extra))
1984
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
1985
        else:
1986
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
1987
                                                         nic.mac, nic_model)
1988
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
1989
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
1990

    
1991
    if incoming:
1992
      target, port = incoming
1993
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
1994

    
1995
    # Changing the vnc password doesn't bother the guest that much. At most it
1996
    # will surprise people who connect to it. Whether positively or negatively
1997
    # it's debatable.
1998
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
1999
    vnc_pwd = None
2000
    if vnc_pwd_file:
2001
      try:
2002
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
2003
      except EnvironmentError, err:
2004
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
2005
                                     % (vnc_pwd_file, err))
2006

    
2007
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
2008
      utils.EnsureDirs([(self._InstanceChrootDir(name),
2009
                         constants.SECURE_DIR_MODE)])
2010

    
2011
    # Automatically enable QMP if version is >= 0.14
2012
    if self._QMP_RE.search(kvmhelp):
2013
      logging.debug("Enabling QMP")
2014
      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
2015
                      self._InstanceQmpMonitor(instance.name)])
2016

    
2017
    # Configure the network now for starting instances and bridged interfaces,
2018
    # during FinalizeMigration for incoming instances' routed interfaces
2019
    for nic_seq, nic in enumerate(kvm_nics):
2020
      if (incoming and
2021
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
2022
        continue
2023
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
2024

    
2025
    # CPU affinity requires kvm to start paused, so we set this flag if the
2026
    # instance is not already paused and if we are not going to accept a
2027
    # migrating instance. In the latter case, pausing is not needed.
2028
    start_kvm_paused = not (_KVM_START_PAUSED_FLAG in kvm_cmd) and not incoming
2029
    if start_kvm_paused:
2030
      kvm_cmd.extend([_KVM_START_PAUSED_FLAG])
2031

    
2032
    # Note: CPU pinning is using up_hvp since changes take effect
2033
    # during instance startup anyway, and to avoid problems when soft
2034
    # rebooting the instance.
2035
    cpu_pinning = False
2036
    if up_hvp.get(constants.HV_CPU_MASK, None):
2037
      cpu_pinning = True
2038

    
2039
    if security_model == constants.HT_SM_POOL:
2040
      ss = ssconf.SimpleStore()
2041
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
2042
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
2043
      uid = uidpool.RequestUnusedUid(all_uids)
2044
      try:
2045
        username = pwd.getpwuid(uid.GetUid()).pw_name
2046
        kvm_cmd.extend(["-runas", username])
2047
        self._RunKVMCmd(name, kvm_cmd, tapfds)
2048
      except:
2049
        uidpool.ReleaseUid(uid)
2050
        raise
2051
      else:
2052
        uid.Unlock()
2053
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
2054
    else:
2055
      self._RunKVMCmd(name, kvm_cmd, tapfds)
2056

    
2057
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
2058
                     constants.RUN_DIRS_MODE)])
2059
    for nic_seq, tap in enumerate(taps):
2060
      nic = kvm_nics[nic_seq]
2061
      self._WriteInstanceNICFiles(instance.name, nic_seq, nic, tap)
2062

    
2063
    if vnc_pwd:
2064
      change_cmd = "change vnc password %s" % vnc_pwd
2065
      self._CallMonitorCommand(instance.name, change_cmd)
2066

    
2067
    # Setting SPICE password. We are not vulnerable to malicious passwordless
2068
    # connection attempts because SPICE by default does not allow connections
2069
    # if neither a password nor the "disable_ticketing" options are specified.
2070
    # As soon as we send the password via QMP, that password is a valid ticket
2071
    # for connection.
2072
    spice_password_file = conf_hvp[constants.HV_KVM_SPICE_PASSWORD_FILE]
2073
    if spice_password_file:
2074
      spice_pwd = ""
2075
      try:
2076
        spice_pwd = utils.ReadOneLineFile(spice_password_file, strict=True)
2077
      except EnvironmentError, err:
2078
        raise errors.HypervisorError("Failed to open SPICE password file %s: %s"
2079
                                     % (spice_password_file, err))
2080

    
2081
      qmp = QmpConnection(self._InstanceQmpMonitor(instance.name))
2082
      qmp.connect()
2083
      arguments = {
2084
          "protocol": "spice",
2085
          "password": spice_pwd,
2086
      }
2087
      qmp.Execute("set_password", arguments)
2088

    
2089
    for filename in temp_files:
2090
      utils.RemoveFile(filename)
2091

    
2092
    # If requested, set CPU affinity and resume instance execution
2093
    if cpu_pinning:
2094
      self._ExecuteCpuAffinity(instance.name, up_hvp[constants.HV_CPU_MASK])
2095

    
2096
    start_memory = self._InstanceStartupMemory(instance)
2097
    if start_memory < instance.beparams[constants.BE_MAXMEM]:
2098
      self.BalloonInstanceMemory(instance, start_memory)
2099

    
2100
    if start_kvm_paused:
2101
      # To control CPU pinning, ballooning, and vnc/spice passwords
2102
      # the VM was started in a frozen state. If freezing was not
2103
      # explicitly requested resume the vm status.
2104
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2105

    
2106
  def StartInstance(self, instance, block_devices, startup_paused):
2107
    """Start an instance.
2108

2109
    """
2110
    self._CheckDown(instance.name)
2111
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2112
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2113
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
2114
                                           startup_paused, kvmhelp)
2115
    self._SaveKVMRuntime(instance, kvm_runtime)
2116
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2117

    
2118
  def _CallMonitorCommand(self, instance_name, command, timeout=None):
2119
    """Invoke a command on the instance monitor.
2120

2121
    """
2122
    if timeout is not None:
2123
      timeout_cmd = "timeout %s" % (timeout, )
2124
    else:
2125
      timeout_cmd = ""
2126

    
2127
    # TODO: Replace monitor calls with QMP once KVM >= 0.14 is the minimum
2128
    # version. The monitor protocol is designed for human consumption, whereas
2129
    # QMP is made for programmatic usage. In the worst case QMP can also
2130
    # execute monitor commands. As it is, all calls to socat take at least
2131
    # 500ms and likely more: socat can't detect the end of the reply and waits
2132
    # for 500ms of no data received before exiting (500 ms is the default for
2133
    # the "-t" parameter).
2134
    socat = ("echo %s | %s %s STDIO UNIX-CONNECT:%s" %
2135
             (utils.ShellQuote(command),
2136
              timeout_cmd,
2137
              constants.SOCAT_PATH,
2138
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
2139

    
2140
    result = utils.RunCmd(socat)
2141
    if result.failed:
2142
      msg = ("Failed to send command '%s' to instance '%s', reason '%s',"
2143
             " output: %s" %
2144
             (command, instance_name, result.fail_reason, result.output))
2145
      raise errors.HypervisorError(msg)
2146

    
2147
    return result
2148

    
2149
  def _GetFreePCISlot(self, instance, dev):
2150
    """Get the first available pci slot of a runnung instance.
2151

2152
    """
2153
    slots = bitarray(32)
2154
    slots.setall(False) # pylint: disable=E1101
2155
    output = self._CallMonitorCommand(instance.name, self._INFO_PCI_CMD)
2156
    for line in output.stdout.splitlines():
2157
      match = self._INFO_PCI_RE.search(line)
2158
      if match:
2159
        slot = int(match.group(1))
2160
        slots[slot] = True
2161

    
2162
    dev.pci = _GetFreeSlot(slots)
2163

    
2164
  def VerifyHotplugSupport(self, instance, action, dev_type):
2165
    """Verifies that hotplug is supported.
2166

2167
    Hotplug is *not* supported in case of:
2168
     - security models and chroot (disk hotplug)
2169
     - fdsend module is missing (nic hot-add)
2170

2171
    @raise errors.HypervisorError: in one of the previous cases
2172

2173
    """
2174
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2175
      hvp = instance.hvparams
2176
      security_model = hvp[constants.HV_SECURITY_MODEL]
2177
      use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
2178
      if use_chroot:
2179
        raise errors.HotplugError("Disk hotplug is not supported"
2180
                                  " in case of chroot.")
2181
      if security_model != constants.HT_SM_NONE:
2182
        raise errors.HotplugError("Disk Hotplug is not supported in case"
2183
                                  " security models are used.")
2184

    
2185
    if (dev_type == constants.HOTPLUG_TARGET_NIC and
2186
        action == constants.HOTPLUG_ACTION_ADD and not fdsend):
2187
      raise errors.HotplugError("Cannot hot-add NIC."
2188
                                " fdsend python module is missing.")
2189

    
2190
  def HotplugSupported(self, instance):
2191
    """Checks if hotplug is generally supported.
2192

2193
    Hotplug is *not* supported in case of:
2194
     - qemu versions < 1.0
2195
     - for stopped instances
2196

2197
    @raise errors.HypervisorError: in one of the previous cases
2198

2199
    """
2200
    try:
2201
      output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
2202
    except errors.HypervisorError:
2203
      raise errors.HotplugError("Instance is probably down")
2204

    
2205
    # TODO: search for netdev_add, drive_add, device_add.....
2206
    match = self._INFO_VERSION_RE.search(output.stdout)
2207
    if not match:
2208
      raise errors.HotplugError("Cannot parse qemu version via monitor")
2209

    
2210
    v_major, v_min, _, _ = match.groups()
2211
    if (int(v_major), int(v_min)) < (1, 0):
2212
      raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
2213

    
2214
  def _CallHotplugCommands(self, name, cmds):
2215
    for c in cmds:
2216
      self._CallMonitorCommand(name, c)
2217
      time.sleep(1)
2218

    
2219
  def _VerifyHotplugCommand(self, instance_name, device, dev_type,
2220
                            should_exist):
2221
    """Checks if a previous hotplug command has succeeded.
2222

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

2226
    @raise errors.HypervisorError: if result is not the expected one
2227

2228
    """
2229
    output = self._CallMonitorCommand(instance_name, self._INFO_PCI_CMD)
2230
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2231
    match = \
2232
      self._FIND_PCI_DEVICE_RE(device.pci, kvm_devid).search(output.stdout)
2233
    if match and not should_exist:
2234
      msg = "Device %s should have been removed but is still there" % kvm_devid
2235
      raise errors.HypervisorError(msg)
2236

    
2237
    if not match and should_exist:
2238
      msg = "Device %s should have been added but is missing" % kvm_devid
2239
      raise errors.HypervisorError(msg)
2240

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

    
2243
  def HotAddDevice(self, instance, dev_type, device, extra, seq):
2244
    """ Helper method to hot-add a new device
2245

2246
    It gets free pci slot generates the device name and invokes the
2247
    device specific method.
2248

2249
    """
2250
    # in case of hot-mod this is given
2251
    if device.pci is None:
2252
      self._GetFreePCISlot(instance, device)
2253
    kvm_devid = _GenerateDeviceKVMId(dev_type, device)
2254
    runtime = self._LoadKVMRuntime(instance.name)
2255
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2256
      cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
2257
                (extra, kvm_devid)]
2258
      cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
2259
                (hex(device.pci), kvm_devid, kvm_devid)]
2260
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2261
      (tap, fd) = _OpenTap()
2262
      self._ConfigureNIC(instance, seq, device, tap)
2263
      self._PassTapFd(instance, fd, device)
2264
      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
2265
      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
2266
               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
2267
      cmds += ["device_add %s" % args]
2268
      self._WriteInstanceNICFiles(instance.name, seq, device, tap)
2269

    
2270
    self._CallHotplugCommands(instance.name, cmds)
2271
    self._VerifyHotplugCommand(instance.name, device, dev_type, True)
2272
    # update relevant entries in runtime file
2273
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2274
    entry = _RUNTIME_ENTRY[dev_type](device, extra)
2275
    runtime[index].append(entry)
2276
    self._SaveKVMRuntime(instance, runtime)
2277

    
2278
  def HotDelDevice(self, instance, dev_type, device, _, seq):
2279
    """ Helper method for hot-del device
2280

2281
    It gets device info from runtime file, generates the device name and
2282
    invokes the device specific method.
2283

2284
    """
2285
    runtime = self._LoadKVMRuntime(instance.name)
2286
    entry = _GetExistingDeviceInfo(dev_type, device, runtime)
2287
    kvm_device = _RUNTIME_DEVICE[dev_type](entry)
2288
    kvm_devid = _GenerateDeviceKVMId(dev_type, kvm_device)
2289
    if dev_type == constants.HOTPLUG_TARGET_DISK:
2290
      cmds = ["device_del %s" % kvm_devid]
2291
      cmds += ["drive_del %s" % kvm_devid]
2292
    elif dev_type == constants.HOTPLUG_TARGET_NIC:
2293
      cmds = ["device_del %s" % kvm_devid]
2294
      cmds += ["netdev_del %s" % kvm_devid]
2295
      self._UnconfigureNic(instance.name, kvm_device, False)
2296
      self._RemoveInstanceNICFiles(instance.name, seq, device)
2297
    self._CallHotplugCommands(instance.name, cmds)
2298
    self._VerifyHotplugCommand(instance.name, kvm_device, dev_type, False)
2299
    index = _DEVICE_RUNTIME_INDEX[dev_type]
2300
    runtime[index].remove(entry)
2301
    self._SaveKVMRuntime(instance, runtime)
2302

    
2303
    return kvm_device.pci
2304

    
2305
  def HotModDevice(self, instance, dev_type, device, _, seq):
2306
    """ Helper method for hot-mod device
2307

2308
    It gets device info from runtime file, generates the device name and
2309
    invokes the device specific method. Currently only NICs support hot-mod
2310

2311
    """
2312
    if dev_type == constants.HOTPLUG_TARGET_NIC:
2313
      # putting it back in the same pci slot
2314
      device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
2315
      self.HotAddDevice(instance, dev_type, device, _, seq)
2316

    
2317
  def _PassTapFd(self, instance, fd, nic):
2318
    """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
2319

2320
    """
2321
    # TODO: factor out code related to unix sockets.
2322
    #       squash common parts between monitor and qmp
2323
    kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
2324
    command = "getfd %s\n" % kvm_devid
2325
    fds = [fd]
2326
    logging.info("%s", fds)
2327
    try:
2328
      monsock = MonitorSocket(self._InstanceMonitor(instance.name))
2329
      monsock.connect()
2330
      fdsend.sendfds(monsock.sock, command, fds=fds)
2331
    finally:
2332
      monsock.close()
2333

    
2334
  @classmethod
2335
  def _ParseKVMVersion(cls, text):
2336
    """Parse the KVM version from the --help output.
2337

2338
    @type text: string
2339
    @param text: output of kvm --help
2340
    @return: (version, v_maj, v_min, v_rev)
2341
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2342

2343
    """
2344
    match = cls._VERSION_RE.search(text.splitlines()[0])
2345
    if not match:
2346
      raise errors.HypervisorError("Unable to get KVM version")
2347

    
2348
    v_all = match.group(0)
2349
    v_maj = int(match.group(1))
2350
    v_min = int(match.group(2))
2351
    if match.group(4):
2352
      v_rev = int(match.group(4))
2353
    else:
2354
      v_rev = 0
2355
    return (v_all, v_maj, v_min, v_rev)
2356

    
2357
  @classmethod
2358
  def _GetKVMOutput(cls, kvm_path, option):
2359
    """Return the output of a kvm invocation
2360

2361
    @type kvm_path: string
2362
    @param kvm_path: path to the kvm executable
2363
    @type option: a key of _KVMOPTS_CMDS
2364
    @param option: kvm option to fetch the output from
2365
    @return: output a supported kvm invocation
2366
    @raise errors.HypervisorError: when the KVM help output cannot be retrieved
2367

2368
    """
2369
    assert option in cls._KVMOPTS_CMDS, "Invalid output option"
2370

    
2371
    optlist, can_fail = cls._KVMOPTS_CMDS[option]
2372

    
2373
    result = utils.RunCmd([kvm_path] + optlist)
2374
    if result.failed and not can_fail:
2375
      raise errors.HypervisorError("Unable to get KVM %s output" %
2376
                                    " ".join(optlist))
2377
    return result.output
2378

    
2379
  @classmethod
2380
  def _GetKVMVersion(cls, kvm_path):
2381
    """Return the installed KVM version.
2382

2383
    @return: (version, v_maj, v_min, v_rev)
2384
    @raise errors.HypervisorError: when the KVM version cannot be retrieved
2385

2386
    """
2387
    return cls._ParseKVMVersion(cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP))
2388

    
2389
  @classmethod
2390
  def _GetDefaultMachineVersion(cls, kvm_path):
2391
    """Return the default hardware revision (e.g. pc-1.1)
2392

2393
    """
2394
    output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2395
    match = cls._DEFAULT_MACHINE_VERSION_RE.search(output)
2396
    if match:
2397
      return match.group(1)
2398
    else:
2399
      return "pc"
2400

    
2401
  def StopInstance(self, instance, force=False, retry=False, name=None,
2402
                   timeout=None):
2403
    """Stop an instance.
2404

2405
    """
2406
    assert(timeout is None or force is not None)
2407

    
2408
    if name is not None and not force:
2409
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
2410
    if name is None:
2411
      name = instance.name
2412
      acpi = instance.hvparams[constants.HV_ACPI]
2413
    else:
2414
      acpi = False
2415
    _, pid, alive = self._InstancePidAlive(name)
2416
    if pid > 0 and alive:
2417
      if force or not acpi:
2418
        utils.KillProcess(pid)
2419
      else:
2420
        self._CallMonitorCommand(name, "system_powerdown", timeout)
2421

    
2422
  @classmethod
2423
  def _UnconfigureInstanceNICs(cls, instance_name, info=None):
2424
    """Get runtime NICs of an instance and unconfigure them
2425

2426
    """
2427
    _, kvm_nics, __, ___ = cls._LoadKVMRuntime(instance_name, info)
2428
    for nic in kvm_nics:
2429
      cls._UnconfigureNic(instance_name, nic)
2430

    
2431
  def CleanupInstance(self, instance_name):
2432
    """Cleanup after a stopped instance
2433

2434
    """
2435
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
2436
    if pid > 0 and alive:
2437
      raise errors.HypervisorError("Cannot cleanup a live instance")
2438
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
2439

    
2440
  def RebootInstance(self, instance):
2441
    """Reboot an instance.
2442

2443
    """
2444
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
2445
    # socket the instance will stop, but now power up again. So we'll resort
2446
    # to shutdown and restart.
2447
    _, _, alive = self._InstancePidAlive(instance.name)
2448
    if not alive:
2449
      raise errors.HypervisorError("Failed to reboot instance %s:"
2450
                                   " not running" % instance.name)
2451
    # StopInstance will delete the saved KVM runtime so:
2452
    # ...first load it...
2453
    kvm_runtime = self._LoadKVMRuntime(instance.name)
2454
    # ...now we can safely call StopInstance...
2455
    if not self.StopInstance(instance):
2456
      self.StopInstance(instance, force=True)
2457
    # ...and finally we can save it again, and execute it...
2458
    self._SaveKVMRuntime(instance, kvm_runtime)
2459
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2460
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2461
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp)
2462

    
2463
  def MigrationInfo(self, instance):
2464
    """Get instance information to perform a migration.
2465

2466
    @type instance: L{objects.Instance}
2467
    @param instance: instance to be migrated
2468
    @rtype: string
2469
    @return: content of the KVM runtime file
2470

2471
    """
2472
    return self._ReadKVMRuntime(instance.name)
2473

    
2474
  def AcceptInstance(self, instance, info, target):
2475
    """Prepare to accept an instance.
2476

2477
    @type instance: L{objects.Instance}
2478
    @param instance: instance to be accepted
2479
    @type info: string
2480
    @param info: content of the KVM runtime file on the source node
2481
    @type target: string
2482
    @param target: target host (usually ip), on this node
2483

2484
    """
2485
    kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2486
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
2487
    kvmpath = instance.hvparams[constants.HV_KVM_PATH]
2488
    kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
2489
    self._ExecuteKVMRuntime(instance, kvm_runtime, kvmhelp,
2490
                            incoming=incoming_address)
2491

    
2492
  def FinalizeMigrationDst(self, instance, info, success):
2493
    """Finalize the instance migration on the target node.
2494

2495
    Stop the incoming mode KVM.
2496

2497
    @type instance: L{objects.Instance}
2498
    @param instance: instance whose migration is being finalized
2499

2500
    """
2501
    if success:
2502
      kvm_runtime = self._LoadKVMRuntime(instance.name, serialized_runtime=info)
2503
      kvm_nics = kvm_runtime[1]
2504

    
2505
      for nic_seq, nic in enumerate(kvm_nics):
2506
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
2507
          # Bridged interfaces have already been configured
2508
          continue
2509
        try:
2510
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
2511
        except EnvironmentError, err:
2512
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
2513
                          instance.name, nic_seq, str(err))
2514
          continue
2515
        try:
2516
          self._ConfigureNIC(instance, nic_seq, nic, tap)
2517
        except errors.HypervisorError, err:
2518
          logging.warning(str(err))
2519

    
2520
      self._WriteKVMRuntime(instance.name, info)
2521
    else:
2522
      self._UnconfigureInstanceNICs(instance.name, info)
2523
      self.StopInstance(instance, force=True)
2524

    
2525
  def MigrateInstance(self, cluster_name, instance, target, live):
2526
    """Migrate an instance to a target node.
2527

2528
    The migration will not be attempted if the instance is not
2529
    currently running.
2530

2531
    @type cluster_name: string
2532
    @param cluster_name: name of the cluster
2533
    @type instance: L{objects.Instance}
2534
    @param instance: the instance to be migrated
2535
    @type target: string
2536
    @param target: ip address of the target node
2537
    @type live: boolean
2538
    @param live: perform a live migration
2539

2540
    """
2541
    instance_name = instance.name
2542
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
2543
    _, _, alive = self._InstancePidAlive(instance_name)
2544
    if not alive:
2545
      raise errors.HypervisorError("Instance not running, cannot migrate")
2546

    
2547
    if not live:
2548
      self._CallMonitorCommand(instance_name, "stop")
2549

    
2550
    migrate_command = ("migrate_set_speed %dm" %
2551
                       instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
2552
    self._CallMonitorCommand(instance_name, migrate_command)
2553

    
2554
    migrate_command = ("migrate_set_downtime %dms" %
2555
                       instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
2556
    self._CallMonitorCommand(instance_name, migrate_command)
2557

    
2558
    migration_caps = instance.hvparams[constants.HV_KVM_MIGRATION_CAPS]
2559
    if migration_caps:
2560
      for c in migration_caps.split(_MIGRATION_CAPS_DELIM):
2561
        migrate_command = ("migrate_set_capability %s on" % c)
2562
        self._CallMonitorCommand(instance_name, migrate_command)
2563

    
2564
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
2565
    self._CallMonitorCommand(instance_name, migrate_command)
2566

    
2567
  def FinalizeMigrationSource(self, instance, success, live):
2568
    """Finalize the instance migration on the source node.
2569

2570
    @type instance: L{objects.Instance}
2571
    @param instance: the instance that was migrated
2572
    @type success: bool
2573
    @param success: whether the migration succeeded or not
2574
    @type live: bool
2575
    @param live: whether the user requested a live migration or not
2576

2577
    """
2578
    if success:
2579
      pidfile, pid, _ = self._InstancePidAlive(instance.name)
2580
      utils.KillProcess(pid)
2581
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
2582
    elif live:
2583
      self._CallMonitorCommand(instance.name, self._CONT_CMD)
2584

    
2585
  def GetMigrationStatus(self, instance):
2586
    """Get the migration status
2587

2588
    @type instance: L{objects.Instance}
2589
    @param instance: the instance that is being migrated
2590
    @rtype: L{objects.MigrationStatus}
2591
    @return: the status of the current migration (one of
2592
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
2593
             progress info that can be retrieved from the hypervisor
2594

2595
    """
2596
    info_command = "info migrate"
2597
    for _ in range(self._MIGRATION_INFO_MAX_BAD_ANSWERS):
2598
      result = self._CallMonitorCommand(instance.name, info_command)
2599
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
2600
      if not match:
2601
        if not result.stdout:
2602
          logging.info("KVM: empty 'info migrate' result")
2603
        else:
2604
          logging.warning("KVM: unknown 'info migrate' result: %s",
2605
                          result.stdout)
2606
      else:
2607
        status = match.group(1)
2608
        if status in constants.HV_KVM_MIGRATION_VALID_STATUSES:
2609
          migration_status = objects.MigrationStatus(status=status)
2610
          match = self._MIGRATION_PROGRESS_RE.search(result.stdout)
2611
          if match:
2612
            migration_status.transferred_ram = match.group("transferred")
2613
            migration_status.total_ram = match.group("total")
2614

    
2615
          return migration_status
2616

    
2617
        logging.warning("KVM: unknown migration status '%s'", status)
2618

    
2619
      time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
2620

    
2621
    return objects.MigrationStatus(status=constants.HV_MIGRATION_FAILED)
2622

    
2623
  def BalloonInstanceMemory(self, instance, mem):
2624
    """Balloon an instance memory to a certain value.
2625

2626
    @type instance: L{objects.Instance}
2627
    @param instance: instance to be accepted
2628
    @type mem: int
2629
    @param mem: actual memory size to use for instance runtime
2630

2631
    """
2632
    self._CallMonitorCommand(instance.name, "balloon %d" % mem)
2633

    
2634
  def GetNodeInfo(self, hvparams=None):
2635
    """Return information about the node.
2636

2637
    @type hvparams: dict of strings
2638
    @param hvparams: hypervisor parameters, not used in this class
2639

2640
    @return: a dict as returned by L{BaseHypervisor.GetLinuxNodeInfo} plus
2641
        the following keys:
2642
          - hv_version: the hypervisor version in the form (major, minor,
2643
                        revision)
2644

2645
    """
2646
    result = self.GetLinuxNodeInfo()
2647
    kvmpath = constants.KVM_PATH
2648
    if hvparams is not None:
2649
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2650
    _, v_major, v_min, v_rev = self._GetKVMVersion(kvmpath)
2651
    result[constants.HV_NODEINFO_KEY_VERSION] = (v_major, v_min, v_rev)
2652
    return result
2653

    
2654
  @classmethod
2655
  def GetInstanceConsole(cls, instance, primary_node, hvparams, beparams):
2656
    """Return a command for connecting to the console of an instance.
2657

2658
    """
2659
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2660
      cmd = [pathutils.KVM_CONSOLE_WRAPPER,
2661
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
2662
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
2663
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
2664
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
2665
      return objects.InstanceConsole(instance=instance.name,
2666
                                     kind=constants.CONS_SSH,
2667
                                     host=primary_node.name,
2668
                                     user=constants.SSH_CONSOLE_USER,
2669
                                     command=cmd)
2670

    
2671
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2672
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
2673
      display = instance.network_port - constants.VNC_BASE_PORT
2674
      return objects.InstanceConsole(instance=instance.name,
2675
                                     kind=constants.CONS_VNC,
2676
                                     host=vnc_bind_address,
2677
                                     port=instance.network_port,
2678
                                     display=display)
2679

    
2680
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2681
    if spice_bind:
2682
      return objects.InstanceConsole(instance=instance.name,
2683
                                     kind=constants.CONS_SPICE,
2684
                                     host=spice_bind,
2685
                                     port=instance.network_port)
2686

    
2687
    return objects.InstanceConsole(instance=instance.name,
2688
                                   kind=constants.CONS_MESSAGE,
2689
                                   message=("No serial shell for instance %s" %
2690
                                            instance.name))
2691

    
2692
  def Verify(self, hvparams=None):
2693
    """Verify the hypervisor.
2694

2695
    Check that the required binaries exist.
2696

2697
    @type hvparams: dict of strings
2698
    @param hvparams: hypervisor parameters to be verified against, not used here
2699

2700
    @return: Problem description if something is wrong, C{None} otherwise
2701

2702
    """
2703
    msgs = []
2704
    kvmpath = constants.KVM_PATH
2705
    if hvparams is not None:
2706
      kvmpath = hvparams.get(constants.HV_KVM_PATH, constants.KVM_PATH)
2707
    if not os.path.exists(kvmpath):
2708
      msgs.append("The KVM binary ('%s') does not exist" % kvmpath)
2709
    if not os.path.exists(constants.SOCAT_PATH):
2710
      msgs.append("The socat binary ('%s') does not exist" %
2711
                  constants.SOCAT_PATH)
2712

    
2713
    return self._FormatVerifyResults(msgs)
2714

    
2715
  @classmethod
2716
  def CheckParameterSyntax(cls, hvparams):
2717
    """Check the given parameters for validity.
2718

2719
    @type hvparams:  dict
2720
    @param hvparams: dictionary with parameter names/value
2721
    @raise errors.HypervisorError: when a parameter is not valid
2722

2723
    """
2724
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
2725

    
2726
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
2727
    if kernel_path:
2728
      if not hvparams[constants.HV_ROOT_PATH]:
2729
        raise errors.HypervisorError("Need a root partition for the instance,"
2730
                                     " if a kernel is defined")
2731

    
2732
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
2733
        not hvparams[constants.HV_VNC_X509]):
2734
      raise errors.HypervisorError("%s must be defined, if %s is" %
2735
                                   (constants.HV_VNC_X509,
2736
                                    constants.HV_VNC_X509_VERIFY))
2737

    
2738
    if hvparams[constants.HV_SERIAL_CONSOLE]:
2739
      serial_speed = hvparams[constants.HV_SERIAL_SPEED]
2740
      valid_speeds = constants.VALID_SERIAL_SPEEDS
2741
      if not serial_speed or serial_speed not in valid_speeds:
2742
        raise errors.HypervisorError("Invalid serial console speed, must be"
2743
                                     " one of: %s" %
2744
                                     utils.CommaJoin(valid_speeds))
2745

    
2746
    boot_order = hvparams[constants.HV_BOOT_ORDER]
2747
    if (boot_order == constants.HT_BO_CDROM and
2748
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
2749
      raise errors.HypervisorError("Cannot boot from cdrom without an"
2750
                                   " ISO path")
2751

    
2752
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2753
    if security_model == constants.HT_SM_USER:
2754
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
2755
        raise errors.HypervisorError("A security domain (user to run kvm as)"
2756
                                     " must be specified")
2757
    elif (security_model == constants.HT_SM_NONE or
2758
          security_model == constants.HT_SM_POOL):
2759
      if hvparams[constants.HV_SECURITY_DOMAIN]:
2760
        raise errors.HypervisorError("Cannot have a security domain when the"
2761
                                     " security model is 'none' or 'pool'")
2762

    
2763
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2764
    spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
2765
    if spice_bind:
2766
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
2767
        # if an IP version is specified, the spice_bind parameter must be an
2768
        # IP of that family
2769
        if (netutils.IP4Address.IsValid(spice_bind) and
2770
            spice_ip_version != constants.IP4_VERSION):
2771
          raise errors.HypervisorError("SPICE: Got an IPv4 address (%s), but"
2772
                                       " the specified IP version is %s" %
2773
                                       (spice_bind, spice_ip_version))
2774

    
2775
        if (netutils.IP6Address.IsValid(spice_bind) and
2776
            spice_ip_version != constants.IP6_VERSION):
2777
          raise errors.HypervisorError("SPICE: Got an IPv6 address (%s), but"
2778
                                       " the specified IP version is %s" %
2779
                                       (spice_bind, spice_ip_version))
2780
    else:
2781
      # All the other SPICE parameters depend on spice_bind being set. Raise an
2782
      # error if any of them is set without it.
2783
      for param in _SPICE_ADDITIONAL_PARAMS:
2784
        if hvparams[param]:
2785
          raise errors.HypervisorError("SPICE: %s requires %s to be set" %
2786
                                       (param, constants.HV_KVM_SPICE_BIND))
2787

    
2788
  @classmethod
2789
  def ValidateParameters(cls, hvparams):
2790
    """Check the given parameters for validity.
2791

2792
    @type hvparams:  dict
2793
    @param hvparams: dictionary with parameter names/value
2794
    @raise errors.HypervisorError: when a parameter is not valid
2795

2796
    """
2797
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
2798

    
2799
    kvm_path = hvparams[constants.HV_KVM_PATH]
2800

    
2801
    security_model = hvparams[constants.HV_SECURITY_MODEL]
2802
    if security_model == constants.HT_SM_USER:
2803
      username = hvparams[constants.HV_SECURITY_DOMAIN]
2804
      try:
2805
        pwd.getpwnam(username)
2806
      except KeyError:
2807
        raise errors.HypervisorError("Unknown security domain user %s"
2808
                                     % username)
2809
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
2810
    if vnc_bind_address:
2811
      bound_to_addr = netutils.IP4Address.IsValid(vnc_bind_address)
2812
      is_interface = netutils.IsValidInterface(vnc_bind_address)
2813
      is_path = utils.IsNormAbsPath(vnc_bind_address)
2814
      if not bound_to_addr and not is_interface and not is_path:
2815
        raise errors.HypervisorError("VNC: The %s parameter must be either"
2816
                                     " a valid IP address, an interface name,"
2817
                                     " or an absolute path" %
2818
                                     constants.HV_KVM_SPICE_BIND)
2819

    
2820
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
2821
    if spice_bind:
2822
      # only one of VNC and SPICE can be used currently.
2823
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
2824
        raise errors.HypervisorError("Both SPICE and VNC are configured, but"
2825
                                     " only one of them can be used at a"
2826
                                     " given time")
2827

    
2828
      # check that KVM supports SPICE
2829
      kvmhelp = cls._GetKVMOutput(kvm_path, cls._KVMOPT_HELP)
2830
      if not cls._SPICE_RE.search(kvmhelp):
2831
        raise errors.HypervisorError("SPICE is configured, but it is not"
2832
                                     " supported according to 'kvm --help'")
2833

    
2834
      # if spice_bind is not an IP address, it must be a valid interface
2835
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind) or
2836
                       netutils.IP6Address.IsValid(spice_bind))
2837
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
2838
        raise errors.HypervisorError("SPICE: The %s parameter must be either"
2839
                                     " a valid IP address or interface name" %
2840
                                     constants.HV_KVM_SPICE_BIND)
2841

    
2842
    machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION]
2843
    if machine_version:
2844
      output = cls._GetKVMOutput(kvm_path, cls._KVMOPT_MLIST)
2845
      if not cls._CHECK_MACHINE_VERSION_RE(machine_version).search(output):
2846
        raise errors.HypervisorError("Unsupported machine version: %s" %
2847
                                     machine_version)
2848

    
2849
  @classmethod
2850
  def PowercycleNode(cls, hvparams=None):
2851
    """KVM powercycle, just a wrapper over Linux powercycle.
2852

2853
    @type hvparams: dict of strings
2854
    @param hvparams: hypervisor params to be used on this node
2855

2856
    """
2857
    cls.LinuxPowercycle()