Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ c0c3fa27

History | View | Annotate | Download (37 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.IsValidIP4(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_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
92
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
93
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
94
    constants.HV_DISK_CACHE:
95
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
96
    constants.HV_SECURITY_MODEL:
97
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
98
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
99
    constants.HV_KVM_FLAG:
100
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
101
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
102
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
103
    }
104

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

    
110
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
111

    
112
  ANCILLARY_FILES = [
113
    _KVM_NETWORK_SCRIPT,
114
    ]
115

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

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

127
    """
128
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
129

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

134
    """
135
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
136

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

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

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

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

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

    
162
    instance = None
163
    memory = 0
164
    vcpus = 0
165

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

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

    
180
    return (instance, memory, vcpus)
181

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

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

190
    """
191
    pidfile = self._InstancePidFile(instance_name)
192
    pid = utils.ReadPidFile(pidfile)
193

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

    
201
    return (pidfile, pid, alive)
202

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

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

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

216
    """
217
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
218

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

223
    """
224
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
225

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

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

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

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

242
    """
243
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
244

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

249
    """
250
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
251

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

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

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

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

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

303
    This can be used by any qemu-type hypervisor.
304

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

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

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

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

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

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

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

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

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

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

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

409
    @return: list of tuples (name, id, memory, vcpus, stat, times)
410

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

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

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

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

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

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

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

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

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

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

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

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

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

    
544
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
545

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

    
549
      kvm_cmd.extend(['-vnc', vnc_arg])
550

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

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

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

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

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

    
578
    return (kvm_cmd, kvm_nics, hvparams)
579

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

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

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

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

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

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

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

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

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

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

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

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

639
    @type incoming: tuple of strings
640
    @param incoming: (target_host_ip, port)
641

642
    """
643
    # Small _ExecuteKVMRuntime hv parameters programming howto:
644
    #  - conf_hvp contains the parameters as configured on ganeti. they might
645
    #    have changed since the instance started; only use them if the change
646
    #    won't affect the inside of the instance (which hasn't been rebooted).
647
    #  - up_hvp contains the parameters as they were when the instance was
648
    #    started, plus any new parameter which has been added between ganeti
649
    #    versions: it is paramount that those default to a value which won't
650
    #    affect the inside of the instance as well.
651
    conf_hvp = instance.hvparams
652
    name = instance.name
653
    self._CheckDown(name)
654

    
655
    temp_files = []
656

    
657
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
658
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
659

    
660
    # We know it's safe to run as a different user upon migration, so we'll use
661
    # the latest conf, from conf_hvp.
662
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
663
    if security_model == constants.HT_SM_USER:
664
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
665

    
666
    # We have reasons to believe changing something like the nic driver/type
667
    # upon migration won't exactly fly with the instance kernel, so for nic
668
    # related parameters we'll use up_hvp
669
    if not kvm_nics:
670
      kvm_cmd.extend(["-net", "none"])
671
    else:
672
      tap_extra = ""
673
      nic_type = up_hvp[constants.HV_NIC_TYPE]
674
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
675
        nic_model = "model=virtio"
676
        if up_hvp[constants.HV_VHOST_NET]:
677
          tap_extra = ",vhost=on"
678
      else:
679
        nic_model = "model=%s" % nic_type
680

    
681
      for nic_seq, nic in enumerate(kvm_nics):
682
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
683
        script = self._WriteNetScript(instance, nic_seq, nic)
684
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
685
        kvm_cmd.extend(["-net", nic_val])
686
        kvm_cmd.extend(["-net", tap_val])
687
        temp_files.append(script)
688

    
689
    if incoming:
690
      target, port = incoming
691
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
692

    
693
    # Changing the vnc password doesn't bother the guest that much. At most it
694
    # will surprise people who connect to it. Whether positively or negatively
695
    # it's debatable.
696
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
697
    vnc_pwd = None
698
    if vnc_pwd_file:
699
      try:
700
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
701
      except EnvironmentError, err:
702
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
703
                                     % (vnc_pwd_file, err))
704

    
705
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
706
      utils.EnsureDirs([(self._InstanceChrootDir(name),
707
                         constants.SECURE_DIR_MODE)])
708

    
709
    if security_model == constants.HT_SM_POOL:
710
      ss = ssconf.SimpleStore()
711
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
712
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
713
      uid = uidpool.RequestUnusedUid(all_uids)
714
      try:
715
        username = pwd.getpwuid(uid.GetUid()).pw_name
716
        kvm_cmd.extend(["-runas", username])
717
        self._RunKVMCmd(name, kvm_cmd)
718
      except:
719
        uidpool.ReleaseUid(uid)
720
        raise
721
      else:
722
        uid.Unlock()
723
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
724
    else:
725
      self._RunKVMCmd(name, kvm_cmd)
726

    
727
    if vnc_pwd:
728
      change_cmd = 'change vnc password %s' % vnc_pwd
729
      self._CallMonitorCommand(instance.name, change_cmd)
730

    
731
    for filename in temp_files:
732
      utils.RemoveFile(filename)
733

    
734
  def StartInstance(self, instance, block_devices):
735
    """Start an instance.
736

737
    """
738
    self._CheckDown(instance.name)
739
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
740
    self._SaveKVMRuntime(instance, kvm_runtime)
741
    self._ExecuteKVMRuntime(instance, kvm_runtime)
742

    
743
  def _CallMonitorCommand(self, instance_name, command):
744
    """Invoke a command on the instance monitor.
745

746
    """
747
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
748
             (utils.ShellQuote(command),
749
              constants.SOCAT_PATH,
750
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
751
    result = utils.RunCmd(socat)
752
    if result.failed:
753
      msg = ("Failed to send command '%s' to instance %s."
754
             " output: %s, error: %s, fail_reason: %s" %
755
             (command, instance_name,
756
              result.stdout, result.stderr, result.fail_reason))
757
      raise errors.HypervisorError(msg)
758

    
759
    return result
760

    
761
  def StopInstance(self, instance, force=False, retry=False, name=None):
762
    """Stop an instance.
763

764
    """
765
    if name is not None and not force:
766
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
767
    if name is None:
768
      name = instance.name
769
      acpi = instance.hvparams[constants.HV_ACPI]
770
    else:
771
      acpi = False
772
    _, pid, alive = self._InstancePidAlive(name)
773
    if pid > 0 and alive:
774
      if force or not acpi:
775
        utils.KillProcess(pid)
776
      else:
777
        self._CallMonitorCommand(name, 'system_powerdown')
778

    
779
  def CleanupInstance(self, instance_name):
780
    """Cleanup after a stopped instance
781

782
    """
783
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
784
    if pid > 0 and alive:
785
      raise errors.HypervisorError("Cannot cleanup a live instance")
786
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
787

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

791
    """
792
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
793
    # socket the instance will stop, but now power up again. So we'll resort
794
    # to shutdown and restart.
795
    _, _, alive = self._InstancePidAlive(instance.name)
796
    if not alive:
797
      raise errors.HypervisorError("Failed to reboot instance %s:"
798
                                   " not running" % instance.name)
799
    # StopInstance will delete the saved KVM runtime so:
800
    # ...first load it...
801
    kvm_runtime = self._LoadKVMRuntime(instance)
802
    # ...now we can safely call StopInstance...
803
    if not self.StopInstance(instance):
804
      self.StopInstance(instance, force=True)
805
    # ...and finally we can save it again, and execute it...
806
    self._SaveKVMRuntime(instance, kvm_runtime)
807
    self._ExecuteKVMRuntime(instance, kvm_runtime)
808

    
809
  def MigrationInfo(self, instance):
810
    """Get instance information to perform a migration.
811

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

817
    """
818
    return self._ReadKVMRuntime(instance.name)
819

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

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

830
    """
831
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
832
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
833
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
834

    
835
  def FinalizeMigration(self, instance, info, success):
836
    """Finalize an instance migration.
837

838
    Stop the incoming mode KVM.
839

840
    @type instance: L{objects.Instance}
841
    @param instance: instance whose migration is being finalized
842

843
    """
844
    if success:
845
      self._WriteKVMRuntime(instance.name, info)
846
    else:
847
      self.StopInstance(instance, force=True)
848

    
849
  def MigrateInstance(self, instance, target, live):
850
    """Migrate an instance to a target node.
851

852
    The migration will not be attempted if the instance is not
853
    currently running.
854

855
    @type instance: L{objects.Instance}
856
    @param instance: the instance to be migrated
857
    @type target: string
858
    @param target: ip address of the target node
859
    @type live: boolean
860
    @param live: perform a live migration
861

862
    """
863
    instance_name = instance.name
864
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
865
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
866
    if not alive:
867
      raise errors.HypervisorError("Instance not running, cannot migrate")
868

    
869
    if not utils.TcpPing(target, port, live_port_needed=True):
870
      raise errors.HypervisorError("Remote host %s not listening on port"
871
                                   " %s, cannot migrate" % (target, port))
872

    
873
    if not live:
874
      self._CallMonitorCommand(instance_name, 'stop')
875

    
876
    migrate_command = ('migrate_set_speed %dm' %
877
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
878
    self._CallMonitorCommand(instance_name, migrate_command)
879

    
880
    migrate_command = ('migrate_set_downtime %dms' %
881
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
882
    self._CallMonitorCommand(instance_name, migrate_command)
883

    
884
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
885
    self._CallMonitorCommand(instance_name, migrate_command)
886

    
887
    info_command = 'info migrate'
888
    done = False
889
    broken_answers = 0
890
    while not done:
891
      result = self._CallMonitorCommand(instance_name, info_command)
892
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
893
      if not match:
894
        broken_answers += 1
895
        if not result.stdout:
896
          logging.info("KVM: empty 'info migrate' result")
897
        else:
898
          logging.warning("KVM: unknown 'info migrate' result: %s",
899
                          result.stdout)
900
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
901
      else:
902
        status = match.group(1)
903
        if status == 'completed':
904
          done = True
905
        elif status == 'active':
906
          # reset the broken answers count
907
          broken_answers = 0
908
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
909
        elif status == 'failed' or status == 'cancelled':
910
          if not live:
911
            self._CallMonitorCommand(instance_name, 'cont')
912
          raise errors.HypervisorError("Migration %s at the kvm level" %
913
                                       status)
914
        else:
915
          logging.warning("KVM: unknown migration status '%s'", status)
916
          broken_answers += 1
917
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
918
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
919
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
920

    
921
    utils.KillProcess(pid)
922
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
923

    
924
  def GetNodeInfo(self):
925
    """Return information about the node.
926

927
    This is just a wrapper over the base GetLinuxNodeInfo method.
928

929
    @return: a dict with the following keys (values in MiB):
930
          - memory_total: the total memory size on the node
931
          - memory_free: the available memory on the node for instances
932
          - memory_dom0: the memory used by the node itself, if available
933

934
    """
935
    return self.GetLinuxNodeInfo()
936

    
937
  @classmethod
938
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
939
    """Return a command for connecting to the console of an instance.
940

941
    """
942
    if hvparams[constants.HV_SERIAL_CONSOLE]:
943
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
944
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
945
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
946
    else:
947
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
948

    
949
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
950
    if vnc_bind_address:
951
      if instance.network_port > constants.VNC_BASE_PORT:
952
        display = instance.network_port - constants.VNC_BASE_PORT
953
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
954
                       " (display: %d)'" % (vnc_bind_address,
955
                                            instance.network_port,
956
                                            display))
957
        shell_command = "%s; %s" % (vnc_command, shell_command)
958

    
959
    return shell_command
960

    
961
  def Verify(self):
962
    """Verify the hypervisor.
963

964
    Check that the binary exists.
965

966
    """
967
    if not os.path.exists(constants.KVM_PATH):
968
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
969
    if not os.path.exists(constants.SOCAT_PATH):
970
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
971

    
972

    
973
  @classmethod
974
  def CheckParameterSyntax(cls, hvparams):
975
    """Check the given parameters for validity.
976

977
    @type hvparams:  dict
978
    @param hvparams: dictionary with parameter names/value
979
    @raise errors.HypervisorError: when a parameter is not valid
980

981
    """
982
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
983

    
984
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
985
    if kernel_path:
986
      if not hvparams[constants.HV_ROOT_PATH]:
987
        raise errors.HypervisorError("Need a root partition for the instance,"
988
                                     " if a kernel is defined")
989

    
990
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
991
        not hvparams[constants.HV_VNC_X509]):
992
      raise errors.HypervisorError("%s must be defined, if %s is" %
993
                                   (constants.HV_VNC_X509,
994
                                    constants.HV_VNC_X509_VERIFY))
995

    
996
    boot_order = hvparams[constants.HV_BOOT_ORDER]
997
    if (boot_order == constants.HT_BO_CDROM and
998
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
999
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1000
                                   " ISO path")
1001

    
1002
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1003
    if security_model == constants.HT_SM_USER:
1004
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1005
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1006
                                     " must be specified")
1007
    elif (security_model == constants.HT_SM_NONE or
1008
          security_model == constants.HT_SM_POOL):
1009
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1010
        raise errors.HypervisorError("Cannot have a security domain when the"
1011
                                     " security model is 'none' or 'pool'")
1012

    
1013
  @classmethod
1014
  def ValidateParameters(cls, hvparams):
1015
    """Check the given parameters for validity.
1016

1017
    @type hvparams:  dict
1018
    @param hvparams: dictionary with parameter names/value
1019
    @raise errors.HypervisorError: when a parameter is not valid
1020

1021
    """
1022
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1023

    
1024
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1025
    if security_model == constants.HT_SM_USER:
1026
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1027
      try:
1028
        pwd.getpwnam(username)
1029
      except KeyError:
1030
        raise errors.HypervisorError("Unknown security domain user %s"
1031
                                     % username)
1032

    
1033
  @classmethod
1034
  def PowercycleNode(cls):
1035
    """KVM powercycle, just a wrapper over Linux powercycle.
1036

1037
    """
1038
    cls.LinuxPowercycle()