Statistics
| Branch: | Tag: | Revision:

root / tools / lvmstrap @ 3374afa9

History | View | Annotate | Download (23.6 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007 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
"""Program which configures LVM on the Ganeti nodes.
23

    
24
This program wipes disks and creates a volume group on top of them. It
25
can also show disk information to help you decide which disks you want
26
to wipe.
27

    
28
The error handling is done by raising our own exceptions from most of
29
the functions; these exceptions then handled globally in the main()
30
function. The exceptions that each function can raise are not
31
documented individually, since almost every error path ends in a
32
raise.
33

    
34
Another two exceptions that are handled globally are IOError and
35
OSError. The idea behind this is, since we run as root, we should
36
usually not get these errors, but if we do it's most probably a system
37
error, so they should be handled and the user instructed to report
38
them.
39
"""
40

    
41

    
42
import os
43
import sys
44
import optparse
45
import time
46

    
47
from ganeti.utils import RunCmd, ReadFile
48
from ganeti import constants
49

    
50
USAGE = ("\tlvmstrap diskinfo\n"
51
         "\tlvmstrap [--vgname=NAME] [--allow-removable]"
52
         " { --alldisks | --disks DISKLIST }"
53
         " create")
54

    
55
verbose_flag = False
56

    
57

    
58
class Error(Exception):
59
  """Generic exception"""
60
  pass
61

    
62

    
63
class ProgrammingError(Error):
64
  """Exception denoting invalid assumptions in programming.
65

    
66
  This should catch sysfs tree changes, or otherwise incorrect
67
  assumptions about the contents of the /sys/block/... directories.
68
  """
69
  pass
70

    
71

    
72
class SysconfigError(Error):
73
  """Exception denoting invalid system configuration.
74

    
75
  If the system configuration is somehow wrong (e.g. /dev files
76
  missing, or having mismatched major/minor numbers relative to
77
  /sys/block devices), this exception will be raised.
78

    
79
  This should usually mean that the installation of the Xen node
80
  failed in some steps.
81
  """
82
  pass
83

    
84

    
85
class PrereqError(Error):
86
  """Exception denoting invalid prerequisites.
87

    
88
  If the node does not meet the requirements for cluster membership, this
89
  exception will be raised. Things like wrong kernel version, or no
90
  free disks, etc. belong here.
91

    
92
  This should usually mean that the build steps for the Xen node were
93
  not followed correctly.
94
  """
95
  pass
96

    
97

    
98
class OperationalError(Error):
99
  """Exception denoting actual errors.
100

    
101
  Errors during the bootstrapping are signaled using this exception.
102
  """
103
  pass
104

    
105

    
106
class ParameterError(Error):
107
  """Exception denoting invalid input from user.
108

    
109
  Wrong disks given as parameters will be signaled using this
110
  exception.
111
  """
112
  pass
113

    
114

    
115
def Usage():
116
  """Shows program usage information and exits the program."""
117

    
118
  print >> sys.stderr, "Usage:"
119
  print >> sys.stderr, USAGE
120
  sys.exit(2)
121

    
122

    
123
def ParseOptions():
124
  """Parses the command line options.
125

    
126
  In case of command line errors, it will show the usage and exit the
127
  program.
128

    
129
  Returns:
130
    (options, args), as returned by OptionParser.parse_args
131
  """
132
  global verbose_flag
133

    
134
  parser = optparse.OptionParser(usage="\n%s" % USAGE,
135
                                 version="%%prog (ganeti) %s" %
136
                                 constants.RELEASE_VERSION)
137

    
138
  parser.add_option("--alldisks", dest="alldisks",
139
                    help="erase ALL disks", action="store_true",
140
                    default=False)
141
  parser.add_option("-d", "--disks", dest="disks",
142
                    help="Choose disks (e.g. hda,hdg)",
143
                    metavar="DISKLIST")
144
  parser.add_option("-v", "--verbose",
145
                    action="store_true", dest="verbose", default=False,
146
                    help="print command execution messages to stdout")
147
  parser.add_option("-r", "--allow-removable",
148
                    action="store_true", dest="removable_ok", default=False,
149
                    help="allow and use removable devices too")
150
  parser.add_option("-g", "--vg-name", type="string",
151
                    dest="vgname", default="xenvg", metavar="NAME",
152
                    help="the volume group to be created [default: xenvg]")
153

    
154

    
155
  options, args = parser.parse_args()
156
  if len(args) != 1:
157
    Usage()
158

    
159
  verbose_flag = options.verbose
160

    
161
  return options, args
162

    
163

    
164
def ExecCommand(command):
165
  """Executes a command.
166

    
167
  This is just a wrapper around commands.getstatusoutput, with the
168
  difference that if the command line argument -v has been given, it
169
  will print the command line and the command output on stdout.
170

    
171
  Args:
172
    the command line
173
  Returns:
174
    (status, output) where status is the exit status and output the
175
      stdout and stderr of the command together
176
  """
177

    
178
  if verbose_flag:
179
    print command
180
  result = RunCmd(command)
181
  if verbose_flag:
182
    print result.output
183
  return result
184

    
185

    
186
def CheckPrereq():
187
  """Check the prerequisites of this program.
188

    
189
  It check that it runs on Linux 2.6, and that /sys is mounted and the
190
  fact that /sys/block is a directory.
191
  """
192

    
193
  if os.getuid() != 0:
194
    raise PrereqError("This tool runs as root only. Really.")
195

    
196
  osname, nodename, release, version, arch = os.uname()
197
  if osname != 'Linux':
198
    raise PrereqError("This tool only runs on Linux"
199
                      " (detected OS: %s)." % osname)
200

    
201
  if not release.startswith("2.6."):
202
    raise PrereqError("Wrong major kernel version (detected %s, needs"
203
                      " 2.6.*)" % release)
204

    
205
  if not os.path.ismount("/sys"):
206
    raise PrereqError("Can't find a filesystem mounted at /sys."
207
                      " Please mount /sys.")
208

    
209
  if not os.path.isdir("/sys/block"):
210
    raise SysconfigError("Can't find /sys/block directory. Has the"
211
                         " layout of /sys changed?")
212

    
213
  if not os.path.ismount("/proc"):
214
    raise PrereqError("Can't find a filesystem mounted at /proc."
215
                      " Please mount /proc.")
216

    
217
  if not os.path.exists("/proc/mounts"):
218
    raise SysconfigError("Can't find /proc/mounts")
219

    
220

    
221
def CheckVGExists(vgname):
222
  """Checks to see if a volume group exists.
223

    
224
  Args:
225
    vgname: the volume group name
226

    
227
  Returns:
228
    a four-tuple (exists, lv_count, vg_size, vg_free), where:
229
      exists: True if the volume exists, otherwise False; if False,
230
        all other members of the tuple are None
231
      lv_count: The number of logical volumes in the volume group
232
      vg_size: The total size of the volume group (in gibibytes)
233
      vg_free: The available space in the volume group
234
  """
235

    
236
  result = ExecCommand("vgs --nohead -o lv_count,vg_size,vg_free"
237
                       " --nosuffix --units g"
238
                       " --ignorelockingfailure %s" % vgname)
239
  if not result.failed:
240
    try:
241
      lv_count, vg_size, vg_free = result.stdout.strip().split()
242
    except ValueError:
243
      # This means the output of vgdisplay can't be parsed
244
      raise PrereqError("cannot parse output of vgs (%s)" % result.stdout)
245
  else:
246
    lv_count = vg_size = vg_free = None
247

    
248
  return not result.failed, lv_count, vg_size, vg_free
249

    
250

    
251
def CheckSysDev(name, devnum):
252
  """Checks consistency between /sys and /dev trees.
253

    
254
  In /sys/block/<name>/dev and /sys/block/<name>/<part>/dev are the
255
  kernel-known device numbers. The /dev/<name> block/char devices are
256
  created by userspace and thus could differ from the kernel
257
  view. This function checks the consistency between the device number
258
  read from /sys and the actual device number in /dev.
259

    
260
  Note that since the system could be using udev which removes and
261
  recreates the device nodes on partition table rescan, we need to do
262
  some retries here. Since we only do a stat, we can afford to do many
263
  short retries.
264

    
265
  Args:
266
   name: the device name, e.g. 'sda'
267
   devnum: the device number, e.g. 0x803 (2051 in decimal) for sda3
268

    
269
  Returns:
270
    None; failure of the check is signalled by raising a
271
      SysconfigError exception
272
  """
273

    
274
  path = "/dev/%s" % name
275
  for retries in range(40):
276
    if os.path.exists(path):
277
      break
278
    time.sleep(0.250)
279
  else:
280
    raise SysconfigError("the device file %s does not exist, but the block"
281
                         " device exists in the /sys/block tree" % path)
282
  rdev = os.stat(path).st_rdev
283
  if devnum != rdev:
284
    raise SysconfigError("For device %s, the major:minor in /dev is %04x"
285
                         " while the major:minor in sysfs is %s" %
286
                         (path, rdev, devnum))
287

    
288

    
289
def ReadDev(syspath):
290
  """Reads the device number from a sysfs path.
291

    
292
  The device number is given in sysfs under a block device directory
293
  in a file named 'dev' which contains major:minor (in ASCII). This
294
  function reads that file and converts the major:minor pair to a dev
295
  number.
296

    
297
  Args:
298
    syspath: the path to a block device dir in sysfs, e.g. /sys/block/sda
299

    
300
  Returns:
301
    the device number
302
  """
303

    
304
  if not os.path.exists("%s/dev" % syspath):
305
    raise ProgrammingError("Invalid path passed to ReadDev: %s" % syspath)
306
  f = open("%s/dev" % syspath)
307
  data = f.read().strip()
308
  f.close()
309
  major, minor = data.split(":", 1)
310
  major = int(major)
311
  minor = int(minor)
312
  dev = os.makedev(major, minor)
313
  return dev
314

    
315

    
316
def ReadSize(syspath):
317
  """Reads the size from a sysfs path.
318

    
319
  The size is given in sysfs under a block device directory in a file
320
  named 'size' which contains the number of sectors (in ASCII). This
321
  function reads that file and converts the number in sectors to the
322
  size in bytes.
323

    
324
  Args:
325
    syspath: the path to a block device dir in sysfs, e.g. /sys/block/sda
326

    
327
  Returns:
328
    the device size in bytes
329
  """
330

    
331
  if not os.path.exists("%s/size" % syspath):
332
    raise ProgrammingError("Invalid path passed to ReadSize: %s" % syspath)
333
  f = open("%s/size" % syspath)
334
  data = f.read().strip()
335
  f.close()
336
  size = 512L * int(data)
337
  return size
338

    
339

    
340
def ReadPV(name):
341
  """Reads physical volume information.
342

    
343
  This function tries to see if a block device is a physical volume.
344

    
345
  Args:
346
    dev: the device name (e.g. sda)
347
  Returns:
348
    The name of the volume group to which this PV belongs, or
349
    "" if this PV is not in use, or
350
    None if this is not a PV
351
  """
352

    
353
  result = ExecCommand("pvdisplay -c /dev/%s" % name)
354
  if result.failed:
355
    return None
356
  vgname = result.stdout.strip().split(":")[1]
357
  return vgname
358

    
359

    
360
def GetDiskList(opts):
361
  """Computes the block device list for this system.
362

    
363
  This function examines the /sys/block tree and using information
364
  therein, computes the status of the block device.
365

    
366
  Returns:
367
    [(name, size, dev, partitions, inuse), ...]
368
  where:
369
    name is the block device name (e.g. sda)
370
    size the size in bytes
371
    dev  the device number (e.g. 8704 for hdg)
372
    partitions is [(name, size, dev), ...] mirroring the disk list data
373
    inuse is a boolean showing the in-use status of the disk, computed as the
374
      possibility of re-reading the partition table (the meaning of the
375
      operation varies with the kernel version, but is usually accurate;
376
      a mounted disk/partition or swap-area or PV with active LVs on it
377
      is busy)
378
  """
379

    
380
  dlist = []
381
  for name in os.listdir("/sys/block"):
382
    if (not name.startswith("hd") and
383
        not name.startswith("sd") and
384
        not name.startswith("ubd")):
385
      continue
386

    
387
    size = ReadSize("/sys/block/%s" % name)
388

    
389
    f = open("/sys/block/%s/removable" % name)
390
    removable = int(f.read().strip())
391
    f.close()
392

    
393
    if removable and not opts.removable_ok:
394
      continue
395

    
396
    dev = ReadDev("/sys/block/%s" % name)
397
    CheckSysDev(name, dev)
398
    inuse = not CheckReread(name)
399
    # Enumerate partitions of the block device
400
    partitions = []
401
    for partname in os.listdir("/sys/block/%s" % name):
402
      if not partname.startswith(name):
403
        continue
404
      partdev = ReadDev("/sys/block/%s/%s" % (name, partname))
405
      partsize = ReadSize("/sys/block/%s/%s" % (name, partname))
406
      CheckSysDev(partname, partdev)
407
      partitions.append((partname, partsize, partdev))
408
    partitions.sort()
409
    dlist.append((name, size, dev, partitions, inuse))
410
  dlist.sort()
411
  return dlist
412

    
413

    
414
def GetMountInfo():
415
  """Reads /proc/mounts and computes the mountpoint-devnum mapping.
416

    
417
  This function reads /proc/mounts, finds the mounted filesystems
418
  (excepting a hard-coded blacklist of network and virtual
419
  filesystems) and does a stat on these mountpoints. The st_dev number
420
  of the results is memorised for later matching against the
421
  /sys/block devices.
422

    
423
  Returns:
424
   a mountpoint: device number dictionary
425
  """
426

    
427
  mountlines = ReadFile("/proc/mounts").splitlines()
428
  mounts = {}
429
  for line in mountlines:
430
    device, mountpoint, fstype, rest = line.split(None, 3)
431
    # fs type blacklist
432
    if fstype in ["nfs", "nfs4", "autofs", "tmpfs", "proc", "sysfs"]:
433
      continue
434
    try:
435
      dev = os.stat(mountpoint).st_dev
436
    except OSError, err:
437
      # this should be a fairly rare error, since we are blacklisting
438
      # network filesystems; with this in mind, we'll ignore it,
439
      # since the rereadpt check catches in-use filesystems,
440
      # and this is used for disk information only
441
      print >> sys.stderr, ("Can't stat mountpoint '%s': %s" %
442
                            (mountpoint, err))
443
      print >> sys.stderr, "Ignoring."
444
      continue
445
    mounts[dev] = mountpoint
446
  return mounts
447

    
448

    
449
def DevInfo(name, dev, mountinfo):
450
  """Computes miscellaneous informations about a block device.
451

    
452
  Args:
453
    name: the device name, e.g. sda
454

    
455
  Returns:
456
    (mpath, whatvg, fileinfo), where
457
    mpath is the mount path where this device is mounted or None
458
    whatvg is the result of the ReadPV function
459
    fileinfo is the output of file -bs on the device
460
  """
461

    
462
  if dev in mountinfo:
463
    mpath = mountinfo[dev]
464
  else:
465
    mpath = None
466

    
467
  whatvg = ReadPV(name)
468

    
469
  result = ExecCommand("file -bs /dev/%s" % name)
470
  if result.failed:
471
    fileinfo = "<error: %s>" % result.stderr
472
  fileinfo = result.stdout[:45]
473
  return mpath, whatvg, fileinfo
474

    
475

    
476
def ShowDiskInfo(opts):
477
  """Shows a nicely formatted block device list for this system.
478

    
479
  This function shows the user a table with the informations gathered
480
  by the other functions defined, in order to help the user make a
481
  choice about which disks should be allocated to our volume group.
482

    
483
  """
484
  mounts = GetMountInfo()
485
  dlist = GetDiskList(opts)
486

    
487
  print "------- Disk information -------"
488
  print ("%5s %7s %4s %5s %-10s %s" %
489
         ("Name", "Size[M]", "Used", "Mount", "LVM?", "Info"))
490

    
491
  flatlist = []
492
  # Flatten the [(disk, [partition,...]), ...] list
493
  for name, size, dev, parts, inuse in dlist:
494
    if inuse:
495
      str_inuse = "yes"
496
    else:
497
      str_inuse = "no"
498
    flatlist.append((name, size, dev, str_inuse))
499
    for partname, partsize, partdev in parts:
500
      flatlist.append((partname, partsize, partdev, ""))
501

    
502
  for name, size, dev, in_use in flatlist:
503
    mp, vgname, fileinfo = DevInfo(name, dev, mounts)
504
    if mp is None:
505
      mp = "-"
506
    if vgname is None:
507
      lvminfo = "-"
508
    elif vgname == "":
509
      lvminfo = "yes,free"
510
    else:
511
      lvminfo = "in %s" % vgname
512

    
513
    if len(name) > 3:
514
      # Indent partitions
515
      name = " %s" % name
516
    print ("%-5s %7.2f %-4s %-5s %-10s %s" %
517
           (name, float(size) / 1024 / 1024, in_use, mp, lvminfo, fileinfo))
518

    
519

    
520
def CheckReread(name):
521
  """Check to see if a block device is in use.
522

    
523
  Uses blockdev to reread the partition table of a block device, and
524
  thus compute the in-use status. See the discussion in GetDiskList
525
  about the meaning of 'in use'.
526

    
527
  Returns:
528
    boolean, the in-use status of the device
529
  """
530

    
531
  for retries in range(3):
532
    result = ExecCommand("blockdev --rereadpt /dev/%s" % name)
533
    if not result.failed:
534
      break
535
    time.sleep(2)
536

    
537
  return not result.failed
538

    
539

    
540
def WipeDisk(name):
541
  """Wipes a block device.
542

    
543
  This function wipes a block device, by clearing and re-reading the
544
  partition table. If not successful, it writes back the old partition
545
  data, and leaves the cleanup to the user.
546

    
547
  Args:
548
    the device name (e.g. sda)
549
  """
550

    
551
  if not CheckReread(name):
552
    raise OperationalError("CRITICAL: disk %s you selected seems to be in"
553
                           " use. ABORTING!" % name)
554

    
555
  fd = os.open("/dev/%s" % name, os.O_RDWR | os.O_SYNC)
556
  olddata = os.read(fd, 512)
557
  if len(olddata) != 512:
558
    raise OperationalError("CRITICAL: Can't read partition table information"
559
                           " from /dev/%s (needed 512 bytes, got %d" %
560
                           (name, len(olddata)))
561
  newdata = "\0" * 512
562
  os.lseek(fd, 0, 0)
563
  bytes_written = os.write(fd, newdata)
564
  os.close(fd)
565
  if bytes_written != 512:
566
    raise OperationalError("CRITICAL: Can't write partition table information"
567
                           " to /dev/%s (tried to write 512 bytes, written"
568
                           " %d. I don't know how to cleanup. Sorry." %
569
                           (name, bytes_written))
570

    
571
  if not CheckReread(name):
572
    fd = os.open("/dev/%s" % name, os.O_RDWR | os.O_SYNC)
573
    os.write(fd, olddata)
574
    os.close(fd)
575
    raise OperationalError("CRITICAL: disk %s which I have just wiped cannot"
576
                           " reread partition table. Most likely, it is"
577
                           " in use. You have to clean after this yourself."
578
                           " I tried to restore the old partition table,"
579
                           " but I cannot guarantee nothing has broken." %
580
                           name)
581

    
582

    
583
def PartitionDisk(name):
584
  """Partitions a disk.
585

    
586
  This function creates a single partition spanning the entire disk,
587
  by means of fdisk.
588

    
589
  Args:
590
    the device name, e.g. sda
591
  """
592
  result = ExecCommand(
593
    'echo ,,8e, | sfdisk /dev/%s' % name)
594
  if result.failed:
595
    raise OperationalError("CRITICAL: disk %s which I have just partitioned"
596
                           " cannot reread its partition table, or there"
597
                           " is some other sfdisk error. Likely, it is in"
598
                           " use. You have to clean this yourself. Error"
599
                           " message from sfdisk: %s" %
600
                           (name, result.output))
601

    
602

    
603
def CreatePVOnDisk(name):
604
  """Creates a physical volume on a block device.
605

    
606
  This function creates a physical volume on a block device, overriding
607
  all warnings. So it can wipe existing PVs and PVs which are in a VG.
608

    
609
  Args:
610
    the device name, e.g. sda
611

    
612
  """
613
  result = ExecCommand("pvcreate -yff /dev/%s1 " % name)
614
  if result.failed:
615
    raise OperationalError("I cannot create a physical volume on"
616
                           " partition /dev/%s1. Error message: %s."
617
                           " Please clean up yourself." %
618
                           (name, result.output))
619

    
620

    
621
def CreateVG(vgname, disks):
622
  """Creates the volume group.
623

    
624
  This function creates a volume group named `vgname` on the disks
625
  given as parameters. The physical extent size is set to 64MB.
626

    
627
  Args:
628
    disks: a list of disk names, e.g. ['sda','sdb']
629

    
630
  """
631
  pnames = ["'/dev/%s1'" % disk for disk in disks]
632
  result = ExecCommand("vgcreate -s 64MB '%s' %s" % (vgname, " ".join(pnames)))
633
  if result.failed:
634
    raise OperationalError("I cannot create the volume group %s from"
635
                           " disks %s. Error message: %s. Please clean up"
636
                           " yourself." %
637
                           (vgname, " ".join(disks), result.output))
638

    
639

    
640
def ValidateDiskList(options):
641
  """Validates or computes the disk list for create.
642

    
643
  This function either computes the available disk list (if the user
644
  gave --alldisks option), or validates the user-given disk list (by
645
  using the --disks option) such that all given disks are present and
646
  not in use.
647

    
648
  Args:
649
    the options returned from OptParser.parse_options
650

    
651
  Returns:
652
    a list of disk names, e.g. ['sda', 'sdb']
653
  """
654

    
655
  sysdisks = GetDiskList(options)
656
  if not sysdisks:
657
    raise PrereqError("no disks found (I looked for"
658
                      " non-removable block devices).")
659
  sysd_free = []
660
  sysd_used = []
661
  for name, size, dev, part, used in sysdisks:
662
    if used:
663
      sysd_used.append(name)
664
    else:
665
      sysd_free.append(name)
666

    
667
  if not sysd_free:
668
    raise PrereqError("no free disks found! (%d in-use disks)" %
669
                      len(sysd_used))
670
  if options.alldisks:
671
    disklist = sysd_free
672
  elif options.disks:
673
    disklist = options.disks.split(",")
674
    for name in disklist:
675
      if name in sysd_used:
676
        raise ParameterError("disk %s is in use, cannot wipe!" % name)
677
      if name not in sysd_free:
678
        raise ParameterError("cannot find disk %s!" % name)
679
  else:
680
    raise ParameterError("Please use either --alldisks or --disks!")
681

    
682
  return disklist
683

    
684

    
685
def BootStrap():
686
  """Actual main routine."""
687

    
688
  CheckPrereq()
689

    
690
  options, args = ParseOptions()
691
  vgname = options.vgname
692
  command = args.pop(0)
693
  if command == "diskinfo":
694
    ShowDiskInfo(options)
695
    return
696
  if command != "create":
697
    Usage()
698

    
699
  exists, lv_count, vg_size, vg_free = CheckVGExists(vgname)
700
  if exists:
701
    raise PrereqError("It seems volume group '%s' already exists:\n"
702
                      "  LV count: %s, size: %s, free: %s." %
703
                      (vgname, lv_count, vg_size, vg_free))
704

    
705

    
706
  disklist = ValidateDiskList(options)
707

    
708
  for disk in disklist:
709
    WipeDisk(disk)
710
    PartitionDisk(disk)
711
  for disk in disklist:
712
    CreatePVOnDisk(disk)
713
  CreateVG(vgname, disklist)
714

    
715
  status, lv_count, size, free = CheckVGExists(vgname)
716
  if status:
717
    print "Done! %s: size %s GiB, disks: %s" % (vgname, size,
718
                                              ",".join(disklist))
719
  else:
720
    raise OperationalError("Although everything seemed ok, the volume"
721
                           " group did not get created.")
722

    
723

    
724
def main():
725
  """application entry point.
726

    
727
  This is just a wrapper over BootStrap, to handle our own exceptions.
728
  """
729

    
730
  try:
731
    BootStrap()
732
  except PrereqError, err:
733
    print >> sys.stderr, "The prerequisites for running this tool are not met."
734
    print >> sys.stderr, ("Please make sure you followed all the steps in"
735
                          " the build document.")
736
    print >> sys.stderr, "Description: %s" % str(err)
737
    sys.exit(1)
738
  except SysconfigError, err:
739
    print >> sys.stderr, ("This system's configuration seems wrong, at"
740
                          " least is not what I expect.")
741
    print >> sys.stderr, ("Please check that the installation didn't fail"
742
                          " at some step.")
743
    print >> sys.stderr, "Description: %s" % str(err)
744
    sys.exit(1)
745
  except ParameterError, err:
746
    print >> sys.stderr, ("Some parameters you gave to the program or the"
747
                          " invocation is wrong. ")
748
    print >> sys.stderr, "Description: %s" % str(err)
749
    Usage()
750
  except OperationalError, err:
751
    print >> sys.stderr, ("A serious error has happened while modifying"
752
                          " the system's configuration.")
753
    print >> sys.stderr, ("Please review the error message below and make"
754
                          " sure you clean up yourself.")
755
    print >> sys.stderr, ("It is most likely that the system configuration"
756
                          " has been partially altered.")
757
    print >> sys.stderr, str(err)
758
    sys.exit(1)
759
  except ProgrammingError, err:
760
    print >> sys.stderr, ("Internal application error. Please signal this"
761
                          " to xencluster-team.")
762
    print >> sys.stderr, "Error description: %s" % str(err)
763
    sys.exit(1)
764
  except Error, err:
765
    print >> sys.stderr, "Unhandled application error: %s" % err
766
    sys.exit(1)
767
  except (IOError, OSError), err:
768
    print >> sys.stderr, "I/O error detected, please report."
769
    print >> sys.stderr, "Description: %s" % str(err)
770
    sys.exit(1)
771

    
772

    
773
if __name__ == "__main__":
774
  main()