Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ b52d85c1

History | View | Annotate | Download (37.9 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.IsValidIP4(x) or utils.IsNormAbsPath(x)),
179
       "the VNC bind address must be either a valid IP address or an absolute"
180
       " pathname", None, None),
181
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
182
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
183
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
184
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
185
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
186
    constants.HV_BOOT_ORDER:
187
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
188
    constants.HV_NIC_TYPE:
189
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
190
    constants.HV_DISK_TYPE:
191
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
192
    constants.HV_USB_MOUSE:
193
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
194
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
195
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
196
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
197
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
198
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
199
    constants.HV_DISK_CACHE:
200
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
201
    constants.HV_SECURITY_MODEL:
202
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
203
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
204
    constants.HV_KVM_FLAG:
205
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
206
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
207
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
208
    }
209

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

    
215
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
216

    
217
  ANCILLARY_FILES = [
218
    _KVM_NETWORK_SCRIPT,
219
    ]
220

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

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

232
    """
233
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
234

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

239
    """
240
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
241

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

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

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

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

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

    
267
    instance = None
268
    memory = 0
269
    vcpus = 0
270

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

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

    
285
    return (instance, memory, vcpus)
286

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

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

295
    """
296
    pidfile = self._InstancePidFile(instance_name)
297
    pid = utils.ReadPidFile(pidfile)
298

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

    
306
    return (pidfile, pid, alive)
307

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

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

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

321
    """
322
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
323

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

328
    """
329
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
330

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

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

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

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

347
    """
348
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
349

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

354
    """
355
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
356

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

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

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

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

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

409
    This can be used by any qemu-type hypervisor.
410

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

420
    """
421
    script = _WriteNetScript(instance, nic, seq)
422

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

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

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

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

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

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

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

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

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

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

469
    @return: list of tuples (name, id, memory, vcpus, stat, times)
470

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

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

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

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

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

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

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

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

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

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

    
570
    mouse_type = hvp[constants.HV_USB_MOUSE]
571
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
572

    
573
    if mouse_type:
574
      kvm_cmd.extend(['-usb'])
575
      kvm_cmd.extend(['-usbdevice', mouse_type])
576
    elif vnc_bind_address:
577
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
578

    
579
    if vnc_bind_address:
580
      if netutils.IsValidIP4(vnc_bind_address):
581
        if instance.network_port > constants.VNC_BASE_PORT:
582
          display = instance.network_port - constants.VNC_BASE_PORT
583
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
584
            vnc_arg = ':%d' % (display)
585
          else:
586
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
587
        else:
588
          logging.error("Network port is not a valid VNC display (%d < %d)."
589
                        " Not starting VNC", instance.network_port,
590
                        constants.VNC_BASE_PORT)
591
          vnc_arg = 'none'
592

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

    
607
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
608

    
609
      else:
610
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
611

    
612
      kvm_cmd.extend(['-vnc', vnc_arg])
613
    else:
614
      kvm_cmd.extend(['-nographic'])
615

    
616
    monitor_dev = ("unix:%s,server,nowait" %
617
                   self._InstanceMonitor(instance.name))
618
    kvm_cmd.extend(['-monitor', monitor_dev])
619
    if hvp[constants.HV_SERIAL_CONSOLE]:
620
      serial_dev = ('unix:%s,server,nowait' %
621
                    self._InstanceSerial(instance.name))
622
      kvm_cmd.extend(['-serial', serial_dev])
623
    else:
624
      kvm_cmd.extend(['-serial', 'none'])
625

    
626
    if hvp[constants.HV_USE_LOCALTIME]:
627
      kvm_cmd.extend(['-localtime'])
628

    
629
    if hvp[constants.HV_KVM_USE_CHROOT]:
630
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
631

    
632
    # Save the current instance nics, but defer their expansion as parameters,
633
    # as we'll need to generate executable temp files for them.
634
    kvm_nics = instance.nics
635
    hvparams = hvp
636

    
637
    return (kvm_cmd, kvm_nics, hvparams)
638

    
639
  def _WriteKVMRuntime(self, instance_name, data):
640
    """Write an instance's KVM runtime
641

642
    """
643
    try:
644
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
645
                      data=data)
646
    except EnvironmentError, err:
647
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
648

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

652
    """
653
    try:
654
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
655
    except EnvironmentError, err:
656
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
657
    return file_content
658

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

662
    """
663
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
664
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
665
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
666
    self._WriteKVMRuntime(instance.name, serialized_form)
667

    
668
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
669
    """Load an instance's KVM runtime
670

671
    """
672
    if not serialized_runtime:
673
      serialized_runtime = self._ReadKVMRuntime(instance.name)
674
    loaded_runtime = serializer.Load(serialized_runtime)
675
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
676
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
677
    return (kvm_cmd, kvm_nics, hvparams)
678

    
679
  def _RunKVMCmd(self, name, kvm_cmd):
680
    """Run the KVM cmd and check for errors
681

682
    @type name: string
683
    @param name: instance name
684
    @type kvm_cmd: list of strings
685
    @param kvm_cmd: runcmd input for kvm
686

687
    """
688
    result = utils.RunCmd(kvm_cmd)
689
    if result.failed:
690
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
691
                                   (name, result.fail_reason, result.output))
692
    if not self._InstancePidAlive(name)[2]:
693
      raise errors.HypervisorError("Failed to start instance %s" % name)
694

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

698
    @type incoming: tuple of strings
699
    @param incoming: (target_host_ip, port)
700

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

    
714
    temp_files = []
715

    
716
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
717
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
718

    
719
    # We know it's safe to run as a different user upon migration, so we'll use
720
    # the latest conf, from conf_hvp.
721
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
722
    if security_model == constants.HT_SM_USER:
723
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
724

    
725
    # We have reasons to believe changing something like the nic driver/type
726
    # upon migration won't exactly fly with the instance kernel, so for nic
727
    # related parameters we'll use up_hvp
728
    if not kvm_nics:
729
      kvm_cmd.extend(["-net", "none"])
730
    else:
731
      tap_extra = ""
732
      nic_type = up_hvp[constants.HV_NIC_TYPE]
733
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
734
        nic_model = "model=virtio"
735
        if up_hvp[constants.HV_VHOST_NET]:
736
          tap_extra = ",vhost=on"
737
      else:
738
        nic_model = "model=%s" % nic_type
739

    
740
      for nic_seq, nic in enumerate(kvm_nics):
741
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
742
        script = self._WriteNetScriptFile(instance, nic_seq, nic)
743
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
744
        kvm_cmd.extend(["-net", nic_val])
745
        kvm_cmd.extend(["-net", tap_val])
746
        temp_files.append(script)
747

    
748
    if incoming:
749
      target, port = incoming
750
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
751

    
752
    # Changing the vnc password doesn't bother the guest that much. At most it
753
    # will surprise people who connect to it. Whether positively or negatively
754
    # it's debatable.
755
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
756
    vnc_pwd = None
757
    if vnc_pwd_file:
758
      try:
759
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
760
      except EnvironmentError, err:
761
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
762
                                     % (vnc_pwd_file, err))
763

    
764
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
765
      utils.EnsureDirs([(self._InstanceChrootDir(name),
766
                         constants.SECURE_DIR_MODE)])
767

    
768
    if security_model == constants.HT_SM_POOL:
769
      ss = ssconf.SimpleStore()
770
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
771
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
772
      uid = uidpool.RequestUnusedUid(all_uids)
773
      try:
774
        username = pwd.getpwuid(uid.GetUid()).pw_name
775
        kvm_cmd.extend(["-runas", username])
776
        self._RunKVMCmd(name, kvm_cmd)
777
      except:
778
        uidpool.ReleaseUid(uid)
779
        raise
780
      else:
781
        uid.Unlock()
782
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
783
    else:
784
      self._RunKVMCmd(name, kvm_cmd)
785

    
786
    if vnc_pwd:
787
      change_cmd = 'change vnc password %s' % vnc_pwd
788
      self._CallMonitorCommand(instance.name, change_cmd)
789

    
790
    for filename in temp_files:
791
      utils.RemoveFile(filename)
792

    
793
  def StartInstance(self, instance, block_devices):
794
    """Start an instance.
795

796
    """
797
    self._CheckDown(instance.name)
798
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
799
    self._SaveKVMRuntime(instance, kvm_runtime)
800
    self._ExecuteKVMRuntime(instance, kvm_runtime)
801

    
802
  def _CallMonitorCommand(self, instance_name, command):
803
    """Invoke a command on the instance monitor.
804

805
    """
806
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
807
             (utils.ShellQuote(command),
808
              constants.SOCAT_PATH,
809
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
810
    result = utils.RunCmd(socat)
811
    if result.failed:
812
      msg = ("Failed to send command '%s' to instance %s."
813
             " output: %s, error: %s, fail_reason: %s" %
814
             (command, instance_name,
815
              result.stdout, result.stderr, result.fail_reason))
816
      raise errors.HypervisorError(msg)
817

    
818
    return result
819

    
820
  @classmethod
821
  def _GetKVMVersion(cls):
822
    """Return the installed KVM version
823

824
    @return: (version, v_maj, v_min, v_rev), or None
825

826
    """
827
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
828
    if result.failed:
829
      return None
830
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
831
    if not match:
832
      return None
833
    return (match.group(0), match.group(1), match.group(2), match.group(3))
834

    
835
  def StopInstance(self, instance, force=False, retry=False, name=None):
836
    """Stop an instance.
837

838
    """
839
    if name is not None and not force:
840
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
841
    if name is None:
842
      name = instance.name
843
      acpi = instance.hvparams[constants.HV_ACPI]
844
    else:
845
      acpi = False
846
    _, pid, alive = self._InstancePidAlive(name)
847
    if pid > 0 and alive:
848
      if force or not acpi:
849
        utils.KillProcess(pid)
850
      else:
851
        self._CallMonitorCommand(name, 'system_powerdown')
852

    
853
  def CleanupInstance(self, instance_name):
854
    """Cleanup after a stopped instance
855

856
    """
857
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
858
    if pid > 0 and alive:
859
      raise errors.HypervisorError("Cannot cleanup a live instance")
860
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
861

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

865
    """
866
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
867
    # socket the instance will stop, but now power up again. So we'll resort
868
    # to shutdown and restart.
869
    _, _, alive = self._InstancePidAlive(instance.name)
870
    if not alive:
871
      raise errors.HypervisorError("Failed to reboot instance %s:"
872
                                   " not running" % instance.name)
873
    # StopInstance will delete the saved KVM runtime so:
874
    # ...first load it...
875
    kvm_runtime = self._LoadKVMRuntime(instance)
876
    # ...now we can safely call StopInstance...
877
    if not self.StopInstance(instance):
878
      self.StopInstance(instance, force=True)
879
    # ...and finally we can save it again, and execute it...
880
    self._SaveKVMRuntime(instance, kvm_runtime)
881
    self._ExecuteKVMRuntime(instance, kvm_runtime)
882

    
883
  def MigrationInfo(self, instance):
884
    """Get instance information to perform a migration.
885

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

891
    """
892
    return self._ReadKVMRuntime(instance.name)
893

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

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

904
    """
905
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
906
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
907
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
908

    
909
  def FinalizeMigration(self, instance, info, success):
910
    """Finalize an instance migration.
911

912
    Stop the incoming mode KVM.
913

914
    @type instance: L{objects.Instance}
915
    @param instance: instance whose migration is being finalized
916

917
    """
918
    if success:
919
      self._WriteKVMRuntime(instance.name, info)
920
    else:
921
      self.StopInstance(instance, force=True)
922

    
923
  def MigrateInstance(self, instance, target, live):
924
    """Migrate an instance to a target node.
925

926
    The migration will not be attempted if the instance is not
927
    currently running.
928

929
    @type instance: L{objects.Instance}
930
    @param instance: the instance to be migrated
931
    @type target: string
932
    @param target: ip address of the target node
933
    @type live: boolean
934
    @param live: perform a live migration
935

936
    """
937
    instance_name = instance.name
938
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
939
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
940
    if not alive:
941
      raise errors.HypervisorError("Instance not running, cannot migrate")
942

    
943
    if not live:
944
      self._CallMonitorCommand(instance_name, 'stop')
945

    
946
    migrate_command = ('migrate_set_speed %dm' %
947
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
948
    self._CallMonitorCommand(instance_name, migrate_command)
949

    
950
    migrate_command = ('migrate_set_downtime %dms' %
951
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
952
    self._CallMonitorCommand(instance_name, migrate_command)
953

    
954
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
955
    self._CallMonitorCommand(instance_name, migrate_command)
956

    
957
    info_command = 'info migrate'
958
    done = False
959
    broken_answers = 0
960
    while not done:
961
      result = self._CallMonitorCommand(instance_name, info_command)
962
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
963
      if not match:
964
        broken_answers += 1
965
        if not result.stdout:
966
          logging.info("KVM: empty 'info migrate' result")
967
        else:
968
          logging.warning("KVM: unknown 'info migrate' result: %s",
969
                          result.stdout)
970
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
971
      else:
972
        status = match.group(1)
973
        if status == 'completed':
974
          done = True
975
        elif status == 'active':
976
          # reset the broken answers count
977
          broken_answers = 0
978
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
979
        elif status == 'failed' or status == 'cancelled':
980
          if not live:
981
            self._CallMonitorCommand(instance_name, 'cont')
982
          raise errors.HypervisorError("Migration %s at the kvm level" %
983
                                       status)
984
        else:
985
          logging.warning("KVM: unknown migration status '%s'", status)
986
          broken_answers += 1
987
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
988
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
989
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
990

    
991
    utils.KillProcess(pid)
992
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
993

    
994
  def GetNodeInfo(self):
995
    """Return information about the node.
996

997
    This is just a wrapper over the base GetLinuxNodeInfo method.
998

999
    @return: a dict with the following keys (values in MiB):
1000
          - memory_total: the total memory size on the node
1001
          - memory_free: the available memory on the node for instances
1002
          - memory_dom0: the memory used by the node itself, if available
1003

1004
    """
1005
    return self.GetLinuxNodeInfo()
1006

    
1007
  @classmethod
1008
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
1009
    """Return a command for connecting to the console of an instance.
1010

1011
    """
1012
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1013
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
1014
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
1015
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
1016
    else:
1017
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
1018

    
1019
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1020
    if vnc_bind_address:
1021
      if instance.network_port > constants.VNC_BASE_PORT:
1022
        display = instance.network_port - constants.VNC_BASE_PORT
1023
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
1024
                       " (display: %d)'" % (vnc_bind_address,
1025
                                            instance.network_port,
1026
                                            display))
1027
        shell_command = "%s; %s" % (vnc_command, shell_command)
1028

    
1029
    return shell_command
1030

    
1031
  def Verify(self):
1032
    """Verify the hypervisor.
1033

1034
    Check that the binary exists.
1035

1036
    """
1037
    if not os.path.exists(constants.KVM_PATH):
1038
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1039
    if not os.path.exists(constants.SOCAT_PATH):
1040
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1041

    
1042

    
1043
  @classmethod
1044
  def CheckParameterSyntax(cls, hvparams):
1045
    """Check the given parameters for validity.
1046

1047
    @type hvparams:  dict
1048
    @param hvparams: dictionary with parameter names/value
1049
    @raise errors.HypervisorError: when a parameter is not valid
1050

1051
    """
1052
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1053

    
1054
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1055
    if kernel_path:
1056
      if not hvparams[constants.HV_ROOT_PATH]:
1057
        raise errors.HypervisorError("Need a root partition for the instance,"
1058
                                     " if a kernel is defined")
1059

    
1060
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1061
        not hvparams[constants.HV_VNC_X509]):
1062
      raise errors.HypervisorError("%s must be defined, if %s is" %
1063
                                   (constants.HV_VNC_X509,
1064
                                    constants.HV_VNC_X509_VERIFY))
1065

    
1066
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1067
    if (boot_order == constants.HT_BO_CDROM and
1068
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1069
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1070
                                   " ISO path")
1071

    
1072
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1073
    if security_model == constants.HT_SM_USER:
1074
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1075
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1076
                                     " must be specified")
1077
    elif (security_model == constants.HT_SM_NONE or
1078
          security_model == constants.HT_SM_POOL):
1079
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1080
        raise errors.HypervisorError("Cannot have a security domain when the"
1081
                                     " security model is 'none' or 'pool'")
1082

    
1083
  @classmethod
1084
  def ValidateParameters(cls, hvparams):
1085
    """Check the given parameters for validity.
1086

1087
    @type hvparams:  dict
1088
    @param hvparams: dictionary with parameter names/value
1089
    @raise errors.HypervisorError: when a parameter is not valid
1090

1091
    """
1092
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1093

    
1094
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1095
    if security_model == constants.HT_SM_USER:
1096
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1097
      try:
1098
        pwd.getpwnam(username)
1099
      except KeyError:
1100
        raise errors.HypervisorError("Unknown security domain user %s"
1101
                                     % username)
1102

    
1103
  @classmethod
1104
  def PowercycleNode(cls):
1105
    """KVM powercycle, just a wrapper over Linux powercycle.
1106

1107
    """
1108
    cls.LinuxPowercycle()