Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 1e37ad45

History | View | Annotate | Download (25.7 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_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
66
    constants.HV_BOOT_ORDER:
67
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
68
    constants.HV_NIC_TYPE:
69
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
70
    constants.HV_DISK_TYPE:
71
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
72
    constants.HV_USB_MOUSE:
73
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
74
    }
75

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

    
79
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
80

    
81
  ANCILLARY_FILES = [
82
    _KVM_NETWORK_SCRIPT,
83
    ]
84

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

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

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

    
100
    return (pidfile, pid, alive)
101

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

106
    """
107
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
108

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

113
    """
114
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
115

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

120
    """
121
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
122

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

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

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

136
    This can be used by any qemu-type hypervisor.
137

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

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

    
187
  def ListInstances(self):
188
    """Get the list of running instances.
189

190
    We can do this by listing our live instances directory and
191
    checking whether the associated kvm process is still alive.
192

193
    """
194
    result = []
195
    for name in os.listdir(self._PIDS_DIR):
196
      filename = "%s/%s" % (self._PIDS_DIR, name)
197
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
198
        result.append(name)
199
    return result
200

    
201
  def GetInstanceInfo(self, instance_name):
202
    """Get instance properties.
203

204
    @param instance_name: the instance name
205

206
    @return: tuple (name, id, memory, vcpus, stat, times)
207

208
    """
209
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
210
    if not alive:
211
      return None
212

    
213
    cmdline_file = "/proc/%s/cmdline" % pid
214
    try:
215
      cmdline = utils.ReadFile(cmdline_file)
216
    except EnvironmentError, err:
217
      raise errors.HypervisorError("Failed to list instance %s: %s" %
218
                                   (instance_name, err))
219

    
220
    memory = 0
221
    vcpus = 0
222
    stat = "---b-"
223
    times = "0"
224

    
225
    arg_list = cmdline.split('\x00')
226
    while arg_list:
227
      arg =  arg_list.pop(0)
228
      if arg == '-m':
229
        memory = int(arg_list.pop(0))
230
      elif arg == '-smp':
231
        vcpus = int(arg_list.pop(0))
232

    
233
    return (instance_name, pid, memory, vcpus, stat, times)
234

    
235
  def GetAllInstancesInfo(self):
236
    """Get properties of all instances.
237

238
    @return: list of tuples (name, id, memory, vcpus, stat, times)
239

240
    """
241
    data = []
242
    for name in os.listdir(self._PIDS_DIR):
243
      filename = "%s/%s" % (self._PIDS_DIR, name)
244
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
245
        try:
246
          info = self.GetInstanceInfo(name)
247
        except errors.HypervisorError, err:
248
          continue
249
        if info:
250
          data.append(info)
251

    
252
    return data
253

    
254
  def _GenerateKVMRuntime(self, instance, block_devices):
255
    """Generate KVM information to start an instance.
256

257
    """
258
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
259
    kvm = constants.KVM_PATH
260
    kvm_cmd = [kvm]
261
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
262
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
263
    kvm_cmd.extend(['-pidfile', pidfile])
264
    # used just by the vnc server, if enabled
265
    kvm_cmd.extend(['-name', instance.name])
266
    kvm_cmd.extend(['-daemonize'])
267
    if not instance.hvparams[constants.HV_ACPI]:
268
      kvm_cmd.extend(['-no-acpi'])
269

    
270
    hvp = instance.hvparams
271
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
272
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
273
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
274

    
275
    if boot_network:
276
      kvm_cmd.extend(['-boot', 'n'])
277

    
278
    disk_type = hvp[constants.HV_DISK_TYPE]
279
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
280
      if_val = ',if=virtio'
281
    else:
282
      if_val = ',if=%s' % disk_type
283
    for cfdev, dev_path in block_devices:
284
      if cfdev.mode != constants.DISK_RDWR:
285
        raise errors.HypervisorError("Instance has read-only disks which"
286
                                     " are not supported by KVM")
287
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
288
      if boot_disk:
289
        kvm_cmd.extend(['-boot', 'c'])
290
        boot_val = ',boot=on'
291
        # We only boot from the first disk
292
        boot_disk = False
293
      else:
294
        boot_val = ''
295

    
296
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
297
      kvm_cmd.extend(['-drive', drive_val])
298

    
299
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
300
    if iso_image:
301
      options = ',format=raw,media=cdrom'
302
      if boot_cdrom:
303
        kvm_cmd.extend(['-boot', 'd'])
304
        options = '%s,boot=on' % options
305
      else:
306
        options = '%s,if=virtio' % options
307
      drive_val = 'file=%s%s' % (iso_image, options)
308
      kvm_cmd.extend(['-drive', drive_val])
309

    
310
    kernel_path = hvp[constants.HV_KERNEL_PATH]
311
    if kernel_path:
312
      kvm_cmd.extend(['-kernel', kernel_path])
313
      initrd_path = hvp[constants.HV_INITRD_PATH]
314
      if initrd_path:
315
        kvm_cmd.extend(['-initrd', initrd_path])
316
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
317
                     hvp[constants.HV_KERNEL_ARGS]]
318
      if hvp[constants.HV_SERIAL_CONSOLE]:
319
        root_append.append('console=ttyS0,38400')
320
      kvm_cmd.extend(['-append', ' '.join(root_append)])
321

    
322
    mouse_type = hvp[constants.HV_USB_MOUSE]
323
    if mouse_type:
324
      kvm_cmd.extend(['-usb'])
325
      kvm_cmd.extend(['-usbdevice', mouse_type])
326

    
327
    # FIXME: handle vnc password
328
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
329
    if vnc_bind_address:
330
      if utils.IsValidIP(vnc_bind_address):
331
        if instance.network_port > constants.VNC_BASE_PORT:
332
          display = instance.network_port - constants.VNC_BASE_PORT
333
          if vnc_bind_address == '0.0.0.0':
334
            vnc_arg = ':%d' % (display)
335
          else:
336
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
337
        else:
338
          logging.error("Network port is not a valid VNC display (%d < %d)."
339
                        " Not starting VNC" %
340
                        (instance.network_port,
341
                         constants.VNC_BASE_PORT))
342
          vnc_arg = 'none'
343

    
344
        # Only allow tls and other option when not binding to a file, for now.
345
        # kvm/qemu gets confused otherwise about the filename to use.
346
        vnc_append = ''
347
        if hvp[constants.HV_VNC_TLS]:
348
          vnc_append = '%s,tls' % vnc_append
349
          if hvp[constants.HV_VNC_X509_VERIFY]:
350
            vnc_append = '%s,x509verify=%s' % (vnc_append,
351
                                               hvp[constants.HV_VNC_X509])
352
          elif hvp[constants.HV_VNC_X509]:
353
            vnc_append = '%s,x509=%s' % (vnc_append,
354
                                         hvp[constants.HV_VNC_X509])
355
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
356

    
357
      else:
358
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
359

    
360
      kvm_cmd.extend(['-vnc', vnc_arg])
361
    else:
362
      kvm_cmd.extend(['-nographic'])
363

    
364
    monitor_dev = 'unix:%s,server,nowait' % \
365
      self._InstanceMonitor(instance.name)
366
    kvm_cmd.extend(['-monitor', monitor_dev])
367
    if hvp[constants.HV_SERIAL_CONSOLE]:
368
      serial_dev = ('unix:%s,server,nowait' %
369
                    self._InstanceSerial(instance.name))
370
      kvm_cmd.extend(['-serial', serial_dev])
371
    else:
372
      kvm_cmd.extend(['-serial', 'none'])
373

    
374
    # Save the current instance nics, but defer their expansion as parameters,
375
    # as we'll need to generate executable temp files for them.
376
    kvm_nics = instance.nics
377
    hvparams = hvp
378

    
379
    return (kvm_cmd, kvm_nics, hvparams)
380

    
381
  def _WriteKVMRuntime(self, instance_name, data):
382
    """Write an instance's KVM runtime
383

384
    """
385
    try:
386
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
387
                      data=data)
388
    except EnvironmentError, err:
389
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
390

    
391
  def _ReadKVMRuntime(self, instance_name):
392
    """Read an instance's KVM runtime
393

394
    """
395
    try:
396
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
397
    except EnvironmentError, err:
398
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
399
    return file_content
400

    
401
  def _SaveKVMRuntime(self, instance, kvm_runtime):
402
    """Save an instance's KVM runtime
403

404
    """
405
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
406
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
407
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
408
    self._WriteKVMRuntime(instance.name, serialized_form)
409

    
410
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
411
    """Load an instance's KVM runtime
412

413
    """
414
    if not serialized_runtime:
415
      serialized_runtime = self._ReadKVMRuntime(instance.name)
416
    loaded_runtime = serializer.Load(serialized_runtime)
417
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
418
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
419
    return (kvm_cmd, kvm_nics, hvparams)
420

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

424
    @type incoming: tuple of strings
425
    @param incoming: (target_host_ip, port)
426

427
    """
428
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
429
    if alive:
430
      raise errors.HypervisorError("Failed to start instance %s: %s" %
431
                                   (instance.name, "already running"))
432

    
433
    temp_files = []
434

    
435
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
436

    
437
    if not kvm_nics:
438
      kvm_cmd.extend(['-net', 'none'])
439
    else:
440
      nic_type = hvparams[constants.HV_NIC_TYPE]
441
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
442
        nic_model = "model=virtio"
443
      else:
444
        nic_model = "model=%s" % nic_type
445

    
446
      for nic_seq, nic in enumerate(kvm_nics):
447
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
448
        script = self._WriteNetScript(instance, nic_seq, nic)
449
        kvm_cmd.extend(['-net', nic_val])
450
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
451
        temp_files.append(script)
452

    
453
    if incoming:
454
      target, port = incoming
455
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
456

    
457
    result = utils.RunCmd(kvm_cmd)
458
    if result.failed:
459
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
460
                                   (instance.name, result.fail_reason,
461
                                    result.output))
462

    
463
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
464
      raise errors.HypervisorError("Failed to start instance %s: %s" %
465
                                   (instance.name))
466

    
467
    for filename in temp_files:
468
      utils.RemoveFile(filename)
469

    
470
  def StartInstance(self, instance, block_devices):
471
    """Start an instance.
472

473
    """
474
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
475
    if alive:
476
      raise errors.HypervisorError("Failed to start instance %s: %s" %
477
                                   (instance.name, "already running"))
478

    
479
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
480
    self._SaveKVMRuntime(instance, kvm_runtime)
481
    self._ExecuteKVMRuntime(instance, kvm_runtime)
482

    
483
  def _CallMonitorCommand(self, instance_name, command):
484
    """Invoke a command on the instance monitor.
485

486
    """
487
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
488
             (utils.ShellQuote(command),
489
              constants.SOCAT_PATH,
490
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
491
    result = utils.RunCmd(socat)
492
    if result.failed:
493
      msg = ("Failed to send command '%s' to instance %s."
494
             " output: %s, error: %s, fail_reason: %s" %
495
             (command, instance_name,
496
              result.stdout, result.stderr, result.fail_reason))
497
      raise errors.HypervisorError(msg)
498

    
499
    return result
500

    
501
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
502
    """Wait for an instance  to power down.
503

504
    """
505
    # Wait up to $timeout seconds
506
    end = time.time() + timeout
507
    wait = 1
508
    while time.time() < end and utils.IsProcessAlive(pid):
509
      self._CallMonitorCommand(instance.name, 'system_powerdown')
510
      time.sleep(wait)
511
      # Make wait time longer for next try
512
      if wait < 5:
513
        wait *= 1.3
514

    
515
  def StopInstance(self, instance, force=False):
516
    """Stop an instance.
517

518
    """
519
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
520
    if pid > 0 and alive:
521
      if force or not instance.hvparams[constants.HV_ACPI]:
522
        utils.KillProcess(pid)
523
      else:
524
        self._RetryInstancePowerdown(instance, pid)
525

    
526
    if not utils.IsProcessAlive(pid):
527
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
528
      return True
529
    else:
530
      return False
531

    
532
  def RebootInstance(self, instance):
533
    """Reboot an instance.
534

535
    """
536
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
537
    # socket the instance will stop, but now power up again. So we'll resort
538
    # to shutdown and restart.
539
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
540
    if not alive:
541
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
542
                                             (instance.name))
543
    # StopInstance will delete the saved KVM runtime so:
544
    # ...first load it...
545
    kvm_runtime = self._LoadKVMRuntime(instance)
546
    # ...now we can safely call StopInstance...
547
    if not self.StopInstance(instance):
548
      self.StopInstance(instance, force=True)
549
    # ...and finally we can save it again, and execute it...
550
    self._SaveKVMRuntime(instance, kvm_runtime)
551
    self._ExecuteKVMRuntime(instance, kvm_runtime)
552

    
553
  def MigrationInfo(self, instance):
554
    """Get instance information to perform a migration.
555

556
    @type instance: L{objects.Instance}
557
    @param instance: instance to be migrated
558
    @rtype: string
559
    @return: content of the KVM runtime file
560

561
    """
562
    return self._ReadKVMRuntime(instance.name)
563

    
564
  def AcceptInstance(self, instance, info, target):
565
    """Prepare to accept an instance.
566

567
    @type instance: L{objects.Instance}
568
    @param instance: instance to be accepted
569
    @type info: string
570
    @param info: content of the KVM runtime file on the source node
571
    @type target: string
572
    @param target: target host (usually ip), on this node
573

574
    """
575
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
576
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
577
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
578

    
579
  def FinalizeMigration(self, instance, info, success):
580
    """Finalize an instance migration.
581

582
    Stop the incoming mode KVM.
583

584
    @type instance: L{objects.Instance}
585
    @param instance: instance whose migration is being aborted
586

587
    """
588
    if success:
589
      self._WriteKVMRuntime(instance.name, info)
590
    else:
591
      self.StopInstance(instance, force=True)
592

    
593
  def MigrateInstance(self, instance_name, target, live):
594
    """Migrate an instance to a target node.
595

596
    The migration will not be attempted if the instance is not
597
    currently running.
598

599
    @type instance_name: string
600
    @param instance_name: name of the instance to be migrated
601
    @type target: string
602
    @param target: ip address of the target node
603
    @type live: boolean
604
    @param live: perform a live migration
605

606
    """
607
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
608
    if not alive:
609
      raise errors.HypervisorError("Instance not running, cannot migrate")
610

    
611
    if not live:
612
      self._CallMonitorCommand(instance_name, 'stop')
613

    
614
    migrate_command = ('migrate -d tcp:%s:%s' %
615
                       (target, constants.KVM_MIGRATION_PORT))
616
    self._CallMonitorCommand(instance_name, migrate_command)
617

    
618
    info_command = 'info migrate'
619
    done = False
620
    while not done:
621
      result = self._CallMonitorCommand(instance_name, info_command)
622
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
623
      if not match:
624
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
625
                                     result.stdout)
626
      else:
627
        status = match.group(1)
628
        if status == 'completed':
629
          done = True
630
        elif status == 'active':
631
          time.sleep(2)
632
        elif status == 'failed' or status == 'cancelled':
633
          if not live:
634
            self._CallMonitorCommand(instance_name, 'cont')
635
          raise errors.HypervisorError("Migration %s at the kvm level" %
636
                                       status)
637
        else:
638
          logging.info("KVM: unknown migration status '%s'" % status)
639
          time.sleep(2)
640

    
641
    utils.KillProcess(pid)
642
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
643

    
644
  def GetNodeInfo(self):
645
    """Return information about the node.
646

647
    This is just a wrapper over the base GetLinuxNodeInfo method.
648

649
    @return: a dict with the following keys (values in MiB):
650
          - memory_total: the total memory size on the node
651
          - memory_free: the available memory on the node for instances
652
          - memory_dom0: the memory used by the node itself, if available
653

654
    """
655
    return self.GetLinuxNodeInfo()
656

    
657
  @classmethod
658
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
659
    """Return a command for connecting to the console of an instance.
660

661
    """
662
    if hvparams[constants.HV_SERIAL_CONSOLE]:
663
      # FIXME: The socat shell is not perfect. In particular the way we start
664
      # it ctrl+c will close it, rather than being passed to the other end.
665
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
666
      # will be no way of exiting socat (except killing it from another shell)
667
      # and ctrl+c doesn't work anyway, printing ^C rather than being
668
      # interpreted by kvm. For now we'll leave it this way, which at least
669
      # allows a minimal interaction and changes on the machine.
670
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
671
                       (constants.SOCAT_PATH,
672
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
673
    else:
674
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
675

    
676
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
677
    if vnc_bind_address:
678
      if instance.network_port > constants.VNC_BASE_PORT:
679
        display = instance.network_port - constants.VNC_BASE_PORT
680
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
681
                       " (display: %d)'" % (vnc_bind_address,
682
                                            instance.network_port,
683
                                            display))
684
        shell_command = "%s; %s" % (vnc_command, shell_command)
685

    
686
    return shell_command
687

    
688
  def Verify(self):
689
    """Verify the hypervisor.
690

691
    Check that the binary exists.
692

693
    """
694
    if not os.path.exists(constants.KVM_PATH):
695
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
696
    if not os.path.exists(constants.SOCAT_PATH):
697
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
698

    
699

    
700
  @classmethod
701
  def CheckParameterSyntax(cls, hvparams):
702
    """Check the given parameters for validity.
703

704
    @type hvparams:  dict
705
    @param hvparams: dictionary with parameter names/value
706
    @raise errors.HypervisorError: when a parameter is not valid
707

708
    """
709
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
710

    
711
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
712
    if kernel_path:
713
      if not hvparams[constants.HV_ROOT_PATH]:
714
        raise errors.HypervisorError("Need a root partition for the instance,"
715
                                     " if a kernel is defined")
716

    
717
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
718
        not hvparams[constants.HV_VNC_X509]):
719
      raise errors.HypervisorError("%s must be defined, if %s is" %
720
                                   (constants.HV_VNC_X509,
721
                                    constants.HV_VNC_X509_VERIFY))
722

    
723
    boot_order = hvparams[constants.HV_BOOT_ORDER]
724

    
725
    if (boot_order == constants.HT_BO_CDROM and
726
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
727
      raise errors.HypervisorError("Cannot boot from cdrom without an"
728
                                   " ISO path")
729
    if (boot_order == constants.HT_BO_NETWORK and
730
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
731
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
732
                                   " change the NIC type.")
733

    
734
  @classmethod
735
  def PowercycleNode(cls):
736
    """KVM powercycle, just a wrapper over Linux powercycle.
737

738
    """
739
    cls.LinuxPowercycle()