You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-lib/pym/calculate/lib/utils/device.py

427 lines
15 KiB

#-*- coding: utf-8 -*-
# Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import re
import os
import time
import os
from os import path
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__])
def getUUIDDict(revers=False,devs=[]):
"""Get dict UUID -> dev"""
blkidProcess = process("/sbin/blkid","-s","UUID","-c","/dev/null",*devs)
if revers:
datafunc = lambda x,y: (y,x)
else:
datafunc = lambda x,y: (x,y)
DEV,UUID = 0,1
reSplit = re.compile('^([^:]+):.*UUID="([^"]+)"',re.S)
return dict(
map(lambda x:datafunc("UUID=%s"%x[UUID],
getUdevDeviceInfo(name=x[DEV]).get('DEVNAME',x[DEV])),
map(lambda x:x.groups(),
filter(lambda x:x,
map(reSplit.search,
blkidProcess)))))
from files import (getProgPath,pathJoin,listDirectory,
checkUtils,readFile,process)
from common import getpathenv
def detectDeviceForPartition(dev):
"""Detect parent device for partition by udev and return property"""
prop = getUdevDeviceInfo(name=dev)
if prop.get('DEVTYPE','') != 'partition':
return ''
parentpath = path.dirname(prop.get('DEVPATH',''))
if parentpath:
devProp = getUdevDeviceInfo(path=parentpath)
return devProp.get('DEVNAME','')
return None
def countPartitions(devname):
"""Count partition for specified device"""
syspath = getUdevDeviceInfo(name=devname).get('DEVPATH','')
if not syspath:
return 0
deviceName = path.basename(syspath)
if not syspath.startswith("/sys"):
syspath = pathJoin("/sys",syspath)
return len(filter(lambda x:x.startswith(deviceName),
listDirectory(syspath)))
def getLvmGroups():
"""Get LVM groups"""
pvdisplayCmd = getProgPath('/sbin/pvdisplay')
pvdata = process(pvdisplayCmd,"-C","-o", "vg_name","--noh")
return filter(lambda x:x,pvdata.read().split())
def getLvmPartitions(vg_name,lv_name,cache=[]):
"""Get lvm partitions"""
if not cache:
pvdisplayCmd = getProgPath('/sbin/pvdisplay')
pvdata = process(pvdisplayCmd,"-C","-o",
"vg_name,lv_name,pv_name","--noh")
if pvdata.success():
cache.extend(
filter(lambda x:x and len(x)==3,
map(lambda x:x.split(),
pvdata.read().split('\n'))))
if cache:
res = map(lambda x:x[2],
filter(lambda x:x[0]==vg_name and x[1]==lv_name,cache))
if res:
return res
return []
def getPartitionDevice(syspath):
"""Get real parent device by partition,lvm,mdraid"""
prop = getUdevDeviceInfo(path=syspath)
# real device
if prop.get('ID_TYPE',"") == "disk" and \
prop.get('DEVTYPE',"") == "disk":
return prop.get('DEVNAME',"")
# partition
if prop.get('DEVTYPE') == "partition":
return getPartitionDevice(path.dirname(syspath))
# md raid
if prop.get('MD_LEVEL',"").startswith("raid"):
if not syspath.startswith('/sys'):
syspath = pathJoin('/sys',syspath)
syspath = pathJoin(syspath,"md")
for rd in filter(lambda x:path.basename(x).startswith('rd'),
listDirectory(syspath,fullPath=True)):
rdBlockPath = path.join(rd,"block")
if path.exists(rdBlockPath):
return getPartitionDevice(path.realpath(rdBlockPath))
else:
return ""
# lvm
if prop.get('DM_LV_NAME',"") != "":
parts = getLvmPartitions(prop.get('DM_VG_NAME',''),
prop.get('DM_LV_NAME',''))
if parts:
propPartLvm = getUdevDeviceInfo(name=parts[0])
if 'DEVPATH' in propPartLvm:
return getPartitionDevice(propPartLvm['DEVPATH'])
return ""
def lspci(filtername=None,shortInfo=False):
"""Get hash of lspci, filtred by filtername. If shortInfo, then
type,vendor and name get only first word
pcidata(domain,bus,slot,func)
'type'
'vendor'
'name'"""
reData = re.compile(r'(\S+)\s"([^"]+)"\s+"([^"]+)"\s+"([^"]+)"',re.S)
if filtername:
if hasattr(filtername,'__call__'):
filterfunc = filtername
else:
filterfunc = lambda x: filtername in x
else:
filterfunc = lambda x:x
if shortInfo:
sfunc = lambda x:x.partition(" ")[0]
else:
sfunc = lambda x:x
lspciProg = checkUtils('/usr/sbin/lspci')
processLsPci = process(lspciProg,"-m")
retData = {}
for device in map(lambda x:x.groups(),
filter(lambda x:x,
map(reData.search,
filter(filterfunc,
processLsPci)))):
retData[device[0]] = {'type':sfunc(device[1]),\
'vendor':sfunc(device[2]),\
'name':sfunc(device[3])}
return retData
class getUdevDeviceInfo:
cache = {}
udevadmCmd = ""
def clearCache(self):
self.cache = {}
def __call__(self,path="",name=""):
"""Get device info by syspath of name"""
typeQuery = "--name" if name else "--path"
value = name or os.path.realpath(path)
keyCache = "%s=%s"%(typeQuery,value)
if not self.cache:
refreshUdev(onlyTrigger=True)
if not keyCache in self.cache:
if not self.udevadmCmd:
self.udevadmCmd = getProgPath('/sbin/udevadm')
data = \
dict(
filter(lambda x:x[0],
map(lambda x:x.partition("=")[0::2],
process(self.udevadmCmd,"info","--query","property",
typeQuery,value).read().split("\n"))))
tm = time.time()
keyNameCache = data.get('DEVNAME','')
keyPathCache = data.get('DEVPATH','')
if keyNameCache:
keyNameCache = "--name="+keyNameCache
if keyPathCache:
keyPathCache = "--path=/sys"+keyPathCache
if "DEVNAME" in data and not keyNameCache in self.cache:
self.cache[keyNameCache] = data
if "DEVPATH" in data and not keyPathCache in self.cache:
self.cache[keyPathCache] = data
return data
else:
#print "fromCache",keyCache
return self.cache[keyCache]
getUdevDeviceInfo = getUdevDeviceInfo()
def convertNameToSyspath(name):
dev = getUdevDeviceInfo(name=name).get('DEVPATH',"")
if dev and not dev.startswith('/sys'):
dev = pathJoin('/sys',dev)
return dev
def humanreadableSize(size):
"""
Human readable size (1024 -> 1K and etc)
"""
try:
size = int(size)
except ValueError:
return ""
suffix = (((1024**0),"",False),
((1024**1),"K",False),
((1024**2),"M",False),
((1024**3),"G",True),
((1024**4),"T",True),
((1024**5),"P",True))
suffix = filter(lambda x:size >x[0],suffix)
if suffix:
suffix = suffix[-1]
printSize = int(size / (float(suffix[0])/10))
printSizeTail = printSize % 10
printSize = printSize / 10
if suffix[2] and printSizeTail:
return "%d.%d%s"%(printSize,printSizeTail,suffix[1])
else:
return "%d%s"%(printSize,suffix[1])
return str(size)
def getPartitionSize(syspath=None,name=None,inBytes=False):
"""
Get partition size
"""
if name:
dev = convertNameToSyspath(name)
else:
dev = syspath
SECTORSIZE=512
sizeFile = pathJoin(dev,"size")
if path.exists(sizeFile):
size = int(open(sizeFile,'r').read().strip())*SECTORSIZE
if inBytes:
return str(size)
else:
return humanreadableSize(size)
return ""
def getDeviceType(syspath=None,name=None):
"""Get device type (disk,partition,lvm,raid)"""
if name:
prop = getUdevDeviceInfo(name=name)
syspath = prop.get('DEVPATH','')
else:
prop = getUdevDeviceInfo(path=syspath)
# real device
if prop.get('ID_CDROM',""):
return "cdrom"
if prop.get('ID_TYPE',"") == "disk" and \
prop.get('DEVTYPE',"") == "disk":
return "disk"
# partition
if prop.get('DEVTYPE') == "partition":
return getDeviceType(path.dirname(syspath))+"-partition"
# md raid
if prop.get('MD_LEVEL',"").startswith("raid"):
if not syspath.startswith('/sys'):
syspath = pathJoin('/sys',syspath)
syspath = pathJoin(syspath,"md")
for rd in filter(lambda x:path.basename(x).startswith('rd'),
listDirectory(syspath,fullPath=True)):
rdBlockPath = path.join(rd,"block")
if path.exists(rdBlockPath):
return getDeviceType(path.realpath(rdBlockPath))+"-raid"
else:
return "loop"
# lvm
if prop.get('DM_LV_NAME',"") != "":
parts = getLvmPartitions(prop.get('DM_VG_NAME',''),
prop.get('DM_LV_NAME',''))
if parts:
propPartLvm = getUdevDeviceInfo(name=parts[0])
if 'DEVPATH' in propPartLvm:
return getDeviceType(propPartLvm['DEVPATH'])+"-lvm"
return "loop"
def getRaidPartitions(raidpath):
"""Get raid partitions"""
prop = getUdevDeviceInfo(path=raidpath)
raidParts = []
if prop.get('MD_LEVEL',"").startswith("raid"):
if not raidpath.startswith('/sys'):
raidpath = pathJoin('/sys',raidpath)
raidpath = pathJoin(raidpath,"md")
for rd in filter(lambda x:path.basename(x).startswith('rd'),
listDirectory(raidpath,fullPath=True)):
rdpath = path.join(raidpath,rd,"block")
if path.exists(rdpath):
raidParts.append(
getUdevDeviceInfo(path=path.realpath(rdpath)).get(
"DEVNAME",''))
return filter(lambda x:x,raidParts)
def getPartitionType(prop):
"""Get type of dos part table (primary,extended or logical)"""
if prop.get('ID_PART_ENTRY_SCHEME') == 'dos':
partId = prop.get('ID_PART_ENTRY_TYPE','')
partNumber = prop.get('ID_PART_ENTRY_NUMBER','')
if partId and partNumber:
if partId == "0x5":
return "extended"
elif int(partNumber)>4:
return "logical"
else:
return "primary"
return prop.get('ID_PART_TABLE_TYPE','')
def detectBuild(pathname,dictInfo):
"""Detect build by root passwd 'root'"""
shadowPath = pathJoin(pathname,'/etc/shadow')
if r"root:$1$JMvNh5xg$VnV1DyJdTcwuZ0hp5YiJG0:14349:0:::::" in \
readFile(shadowPath):
dictInfo['type'] = ' assemble'
elif path.exists(pathJoin(pathname,"delta")) and \
path.exists(pathJoin(pathname,"workspace")):
dictInfo['type'] = " builder"
issue = readFile(pathJoin(pathname,'etc/gentoo-release'))
if "Server" in issue:
if "Scratch" in issue:
dictInfo['name'] = "CSS"
else:
dictInfo['name'] = "CDS"
elif "Desktop" in issue:
if "XFCE" in issue:
dictInfo['name'] = "CLDX"
elif "KDE" in issue:
dictInfo['name'] = "CLD"
elif "GNOME" in issue:
dictInfo['name'] = "CLDG"
elif "Scratch" in issue:
dictInfo['name'] = "CLS"
else:
dictInfo['type'] = ''
return dictInfo
def getOsProberHash(getContentFunc=None):
"""Get partition content by os-prober"""
os_prober = getProgPath('/usr/bin/os-prober')
if os_prober:
DEV,LONG,SHORT,TYPE = 0,1,2,3
osProberList = \
map(lambda x:[getUdevDeviceInfo(name=x[DEV]).get('DEVNAME',''),
x[LONG],x[SHORT],x[TYPE]],
filter(lambda x:len(x)>=4,
map(lambda x:x.split(":"),
process(os_prober))))
for osRecord in osProberList:
if "Gentoo" in osRecord[SHORT] and getContentFunc:
osDescr = getContentFunc(osRecord[DEV],addFunc=detectBuild)
if "name" in osDescr and "march" in osDescr and \
"build" in osDescr and "ver" in osDescr and \
(osDescr["ver"] != "0" or osDescr["build"]):
if osDescr['build']:
osDescr['build'] = "-%s"%osDescr['build']
else:
osDescr['build'] = "-%s"%osDescr['ver']
osRecord[SHORT] = \
"{name}-{march}{build}{type}".format(**osDescr)
else:
osRecord[SHORT] = "Gentoo"
elif "Gentoo" in osRecord[SHORT] and "Calculate" in osRecord[LONG]:
osRecord[SHORT] = "Calculate"
osProberHash = \
dict(
map(lambda x:(x[DEV],x[SHORT]),
osProberList))
else:
osProberHash = {}
return osProberHash
def refreshLVM():
"""Run command which refresh information about LVM"""
vgscan = getProgPath('/sbin/vgscan')
vgchange = getProgPath('/sbin/vgchange')
lvchange = getProgPath('/sbin/lvchange')
if vgscan and vgchange and lvchange:
process(vgscan).success()
process(vgchange,'-ay').success()
process(vgchange,'--refresh').success()
for group in getLvmGroups():
if not os.environ.get('EBUILD_PHASE'):
process(lvchange,'-ay',group).success()
process(lvchange,'--refresh',group).success()
def loadEfiVars():
"""
Load module with efivars
"""
modprobe = getProgPath('/sbin/modprobe')
if not path.exists('/sys/firmware/efi') and modprobe:
process(modprobe,'efivars').success()
def refreshUdev(onlyTrigger=False):
"""Run command which refresh information about device in udev"""
if not onlyTrigger:
getUdevDeviceInfo.clearCache()
udevadm = getProgPath('/bin/udevadm')
if udevadm:
blkidFile = '/etc/blkid.tab'
try:
if path.exists(blkidFile):
os.unlink(blkidFile)
except:
pass
if not os.environ.get('EBUILD_PHASE'):
process(udevadm,"trigger","--subsystem-match","block").success()
process(udevadm,"settle","--timeout=15").success()
def getCommonDeviceName(dev):
"""
Get common device name (example: LVM -> /dev/dm- and etc)
"""
return getUdevDeviceInfo(name=dev).get('DEVNAME',dev)