Statistics
| Branch: | Tag: | Revision:

root / tools / lvmstrap @ 8b3fd458

History | View | Annotate | Download (23.3 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
48
from ganeti import constants
49

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

    
54
verbose_flag = False
55

    
56

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

    
61

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

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

    
70

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

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

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

    
83

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

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

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

    
96

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

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

    
104

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

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

    
113

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

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

    
121

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

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

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

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

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

    
150

    
151
  options, args = parser.parse_args()
152
  if len(args) != 1:
153
    Usage()
154

    
155
  verbose_flag = options.verbose
156

    
157
  return options, args
158

    
159

    
160
def ExecCommand(command):
161
  """Executes a command.
162

    
163
  This is just a wrapper around commands.getstatusoutput, with the
164
  difference that if the command line argument -v has been given, it
165
  will print the command line and the command output on stdout.
166

    
167
  Args:
168
    the command line
169
  Returns:
170
    (status, output) where status is the exit status and output the
171
      stdout and stderr of the command together
172
  """
173

    
174
  if verbose_flag:
175
    print command
176
  result = RunCmd(command)
177
  if verbose_flag:
178
    print result.output
179
  return result
180

    
181

    
182
def CheckPrereq():
183
  """Check the prerequisites of this program.
184

    
185
  It check that it runs on Linux 2.6, and that /sys is mounted and the
186
  fact that /sys/block is a directory.
187
  """
188

    
189
  if os.getuid() != 0:
190
    raise PrereqError("This tool runs as root only. Really.")
191

    
192
  osname, nodename, release, version, arch = os.uname()
193
  if osname != 'Linux':
194
    raise PrereqError("This tool only runs on Linux"
195
                      " (detected OS: %s)." % osname)
196

    
197
  if not release.startswith("2.6."):
198
    raise PrereqError("Wrong major kernel version (detected %s, needs"
199
                      " 2.6.*)" % release)
200

    
201
  if not os.path.ismount("/sys"):
202
    raise PrereqError("Can't find a filesystem mounted at /sys."
203
                      " Please mount /sys.")
204

    
205
  if not os.path.isdir("/sys/block"):
206
    raise SysconfigError("Can't find /sys/block directory. Has the"
207
                         " layout of /sys changed?")
208

    
209
  if not os.path.ismount("/proc"):
210
    raise PrereqError("Can't find a filesystem mounted at /proc."
211
                      " Please mount /proc.")
212

    
213
  if not os.path.exists("/proc/mounts"):
214
    raise SysconfigError("Can't find /proc/mounts")
215

    
216

    
217
def CheckVGExists(vgname):
218
  """Checks to see if a volume group exists.
219

    
220
  Args:
221
    vgname: the volume group name
222

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

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

    
244
  return not result.failed, lv_count, vg_size, vg_free
245

    
246

    
247
def CheckSysDev(name, devnum):
248
  """Checks consistency between /sys and /dev trees.
249

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

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

    
261
  Args:
262
   name: the device name, e.g. 'sda'
263
   devnum: the device number, e.g. 0x803 (2051 in decimal) for sda3
264

    
265
  Returns:
266
    None; failure of the check is signalled by raising a
267
      SysconfigError exception
268
  """
269

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

    
284

    
285
def ReadDev(syspath):
286
  """Reads the device number from a sysfs path.
287

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

    
293
  Args:
294
    syspath: the path to a block device dir in sysfs, e.g. /sys/block/sda
295

    
296
  Returns:
297
    the device number
298
  """
299

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

    
311

    
312
def ReadSize(syspath):
313
  """Reads the size from a sysfs path.
314

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

    
320
  Args:
321
    syspath: the path to a block device dir in sysfs, e.g. /sys/block/sda
322

    
323
  Returns:
324
    the device size in bytes
325
  """
326

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

    
335

    
336
def ReadPV(name):
337
  """Reads physical volume information.
338

    
339
  This function tries to see if a block device is a physical volume.
340

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

    
349
  result = ExecCommand("pvdisplay -c /dev/%s" % name)
350
  if result.failed:
351
    return None
352
  vgname = result.stdout.strip().split(":")[1]
353
  return vgname
354

    
355

    
356
def GetDiskList():
357
  """Computes the block device list for this system.
358

    
359
  This function examines the /sys/block tree and using information
360
  therein, computes the status of the block device.
361

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

    
376
  dlist = []
377
  for name in os.listdir("/sys/block"):
378
    if (not name.startswith("hd") and
379
        not name.startswith("sd") and
380
        not name.startswith("ubd")):
381
      continue
382

    
383
    size = ReadSize("/sys/block/%s" % name)
384

    
385
    f = open("/sys/block/%s/removable" % name)
386
    removable = int(f.read().strip())
387
    f.close()
388

    
389
    if removable:
390
      continue
391

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

    
409

    
410
def GetMountInfo():
411
  """Reads /proc/mounts and computes the mountpoint-devnum mapping.
412

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

    
419
  Returns:
420
   a mountpoint: device number dictionary
421
  """
422

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

    
446

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

    
450
  Args:
451
    name: the device name, e.g. sda
452

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

    
460
  if dev in mountinfo:
461
    mpath = mountinfo[dev]
462
  else:
463
    mpath = None
464

    
465
  whatvg = ReadPV(name)
466

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

    
473

    
474
def ShowDiskInfo():
475
  """Shows a nicely formatted block device list for this system.
476

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

    
481
  """
482
  mounts = GetMountInfo()
483
  dlist = GetDiskList()
484

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

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

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

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

    
517

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

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

    
525
  Returns:
526
    boolean, the in-use status of the device
527
  """
528

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

    
535
  return not result.failed
536

    
537

    
538
def WipeDisk(name):
539
  """Wipes a block device.
540

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

    
545
  Args:
546
    the device name (e.g. sda)
547
  """
548

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

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

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

    
580

    
581
def PartitionDisk(name):
582
  """Partitions a disk.
583

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

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

    
600

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

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

    
607
  Args:
608
    the device name, e.g. sda
609

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

    
618

    
619
def CreateVG(vgname, disks):
620
  """Creates the volume group.
621

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

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

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

    
637

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

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

    
646
  Args:
647
    the options returned from OptParser.parse_options
648

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

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

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

    
680
  return disklist
681

    
682

    
683
def BootStrap():
684
  """Actual main routine."""
685

    
686
  CheckPrereq()
687

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

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

    
703

    
704
  disklist = ValidateDiskList(options)
705

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

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

    
721

    
722
def main():
723
  """application entry point.
724

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

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

    
770

    
771
if __name__ == "__main__":
772
  main()