Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 4e5a68f8

History | View | Annotate | Download (21.6 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_ROOT_PATH,
55
    constants.HV_ACPI,
56
    ]
57

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

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

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

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

    
77
    return (pidfile, pid, alive)
78

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

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

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

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

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

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

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

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

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

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

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

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

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

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

158
    @param instance_name: the instance name
159

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

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

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

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

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

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

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

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

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

    
205
    return data
206

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

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

    
223
    boot_drive = True
224
    for cfdev, dev_path in block_devices:
225
      if cfdev.mode != constants.DISK_RDWR:
226
        raise errors.HypervisorError("Instance has read-only disks which"
227
                                     " are not supported by KVM")
228
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
229
      if boot_drive:
230
        boot_val = ',boot=on'
231
        boot_drive = False
232
      else:
233
        boot_val = ''
234

    
235
      # TODO: handle different if= types
236
      if_val = ',if=virtio'
237

    
238
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
239
      kvm_cmd.extend(['-drive', drive_val])
240

    
241
    kvm_cmd.extend(['-kernel', instance.hvparams[constants.HV_KERNEL_PATH]])
242

    
243
    initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
244
    if initrd_path:
245
      kvm_cmd.extend(['-initrd', initrd_path])
246

    
247
    root_path = instance.hvparams[constants.HV_ROOT_PATH]
248
    kvm_cmd.extend(['-append', 'console=ttyS0,38400 root=%s ro' % root_path])
249

    
250
    #"hvm_boot_order",
251
    #"hvm_cdrom_image_path",
252

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

    
264
    # Save the current instance nics, but defer their expansion as parameters,
265
    # as we'll need to generate executable temp files for them.
266
    kvm_nics = instance.nics
267

    
268
    return (kvm_cmd, kvm_nics)
269

    
270
  def _WriteKVMRuntime(self, instance_name, data):
271
    """Write an instance's KVM runtime
272

273
    """
274
    try:
275
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
276
                      data=data)
277
    except EnvironmentError, err:
278
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
279

    
280
  def _ReadKVMRuntime(self, instance_name):
281
    """Read an instance's KVM runtime
282

283
    """
284
    try:
285
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
286
    except EnvironmentError, err:
287
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
288
    return file_content
289

    
290
  def _SaveKVMRuntime(self, instance, kvm_runtime):
291
    """Save an instance's KVM runtime
292

293
    """
294
    kvm_cmd, kvm_nics = kvm_runtime
295
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
296
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics))
297
    self._WriteKVMRuntime(instance.name, serialized_form)
298

    
299
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
300
    """Load an instance's KVM runtime
301

302
    """
303
    if not serialized_runtime:
304
      serialized_runtime = self._ReadKVMRuntime(instance.name)
305
    loaded_runtime = serializer.Load(serialized_runtime)
306
    kvm_cmd, serialized_nics = loaded_runtime
307
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
308
    return (kvm_cmd, kvm_nics)
309

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

313
    @type incoming: tuple of strings
314
    @param incoming: (target_host_ip, port)
315

316
    """
317
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
318
    if alive:
319
      raise errors.HypervisorError("Failed to start instance %s: %s" %
320
                                   (instance.name, "already running"))
321

    
322
    temp_files = []
323

    
324
    kvm_cmd, kvm_nics = kvm_runtime
325

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

    
336
    if incoming:
337
      target, port = incoming
338
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
339

    
340
    result = utils.RunCmd(kvm_cmd)
341
    if result.failed:
342
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
343
                                   (instance.name, result.fail_reason,
344
                                    result.output))
345

    
346
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
347
      raise errors.HypervisorError("Failed to start instance %s: %s" %
348
                                   (instance.name))
349

    
350
    for filename in temp_files:
351
      utils.RemoveFile(filename)
352

    
353
  def StartInstance(self, instance, block_devices, extra_args):
354
    """Start an instance.
355

356
    """
357
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
358
    if alive:
359
      raise errors.HypervisorError("Failed to start instance %s: %s" %
360
                                   (instance.name, "already running"))
361

    
362
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, extra_args)
363
    self._SaveKVMRuntime(instance, kvm_runtime)
364
    self._ExecuteKVMRuntime(instance, kvm_runtime)
365

    
366
  def _CallMonitorCommand(self, instance_name, command):
367
    """Invoke a command on the instance monitor.
368

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

    
381
    return result
382

    
383
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
384
    """Wait for an instance  to power down.
385

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

    
397
  def StopInstance(self, instance, force=False):
398
    """Stop an instance.
399

400
    """
401
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
402
    if pid > 0 and alive:
403
      if force or not instance.hvparams[constants.HV_ACPI]:
404
        utils.KillProcess(pid)
405
      else:
406
        self._RetryInstancePowerdown(instance, pid)
407

    
408
    if not utils.IsProcessAlive(pid):
409
      utils.RemoveFile(pidfile)
410
      utils.RemoveFile(self._InstanceMonitor(instance.name))
411
      utils.RemoveFile(self._InstanceSerial(instance.name))
412
      utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
413
      return True
414
    else:
415
      return False
416

    
417
  def RebootInstance(self, instance):
418
    """Reboot an instance.
419

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

    
438
  def MigrationInfo(self, instance):
439
    """Get instance information to perform a migration.
440

441
    @type instance: L{objects.Instance}
442
    @param instance: instance to be migrated
443
    @rtype: string
444
    @return: content of the KVM runtime file
445

446
    """
447
    return self._ReadKVMRuntime(instance.name)
448

    
449
  def AcceptInstance(self, instance, info, target):
450
    """Prepare to accept an instance.
451

452
    @type instance: L{objects.Instance}
453
    @param instance: instance to be accepted
454
    @type info: string
455
    @param info: content of the KVM runtime file on the source node
456
    @type target: string
457
    @param target: target host (usually ip), on this node
458

459
    """
460
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
461
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
462
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
463

    
464
  def FinalizeMigration(self, instance, info, success):
465
    """Finalize an instance migration.
466

467
    Stop the incoming mode KVM.
468

469
    @type instance: L{objects.Instance}
470
    @param instance: instance whose migration is being aborted
471

472
    """
473
    if success:
474
      self._WriteKVMRuntime(instance.name, info)
475
    else:
476
      self.StopInstance(instance, force=True)
477

    
478
  def MigrateInstance(self, instance_name, target, live):
479
    """Migrate an instance to a target node.
480

481
    The migration will not be attempted if the instance is not
482
    currently running.
483

484
    @type instance_name: string
485
    @param instance_name: name of the instance to be migrated
486
    @type target: string
487
    @param target: ip address of the target node
488
    @type live: boolean
489
    @param live: perform a live migration
490

491
    """
492
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
493
    if not alive:
494
      raise errors.HypervisorError("Instance not running, cannot migrate")
495

    
496
    if not live:
497
      self._CallMonitorCommand(instance_name, 'stop')
498

    
499
    migrate_command = ('migrate -d tcp:%s:%s' %
500
                       (target, constants.KVM_MIGRATION_PORT))
501
    self._CallMonitorCommand(instance_name, migrate_command)
502

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

    
526
    utils.KillProcess(pid)
527
    utils.RemoveFile(pidfile)
528
    utils.RemoveFile(self._InstanceMonitor(instance_name))
529
    utils.RemoveFile(self._InstanceSerial(instance_name))
530
    utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
531

    
532
  def GetNodeInfo(self):
533
    """Return information about the node.
534

535
    @return: a dict with the following keys (values in MiB):
536
          - memory_total: the total memory size on the node
537
          - memory_free: the available memory on the node for instances
538
          - memory_dom0: the memory used by the node itself, if available
539

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

    
554
    result = {}
555
    sum_free = 0
556
    for line in data:
557
      splitfields = line.split(":", 1)
558

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

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

    
582
    return result
583

    
584
  @classmethod
585
  def GetShellCommandForConsole(cls, instance):
586
    """Return a command for connecting to the console of an instance.
587

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

    
600
    return socat_shell
601

    
602
  def Verify(self):
603
    """Verify the hypervisor.
604

605
    Check that the binary exists.
606

607
    """
608
    if not os.path.exists(constants.KVM_PATH):
609
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
610
    if not os.path.exists(constants.SOCAT_PATH):
611
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
612

    
613

    
614
  @classmethod
615
  def CheckParameterSyntax(cls, hvparams):
616
    """Check the given parameters for validity.
617

618
    For the KVM hypervisor, this only check the existence of the
619
    kernel.
620

621
    @type hvparams:  dict
622
    @param hvparams: dictionary with parameter names/value
623
    @raise errors.HypervisorError: when a parameter is not valid
624

625
    """
626
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
627

    
628
    if not hvparams[constants.HV_KERNEL_PATH]:
629
      raise errors.HypervisorError("Need a kernel for the instance")
630

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

    
634
    if not hvparams[constants.HV_ROOT_PATH]:
635
      raise errors.HypervisorError("Need a root partition for the instance")
636

    
637
    if hvparams[constants.HV_INITRD_PATH]:
638
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
639
        raise errors.HypervisorError("The initrd path must be an absolute path"
640
                                     ", if defined")
641

    
642
  def ValidateParameters(self, hvparams):
643
    """Check the given parameters for validity.
644

645
    For the KVM hypervisor, this checks the existence of the
646
    kernel.
647

648
    """
649
    super(KVMHypervisor, self).ValidateParameters(hvparams)
650

    
651
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
652
    if not os.path.isfile(kernel_path):
653
      raise errors.HypervisorError("Instance kernel '%s' not found or"
654
                                   " not a file" % kernel_path)
655
    initrd_path = hvparams[constants.HV_INITRD_PATH]
656
    if initrd_path and not os.path.isfile(initrd_path):
657
      raise errors.HypervisorError("Instance initrd '%s' not found or"
658
                                   " not a file" % initrd_path)