Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ e43d4f9f

History | View | Annotate | Download (33.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 os
27
import os.path
28
import re
29
import tempfile
30
import time
31
import logging
32
import pwd
33
from cStringIO import StringIO
34

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

    
44

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

    
49
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
50
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
51
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
52
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
53
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
54
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR]
55

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

    
94
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
95
                                    re.M | re.I)
96
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
97
  _MIGRATION_INFO_RETRY_DELAY = 2
98

    
99
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
100

    
101
  ANCILLARY_FILES = [
102
    _KVM_NETWORK_SCRIPT,
103
    ]
104

    
105
  def __init__(self):
106
    hv_base.BaseHypervisor.__init__(self)
107
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
108
    # in a tmpfs filesystem or has been otherwise wiped out.
109
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
110
    utils.EnsureDirs(dirs)
111

    
112
  @classmethod
113
  def _InstancePidFile(cls, instance_name):
114
    """Returns the instance pidfile.
115

116
    """
117
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
118

    
119
  @classmethod
120
  def _InstanceUidFile(cls, instance_name):
121
    """Returns the instance uidfile.
122

123
    """
124
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
125

    
126
  @classmethod
127
  def _InstancePidInfo(cls, pid):
128
    """Check pid file for instance information.
129

130
    Check that a pid file is associated with an instance, and retrieve
131
    information from its command line.
132

133
    @type pid: string or int
134
    @param pid: process id of the instance to check
135
    @rtype: tuple
136
    @return: (instance_name, memory, vcpus)
137
    @raise errors.HypervisorError: when an instance cannot be found
138

139
    """
140
    alive = utils.IsProcessAlive(pid)
141
    if not alive:
142
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
143

    
144
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
145
    try:
146
      cmdline = utils.ReadFile(cmdline_file)
147
    except EnvironmentError, err:
148
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
149
                                   (pid, err))
150

    
151
    instance = None
152
    memory = 0
153
    vcpus = 0
154

    
155
    arg_list = cmdline.split('\x00')
156
    while arg_list:
157
      arg =  arg_list.pop(0)
158
      if arg == "-name":
159
        instance = arg_list.pop(0)
160
      elif arg == "-m":
161
        memory = int(arg_list.pop(0))
162
      elif arg == "-smp":
163
        vcpus = int(arg_list.pop(0))
164

    
165
    if instance is None:
166
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
167
                                   " instance" % pid)
168

    
169
    return (instance, memory, vcpus)
170

    
171
  def _InstancePidAlive(self, instance_name):
172
    """Returns the instance pidfile, pid, and liveness.
173

174
    @type instance_name: string
175
    @param instance_name: instance name
176
    @rtype: tuple
177
    @return: (pid file name, pid, liveness)
178

179
    """
180
    pidfile = self._InstancePidFile(instance_name)
181
    pid = utils.ReadPidFile(pidfile)
182

    
183
    alive = False
184
    try:
185
      cmd_instance = self._InstancePidInfo(pid)[0]
186
      alive = (cmd_instance == instance_name)
187
    except errors.HypervisorError:
188
      pass
189

    
190
    return (pidfile, pid, alive)
191

    
192
  def _CheckDown(self, instance_name):
193
    """Raises an error unless the given instance is down.
194

195
    """
196
    alive = self._InstancePidAlive(instance_name)[2]
197
    if alive:
198
      raise errors.HypervisorError("Failed to start instance %s: %s" %
199
                                   (instance_name, "already running"))
200

    
201
  @classmethod
202
  def _InstanceMonitor(cls, instance_name):
203
    """Returns the instance monitor socket name
204

205
    """
206
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
207

    
208
  @classmethod
209
  def _InstanceSerial(cls, instance_name):
210
    """Returns the instance serial socket name
211

212
    """
213
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
214

    
215
  @staticmethod
216
  def _SocatUnixConsoleParams():
217
    """Returns the correct parameters for socat
218

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

221
    """
222
    if constants.SOCAT_USE_ESCAPE:
223
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
224
    else:
225
      return "echo=0,icanon=0"
226

    
227
  @classmethod
228
  def _InstanceKVMRuntime(cls, instance_name):
229
    """Returns the instance KVM runtime filename
230

231
    """
232
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
233

    
234
  @classmethod
235
  def _TryReadUidFile(cls, uid_file):
236
    """Try to read a uid file
237

238
    """
239
    if os.path.exists(uid_file):
240
      try:
241
        uid = int(utils.ReadOneLineFile(uid_file))
242
        return uid
243
      except EnvironmentError:
244
        logging.warning("Can't read uid file", exc_info=True)
245
      except (TypeError, ValueError):
246
        logging.warning("Can't parse uid file contents", exc_info=True)
247
    return None
248

    
249
  @classmethod
250
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
251
    """Removes an instance's rutime sockets/files.
252

253
    """
254
    utils.RemoveFile(pidfile)
255
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
256
    utils.RemoveFile(cls._InstanceSerial(instance_name))
257
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
258
    uid_file = cls._InstanceUidFile(instance_name)
259
    uid = cls._TryReadUidFile(uid_file)
260
    utils.RemoveFile(uid_file)
261
    if uid is not None:
262
      uidpool.ReleaseUid(uid)
263

    
264
  def _WriteNetScript(self, instance, seq, nic):
265
    """Write a script to connect a net interface to the proper bridge.
266

267
    This can be used by any qemu-type hypervisor.
268

269
    @param instance: instance we're acting on
270
    @type instance: instance object
271
    @param seq: nic sequence number
272
    @type seq: int
273
    @param nic: nic we're acting on
274
    @type nic: nic object
275
    @return: netscript file name
276
    @rtype: string
277

278
    """
279
    script = StringIO()
280
    script.write("#!/bin/sh\n")
281
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
282
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
283
    script.write("export INSTANCE=%s\n" % instance.name)
284
    script.write("export MAC=%s\n" % nic.mac)
285
    if nic.ip:
286
      script.write("export IP=%s\n" % nic.ip)
287
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
288
    if nic.nicparams[constants.NIC_LINK]:
289
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
290
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
291
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
292
    script.write("export INTERFACE=$1\n")
293
    if instance.tags:
294
      script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
295
    # TODO: make this configurable at ./configure time
296
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
297
    script.write("  # Execute the user-specific vif file\n")
298
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
299
    script.write("else\n")
300
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
301
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
302
      script.write("  # Connect the interface to the bridge\n")
303
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
304
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
305
      if not nic.ip:
306
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
307
      script.write("  # Route traffic targeted at the IP to the interface\n")
308
      if nic.nicparams[constants.NIC_LINK]:
309
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
310
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
311
        script.write("  ip route replace $IP table $LINK proto static"
312
                     " dev $INTERFACE\n")
313
      else:
314
        script.write("  ip route replace $IP proto static"
315
                     " dev $INTERFACE\n")
316
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
317
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
318
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
319
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
320
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
321
      script.write("  fi\n")
322
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
323
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
324
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
325
      script.write("  fi\n")
326
    script.write("fi\n\n")
327
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
328
    # mounted noexec sometimes, so we'll have to find another place.
329
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
330
    tmpfile = os.fdopen(tmpfd, 'w')
331
    try:
332
      tmpfile.write(script.getvalue())
333
    finally:
334
      tmpfile.close()
335
    os.chmod(tmpfile_name, 0755)
336
    return tmpfile_name
337

    
338
  def ListInstances(self):
339
    """Get the list of running instances.
340

341
    We can do this by listing our live instances directory and
342
    checking whether the associated kvm process is still alive.
343

344
    """
345
    result = []
346
    for name in os.listdir(self._PIDS_DIR):
347
      if self._InstancePidAlive(name)[2]:
348
        result.append(name)
349
    return result
350

    
351
  def GetInstanceInfo(self, instance_name):
352
    """Get instance properties.
353

354
    @type instance_name: string
355
    @param instance_name: the instance name
356
    @rtype: tuple of strings
357
    @return: (name, id, memory, vcpus, stat, times)
358

359
    """
360
    _, pid, alive = self._InstancePidAlive(instance_name)
361
    if not alive:
362
      return None
363

    
364
    _, memory, vcpus = self._InstancePidInfo(pid)
365
    stat = "---b-"
366
    times = "0"
367

    
368
    return (instance_name, pid, memory, vcpus, stat, times)
369

    
370
  def GetAllInstancesInfo(self):
371
    """Get properties of all instances.
372

373
    @return: list of tuples (name, id, memory, vcpus, stat, times)
374

375
    """
376
    data = []
377
    for name in os.listdir(self._PIDS_DIR):
378
      try:
379
        info = self.GetInstanceInfo(name)
380
      except errors.HypervisorError:
381
        continue
382
      if info:
383
        data.append(info)
384
    return data
385

    
386
  def _GenerateKVMRuntime(self, instance, block_devices):
387
    """Generate KVM information to start an instance.
388

389
    """
390
    pidfile  = self._InstancePidFile(instance.name)
391
    kvm = constants.KVM_PATH
392
    kvm_cmd = [kvm]
393
    # used just by the vnc server, if enabled
394
    kvm_cmd.extend(['-name', instance.name])
395
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
396
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
397
    kvm_cmd.extend(['-pidfile', pidfile])
398
    kvm_cmd.extend(['-daemonize'])
399
    if not instance.hvparams[constants.HV_ACPI]:
400
      kvm_cmd.extend(['-no-acpi'])
401

    
402
    hvp = instance.hvparams
403
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
404
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
405
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
406

    
407
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
408
      kvm_cmd.extend(["-enable-kvm"])
409
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
410
      kvm_cmd.extend(["-disable-kvm"])
411

    
412
    if boot_network:
413
      kvm_cmd.extend(['-boot', 'n'])
414

    
415
    disk_type = hvp[constants.HV_DISK_TYPE]
416
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
417
      if_val = ',if=virtio'
418
    else:
419
      if_val = ',if=%s' % disk_type
420
    # Cache mode
421
    disk_cache = hvp[constants.HV_DISK_CACHE]
422
    if disk_cache != constants.HT_CACHE_DEFAULT:
423
      cache_val = ",cache=%s" % disk_cache
424
    else:
425
      cache_val = ""
426
    for cfdev, dev_path in block_devices:
427
      if cfdev.mode != constants.DISK_RDWR:
428
        raise errors.HypervisorError("Instance has read-only disks which"
429
                                     " are not supported by KVM")
430
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
431
      if boot_disk:
432
        kvm_cmd.extend(['-boot', 'c'])
433
        if disk_type != constants.HT_DISK_IDE:
434
          boot_val = ',boot=on'
435
        else:
436
          boot_val = ''
437
        # We only boot from the first disk
438
        boot_disk = False
439
      else:
440
        boot_val = ''
441

    
442
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
443
                                                cache_val)
444
      kvm_cmd.extend(['-drive', drive_val])
445

    
446
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
447
    if iso_image:
448
      options = ',format=raw,media=cdrom'
449
      if boot_cdrom:
450
        kvm_cmd.extend(['-boot', 'd'])
451
        if disk_type != constants.HT_DISK_IDE:
452
          options = '%s,boot=on' % options
453
      else:
454
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
455
          if_val = ',if=virtio'
456
        else:
457
          if_val = ',if=%s' % disk_type
458
        options = '%s%s' % (options, if_val)
459
      drive_val = 'file=%s%s' % (iso_image, options)
460
      kvm_cmd.extend(['-drive', drive_val])
461

    
462
    kernel_path = hvp[constants.HV_KERNEL_PATH]
463
    if kernel_path:
464
      kvm_cmd.extend(['-kernel', kernel_path])
465
      initrd_path = hvp[constants.HV_INITRD_PATH]
466
      if initrd_path:
467
        kvm_cmd.extend(['-initrd', initrd_path])
468
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
469
                     hvp[constants.HV_KERNEL_ARGS]]
470
      if hvp[constants.HV_SERIAL_CONSOLE]:
471
        root_append.append('console=ttyS0,38400')
472
      kvm_cmd.extend(['-append', ' '.join(root_append)])
473

    
474
    mouse_type = hvp[constants.HV_USB_MOUSE]
475
    if mouse_type:
476
      kvm_cmd.extend(['-usb'])
477
      kvm_cmd.extend(['-usbdevice', mouse_type])
478

    
479
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
480
    if vnc_bind_address:
481
      if utils.IsValidIP(vnc_bind_address):
482
        if instance.network_port > constants.VNC_BASE_PORT:
483
          display = instance.network_port - constants.VNC_BASE_PORT
484
          if vnc_bind_address == '0.0.0.0':
485
            vnc_arg = ':%d' % (display)
486
          else:
487
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
488
        else:
489
          logging.error("Network port is not a valid VNC display (%d < %d)."
490
                        " Not starting VNC", instance.network_port,
491
                        constants.VNC_BASE_PORT)
492
          vnc_arg = 'none'
493

    
494
        # Only allow tls and other option when not binding to a file, for now.
495
        # kvm/qemu gets confused otherwise about the filename to use.
496
        vnc_append = ''
497
        if hvp[constants.HV_VNC_TLS]:
498
          vnc_append = '%s,tls' % vnc_append
499
          if hvp[constants.HV_VNC_X509_VERIFY]:
500
            vnc_append = '%s,x509verify=%s' % (vnc_append,
501
                                               hvp[constants.HV_VNC_X509])
502
          elif hvp[constants.HV_VNC_X509]:
503
            vnc_append = '%s,x509=%s' % (vnc_append,
504
                                         hvp[constants.HV_VNC_X509])
505
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
506
          vnc_append = '%s,password' % vnc_append
507

    
508
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
509

    
510
      else:
511
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
512

    
513
      kvm_cmd.extend(['-vnc', vnc_arg])
514

    
515
      # Also add a tablet USB device to act as a mouse
516
      # This solves various mouse alignment issues
517
      kvm_cmd.extend(['-usbdevice', 'tablet'])
518
    else:
519
      kvm_cmd.extend(['-nographic'])
520

    
521
    monitor_dev = ("unix:%s,server,nowait" %
522
                   self._InstanceMonitor(instance.name))
523
    kvm_cmd.extend(['-monitor', monitor_dev])
524
    if hvp[constants.HV_SERIAL_CONSOLE]:
525
      serial_dev = ('unix:%s,server,nowait' %
526
                    self._InstanceSerial(instance.name))
527
      kvm_cmd.extend(['-serial', serial_dev])
528
    else:
529
      kvm_cmd.extend(['-serial', 'none'])
530

    
531
    if hvp[constants.HV_USE_LOCALTIME]:
532
      kvm_cmd.extend(['-localtime'])
533

    
534
    # Save the current instance nics, but defer their expansion as parameters,
535
    # as we'll need to generate executable temp files for them.
536
    kvm_nics = instance.nics
537
    hvparams = hvp
538

    
539
    return (kvm_cmd, kvm_nics, hvparams)
540

    
541
  def _WriteKVMRuntime(self, instance_name, data):
542
    """Write an instance's KVM runtime
543

544
    """
545
    try:
546
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
547
                      data=data)
548
    except EnvironmentError, err:
549
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
550

    
551
  def _ReadKVMRuntime(self, instance_name):
552
    """Read an instance's KVM runtime
553

554
    """
555
    try:
556
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
557
    except EnvironmentError, err:
558
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
559
    return file_content
560

    
561
  def _SaveKVMRuntime(self, instance, kvm_runtime):
562
    """Save an instance's KVM runtime
563

564
    """
565
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
566
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
567
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
568
    self._WriteKVMRuntime(instance.name, serialized_form)
569

    
570
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
571
    """Load an instance's KVM runtime
572

573
    """
574
    if not serialized_runtime:
575
      serialized_runtime = self._ReadKVMRuntime(instance.name)
576
    loaded_runtime = serializer.Load(serialized_runtime)
577
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
578
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
579
    return (kvm_cmd, kvm_nics, hvparams)
580

    
581
  def _RunKVMCmd(self, name, kvm_cmd):
582
    """Run the KVM cmd and check for errors
583

584
    @type name: string
585
    @param name: instance name
586
    @type kvm_cmd: list of strings
587
    @param kvm_cmd: runcmd input for kvm
588

589
    """
590
    result = utils.RunCmd(kvm_cmd)
591
    if result.failed:
592
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
593
                                   (name, result.fail_reason, result.output))
594
    if not self._InstancePidAlive(name)[2]:
595
      raise errors.HypervisorError("Failed to start instance %s" % name)
596

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

600
    @type incoming: tuple of strings
601
    @param incoming: (target_host_ip, port)
602

603
    """
604
    hvp = instance.hvparams
605
    name = instance.name
606
    self._CheckDown(name)
607

    
608
    temp_files = []
609

    
610
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
611

    
612
    security_model = hvp[constants.HV_SECURITY_MODEL]
613
    if security_model == constants.HT_SM_USER:
614
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
615

    
616
    if not kvm_nics:
617
      kvm_cmd.extend(["-net", "none"])
618
    else:
619
      tap_extra = ""
620
      nic_type = hvparams[constants.HV_NIC_TYPE]
621
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
622
        nic_model = "model=virtio"
623
        if hvparams[constants.HV_VHOST_NET]:
624
          tap_extra = ",vhost=on"
625
      else:
626
        nic_model = "model=%s" % nic_type
627

    
628
      for nic_seq, nic in enumerate(kvm_nics):
629
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
630
        script = self._WriteNetScript(instance, nic_seq, nic)
631
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
632
        kvm_cmd.extend(["-net", nic_val])
633
        kvm_cmd.extend(["-net", tap_val])
634
        temp_files.append(script)
635

    
636
    if incoming:
637
      target, port = incoming
638
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
639

    
640
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
641
    vnc_pwd = None
642
    if vnc_pwd_file:
643
      try:
644
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
645
      except EnvironmentError, err:
646
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
647
                                     % (vnc_pwd_file, err))
648

    
649
    if security_model == constants.HT_SM_POOL:
650
      ss = ssconf.SimpleStore()
651
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
652
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
653
      uid = uidpool.RequestUnusedUid(all_uids)
654
      try:
655
        username = pwd.getpwuid(uid.GetUid()).pw_name
656
        kvm_cmd.extend(["-runas", username])
657
        self._RunKVMCmd(name, kvm_cmd)
658
      except:
659
        uidpool.ReleaseUid(uid)
660
        raise
661
      else:
662
        uid.Unlock()
663
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
664
    else:
665
      self._RunKVMCmd(name, kvm_cmd)
666

    
667
    if vnc_pwd:
668
      change_cmd = 'change vnc password %s' % vnc_pwd
669
      self._CallMonitorCommand(instance.name, change_cmd)
670

    
671
    for filename in temp_files:
672
      utils.RemoveFile(filename)
673

    
674
  def StartInstance(self, instance, block_devices):
675
    """Start an instance.
676

677
    """
678
    self._CheckDown(instance.name)
679
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
680
    self._SaveKVMRuntime(instance, kvm_runtime)
681
    self._ExecuteKVMRuntime(instance, kvm_runtime)
682

    
683
  def _CallMonitorCommand(self, instance_name, command):
684
    """Invoke a command on the instance monitor.
685

686
    """
687
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
688
             (utils.ShellQuote(command),
689
              constants.SOCAT_PATH,
690
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
691
    result = utils.RunCmd(socat)
692
    if result.failed:
693
      msg = ("Failed to send command '%s' to instance %s."
694
             " output: %s, error: %s, fail_reason: %s" %
695
             (command, instance_name,
696
              result.stdout, result.stderr, result.fail_reason))
697
      raise errors.HypervisorError(msg)
698

    
699
    return result
700

    
701
  def StopInstance(self, instance, force=False, retry=False, name=None):
702
    """Stop an instance.
703

704
    """
705
    if name is not None and not force:
706
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
707
    if name is None:
708
      name = instance.name
709
      acpi = instance.hvparams[constants.HV_ACPI]
710
    else:
711
      acpi = False
712
    _, pid, alive = self._InstancePidAlive(name)
713
    if pid > 0 and alive:
714
      if force or not acpi:
715
        utils.KillProcess(pid)
716
      else:
717
        self._CallMonitorCommand(name, 'system_powerdown')
718

    
719
  def CleanupInstance(self, instance_name):
720
    """Cleanup after a stopped instance
721

722
    """
723
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
724
    if pid > 0 and alive:
725
      raise errors.HypervisorError("Cannot cleanup a live instance")
726
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
727

    
728
  def RebootInstance(self, instance):
729
    """Reboot an instance.
730

731
    """
732
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
733
    # socket the instance will stop, but now power up again. So we'll resort
734
    # to shutdown and restart.
735
    _, _, alive = self._InstancePidAlive(instance.name)
736
    if not alive:
737
      raise errors.HypervisorError("Failed to reboot instance %s:"
738
                                   " not running" % instance.name)
739
    # StopInstance will delete the saved KVM runtime so:
740
    # ...first load it...
741
    kvm_runtime = self._LoadKVMRuntime(instance)
742
    # ...now we can safely call StopInstance...
743
    if not self.StopInstance(instance):
744
      self.StopInstance(instance, force=True)
745
    # ...and finally we can save it again, and execute it...
746
    self._SaveKVMRuntime(instance, kvm_runtime)
747
    self._ExecuteKVMRuntime(instance, kvm_runtime)
748

    
749
  def MigrationInfo(self, instance):
750
    """Get instance information to perform a migration.
751

752
    @type instance: L{objects.Instance}
753
    @param instance: instance to be migrated
754
    @rtype: string
755
    @return: content of the KVM runtime file
756

757
    """
758
    return self._ReadKVMRuntime(instance.name)
759

    
760
  def AcceptInstance(self, instance, info, target):
761
    """Prepare to accept an instance.
762

763
    @type instance: L{objects.Instance}
764
    @param instance: instance to be accepted
765
    @type info: string
766
    @param info: content of the KVM runtime file on the source node
767
    @type target: string
768
    @param target: target host (usually ip), on this node
769

770
    """
771
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
772
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
773
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
774

    
775
  def FinalizeMigration(self, instance, info, success):
776
    """Finalize an instance migration.
777

778
    Stop the incoming mode KVM.
779

780
    @type instance: L{objects.Instance}
781
    @param instance: instance whose migration is being aborted
782

783
    """
784
    if success:
785
      self._WriteKVMRuntime(instance.name, info)
786
    else:
787
      self.StopInstance(instance, force=True)
788

    
789
  def MigrateInstance(self, instance, target, live):
790
    """Migrate an instance to a target node.
791

792
    The migration will not be attempted if the instance is not
793
    currently running.
794

795
    @type instance: L{objects.Instance}
796
    @param instance: the instance to be migrated
797
    @type target: string
798
    @param target: ip address of the target node
799
    @type live: boolean
800
    @param live: perform a live migration
801

802
    """
803
    instance_name = instance.name
804
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
805
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
806
    if not alive:
807
      raise errors.HypervisorError("Instance not running, cannot migrate")
808

    
809
    if not utils.TcpPing(target, port, live_port_needed=True):
810
      raise errors.HypervisorError("Remote host %s not listening on port"
811
                                   " %s, cannot migrate" % (target, port))
812

    
813
    if not live:
814
      self._CallMonitorCommand(instance_name, 'stop')
815

    
816
    migrate_command = ('migrate_set_speed %dm' %
817
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
818
    self._CallMonitorCommand(instance_name, migrate_command)
819

    
820
    migrate_command = ('migrate_set_downtime %dms' %
821
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
822
    self._CallMonitorCommand(instance_name, migrate_command)
823

    
824
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
825
    self._CallMonitorCommand(instance_name, migrate_command)
826

    
827
    info_command = 'info migrate'
828
    done = False
829
    broken_answers = 0
830
    while not done:
831
      result = self._CallMonitorCommand(instance_name, info_command)
832
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
833
      if not match:
834
        broken_answers += 1
835
        if not result.stdout:
836
          logging.info("KVM: empty 'info migrate' result")
837
        else:
838
          logging.warning("KVM: unknown 'info migrate' result: %s",
839
                          result.stdout)
840
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
841
      else:
842
        status = match.group(1)
843
        if status == 'completed':
844
          done = True
845
        elif status == 'active':
846
          # reset the broken answers count
847
          broken_answers = 0
848
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
849
        elif status == 'failed' or status == 'cancelled':
850
          if not live:
851
            self._CallMonitorCommand(instance_name, 'cont')
852
          raise errors.HypervisorError("Migration %s at the kvm level" %
853
                                       status)
854
        else:
855
          logging.warning("KVM: unknown migration status '%s'", status)
856
          broken_answers += 1
857
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
858
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
859
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
860

    
861
    utils.KillProcess(pid)
862
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
863

    
864
  def GetNodeInfo(self):
865
    """Return information about the node.
866

867
    This is just a wrapper over the base GetLinuxNodeInfo method.
868

869
    @return: a dict with the following keys (values in MiB):
870
          - memory_total: the total memory size on the node
871
          - memory_free: the available memory on the node for instances
872
          - memory_dom0: the memory used by the node itself, if available
873

874
    """
875
    return self.GetLinuxNodeInfo()
876

    
877
  @classmethod
878
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
879
    """Return a command for connecting to the console of an instance.
880

881
    """
882
    if hvparams[constants.HV_SERIAL_CONSOLE]:
883
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
884
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
885
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
886
    else:
887
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
888

    
889
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
890
    if vnc_bind_address:
891
      if instance.network_port > constants.VNC_BASE_PORT:
892
        display = instance.network_port - constants.VNC_BASE_PORT
893
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
894
                       " (display: %d)'" % (vnc_bind_address,
895
                                            instance.network_port,
896
                                            display))
897
        shell_command = "%s; %s" % (vnc_command, shell_command)
898

    
899
    return shell_command
900

    
901
  def Verify(self):
902
    """Verify the hypervisor.
903

904
    Check that the binary exists.
905

906
    """
907
    if not os.path.exists(constants.KVM_PATH):
908
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
909
    if not os.path.exists(constants.SOCAT_PATH):
910
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
911

    
912

    
913
  @classmethod
914
  def CheckParameterSyntax(cls, hvparams):
915
    """Check the given parameters for validity.
916

917
    @type hvparams:  dict
918
    @param hvparams: dictionary with parameter names/value
919
    @raise errors.HypervisorError: when a parameter is not valid
920

921
    """
922
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
923

    
924
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
925
    if kernel_path:
926
      if not hvparams[constants.HV_ROOT_PATH]:
927
        raise errors.HypervisorError("Need a root partition for the instance,"
928
                                     " if a kernel is defined")
929

    
930
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
931
        not hvparams[constants.HV_VNC_X509]):
932
      raise errors.HypervisorError("%s must be defined, if %s is" %
933
                                   (constants.HV_VNC_X509,
934
                                    constants.HV_VNC_X509_VERIFY))
935

    
936
    boot_order = hvparams[constants.HV_BOOT_ORDER]
937
    if (boot_order == constants.HT_BO_CDROM and
938
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
939
      raise errors.HypervisorError("Cannot boot from cdrom without an"
940
                                   " ISO path")
941

    
942
    security_model = hvparams[constants.HV_SECURITY_MODEL]
943
    if security_model == constants.HT_SM_USER:
944
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
945
        raise errors.HypervisorError("A security domain (user to run kvm as)"
946
                                     " must be specified")
947
    elif (security_model == constants.HT_SM_NONE or
948
          security_model == constants.HT_SM_POOL):
949
      if hvparams[constants.HV_SECURITY_DOMAIN]:
950
        raise errors.HypervisorError("Cannot have a security domain when the"
951
                                     " security model is 'none' or 'pool'")
952

    
953
  @classmethod
954
  def ValidateParameters(cls, hvparams):
955
    """Check the given parameters for validity.
956

957
    @type hvparams:  dict
958
    @param hvparams: dictionary with parameter names/value
959
    @raise errors.HypervisorError: when a parameter is not valid
960

961
    """
962
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
963

    
964
    security_model = hvparams[constants.HV_SECURITY_MODEL]
965
    if security_model == constants.HT_SM_USER:
966
      username = hvparams[constants.HV_SECURITY_DOMAIN]
967
      try:
968
        pwd.getpwnam(username)
969
      except KeyError:
970
        raise errors.HypervisorError("Unknown security domain user %s"
971
                                     % username)
972

    
973
  @classmethod
974
  def PowercycleNode(cls):
975
    """KVM powercycle, just a wrapper over Linux powercycle.
976

977
    """
978
    cls.LinuxPowercycle()