Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 84c08e4e

History | View | Annotate | Download (35.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008 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

    
45

    
46
class KVMHypervisor(hv_base.BaseHypervisor):
47
  """KVM hypervisor interface"""
48
  CAN_MIGRATE = True
49

    
50
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
51
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
52
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
53
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
54
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
55
  # KVM instances with chroot enabled are started in empty chroot directories.
56
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
57
  # After an instance is stopped, its chroot directory is removed.
58
  # If the chroot directory is not empty, it can't be removed.
59
  # A non-empty chroot directory indicates a possible security incident.
60
  # To support forensics, the non-empty chroot directory is quarantined in
61
  # a separate directory, called 'chroot-quarantine'.
62
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
63
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
64
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
65

    
66
  PARAMETERS = {
67
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
68
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
69
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
70
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
71
    constants.HV_ACPI: hv_base.NO_CHECK,
72
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
73
    constants.HV_VNC_BIND_ADDRESS:
74
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
75
       "the VNC bind address must be either a valid IP address or an absolute"
76
       " pathname", None, None),
77
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
78
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
79
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
80
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
81
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
82
    constants.HV_BOOT_ORDER:
83
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
84
    constants.HV_NIC_TYPE:
85
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
86
    constants.HV_DISK_TYPE:
87
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
88
    constants.HV_USB_MOUSE:
89
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
90
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
91
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
92
    constants.HV_DISK_CACHE:
93
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
94
    constants.HV_SECURITY_MODEL:
95
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
96
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
97
    constants.HV_KVM_FLAG:
98
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
99
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
100
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
101
    }
102

    
103
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
104
                                    re.M | re.I)
105
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
106
  _MIGRATION_INFO_RETRY_DELAY = 2
107

    
108
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
109

    
110
  ANCILLARY_FILES = [
111
    _KVM_NETWORK_SCRIPT,
112
    ]
113

    
114
  def __init__(self):
115
    hv_base.BaseHypervisor.__init__(self)
116
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
117
    # in a tmpfs filesystem or has been otherwise wiped out.
118
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
119
    utils.EnsureDirs(dirs)
120

    
121
  @classmethod
122
  def _InstancePidFile(cls, instance_name):
123
    """Returns the instance pidfile.
124

125
    """
126
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
127

    
128
  @classmethod
129
  def _InstanceUidFile(cls, instance_name):
130
    """Returns the instance uidfile.
131

132
    """
133
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
134

    
135
  @classmethod
136
  def _InstancePidInfo(cls, pid):
137
    """Check pid file for instance information.
138

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

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

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

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

    
160
    instance = None
161
    memory = 0
162
    vcpus = 0
163

    
164
    arg_list = cmdline.split('\x00')
165
    while arg_list:
166
      arg =  arg_list.pop(0)
167
      if arg == "-name":
168
        instance = arg_list.pop(0)
169
      elif arg == "-m":
170
        memory = int(arg_list.pop(0))
171
      elif arg == "-smp":
172
        vcpus = int(arg_list.pop(0))
173

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

    
178
    return (instance, memory, vcpus)
179

    
180
  def _InstancePidAlive(self, instance_name):
181
    """Returns the instance pidfile, pid, and liveness.
182

183
    @type instance_name: string
184
    @param instance_name: instance name
185
    @rtype: tuple
186
    @return: (pid file name, pid, liveness)
187

188
    """
189
    pidfile = self._InstancePidFile(instance_name)
190
    pid = utils.ReadPidFile(pidfile)
191

    
192
    alive = False
193
    try:
194
      cmd_instance = self._InstancePidInfo(pid)[0]
195
      alive = (cmd_instance == instance_name)
196
    except errors.HypervisorError:
197
      pass
198

    
199
    return (pidfile, pid, alive)
200

    
201
  def _CheckDown(self, instance_name):
202
    """Raises an error unless the given instance is down.
203

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

    
210
  @classmethod
211
  def _InstanceMonitor(cls, instance_name):
212
    """Returns the instance monitor socket name
213

214
    """
215
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
216

    
217
  @classmethod
218
  def _InstanceSerial(cls, instance_name):
219
    """Returns the instance serial socket name
220

221
    """
222
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
223

    
224
  @staticmethod
225
  def _SocatUnixConsoleParams():
226
    """Returns the correct parameters for socat
227

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

230
    """
231
    if constants.SOCAT_USE_ESCAPE:
232
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
233
    else:
234
      return "echo=0,icanon=0"
235

    
236
  @classmethod
237
  def _InstanceKVMRuntime(cls, instance_name):
238
    """Returns the instance KVM runtime filename
239

240
    """
241
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
242

    
243
  @classmethod
244
  def _InstanceChrootDir(cls, instance_name):
245
    """Returns the name of the KVM chroot dir of the instance
246

247
    """
248
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
249

    
250
  @classmethod
251
  def _TryReadUidFile(cls, uid_file):
252
    """Try to read a uid file
253

254
    """
255
    if os.path.exists(uid_file):
256
      try:
257
        uid = int(utils.ReadOneLineFile(uid_file))
258
        return uid
259
      except EnvironmentError:
260
        logging.warning("Can't read uid file", exc_info=True)
261
      except (TypeError, ValueError):
262
        logging.warning("Can't parse uid file contents", exc_info=True)
263
    return None
264

    
265
  @classmethod
266
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
267
    """Removes an instance's rutime sockets/files/dirs.
268

269
    """
270
    utils.RemoveFile(pidfile)
271
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
272
    utils.RemoveFile(cls._InstanceSerial(instance_name))
273
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
274
    uid_file = cls._InstanceUidFile(instance_name)
275
    uid = cls._TryReadUidFile(uid_file)
276
    utils.RemoveFile(uid_file)
277
    if uid is not None:
278
      uidpool.ReleaseUid(uid)
279
    try:
280
      chroot_dir = cls._InstanceChrootDir(instance_name)
281
      utils.RemoveDir(chroot_dir)
282
    except OSError, err:
283
      if err.errno == errno.ENOTEMPTY:
284
        # The chroot directory is expected to be empty, but it isn't.
285
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
286
                                          prefix="%s-%s-" %
287
                                          (instance_name,
288
                                           utils.TimestampForFilename()))
289
        logging.warning("The chroot directory of instance %s can not be"
290
                        " removed as it is not empty. Moving it to the"
291
                        " quarantine instead. Please investigate the"
292
                        " contents (%s) and clean up manually",
293
                        instance_name, new_chroot_dir)
294
        utils.RenameFile(chroot_dir, new_chroot_dir)
295
      else:
296
        raise
297

    
298
  def _WriteNetScript(self, instance, seq, nic):
299
    """Write a script to connect a net interface to the proper bridge.
300

301
    This can be used by any qemu-type hypervisor.
302

303
    @param instance: instance we're acting on
304
    @type instance: instance object
305
    @param seq: nic sequence number
306
    @type seq: int
307
    @param nic: nic we're acting on
308
    @type nic: nic object
309
    @return: netscript file name
310
    @rtype: string
311

312
    """
313
    script = StringIO()
314
    script.write("#!/bin/sh\n")
315
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
316
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
317
    script.write("export INSTANCE=%s\n" % instance.name)
318
    script.write("export MAC=%s\n" % nic.mac)
319
    if nic.ip:
320
      script.write("export IP=%s\n" % nic.ip)
321
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
322
    if nic.nicparams[constants.NIC_LINK]:
323
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
324
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
325
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
326
    script.write("export INTERFACE=$1\n")
327
    if instance.tags:
328
      script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
329
    # TODO: make this configurable at ./configure time
330
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
331
    script.write("  # Execute the user-specific vif file\n")
332
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
333
    script.write("else\n")
334
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
335
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
336
      script.write("  # Connect the interface to the bridge\n")
337
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
338
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
339
      if not nic.ip:
340
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
341
      script.write("  # Route traffic targeted at the IP to the interface\n")
342
      if nic.nicparams[constants.NIC_LINK]:
343
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
344
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
345
        script.write("  ip route replace $IP table $LINK proto static"
346
                     " dev $INTERFACE\n")
347
      else:
348
        script.write("  ip route replace $IP proto static"
349
                     " dev $INTERFACE\n")
350
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
351
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
352
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
353
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
354
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
355
      script.write("  fi\n")
356
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
357
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
358
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
359
      script.write("  fi\n")
360
    script.write("fi\n\n")
361
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
362
    # mounted noexec sometimes, so we'll have to find another place.
363
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
364
    tmpfile = os.fdopen(tmpfd, 'w')
365
    try:
366
      tmpfile.write(script.getvalue())
367
    finally:
368
      tmpfile.close()
369
    os.chmod(tmpfile_name, 0755)
370
    return tmpfile_name
371

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

375
    We can do this by listing our live instances directory and
376
    checking whether the associated kvm process is still alive.
377

378
    """
379
    result = []
380
    for name in os.listdir(self._PIDS_DIR):
381
      if self._InstancePidAlive(name)[2]:
382
        result.append(name)
383
    return result
384

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

388
    @type instance_name: string
389
    @param instance_name: the instance name
390
    @rtype: tuple of strings
391
    @return: (name, id, memory, vcpus, stat, times)
392

393
    """
394
    _, pid, alive = self._InstancePidAlive(instance_name)
395
    if not alive:
396
      return None
397

    
398
    _, memory, vcpus = self._InstancePidInfo(pid)
399
    stat = "---b-"
400
    times = "0"
401

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

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

407
    @return: list of tuples (name, id, memory, vcpus, stat, times)
408

409
    """
410
    data = []
411
    for name in os.listdir(self._PIDS_DIR):
412
      try:
413
        info = self.GetInstanceInfo(name)
414
      except errors.HypervisorError:
415
        continue
416
      if info:
417
        data.append(info)
418
    return data
419

    
420
  def _GenerateKVMRuntime(self, instance, block_devices):
421
    """Generate KVM information to start an instance.
422

423
    """
424
    pidfile  = self._InstancePidFile(instance.name)
425
    kvm = constants.KVM_PATH
426
    kvm_cmd = [kvm]
427
    # used just by the vnc server, if enabled
428
    kvm_cmd.extend(['-name', instance.name])
429
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
430
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
431
    kvm_cmd.extend(['-pidfile', pidfile])
432
    kvm_cmd.extend(['-daemonize'])
433
    if not instance.hvparams[constants.HV_ACPI]:
434
      kvm_cmd.extend(['-no-acpi'])
435

    
436
    hvp = instance.hvparams
437
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
438
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
439
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
440

    
441
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
442
      kvm_cmd.extend(["-enable-kvm"])
443
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
444
      kvm_cmd.extend(["-disable-kvm"])
445

    
446
    if boot_network:
447
      kvm_cmd.extend(['-boot', 'n'])
448

    
449
    disk_type = hvp[constants.HV_DISK_TYPE]
450
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
451
      if_val = ',if=virtio'
452
    else:
453
      if_val = ',if=%s' % disk_type
454
    # Cache mode
455
    disk_cache = hvp[constants.HV_DISK_CACHE]
456
    if disk_cache != constants.HT_CACHE_DEFAULT:
457
      cache_val = ",cache=%s" % disk_cache
458
    else:
459
      cache_val = ""
460
    for cfdev, dev_path in block_devices:
461
      if cfdev.mode != constants.DISK_RDWR:
462
        raise errors.HypervisorError("Instance has read-only disks which"
463
                                     " are not supported by KVM")
464
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
465
      if boot_disk:
466
        kvm_cmd.extend(['-boot', 'c'])
467
        if disk_type != constants.HT_DISK_IDE:
468
          boot_val = ',boot=on'
469
        else:
470
          boot_val = ''
471
        # We only boot from the first disk
472
        boot_disk = False
473
      else:
474
        boot_val = ''
475

    
476
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
477
                                                cache_val)
478
      kvm_cmd.extend(['-drive', drive_val])
479

    
480
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
481
    if iso_image:
482
      options = ',format=raw,media=cdrom'
483
      if boot_cdrom:
484
        kvm_cmd.extend(['-boot', 'd'])
485
        if disk_type != constants.HT_DISK_IDE:
486
          options = '%s,boot=on' % options
487
      else:
488
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
489
          if_val = ',if=virtio'
490
        else:
491
          if_val = ',if=%s' % disk_type
492
        options = '%s%s' % (options, if_val)
493
      drive_val = 'file=%s%s' % (iso_image, options)
494
      kvm_cmd.extend(['-drive', drive_val])
495

    
496
    kernel_path = hvp[constants.HV_KERNEL_PATH]
497
    if kernel_path:
498
      kvm_cmd.extend(['-kernel', kernel_path])
499
      initrd_path = hvp[constants.HV_INITRD_PATH]
500
      if initrd_path:
501
        kvm_cmd.extend(['-initrd', initrd_path])
502
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
503
                     hvp[constants.HV_KERNEL_ARGS]]
504
      if hvp[constants.HV_SERIAL_CONSOLE]:
505
        root_append.append('console=ttyS0,38400')
506
      kvm_cmd.extend(['-append', ' '.join(root_append)])
507

    
508
    mouse_type = hvp[constants.HV_USB_MOUSE]
509
    if mouse_type:
510
      kvm_cmd.extend(['-usb'])
511
      kvm_cmd.extend(['-usbdevice', mouse_type])
512

    
513
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
514
    if vnc_bind_address:
515
      if utils.IsValidIP(vnc_bind_address):
516
        if instance.network_port > constants.VNC_BASE_PORT:
517
          display = instance.network_port - constants.VNC_BASE_PORT
518
          if vnc_bind_address == '0.0.0.0':
519
            vnc_arg = ':%d' % (display)
520
          else:
521
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
522
        else:
523
          logging.error("Network port is not a valid VNC display (%d < %d)."
524
                        " Not starting VNC", instance.network_port,
525
                        constants.VNC_BASE_PORT)
526
          vnc_arg = 'none'
527

    
528
        # Only allow tls and other option when not binding to a file, for now.
529
        # kvm/qemu gets confused otherwise about the filename to use.
530
        vnc_append = ''
531
        if hvp[constants.HV_VNC_TLS]:
532
          vnc_append = '%s,tls' % vnc_append
533
          if hvp[constants.HV_VNC_X509_VERIFY]:
534
            vnc_append = '%s,x509verify=%s' % (vnc_append,
535
                                               hvp[constants.HV_VNC_X509])
536
          elif hvp[constants.HV_VNC_X509]:
537
            vnc_append = '%s,x509=%s' % (vnc_append,
538
                                         hvp[constants.HV_VNC_X509])
539
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
540
          vnc_append = '%s,password' % vnc_append
541

    
542
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
543

    
544
      else:
545
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
546

    
547
      kvm_cmd.extend(['-vnc', vnc_arg])
548

    
549
      # Also add a tablet USB device to act as a mouse
550
      # This solves various mouse alignment issues
551
      kvm_cmd.extend(['-usbdevice', 'tablet'])
552
    else:
553
      kvm_cmd.extend(['-nographic'])
554

    
555
    monitor_dev = ("unix:%s,server,nowait" %
556
                   self._InstanceMonitor(instance.name))
557
    kvm_cmd.extend(['-monitor', monitor_dev])
558
    if hvp[constants.HV_SERIAL_CONSOLE]:
559
      serial_dev = ('unix:%s,server,nowait' %
560
                    self._InstanceSerial(instance.name))
561
      kvm_cmd.extend(['-serial', serial_dev])
562
    else:
563
      kvm_cmd.extend(['-serial', 'none'])
564

    
565
    if hvp[constants.HV_USE_LOCALTIME]:
566
      kvm_cmd.extend(['-localtime'])
567

    
568
    if hvp[constants.HV_KVM_USE_CHROOT]:
569
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
570

    
571
    # Save the current instance nics, but defer their expansion as parameters,
572
    # as we'll need to generate executable temp files for them.
573
    kvm_nics = instance.nics
574
    hvparams = hvp
575

    
576
    return (kvm_cmd, kvm_nics, hvparams)
577

    
578
  def _WriteKVMRuntime(self, instance_name, data):
579
    """Write an instance's KVM runtime
580

581
    """
582
    try:
583
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
584
                      data=data)
585
    except EnvironmentError, err:
586
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
587

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

591
    """
592
    try:
593
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
594
    except EnvironmentError, err:
595
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
596
    return file_content
597

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

601
    """
602
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
603
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
604
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
605
    self._WriteKVMRuntime(instance.name, serialized_form)
606

    
607
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
608
    """Load an instance's KVM runtime
609

610
    """
611
    if not serialized_runtime:
612
      serialized_runtime = self._ReadKVMRuntime(instance.name)
613
    loaded_runtime = serializer.Load(serialized_runtime)
614
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
615
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
616
    return (kvm_cmd, kvm_nics, hvparams)
617

    
618
  def _RunKVMCmd(self, name, kvm_cmd):
619
    """Run the KVM cmd and check for errors
620

621
    @type name: string
622
    @param name: instance name
623
    @type kvm_cmd: list of strings
624
    @param kvm_cmd: runcmd input for kvm
625

626
    """
627
    result = utils.RunCmd(kvm_cmd)
628
    if result.failed:
629
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
630
                                   (name, result.fail_reason, result.output))
631
    if not self._InstancePidAlive(name)[2]:
632
      raise errors.HypervisorError("Failed to start instance %s" % name)
633

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

637
    @type incoming: tuple of strings
638
    @param incoming: (target_host_ip, port)
639

640
    """
641
    hvp = instance.hvparams
642
    name = instance.name
643
    self._CheckDown(name)
644

    
645
    temp_files = []
646

    
647
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
648

    
649
    security_model = hvp[constants.HV_SECURITY_MODEL]
650
    if security_model == constants.HT_SM_USER:
651
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
652

    
653
    if not kvm_nics:
654
      kvm_cmd.extend(["-net", "none"])
655
    else:
656
      tap_extra = ""
657
      nic_type = hvparams[constants.HV_NIC_TYPE]
658
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
659
        nic_model = "model=virtio"
660
        if hvparams[constants.HV_VHOST_NET]:
661
          tap_extra = ",vhost=on"
662
      else:
663
        nic_model = "model=%s" % nic_type
664

    
665
      for nic_seq, nic in enumerate(kvm_nics):
666
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
667
        script = self._WriteNetScript(instance, nic_seq, nic)
668
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
669
        kvm_cmd.extend(["-net", nic_val])
670
        kvm_cmd.extend(["-net", tap_val])
671
        temp_files.append(script)
672

    
673
    if incoming:
674
      target, port = incoming
675
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
676

    
677
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
678
    vnc_pwd = None
679
    if vnc_pwd_file:
680
      try:
681
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
682
      except EnvironmentError, err:
683
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
684
                                     % (vnc_pwd_file, err))
685

    
686
    if hvp[constants.HV_KVM_USE_CHROOT]:
687
      utils.EnsureDirs([(self._InstanceChrootDir(name),
688
                         constants.SECURE_DIR_MODE)])
689

    
690
    if security_model == constants.HT_SM_POOL:
691
      ss = ssconf.SimpleStore()
692
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
693
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
694
      uid = uidpool.RequestUnusedUid(all_uids)
695
      try:
696
        username = pwd.getpwuid(uid.GetUid()).pw_name
697
        kvm_cmd.extend(["-runas", username])
698
        self._RunKVMCmd(name, kvm_cmd)
699
      except:
700
        uidpool.ReleaseUid(uid)
701
        raise
702
      else:
703
        uid.Unlock()
704
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
705
    else:
706
      self._RunKVMCmd(name, kvm_cmd)
707

    
708
    if vnc_pwd:
709
      change_cmd = 'change vnc password %s' % vnc_pwd
710
      self._CallMonitorCommand(instance.name, change_cmd)
711

    
712
    for filename in temp_files:
713
      utils.RemoveFile(filename)
714

    
715
  def StartInstance(self, instance, block_devices):
716
    """Start an instance.
717

718
    """
719
    self._CheckDown(instance.name)
720
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
721
    self._SaveKVMRuntime(instance, kvm_runtime)
722
    self._ExecuteKVMRuntime(instance, kvm_runtime)
723

    
724
  def _CallMonitorCommand(self, instance_name, command):
725
    """Invoke a command on the instance monitor.
726

727
    """
728
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
729
             (utils.ShellQuote(command),
730
              constants.SOCAT_PATH,
731
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
732
    result = utils.RunCmd(socat)
733
    if result.failed:
734
      msg = ("Failed to send command '%s' to instance %s."
735
             " output: %s, error: %s, fail_reason: %s" %
736
             (command, instance_name,
737
              result.stdout, result.stderr, result.fail_reason))
738
      raise errors.HypervisorError(msg)
739

    
740
    return result
741

    
742
  def StopInstance(self, instance, force=False, retry=False, name=None):
743
    """Stop an instance.
744

745
    """
746
    if name is not None and not force:
747
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
748
    if name is None:
749
      name = instance.name
750
      acpi = instance.hvparams[constants.HV_ACPI]
751
    else:
752
      acpi = False
753
    _, pid, alive = self._InstancePidAlive(name)
754
    if pid > 0 and alive:
755
      if force or not acpi:
756
        utils.KillProcess(pid)
757
      else:
758
        self._CallMonitorCommand(name, 'system_powerdown')
759

    
760
  def CleanupInstance(self, instance_name):
761
    """Cleanup after a stopped instance
762

763
    """
764
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
765
    if pid > 0 and alive:
766
      raise errors.HypervisorError("Cannot cleanup a live instance")
767
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
768

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

772
    """
773
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
774
    # socket the instance will stop, but now power up again. So we'll resort
775
    # to shutdown and restart.
776
    _, _, alive = self._InstancePidAlive(instance.name)
777
    if not alive:
778
      raise errors.HypervisorError("Failed to reboot instance %s:"
779
                                   " not running" % instance.name)
780
    # StopInstance will delete the saved KVM runtime so:
781
    # ...first load it...
782
    kvm_runtime = self._LoadKVMRuntime(instance)
783
    # ...now we can safely call StopInstance...
784
    if not self.StopInstance(instance):
785
      self.StopInstance(instance, force=True)
786
    # ...and finally we can save it again, and execute it...
787
    self._SaveKVMRuntime(instance, kvm_runtime)
788
    self._ExecuteKVMRuntime(instance, kvm_runtime)
789

    
790
  def MigrationInfo(self, instance):
791
    """Get instance information to perform a migration.
792

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

798
    """
799
    return self._ReadKVMRuntime(instance.name)
800

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

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

811
    """
812
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
813
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
814
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
815

    
816
  def FinalizeMigration(self, instance, info, success):
817
    """Finalize an instance migration.
818

819
    Stop the incoming mode KVM.
820

821
    @type instance: L{objects.Instance}
822
    @param instance: instance whose migration is being aborted
823

824
    """
825
    if success:
826
      self._WriteKVMRuntime(instance.name, info)
827
    else:
828
      self.StopInstance(instance, force=True)
829

    
830
  def MigrateInstance(self, instance, target, live):
831
    """Migrate an instance to a target node.
832

833
    The migration will not be attempted if the instance is not
834
    currently running.
835

836
    @type instance: L{objects.Instance}
837
    @param instance: the instance to be migrated
838
    @type target: string
839
    @param target: ip address of the target node
840
    @type live: boolean
841
    @param live: perform a live migration
842

843
    """
844
    instance_name = instance.name
845
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
846
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
847
    if not alive:
848
      raise errors.HypervisorError("Instance not running, cannot migrate")
849

    
850
    if not utils.TcpPing(target, port, live_port_needed=True):
851
      raise errors.HypervisorError("Remote host %s not listening on port"
852
                                   " %s, cannot migrate" % (target, port))
853

    
854
    if not live:
855
      self._CallMonitorCommand(instance_name, 'stop')
856

    
857
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
858
    self._CallMonitorCommand(instance_name, migrate_command)
859

    
860
    info_command = 'info migrate'
861
    done = False
862
    broken_answers = 0
863
    while not done:
864
      result = self._CallMonitorCommand(instance_name, info_command)
865
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
866
      if not match:
867
        broken_answers += 1
868
        if not result.stdout:
869
          logging.info("KVM: empty 'info migrate' result")
870
        else:
871
          logging.warning("KVM: unknown 'info migrate' result: %s",
872
                          result.stdout)
873
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
874
      else:
875
        status = match.group(1)
876
        if status == 'completed':
877
          done = True
878
        elif status == 'active':
879
          # reset the broken answers count
880
          broken_answers = 0
881
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
882
        elif status == 'failed' or status == 'cancelled':
883
          if not live:
884
            self._CallMonitorCommand(instance_name, 'cont')
885
          raise errors.HypervisorError("Migration %s at the kvm level" %
886
                                       status)
887
        else:
888
          logging.warning("KVM: unknown migration status '%s'", status)
889
          broken_answers += 1
890
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
891
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
892
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
893

    
894
    utils.KillProcess(pid)
895
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
896

    
897
  def GetNodeInfo(self):
898
    """Return information about the node.
899

900
    This is just a wrapper over the base GetLinuxNodeInfo method.
901

902
    @return: a dict with the following keys (values in MiB):
903
          - memory_total: the total memory size on the node
904
          - memory_free: the available memory on the node for instances
905
          - memory_dom0: the memory used by the node itself, if available
906

907
    """
908
    return self.GetLinuxNodeInfo()
909

    
910
  @classmethod
911
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
912
    """Return a command for connecting to the console of an instance.
913

914
    """
915
    if hvparams[constants.HV_SERIAL_CONSOLE]:
916
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
917
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
918
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
919
    else:
920
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
921

    
922
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
923
    if vnc_bind_address:
924
      if instance.network_port > constants.VNC_BASE_PORT:
925
        display = instance.network_port - constants.VNC_BASE_PORT
926
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
927
                       " (display: %d)'" % (vnc_bind_address,
928
                                            instance.network_port,
929
                                            display))
930
        shell_command = "%s; %s" % (vnc_command, shell_command)
931

    
932
    return shell_command
933

    
934
  def Verify(self):
935
    """Verify the hypervisor.
936

937
    Check that the binary exists.
938

939
    """
940
    if not os.path.exists(constants.KVM_PATH):
941
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
942
    if not os.path.exists(constants.SOCAT_PATH):
943
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
944

    
945

    
946
  @classmethod
947
  def CheckParameterSyntax(cls, hvparams):
948
    """Check the given parameters for validity.
949

950
    @type hvparams:  dict
951
    @param hvparams: dictionary with parameter names/value
952
    @raise errors.HypervisorError: when a parameter is not valid
953

954
    """
955
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
956

    
957
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
958
    if kernel_path:
959
      if not hvparams[constants.HV_ROOT_PATH]:
960
        raise errors.HypervisorError("Need a root partition for the instance,"
961
                                     " if a kernel is defined")
962

    
963
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
964
        not hvparams[constants.HV_VNC_X509]):
965
      raise errors.HypervisorError("%s must be defined, if %s is" %
966
                                   (constants.HV_VNC_X509,
967
                                    constants.HV_VNC_X509_VERIFY))
968

    
969
    boot_order = hvparams[constants.HV_BOOT_ORDER]
970
    if (boot_order == constants.HT_BO_CDROM and
971
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
972
      raise errors.HypervisorError("Cannot boot from cdrom without an"
973
                                   " ISO path")
974

    
975
    security_model = hvparams[constants.HV_SECURITY_MODEL]
976
    if security_model == constants.HT_SM_USER:
977
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
978
        raise errors.HypervisorError("A security domain (user to run kvm as)"
979
                                     " must be specified")
980
    elif (security_model == constants.HT_SM_NONE or
981
          security_model == constants.HT_SM_POOL):
982
      if hvparams[constants.HV_SECURITY_DOMAIN]:
983
        raise errors.HypervisorError("Cannot have a security domain when the"
984
                                     " security model is 'none' or 'pool'")
985

    
986
  @classmethod
987
  def ValidateParameters(cls, hvparams):
988
    """Check the given parameters for validity.
989

990
    @type hvparams:  dict
991
    @param hvparams: dictionary with parameter names/value
992
    @raise errors.HypervisorError: when a parameter is not valid
993

994
    """
995
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
996

    
997
    security_model = hvparams[constants.HV_SECURITY_MODEL]
998
    if security_model == constants.HT_SM_USER:
999
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1000
      try:
1001
        pwd.getpwnam(username)
1002
      except KeyError:
1003
        raise errors.HypervisorError("Unknown security domain user %s"
1004
                                     % username)
1005

    
1006
  @classmethod
1007
  def PowercycleNode(cls):
1008
    """KVM powercycle, just a wrapper over Linux powercycle.
1009

1010
    """
1011
    cls.LinuxPowercycle()