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
"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):
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.
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)
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)