#!/usr/bin/python
#
-# Copyright (C) 2006, 2007, 2011 Google Inc.
+# Copyright (C) 2006, 2007, 2011, 2012 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
import optparse
import time
import errno
+import re
from ganeti.utils import RunCmd, ReadFile
from ganeti import constants
from ganeti import compat
USAGE = ("\tlvmstrap diskinfo\n"
- "\tlvmstrap [--vgname=NAME] [--allow-removable]"
- " { --alldisks | --disks DISKLIST }"
+ "\tlvmstrap [--vg-name=NAME] [--allow-removable]"
+ " { --alldisks | --disks DISKLIST } [--use-sfdisk]"
" create")
verbose_flag = False
]
#: Excluded filesystem types
-EXCLUDED_FS = frozenset([
+EXCLUDED_FS = compat.UniqueFrozenset([
"nfs",
"nfs4",
"autofs",
"devpts",
])
+#: A regular expression that matches partitions (must be kept in sync
+# with L{SUPPORTED_TYPES}
+PART_RE = re.compile("^((?:h|s|m|ub)d[a-z]{1,2})[0-9]+$")
+
#: Minimum partition size to be considered (1 GB)
PART_MINSIZE = 1024 * 1024 * 1024
+MBR_MAX_SIZE = 2 * (10 ** 12)
class Error(Exception):
OptionParser.parse_args
"""
- global verbose_flag # pylint: disable-msg=W0603
+ global verbose_flag # pylint: disable=W0603
parser = optparse.OptionParser(usage="\n%s" % USAGE,
version="%%prog (ganeti) %s" %
parser.add_option("-g", "--vg-name", type="string",
dest="vgname", default="xenvg", metavar="NAME",
help="the volume group to be created [default: xenvg]")
-
+ parser.add_option("--use-sfdisk", dest="use_sfdisk",
+ action="store_true", default=False,
+ help="use sfdisk instead of parted")
options, args = parser.parse_args()
if len(args) != 1:
Currently only md devices are used as is.
"""
- return not disk.startswith('md')
+ return not (disk.startswith("md") or PART_RE.match(disk))
def DeviceName(disk):
"""
if IsPartitioned(disk):
- device = '/dev/%s1' % disk
+ device = "/dev/%s1" % disk
else:
- device = '/dev/%s' % disk
+ device = "/dev/%s" % disk
return device
+def SysfsName(disk):
+ """Returns the sysfs name for a disk or partition.
+
+ """
+ match = PART_RE.match(disk)
+ if match:
+ # this is a partition, which resides in /sys/block under a different name
+ disk = "%s/%s" % (match.group(1), disk)
+ return "/sys/block/%s" % disk
+
+
def ExecCommand(command):
"""Executes a command.
raise PrereqError("This tool runs as root only. Really.")
osname, _, release, _, _ = os.uname()
- if osname != 'Linux':
+ if osname != "Linux":
raise PrereqError("This tool only runs on Linux"
" (detected OS: %s)." % osname)
- if not release.startswith("2.6."):
+ if not (release.startswith("2.6.") or release.startswith("3.")):
raise PrereqError("Wrong major kernel version (detected %s, needs"
- " 2.6.*)" % release)
+ " 2.6.* or 3.*)" % release)
if not os.path.ismount("/sys"):
raise PrereqError("Can't find a filesystem mounted at /sys."
@param name: the device name, e.g. 'sda'
@param devnum: the device number, e.g. 0x803 (2051 in decimal) for sda3
- @raises L{SysconfigError}: in case of failure of the check
+ @raises SysconfigError: in case of failure of the check
"""
path = "/dev/%s" % name
if not compat.any([name.startswith(pfx) for pfx in SUPPORTED_TYPES]):
continue
- size = ReadSize("/sys/block/%s" % name)
+ disksysfsname = "/sys/block/%s" % name
+ size = ReadSize(disksysfsname)
f = open("/sys/block/%s/removable" % name)
removable = int(f.read().strip())
if removable and not opts.removable_ok:
continue
- dev = ReadDev("/sys/block/%s" % name)
+ dev = ReadDev(disksysfsname)
CheckSysDev(name, dev)
inuse = InUse(name)
# Enumerate partitions of the block device
partitions = []
- for partname in os.listdir("/sys/block/%s" % name):
+ for partname in os.listdir(disksysfsname):
if not partname.startswith(name):
continue
- partdev = ReadDev("/sys/block/%s/%s" % (name, partname))
- partsize = ReadSize("/sys/block/%s/%s" % (name, partname))
+ partsysfsname = "%s/%s" % (disksysfsname, partname)
+ partdev = ReadDev(partsysfsname)
+ partsize = ReadSize(partsysfsname)
if partsize >= PART_MINSIZE:
CheckSysDev(partname, partdev)
- partitions.append((partname, partsize, partdev))
+ partinuse = InUse(partname)
+ partitions.append((partname, partsize, partdev, partinuse))
partitions.sort()
dlist.append((name, size, dev, partitions, inuse))
dlist.sort()
choice about which disks should be allocated to our volume group.
"""
+ def _inuse(inuse):
+ if inuse:
+ return "yes"
+ else:
+ return "no"
+
mounts = GetMountInfo()
dlist = GetDiskList(opts)
print "------- Disk information -------"
headers = {
- "name": "Name",
- "size": "Size[M]",
- "used": "Used",
- "mount": "Mount",
- "lvm": "LVM?",
- "info": "Info"
- }
+ "name": "Name",
+ "size": "Size[M]",
+ "used": "Used",
+ "mount": "Mount",
+ "lvm": "LVM?",
+ "info": "Info",
+ }
fields = ["name", "size", "used", "mount", "lvm", "info"]
flatlist = []
# Flatten the [(disk, [partition,...]), ...] list
for name, size, dev, parts, inuse in dlist:
- if inuse:
- str_inuse = "yes"
- else:
- str_inuse = "no"
- flatlist.append((name, size, dev, str_inuse))
- for partname, partsize, partdev in parts:
- flatlist.append((partname, partsize, partdev, ""))
+ flatlist.append((name, size, dev, _inuse(inuse)))
+ for partname, partsize, partdev, partinuse in parts:
+ flatlist.append((partname, partsize, partdev, _inuse(partinuse)))
strlist = []
for name, size, dev, in_use in flatlist:
"""
try:
- contents = os.listdir("/sys/block/%s/holders/" % name)
+ contents = os.listdir("%s/holders/" % SysfsName(name))
except OSError, err:
if err.errno == errno.ENOENT:
contents = []
result = ExecCommand(cmd)
if not use_blockdev and result.failed:
break
- elif not result.failed:
+ elif use_blockdev and not result.failed:
break
time.sleep(2)
"""
minfo = GetMountInfo()
- dev = ReadDev("/sys/block/%s" % name)
+ dev = ReadDev(SysfsName(name))
return dev not in minfo
name)
-def PartitionDisk(name):
+def PartitionDisk(name, use_sfdisk):
"""Partitions a disk.
This function creates a single partition spanning the entire disk,
@param name: the device name, e.g. sda
"""
- result = ExecCommand(
- 'echo ,,8e, | sfdisk /dev/%s' % name)
+
+ # Check that parted exists
+ result = ExecCommand("parted --help")
if result.failed:
- raise OperationalError("CRITICAL: disk %s which I have just partitioned"
- " cannot reread its partition table, or there"
- " is some other sfdisk error. Likely, it is in"
- " use. You have to clean this yourself. Error"
- " message from sfdisk: %s" %
- (name, result.output))
+ use_sfdisk = True
+ print >> sys.stderr, ("Unable to execute \"parted --help\","
+ " falling back to sfdisk.")
+
+ # Check disk size - over 2TB means we need to use GPT
+ size = ReadSize("/sys/block/%s" % name)
+ if size > MBR_MAX_SIZE:
+ label_type = "gpt"
+ if use_sfdisk:
+ raise OperationalError("Critical: Disk larger than 2TB detected, but"
+ " parted is either not installed or --use-sfdisk"
+ " has been specified")
+ else:
+ label_type = "msdos"
+
+ if use_sfdisk:
+ result = ExecCommand(
+ "echo ,,8e, | sfdisk /dev/%s" % name)
+ if result.failed:
+ raise OperationalError("CRITICAL: disk %s which I have just partitioned"
+ " cannot reread its partition table, or there"
+ " is some other sfdisk error. Likely, it is in"
+ " use. You have to clean this yourself. Error"
+ " message from sfdisk: %s" %
+ (name, result.output))
+
+ else:
+ result = ExecCommand("parted -s /dev/%s mklabel %s" % (name, label_type))
+ if result.failed:
+ raise OperationalError("Critical: failed to create %s label on %s" %
+ (label_type, name))
+ result = ExecCommand("parted -s /dev/%s mkpart pri ext2 1 100%%" % name)
+ if result.failed:
+ raise OperationalError("Critical: failed to create partition on %s" %
+ name)
+ result = ExecCommand("parted -s /dev/%s set 1 lvm on" % name)
+ if result.failed:
+ raise OperationalError("Critical: failed to set partition on %s to LVM" %
+ name)
def CreatePVOnDisk(name):
" non-removable block devices).")
sysd_free = []
sysd_used = []
- for name, _, _, _, used in sysdisks:
+ for name, _, _, parts, used in sysdisks:
if used:
sysd_used.append(name)
+ for partname, _, _, partused in parts:
+ if partused:
+ sysd_used.append(partname)
+ else:
+ sysd_free.append(partname)
else:
sysd_free.append(name)
" LV count: %s, size: %s, free: %s." %
(vgname, lv_count, vg_size, vg_free))
-
disklist = ValidateDiskList(options)
for disk in disklist:
WipeDisk(disk)
if IsPartitioned(disk):
- PartitionDisk(disk)
+ PartitionDisk(disk, options.use_sfdisk)
for disk in disklist:
CreatePVOnDisk(disk)
CreateVG(vgname, disklist)
status, lv_count, size, _ = CheckVGExists(vgname)
if status:
print "Done! %s: size %s GiB, disks: %s" % (vgname, size,
- ",".join(disklist))
+ ",".join(disklist))
else:
raise OperationalError("Although everything seemed ok, the volume"
" group did not get created.")