Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 78411c60

History | View | Annotate | Download (26.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
from cStringIO import StringIO
33

    
34
from ganeti import utils
35
from ganeti import constants
36
from ganeti import errors
37
from ganeti import serializer
38
from ganeti import objects
39
from ganeti.hypervisor import hv_base
40

    
41

    
42
class KVMHypervisor(hv_base.BaseHypervisor):
43
  """KVM hypervisor interface"""
44

    
45
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
46
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
47
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
48
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
49
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
50

    
51
  PARAMETERS = {
52
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
53
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
54
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
55
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
56
    constants.HV_ACPI: hv_base.NO_CHECK,
57
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
58
    constants.HV_VNC_BIND_ADDRESS:
59
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
60
       "the VNC bind address must be either a valid IP address or an absolute"
61
       " pathname", None, None),
62
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
63
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
64
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
65
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
66
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
67
    constants.HV_BOOT_ORDER:
68
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
69
    constants.HV_NIC_TYPE:
70
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
71
    constants.HV_DISK_TYPE:
72
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
73
    constants.HV_USB_MOUSE:
74
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
75
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
76
    }
77

    
78
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
79
                                    re.M | re.I)
80

    
81
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
82

    
83
  ANCILLARY_FILES = [
84
    _KVM_NETWORK_SCRIPT,
85
    ]
86

    
87
  def __init__(self):
88
    hv_base.BaseHypervisor.__init__(self)
89
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
90
    # in a tmpfs filesystem or has been otherwise wiped out.
91
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
92
    utils.EnsureDirs(dirs)
93

    
94
  def _InstancePidAlive(self, instance_name):
95
    """Returns the instance pid and pidfile
96

97
    """
98
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
99
    pid = utils.ReadPidFile(pidfile)
100
    alive = utils.IsProcessAlive(pid)
101

    
102
    return (pidfile, pid, alive)
103

    
104
  @classmethod
105
  def _InstanceMonitor(cls, instance_name):
106
    """Returns the instance monitor socket name
107

108
    """
109
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
110

    
111
  @classmethod
112
  def _InstanceSerial(cls, instance_name):
113
    """Returns the instance serial socket name
114

115
    """
116
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
117

    
118
  @classmethod
119
  def _InstanceKVMRuntime(cls, instance_name):
120
    """Returns the instance KVM runtime filename
121

122
    """
123
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
124

    
125
  @classmethod
126
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
127
    """Removes an instance's rutime sockets/files.
128

129
    """
130
    utils.RemoveFile(pidfile)
131
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
132
    utils.RemoveFile(cls._InstanceSerial(instance_name))
133
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
134

    
135
  def _WriteNetScript(self, instance, seq, nic):
136
    """Write a script to connect a net interface to the proper bridge.
137

138
    This can be used by any qemu-type hypervisor.
139

140
    @param instance: instance we're acting on
141
    @type instance: instance object
142
    @param seq: nic sequence number
143
    @type seq: int
144
    @param nic: nic we're acting on
145
    @type nic: nic object
146
    @return: netscript file name
147
    @rtype: string
148

149
    """
150
    script = StringIO()
151
    script.write("#!/bin/sh\n")
152
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
153
    script.write("export INSTANCE=%s\n" % instance.name)
154
    script.write("export MAC=%s\n" % nic.mac)
155
    if nic.ip:
156
      script.write("export IP=%s\n" % nic.ip)
157
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
158
    if nic.nicparams[constants.NIC_LINK]:
159
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
160
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
161
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
162
    script.write("export INTERFACE=$1\n")
163
    # TODO: make this configurable at ./configure time
164
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
165
    script.write("  # Execute the user-specific vif file\n")
166
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
167
    script.write("else\n")
168
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
169
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
170
      script.write("  # Connect the interface to the bridge\n")
171
      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
172
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
173
      script.write("  # Route traffic targeted at the IP to the interface\n")
174
      if nic.nicparams[constants.NIC_LINK]:
175
        script.write("  while /sbin/ip rule del dev $INTERFACE; do :; done\n")
176
        script.write("  /sbin/ip rule add dev $INTERFACE table $LINK\n")
177
        script.write("  /sbin/ip route replace $IP table $LINK proto static"
178
                     " dev $INTERFACE\n")
179
      else:
180
        script.write("  /sbin/ip route replace $IP proto static"
181
                     " dev $INTERFACE\n")
182
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
183
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
184
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
185
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
186
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
187
      script.write("  fi\n")
188
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
189
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
190
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
191
      script.write("  fi\n")
192
    script.write("fi\n\n")
193
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
194
    # mounted noexec sometimes, so we'll have to find another place.
195
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
196
    tmpfile = os.fdopen(tmpfd, 'w')
197
    try:
198
      tmpfile.write(script.getvalue())
199
    finally:
200
      tmpfile.close()
201
    os.chmod(tmpfile_name, 0755)
202
    return tmpfile_name
203

    
204
  def ListInstances(self):
205
    """Get the list of running instances.
206

207
    We can do this by listing our live instances directory and
208
    checking whether the associated kvm process is still alive.
209

210
    """
211
    result = []
212
    for name in os.listdir(self._PIDS_DIR):
213
      filename = "%s/%s" % (self._PIDS_DIR, name)
214
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
215
        result.append(name)
216
    return result
217

    
218
  def GetInstanceInfo(self, instance_name):
219
    """Get instance properties.
220

221
    @param instance_name: the instance name
222

223
    @return: tuple (name, id, memory, vcpus, stat, times)
224

225
    """
226
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
227
    if not alive:
228
      return None
229

    
230
    cmdline_file = "/proc/%s/cmdline" % pid
231
    try:
232
      cmdline = utils.ReadFile(cmdline_file)
233
    except EnvironmentError, err:
234
      raise errors.HypervisorError("Failed to list instance %s: %s" %
235
                                   (instance_name, err))
236

    
237
    memory = 0
238
    vcpus = 0
239
    stat = "---b-"
240
    times = "0"
241

    
242
    arg_list = cmdline.split('\x00')
243
    while arg_list:
244
      arg =  arg_list.pop(0)
245
      if arg == '-m':
246
        memory = int(arg_list.pop(0))
247
      elif arg == '-smp':
248
        vcpus = int(arg_list.pop(0))
249

    
250
    return (instance_name, pid, memory, vcpus, stat, times)
251

    
252
  def GetAllInstancesInfo(self):
253
    """Get properties of all instances.
254

255
    @return: list of tuples (name, id, memory, vcpus, stat, times)
256

257
    """
258
    data = []
259
    for name in os.listdir(self._PIDS_DIR):
260
      filename = "%s/%s" % (self._PIDS_DIR, name)
261
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
262
        try:
263
          info = self.GetInstanceInfo(name)
264
        except errors.HypervisorError, err:
265
          continue
266
        if info:
267
          data.append(info)
268

    
269
    return data
270

    
271
  def _GenerateKVMRuntime(self, instance, block_devices):
272
    """Generate KVM information to start an instance.
273

274
    """
275
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
276
    kvm = constants.KVM_PATH
277
    kvm_cmd = [kvm]
278
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
279
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
280
    kvm_cmd.extend(['-pidfile', pidfile])
281
    # used just by the vnc server, if enabled
282
    kvm_cmd.extend(['-name', instance.name])
283
    kvm_cmd.extend(['-daemonize'])
284
    if not instance.hvparams[constants.HV_ACPI]:
285
      kvm_cmd.extend(['-no-acpi'])
286

    
287
    hvp = instance.hvparams
288
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
289
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
290
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
291

    
292
    if boot_network:
293
      kvm_cmd.extend(['-boot', 'n'])
294

    
295
    disk_type = hvp[constants.HV_DISK_TYPE]
296
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
297
      if_val = ',if=virtio'
298
    else:
299
      if_val = ',if=%s' % disk_type
300
    for cfdev, dev_path in block_devices:
301
      if cfdev.mode != constants.DISK_RDWR:
302
        raise errors.HypervisorError("Instance has read-only disks which"
303
                                     " are not supported by KVM")
304
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
305
      if boot_disk:
306
        kvm_cmd.extend(['-boot', 'c'])
307
        boot_val = ',boot=on'
308
        # We only boot from the first disk
309
        boot_disk = False
310
      else:
311
        boot_val = ''
312

    
313
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
314
      kvm_cmd.extend(['-drive', drive_val])
315

    
316
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
317
    if iso_image:
318
      options = ',format=raw,media=cdrom'
319
      if boot_cdrom:
320
        kvm_cmd.extend(['-boot', 'd'])
321
        options = '%s,boot=on' % options
322
      else:
323
        options = '%s,if=virtio' % options
324
      drive_val = 'file=%s%s' % (iso_image, options)
325
      kvm_cmd.extend(['-drive', drive_val])
326

    
327
    kernel_path = hvp[constants.HV_KERNEL_PATH]
328
    if kernel_path:
329
      kvm_cmd.extend(['-kernel', kernel_path])
330
      initrd_path = hvp[constants.HV_INITRD_PATH]
331
      if initrd_path:
332
        kvm_cmd.extend(['-initrd', initrd_path])
333
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
334
                     hvp[constants.HV_KERNEL_ARGS]]
335
      if hvp[constants.HV_SERIAL_CONSOLE]:
336
        root_append.append('console=ttyS0,38400')
337
      kvm_cmd.extend(['-append', ' '.join(root_append)])
338

    
339
    mouse_type = hvp[constants.HV_USB_MOUSE]
340
    if mouse_type:
341
      kvm_cmd.extend(['-usb'])
342
      kvm_cmd.extend(['-usbdevice', mouse_type])
343

    
344
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
345
    if vnc_bind_address:
346
      if utils.IsValidIP(vnc_bind_address):
347
        if instance.network_port > constants.VNC_BASE_PORT:
348
          display = instance.network_port - constants.VNC_BASE_PORT
349
          if vnc_bind_address == '0.0.0.0':
350
            vnc_arg = ':%d' % (display)
351
          else:
352
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
353
        else:
354
          logging.error("Network port is not a valid VNC display (%d < %d)."
355
                        " Not starting VNC" %
356
                        (instance.network_port,
357
                         constants.VNC_BASE_PORT))
358
          vnc_arg = 'none'
359

    
360
        # Only allow tls and other option when not binding to a file, for now.
361
        # kvm/qemu gets confused otherwise about the filename to use.
362
        vnc_append = ''
363
        if hvp[constants.HV_VNC_TLS]:
364
          vnc_append = '%s,tls' % vnc_append
365
          if hvp[constants.HV_VNC_X509_VERIFY]:
366
            vnc_append = '%s,x509verify=%s' % (vnc_append,
367
                                               hvp[constants.HV_VNC_X509])
368
          elif hvp[constants.HV_VNC_X509]:
369
            vnc_append = '%s,x509=%s' % (vnc_append,
370
                                         hvp[constants.HV_VNC_X509])
371
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
372
          vnc_append = '%s,password' % vnc_append
373

    
374
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
375

    
376
      else:
377
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
378

    
379
      kvm_cmd.extend(['-vnc', vnc_arg])
380
    else:
381
      kvm_cmd.extend(['-nographic'])
382

    
383
    monitor_dev = ("unix:%s,server,nowait" %
384
                   self._InstanceMonitor(instance.name))
385
    kvm_cmd.extend(['-monitor', monitor_dev])
386
    if hvp[constants.HV_SERIAL_CONSOLE]:
387
      serial_dev = ('unix:%s,server,nowait' %
388
                    self._InstanceSerial(instance.name))
389
      kvm_cmd.extend(['-serial', serial_dev])
390
    else:
391
      kvm_cmd.extend(['-serial', 'none'])
392

    
393
    # Save the current instance nics, but defer their expansion as parameters,
394
    # as we'll need to generate executable temp files for them.
395
    kvm_nics = instance.nics
396
    hvparams = hvp
397

    
398
    return (kvm_cmd, kvm_nics, hvparams)
399

    
400
  def _WriteKVMRuntime(self, instance_name, data):
401
    """Write an instance's KVM runtime
402

403
    """
404
    try:
405
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
406
                      data=data)
407
    except EnvironmentError, err:
408
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
409

    
410
  def _ReadKVMRuntime(self, instance_name):
411
    """Read an instance's KVM runtime
412

413
    """
414
    try:
415
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
416
    except EnvironmentError, err:
417
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
418
    return file_content
419

    
420
  def _SaveKVMRuntime(self, instance, kvm_runtime):
421
    """Save an instance's KVM runtime
422

423
    """
424
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
425
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
426
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
427
    self._WriteKVMRuntime(instance.name, serialized_form)
428

    
429
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
430
    """Load an instance's KVM runtime
431

432
    """
433
    if not serialized_runtime:
434
      serialized_runtime = self._ReadKVMRuntime(instance.name)
435
    loaded_runtime = serializer.Load(serialized_runtime)
436
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
437
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
438
    return (kvm_cmd, kvm_nics, hvparams)
439

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

443
    @type incoming: tuple of strings
444
    @param incoming: (target_host_ip, port)
445

446
    """
447
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
448
    hvp = instance.hvparams
449
    if alive:
450
      raise errors.HypervisorError("Failed to start instance %s: %s" %
451
                                   (instance.name, "already running"))
452

    
453
    temp_files = []
454

    
455
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
456

    
457
    if not kvm_nics:
458
      kvm_cmd.extend(['-net', 'none'])
459
    else:
460
      nic_type = hvparams[constants.HV_NIC_TYPE]
461
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
462
        nic_model = "model=virtio"
463
      else:
464
        nic_model = "model=%s" % nic_type
465

    
466
      for nic_seq, nic in enumerate(kvm_nics):
467
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
468
        script = self._WriteNetScript(instance, nic_seq, nic)
469
        kvm_cmd.extend(['-net', nic_val])
470
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
471
        temp_files.append(script)
472

    
473
    if incoming:
474
      target, port = incoming
475
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
476

    
477
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
478
    vnc_pwd = None
479
    if vnc_pwd_file:
480
      try:
481
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
482
      except EnvironmentError, err:
483
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
484
                                     % (vnc_pwd_file, err))
485

    
486
    result = utils.RunCmd(kvm_cmd)
487
    if result.failed:
488
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
489
                                   (instance.name, result.fail_reason,
490
                                    result.output))
491

    
492
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
493
      raise errors.HypervisorError("Failed to start instance %s: %s" %
494
                                   (instance.name))
495

    
496
    if vnc_pwd:
497
      change_cmd = 'change vnc password %s' % vnc_pwd
498
      self._CallMonitorCommand(instance.name, change_cmd)
499

    
500
    for filename in temp_files:
501
      utils.RemoveFile(filename)
502

    
503
  def StartInstance(self, instance, block_devices):
504
    """Start an instance.
505

506
    """
507
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
508
    if alive:
509
      raise errors.HypervisorError("Failed to start instance %s: %s" %
510
                                   (instance.name, "already running"))
511

    
512
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
513
    self._SaveKVMRuntime(instance, kvm_runtime)
514
    self._ExecuteKVMRuntime(instance, kvm_runtime)
515

    
516
  def _CallMonitorCommand(self, instance_name, command):
517
    """Invoke a command on the instance monitor.
518

519
    """
520
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
521
             (utils.ShellQuote(command),
522
              constants.SOCAT_PATH,
523
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
524
    result = utils.RunCmd(socat)
525
    if result.failed:
526
      msg = ("Failed to send command '%s' to instance %s."
527
             " output: %s, error: %s, fail_reason: %s" %
528
             (command, instance_name,
529
              result.stdout, result.stderr, result.fail_reason))
530
      raise errors.HypervisorError(msg)
531

    
532
    return result
533

    
534
  def StopInstance(self, instance, force=False, retry=False):
535
    """Stop an instance.
536

537
    """
538
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
539
    if pid > 0 and alive:
540
      if force or not instance.hvparams[constants.HV_ACPI]:
541
        utils.KillProcess(pid)
542
      else:
543
        self._CallMonitorCommand(instance.name, 'system_powerdown')
544

    
545
    if not utils.IsProcessAlive(pid):
546
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
547
      return True
548
    else:
549
      return False
550

    
551
  def RebootInstance(self, instance):
552
    """Reboot an instance.
553

554
    """
555
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
556
    # socket the instance will stop, but now power up again. So we'll resort
557
    # to shutdown and restart.
558
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
559
    if not alive:
560
      raise errors.HypervisorError("Failed to reboot instance %s:"
561
                                   " not running" % instance.name)
562
    # StopInstance will delete the saved KVM runtime so:
563
    # ...first load it...
564
    kvm_runtime = self._LoadKVMRuntime(instance)
565
    # ...now we can safely call StopInstance...
566
    if not self.StopInstance(instance):
567
      self.StopInstance(instance, force=True)
568
    # ...and finally we can save it again, and execute it...
569
    self._SaveKVMRuntime(instance, kvm_runtime)
570
    self._ExecuteKVMRuntime(instance, kvm_runtime)
571

    
572
  def MigrationInfo(self, instance):
573
    """Get instance information to perform a migration.
574

575
    @type instance: L{objects.Instance}
576
    @param instance: instance to be migrated
577
    @rtype: string
578
    @return: content of the KVM runtime file
579

580
    """
581
    return self._ReadKVMRuntime(instance.name)
582

    
583
  def AcceptInstance(self, instance, info, target):
584
    """Prepare to accept an instance.
585

586
    @type instance: L{objects.Instance}
587
    @param instance: instance to be accepted
588
    @type info: string
589
    @param info: content of the KVM runtime file on the source node
590
    @type target: string
591
    @param target: target host (usually ip), on this node
592

593
    """
594
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
595
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
596
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
597

    
598
  def FinalizeMigration(self, instance, info, success):
599
    """Finalize an instance migration.
600

601
    Stop the incoming mode KVM.
602

603
    @type instance: L{objects.Instance}
604
    @param instance: instance whose migration is being aborted
605

606
    """
607
    if success:
608
      self._WriteKVMRuntime(instance.name, info)
609
    else:
610
      self.StopInstance(instance, force=True)
611

    
612
  def MigrateInstance(self, instance, target, live):
613
    """Migrate an instance to a target node.
614

615
    The migration will not be attempted if the instance is not
616
    currently running.
617

618
    @type instance: L{objects.Instance}
619
    @param instance: the instance to be migrated
620
    @type target: string
621
    @param target: ip address of the target node
622
    @type live: boolean
623
    @param live: perform a live migration
624

625
    """
626
    instance_name = instance.name
627
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
628
    if not alive:
629
      raise errors.HypervisorError("Instance not running, cannot migrate")
630

    
631
    if not live:
632
      self._CallMonitorCommand(instance_name, 'stop')
633

    
634
    migrate_command = ('migrate -d tcp:%s:%s' %
635
                       (target, constants.KVM_MIGRATION_PORT))
636
    self._CallMonitorCommand(instance_name, migrate_command)
637

    
638
    info_command = 'info migrate'
639
    done = False
640
    while not done:
641
      result = self._CallMonitorCommand(instance_name, info_command)
642
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
643
      if not match:
644
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
645
                                     result.stdout)
646
      else:
647
        status = match.group(1)
648
        if status == 'completed':
649
          done = True
650
        elif status == 'active':
651
          time.sleep(2)
652
        elif status == 'failed' or status == 'cancelled':
653
          if not live:
654
            self._CallMonitorCommand(instance_name, 'cont')
655
          raise errors.HypervisorError("Migration %s at the kvm level" %
656
                                       status)
657
        else:
658
          logging.info("KVM: unknown migration status '%s'" % status)
659
          time.sleep(2)
660

    
661
    utils.KillProcess(pid)
662
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
663

    
664
  def GetNodeInfo(self):
665
    """Return information about the node.
666

667
    This is just a wrapper over the base GetLinuxNodeInfo method.
668

669
    @return: a dict with the following keys (values in MiB):
670
          - memory_total: the total memory size on the node
671
          - memory_free: the available memory on the node for instances
672
          - memory_dom0: the memory used by the node itself, if available
673

674
    """
675
    return self.GetLinuxNodeInfo()
676

    
677
  @classmethod
678
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
679
    """Return a command for connecting to the console of an instance.
680

681
    """
682
    if hvparams[constants.HV_SERIAL_CONSOLE]:
683
      # FIXME: The socat shell is not perfect. In particular the way we start
684
      # it ctrl+c will close it, rather than being passed to the other end.
685
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
686
      # will be no way of exiting socat (except killing it from another shell)
687
      # and ctrl+c doesn't work anyway, printing ^C rather than being
688
      # interpreted by kvm. For now we'll leave it this way, which at least
689
      # allows a minimal interaction and changes on the machine.
690
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
691
                       (constants.SOCAT_PATH,
692
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
693
    else:
694
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
695

    
696
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
697
    if vnc_bind_address:
698
      if instance.network_port > constants.VNC_BASE_PORT:
699
        display = instance.network_port - constants.VNC_BASE_PORT
700
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
701
                       " (display: %d)'" % (vnc_bind_address,
702
                                            instance.network_port,
703
                                            display))
704
        shell_command = "%s; %s" % (vnc_command, shell_command)
705

    
706
    return shell_command
707

    
708
  def Verify(self):
709
    """Verify the hypervisor.
710

711
    Check that the binary exists.
712

713
    """
714
    if not os.path.exists(constants.KVM_PATH):
715
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
716
    if not os.path.exists(constants.SOCAT_PATH):
717
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
718

    
719

    
720
  @classmethod
721
  def CheckParameterSyntax(cls, hvparams):
722
    """Check the given parameters for validity.
723

724
    @type hvparams:  dict
725
    @param hvparams: dictionary with parameter names/value
726
    @raise errors.HypervisorError: when a parameter is not valid
727

728
    """
729
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
730

    
731
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
732
    if kernel_path:
733
      if not hvparams[constants.HV_ROOT_PATH]:
734
        raise errors.HypervisorError("Need a root partition for the instance,"
735
                                     " if a kernel is defined")
736

    
737
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
738
        not hvparams[constants.HV_VNC_X509]):
739
      raise errors.HypervisorError("%s must be defined, if %s is" %
740
                                   (constants.HV_VNC_X509,
741
                                    constants.HV_VNC_X509_VERIFY))
742

    
743
    boot_order = hvparams[constants.HV_BOOT_ORDER]
744

    
745
    if (boot_order == constants.HT_BO_CDROM and
746
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
747
      raise errors.HypervisorError("Cannot boot from cdrom without an"
748
                                   " ISO path")
749
    if (boot_order == constants.HT_BO_NETWORK and
750
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
751
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
752
                                   " change the NIC type.")
753

    
754
  @classmethod
755
  def PowercycleNode(cls):
756
    """KVM powercycle, just a wrapper over Linux powercycle.
757

758
    """
759
    cls.LinuxPowercycle()