Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ e687ec01

History | View | Annotate | Download (49.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010, 2011 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

    
38
from ganeti import utils
39
from ganeti import constants
40
from ganeti import errors
41
from ganeti import serializer
42
from ganeti import objects
43
from ganeti import uidpool
44
from ganeti import ssconf
45
from ganeti.hypervisor import hv_base
46
from ganeti import netutils
47
from ganeti.utils import wrapper as utils_wrapper
48

    
49

    
50
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
51

    
52
# TUN/TAP driver constants, taken from <linux/if_tun.h>
53
# They are architecture-independent and already hardcoded in qemu-kvm source,
54
# so we can safely include them here.
55
TUNSETIFF = 0x400454ca
56
TUNGETIFF = 0x800454d2
57
TUNGETFEATURES = 0x800454cf
58
IFF_TAP = 0x0002
59
IFF_NO_PI = 0x1000
60
IFF_VNET_HDR = 0x4000
61

    
62

    
63
def _ProbeTapVnetHdr(fd):
64
  """Check whether to enable the IFF_VNET_HDR flag.
65

66
  To do this, _all_ of the following conditions must be met:
67
   1. TUNGETFEATURES ioctl() *must* be implemented
68
   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
69
   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
70
      drivers/net/tun.c there is no way to test this until after the tap device
71
      has been created using TUNSETIFF, and there is no way to change the
72
      IFF_VNET_HDR flag after creating the interface, catch-22! However both
73
      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
74
      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
75

76
   @type fd: int
77
   @param fd: the file descriptor of /dev/net/tun
78

79
  """
80
  req = struct.pack("I", 0)
81
  try:
82
    res = fcntl.ioctl(fd, TUNGETFEATURES, req)
83
  except EnvironmentError:
84
    logging.warning("TUNGETFEATURES ioctl() not implemented")
85
    return False
86

    
87
  tunflags = struct.unpack("I", res)[0]
88
  if tunflags & IFF_VNET_HDR:
89
    return True
90
  else:
91
    logging.warning("Host does not support IFF_VNET_HDR, not enabling")
92
    return False
93

    
94

    
95
def _OpenTap(vnet_hdr=True):
96
  """Open a new tap device and return its file descriptor.
97

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

101
  @type vnet_hdr: boolean
102
  @param vnet_hdr: Enable the VNET Header
103
  @return: (ifname, tapfd)
104
  @rtype: tuple
105

106
  """
107
  try:
108
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
109
  except EnvironmentError:
110
    raise errors.HypervisorError("Failed to open /dev/net/tun")
111

    
112
  flags = IFF_TAP | IFF_NO_PI
113

    
114
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
115
    flags |= IFF_VNET_HDR
116

    
117
  # The struct ifreq ioctl request (see netdevice(7))
118
  ifr = struct.pack("16sh", "", flags)
119

    
120
  try:
121
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
122
  except EnvironmentError:
123
    raise errors.HypervisorError("Failed to allocate a new TAP device")
124

    
125
  # Get the interface name from the ioctl
126
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
127
  return (ifname, tapfd)
128

    
129

    
130
class KVMHypervisor(hv_base.BaseHypervisor):
131
  """KVM hypervisor interface"""
132
  CAN_MIGRATE = True
133

    
134
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
135
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
136
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
137
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
138
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
139
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
140
  _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
141
  # KVM instances with chroot enabled are started in empty chroot directories.
142
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
143
  # After an instance is stopped, its chroot directory is removed.
144
  # If the chroot directory is not empty, it can't be removed.
145
  # A non-empty chroot directory indicates a possible security incident.
146
  # To support forensics, the non-empty chroot directory is quarantined in
147
  # a separate directory, called 'chroot-quarantine'.
148
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
149
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
150
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
151

    
152
  PARAMETERS = {
153
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
154
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
155
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
156
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
157
    constants.HV_ACPI: hv_base.NO_CHECK,
158
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
159
    constants.HV_VNC_BIND_ADDRESS:
160
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
161
                         utils.IsNormAbsPath(x)),
162
       "the VNC bind address must be either a valid IP address or an absolute"
163
       " pathname", None, None),
164
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
165
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
166
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
167
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
168
    constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
169
    constants.HV_KVM_SPICE_IP_VERSION:
170
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
171
                         x in constants.VALID_IP_VERSIONS),
172
       "the SPICE IP version should be 4 or 6",
173
       None, None),
174
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
175
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
176
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
177
    constants.HV_BOOT_ORDER:
178
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
179
    constants.HV_NIC_TYPE:
180
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
181
    constants.HV_DISK_TYPE:
182
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
183
    constants.HV_KVM_CDROM_DISK_TYPE:
184
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
185
    constants.HV_USB_MOUSE:
186
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
187
    constants.HV_KEYMAP: hv_base.NO_CHECK,
188
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
189
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
190
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
191
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
192
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
193
    constants.HV_DISK_CACHE:
194
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
195
    constants.HV_SECURITY_MODEL:
196
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
197
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
198
    constants.HV_KVM_FLAG:
199
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
200
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
201
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
202
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
203
    constants.HV_REBOOT_BEHAVIOR:
204
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
205
    }
206

    
207
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
208
                                    re.M | re.I)
209
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
210
  _MIGRATION_INFO_RETRY_DELAY = 2
211

    
212
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
213

    
214
  ANCILLARY_FILES = [
215
    _KVM_NETWORK_SCRIPT,
216
    ]
217

    
218
  def __init__(self):
219
    hv_base.BaseHypervisor.__init__(self)
220
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
221
    # in a tmpfs filesystem or has been otherwise wiped out.
222
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
223
    utils.EnsureDirs(dirs)
224

    
225
  @classmethod
226
  def _InstancePidFile(cls, instance_name):
227
    """Returns the instance pidfile.
228

229
    """
230
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
231

    
232
  @classmethod
233
  def _InstanceUidFile(cls, instance_name):
234
    """Returns the instance uidfile.
235

236
    """
237
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
238

    
239
  @classmethod
240
  def _InstancePidInfo(cls, pid):
241
    """Check pid file for instance information.
242

243
    Check that a pid file is associated with an instance, and retrieve
244
    information from its command line.
245

246
    @type pid: string or int
247
    @param pid: process id of the instance to check
248
    @rtype: tuple
249
    @return: (instance_name, memory, vcpus)
250
    @raise errors.HypervisorError: when an instance cannot be found
251

252
    """
253
    alive = utils.IsProcessAlive(pid)
254
    if not alive:
255
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
256

    
257
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
258
    try:
259
      cmdline = utils.ReadFile(cmdline_file)
260
    except EnvironmentError, err:
261
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
262
                                   (pid, err))
263

    
264
    instance = None
265
    memory = 0
266
    vcpus = 0
267

    
268
    arg_list = cmdline.split("\x00")
269
    while arg_list:
270
      arg = arg_list.pop(0)
271
      if arg == "-name":
272
        instance = arg_list.pop(0)
273
      elif arg == "-m":
274
        memory = int(arg_list.pop(0))
275
      elif arg == "-smp":
276
        vcpus = int(arg_list.pop(0))
277

    
278
    if instance is None:
279
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
280
                                   " instance" % pid)
281

    
282
    return (instance, memory, vcpus)
283

    
284
  def _InstancePidAlive(self, instance_name):
285
    """Returns the instance pidfile, pid, and liveness.
286

287
    @type instance_name: string
288
    @param instance_name: instance name
289
    @rtype: tuple
290
    @return: (pid file name, pid, liveness)
291

292
    """
293
    pidfile = self._InstancePidFile(instance_name)
294
    pid = utils.ReadPidFile(pidfile)
295

    
296
    alive = False
297
    try:
298
      cmd_instance = self._InstancePidInfo(pid)[0]
299
      alive = (cmd_instance == instance_name)
300
    except errors.HypervisorError:
301
      pass
302

    
303
    return (pidfile, pid, alive)
304

    
305
  def _CheckDown(self, instance_name):
306
    """Raises an error unless the given instance is down.
307

308
    """
309
    alive = self._InstancePidAlive(instance_name)[2]
310
    if alive:
311
      raise errors.HypervisorError("Failed to start instance %s: %s" %
312
                                   (instance_name, "already running"))
313

    
314
  @classmethod
315
  def _InstanceMonitor(cls, instance_name):
316
    """Returns the instance monitor socket name
317

318
    """
319
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
320

    
321
  @classmethod
322
  def _InstanceSerial(cls, instance_name):
323
    """Returns the instance serial socket name
324

325
    """
326
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
327

    
328
  @staticmethod
329
  def _SocatUnixConsoleParams():
330
    """Returns the correct parameters for socat
331

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

334
    """
335
    if constants.SOCAT_USE_ESCAPE:
336
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
337
    else:
338
      return "echo=0,icanon=0"
339

    
340
  @classmethod
341
  def _InstanceKVMRuntime(cls, instance_name):
342
    """Returns the instance KVM runtime filename
343

344
    """
345
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
346

    
347
  @classmethod
348
  def _InstanceChrootDir(cls, instance_name):
349
    """Returns the name of the KVM chroot dir of the instance
350

351
    """
352
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
353

    
354
  @classmethod
355
  def _InstanceNICDir(cls, instance_name):
356
    """Returns the name of the directory holding the tap device files for a
357
    given instance.
358

359
    """
360
    return utils.PathJoin(cls._NICS_DIR, instance_name)
361

    
362
  @classmethod
363
  def _InstanceNICFile(cls, instance_name, seq):
364
    """Returns the name of the file containing the tap device for a given NIC
365

366
    """
367
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
368

    
369
  @classmethod
370
  def _InstanceKeymapFile(cls, instance_name):
371
    """Returns the name of the file containing the keymap for a given instance
372

373
    """
374
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
375

    
376
  @classmethod
377
  def _TryReadUidFile(cls, uid_file):
378
    """Try to read a uid file
379

380
    """
381
    if os.path.exists(uid_file):
382
      try:
383
        uid = int(utils.ReadOneLineFile(uid_file))
384
        return uid
385
      except EnvironmentError:
386
        logging.warning("Can't read uid file", exc_info=True)
387
      except (TypeError, ValueError):
388
        logging.warning("Can't parse uid file contents", exc_info=True)
389
    return None
390

    
391
  @classmethod
392
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
393
    """Removes an instance's rutime sockets/files/dirs.
394

395
    """
396
    utils.RemoveFile(pidfile)
397
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
398
    utils.RemoveFile(cls._InstanceSerial(instance_name))
399
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
400
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
401
    uid_file = cls._InstanceUidFile(instance_name)
402
    uid = cls._TryReadUidFile(uid_file)
403
    utils.RemoveFile(uid_file)
404
    if uid is not None:
405
      uidpool.ReleaseUid(uid)
406
    try:
407
      shutil.rmtree(cls._InstanceNICDir(instance_name))
408
    except OSError, err:
409
      if err.errno != errno.ENOENT:
410
        raise
411
    try:
412
      chroot_dir = cls._InstanceChrootDir(instance_name)
413
      utils.RemoveDir(chroot_dir)
414
    except OSError, err:
415
      if err.errno == errno.ENOTEMPTY:
416
        # The chroot directory is expected to be empty, but it isn't.
417
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
418
                                          prefix="%s-%s-" %
419
                                          (instance_name,
420
                                           utils.TimestampForFilename()))
421
        logging.warning("The chroot directory of instance %s can not be"
422
                        " removed as it is not empty. Moving it to the"
423
                        " quarantine instead. Please investigate the"
424
                        " contents (%s) and clean up manually",
425
                        instance_name, new_chroot_dir)
426
        utils.RenameFile(chroot_dir, new_chroot_dir)
427
      else:
428
        raise
429

    
430
  @staticmethod
431
  def _ConfigureNIC(instance, seq, nic, tap):
432
    """Run the network configuration script for a specified NIC
433

434
    @param instance: instance we're acting on
435
    @type instance: instance object
436
    @param seq: nic sequence number
437
    @type seq: int
438
    @param nic: nic we're acting on
439
    @type nic: nic object
440
    @param tap: the host's tap interface this NIC corresponds to
441
    @type tap: str
442

443
    """
444

    
445
    if instance.tags:
446
      tags = " ".join(instance.tags)
447
    else:
448
      tags = ""
449

    
450
    env = {
451
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
452
      "INSTANCE": instance.name,
453
      "MAC": nic.mac,
454
      "MODE": nic.nicparams[constants.NIC_MODE],
455
      "INTERFACE": tap,
456
      "INTERFACE_INDEX": str(seq),
457
      "TAGS": tags,
458
    }
459

    
460
    if nic.ip:
461
      env["IP"] = nic.ip
462

    
463
    if nic.nicparams[constants.NIC_LINK]:
464
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
465

    
466
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
467
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
468

    
469
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
470
    if result.failed:
471
      raise errors.HypervisorError("Failed to configure interface %s: %s."
472
                                   " Network configuration script output: %s" %
473
                                   (tap, result.fail_reason, result.output))
474

    
475
  def ListInstances(self):
476
    """Get the list of running instances.
477

478
    We can do this by listing our live instances directory and
479
    checking whether the associated kvm process is still alive.
480

481
    """
482
    result = []
483
    for name in os.listdir(self._PIDS_DIR):
484
      if self._InstancePidAlive(name)[2]:
485
        result.append(name)
486
    return result
487

    
488
  def GetInstanceInfo(self, instance_name):
489
    """Get instance properties.
490

491
    @type instance_name: string
492
    @param instance_name: the instance name
493
    @rtype: tuple of strings
494
    @return: (name, id, memory, vcpus, stat, times)
495

496
    """
497
    _, pid, alive = self._InstancePidAlive(instance_name)
498
    if not alive:
499
      return None
500

    
501
    _, memory, vcpus = self._InstancePidInfo(pid)
502
    stat = "---b-"
503
    times = "0"
504

    
505
    return (instance_name, pid, memory, vcpus, stat, times)
506

    
507
  def GetAllInstancesInfo(self):
508
    """Get properties of all instances.
509

510
    @return: list of tuples (name, id, memory, vcpus, stat, times)
511

512
    """
513
    data = []
514
    for name in os.listdir(self._PIDS_DIR):
515
      try:
516
        info = self.GetInstanceInfo(name)
517
      except errors.HypervisorError:
518
        continue
519
      if info:
520
        data.append(info)
521
    return data
522

    
523
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
524
    """Generate KVM information to start an instance.
525

526
    """
527
    _, v_major, v_min, _ = self._GetKVMVersion()
528

    
529
    pidfile = self._InstancePidFile(instance.name)
530
    kvm = constants.KVM_PATH
531
    kvm_cmd = [kvm]
532
    # used just by the vnc server, if enabled
533
    kvm_cmd.extend(["-name", instance.name])
534
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
535
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
536
    kvm_cmd.extend(["-pidfile", pidfile])
537
    kvm_cmd.extend(["-daemonize"])
538
    if not instance.hvparams[constants.HV_ACPI]:
539
      kvm_cmd.extend(["-no-acpi"])
540
    if startup_paused:
541
      kvm_cmd.extend(["-S"])
542
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
543
        constants.INSTANCE_REBOOT_EXIT:
544
      kvm_cmd.extend(["-no-reboot"])
545

    
546
    hvp = instance.hvparams
547
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
548
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
549
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
550
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
551

    
552
    self.ValidateParameters(hvp)
553

    
554
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
555
      kvm_cmd.extend(["-enable-kvm"])
556
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
557
      kvm_cmd.extend(["-disable-kvm"])
558

    
559
    if boot_network:
560
      kvm_cmd.extend(["-boot", "n"])
561

    
562
    disk_type = hvp[constants.HV_DISK_TYPE]
563
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
564
      if_val = ",if=virtio"
565
    else:
566
      if_val = ",if=%s" % disk_type
567
    # Cache mode
568
    disk_cache = hvp[constants.HV_DISK_CACHE]
569
    if instance.disk_template in constants.DTS_EXT_MIRROR:
570
      if disk_cache != "none":
571
        # TODO: make this a hard error, instead of a silent overwrite
572
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
573
                        " to prevent shared storage corruption on migration",
574
                        disk_cache)
575
      cache_val = ",cache=none"
576
    elif disk_cache != constants.HT_CACHE_DEFAULT:
577
      cache_val = ",cache=%s" % disk_cache
578
    else:
579
      cache_val = ""
580
    for cfdev, dev_path in block_devices:
581
      if cfdev.mode != constants.DISK_RDWR:
582
        raise errors.HypervisorError("Instance has read-only disks which"
583
                                     " are not supported by KVM")
584
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
585
      boot_val = ""
586
      if boot_disk:
587
        kvm_cmd.extend(["-boot", "c"])
588
        boot_disk = False
589
        if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
590
          boot_val = ",boot=on"
591

    
592
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
593
                                                cache_val)
594
      kvm_cmd.extend(["-drive", drive_val])
595

    
596
    #Now we can specify a different device type for CDROM devices.
597
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
598
    if not cdrom_disk_type:
599
      cdrom_disk_type = disk_type
600

    
601
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
602
    if iso_image:
603
      options = ",format=raw,media=cdrom"
604
      if boot_cdrom:
605
        kvm_cmd.extend(["-boot", "d"])
606
        if cdrom_disk_type != constants.HT_DISK_IDE:
607
          options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
608
        else:
609
          options = "%s,boot=on" % options
610
      else:
611
        if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
612
          if_val = ",if=virtio"
613
        else:
614
          if_val = ",if=%s" % cdrom_disk_type
615
        options = "%s%s" % (options, if_val)
616
      drive_val = "file=%s%s" % (iso_image, options)
617
      kvm_cmd.extend(["-drive", drive_val])
618

    
619
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
620
    if iso_image2:
621
      options = ",format=raw,media=cdrom"
622
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
623
        if_val = ",if=virtio"
624
      else:
625
        if_val = ",if=%s" % cdrom_disk_type
626
      options = "%s%s" % (options, if_val)
627
      drive_val = "file=%s%s" % (iso_image2, options)
628
      kvm_cmd.extend(["-drive", drive_val])
629

    
630
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
631
    if floppy_image:
632
      options = ",format=raw,media=disk"
633
      if boot_floppy:
634
        kvm_cmd.extend(["-boot", "a"])
635
        options = "%s,boot=on" % options
636
      if_val = ",if=floppy"
637
      options = "%s%s" % (options, if_val)
638
      drive_val = "file=%s%s" % (floppy_image, options)
639
      kvm_cmd.extend(["-drive", drive_val])
640

    
641
    kernel_path = hvp[constants.HV_KERNEL_PATH]
642
    if kernel_path:
643
      kvm_cmd.extend(["-kernel", kernel_path])
644
      initrd_path = hvp[constants.HV_INITRD_PATH]
645
      if initrd_path:
646
        kvm_cmd.extend(["-initrd", initrd_path])
647
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
648
                     hvp[constants.HV_KERNEL_ARGS]]
649
      if hvp[constants.HV_SERIAL_CONSOLE]:
650
        root_append.append("console=ttyS0,38400")
651
      kvm_cmd.extend(["-append", " ".join(root_append)])
652

    
653
    mem_path = hvp[constants.HV_MEM_PATH]
654
    if mem_path:
655
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
656

    
657
    mouse_type = hvp[constants.HV_USB_MOUSE]
658
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
659

    
660
    if mouse_type:
661
      kvm_cmd.extend(["-usb"])
662
      kvm_cmd.extend(["-usbdevice", mouse_type])
663
    elif vnc_bind_address:
664
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
665

    
666
    keymap = hvp[constants.HV_KEYMAP]
667
    if keymap:
668
      keymap_path = self._InstanceKeymapFile(instance.name)
669
      # If a keymap file is specified, KVM won't use its internal defaults. By
670
      # first including the "en-us" layout, an error on loading the actual
671
      # layout (e.g. because it can't be found) won't lead to a non-functional
672
      # keyboard. A keyboard with incorrect keys is still better than none.
673
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
674
      kvm_cmd.extend(["-k", keymap_path])
675

    
676
    if vnc_bind_address:
677
      if netutils.IP4Address.IsValid(vnc_bind_address):
678
        if instance.network_port > constants.VNC_BASE_PORT:
679
          display = instance.network_port - constants.VNC_BASE_PORT
680
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
681
            vnc_arg = ":%d" % (display)
682
          else:
683
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
684
        else:
685
          logging.error("Network port is not a valid VNC display (%d < %d)."
686
                        " Not starting VNC", instance.network_port,
687
                        constants.VNC_BASE_PORT)
688
          vnc_arg = "none"
689

    
690
        # Only allow tls and other option when not binding to a file, for now.
691
        # kvm/qemu gets confused otherwise about the filename to use.
692
        vnc_append = ""
693
        if hvp[constants.HV_VNC_TLS]:
694
          vnc_append = "%s,tls" % vnc_append
695
          if hvp[constants.HV_VNC_X509_VERIFY]:
696
            vnc_append = "%s,x509verify=%s" % (vnc_append,
697
                                               hvp[constants.HV_VNC_X509])
698
          elif hvp[constants.HV_VNC_X509]:
699
            vnc_append = "%s,x509=%s" % (vnc_append,
700
                                         hvp[constants.HV_VNC_X509])
701
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
702
          vnc_append = "%s,password" % vnc_append
703

    
704
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
705

    
706
      else:
707
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
708

    
709
      kvm_cmd.extend(["-vnc", vnc_arg])
710
    else:
711
      kvm_cmd.extend(["-nographic"])
712

    
713
    monitor_dev = ("unix:%s,server,nowait" %
714
                   self._InstanceMonitor(instance.name))
715
    kvm_cmd.extend(["-monitor", monitor_dev])
716
    if hvp[constants.HV_SERIAL_CONSOLE]:
717
      serial_dev = ("unix:%s,server,nowait" %
718
                    self._InstanceSerial(instance.name))
719
      kvm_cmd.extend(["-serial", serial_dev])
720
    else:
721
      kvm_cmd.extend(["-serial", "none"])
722

    
723
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
724
    spice_ip_version = None
725
    if spice_bind:
726
      if netutils.IsValidInterface(spice_bind):
727
        # The user specified a network interface, we have to figure out the IP
728
        # address.
729
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
730
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
731

    
732
        # if the user specified an IP version and the interface does not
733
        # have that kind of IP addresses, throw an exception
734
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
735
          if not addresses[spice_ip_version]:
736
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
737
                                         " for %s" % (spice_ip_version,
738
                                                      spice_bind))
739

    
740
        # the user did not specify an IP version, we have to figure it out
741
        elif (addresses[constants.IP4_VERSION] and
742
              addresses[constants.IP6_VERSION]):
743
          # we have both ipv4 and ipv6, let's use the cluster default IP
744
          # version
745
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
746
          spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
747
              cluster_family)
748
        elif addresses[constants.IP4_VERSION]:
749
          spice_ip_version = constants.IP4_VERSION
750
        elif addresses[constants.IP6_VERSION]:
751
          spice_ip_version = constants.IP6_VERSION
752
        else:
753
          raise errors.HypervisorError("spice: unable to get an IP address"
754
                                       " for %s" % (spice_bind))
755

    
756
        spice_address = addresses[spice_ip_version][0]
757

    
758
      else:
759
        # spice_bind is known to be a valid IP address, because
760
        # ValidateParameters checked it.
761
        spice_address = spice_bind
762

    
763
      spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address,
764
                                                         instance.network_port)
765
      if spice_ip_version:
766
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
767

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

    
771
    if hvp[constants.HV_USE_LOCALTIME]:
772
      kvm_cmd.extend(["-localtime"])
773

    
774
    if hvp[constants.HV_KVM_USE_CHROOT]:
775
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
776

    
777
    # Save the current instance nics, but defer their expansion as parameters,
778
    # as we'll need to generate executable temp files for them.
779
    kvm_nics = instance.nics
780
    hvparams = hvp
781

    
782
    return (kvm_cmd, kvm_nics, hvparams)
783

    
784
  def _WriteKVMRuntime(self, instance_name, data):
785
    """Write an instance's KVM runtime
786

787
    """
788
    try:
789
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
790
                      data=data)
791
    except EnvironmentError, err:
792
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
793

    
794
  def _ReadKVMRuntime(self, instance_name):
795
    """Read an instance's KVM runtime
796

797
    """
798
    try:
799
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
800
    except EnvironmentError, err:
801
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
802
    return file_content
803

    
804
  def _SaveKVMRuntime(self, instance, kvm_runtime):
805
    """Save an instance's KVM runtime
806

807
    """
808
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
809
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
810
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
811
    self._WriteKVMRuntime(instance.name, serialized_form)
812

    
813
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
814
    """Load an instance's KVM runtime
815

816
    """
817
    if not serialized_runtime:
818
      serialized_runtime = self._ReadKVMRuntime(instance.name)
819
    loaded_runtime = serializer.Load(serialized_runtime)
820
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
821
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
822
    return (kvm_cmd, kvm_nics, hvparams)
823

    
824
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
825
    """Run the KVM cmd and check for errors
826

827
    @type name: string
828
    @param name: instance name
829
    @type kvm_cmd: list of strings
830
    @param kvm_cmd: runcmd input for kvm
831
    @type tap_fds: list of int
832
    @param tap_fds: fds of tap devices opened by Ganeti
833

834
    """
835
    try:
836
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
837
    finally:
838
      for fd in tap_fds:
839
        utils_wrapper.CloseFdNoError(fd)
840

    
841
    if result.failed:
842
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
843
                                   (name, result.fail_reason, result.output))
844
    if not self._InstancePidAlive(name)[2]:
845
      raise errors.HypervisorError("Failed to start instance %s" % name)
846

    
847
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
848
    """Execute a KVM cmd, after completing it with some last minute data
849

850
    @type incoming: tuple of strings
851
    @param incoming: (target_host_ip, port)
852

853
    """
854
    # Small _ExecuteKVMRuntime hv parameters programming howto:
855
    #  - conf_hvp contains the parameters as configured on ganeti. they might
856
    #    have changed since the instance started; only use them if the change
857
    #    won't affect the inside of the instance (which hasn't been rebooted).
858
    #  - up_hvp contains the parameters as they were when the instance was
859
    #    started, plus any new parameter which has been added between ganeti
860
    #    versions: it is paramount that those default to a value which won't
861
    #    affect the inside of the instance as well.
862
    conf_hvp = instance.hvparams
863
    name = instance.name
864
    self._CheckDown(name)
865

    
866
    temp_files = []
867

    
868
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
869
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
870

    
871
    _, v_major, v_min, _ = self._GetKVMVersion()
872

    
873
    # We know it's safe to run as a different user upon migration, so we'll use
874
    # the latest conf, from conf_hvp.
875
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
876
    if security_model == constants.HT_SM_USER:
877
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
878

    
879
    # We have reasons to believe changing something like the nic driver/type
880
    # upon migration won't exactly fly with the instance kernel, so for nic
881
    # related parameters we'll use up_hvp
882
    tapfds = []
883
    taps = []
884
    if not kvm_nics:
885
      kvm_cmd.extend(["-net", "none"])
886
    else:
887
      vnet_hdr = False
888
      tap_extra = ""
889
      nic_type = up_hvp[constants.HV_NIC_TYPE]
890
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
891
        # From version 0.12.0, kvm uses a new sintax for network configuration.
892
        if (v_major, v_min) >= (0, 12):
893
          nic_model = "virtio-net-pci"
894
          vnet_hdr = True
895
        else:
896
          nic_model = "virtio"
897

    
898
        if up_hvp[constants.HV_VHOST_NET]:
899
          # vhost_net is only available from version 0.13.0 or newer
900
          if (v_major, v_min) >= (0, 13):
901
            tap_extra = ",vhost=on"
902
          else:
903
            raise errors.HypervisorError("vhost_net is configured"
904
                                        " but it is not available")
905
      else:
906
        nic_model = nic_type
907

    
908
      for nic_seq, nic in enumerate(kvm_nics):
909
        tapname, tapfd = _OpenTap(vnet_hdr)
910
        tapfds.append(tapfd)
911
        taps.append(tapname)
912
        if (v_major, v_min) >= (0, 12):
913
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
914
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
915
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
916
        else:
917
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
918
                                                         nic.mac, nic_model)
919
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
920
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
921

    
922
    if incoming:
923
      target, port = incoming
924
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
925

    
926
    # Changing the vnc password doesn't bother the guest that much. At most it
927
    # will surprise people who connect to it. Whether positively or negatively
928
    # it's debatable.
929
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
930
    vnc_pwd = None
931
    if vnc_pwd_file:
932
      try:
933
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
934
      except EnvironmentError, err:
935
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
936
                                     % (vnc_pwd_file, err))
937

    
938
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
939
      utils.EnsureDirs([(self._InstanceChrootDir(name),
940
                         constants.SECURE_DIR_MODE)])
941

    
942
    # Configure the network now for starting instances and bridged interfaces,
943
    # during FinalizeMigration for incoming instances' routed interfaces
944
    for nic_seq, nic in enumerate(kvm_nics):
945
      if (incoming and
946
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
947
        continue
948
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
949

    
950
    if security_model == constants.HT_SM_POOL:
951
      ss = ssconf.SimpleStore()
952
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
953
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
954
      uid = uidpool.RequestUnusedUid(all_uids)
955
      try:
956
        username = pwd.getpwuid(uid.GetUid()).pw_name
957
        kvm_cmd.extend(["-runas", username])
958
        self._RunKVMCmd(name, kvm_cmd, tapfds)
959
      except:
960
        uidpool.ReleaseUid(uid)
961
        raise
962
      else:
963
        uid.Unlock()
964
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
965
    else:
966
      self._RunKVMCmd(name, kvm_cmd, tapfds)
967

    
968
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
969
                     constants.RUN_DIRS_MODE)])
970
    for nic_seq, tap in enumerate(taps):
971
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
972
                      data=tap)
973

    
974
    if vnc_pwd:
975
      change_cmd = "change vnc password %s" % vnc_pwd
976
      self._CallMonitorCommand(instance.name, change_cmd)
977

    
978
    for filename in temp_files:
979
      utils.RemoveFile(filename)
980

    
981
  def StartInstance(self, instance, block_devices, startup_paused):
982
    """Start an instance.
983

984
    """
985
    self._CheckDown(instance.name)
986
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
987
                                           startup_paused)
988
    self._SaveKVMRuntime(instance, kvm_runtime)
989
    self._ExecuteKVMRuntime(instance, kvm_runtime)
990

    
991
  def _CallMonitorCommand(self, instance_name, command):
992
    """Invoke a command on the instance monitor.
993

994
    """
995
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
996
             (utils.ShellQuote(command),
997
              constants.SOCAT_PATH,
998
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
999
    result = utils.RunCmd(socat)
1000
    if result.failed:
1001
      msg = ("Failed to send command '%s' to instance %s."
1002
             " output: %s, error: %s, fail_reason: %s" %
1003
             (command, instance_name,
1004
              result.stdout, result.stderr, result.fail_reason))
1005
      raise errors.HypervisorError(msg)
1006

    
1007
    return result
1008

    
1009
  @classmethod
1010
  def _GetKVMVersion(cls):
1011
    """Return the installed KVM version.
1012

1013
    @return: (version, v_maj, v_min, v_rev)
1014
    @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1015

1016
    """
1017
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1018
    if result.failed:
1019
      raise errors.HypervisorError("Unable to get KVM version")
1020
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
1021
    if not match:
1022
      raise errors.HypervisorError("Unable to get KVM version")
1023

    
1024
    return (match.group(0), int(match.group(1)), int(match.group(2)),
1025
            int(match.group(3)))
1026

    
1027
  def StopInstance(self, instance, force=False, retry=False, name=None):
1028
    """Stop an instance.
1029

1030
    """
1031
    if name is not None and not force:
1032
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1033
    if name is None:
1034
      name = instance.name
1035
      acpi = instance.hvparams[constants.HV_ACPI]
1036
    else:
1037
      acpi = False
1038
    _, pid, alive = self._InstancePidAlive(name)
1039
    if pid > 0 and alive:
1040
      if force or not acpi:
1041
        utils.KillProcess(pid)
1042
      else:
1043
        self._CallMonitorCommand(name, "system_powerdown")
1044

    
1045
  def CleanupInstance(self, instance_name):
1046
    """Cleanup after a stopped instance
1047

1048
    """
1049
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1050
    if pid > 0 and alive:
1051
      raise errors.HypervisorError("Cannot cleanup a live instance")
1052
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1053

    
1054
  def RebootInstance(self, instance):
1055
    """Reboot an instance.
1056

1057
    """
1058
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1059
    # socket the instance will stop, but now power up again. So we'll resort
1060
    # to shutdown and restart.
1061
    _, _, alive = self._InstancePidAlive(instance.name)
1062
    if not alive:
1063
      raise errors.HypervisorError("Failed to reboot instance %s:"
1064
                                   " not running" % instance.name)
1065
    # StopInstance will delete the saved KVM runtime so:
1066
    # ...first load it...
1067
    kvm_runtime = self._LoadKVMRuntime(instance)
1068
    # ...now we can safely call StopInstance...
1069
    if not self.StopInstance(instance):
1070
      self.StopInstance(instance, force=True)
1071
    # ...and finally we can save it again, and execute it...
1072
    self._SaveKVMRuntime(instance, kvm_runtime)
1073
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1074

    
1075
  def MigrationInfo(self, instance):
1076
    """Get instance information to perform a migration.
1077

1078
    @type instance: L{objects.Instance}
1079
    @param instance: instance to be migrated
1080
    @rtype: string
1081
    @return: content of the KVM runtime file
1082

1083
    """
1084
    return self._ReadKVMRuntime(instance.name)
1085

    
1086
  def AcceptInstance(self, instance, info, target):
1087
    """Prepare to accept an instance.
1088

1089
    @type instance: L{objects.Instance}
1090
    @param instance: instance to be accepted
1091
    @type info: string
1092
    @param info: content of the KVM runtime file on the source node
1093
    @type target: string
1094
    @param target: target host (usually ip), on this node
1095

1096
    """
1097
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1098
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1099
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1100

    
1101
  def FinalizeMigration(self, instance, info, success):
1102
    """Finalize an instance migration.
1103

1104
    Stop the incoming mode KVM.
1105

1106
    @type instance: L{objects.Instance}
1107
    @param instance: instance whose migration is being finalized
1108

1109
    """
1110
    if success:
1111
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1112
      kvm_nics = kvm_runtime[1]
1113

    
1114
      for nic_seq, nic in enumerate(kvm_nics):
1115
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1116
          # Bridged interfaces have already been configured
1117
          continue
1118
        try:
1119
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1120
        except EnvironmentError, err:
1121
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1122
                          instance.name, nic_seq, str(err))
1123
          continue
1124
        try:
1125
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1126
        except errors.HypervisorError, err:
1127
          logging.warning(str(err))
1128

    
1129
      self._WriteKVMRuntime(instance.name, info)
1130
    else:
1131
      self.StopInstance(instance, force=True)
1132

    
1133
  def MigrateInstance(self, instance, target, live):
1134
    """Migrate an instance to a target node.
1135

1136
    The migration will not be attempted if the instance is not
1137
    currently running.
1138

1139
    @type instance: L{objects.Instance}
1140
    @param instance: the instance to be migrated
1141
    @type target: string
1142
    @param target: ip address of the target node
1143
    @type live: boolean
1144
    @param live: perform a live migration
1145

1146
    """
1147
    instance_name = instance.name
1148
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1149
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1150
    if not alive:
1151
      raise errors.HypervisorError("Instance not running, cannot migrate")
1152

    
1153
    if not live:
1154
      self._CallMonitorCommand(instance_name, "stop")
1155

    
1156
    migrate_command = ("migrate_set_speed %dm" %
1157
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1158
    self._CallMonitorCommand(instance_name, migrate_command)
1159

    
1160
    migrate_command = ("migrate_set_downtime %dms" %
1161
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1162
    self._CallMonitorCommand(instance_name, migrate_command)
1163

    
1164
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1165
    self._CallMonitorCommand(instance_name, migrate_command)
1166

    
1167
    info_command = "info migrate"
1168
    done = False
1169
    broken_answers = 0
1170
    while not done:
1171
      result = self._CallMonitorCommand(instance_name, info_command)
1172
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1173
      if not match:
1174
        broken_answers += 1
1175
        if not result.stdout:
1176
          logging.info("KVM: empty 'info migrate' result")
1177
        else:
1178
          logging.warning("KVM: unknown 'info migrate' result: %s",
1179
                          result.stdout)
1180
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1181
      else:
1182
        status = match.group(1)
1183
        if status == "completed":
1184
          done = True
1185
        elif status == "active":
1186
          # reset the broken answers count
1187
          broken_answers = 0
1188
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1189
        elif status == "failed" or status == "cancelled":
1190
          if not live:
1191
            self._CallMonitorCommand(instance_name, 'cont')
1192
          raise errors.HypervisorError("Migration %s at the kvm level" %
1193
                                       status)
1194
        else:
1195
          logging.warning("KVM: unknown migration status '%s'", status)
1196
          broken_answers += 1
1197
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1198
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1199
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1200

    
1201
    utils.KillProcess(pid)
1202
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1203

    
1204
  def GetNodeInfo(self):
1205
    """Return information about the node.
1206

1207
    This is just a wrapper over the base GetLinuxNodeInfo method.
1208

1209
    @return: a dict with the following keys (values in MiB):
1210
          - memory_total: the total memory size on the node
1211
          - memory_free: the available memory on the node for instances
1212
          - memory_dom0: the memory used by the node itself, if available
1213

1214
    """
1215
    return self.GetLinuxNodeInfo()
1216

    
1217
  @classmethod
1218
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1219
    """Return a command for connecting to the console of an instance.
1220

1221
    """
1222
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1223
      cmd = [constants.KVM_CONSOLE_WRAPPER,
1224
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1225
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1226
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1227
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1228
      return objects.InstanceConsole(instance=instance.name,
1229
                                     kind=constants.CONS_SSH,
1230
                                     host=instance.primary_node,
1231
                                     user=constants.GANETI_RUNAS,
1232
                                     command=cmd)
1233

    
1234
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1235
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1236
      display = instance.network_port - constants.VNC_BASE_PORT
1237
      return objects.InstanceConsole(instance=instance.name,
1238
                                     kind=constants.CONS_VNC,
1239
                                     host=vnc_bind_address,
1240
                                     port=instance.network_port,
1241
                                     display=display)
1242

    
1243
    return objects.InstanceConsole(instance=instance.name,
1244
                                   kind=constants.CONS_MESSAGE,
1245
                                   message=("No serial shell for instance %s" %
1246
                                            instance.name))
1247

    
1248
  def Verify(self):
1249
    """Verify the hypervisor.
1250

1251
    Check that the binary exists.
1252

1253
    """
1254
    if not os.path.exists(constants.KVM_PATH):
1255
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1256
    if not os.path.exists(constants.SOCAT_PATH):
1257
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1258

    
1259
  @classmethod
1260
  def CheckParameterSyntax(cls, hvparams):
1261
    """Check the given parameters for validity.
1262

1263
    @type hvparams:  dict
1264
    @param hvparams: dictionary with parameter names/value
1265
    @raise errors.HypervisorError: when a parameter is not valid
1266

1267
    """
1268
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1269

    
1270
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1271
    if kernel_path:
1272
      if not hvparams[constants.HV_ROOT_PATH]:
1273
        raise errors.HypervisorError("Need a root partition for the instance,"
1274
                                     " if a kernel is defined")
1275

    
1276
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1277
        not hvparams[constants.HV_VNC_X509]):
1278
      raise errors.HypervisorError("%s must be defined, if %s is" %
1279
                                   (constants.HV_VNC_X509,
1280
                                    constants.HV_VNC_X509_VERIFY))
1281

    
1282
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1283
    if (boot_order == constants.HT_BO_CDROM and
1284
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1285
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1286
                                   " ISO path")
1287

    
1288
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1289
    if security_model == constants.HT_SM_USER:
1290
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1291
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1292
                                     " must be specified")
1293
    elif (security_model == constants.HT_SM_NONE or
1294
          security_model == constants.HT_SM_POOL):
1295
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1296
        raise errors.HypervisorError("Cannot have a security domain when the"
1297
                                     " security model is 'none' or 'pool'")
1298

    
1299
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1300
    if spice_bind:
1301
      spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1302
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1303
        # if an IP version is specified, the spice_bind parameter must be an
1304
        # IP of that family
1305
        if (netutils.IP4Address.IsValid(spice_bind) and
1306
            spice_ip_version != constants.IP4_VERSION):
1307
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1308
                                       " the specified IP version is %s" %
1309
                                       (spice_bind, spice_ip_version))
1310

    
1311
        if (netutils.IP6Address.IsValid(spice_bind) and
1312
            spice_ip_version != constants.IP6_VERSION):
1313
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1314
                                       " the specified IP version is %s" %
1315
                                       (spice_bind, spice_ip_version))
1316

    
1317
  @classmethod
1318
  def ValidateParameters(cls, hvparams):
1319
    """Check the given parameters for validity.
1320

1321
    @type hvparams:  dict
1322
    @param hvparams: dictionary with parameter names/value
1323
    @raise errors.HypervisorError: when a parameter is not valid
1324

1325
    """
1326
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1327

    
1328
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1329
    if security_model == constants.HT_SM_USER:
1330
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1331
      try:
1332
        pwd.getpwnam(username)
1333
      except KeyError:
1334
        raise errors.HypervisorError("Unknown security domain user %s"
1335
                                     % username)
1336

    
1337
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1338
    if spice_bind:
1339
      # only one of VNC and SPICE can be used currently.
1340
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1341
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
1342
                                     " only one of them can be used at a"
1343
                                     " given time.")
1344

    
1345
      # KVM version should be >= 0.14.0
1346
      _, v_major, v_min, _ = cls._GetKVMVersion()
1347
      if (v_major, v_min) < (0, 14):
1348
        raise errors.HypervisorError("spice is configured, but it is not"
1349
                                     " available in versions of KVM < 0.14")
1350

    
1351
      # if spice_bind is not an IP address, it must be a valid interface
1352
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1353
                       or netutils.IP6Address.IsValid(spice_bind))
1354
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1355
        raise errors.HypervisorError("spice: the %s parameter must be either"
1356
                                     " a valid IP address or interface name" %
1357
                                     constants.HV_KVM_SPICE_BIND)
1358

    
1359
  @classmethod
1360
  def PowercycleNode(cls):
1361
    """KVM powercycle, just a wrapper over Linux powercycle.
1362

1363
    """
1364
    cls.LinuxPowercycle()