refs #1086 add ogDiskToDev()

pull/1/head
Natalia Serrano 2024-11-04 17:36:00 +01:00
parent 063ddb8913
commit c94d2bb381
2 changed files with 167 additions and 120 deletions

View File

@ -2,6 +2,8 @@ import filecmp
import subprocess import subprocess
import shutil import shutil
import os import os
import stat
from pathlib import Path
from CacheLib import * from CacheLib import *
from FileSystemLib import * from FileSystemLib import *
@ -365,142 +367,187 @@ def ogDevToDisk(dev):
ogRaiseError(OG_ERR_NOTFOUND, dev) ogRaiseError(OG_ERR_NOTFOUND, dev)
return OG_ERR_NOTFOUND return OG_ERR_NOTFOUND
def ogDiskToDev(*args): def _getAllDisks():
# Variables locales ret = []
CACHEFILE = "/var/cache/disks.cfg" all_disks = subprocess.run("lsblk -n -e 1,2 -x MAJ:MIN 2>/dev/null || lsblk -n -e 1,2", shell=True, capture_output=True, text=True).stdout.splitlines()
ALLDISKS = []
MPATH = VOLGROUPS = ZFSVOLS = DISK = PART = ZPOOL = None
i = 1
# Si se solicita, mostrar ayuda.
if len(args) == 1 and args[0] == "help":
ogHelp('ogDiskToDev', 'ogDiskToDev int_ndisk [int_npartition]',
'ogDiskToDev => /dev/sda /dev/sdb',
'ogDiskToDev 1 => /dev/sda',
'ogDiskToDev 1 1 => /dev/sda1')
return
# Borrar fichero de caché de configuración si hay cambios en las particiones.
if not filecmp.cmp('/proc/partitions', '/tmp/.partitions'):
# Guardar copia de las particiones definidas para comprobar cambios.
shutil.copy2('/proc/partitions', '/tmp/.partitions')
os.remove(CACHEFILE)
# Si existe una correspondencia con disco/dispositivo en el caché; mostrarlo y salir.
with open(CACHEFILE, 'r') as f:
for line in f:
parts = line.strip().split(':')
if len(parts) == 2 and parts[0] == ":".join(args):
print(parts[1])
return
# Continuar para detectar nuevos dispositivos.
# Listar dispositivos de discos.
all_disks = subprocess.getoutput("lsblk -n -e 1,2 -x MAJ:MIN 2>/dev/null || lsblk -n -e 1,2").splitlines()
for line in all_disks: for line in all_disks:
parts = line.split() parts = line.split()
if parts[5] == "disk": if parts[5] == "disk":
ALLDISKS.append(f"/dev/{parts[0]}") parts[0].replace ('!', '/')
ret.append(f"/dev/{parts[0]}")
return ret
# Listar volúmenes lógicos. def _getAllVolGroups():
VOLGROUPS = subprocess.getoutput("vgs -a --noheadings 2>/dev/null").splitlines() vgs = subprocess.run(['vgs', '-a', '--noheadings'], capture_output=True, text=True).stdout.splitlines()
VOLGROUPS = [f"/dev/{line.split()[0]}" for line in VOLGROUPS] ret = [f"/dev/{line.split()[0]}" for line in vgs]
return ret
# Detectar caminos múltiples (ignorar mensaje si no está configurado Multipath). def _getMPath():
ret = alldisks2remove = []
try: try:
MPATH = subprocess.getoutput("multipath -l -v 1 2>/dev/null").splitlines() mpath = subprocess.run(['multipath', '-l', '-v', '1'], capture_output=True, text=True).stdout.splitlines()
MPATH = [f"/dev/mapper/{line.split()[0]}" for line in MPATH] ret = [f"/dev/mapper/{line.split()[0]}" for line in mpath]
# Quitar de la lista los discos que forman parte de Multipath.
for line in subprocess.getoutput("multipath -ll").splitlines(): for line in subprocess.getoutput("multipath -ll").splitlines():
if line.split()[5] == "ready": if line.split()[5] == "ready":
disk = f"/dev/{line.split()[2]}" alldisks2remove.append (f"/dev/{line.split()[2]}")
if disk in ALLDISKS: except FileNotFoundError:
ALLDISKS.remove(disk) pass
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
MPATH = [] pass
return ret, alldisks2remove
# Detectar volúmenes ZFS. def _getAllZFSVols():
ZFSVOLS = subprocess.getoutput("blkid").splitlines() zfsvols = subprocess.run(['blkid'], capture_output=True, text=True).stdout.splitlines()
ZFSVOLS = [line.split(":")[0] for line in ZFSVOLS if "zfs" in line] return [line.split(":")[0] for line in zfsvols if "zfs" in line]
# Mostrar salidas segun el número de parametros. #/**
if len(args) == 0: # ogDiskToDev [int_ndisk [int_npartition]]
print(" ".join(ALLDISKS)) #@brief Devuelve la equivalencia entre el nº de orden del dispositivo (dicso o partición) y el nombre de fichero de dispositivo correspondiente.
elif len(args) == 1: #@param int_ndisk nº de orden del disco
# Error si el parámetro no es un número positivo. #@param int_npartition nº de orden de la partición
if not args[0].isdigit() or int(args[0]) <= 0: #@return Para 0 parametros: Devuelve los nombres de ficheros de los dispositivos sata/ata/usb linux encontrados.
ogRaiseError(OG_ERR_FORMAT, args[0]) #@return Para 1 parametros: Devuelve la ruta del disco duro indicado.
#@return Para 2 parametros: Devuelve la ruta de la particion indicada.
#@exception OG_ERR_FORMAT Formato incorrecto.
#@exception OG_ERR_NOTFOUND Dispositivo no detectado.
#*/ ##
def ogDiskToDev (arg_disk=None, arg_part=None):
CACHEFILE = "/var/cache/disks.cfg"
try:
if arg_part != None:
arg_part = int (arg_part)
except:
ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"{arg_disk} {arg_part}")
return
try:
if arg_disk != None:
arg_disk = int (arg_disk)
except:
ogRaiseError([], ogGlobals.OG_ERR_FORMAT, arg_disk)
return
# Si se solicita, mostrar ayuda.
#if len(args) == 1 and args[0] == "help":
# ogHelp('ogDiskToDev', 'ogDiskToDev int_ndisk [int_npartition]',
# 'ogDiskToDev => /dev/sda /dev/sdb',
# 'ogDiskToDev 1 => /dev/sda',
# 'ogDiskToDev 1 1 => /dev/sda1')
# return
# Borrar fichero de caché de configuración si hay cambios en las particiones.
proc_partitions = Path ('/proc/partitions').read_text()
tmp_partitions = Path ('/tmp/.partitions').read_text() if os.path.exists ('/tmp/.partitions') else ''
if proc_partitions != tmp_partitions:
# Guardar copia de las particiones definidas para comprobar cambios.
shutil.copy2('/proc/partitions', '/tmp/.partitions')
try:
ogEcho ([], 'info', f'removing cachefile')
os.remove(CACHEFILE)
except FileNotFoundError:
pass
# Si existe una correspondencia con disco/dispositivo en el caché; mostrarlo y salir.
if arg_disk and os.path.exists (CACHEFILE):
with open(CACHEFILE, 'r') as f:
args_joined = ' '.join (map (str, filter (None, [arg_disk,arg_part])))
ogEcho ([], 'info', f'args_joined ({args_joined})')
for line in f:
parts = line.strip().split(':')
if len(parts) == 2 and parts[0] == args_joined:
return parts[1]
# Continuar para detectar nuevos dispositivos.
ALLDISKS = _getAllDisks()
VOLGROUPS = _getAllVolGroups()
ALLDISKS += VOLGROUPS
MPATH, ALLDISKS_to_remove =_getMPath()
ALLDISKS = list (set (ALLDISKS) - set (ALLDISKS_to_remove))
ZFSVOLS = _getAllZFSVols()
ALLDISKS += ZFSVOLS
print (f'ALLDISKS ({ALLDISKS}) VOLGROUPS ({VOLGROUPS}) MPATH ({MPATH}) ALLDISKS_to_remove ({ALLDISKS_to_remove}) ZFSVOLS ({ZFSVOLS})')
# No params: return all disks
if arg_disk is None:
return " ".join(ALLDISKS)
# arg_disk is set: return it
if arg_part is None:
if arg_disk > len (ALLDISKS):
ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
return return
disk = ALLDISKS[int(args[0])-1] disk = ALLDISKS[arg_disk-1]
# Error si el fichero no existe.
if not os.path.exists(disk): if not os.path.exists(disk):
ogRaiseError(OG_ERR_NOTFOUND, args[0]) ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
return return
# Actualizar caché de configuración y mostrar dispositivo. # Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f: with open(CACHEFILE, 'a') as f:
f.write(f"{args[0]}:{disk}\n") f.write(f"{arg_disk}:{disk}\n")
print(disk) return disk
elif len(args) == 2:
# Error si los 2 parámetros no son números positivos. # arg_disk and arg_part are set: there are several possibilities
if not args[0].isdigit() or int(args[0]) <= 0 or not args[1].isdigit() or int(args[1]) <= 0: disk = ALLDISKS[arg_disk-1]
ogRaiseError(OG_ERR_FORMAT, f"{args[0]} {args[1]}") if not os.path.exists(disk):
return ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
disk = ALLDISKS[int(args[0])-1]
# Error si el fichero no existe.
if not os.path.exists(disk):
ogRaiseError(OG_ERR_NOTFOUND, args[0])
return
part = f"{disk}{args[1]}"
# Comprobar si es partición.
if os.path.exists(part):
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{args[0]} {args[1]}:{part}\n")
print(part)
else:
# Comprobar si RAID o Multipath (tener en cuenta enlace simbólico).
part = f"{disk}p{args[1]}"
if os.path.exists(part) and os.stat(part, follow_symlinks=True).st_mode[0] == "b":
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{args[0]} {args[1]}:{part}\n")
print(part)
else:
part = ""
# Comprobar si volumen lógico.
if disk in VOLGROUPS:
lvscan = subprocess.getoutput("lvscan -a 2>/dev/null").splitlines()
for line in lvscan:
parts = line.split("'")
if parts[1].startswith(f"{disk}/") and i == int(args[1]):
part = parts[1]
break
i += 1
# Comprobar si volumen ZFS que puede ser montado.
if disk in ZFSVOLS:
subprocess.run(["zpool", "import", "-f", "-R", "/mnt", "-N", "-a"], stderr=subprocess.DEVNULL)
zpool = subprocess.getoutput(f"blkid -s LABEL -o value {disk}")
zfs_list = subprocess.getoutput(f"zfs list -Hp -o name,canmount,mountpoint -r {zpool}").splitlines()
for line in zfs_list:
parts = line.split()
if parts[1] == "on" and parts[2] != "none":
if i == int(args[1]):
part = parts[0]
break
i += 1
# Salir si no se encuentra dispositivo.
if not part:
ogRaiseError(OG_ERR_NOTFOUND, f"{args[0]} {args[1]}")
return
# Devolver camino al dispositivo.
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{args[0]} {args[1]}:{part}\n")
print(part)
else:
ogRaiseError(OG_ERR_FORMAT)
return return
# Plain partition
part = f"{disk}{arg_part}"
if os.path.exists(part):
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{arg_disk} {arg_part}:{part}\n")
return part
# RAID/Multipath (tener en cuenta enlace simbólico).
part = f"{disk}p{arg_part}"
if os.path.exists(part) and stat.S_ISBLK (os.stat (part, follow_symlinks=True).st_mode):
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{arg_disk} {arg_part}:{part}\n")
return part
part = ""
# Logical volume
if disk in VOLGROUPS:
lvscan = subprocess.run (['lvscan', '-a'], capture_output=True, text=True).stdout.splitlines()
i = 0
for line in lvscan:
parts = line.split("'")
if parts[1].startswith(f"{disk}/") and i == arg_part:
part = parts[1]
break
i += 1
# ZFS volume
if disk in ZFSVOLS:
subprocess.run(["zpool", "import", "-f", "-R", "/mnt", "-N", "-a"])
zpool = subprocess.run(['blkid', '-s', 'LABEL', '-o', 'value', disk], capture_output=True, text=True).stdout
zfs_list = subprocess.run(['zfs', 'list', '-Hp', '-o', 'name,canmount,mountpoint', '-r', zpool], capture_output=True, text=True).stdout.splitlines()
i = 0
for line in zfs_list:
parts = line.split()
if parts[1] == "on" and parts[2] != "none":
if i == int(args[1]):
part = parts[0]
break
i += 1
if not part:
ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, f"{arg_disk} {arg_part}")
return
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{arg_disk} {arg_part}:{part}\n")
return part
def ogGetDiskSize(*args): def ogGetDiskSize(*args):
# Variables locales # Variables locales
DISK = SIZE = None DISK = SIZE = None