Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 637ce7f9

History | View | Annotate | Download (21.3 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,
53
    constants.HV_INITRD_PATH,
54
    constants.HV_ACPI,
55
    ]
56

    
57
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
58
                                    re.M | re.I)
59

    
60
  def __init__(self):
61
    hv_base.BaseHypervisor.__init__(self)
62
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
63
    # in a tmpfs filesystem or has been otherwise wiped out.
64
    for mydir in self._DIRS:
65
      if not os.path.exists(mydir):
66
        os.mkdir(mydir)
67

    
68
  def _InstancePidAlive(self, instance_name):
69
    """Returns the instance pid and pidfile
70

71
    """
72
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
73
    pid = utils.ReadPidFile(pidfile)
74
    alive = utils.IsProcessAlive(pid)
75

    
76
    return (pidfile, pid, alive)
77

    
78
  @classmethod
79
  def _InstanceMonitor(cls, instance_name):
80
    """Returns the instance monitor socket name
81

82
    """
83
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
84

    
85
  @classmethod
86
  def _InstanceSerial(cls, instance_name):
87
    """Returns the instance serial socket name
88

89
    """
90
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
91

    
92
  @classmethod
93
  def _InstanceKVMRuntime(cls, instance_name):
94
    """Returns the instance KVM runtime filename
95

96
    """
97
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
98

    
99
  def _WriteNetScript(self, instance, seq, nic):
100
    """Write a script to connect a net interface to the proper bridge.
101

102
    This can be used by any qemu-type hypervisor.
103

104
    @param instance: instance we're acting on
105
    @type instance: instance object
106
    @param seq: nic sequence number
107
    @type seq: int
108
    @param nic: nic we're acting on
109
    @type nic: nic object
110
    @return: netscript file name
111
    @rtype: string
112

113
    """
114
    script = StringIO()
115
    script.write("#!/bin/sh\n")
116
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
117
    script.write("export INSTANCE=%s\n" % instance.name)
118
    script.write("export MAC=%s\n" % nic.mac)
119
    script.write("export IP=%s\n" % nic.ip)
120
    script.write("export BRIDGE=%s\n" % nic.bridge)
121
    script.write("export INTERFACE=$1\n")
122
    # TODO: make this configurable at ./configure time
123
    script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
124
    script.write("  # Execute the user-specific vif file\n")
125
    script.write("  /etc/ganeti/kvm-vif-bridge\n")
126
    script.write("else\n")
127
    script.write("  # Connect the interface to the bridge\n")
128
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
129
    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
130
    script.write("fi\n\n")
131
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
132
    # mounted noexec sometimes, so we'll have to find another place.
133
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
134
    tmpfile = os.fdopen(tmpfd, 'w')
135
    tmpfile.write(script.getvalue())
136
    tmpfile.close()
137
    os.chmod(tmpfile_name, 0755)
138
    return tmpfile_name
139

    
140
  def ListInstances(self):
141
    """Get the list of running instances.
142

143
    We can do this by listing our live instances directory and
144
    checking whether the associated kvm process is still alive.
145

146
    """
147
    result = []
148
    for name in os.listdir(self._PIDS_DIR):
149
      filename = "%s/%s" % (self._PIDS_DIR, name)
150
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
151
        result.append(name)
152
    return result
153

    
154
  def GetInstanceInfo(self, instance_name):
155
    """Get instance properties.
156

157
    @param instance_name: the instance name
158

159
    @return: tuple (name, id, memory, vcpus, stat, times)
160

161
    """
162
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
163
    if not alive:
164
      return None
165

    
166
    cmdline_file = "/proc/%s/cmdline" % pid
167
    try:
168
      fh = open(cmdline_file, 'r')
169
      try:
170
        cmdline = fh.read()
171
      finally:
172
        fh.close()
173
    except EnvironmentError, err:
174
      raise errors.HypervisorError("Failed to list instance %s: %s" %
175
                                   (instance_name, err))
176

    
177
    memory = 0
178
    vcpus = 0
179
    stat = "---b-"
180
    times = "0"
181

    
182
    arg_list = cmdline.split('\x00')
183
    while arg_list:
184
      arg =  arg_list.pop(0)
185
      if arg == '-m':
186
        memory = arg_list.pop(0)
187
      elif arg == '-smp':
188
        vcpus = arg_list.pop(0)
189

    
190
    return (instance_name, pid, memory, vcpus, stat, times)
191

    
192
  def GetAllInstancesInfo(self):
193
    """Get properties of all instances.
194

195
    @return: list of tuples (name, id, memory, vcpus, stat, times)
196

197
    """
198
    data = []
199
    for name in os.listdir(self._PIDS_DIR):
200
      filename = "%s/%s" % (self._PIDS_DIR, name)
201
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
202
        data.append(self.GetInstanceInfo(name))
203

    
204
    return data
205

    
206
  def _GenerateKVMRuntime(self, instance, block_devices, extra_args):
207
    """Generate KVM information to start an instance.
208

209
    """
210
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
211
    kvm = constants.KVM_PATH
212
    kvm_cmd = [kvm]
213
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
214
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
215
    kvm_cmd.extend(['-pidfile', pidfile])
216
    # used just by the vnc server, if enabled
217
    kvm_cmd.extend(['-name', instance.name])
218
    kvm_cmd.extend(['-daemonize'])
219
    if not instance.hvparams[constants.HV_ACPI]:
220
      kvm_cmd.extend(['-no-acpi'])
221

    
222
    boot_drive = True
223
    for cfdev, dev_path in block_devices:
224
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
225
      if boot_drive:
226
        boot_val = ',boot=on'
227
        boot_drive = False
228
      else:
229
        boot_val = ''
230

    
231
      # TODO: handle different if= types
232
      if_val = ',if=virtio'
233

    
234
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
235
      kvm_cmd.extend(['-drive', drive_val])
236

    
237
    kvm_cmd.extend(['-kernel', instance.hvparams[constants.HV_KERNEL_PATH]])
238

    
239
    initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
240
    if initrd_path:
241
      kvm_cmd.extend(['-initrd', initrd_path])
242

    
243
    kvm_cmd.extend(['-append', 'console=ttyS0,38400 root=/dev/vda'])
244

    
245
    #"hvm_boot_order",
246
    #"hvm_cdrom_image_path",
247

    
248
    kvm_cmd.extend(['-nographic'])
249
    # FIXME: handle vnc, if needed
250
    # How do we decide whether to have it or not?? :(
251
    #"vnc_bind_address",
252
    #"network_port"
253
    monitor_dev = 'unix:%s,server,nowait' % \
254
      self._InstanceMonitor(instance.name)
255
    kvm_cmd.extend(['-monitor', monitor_dev])
256
    serial_dev = 'unix:%s,server,nowait' % self._InstanceSerial(instance.name)
257
    kvm_cmd.extend(['-serial', serial_dev])
258

    
259
    # Save the current instance nics, but defer their expansion as parameters,
260
    # as we'll need to generate executable temp files for them.
261
    kvm_nics = instance.nics
262

    
263
    return (kvm_cmd, kvm_nics)
264

    
265
  def _WriteKVMRuntime(self, instance_name, data):
266
    """Write an instance's KVM runtime
267

268
    """
269
    try:
270
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
271
                      data=data)
272
    except EnvironmentError, err:
273
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
274

    
275
  def _ReadKVMRuntime(self, instance_name):
276
    """Read an instance's KVM runtime
277

278
    """
279
    try:
280
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
281
    except EnvironmentError, err:
282
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
283
    return file_content
284

    
285
  def _SaveKVMRuntime(self, instance, kvm_runtime):
286
    """Save an instance's KVM runtime
287

288
    """
289
    kvm_cmd, kvm_nics = kvm_runtime
290
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
291
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics))
292
    self._WriteKVMRuntime(instance.name, serialized_form)
293

    
294
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
295
    """Load an instance's KVM runtime
296

297
    """
298
    if not serialized_runtime:
299
      serialized_runtime = self._ReadKVMRuntime(instance.name)
300
    loaded_runtime = serializer.Load(serialized_runtime)
301
    kvm_cmd, serialized_nics = loaded_runtime
302
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
303
    return (kvm_cmd, kvm_nics)
304

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

308
    @type incoming: tuple of strings
309
    @param incoming: (target_host_ip, port)
310

311
    """
312
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
313
    if alive:
314
      raise errors.HypervisorError("Failed to start instance %s: %s" %
315
                                   (instance.name, "already running"))
316

    
317
    temp_files = []
318

    
319
    kvm_cmd, kvm_nics = kvm_runtime
320

    
321
    if not kvm_nics:
322
      kvm_cmd.extend(['-net', 'none'])
323
    else:
324
      for nic_seq, nic in enumerate(kvm_nics):
325
        nic_val = "nic,macaddr=%s,model=virtio" % nic.mac
326
        script = self._WriteNetScript(instance, nic_seq, nic)
327
        kvm_cmd.extend(['-net', nic_val])
328
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
329
        temp_files.append(script)
330

    
331
    if incoming:
332
      target, port = incoming
333
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
334

    
335
    result = utils.RunCmd(kvm_cmd)
336
    if result.failed:
337
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
338
                                   (instance.name, result.fail_reason,
339
                                    result.output))
340

    
341
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
342
      raise errors.HypervisorError("Failed to start instance %s: %s" %
343
                                   (instance.name))
344

    
345
    for filename in temp_files:
346
      utils.RemoveFile(filename)
347

    
348
  def StartInstance(self, instance, block_devices, extra_args):
349
    """Start an instance.
350

351
    """
352
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
353
    if alive:
354
      raise errors.HypervisorError("Failed to start instance %s: %s" %
355
                                   (instance.name, "already running"))
356

    
357
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, extra_args)
358
    self._SaveKVMRuntime(instance, kvm_runtime)
359
    self._ExecuteKVMRuntime(instance, kvm_runtime)
360

    
361
  def _CallMonitorCommand(self, instance_name, command):
362
    """Invoke a command on the instance monitor.
363

364
    """
365
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
366
             (utils.ShellQuote(command),
367
              constants.SOCAT_PATH,
368
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
369
    result = utils.RunCmd(socat)
370
    if result.failed:
371
      msg = ("Failed to send command '%s' to instance %s."
372
             " output: %s, error: %s, fail_reason: %s" %
373
             (instance.name, result.stdout, result.stderr, result.fail_reason))
374
      raise errors.HypervisorError(msg)
375

    
376
    return result
377

    
378
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
379
    """Wait for an instance  to power down.
380

381
    """
382
    # Wait up to $timeout seconds
383
    end = time.time() + timeout
384
    wait = 1
385
    while time.time() < end and utils.IsProcessAlive(pid):
386
      self._CallMonitorCommand(instance.name, 'system_powerdown')
387
      time.sleep(wait)
388
      # Make wait time longer for next try
389
      if wait < 5:
390
        wait *= 1.3
391

    
392
  def StopInstance(self, instance, force=False):
393
    """Stop an instance.
394

395
    """
396
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
397
    if pid > 0 and alive:
398
      if force or not instance.hvparams[constants.HV_ACPI]:
399
        utils.KillProcess(pid)
400
      else:
401
        self._RetryInstancePowerdown(instance, pid)
402

    
403
    if not utils.IsProcessAlive(pid):
404
      utils.RemoveFile(pidfile)
405
      utils.RemoveFile(self._InstanceMonitor(instance.name))
406
      utils.RemoveFile(self._InstanceSerial(instance.name))
407
      utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
408
      return True
409
    else:
410
      return False
411

    
412
  def RebootInstance(self, instance):
413
    """Reboot an instance.
414

415
    """
416
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
417
    # socket the instance will stop, but now power up again. So we'll resort
418
    # to shutdown and restart.
419
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
420
    if not alive:
421
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
422
                                             (instance.name))
423
    # StopInstance will delete the saved KVM runtime so:
424
    # ...first load it...
425
    kvm_runtime = self._LoadKVMRuntime(instance)
426
    # ...now we can safely call StopInstance...
427
    if not self.StopInstance(instance):
428
      self.StopInstance(instance, force=True)
429
    # ...and finally we can save it again, and execute it...
430
    self._SaveKVMRuntime(instance, kvm_runtime)
431
    self._ExecuteKVMRuntime(instance, kvm_runtime)
432

    
433
  def MigrationInfo(self, instance):
434
    """Get instance information to perform a migration.
435

436
    @type instance: L{objects.Instance}
437
    @param instance: instance to be migrated
438
    @rtype: string
439
    @return: content of the KVM runtime file
440

441
    """
442
    return self._ReadKVMRuntime(instance.name)
443

    
444
  def AcceptInstance(self, instance, info, target):
445
    """Prepare to accept an instance.
446

447
    @type instance: L{objects.Instance}
448
    @param instance: instance to be accepted
449
    @type info: string
450
    @param info: content of the KVM runtime file on the source node
451
    @type target: string
452
    @param target: target host (usually ip), on this node
453

454
    """
455
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
456
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
457
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
458

    
459
  def FinalizeMigration(self, instance, info, success):
460
    """Finalize an instance migration.
461

462
    Stop the incoming mode KVM.
463

464
    @type instance: L{objects.Instance}
465
    @param instance: instance whose migration is being aborted
466

467
    """
468
    if success:
469
      self._WriteKVMRuntime(instance.name, info)
470
    else:
471
      self.StopInstance(instance, force=True)
472

    
473
  def MigrateInstance(self, instance_name, target, live):
474
    """Migrate an instance to a target node.
475

476
    The migration will not be attempted if the instance is not
477
    currently running.
478

479
    @type instance_name: string
480
    @param instance_name: name of the instance to be migrated
481
    @type target: string
482
    @param target: ip address of the target node
483
    @type live: boolean
484
    @param live: perform a live migration
485

486
    """
487
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
488
    if not alive:
489
      raise errors.HypervisorError("Instance not running, cannot migrate")
490

    
491
    if not live:
492
      self._CallMonitorCommand(instance_name, 'stop')
493

    
494
    migrate_command = ('migrate -d tcp:%s:%s' %
495
                       (target, constants.KVM_MIGRATION_PORT))
496
    self._CallMonitorCommand(instance_name, migrate_command)
497

    
498
    info_command = 'info migrate'
499
    done = False
500
    while not done:
501
      result = self._CallMonitorCommand(instance_name, info_command)
502
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
503
      if not match:
504
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
505
                                     result.stdout)
506
      else:
507
        status = match.group(1)
508
        if status == 'completed':
509
          done = True
510
        elif status == 'active':
511
          time.sleep(2)
512
        elif status == 'failed' or status == 'cancelled':
513
          if not live:
514
            self._CallMonitorCommand(instance_name, 'cont')
515
          raise errors.HypervisorError("Migration %s at the kvm level" %
516
                                       status)
517
        else:
518
          logging.info("KVM: unknown migration status '%s'" % status)
519
          time.sleep(2)
520

    
521
    utils.KillProcess(pid)
522
    utils.RemoveFile(pidfile)
523
    utils.RemoveFile(self._InstanceMonitor(instance_name))
524
    utils.RemoveFile(self._InstanceSerial(instance_name))
525
    utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
526

    
527
  def GetNodeInfo(self):
528
    """Return information about the node.
529

530
    @return: a dict with the following keys (values in MiB):
531
          - memory_total: the total memory size on the node
532
          - memory_free: the available memory on the node for instances
533
          - memory_dom0: the memory used by the node itself, if available
534

535
    """
536
    # global ram usage from the xm info command
537
    # memory                 : 3583
538
    # free_memory            : 747
539
    # note: in xen 3, memory has changed to total_memory
540
    try:
541
      fh = file("/proc/meminfo")
542
      try:
543
        data = fh.readlines()
544
      finally:
545
        fh.close()
546
    except EnvironmentError, err:
547
      raise errors.HypervisorError("Failed to list node info: %s" % err)
548

    
549
    result = {}
550
    sum_free = 0
551
    for line in data:
552
      splitfields = line.split(":", 1)
553

    
554
      if len(splitfields) > 1:
555
        key = splitfields[0].strip()
556
        val = splitfields[1].strip()
557
        if key == 'MemTotal':
558
          result['memory_total'] = int(val.split()[0])/1024
559
        elif key in ('MemFree', 'Buffers', 'Cached'):
560
          sum_free += int(val.split()[0])/1024
561
        elif key == 'Active':
562
          result['memory_dom0'] = int(val.split()[0])/1024
563
    result['memory_free'] = sum_free
564

    
565
    cpu_total = 0
566
    try:
567
      fh = open("/proc/cpuinfo")
568
      try:
569
        cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
570
                                   fh.read()))
571
      finally:
572
        fh.close()
573
    except EnvironmentError, err:
574
      raise errors.HypervisorError("Failed to list node info: %s" % err)
575
    result['cpu_total'] = cpu_total
576

    
577
    return result
578

    
579
  @classmethod
580
  def GetShellCommandForConsole(cls, instance):
581
    """Return a command for connecting to the console of an instance.
582

583
    """
584
    # FIXME: The socat shell is not perfect. In particular the way we start
585
    # it ctrl+c will close it, rather than being passed to the other end.
586
    # On the other hand if we pass the option 'raw' (or ignbrk=1) there
587
    # will be no way of exiting socat (except killing it from another shell)
588
    # and ctrl+c doesn't work anyway, printing ^C rather than being
589
    # interpreted by kvm. For now we'll leave it this way, which at least
590
    # allows a minimal interaction and changes on the machine.
591
    socat_shell = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
592
                   (constants.SOCAT_PATH,
593
                    utils.ShellQuote(cls._InstanceSerial(instance.name))))
594

    
595
    return socat_shell
596

    
597
  def Verify(self):
598
    """Verify the hypervisor.
599

600
    Check that the binary exists.
601

602
    """
603
    if not os.path.exists(constants.KVM_PATH):
604
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
605
    if not os.path.exists(constants.SOCAT_PATH):
606
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
607

    
608

    
609
  @classmethod
610
  def CheckParameterSyntax(cls, hvparams):
611
    """Check the given parameters for validity.
612

613
    For the KVM hypervisor, this only check the existence of the
614
    kernel.
615

616
    @type hvparams:  dict
617
    @param hvparams: dictionary with parameter names/value
618
    @raise errors.HypervisorError: when a parameter is not valid
619

620
    """
621
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
622

    
623
    if not hvparams[constants.HV_KERNEL_PATH]:
624
      raise errors.HypervisorError("Need a kernel for the instance")
625

    
626
    if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
627
      raise errors.HypervisorError("The kernel path must be an absolute path")
628

    
629
    if hvparams[constants.HV_INITRD_PATH]:
630
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
631
        raise errors.HypervisorError("The initrd path must be an absolute path"
632
                                     ", if defined")
633

    
634
  def ValidateParameters(self, hvparams):
635
    """Check the given parameters for validity.
636

637
    For the KVM hypervisor, this checks the existence of the
638
    kernel.
639

640
    """
641
    super(KVMHypervisor, self).ValidateParameters(hvparams)
642

    
643
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
644
    if not os.path.isfile(kernel_path):
645
      raise errors.HypervisorError("Instance kernel '%s' not found or"
646
                                   " not a file" % kernel_path)
647
    initrd_path = hvparams[constants.HV_INITRD_PATH]
648
    if initrd_path and not os.path.isfile(initrd_path):
649
      raise errors.HypervisorError("Instance initrd '%s' not found or"
650
                                   " not a file" % initrd_path)