Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ d19d94db

History | View | Annotate | Download (29.5 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.hypervisor import hv_base
41

    
42

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

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

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

    
85
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
86
                                    re.M | re.I)
87
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
88
  _MIGRATION_INFO_RETRY_DELAY = 2
89

    
90
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
91

    
92
  ANCILLARY_FILES = [
93
    _KVM_NETWORK_SCRIPT,
94
    ]
95

    
96
  def __init__(self):
97
    hv_base.BaseHypervisor.__init__(self)
98
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
99
    # in a tmpfs filesystem or has been otherwise wiped out.
100
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
101
    utils.EnsureDirs(dirs)
102

    
103
  @classmethod
104
  def _InstancePidFile(cls, instance_name):
105
    """Returns the instance pidfile.
106

107
    """
108
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
109

    
110
  def _InstancePidAlive(self, instance_name):
111
    """Returns the instance pid and pidfile
112

113
    """
114
    pidfile = self._InstancePidFile(instance_name)
115
    pid = utils.ReadPidFile(pidfile)
116
    alive = utils.IsProcessAlive(pid)
117

    
118
    return (pidfile, pid, alive)
119

    
120
  def _CheckDown(self, instance_name):
121
    """Raises an error unless the given instance is down.
122

123
    """
124
    alive = self._InstancePidAlive(instance_name)[2]
125
    if alive:
126
      raise errors.HypervisorError("Failed to start instance %s: %s" %
127
                                   (instance_name, "already running"))
128

    
129
  @classmethod
130
  def _InstanceMonitor(cls, instance_name):
131
    """Returns the instance monitor socket name
132

133
    """
134
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
135

    
136
  @classmethod
137
  def _InstanceSerial(cls, instance_name):
138
    """Returns the instance serial socket name
139

140
    """
141
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
142

    
143
  @staticmethod
144
  def _SocatUnixConsoleParams():
145
    """Returns the correct parameters for socat
146

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

149
    """
150
    if constants.SOCAT_USE_ESCAPE:
151
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
152
    else:
153
      return "echo=0,icanon=0"
154

    
155
  @classmethod
156
  def _InstanceKVMRuntime(cls, instance_name):
157
    """Returns the instance KVM runtime filename
158

159
    """
160
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
161

    
162
  @classmethod
163
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
164
    """Removes an instance's rutime sockets/files.
165

166
    """
167
    utils.RemoveFile(pidfile)
168
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
169
    utils.RemoveFile(cls._InstanceSerial(instance_name))
170
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
171

    
172
  def _WriteNetScript(self, instance, seq, nic):
173
    """Write a script to connect a net interface to the proper bridge.
174

175
    This can be used by any qemu-type hypervisor.
176

177
    @param instance: instance we're acting on
178
    @type instance: instance object
179
    @param seq: nic sequence number
180
    @type seq: int
181
    @param nic: nic we're acting on
182
    @type nic: nic object
183
    @return: netscript file name
184
    @rtype: string
185

186
    """
187
    script = StringIO()
188
    script.write("#!/bin/sh\n")
189
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
190
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
191
    script.write("export INSTANCE=%s\n" % instance.name)
192
    script.write("export MAC=%s\n" % nic.mac)
193
    if nic.ip:
194
      script.write("export IP=%s\n" % nic.ip)
195
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
196
    if nic.nicparams[constants.NIC_LINK]:
197
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
198
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
199
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
200
    script.write("export INTERFACE=$1\n")
201
    # TODO: make this configurable at ./configure time
202
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
203
    script.write("  # Execute the user-specific vif file\n")
204
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
205
    script.write("else\n")
206
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
207
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
208
      script.write("  # Connect the interface to the bridge\n")
209
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
210
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
211
      if not nic.ip:
212
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
213
      script.write("  # Route traffic targeted at the IP to the interface\n")
214
      if nic.nicparams[constants.NIC_LINK]:
215
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
216
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
217
        script.write("  ip route replace $IP table $LINK proto static"
218
                     " dev $INTERFACE\n")
219
      else:
220
        script.write("  ip route replace $IP proto static"
221
                     " dev $INTERFACE\n")
222
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
223
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
224
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
225
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
226
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
227
      script.write("  fi\n")
228
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
229
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
230
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
231
      script.write("  fi\n")
232
    script.write("fi\n\n")
233
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
234
    # mounted noexec sometimes, so we'll have to find another place.
235
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
236
    tmpfile = os.fdopen(tmpfd, 'w')
237
    try:
238
      tmpfile.write(script.getvalue())
239
    finally:
240
      tmpfile.close()
241
    os.chmod(tmpfile_name, 0755)
242
    return tmpfile_name
243

    
244
  def ListInstances(self):
245
    """Get the list of running instances.
246

247
    We can do this by listing our live instances directory and
248
    checking whether the associated kvm process is still alive.
249

250
    """
251
    result = []
252
    for name in os.listdir(self._PIDS_DIR):
253
      filename = utils.PathJoin(self._PIDS_DIR, name)
254
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
255
        result.append(name)
256
    return result
257

    
258
  def GetInstanceInfo(self, instance_name):
259
    """Get instance properties.
260

261
    @param instance_name: the instance name
262

263
    @return: tuple (name, id, memory, vcpus, stat, times)
264

265
    """
266
    _, pid, alive = self._InstancePidAlive(instance_name)
267
    if not alive:
268
      return None
269

    
270
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
271
    try:
272
      cmdline = utils.ReadFile(cmdline_file)
273
    except EnvironmentError, err:
274
      raise errors.HypervisorError("Failed to list instance %s: %s" %
275
                                   (instance_name, err))
276

    
277
    memory = 0
278
    vcpus = 0
279
    stat = "---b-"
280
    times = "0"
281

    
282
    arg_list = cmdline.split('\x00')
283
    while arg_list:
284
      arg =  arg_list.pop(0)
285
      if arg == '-m':
286
        memory = int(arg_list.pop(0))
287
      elif arg == '-smp':
288
        vcpus = int(arg_list.pop(0))
289

    
290
    return (instance_name, pid, memory, vcpus, stat, times)
291

    
292
  def GetAllInstancesInfo(self):
293
    """Get properties of all instances.
294

295
    @return: list of tuples (name, id, memory, vcpus, stat, times)
296

297
    """
298
    data = []
299
    for name in os.listdir(self._PIDS_DIR):
300
      filename = utils.PathJoin(self._PIDS_DIR, name)
301
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
302
        try:
303
          info = self.GetInstanceInfo(name)
304
        except errors.HypervisorError:
305
          continue
306
        if info:
307
          data.append(info)
308

    
309
    return data
310

    
311
  def _GenerateKVMRuntime(self, instance, block_devices):
312
    """Generate KVM information to start an instance.
313

314
    """
315
    pidfile  = self._InstancePidFile(instance.name)
316
    kvm = constants.KVM_PATH
317
    kvm_cmd = [kvm]
318
    # used just by the vnc server, if enabled
319
    kvm_cmd.extend(['-name', instance.name])
320
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
321
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
322
    kvm_cmd.extend(['-pidfile', pidfile])
323
    kvm_cmd.extend(['-daemonize'])
324
    if not instance.hvparams[constants.HV_ACPI]:
325
      kvm_cmd.extend(['-no-acpi'])
326

    
327
    hvp = instance.hvparams
328
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
329
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
330
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
331

    
332
    security_model = hvp[constants.HV_SECURITY_MODEL]
333
    if security_model == constants.HT_SM_USER:
334
      kvm_cmd.extend(['-runas', hvp[constants.HV_SECURITY_DOMAIN]])
335

    
336
    if boot_network:
337
      kvm_cmd.extend(['-boot', 'n'])
338

    
339
    disk_type = hvp[constants.HV_DISK_TYPE]
340
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
341
      if_val = ',if=virtio'
342
    else:
343
      if_val = ',if=%s' % disk_type
344
    # Cache mode
345
    disk_cache = hvp[constants.HV_DISK_CACHE]
346
    if disk_cache != constants.HT_CACHE_DEFAULT:
347
      cache_val = ",cache=%s" % disk_cache
348
    else:
349
      cache_val = ""
350
    for cfdev, dev_path in block_devices:
351
      if cfdev.mode != constants.DISK_RDWR:
352
        raise errors.HypervisorError("Instance has read-only disks which"
353
                                     " are not supported by KVM")
354
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
355
      if boot_disk:
356
        kvm_cmd.extend(['-boot', 'c'])
357
        boot_val = ',boot=on'
358
        # We only boot from the first disk
359
        boot_disk = False
360
      else:
361
        boot_val = ''
362

    
363
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
364
                                                cache_val)
365
      kvm_cmd.extend(['-drive', drive_val])
366

    
367
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
368
    if iso_image:
369
      options = ',format=raw,media=cdrom'
370
      if boot_cdrom:
371
        kvm_cmd.extend(['-boot', 'd'])
372
        options = '%s,boot=on' % options
373
      else:
374
        options = '%s,if=virtio' % options
375
      drive_val = 'file=%s%s' % (iso_image, options)
376
      kvm_cmd.extend(['-drive', drive_val])
377

    
378
    kernel_path = hvp[constants.HV_KERNEL_PATH]
379
    if kernel_path:
380
      kvm_cmd.extend(['-kernel', kernel_path])
381
      initrd_path = hvp[constants.HV_INITRD_PATH]
382
      if initrd_path:
383
        kvm_cmd.extend(['-initrd', initrd_path])
384
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
385
                     hvp[constants.HV_KERNEL_ARGS]]
386
      if hvp[constants.HV_SERIAL_CONSOLE]:
387
        root_append.append('console=ttyS0,38400')
388
      kvm_cmd.extend(['-append', ' '.join(root_append)])
389

    
390
    mouse_type = hvp[constants.HV_USB_MOUSE]
391
    if mouse_type:
392
      kvm_cmd.extend(['-usb'])
393
      kvm_cmd.extend(['-usbdevice', mouse_type])
394

    
395
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
396
    if vnc_bind_address:
397
      if utils.IsValidIP(vnc_bind_address):
398
        if instance.network_port > constants.VNC_BASE_PORT:
399
          display = instance.network_port - constants.VNC_BASE_PORT
400
          if vnc_bind_address == '0.0.0.0':
401
            vnc_arg = ':%d' % (display)
402
          else:
403
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
404
        else:
405
          logging.error("Network port is not a valid VNC display (%d < %d)."
406
                        " Not starting VNC", instance.network_port,
407
                        constants.VNC_BASE_PORT)
408
          vnc_arg = 'none'
409

    
410
        # Only allow tls and other option when not binding to a file, for now.
411
        # kvm/qemu gets confused otherwise about the filename to use.
412
        vnc_append = ''
413
        if hvp[constants.HV_VNC_TLS]:
414
          vnc_append = '%s,tls' % vnc_append
415
          if hvp[constants.HV_VNC_X509_VERIFY]:
416
            vnc_append = '%s,x509verify=%s' % (vnc_append,
417
                                               hvp[constants.HV_VNC_X509])
418
          elif hvp[constants.HV_VNC_X509]:
419
            vnc_append = '%s,x509=%s' % (vnc_append,
420
                                         hvp[constants.HV_VNC_X509])
421
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
422
          vnc_append = '%s,password' % vnc_append
423

    
424
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
425

    
426
      else:
427
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
428

    
429
      kvm_cmd.extend(['-vnc', vnc_arg])
430
    else:
431
      kvm_cmd.extend(['-nographic'])
432

    
433
    monitor_dev = ("unix:%s,server,nowait" %
434
                   self._InstanceMonitor(instance.name))
435
    kvm_cmd.extend(['-monitor', monitor_dev])
436
    if hvp[constants.HV_SERIAL_CONSOLE]:
437
      serial_dev = ('unix:%s,server,nowait' %
438
                    self._InstanceSerial(instance.name))
439
      kvm_cmd.extend(['-serial', serial_dev])
440
    else:
441
      kvm_cmd.extend(['-serial', 'none'])
442

    
443
    if hvp[constants.HV_USE_LOCALTIME]:
444
      kvm_cmd.extend(['-localtime'])
445

    
446
    # Save the current instance nics, but defer their expansion as parameters,
447
    # as we'll need to generate executable temp files for them.
448
    kvm_nics = instance.nics
449
    hvparams = hvp
450

    
451
    return (kvm_cmd, kvm_nics, hvparams)
452

    
453
  def _WriteKVMRuntime(self, instance_name, data):
454
    """Write an instance's KVM runtime
455

456
    """
457
    try:
458
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
459
                      data=data)
460
    except EnvironmentError, err:
461
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
462

    
463
  def _ReadKVMRuntime(self, instance_name):
464
    """Read an instance's KVM runtime
465

466
    """
467
    try:
468
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
469
    except EnvironmentError, err:
470
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
471
    return file_content
472

    
473
  def _SaveKVMRuntime(self, instance, kvm_runtime):
474
    """Save an instance's KVM runtime
475

476
    """
477
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
478
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
479
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
480
    self._WriteKVMRuntime(instance.name, serialized_form)
481

    
482
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
483
    """Load an instance's KVM runtime
484

485
    """
486
    if not serialized_runtime:
487
      serialized_runtime = self._ReadKVMRuntime(instance.name)
488
    loaded_runtime = serializer.Load(serialized_runtime)
489
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
490
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
491
    return (kvm_cmd, kvm_nics, hvparams)
492

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

496
    @type incoming: tuple of strings
497
    @param incoming: (target_host_ip, port)
498

499
    """
500
    hvp = instance.hvparams
501
    name = instance.name
502
    self._CheckDown(name)
503

    
504
    temp_files = []
505

    
506
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
507

    
508
    if not kvm_nics:
509
      kvm_cmd.extend(['-net', 'none'])
510
    else:
511
      nic_type = hvparams[constants.HV_NIC_TYPE]
512
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
513
        nic_model = "model=virtio"
514
      else:
515
        nic_model = "model=%s" % nic_type
516

    
517
      for nic_seq, nic in enumerate(kvm_nics):
518
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
519
        script = self._WriteNetScript(instance, nic_seq, nic)
520
        kvm_cmd.extend(['-net', nic_val])
521
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
522
        temp_files.append(script)
523

    
524
    if incoming:
525
      target, port = incoming
526
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
527

    
528
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
529
    vnc_pwd = None
530
    if vnc_pwd_file:
531
      try:
532
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
533
      except EnvironmentError, err:
534
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
535
                                     % (vnc_pwd_file, err))
536

    
537
    result = utils.RunCmd(kvm_cmd)
538
    if result.failed:
539
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
540
                                   (name, result.fail_reason, result.output))
541

    
542
    if not self._InstancePidAlive(name)[2]:
543
      raise errors.HypervisorError("Failed to start instance %s" % name)
544

    
545
    if vnc_pwd:
546
      change_cmd = 'change vnc password %s' % vnc_pwd
547
      self._CallMonitorCommand(instance.name, change_cmd)
548

    
549
    for filename in temp_files:
550
      utils.RemoveFile(filename)
551

    
552
  def StartInstance(self, instance, block_devices):
553
    """Start an instance.
554

555
    """
556
    self._CheckDown(instance.name)
557
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
558
    self._SaveKVMRuntime(instance, kvm_runtime)
559
    self._ExecuteKVMRuntime(instance, kvm_runtime)
560

    
561
  def _CallMonitorCommand(self, instance_name, command):
562
    """Invoke a command on the instance monitor.
563

564
    """
565
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
566
             (utils.ShellQuote(command),
567
              constants.SOCAT_PATH,
568
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
569
    result = utils.RunCmd(socat)
570
    if result.failed:
571
      msg = ("Failed to send command '%s' to instance %s."
572
             " output: %s, error: %s, fail_reason: %s" %
573
             (command, instance_name,
574
              result.stdout, result.stderr, result.fail_reason))
575
      raise errors.HypervisorError(msg)
576

    
577
    return result
578

    
579
  def StopInstance(self, instance, force=False, retry=False):
580
    """Stop an instance.
581

582
    """
583
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
584
    if pid > 0 and alive:
585
      if force or not instance.hvparams[constants.HV_ACPI]:
586
        utils.KillProcess(pid)
587
      else:
588
        self._CallMonitorCommand(instance.name, 'system_powerdown')
589

    
590
    if not utils.IsProcessAlive(pid):
591
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
592
      return True
593
    else:
594
      return False
595

    
596
  def RebootInstance(self, instance):
597
    """Reboot an instance.
598

599
    """
600
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
601
    # socket the instance will stop, but now power up again. So we'll resort
602
    # to shutdown and restart.
603
    _, _, alive = self._InstancePidAlive(instance.name)
604
    if not alive:
605
      raise errors.HypervisorError("Failed to reboot instance %s:"
606
                                   " not running" % instance.name)
607
    # StopInstance will delete the saved KVM runtime so:
608
    # ...first load it...
609
    kvm_runtime = self._LoadKVMRuntime(instance)
610
    # ...now we can safely call StopInstance...
611
    if not self.StopInstance(instance):
612
      self.StopInstance(instance, force=True)
613
    # ...and finally we can save it again, and execute it...
614
    self._SaveKVMRuntime(instance, kvm_runtime)
615
    self._ExecuteKVMRuntime(instance, kvm_runtime)
616

    
617
  def MigrationInfo(self, instance):
618
    """Get instance information to perform a migration.
619

620
    @type instance: L{objects.Instance}
621
    @param instance: instance to be migrated
622
    @rtype: string
623
    @return: content of the KVM runtime file
624

625
    """
626
    return self._ReadKVMRuntime(instance.name)
627

    
628
  def AcceptInstance(self, instance, info, target):
629
    """Prepare to accept an instance.
630

631
    @type instance: L{objects.Instance}
632
    @param instance: instance to be accepted
633
    @type info: string
634
    @param info: content of the KVM runtime file on the source node
635
    @type target: string
636
    @param target: target host (usually ip), on this node
637

638
    """
639
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
640
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
641
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
642

    
643
  def FinalizeMigration(self, instance, info, success):
644
    """Finalize an instance migration.
645

646
    Stop the incoming mode KVM.
647

648
    @type instance: L{objects.Instance}
649
    @param instance: instance whose migration is being aborted
650

651
    """
652
    if success:
653
      self._WriteKVMRuntime(instance.name, info)
654
    else:
655
      self.StopInstance(instance, force=True)
656

    
657
  def MigrateInstance(self, instance, target, live):
658
    """Migrate an instance to a target node.
659

660
    The migration will not be attempted if the instance is not
661
    currently running.
662

663
    @type instance: L{objects.Instance}
664
    @param instance: the instance to be migrated
665
    @type target: string
666
    @param target: ip address of the target node
667
    @type live: boolean
668
    @param live: perform a live migration
669

670
    """
671
    instance_name = instance.name
672
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
673
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
674
    if not alive:
675
      raise errors.HypervisorError("Instance not running, cannot migrate")
676

    
677
    if not utils.TcpPing(target, port, live_port_needed=True):
678
      raise errors.HypervisorError("Remote host %s not listening on port"
679
                                   " %s, cannot migrate" % (target, port))
680

    
681
    if not live:
682
      self._CallMonitorCommand(instance_name, 'stop')
683

    
684
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
685
    self._CallMonitorCommand(instance_name, migrate_command)
686

    
687
    info_command = 'info migrate'
688
    done = False
689
    broken_answers = 0
690
    while not done:
691
      result = self._CallMonitorCommand(instance_name, info_command)
692
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
693
      if not match:
694
        broken_answers += 1
695
        if not result.stdout:
696
          logging.info("KVM: empty 'info migrate' result")
697
        else:
698
          logging.warning("KVM: unknown 'info migrate' result: %s",
699
                          result.stdout)
700
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
701
      else:
702
        status = match.group(1)
703
        if status == 'completed':
704
          done = True
705
        elif status == 'active':
706
          # reset the broken answers count
707
          broken_answers = 0
708
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
709
        elif status == 'failed' or status == 'cancelled':
710
          if not live:
711
            self._CallMonitorCommand(instance_name, 'cont')
712
          raise errors.HypervisorError("Migration %s at the kvm level" %
713
                                       status)
714
        else:
715
          logging.warning("KVM: unknown migration status '%s'", status)
716
          broken_answers += 1
717
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
718
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
719
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
720

    
721
    utils.KillProcess(pid)
722
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
723

    
724
  def GetNodeInfo(self):
725
    """Return information about the node.
726

727
    This is just a wrapper over the base GetLinuxNodeInfo method.
728

729
    @return: a dict with the following keys (values in MiB):
730
          - memory_total: the total memory size on the node
731
          - memory_free: the available memory on the node for instances
732
          - memory_dom0: the memory used by the node itself, if available
733

734
    """
735
    return self.GetLinuxNodeInfo()
736

    
737
  @classmethod
738
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
739
    """Return a command for connecting to the console of an instance.
740

741
    """
742
    if hvparams[constants.HV_SERIAL_CONSOLE]:
743
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
744
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
745
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
746
    else:
747
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
748

    
749
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
750
    if vnc_bind_address:
751
      if instance.network_port > constants.VNC_BASE_PORT:
752
        display = instance.network_port - constants.VNC_BASE_PORT
753
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
754
                       " (display: %d)'" % (vnc_bind_address,
755
                                            instance.network_port,
756
                                            display))
757
        shell_command = "%s; %s" % (vnc_command, shell_command)
758

    
759
    return shell_command
760

    
761
  def Verify(self):
762
    """Verify the hypervisor.
763

764
    Check that the binary exists.
765

766
    """
767
    if not os.path.exists(constants.KVM_PATH):
768
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
769
    if not os.path.exists(constants.SOCAT_PATH):
770
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
771

    
772

    
773
  @classmethod
774
  def CheckParameterSyntax(cls, hvparams):
775
    """Check the given parameters for validity.
776

777
    @type hvparams:  dict
778
    @param hvparams: dictionary with parameter names/value
779
    @raise errors.HypervisorError: when a parameter is not valid
780

781
    """
782
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
783

    
784
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
785
    if kernel_path:
786
      if not hvparams[constants.HV_ROOT_PATH]:
787
        raise errors.HypervisorError("Need a root partition for the instance,"
788
                                     " if a kernel is defined")
789

    
790
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
791
        not hvparams[constants.HV_VNC_X509]):
792
      raise errors.HypervisorError("%s must be defined, if %s is" %
793
                                   (constants.HV_VNC_X509,
794
                                    constants.HV_VNC_X509_VERIFY))
795

    
796
    boot_order = hvparams[constants.HV_BOOT_ORDER]
797
    if (boot_order == constants.HT_BO_CDROM and
798
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
799
      raise errors.HypervisorError("Cannot boot from cdrom without an"
800
                                   " ISO path")
801

    
802
    security_model = hvparams[constants.HV_SECURITY_MODEL]
803
    if security_model == constants.HT_SM_USER:
804
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
805
        raise errors.HypervisorError("A security domain (user to run kvm as)"
806
                                     " must be specified")
807
    elif (security_model == constants.HT_SM_NONE or
808
          security_model == constants.HT_SM_POOL):
809
      if hvparams[constants.HV_SECURITY_DOMAIN]:
810
        raise errors.HypervisorError("Cannot have a security domain when the"
811
                                     " security model is 'none' or 'pool'")
812
    if security_model == constants.HT_SM_POOL:
813
      raise errors.HypervisorError("Security model pool is not supported yet")
814

    
815
  @classmethod
816
  def ValidateParameters(cls, hvparams):
817
    """Check the given parameters for validity.
818

819
    @type hvparams:  dict
820
    @param hvparams: dictionary with parameter names/value
821
    @raise errors.HypervisorError: when a parameter is not valid
822

823
    """
824
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
825

    
826
    security_model = hvparams[constants.HV_SECURITY_MODEL]
827
    if security_model == constants.HT_SM_USER:
828
      username = hvparams[constants.HV_SECURITY_DOMAIN]
829
      try:
830
        pwdentry = pwd.getpwnam(username)
831
      except KeyError:
832
        raise errors.HypervisorError("Unknown security domain user %s"
833
                                     % username)
834

    
835
  @classmethod
836
  def PowercycleNode(cls):
837
    """KVM powercycle, just a wrapper over Linux powercycle.
838

839
    """
840
    cls.LinuxPowercycle()