Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 78d019a0

History | View | Annotate | Download (36.8 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_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.IsValidIP(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 == '0.0.0.0':
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
    else:
551
      kvm_cmd.extend(['-nographic'])
552

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

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

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

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

    
574
    return (kvm_cmd, kvm_nics, hvparams)
575

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

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

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

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

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

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

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

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

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

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

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

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

635
    @type incoming: tuple of strings
636
    @param incoming: (target_host_ip, port)
637

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

    
651
    temp_files = []
652

    
653
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
654
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
655

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

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

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

    
685
    if incoming:
686
      target, port = incoming
687
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
688

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

    
701
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
702
      utils.EnsureDirs([(self._InstanceChrootDir(name),
703
                         constants.SECURE_DIR_MODE)])
704

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

    
723
    if vnc_pwd:
724
      change_cmd = 'change vnc password %s' % vnc_pwd
725
      self._CallMonitorCommand(instance.name, change_cmd)
726

    
727
    for filename in temp_files:
728
      utils.RemoveFile(filename)
729

    
730
  def StartInstance(self, instance, block_devices):
731
    """Start an instance.
732

733
    """
734
    self._CheckDown(instance.name)
735
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
736
    self._SaveKVMRuntime(instance, kvm_runtime)
737
    self._ExecuteKVMRuntime(instance, kvm_runtime)
738

    
739
  def _CallMonitorCommand(self, instance_name, command):
740
    """Invoke a command on the instance monitor.
741

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

    
755
    return result
756

    
757
  def StopInstance(self, instance, force=False, retry=False, name=None):
758
    """Stop an instance.
759

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

    
775
  def CleanupInstance(self, instance_name):
776
    """Cleanup after a stopped instance
777

778
    """
779
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
780
    if pid > 0 and alive:
781
      raise errors.HypervisorError("Cannot cleanup a live instance")
782
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
783

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

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

    
805
  def MigrationInfo(self, instance):
806
    """Get instance information to perform a migration.
807

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

813
    """
814
    return self._ReadKVMRuntime(instance.name)
815

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

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

826
    """
827
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
828
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
829
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
830

    
831
  def FinalizeMigration(self, instance, info, success):
832
    """Finalize an instance migration.
833

834
    Stop the incoming mode KVM.
835

836
    @type instance: L{objects.Instance}
837
    @param instance: instance whose migration is being finalized
838

839
    """
840
    if success:
841
      self._WriteKVMRuntime(instance.name, info)
842
    else:
843
      self.StopInstance(instance, force=True)
844

    
845
  def MigrateInstance(self, instance, target, live):
846
    """Migrate an instance to a target node.
847

848
    The migration will not be attempted if the instance is not
849
    currently running.
850

851
    @type instance: L{objects.Instance}
852
    @param instance: the instance to be migrated
853
    @type target: string
854
    @param target: ip address of the target node
855
    @type live: boolean
856
    @param live: perform a live migration
857

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

    
865
    if not utils.TcpPing(target, port, live_port_needed=True):
866
      raise errors.HypervisorError("Remote host %s not listening on port"
867
                                   " %s, cannot migrate" % (target, port))
868

    
869
    if not live:
870
      self._CallMonitorCommand(instance_name, 'stop')
871

    
872
    migrate_command = ('migrate_set_speed %dm' %
873
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
874
    self._CallMonitorCommand(instance_name, migrate_command)
875

    
876
    migrate_command = ('migrate_set_downtime %dms' %
877
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
878
    self._CallMonitorCommand(instance_name, migrate_command)
879

    
880
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
881
    self._CallMonitorCommand(instance_name, migrate_command)
882

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

    
917
    utils.KillProcess(pid)
918
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
919

    
920
  def GetNodeInfo(self):
921
    """Return information about the node.
922

923
    This is just a wrapper over the base GetLinuxNodeInfo method.
924

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

930
    """
931
    return self.GetLinuxNodeInfo()
932

    
933
  @classmethod
934
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
935
    """Return a command for connecting to the console of an instance.
936

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

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

    
955
    return shell_command
956

    
957
  def Verify(self):
958
    """Verify the hypervisor.
959

960
    Check that the binary exists.
961

962
    """
963
    if not os.path.exists(constants.KVM_PATH):
964
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
965
    if not os.path.exists(constants.SOCAT_PATH):
966
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
967

    
968

    
969
  @classmethod
970
  def CheckParameterSyntax(cls, hvparams):
971
    """Check the given parameters for validity.
972

973
    @type hvparams:  dict
974
    @param hvparams: dictionary with parameter names/value
975
    @raise errors.HypervisorError: when a parameter is not valid
976

977
    """
978
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
979

    
980
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
981
    if kernel_path:
982
      if not hvparams[constants.HV_ROOT_PATH]:
983
        raise errors.HypervisorError("Need a root partition for the instance,"
984
                                     " if a kernel is defined")
985

    
986
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
987
        not hvparams[constants.HV_VNC_X509]):
988
      raise errors.HypervisorError("%s must be defined, if %s is" %
989
                                   (constants.HV_VNC_X509,
990
                                    constants.HV_VNC_X509_VERIFY))
991

    
992
    boot_order = hvparams[constants.HV_BOOT_ORDER]
993
    if (boot_order == constants.HT_BO_CDROM and
994
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
995
      raise errors.HypervisorError("Cannot boot from cdrom without an"
996
                                   " ISO path")
997

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

    
1009
  @classmethod
1010
  def ValidateParameters(cls, hvparams):
1011
    """Check the given parameters for validity.
1012

1013
    @type hvparams:  dict
1014
    @param hvparams: dictionary with parameter names/value
1015
    @raise errors.HypervisorError: when a parameter is not valid
1016

1017
    """
1018
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1019

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

    
1029
  @classmethod
1030
  def PowercycleNode(cls):
1031
    """KVM powercycle, just a wrapper over Linux powercycle.
1032

1033
    """
1034
    cls.LinuxPowercycle()