Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 049383d9

History | View | Annotate | Download (39.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010 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
from cStringIO import StringIO
35

    
36
from ganeti import utils
37
from ganeti import constants
38
from ganeti import errors
39
from ganeti import serializer
40
from ganeti import objects
41
from ganeti import uidpool
42
from ganeti import ssconf
43
from ganeti.hypervisor import hv_base
44
from ganeti import netutils
45

    
46

    
47
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
48

    
49

    
50
def _WriteNetScript(instance, nic, index):
51
  """Write a script to connect a net interface to the proper bridge.
52

53
  This can be used by any qemu-type hypervisor.
54

55
  @type instance: L{objects.Instance}
56
  @param instance: Instance object
57
  @type nic: L{objects.NIC}
58
  @param nic: NIC object
59
  @type index: int
60
  @param index: NIC index
61
  @return: Script
62
  @rtype: string
63

64
  """
65
  if instance.tags:
66
    tags = " ".join(instance.tags)
67
  else:
68
    tags = ""
69

    
70
  buf = StringIO()
71
  sw = utils.ShellWriter(buf)
72
  sw.Write("#!/bin/sh")
73
  sw.Write("# this is autogenerated by Ganeti, please do not edit")
74
  sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
75
  sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
76
  sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
77
  sw.Write("export MODE=%s",
78
           utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
79
  sw.Write("export INTERFACE=\"$1\"")
80
  sw.Write("export TAGS=%s", utils.ShellQuote(tags))
81

    
82
  if nic.ip:
83
    sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
84

    
85
  if nic.nicparams[constants.NIC_LINK]:
86
    sw.Write("export LINK=%s",
87
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
88

    
89
  if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
90
    sw.Write("export BRIDGE=%s",
91
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
92

    
93
  # TODO: make this configurable at ./configure time
94
  sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
95
  sw.IncIndent()
96
  try:
97
    sw.Write("# Execute the user-specific vif file")
98
    sw.Write(_KVM_NETWORK_SCRIPT)
99
  finally:
100
    sw.DecIndent()
101
  sw.Write("else")
102
  sw.IncIndent()
103
  try:
104
    sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
105

    
106
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
107
      sw.Write("# Connect the interface to the bridge")
108
      sw.Write("brctl addif $BRIDGE $INTERFACE")
109

    
110
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
111
      if not nic.ip:
112
        raise errors.HypervisorError("nic/%d is routed, but has no IP"
113
                                     " address" % index)
114

    
115
      sw.Write("# Route traffic targeted at the IP to the interface")
116
      if nic.nicparams[constants.NIC_LINK]:
117
        sw.Write("while ip rule del dev $INTERFACE; do :; done")
118
        sw.Write("ip rule add dev $INTERFACE table $LINK")
119
        sw.Write("ip route replace $IP table $LINK proto static"
120
                 " dev $INTERFACE")
121
      else:
122
        sw.Write("ip route replace $IP proto static dev $INTERFACE")
123

    
124
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
125
      sw.Write(" if [ -d %s ]; then", interface_v4_conf)
126
      sw.IncIndent()
127
      try:
128
        sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
129
        sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
130
      finally:
131
        sw.DecIndent()
132
      sw.Write("fi")
133

    
134
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
135
      sw.Write("if [ -d %s ]; then", interface_v6_conf)
136
      sw.IncIndent()
137
      try:
138
        sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
139
        sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
140
      finally:
141
        sw.DecIndent()
142
      sw.Write("fi")
143
  finally:
144
    sw.DecIndent()
145
  sw.Write("fi")
146

    
147
  return buf.getvalue()
148

    
149

    
150
class KVMHypervisor(hv_base.BaseHypervisor):
151
  """KVM hypervisor interface"""
152
  CAN_MIGRATE = True
153

    
154
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
155
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
156
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
157
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
158
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
159
  # KVM instances with chroot enabled are started in empty chroot directories.
160
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
161
  # After an instance is stopped, its chroot directory is removed.
162
  # If the chroot directory is not empty, it can't be removed.
163
  # A non-empty chroot directory indicates a possible security incident.
164
  # To support forensics, the non-empty chroot directory is quarantined in
165
  # a separate directory, called 'chroot-quarantine'.
166
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
167
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
168
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
169

    
170
  PARAMETERS = {
171
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
172
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
173
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
174
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
175
    constants.HV_ACPI: hv_base.NO_CHECK,
176
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
177
    constants.HV_VNC_BIND_ADDRESS:
178
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
179
                         utils.IsNormAbsPath(x)),
180
       "the VNC bind address must be either a valid IP address or an absolute"
181
       " pathname", None, None),
182
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
183
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
184
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
185
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
186
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
187
    constants.HV_BOOT_ORDER:
188
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
189
    constants.HV_NIC_TYPE:
190
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
191
    constants.HV_DISK_TYPE:
192
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
193
    constants.HV_USB_MOUSE:
194
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
195
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
196
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
197
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
198
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
199
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
200
    constants.HV_DISK_CACHE:
201
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
202
    constants.HV_SECURITY_MODEL:
203
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
204
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
205
    constants.HV_KVM_FLAG:
206
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
207
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
208
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
209
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
210
    }
211

    
212
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
213
                                    re.M | re.I)
214
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
215
  _MIGRATION_INFO_RETRY_DELAY = 2
216

    
217
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
218

    
219
  ANCILLARY_FILES = [
220
    _KVM_NETWORK_SCRIPT,
221
    ]
222

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

    
230
  @classmethod
231
  def _InstancePidFile(cls, instance_name):
232
    """Returns the instance pidfile.
233

234
    """
235
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
236

    
237
  @classmethod
238
  def _InstanceUidFile(cls, instance_name):
239
    """Returns the instance uidfile.
240

241
    """
242
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
243

    
244
  @classmethod
245
  def _InstancePidInfo(cls, pid):
246
    """Check pid file for instance information.
247

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

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

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

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

    
269
    instance = None
270
    memory = 0
271
    vcpus = 0
272

    
273
    arg_list = cmdline.split('\x00')
274
    while arg_list:
275
      arg =  arg_list.pop(0)
276
      if arg == "-name":
277
        instance = arg_list.pop(0)
278
      elif arg == "-m":
279
        memory = int(arg_list.pop(0))
280
      elif arg == "-smp":
281
        vcpus = int(arg_list.pop(0))
282

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

    
287
    return (instance, memory, vcpus)
288

    
289
  def _InstancePidAlive(self, instance_name):
290
    """Returns the instance pidfile, pid, and liveness.
291

292
    @type instance_name: string
293
    @param instance_name: instance name
294
    @rtype: tuple
295
    @return: (pid file name, pid, liveness)
296

297
    """
298
    pidfile = self._InstancePidFile(instance_name)
299
    pid = utils.ReadPidFile(pidfile)
300

    
301
    alive = False
302
    try:
303
      cmd_instance = self._InstancePidInfo(pid)[0]
304
      alive = (cmd_instance == instance_name)
305
    except errors.HypervisorError:
306
      pass
307

    
308
    return (pidfile, pid, alive)
309

    
310
  def _CheckDown(self, instance_name):
311
    """Raises an error unless the given instance is down.
312

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

    
319
  @classmethod
320
  def _InstanceMonitor(cls, instance_name):
321
    """Returns the instance monitor socket name
322

323
    """
324
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
325

    
326
  @classmethod
327
  def _InstanceSerial(cls, instance_name):
328
    """Returns the instance serial socket name
329

330
    """
331
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
332

    
333
  @staticmethod
334
  def _SocatUnixConsoleParams():
335
    """Returns the correct parameters for socat
336

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

339
    """
340
    if constants.SOCAT_USE_ESCAPE:
341
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
342
    else:
343
      return "echo=0,icanon=0"
344

    
345
  @classmethod
346
  def _InstanceKVMRuntime(cls, instance_name):
347
    """Returns the instance KVM runtime filename
348

349
    """
350
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
351

    
352
  @classmethod
353
  def _InstanceChrootDir(cls, instance_name):
354
    """Returns the name of the KVM chroot dir of the instance
355

356
    """
357
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
358

    
359
  @classmethod
360
  def _TryReadUidFile(cls, uid_file):
361
    """Try to read a uid file
362

363
    """
364
    if os.path.exists(uid_file):
365
      try:
366
        uid = int(utils.ReadOneLineFile(uid_file))
367
        return uid
368
      except EnvironmentError:
369
        logging.warning("Can't read uid file", exc_info=True)
370
      except (TypeError, ValueError):
371
        logging.warning("Can't parse uid file contents", exc_info=True)
372
    return None
373

    
374
  @classmethod
375
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
376
    """Removes an instance's rutime sockets/files/dirs.
377

378
    """
379
    utils.RemoveFile(pidfile)
380
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
381
    utils.RemoveFile(cls._InstanceSerial(instance_name))
382
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
383
    uid_file = cls._InstanceUidFile(instance_name)
384
    uid = cls._TryReadUidFile(uid_file)
385
    utils.RemoveFile(uid_file)
386
    if uid is not None:
387
      uidpool.ReleaseUid(uid)
388
    try:
389
      chroot_dir = cls._InstanceChrootDir(instance_name)
390
      utils.RemoveDir(chroot_dir)
391
    except OSError, err:
392
      if err.errno == errno.ENOTEMPTY:
393
        # The chroot directory is expected to be empty, but it isn't.
394
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
395
                                          prefix="%s-%s-" %
396
                                          (instance_name,
397
                                           utils.TimestampForFilename()))
398
        logging.warning("The chroot directory of instance %s can not be"
399
                        " removed as it is not empty. Moving it to the"
400
                        " quarantine instead. Please investigate the"
401
                        " contents (%s) and clean up manually",
402
                        instance_name, new_chroot_dir)
403
        utils.RenameFile(chroot_dir, new_chroot_dir)
404
      else:
405
        raise
406

    
407
  @staticmethod
408
  def _WriteNetScriptFile(instance, seq, nic):
409
    """Write a script to connect a net interface to the proper bridge.
410

411
    This can be used by any qemu-type hypervisor.
412

413
    @param instance: instance we're acting on
414
    @type instance: instance object
415
    @param seq: nic sequence number
416
    @type seq: int
417
    @param nic: nic we're acting on
418
    @type nic: nic object
419
    @return: netscript file name
420
    @rtype: string
421

422
    """
423
    script = _WriteNetScript(instance, nic, seq)
424

    
425
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
426
    # mounted noexec sometimes, so we'll have to find another place.
427
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
428
    tmpfile = os.fdopen(tmpfd, 'w')
429
    try:
430
      tmpfile.write(script)
431
    finally:
432
      tmpfile.close()
433
    os.chmod(tmpfile_name, 0755)
434
    return tmpfile_name
435

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

439
    We can do this by listing our live instances directory and
440
    checking whether the associated kvm process is still alive.
441

442
    """
443
    result = []
444
    for name in os.listdir(self._PIDS_DIR):
445
      if self._InstancePidAlive(name)[2]:
446
        result.append(name)
447
    return result
448

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

452
    @type instance_name: string
453
    @param instance_name: the instance name
454
    @rtype: tuple of strings
455
    @return: (name, id, memory, vcpus, stat, times)
456

457
    """
458
    _, pid, alive = self._InstancePidAlive(instance_name)
459
    if not alive:
460
      return None
461

    
462
    _, memory, vcpus = self._InstancePidInfo(pid)
463
    stat = "---b-"
464
    times = "0"
465

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

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

471
    @return: list of tuples (name, id, memory, vcpus, stat, times)
472

473
    """
474
    data = []
475
    for name in os.listdir(self._PIDS_DIR):
476
      try:
477
        info = self.GetInstanceInfo(name)
478
      except errors.HypervisorError:
479
        continue
480
      if info:
481
        data.append(info)
482
    return data
483

    
484
  def _GenerateKVMRuntime(self, instance, block_devices):
485
    """Generate KVM information to start an instance.
486

487
    """
488
    pidfile  = self._InstancePidFile(instance.name)
489
    kvm = constants.KVM_PATH
490
    kvm_cmd = [kvm]
491
    # used just by the vnc server, if enabled
492
    kvm_cmd.extend(['-name', instance.name])
493
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
494
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
495
    kvm_cmd.extend(['-pidfile', pidfile])
496
    kvm_cmd.extend(['-daemonize'])
497
    if not instance.hvparams[constants.HV_ACPI]:
498
      kvm_cmd.extend(['-no-acpi'])
499

    
500
    hvp = instance.hvparams
501
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
502
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
503
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
504

    
505
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
506
      kvm_cmd.extend(["-enable-kvm"])
507
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
508
      kvm_cmd.extend(["-disable-kvm"])
509

    
510
    if boot_network:
511
      kvm_cmd.extend(['-boot', 'n'])
512

    
513
    disk_type = hvp[constants.HV_DISK_TYPE]
514
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
515
      if_val = ',if=virtio'
516
    else:
517
      if_val = ',if=%s' % disk_type
518
    # Cache mode
519
    disk_cache = hvp[constants.HV_DISK_CACHE]
520
    if disk_cache != constants.HT_CACHE_DEFAULT:
521
      cache_val = ",cache=%s" % disk_cache
522
    else:
523
      cache_val = ""
524
    for cfdev, dev_path in block_devices:
525
      if cfdev.mode != constants.DISK_RDWR:
526
        raise errors.HypervisorError("Instance has read-only disks which"
527
                                     " are not supported by KVM")
528
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
529
      if boot_disk:
530
        kvm_cmd.extend(['-boot', 'c'])
531
        if disk_type != constants.HT_DISK_IDE:
532
          boot_val = ',boot=on'
533
        else:
534
          boot_val = ''
535
        # We only boot from the first disk
536
        boot_disk = False
537
      else:
538
        boot_val = ''
539

    
540
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
541
                                                cache_val)
542
      kvm_cmd.extend(['-drive', drive_val])
543

    
544
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
545
    if iso_image:
546
      options = ',format=raw,media=cdrom'
547
      if boot_cdrom:
548
        kvm_cmd.extend(['-boot', 'd'])
549
        if disk_type != constants.HT_DISK_IDE:
550
          options = '%s,boot=on' % options
551
      else:
552
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
553
          if_val = ',if=virtio'
554
        else:
555
          if_val = ',if=%s' % disk_type
556
        options = '%s%s' % (options, if_val)
557
      drive_val = 'file=%s%s' % (iso_image, options)
558
      kvm_cmd.extend(['-drive', drive_val])
559

    
560
    kernel_path = hvp[constants.HV_KERNEL_PATH]
561
    if kernel_path:
562
      kvm_cmd.extend(['-kernel', kernel_path])
563
      initrd_path = hvp[constants.HV_INITRD_PATH]
564
      if initrd_path:
565
        kvm_cmd.extend(['-initrd', initrd_path])
566
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
567
                     hvp[constants.HV_KERNEL_ARGS]]
568
      if hvp[constants.HV_SERIAL_CONSOLE]:
569
        root_append.append('console=ttyS0,38400')
570
      kvm_cmd.extend(['-append', ' '.join(root_append)])
571

    
572
    mem_path = hvp[constants.HV_MEM_PATH]
573
    if mem_path:
574
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
575

    
576
    mouse_type = hvp[constants.HV_USB_MOUSE]
577
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
578

    
579
    if mouse_type:
580
      kvm_cmd.extend(['-usb'])
581
      kvm_cmd.extend(['-usbdevice', mouse_type])
582
    elif vnc_bind_address:
583
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
584

    
585
    if vnc_bind_address:
586
      if netutils.IP4Address.IsValid(vnc_bind_address):
587
        if instance.network_port > constants.VNC_BASE_PORT:
588
          display = instance.network_port - constants.VNC_BASE_PORT
589
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
590
            vnc_arg = ':%d' % (display)
591
          else:
592
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
593
        else:
594
          logging.error("Network port is not a valid VNC display (%d < %d)."
595
                        " Not starting VNC", instance.network_port,
596
                        constants.VNC_BASE_PORT)
597
          vnc_arg = 'none'
598

    
599
        # Only allow tls and other option when not binding to a file, for now.
600
        # kvm/qemu gets confused otherwise about the filename to use.
601
        vnc_append = ''
602
        if hvp[constants.HV_VNC_TLS]:
603
          vnc_append = '%s,tls' % vnc_append
604
          if hvp[constants.HV_VNC_X509_VERIFY]:
605
            vnc_append = '%s,x509verify=%s' % (vnc_append,
606
                                               hvp[constants.HV_VNC_X509])
607
          elif hvp[constants.HV_VNC_X509]:
608
            vnc_append = '%s,x509=%s' % (vnc_append,
609
                                         hvp[constants.HV_VNC_X509])
610
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
611
          vnc_append = '%s,password' % vnc_append
612

    
613
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
614

    
615
      else:
616
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
617

    
618
      kvm_cmd.extend(['-vnc', vnc_arg])
619
    else:
620
      kvm_cmd.extend(['-nographic'])
621

    
622
    monitor_dev = ("unix:%s,server,nowait" %
623
                   self._InstanceMonitor(instance.name))
624
    kvm_cmd.extend(['-monitor', monitor_dev])
625
    if hvp[constants.HV_SERIAL_CONSOLE]:
626
      serial_dev = ('unix:%s,server,nowait' %
627
                    self._InstanceSerial(instance.name))
628
      kvm_cmd.extend(['-serial', serial_dev])
629
    else:
630
      kvm_cmd.extend(['-serial', 'none'])
631

    
632
    if hvp[constants.HV_USE_LOCALTIME]:
633
      kvm_cmd.extend(['-localtime'])
634

    
635
    if hvp[constants.HV_KVM_USE_CHROOT]:
636
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
637

    
638
    # Save the current instance nics, but defer their expansion as parameters,
639
    # as we'll need to generate executable temp files for them.
640
    kvm_nics = instance.nics
641
    hvparams = hvp
642

    
643
    return (kvm_cmd, kvm_nics, hvparams)
644

    
645
  def _WriteKVMRuntime(self, instance_name, data):
646
    """Write an instance's KVM runtime
647

648
    """
649
    try:
650
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
651
                      data=data)
652
    except EnvironmentError, err:
653
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
654

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

658
    """
659
    try:
660
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
661
    except EnvironmentError, err:
662
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
663
    return file_content
664

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

668
    """
669
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
670
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
671
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
672
    self._WriteKVMRuntime(instance.name, serialized_form)
673

    
674
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
675
    """Load an instance's KVM runtime
676

677
    """
678
    if not serialized_runtime:
679
      serialized_runtime = self._ReadKVMRuntime(instance.name)
680
    loaded_runtime = serializer.Load(serialized_runtime)
681
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
682
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
683
    return (kvm_cmd, kvm_nics, hvparams)
684

    
685
  def _RunKVMCmd(self, name, kvm_cmd):
686
    """Run the KVM cmd and check for errors
687

688
    @type name: string
689
    @param name: instance name
690
    @type kvm_cmd: list of strings
691
    @param kvm_cmd: runcmd input for kvm
692

693
    """
694
    result = utils.RunCmd(kvm_cmd)
695
    if result.failed:
696
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
697
                                   (name, result.fail_reason, result.output))
698
    if not self._InstancePidAlive(name)[2]:
699
      raise errors.HypervisorError("Failed to start instance %s" % name)
700

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

704
    @type incoming: tuple of strings
705
    @param incoming: (target_host_ip, port)
706

707
    """
708
    # Small _ExecuteKVMRuntime hv parameters programming howto:
709
    #  - conf_hvp contains the parameters as configured on ganeti. they might
710
    #    have changed since the instance started; only use them if the change
711
    #    won't affect the inside of the instance (which hasn't been rebooted).
712
    #  - up_hvp contains the parameters as they were when the instance was
713
    #    started, plus any new parameter which has been added between ganeti
714
    #    versions: it is paramount that those default to a value which won't
715
    #    affect the inside of the instance as well.
716
    conf_hvp = instance.hvparams
717
    name = instance.name
718
    self._CheckDown(name)
719

    
720
    temp_files = []
721

    
722
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
723
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
724

    
725
    kvm_version = self._GetKVMVersion()
726
    if kvm_version:
727
      _, v_major, v_min, _ = kvm_version
728
    else:
729
      raise errors.HypervisorError("Unable to get KVM version")
730

    
731
    # We know it's safe to run as a different user upon migration, so we'll use
732
    # the latest conf, from conf_hvp.
733
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
734
    if security_model == constants.HT_SM_USER:
735
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
736

    
737
    # We have reasons to believe changing something like the nic driver/type
738
    # upon migration won't exactly fly with the instance kernel, so for nic
739
    # related parameters we'll use up_hvp
740
    if not kvm_nics:
741
      kvm_cmd.extend(["-net", "none"])
742
    else:
743
      tap_extra = ""
744
      nic_type = up_hvp[constants.HV_NIC_TYPE]
745
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
746
        # From version 0.12.0, kvm uses a new sintax for network configuration.
747
        if (v_major, v_min) >= (0, 12):
748
          nic_model = "virtio-net-pci"
749
        else:
750
          nic_model = "virtio"
751

    
752
        if up_hvp[constants.HV_VHOST_NET]:
753
          # vhost_net is only available from version 0.13.0 or newer
754
          if (v_major, v_min) >= (0, 13):
755
            tap_extra = ",vhost=on"
756
          else:
757
            raise errors.HypervisorError("vhost_net is configured"
758
                                        " but it is not available")
759
      else:
760
        nic_model = nic_type
761

    
762
      for nic_seq, nic in enumerate(kvm_nics):
763
        script = self._WriteNetScriptFile(instance, nic_seq, nic)
764
        temp_files.append(script)
765
        if (v_major, v_min) >= (0, 12):
766
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
767
          tap_val = "type=tap,id=netdev%s,script=%s%s" % (nic_seq,
768
                                                          script, tap_extra)
769
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
770
        else:
771
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
772
                                                         nic.mac, nic_model)
773
          tap_val = "tap,vlan=%s,script=%s" % (nic_seq, script)
774
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
775

    
776
    if incoming:
777
      target, port = incoming
778
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
779

    
780
    # Changing the vnc password doesn't bother the guest that much. At most it
781
    # will surprise people who connect to it. Whether positively or negatively
782
    # it's debatable.
783
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
784
    vnc_pwd = None
785
    if vnc_pwd_file:
786
      try:
787
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
788
      except EnvironmentError, err:
789
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
790
                                     % (vnc_pwd_file, err))
791

    
792
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
793
      utils.EnsureDirs([(self._InstanceChrootDir(name),
794
                         constants.SECURE_DIR_MODE)])
795

    
796
    if security_model == constants.HT_SM_POOL:
797
      ss = ssconf.SimpleStore()
798
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
799
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
800
      uid = uidpool.RequestUnusedUid(all_uids)
801
      try:
802
        username = pwd.getpwuid(uid.GetUid()).pw_name
803
        kvm_cmd.extend(["-runas", username])
804
        self._RunKVMCmd(name, kvm_cmd)
805
      except:
806
        uidpool.ReleaseUid(uid)
807
        raise
808
      else:
809
        uid.Unlock()
810
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
811
    else:
812
      self._RunKVMCmd(name, kvm_cmd)
813

    
814
    if vnc_pwd:
815
      change_cmd = 'change vnc password %s' % vnc_pwd
816
      self._CallMonitorCommand(instance.name, change_cmd)
817

    
818
    for filename in temp_files:
819
      utils.RemoveFile(filename)
820

    
821
  def StartInstance(self, instance, block_devices):
822
    """Start an instance.
823

824
    """
825
    self._CheckDown(instance.name)
826
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
827
    self._SaveKVMRuntime(instance, kvm_runtime)
828
    self._ExecuteKVMRuntime(instance, kvm_runtime)
829

    
830
  def _CallMonitorCommand(self, instance_name, command):
831
    """Invoke a command on the instance monitor.
832

833
    """
834
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
835
             (utils.ShellQuote(command),
836
              constants.SOCAT_PATH,
837
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
838
    result = utils.RunCmd(socat)
839
    if result.failed:
840
      msg = ("Failed to send command '%s' to instance %s."
841
             " output: %s, error: %s, fail_reason: %s" %
842
             (command, instance_name,
843
              result.stdout, result.stderr, result.fail_reason))
844
      raise errors.HypervisorError(msg)
845

    
846
    return result
847

    
848
  @classmethod
849
  def _GetKVMVersion(cls):
850
    """Return the installed KVM version
851

852
    @return: (version, v_maj, v_min, v_rev), or None
853

854
    """
855
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
856
    if result.failed:
857
      return None
858
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
859
    if not match:
860
      return None
861

    
862
    return (match.group(0), int(match.group(1)), int(match.group(2)),
863
            int(match.group(3)))
864

    
865
  def StopInstance(self, instance, force=False, retry=False, name=None):
866
    """Stop an instance.
867

868
    """
869
    if name is not None and not force:
870
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
871
    if name is None:
872
      name = instance.name
873
      acpi = instance.hvparams[constants.HV_ACPI]
874
    else:
875
      acpi = False
876
    _, pid, alive = self._InstancePidAlive(name)
877
    if pid > 0 and alive:
878
      if force or not acpi:
879
        utils.KillProcess(pid)
880
      else:
881
        self._CallMonitorCommand(name, 'system_powerdown')
882

    
883
  def CleanupInstance(self, instance_name):
884
    """Cleanup after a stopped instance
885

886
    """
887
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
888
    if pid > 0 and alive:
889
      raise errors.HypervisorError("Cannot cleanup a live instance")
890
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
891

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

895
    """
896
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
897
    # socket the instance will stop, but now power up again. So we'll resort
898
    # to shutdown and restart.
899
    _, _, alive = self._InstancePidAlive(instance.name)
900
    if not alive:
901
      raise errors.HypervisorError("Failed to reboot instance %s:"
902
                                   " not running" % instance.name)
903
    # StopInstance will delete the saved KVM runtime so:
904
    # ...first load it...
905
    kvm_runtime = self._LoadKVMRuntime(instance)
906
    # ...now we can safely call StopInstance...
907
    if not self.StopInstance(instance):
908
      self.StopInstance(instance, force=True)
909
    # ...and finally we can save it again, and execute it...
910
    self._SaveKVMRuntime(instance, kvm_runtime)
911
    self._ExecuteKVMRuntime(instance, kvm_runtime)
912

    
913
  def MigrationInfo(self, instance):
914
    """Get instance information to perform a migration.
915

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

921
    """
922
    return self._ReadKVMRuntime(instance.name)
923

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

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

934
    """
935
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
936
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
937
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
938

    
939
  def FinalizeMigration(self, instance, info, success):
940
    """Finalize an instance migration.
941

942
    Stop the incoming mode KVM.
943

944
    @type instance: L{objects.Instance}
945
    @param instance: instance whose migration is being finalized
946

947
    """
948
    if success:
949
      self._WriteKVMRuntime(instance.name, info)
950
    else:
951
      self.StopInstance(instance, force=True)
952

    
953
  def MigrateInstance(self, instance, target, live):
954
    """Migrate an instance to a target node.
955

956
    The migration will not be attempted if the instance is not
957
    currently running.
958

959
    @type instance: L{objects.Instance}
960
    @param instance: the instance to be migrated
961
    @type target: string
962
    @param target: ip address of the target node
963
    @type live: boolean
964
    @param live: perform a live migration
965

966
    """
967
    instance_name = instance.name
968
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
969
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
970
    if not alive:
971
      raise errors.HypervisorError("Instance not running, cannot migrate")
972

    
973
    if not live:
974
      self._CallMonitorCommand(instance_name, 'stop')
975

    
976
    migrate_command = ('migrate_set_speed %dm' %
977
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
978
    self._CallMonitorCommand(instance_name, migrate_command)
979

    
980
    migrate_command = ('migrate_set_downtime %dms' %
981
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
982
    self._CallMonitorCommand(instance_name, migrate_command)
983

    
984
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
985
    self._CallMonitorCommand(instance_name, migrate_command)
986

    
987
    info_command = 'info migrate'
988
    done = False
989
    broken_answers = 0
990
    while not done:
991
      result = self._CallMonitorCommand(instance_name, info_command)
992
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
993
      if not match:
994
        broken_answers += 1
995
        if not result.stdout:
996
          logging.info("KVM: empty 'info migrate' result")
997
        else:
998
          logging.warning("KVM: unknown 'info migrate' result: %s",
999
                          result.stdout)
1000
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1001
      else:
1002
        status = match.group(1)
1003
        if status == 'completed':
1004
          done = True
1005
        elif status == 'active':
1006
          # reset the broken answers count
1007
          broken_answers = 0
1008
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1009
        elif status == 'failed' or status == 'cancelled':
1010
          if not live:
1011
            self._CallMonitorCommand(instance_name, 'cont')
1012
          raise errors.HypervisorError("Migration %s at the kvm level" %
1013
                                       status)
1014
        else:
1015
          logging.warning("KVM: unknown migration status '%s'", status)
1016
          broken_answers += 1
1017
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1018
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1019
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1020

    
1021
    utils.KillProcess(pid)
1022
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1023

    
1024
  def GetNodeInfo(self):
1025
    """Return information about the node.
1026

1027
    This is just a wrapper over the base GetLinuxNodeInfo method.
1028

1029
    @return: a dict with the following keys (values in MiB):
1030
          - memory_total: the total memory size on the node
1031
          - memory_free: the available memory on the node for instances
1032
          - memory_dom0: the memory used by the node itself, if available
1033

1034
    """
1035
    return self.GetLinuxNodeInfo()
1036

    
1037
  @classmethod
1038
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
1039
    """Return a command for connecting to the console of an instance.
1040

1041
    """
1042
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1043
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
1044
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
1045
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
1046
    else:
1047
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
1048

    
1049
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1050
    if vnc_bind_address:
1051
      if instance.network_port > constants.VNC_BASE_PORT:
1052
        display = instance.network_port - constants.VNC_BASE_PORT
1053
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
1054
                       " (display: %d)'" % (vnc_bind_address,
1055
                                            instance.network_port,
1056
                                            display))
1057
        shell_command = "%s; %s" % (vnc_command, shell_command)
1058

    
1059
    return shell_command
1060

    
1061
  def Verify(self):
1062
    """Verify the hypervisor.
1063

1064
    Check that the binary exists.
1065

1066
    """
1067
    if not os.path.exists(constants.KVM_PATH):
1068
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1069
    if not os.path.exists(constants.SOCAT_PATH):
1070
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1071

    
1072

    
1073
  @classmethod
1074
  def CheckParameterSyntax(cls, hvparams):
1075
    """Check the given parameters for validity.
1076

1077
    @type hvparams:  dict
1078
    @param hvparams: dictionary with parameter names/value
1079
    @raise errors.HypervisorError: when a parameter is not valid
1080

1081
    """
1082
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1083

    
1084
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1085
    if kernel_path:
1086
      if not hvparams[constants.HV_ROOT_PATH]:
1087
        raise errors.HypervisorError("Need a root partition for the instance,"
1088
                                     " if a kernel is defined")
1089

    
1090
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1091
        not hvparams[constants.HV_VNC_X509]):
1092
      raise errors.HypervisorError("%s must be defined, if %s is" %
1093
                                   (constants.HV_VNC_X509,
1094
                                    constants.HV_VNC_X509_VERIFY))
1095

    
1096
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1097
    if (boot_order == constants.HT_BO_CDROM and
1098
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1099
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1100
                                   " ISO path")
1101

    
1102
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1103
    if security_model == constants.HT_SM_USER:
1104
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1105
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1106
                                     " must be specified")
1107
    elif (security_model == constants.HT_SM_NONE or
1108
          security_model == constants.HT_SM_POOL):
1109
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1110
        raise errors.HypervisorError("Cannot have a security domain when the"
1111
                                     " security model is 'none' or 'pool'")
1112

    
1113
  @classmethod
1114
  def ValidateParameters(cls, hvparams):
1115
    """Check the given parameters for validity.
1116

1117
    @type hvparams:  dict
1118
    @param hvparams: dictionary with parameter names/value
1119
    @raise errors.HypervisorError: when a parameter is not valid
1120

1121
    """
1122
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1123

    
1124
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1125
    if security_model == constants.HT_SM_USER:
1126
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1127
      try:
1128
        pwd.getpwnam(username)
1129
      except KeyError:
1130
        raise errors.HypervisorError("Unknown security domain user %s"
1131
                                     % username)
1132

    
1133
  @classmethod
1134
  def PowercycleNode(cls):
1135
    """KVM powercycle, just a wrapper over Linux powercycle.
1136

1137
    """
1138
    cls.LinuxPowercycle()