hv_kvm.py 44.6 KB
Newer Older
Guido Trotter's avatar
Guido Trotter committed
1
2
3
#
#

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


"""KVM hypervisor

"""

Balazs Lecz's avatar
Balazs Lecz committed
26
import errno
Guido Trotter's avatar
Guido Trotter committed
27
28
29
30
import os
import os.path
import re
import tempfile
31
import time
Guido Trotter's avatar
Guido Trotter committed
32
import logging
33
import pwd
34
35
import struct
import fcntl
36
import shutil
Guido Trotter's avatar
Guido Trotter committed
37
38
39
40

from ganeti import utils
from ganeti import constants
from ganeti import errors
41
42
from ganeti import serializer
from ganeti import objects
43
44
from ganeti import uidpool
from ganeti import ssconf
Guido Trotter's avatar
Guido Trotter committed
45
from ganeti.hypervisor import hv_base
46
from ganeti import netutils
47
from ganeti.utils import wrapper as utils_wrapper
Guido Trotter's avatar
Guido Trotter committed
48
49


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

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# TUN/TAP driver constants, taken from <linux/if_tun.h>
# They are architecture-independent and already hardcoded in qemu-kvm source,
# so we can safely include them here.
TUNSETIFF = 0x400454ca
TUNGETIFF = 0x800454d2
TUNGETFEATURES = 0x800454cf
IFF_TAP = 0x0002
IFF_NO_PI = 0x1000
IFF_VNET_HDR = 0x4000


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

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

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

  """
  req = struct.pack("I", 0)
  try:
    res = fcntl.ioctl(fd, TUNGETFEATURES, req)
  except EnvironmentError:
    logging.warning("TUNGETFEATURES ioctl() not implemented")
    return False

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


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

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

  @type vnet_hdr: boolean
  @param vnet_hdr: Enable the VNET Header
  @return: (ifname, tapfd)
  @rtype: tuple

  """
  try:
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
  except EnvironmentError:
    raise errors.HypervisorError("Failed to open /dev/net/tun")

  flags = IFF_TAP | IFF_NO_PI

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

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

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

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

129

Guido Trotter's avatar
Guido Trotter committed
130
class KVMHypervisor(hv_base.BaseHypervisor):
Guido Trotter's avatar
Guido Trotter committed
131
  """KVM hypervisor interface"""
132
  CAN_MIGRATE = True
Guido Trotter's avatar
Guido Trotter committed
133
134

  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
Guido Trotter's avatar
Guido Trotter committed
135
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
136
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
Guido Trotter's avatar
Guido Trotter committed
137
138
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
  _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
Balazs Lecz's avatar
Balazs Lecz committed
141
142
143
144
145
146
147
148
  # KVM instances with chroot enabled are started in empty chroot directories.
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
  # After an instance is stopped, its chroot directory is removed.
  # If the chroot directory is not empty, it can't be removed.
  # A non-empty chroot directory indicates a possible security incident.
  # To support forensics, the non-empty chroot directory is quarantined in
  # a separate directory, called 'chroot-quarantine'.
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
149
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
Balazs Lecz's avatar
Balazs Lecz committed
150
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
Guido Trotter's avatar
Guido Trotter committed
151

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

Iustin Pop's avatar
Iustin Pop committed
201
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
Guido Trotter's avatar
Guido Trotter committed
202
                                    re.M | re.I)
203
204
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
  _MIGRATION_INFO_RETRY_DELAY = 2
Guido Trotter's avatar
Guido Trotter committed
205

206
207
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")

208
209
210
211
  ANCILLARY_FILES = [
    _KVM_NETWORK_SCRIPT,
    ]

Guido Trotter's avatar
Guido Trotter committed
212
213
214
215
  def __init__(self):
    hv_base.BaseHypervisor.__init__(self)
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
    # in a tmpfs filesystem or has been otherwise wiped out.
Iustin Pop's avatar
Iustin Pop committed
216
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
Guido Trotter's avatar
Guido Trotter committed
217
    utils.EnsureDirs(dirs)
Guido Trotter's avatar
Guido Trotter committed
218

219
220
  @classmethod
  def _InstancePidFile(cls, instance_name):
221
222
223
    """Returns the instance pidfile.

    """
224
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
225

226
227
228
229
230
231
232
  @classmethod
  def _InstanceUidFile(cls, instance_name):
    """Returns the instance uidfile.

    """
    return utils.PathJoin(cls._UIDS_DIR, instance_name)

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  @classmethod
  def _InstancePidInfo(cls, pid):
    """Check pid file for instance information.

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

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

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

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

    instance = None
    memory = 0
    vcpus = 0

Iustin Pop's avatar
Iustin Pop committed
262
    arg_list = cmdline.split("\x00")
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
    while arg_list:
      arg =  arg_list.pop(0)
      if arg == "-name":
        instance = arg_list.pop(0)
      elif arg == "-m":
        memory = int(arg_list.pop(0))
      elif arg == "-smp":
        vcpus = int(arg_list.pop(0))

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

    return (instance, memory, vcpus)

278
  def _InstancePidAlive(self, instance_name):
279
280
281
282
283
284
    """Returns the instance pidfile, pid, and liveness.

    @type instance_name: string
    @param instance_name: instance name
    @rtype: tuple
    @return: (pid file name, pid, liveness)
285
286

    """
287
    pidfile = self._InstancePidFile(instance_name)
288
    pid = utils.ReadPidFile(pidfile)
289
290
291
292
293
294
295

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

    return (pidfile, pid, alive)

299
300
301
302
303
304
305
306
307
  def _CheckDown(self, instance_name):
    """Raises an error unless the given instance is down.

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

308
309
  @classmethod
  def _InstanceMonitor(cls, instance_name):
310
311
312
    """Returns the instance monitor socket name

    """
313
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
314

315
316
  @classmethod
  def _InstanceSerial(cls, instance_name):
317
318
319
    """Returns the instance serial socket name

    """
320
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
321

322
323
324
325
326
327
328
  @staticmethod
  def _SocatUnixConsoleParams():
    """Returns the correct parameters for socat

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

    """
329
    if constants.SOCAT_USE_ESCAPE:
330
331
332
333
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
    else:
      return "echo=0,icanon=0"

334
335
  @classmethod
  def _InstanceKVMRuntime(cls, instance_name):
336
337
338
    """Returns the instance KVM runtime filename

    """
339
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
340

Balazs Lecz's avatar
Balazs Lecz committed
341
342
343
344
345
346
347
  @classmethod
  def _InstanceChrootDir(cls, instance_name):
    """Returns the name of the KVM chroot dir of the instance

    """
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  @classmethod
  def _InstanceNICDir(cls, instance_name):
    """Returns the name of the directory holding the tap device files for a
    given instance.

    """
    return utils.PathJoin(cls._NICS_DIR, instance_name)

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

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

363
364
365
366
367
368
369
  @classmethod
  def _InstanceKeymapFile(cls, instance_name):
    """Returns the name of the file containing the keymap for a given instance

    """
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)

370
371
372
373
374
375
376
  @classmethod
  def _TryReadUidFile(cls, uid_file):
    """Try to read a uid file

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

385
386
  @classmethod
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
Balazs Lecz's avatar
Balazs Lecz committed
387
    """Removes an instance's rutime sockets/files/dirs.
388
389
390
391
392
393

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

424
  @staticmethod
425
426
  def _ConfigureNIC(instance, seq, nic, tap):
    """Run the network configuration script for a specified NIC
Guido Trotter's avatar
Guido Trotter committed
427
428
429
430
431
432
433

    @param instance: instance we're acting on
    @type instance: instance object
    @param seq: nic sequence number
    @type seq: int
    @param nic: nic we're acting on
    @type nic: nic object
434
435
    @param tap: the host's tap interface this NIC corresponds to
    @type tap: str
Guido Trotter's avatar
Guido Trotter committed
436
437

    """
438

439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
    if instance.tags:
      tags = " ".join(instance.tags)
    else:
      tags = ""

    env = {
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
      "INSTANCE": instance.name,
      "MAC": nic.mac,
      "MODE": nic.nicparams[constants.NIC_MODE],
      "INTERFACE": tap,
      "INTERFACE_INDEX": str(seq),
      "TAGS": tags,
    }

    if nic.ip:
      env["IP"] = nic.ip

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

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

    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
    if result.failed:
      raise errors.HypervisorError("Failed to configure interface %s: %s."
                                   " Network configuration script output: %s" %
                                   (tap, result.fail_reason, result.output))
Guido Trotter's avatar
Guido Trotter committed
468
469
470
471

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

Iustin Pop's avatar
Iustin Pop committed
472
473
    We can do this by listing our live instances directory and
    checking whether the associated kvm process is still alive.
Guido Trotter's avatar
Guido Trotter committed
474
475
476
477

    """
    result = []
    for name in os.listdir(self._PIDS_DIR):
478
      if self._InstancePidAlive(name)[2]:
Guido Trotter's avatar
Guido Trotter committed
479
480
481
482
483
484
        result.append(name)
    return result

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

485
    @type instance_name: string
Iustin Pop's avatar
Iustin Pop committed
486
    @param instance_name: the instance name
487
488
    @rtype: tuple of strings
    @return: (name, id, memory, vcpus, stat, times)
Guido Trotter's avatar
Guido Trotter committed
489
490

    """
491
    _, pid, alive = self._InstancePidAlive(instance_name)
492
    if not alive:
Guido Trotter's avatar
Guido Trotter committed
493
494
      return None

495
    _, memory, vcpus = self._InstancePidInfo(pid)
Guido Trotter's avatar
Guido Trotter committed
496
497
498
499
500
501
502
503
    stat = "---b-"
    times = "0"

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

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

Iustin Pop's avatar
Iustin Pop committed
504
505
    @return: list of tuples (name, id, memory, vcpus, stat, times)

Guido Trotter's avatar
Guido Trotter committed
506
507
508
    """
    data = []
    for name in os.listdir(self._PIDS_DIR):
509
510
511
512
513
514
      try:
        info = self.GetInstanceInfo(name)
      except errors.HypervisorError:
        continue
      if info:
        data.append(info)
Guido Trotter's avatar
Guido Trotter committed
515
516
    return data

517
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
518
    """Generate KVM information to start an instance.
Guido Trotter's avatar
Guido Trotter committed
519
520

    """
521
    _, v_major, v_min, _ = self._GetKVMVersion()
522

523
    pidfile  = self._InstancePidFile(instance.name)
Guido Trotter's avatar
Guido Trotter committed
524
525
    kvm = constants.KVM_PATH
    kvm_cmd = [kvm]
526
    # used just by the vnc server, if enabled
Iustin Pop's avatar
Iustin Pop committed
527
528
529
530
531
    kvm_cmd.extend(["-name", instance.name])
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
    kvm_cmd.extend(["-pidfile", pidfile])
    kvm_cmd.extend(["-daemonize"])
532
    if not instance.hvparams[constants.HV_ACPI]:
Iustin Pop's avatar
Iustin Pop committed
533
      kvm_cmd.extend(["-no-acpi"])
534
    if startup_paused:
Iustin Pop's avatar
Iustin Pop committed
535
      kvm_cmd.extend(["-S"])
536
537
538
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
        constants.INSTANCE_REBOOT_EXIT:
      kvm_cmd.extend(["-no-reboot"])
Guido Trotter's avatar
Guido Trotter committed
539

540
    hvp = instance.hvparams
541
542
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
543
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
544
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
Guido Trotter's avatar
Guido Trotter committed
545

Guido Trotter's avatar
Guido Trotter committed
546
547
548
549
550
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
      kvm_cmd.extend(["-enable-kvm"])
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
      kvm_cmd.extend(["-disable-kvm"])

Guido Trotter's avatar
Guido Trotter committed
551
    if boot_network:
Iustin Pop's avatar
Iustin Pop committed
552
      kvm_cmd.extend(["-boot", "n"])
553

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

Iustin Pop's avatar
Iustin Pop committed
584
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
585
                                                cache_val)
Iustin Pop's avatar
Iustin Pop committed
586
      kvm_cmd.extend(["-drive", drive_val])
Guido Trotter's avatar
Guido Trotter committed
587

588
589
590
591
592
    #Now we can specify a different device type for CDROM devices.
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
    if not cdrom_disk_type:
      cdrom_disk_type = disk_type

593
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
594
    if iso_image:
Iustin Pop's avatar
Iustin Pop committed
595
      options = ",format=raw,media=cdrom"
596
      if boot_cdrom:
Iustin Pop's avatar
Iustin Pop committed
597
        kvm_cmd.extend(["-boot", "d"])
598
        if cdrom_disk_type != constants.HT_DISK_IDE:
Iustin Pop's avatar
Iustin Pop committed
599
          options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
600
        else:
Iustin Pop's avatar
Iustin Pop committed
601
          options = "%s,boot=on" % options
602
      else:
603
        if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
Iustin Pop's avatar
Iustin Pop committed
604
          if_val = ",if=virtio"
605
        else:
Iustin Pop's avatar
Iustin Pop committed
606
607
608
609
          if_val = ",if=%s" % cdrom_disk_type
        options = "%s%s" % (options, if_val)
      drive_val = "file=%s%s" % (iso_image, options)
      kvm_cmd.extend(["-drive", drive_val])
610

611
612
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
    if iso_image2:
Iustin Pop's avatar
Iustin Pop committed
613
      options = ",format=raw,media=cdrom"
614
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
Iustin Pop's avatar
Iustin Pop committed
615
        if_val = ",if=virtio"
616
      else:
Iustin Pop's avatar
Iustin Pop committed
617
618
619
620
        if_val = ",if=%s" % cdrom_disk_type
      options = "%s%s" % (options, if_val)
      drive_val = "file=%s%s" % (iso_image2, options)
      kvm_cmd.extend(["-drive", drive_val])
621
622
623

    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
    if floppy_image:
Iustin Pop's avatar
Iustin Pop committed
624
      options = ",format=raw,media=disk"
625
      if boot_floppy:
Iustin Pop's avatar
Iustin Pop committed
626
627
628
629
630
631
        kvm_cmd.extend(["-boot", "a"])
        options = "%s,boot=on" % options
      if_val = ",if=floppy"
      options = "%s%s" % (options, if_val)
      drive_val = "file=%s%s" % (floppy_image, options)
      kvm_cmd.extend(["-drive", drive_val])
632

633
    kernel_path = hvp[constants.HV_KERNEL_PATH]
634
    if kernel_path:
Iustin Pop's avatar
Iustin Pop committed
635
      kvm_cmd.extend(["-kernel", kernel_path])
636
      initrd_path = hvp[constants.HV_INITRD_PATH]
637
      if initrd_path:
Iustin Pop's avatar
Iustin Pop committed
638
639
        kvm_cmd.extend(["-initrd", initrd_path])
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
640
641
                     hvp[constants.HV_KERNEL_ARGS]]
      if hvp[constants.HV_SERIAL_CONSOLE]:
Iustin Pop's avatar
Iustin Pop committed
642
643
        root_append.append("console=ttyS0,38400")
      kvm_cmd.extend(["-append", " ".join(root_append)])
Guido Trotter's avatar
Guido Trotter committed
644

645
646
    mem_path = hvp[constants.HV_MEM_PATH]
    if mem_path:
647
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
648

649
    mouse_type = hvp[constants.HV_USB_MOUSE]
650
651
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]

652
    if mouse_type:
Iustin Pop's avatar
Iustin Pop committed
653
654
      kvm_cmd.extend(["-usb"])
      kvm_cmd.extend(["-usbdevice", mouse_type])
655
    elif vnc_bind_address:
Iustin Pop's avatar
Iustin Pop committed
656
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
657

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

668
    if vnc_bind_address:
669
      if netutils.IP4Address.IsValid(vnc_bind_address):
670
671
        if instance.network_port > constants.VNC_BASE_PORT:
          display = instance.network_port - constants.VNC_BASE_PORT
672
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
Iustin Pop's avatar
Iustin Pop committed
673
            vnc_arg = ":%d" % (display)
674
          else:
Iustin Pop's avatar
Iustin Pop committed
675
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
676
        else:
677
          logging.error("Network port is not a valid VNC display (%d < %d)."
Iustin Pop's avatar
Iustin Pop committed
678
679
                        " Not starting VNC", instance.network_port,
                        constants.VNC_BASE_PORT)
Iustin Pop's avatar
Iustin Pop committed
680
          vnc_arg = "none"
681
682
683

        # Only allow tls and other option when not binding to a file, for now.
        # kvm/qemu gets confused otherwise about the filename to use.
Iustin Pop's avatar
Iustin Pop committed
684
        vnc_append = ""
685
        if hvp[constants.HV_VNC_TLS]:
Iustin Pop's avatar
Iustin Pop committed
686
          vnc_append = "%s,tls" % vnc_append
687
          if hvp[constants.HV_VNC_X509_VERIFY]:
Iustin Pop's avatar
Iustin Pop committed
688
            vnc_append = "%s,x509verify=%s" % (vnc_append,
689
690
                                               hvp[constants.HV_VNC_X509])
          elif hvp[constants.HV_VNC_X509]:
Iustin Pop's avatar
Iustin Pop committed
691
            vnc_append = "%s,x509=%s" % (vnc_append,
692
                                         hvp[constants.HV_VNC_X509])
693
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
Iustin Pop's avatar
Iustin Pop committed
694
          vnc_append = "%s,password" % vnc_append
695

Iustin Pop's avatar
Iustin Pop committed
696
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
697

698
      else:
Iustin Pop's avatar
Iustin Pop committed
699
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
700

Iustin Pop's avatar
Iustin Pop committed
701
      kvm_cmd.extend(["-vnc", vnc_arg])
702
    else:
Iustin Pop's avatar
Iustin Pop committed
703
      kvm_cmd.extend(["-nographic"])
704

705
706
    monitor_dev = ("unix:%s,server,nowait" %
                   self._InstanceMonitor(instance.name))
Iustin Pop's avatar
Iustin Pop committed
707
    kvm_cmd.extend(["-monitor", monitor_dev])
708
    if hvp[constants.HV_SERIAL_CONSOLE]:
Iustin Pop's avatar
Iustin Pop committed
709
      serial_dev = ("unix:%s,server,nowait" %
710
                    self._InstanceSerial(instance.name))
Iustin Pop's avatar
Iustin Pop committed
711
      kvm_cmd.extend(["-serial", serial_dev])
712
    else:
Iustin Pop's avatar
Iustin Pop committed
713
      kvm_cmd.extend(["-serial", "none"])
Guido Trotter's avatar
Guido Trotter committed
714

715
    if hvp[constants.HV_USE_LOCALTIME]:
Iustin Pop's avatar
Iustin Pop committed
716
      kvm_cmd.extend(["-localtime"])
717

Balazs Lecz's avatar
Balazs Lecz committed
718
    if hvp[constants.HV_KVM_USE_CHROOT]:
Iustin Pop's avatar
Iustin Pop committed
719
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
Balazs Lecz's avatar
Balazs Lecz committed
720

721
722
723
    # Save the current instance nics, but defer their expansion as parameters,
    # as we'll need to generate executable temp files for them.
    kvm_nics = instance.nics
724
    hvparams = hvp
725

726
    return (kvm_cmd, kvm_nics, hvparams)
727

728
729
730
731
732
733
734
  def _WriteKVMRuntime(self, instance_name, data):
    """Write an instance's KVM runtime

    """
    try:
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
                      data=data)
735
    except EnvironmentError, err:
736
737
738
739
740
741
742
743
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)

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

    """
    try:
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
744
    except EnvironmentError, err:
745
746
747
748
749
750
751
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
    return file_content

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

    """
752
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
753
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
754
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
755
756
    self._WriteKVMRuntime(instance.name, serialized_form)

Guido Trotter's avatar
Guido Trotter committed
757
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
758
759
760
    """Load an instance's KVM runtime

    """
Guido Trotter's avatar
Guido Trotter committed
761
762
763
    if not serialized_runtime:
      serialized_runtime = self._ReadKVMRuntime(instance.name)
    loaded_runtime = serializer.Load(serialized_runtime)
764
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
765
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
766
    return (kvm_cmd, kvm_nics, hvparams)
767

768
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
769
770
771
772
773
774
    """Run the KVM cmd and check for errors

    @type name: string
    @param name: instance name
    @type kvm_cmd: list of strings
    @param kvm_cmd: runcmd input for kvm
775
776
    @type tap_fds: list of int
    @param tap_fds: fds of tap devices opened by Ganeti
777
778

    """
779
780
781
782
783
784
    try:
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
    finally:
      for fd in tap_fds:
        utils_wrapper.CloseFdNoError(fd)

785
786
787
788
789
790
    if result.failed:
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
                                   (name, result.fail_reason, result.output))
    if not self._InstancePidAlive(name)[2]:
      raise errors.HypervisorError("Failed to start instance %s" % name)

Guido Trotter's avatar
Guido Trotter committed
791
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
792
793
    """Execute a KVM cmd, after completing it with some last minute data

Guido Trotter's avatar
Guido Trotter committed
794
795
796
    @type incoming: tuple of strings
    @param incoming: (target_host_ip, port)

797
    """
798
799
800
801
802
803
804
805
806
    # Small _ExecuteKVMRuntime hv parameters programming howto:
    #  - conf_hvp contains the parameters as configured on ganeti. they might
    #    have changed since the instance started; only use them if the change
    #    won't affect the inside of the instance (which hasn't been rebooted).
    #  - up_hvp contains the parameters as they were when the instance was
    #    started, plus any new parameter which has been added between ganeti
    #    versions: it is paramount that those default to a value which won't
    #    affect the inside of the instance as well.
    conf_hvp = instance.hvparams
807
808
    name = instance.name
    self._CheckDown(name)
809
810
811

    temp_files = []

812
813
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
814

815
    _, v_major, v_min, _ = self._GetKVMVersion()
816

817
818
819
    # We know it's safe to run as a different user upon migration, so we'll use
    # the latest conf, from conf_hvp.
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
820
    if security_model == constants.HT_SM_USER:
821
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
822

823
824
825
    # We have reasons to believe changing something like the nic driver/type
    # upon migration won't exactly fly with the instance kernel, so for nic
    # related parameters we'll use up_hvp
826
827
    tapfds = []
    taps = []
828
    if not kvm_nics:
829
      kvm_cmd.extend(["-net", "none"])
830
    else:
831
      vnet_hdr = False
832
      tap_extra = ""
833
      nic_type = up_hvp[constants.HV_NIC_TYPE]
834
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
835
        # From version 0.12.0, kvm uses a new sintax for network configuration.
836
        if (v_major, v_min) >= (0, 12):
837
          nic_model = "virtio-net-pci"
838
          vnet_hdr = True
839
840
841
        else:
          nic_model = "virtio"

842
        if up_hvp[constants.HV_VHOST_NET]:
843
          # vhost_net is only available from version 0.13.0 or newer
844
          if (v_major, v_min) >= (0, 13):
845
846
847
848
            tap_extra = ",vhost=on"
          else:
            raise errors.HypervisorError("vhost_net is configured"
                                        " but it is not available")
849
      else:
850
        nic_model = nic_type
851

852
      for nic_seq, nic in enumerate(kvm_nics):
853
854
855
        tapname, tapfd = _OpenTap(vnet_hdr)
        tapfds.append(tapfd)
        taps.append(tapname)
856
        if (v_major, v_min) >= (0, 12):
857
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
858
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
859
860
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
        else:
861
862
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
                                                         nic.mac, nic_model)
863
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
864
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
865

Guido Trotter's avatar
Guido Trotter committed
866
867
    if incoming:
      target, port = incoming
Iustin Pop's avatar
Iustin Pop committed
868
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
Guido Trotter's avatar
Guido Trotter committed
869

870
871
872
873
    # Changing the vnc password doesn't bother the guest that much. At most it
    # will surprise people who connect to it. Whether positively or negatively
    # it's debatable.
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
874
875
876
    vnc_pwd = None
    if vnc_pwd_file:
      try:
877
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
878
879
880
881
      except EnvironmentError, err:
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
                                     % (vnc_pwd_file, err))

882
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
Balazs Lecz's avatar
Balazs Lecz committed
883
884
885
      utils.EnsureDirs([(self._InstanceChrootDir(name),
                         constants.SECURE_DIR_MODE)])

886
887
888
889
890
891
892
    # Configure the network now for starting instances and bridged interfaces,
    # during FinalizeMigration for incoming instances' routed interfaces
    for nic_seq, nic in enumerate(kvm_nics):
      if (incoming and
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
        continue
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
893

894
895
896
897
898
899
900
901
    if security_model == constants.HT_SM_POOL:
      ss = ssconf.SimpleStore()
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
      uid = uidpool.RequestUnusedUid(all_uids)
      try:
        username = pwd.getpwuid(uid.GetUid()).pw_name
        kvm_cmd.extend(["-runas", username])
902
        self._RunKVMCmd(name, kvm_cmd, tapfds)
903
904
905
906
907
      except:
        uidpool.ReleaseUid(uid)
        raise
      else:
        uid.Unlock()
908
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
909
    else:
910
911
912
913
914
915
916
      self._RunKVMCmd(name, kvm_cmd, tapfds)

    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
                     constants.RUN_DIRS_MODE)])
    for nic_seq, tap in enumerate(taps):
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
                      data=tap)
Guido Trotter's avatar
Guido Trotter committed
917

918
    if vnc_pwd:
Iustin Pop's avatar
Iustin Pop committed
919
      change_cmd = "change vnc password %s" % vnc_pwd
920
921
      self._CallMonitorCommand(instance.name, change_cmd)

922
923
    for filename in temp_files:
      utils.RemoveFile(filename)
Guido Trotter's avatar
Guido Trotter committed
924

925
  def StartInstance(self, instance, block_devices, startup_paused):
926
927
928
    """Start an instance.

    """
929
    self._CheckDown(instance.name)
930
931
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
                                           startup_paused)
932
    self._SaveKVMRuntime(instance, kvm_runtime)
933
934
    self._ExecuteKVMRuntime(instance, kvm_runtime)

935
936
937
938
939
940
941
942
943
944
945
946
  def _CallMonitorCommand(self, instance_name, command):
    """Invoke a command on the instance monitor.

    """
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
             (utils.ShellQuote(command),
              constants.SOCAT_PATH,
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
    result = utils.RunCmd(socat)
    if result.failed:
      msg = ("Failed to send command '%s' to instance %s."
             " output: %s, error: %s, fail_reason: %s" %
947
948
             (command, instance_name,
              result.stdout, result.stderr, result.fail_reason))
949
950
951
952
      raise errors.HypervisorError(msg)

    return result

953
954
  @classmethod
  def _GetKVMVersion(cls):
955
    """Return the installed KVM version.
956

957
    @return: (version, v_maj, v_min, v_rev)
958
    @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
959
960
961
962

    """
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
    if result.failed:
963
      raise errors.HypervisorError("Unable to get KVM version")
964
965
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
    if not match:
966
      raise errors.HypervisorError("Unable to get KVM version")
967
968
969

    return (match.group(0), int(match.group(1)), int(match.group(2)),
            int(match.group(3)))
970

971
  def StopInstance(self, instance, force=False, retry=False, name=None):
Guido Trotter's avatar
Guido Trotter committed
972
973
974
    """Stop an instance.

    """
975
976
977
978
979
980
981
    if name is not None and not force:
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
    if name is None:
      name = instance.name
      acpi = instance.hvparams[constants.HV_ACPI]
    else:
      acpi = False
982
    _, pid, alive = self._InstancePidAlive(name)
983
    if pid > 0 and alive:
984
      if force or not acpi:
Guido Trotter's avatar
Guido Trotter committed
985
986
        utils.KillProcess(pid)
      else:
Iustin Pop's avatar
Iustin Pop committed
987
        self._CallMonitorCommand(name, "system_powerdown")
Guido Trotter's avatar
Guido Trotter committed
988

989
990
991
992
993
994
995
996
  def CleanupInstance(self, instance_name):
    """Cleanup after a stopped instance

    """
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
    if pid > 0 and alive:
      raise errors.HypervisorError("Cannot cleanup a live instance")
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
Guido Trotter's avatar
Guido Trotter committed
997
998
999
1000
1001
1002
1003
1004

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

    """
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
    # socket the instance will stop, but now power up again. So we'll resort
    # to shutdown and restart.
1005
    _, _, alive = self._InstancePidAlive(instance.name)
1006
    if not alive:
1007
1008
      raise errors.HypervisorError("Failed to reboot instance %s:"
                                   " not running" % instance.name)
Guido Trotter's avatar
Guido Trotter committed
1009
1010
1011
1012
1013
1014
1015
1016
1017
    # StopInstance will delete the saved KVM runtime so:
    # ...first load it...
    kvm_runtime = self._LoadKVMRuntime(instance)
    # ...now we can safely call StopInstance...
    if not self.StopInstance(instance):
      self.StopInstance(instance, force=True)
    # ...and finally we can save it again, and execute it...
    self._SaveKVMRuntime(instance, kvm_runtime)
    self._ExecuteKVMRuntime(instance, kvm_runtime)
Guido Trotter's avatar
Guido Trotter committed
1018

Guido Trotter's avatar
Guido Trotter committed
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  def MigrationInfo(self, instance):
    """Get instance information to perform a migration.

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

    """
    return self._ReadKVMRuntime(instance.name)

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

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

    """
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1042
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
Guido Trotter's avatar
Guido Trotter committed