Files
sunhpc/lib/sunhpc/core/partition.py
2023-10-08 20:59:00 +08:00

903 lines
25 KiB
Python

#coding:utf-8
import os
import re
import sys
import grp
import stat
import time
import syslog
import tempfile
syslog.openlog('SUNHPC')
def msg(message):
syslog.syslog('DOP: %s' % message)
class Partition:
raidinfo = ''
mountpoints = []
saved_fstab = []
def __init__(self):
if os.path.exists('/mnt/runtime/usr/sbin/parted'):
self.parted = '/mnt/runtime/usr/sbin/parted'
else:
self.parted = '/sbin/parted'
if os.path.exists('/mnt/runtime/usr/sbin/e2label'):
#self.e2label = '/mnt/runtime/usr/sbin/e2label'
self.xfs_admin = '/mnt/runtime/usr/sbin/xfs_admin'
else:
self.xfs_admin = '/sbin/xfs_admin'
if os.path.exists('/mnt/runtime/usr/sbin/mdadm'):
self.mdadm = '/mnt/runtime/usr/sbin/mdadm'
else:
self.mdadm = '/sbin/mdadm'
if not os.path.exists('/tmp/discovered.disks'):
self.discoveredDisks()
def discoveredDisks(self):
raids = []
disks = []
if not disks:
disks = Device().get_disks
if raids:
mdstat = open('/proc/mdstat')
for line in mdstat:
if line.startswith('md'):
devicesName = line.split()[4:]
for devName in devicesName:
devName = devName.strip('0123456789[]')
if devName not in disks:
disks.append(devName)
mdstat.close()
# Print disks
discoveredDisk = open('/tmp/discovered.disks', 'w')
discoveredDisk.write("disks: " + ' '.join(disks) + '\n')
discoveredDisk.write("raids: " + ' '.join(raids) + '\n')
discoveredDisk.close()
def getDisks(self):
disks = []
fe = open('/tmp/discovered.disks', 'r')
for line in fe.readlines():
l = line.split()
if len(l) > 0 and l[0] == 'disks:':
for d in l[1:]:
#
# only include disks that have their
# 'media present' -- this is one way
# to filter out Dell Virtual Floppy
# devices
#
disks.append(d)
fe.close()
return disks
def getRaids(self):
raids = []
file = open('/tmp/discovered.disks', 'r')
for line in file.readlines():
l = line.split()
if len(l) > 0 and l[0] == 'raids:':
raids = l[1:]
file.close()
try:
file = open('/dev/md/md-device-map', 'r')
for line in file.readlines():
l = line.split()
if len(l) > 0 and not (l[0] in raids):
raids.append(l[0])
file.close()
except:
#no raid
pass
return raids
def gptDrive(self, devname):
#
# if this is a drive with a GPT format, then return '1'
#
retval = 0
cmd = '%s /dev/%s print -s 2> /dev/null' % \
(self.parted, devname)
label = 'Partition Table:'
for line in os.popen(cmd).readlines():
if len(line) > len(label) and \
line[0:len(label)] == label:
l = line.split()
if len(l) > 2 and l[2] == 'gpt':
retval = 1
break
return retval
def getDevice(self, strs):
device = ''
a = strs.split('/dev/')
if len(a) > 1:
device = a[1]
return device.strip()
def getSectorStart(self, str):
sectorstart = ''
a = str.split('=')
if len(a) > 1 and a[0].strip() == 'start':
sectorstart = a[1]
else:
sectorstart = a[0]
return sectorstart.strip()
def getPartitionSize(self, str):
partitionsize = ''
a = str.split('=')
if len(a) > 1 and a[0].strip() == 'size':
partitionsize = a[1]
else:
partitionsize = a[0]
return partitionsize.strip()
def getPartId(self, str):
partid = ''
a = str.split('=')
if len(a) > 1 and a[0].strip() == 'Id':
partid = a[1]
else:
partid = a[0]
return partid.strip()
def getFsType(self, mntpoint):
return self.findFsTypeInFstab(mntpoint)
def getBootFlags(self, str):
return str.strip()
def getMountPoint(self, devicename):
mntpoint = ''
cmd = 'blkid -o export /dev/%s | grep UUID ' % devicename
cmd += ' 2> /dev/null'
uuid = os.popen(cmd).readlines()
if len(uuid) > 0:
mntpoint = self.findMntInFstab(uuid[0][:-1])
if mntpoint == '':
mntpoint = self.findMntInFstab('/dev/' + devicename)
if mntpoint == '':
#
# see if the device is part of a raidset
#
mntpoint = self.getRaidName(devicename)
if mntpoint == '':
cmd = '%s /dev/%s 2> /dev/null' % \
(self.xfs_admin, devicename)
label = os.popen(cmd).readlines()
label = ''.join(label)
id = 'LABEL=%s' % (label[:-1])
mntpoint = self.findMntInFstab(id)
return mntpoint
def getRaidName(self, partition_device):
raidname = ''
for info in self.raidinfo:
if len(info) > 3:
(device, partitions, raidlevel,
num_partitions) = info
if partition_device in partitions:
raidname = 'raid.%s' % partition_device
break
return raidname
def findMntInFstab(self, identifier):
for line in self.saved_fstab:
l = line.split()
if len(l) > 0:
if l[0] == identifier:
return l[1]
return ''
def findFsTypeInFstab(self, mntpoint):
for line in self.saved_fstab:
l = line.split()
if len(l) > 2:
if l[1] == mntpoint:
return l[2]
return ''
def formatPartedNodePartInfo(self, devname, info):
#
# this function parses partition info from 'parted'
#
partinfo = []
isDisk = 0
for line in info:
l = line[:-1].split()
if len(l) > 2 and re.match('[0-9]+', l[0]):
if devname[0:2] == 'md':
device = devname
elif len(devname) > 4 and \
devname[0:5] == 'cciss':
#
# special case for HP smart array
# controllers
#
device = devname + 'p' + l[0]
else:
device = devname + l[0]
isDisk = 1
else:
if len(l) > 1 and l[0] == 'Disk':
isDisk = 1
continue
sectorstart = l[1]
partitionsize = l[3]
partid = ''
if devname[0:2] == 'md' and len(l) > 4:
#
# special case for software RAID. there is
# no 'Type' or 'Flags' fields, so the
# 'File system' field is 5th field
#
fstype = l[4]
bootflags = ''
else:
bfs = None
if len(l) > 5 and not self.gptDrive(devname):
#
# there is a case for RAID 0 that the
# second partition of the drive does not
# get the file system label
# (e.g., ext4), so the 'boot flags'
# get misidentified as a
# file system type
#
if 'raid' in l[5] or 'boot' in l[5]:
bfs = l[5:]
fstype = ''
else:
fstype = l[5]
else:
fstype = ''
if len(l) > 4 and self.gptDrive(devname):
# gpt partition there is not
# Type column so fstype is in 4
fstype = l[4]
if len(l) > 5:
bfs = [l[5]]
if not bfs and len(l) > 6:
bfs = l[6:]
if bfs:
bf = []
for b in bfs:
bf.append(b.rstrip(','))
bootflags = ' '.join(bf)
else:
bootflags = ''
if 'linux-swap' in fstype:
mntpoint = 'swap'
else:
mntpoint = self.getMountPoint(device)
# print 'formatPartedNodePartInfo:l: ', l
partinfo.append('%s,%s,%s,%s,%s,%s,%s,%s\n' %
(device, sectorstart, partitionsize,
partid, fstype, bootflags, '',
mntpoint))
# print 'formatPartedNodePartInfo:partinfo: ', partinfo
if partinfo == [] and isDisk:
#
# this disk has no partitions, create a
# dummy null entry for it
#
partinfo = [ '%s,,,,,,,\n' % (devname) ]
return partinfo
def parsePartInfo(self, info):
n = info.split(',')
if len(n) != 8:
return ('', '', '', '', '', '', '', '')
device = n[0].strip()
sectorstart = n[1].strip()
partitionsize = n[2].strip()
partid = n[3].strip()
fstype = n[4].strip()
bootflags = n[5].strip()
partflags = n[6].strip()
mntpoint = n[7].strip()
return (device, sectorstart, partitionsize, partid,
fstype, bootflags, partflags, mntpoint)
def getDiskInfo(self, disk):
syslog.syslog('getDiskInfo: disk:%s' % (disk))
cmd = '%s /dev/%s ' % (self.parted, disk)
cmd += 'print -s 2> /dev/null'
diskinfo = os.popen(cmd).readlines()
syslog.syslog('getNodePartInfo: diskinfo:%s' % (diskinfo))
return diskinfo
def getRaidLevel(self, device):
level = None
cmd = '%s --query --detail ' % (self.mdadm)
cmd += '/dev/%s' % (device)
for line in os.popen(cmd).readlines():
l = line.split()
if len(l) > 3 and l[0] == 'Raid' and l[1] == 'Level':
if l[3][0:4] == 'raid':
level = l[3][4:]
break
return level
def getRaidParts(self, device):
parts = []
foundparts = 0
cmd = '%s --query --detail ' % (self.mdadm)
cmd += '/dev/%s' % (device)
for line in os.popen(cmd).readlines():
l = line.split()
if len(l) > 4 and l[3] == 'RaidDevice':
foundparts = 1
continue
if foundparts == 0:
continue
if len(l) == 0:
continue
part = l[-1].split('/')
parts.append('raid.%s' % part[-1])
return ' '.join(parts)
def getNodePartInfo(self, disks):
arch = os.uname()[4]
partinfo = []
nodedisks = {}
for line in self.getFstab(disks):
self.saved_fstab.append(line)
for devname in disks:
diskinfo = self.getDiskInfo(devname)
partinfo += self.formatPartedNodePartInfo(devname,
diskinfo)
syslog.syslog('getNodePartInfo: partinfo:%s' % (partinfo))
for node in partinfo:
n = self.parsePartInfo(node)
(nodedevice, nodesectorstart, nodepartitionsize,
nodepartid, nodefstype, nodebootflags,
nodepartflags, nodemntpoint) = n
if (len(nodedevice) > 2) and (nodedevice[0:2] == 'md'):
nodepartflags = '--level=%s' % \
self.getRaidLevel(nodedevice)
nodebootflags = self.getRaidParts(nodedevice)
n = (nodedevice, nodesectorstart,
nodepartitionsize,
nodepartid, nodefstype,
nodebootflags,
nodepartflags, nodemntpoint)
elif nodebootflags != '':
if 'raid' in nodebootflags.split():
nodemntpoint = 'raid.%s' % (nodedevice)
n = (nodedevice, nodesectorstart,
nodepartitionsize,
nodepartid, nodefstype,
nodebootflags,
nodepartflags, nodemntpoint)
if nodedevice != '':
key = ''
for disk in disks:
if len(disk) <= len(nodedevice) and \
disk == nodedevice[0:len(disk)]:
key = disk
break
if key != '':
if key not in nodedisks:
nodedisks[key] = [n]
else:
nodedisks[key].append(n)
syslog.syslog('getNodePartInfo:nodedisks:%s' % (nodedisks))
return nodedisks
def listDiskPartitions(self, disk):
list = []
inHeader = 1
if disk[0:2] == 'md':
return [ (disk, 'dummy') ]
for part in self.getDiskInfo(disk):
l = part.split()
#
# skip the 'parted' header
#
if len(l) > 1 and l[0] == 'Number':
inHeader = 0
continue
if inHeader:
continue
partnumber = 0
#
# look for a part number
#
if len(l) > 2 and re.match('[0-9]+', l[0]):
partnumber = int(l[0])
if partnumber > 0:
if len(disk) > 4 and disk[0:5] == 'cciss':
#
# special case for HP smart array
# controllers
#
disk = disk + 'p'
if len(l) > 5:
fstype = l[5]
else:
fstype = ''
if len(l) > 4 and self.gptDrive( disk ):
# this is a gpt partition
fstype = l[4]
list.append(('%s%d' % (disk, partnumber),
fstype))
return list
def defaultDataDisk(self, disk):
basename = '/state/partition'
parts = []
i = 1
while 1:
nextname = '%s%d' % (basename, i)
if nextname not in self.mountpoints:
break
i = i + 1
p = 'part '
p += '%s --size=1 ' % (nextname)
p += '--fstype=ext4 --grow --ondisk=%s ' % (disk)
self.mountpoints.append(nextname)
parts.append(p)
return parts
def SunhpcGetPartsize(self, mountpoint):
size = 0
if mountpoint == 'root':
size = 60000
elif mountpoint == 'var':
size = 10000
elif mountpoint == 'swap':
size = 1000
return size
def defaultRootDisk(self, disk):
arch = os.uname()[4]
parts = []
if arch == 'ia64':
p = 'part /boot/efi --size=1000 --fstype=vfat '
p += '--ondisk=%s\n' % (disk)
p = 'part '
p += '/ --size=%d ' % (self.SunhpcGetPartsize('root'))
p += '--fstype=ext4 --ondisk=%s ' % (disk)
self.mountpoints.append('/')
parts.append(p)
p = 'part '
p += '/var --size=%d ' % (self.SunhpcGetPartsize('var'))
p += '--fstype=ext4 --ondisk=%s ' % (disk)
self.mountpoints.append('/var')
parts.append(p)
p = 'part '
p += 'swap --size=%d ' % (self.SunhpcGetPartsize('swap'))
p += '--fstype=swap --ondisk=%s ' % (disk)
self.mountpoints.append('swap')
parts.append(p)
parts += self.defaultDataDisk(disk)
return parts
def getFstab(self, disks):
if os.path.exists('/upgrade/etc/fstab'):
file = open('/upgrade/etc/fstab')
lines = file.readlines()
file.close()
return lines
#
# if we are here, let's go look at all the disks for /etc/fstab
#
mountpoint = tempfile.mktemp()
os.makedirs(mountpoint)
fstab = mountpoint + '/etc/fstab'
lines = []
for disk in disks:
for (partition, fstype) in \
self.listDiskPartitions(disk):
if not fstype or 'linux-swap' in fstype:
continue
os.system('mount /dev/%s %s' \
% (partition, mountpoint) + \
' > /dev/null 2>&1')
if os.path.exists(fstab):
file = open(fstab)
lines = file.readlines()
file.close()
os.system('umount %s 2> /dev/null' %
(mountpoint))
if len(lines) > 0:
break
if len(lines) > 0:
break
try:
os.removedirs(mountpoint)
except:
pass
return lines
def isSunhpcDisk(self, partinfo, touchit=0):
retval = 0
mountpoint = tempfile.mktemp()
os.makedirs(mountpoint)
for part in partinfo:
(dev,start,size,id,fstype,bootflags,partflags,mnt) = part
if not fstype or fstype == 'linux-swap':
continue
devname = '/dev/%s' % (dev)
os.system('mount %s %s' % (devname, mountpoint))
try:
filename = mountpoint + '/.sunhpc-release'
if touchit == 1:
os.system('touch %s' % filename)
if os.path.exists(filename):
retval = 1
except:
pass
os.system('umount %s' % (mountpoint) + ' > /dev/null 2>&1')
if retval == 1:
break
try:
os.removedirs(mountpoint)
except:
pass
return retval
def addPartitions(self, nodepartinfo, format):
arch = os.uname()[4]
parts = []
#
# for each partition on a drive, build a partition
# specification for anaconda
#
for node in nodepartinfo:
if len(node) == 1: continue
(nodedevice, nodesectorstart, nodepartitionsize,
nodepartid, nodefstype, nodebootflags,
nodepartflags, nodemntpoint) = node
if arch == 'ia64':
if nodefstype == 'fat32':
nodefstype = 'vfat'
elif nodefstype == 'linux-swap':
nodefstype = 'swap'
if nodemntpoint == '':
continue
#
# only add raid partitions if they have a mountpoint
# defined by their respective 'md' device.
#
# anaconda will crash if there is not a valid
# mountpoint for the md device
#
if nodepartid == 'fd':
if not self.getRaidMountPoint(nodedevice):
continue
args = [ nodemntpoint ]
if len(nodemntpoint) > 3 and \
nodemntpoint[0:4] == 'raid':
#
# never format a software raid partition and
# always set its size to 1
#
args.append('--noformat')
args += [ '--size', '1']
elif (nodemntpoint != '/' and nodemntpoint != '/var') \
and not format:
args.append('--noformat')
else:
if nodefstype == '':
args += [ '--fstype', self.fstype ]
else:
args += [ '--fstype', nodefstype ]
israid = 0
if len(nodedevice) > 2 and nodedevice[0:2] == 'md':
israid = 1
args += [ "--device=%s" % (nodedevice) ]
args += [ "--useexisting" ]
if nodepartflags != '':
args += [ nodepartflags ]
else:
args += [ '--onpart', nodedevice ]
if israid:
parts.append('raid %s' % (args.join()))
else:
parts.append('part %s' % (args.join()))
self.mountpoints.append(nodemntpoint)
return parts
def compareDiskInfo(self, dbpartinfo, nodepartinfo):
if len(dbpartinfo) != len(nodepartinfo):
return 0
for db in dbpartinfo:
if len(db) == 1:
continue
(dbdevice, dbsectorstart, dbpartsize, dbpartid,
dbfstype, dbbootflags, dbpartflags,
dbmntpoint) = db
found = 0
for node in nodepartinfo:
if len(node) == 1:
continue
(nodedevice, nodesectorstart, nodepartsize,
nodepartid, nodefstype, nodebootflags,
nodepartflags, nodemntpoint) = node
# print 'compareDiskInfo:node: ', node
# print 'compareDiskInfo:db: ', db
if dbsectorstart == nodesectorstart and \
dbpartsize == nodepartsize and \
dbpartid == nodepartid and \
dbfstype == nodefstype and \
dbbootflags == nodebootflags and \
dbpartflags == nodepartflags and \
dbmntpoint == nodemntpoint:
found = 1
break
if not found: return 0
return 1
class Device:
def __init__(self, subsys='/sys/class/block'):
self.subsys = subsys
self.block = []
self.disks = {}
self.parts = {}
self.build()
def build(self):
if not os.path.isdir(self.subsys):
return self.devices
for sub in os.listdir(self.subsys):
self.block.append(Block(sub, self.subsys))
for disk in self.block:
if disk.device_is_partition:
self.parts[disk.info.get('DEVNAME')] = disk
if not disk.device_is_disk:
continue
if disk.device_is_loop:
continue
if disk.device_is_dm:
continue
self.disks[disk.info.get('DEVNAME')] = disk
@property
def get_parts(self):
return list(self.parts.keys())
@property
def get_disks(self):
return list(self.disks.keys())
@property
def get_disk_object(self, dev=None):
if dev in self.disks:
return self.disks.get(dev)
@property
def get_part_object(self, dev=None):
if dev in self.parts:
return self.parts.get(dev)
class Block:
def __init__(self, devname, block):
self.devname = devname
self.block = block
self.devinfo = {}
self.build()
def build(self):
self.device_info()
@property
def info(self):
return self.devinfo
@property
def device_path(self):
return os.path.realpath(os.path.join(self.block, self.devname))
def device_info(self):
"""read /sys/class/block/sda/uevent info"""
# DEVNAME=sda
# DEVTYPE=disk
uevent = os.path.join(self.device_path, 'uevent')
if not os.path.isfile(uevent):
return self.devinfo
with open(uevent) as f:
for l in f.readlines():
sp = l.split('=', 1)
if not len(sp): continue
self.devinfo[sp[0]] = sp[1].strip()
@property
def device_is_cdrom(self):
try:
dev = os.path.join(os.sep, 'dev', self.info.get('DEVNAME'))
gid = os.stat(dev)[stat.ST_GID]
t = grp.getgrgid(gid).gr_name
except:
t = None
if t and t == 'cdrom':
return True
if os.path.exists('/opt/sunhpc/bin/lsscsi'):
lsscsi = '/opt/sunhpc/bin/lsscsi'
else:
lsscsi = '/sbin/lsscsi'
if not os.path.exists(lsscsi):
return False
line = os.popen('%s |grep "%s"' % (lsscsi, self.info.get('DEVNAME'))).readline().split()
if len(line) > 4:
if line[1] == 'cd/dvd' or line[3] == 'DVD-ROM':
return True
return False
@property
def device_is_disk(self):
if self.device_is_cdrom:
return False
has_range = os.path.exists(os.path.join(self.device_path, 'range'))
return self.info.get("DEVTYPE") == "disk" or has_range
@property
def device_is_partition(self):
has_start = os.path.exists(os.path.join(self.device_path, 'start'))
return self.info.get("DEVTYPE") == "partition" or has_start
@property
def device_is_loop(self):
return self.info.get('DEVNAME').startswith('loop')
@property
def device_is_dm(self):
return self.info.get('DEVNAME').startswith('dm')
def __repr__(self):
return 'Device("%s")' % self.device_path