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 shutil
import os
import stat
from pathlib import Path
from CacheLib import *
from FileSystemLib import *
@ -365,142 +367,187 @@ def ogDevToDisk(dev):
ogRaiseError(OG_ERR_NOTFOUND, dev)
return OG_ERR_NOTFOUND
def ogDiskToDev(*args):
# Variables locales
CACHEFILE = "/var/cache/disks.cfg"
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()
def _getAllDisks():
ret = []
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()
for line in all_disks:
parts = line.split()
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.
VOLGROUPS = subprocess.getoutput("vgs -a --noheadings 2>/dev/null").splitlines()
VOLGROUPS = [f"/dev/{line.split()[0]}" for line in VOLGROUPS]
def _getAllVolGroups():
vgs = subprocess.run(['vgs', '-a', '--noheadings'], capture_output=True, text=True).stdout.splitlines()
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:
MPATH = subprocess.getoutput("multipath -l -v 1 2>/dev/null").splitlines()
MPATH = [f"/dev/mapper/{line.split()[0]}" for line in MPATH]
# Quitar de la lista los discos que forman parte de Multipath.
mpath = subprocess.run(['multipath', '-l', '-v', '1'], capture_output=True, text=True).stdout.splitlines()
ret = [f"/dev/mapper/{line.split()[0]}" for line in mpath]
for line in subprocess.getoutput("multipath -ll").splitlines():
if line.split()[5] == "ready":
disk = f"/dev/{line.split()[2]}"
if disk in ALLDISKS:
ALLDISKS.remove(disk)
alldisks2remove.append (f"/dev/{line.split()[2]}")
except FileNotFoundError:
pass
except subprocess.CalledProcessError:
MPATH = []
pass
return ret, alldisks2remove
# Detectar volúmenes ZFS.
ZFSVOLS = subprocess.getoutput("blkid").splitlines()
ZFSVOLS = [line.split(":")[0] for line in ZFSVOLS if "zfs" in line]
def _getAllZFSVols():
zfsvols = subprocess.run(['blkid'], capture_output=True, text=True).stdout.splitlines()
return [line.split(":")[0] for line in zfsvols if "zfs" in line]
# Mostrar salidas segun el número de parametros.
if len(args) == 0:
print(" ".join(ALLDISKS))
elif len(args) == 1:
# Error si el parámetro no es un número positivo.
if not args[0].isdigit() or int(args[0]) <= 0:
ogRaiseError(OG_ERR_FORMAT, args[0])
#/**
# ogDiskToDev [int_ndisk [int_npartition]]
#@brief Devuelve la equivalencia entre el nº de orden del dispositivo (dicso o partición) y el nombre de fichero de dispositivo correspondiente.
#@param int_ndisk nº de orden del disco
#@param int_npartition nº de orden de la partición
#@return Para 0 parametros: Devuelve los nombres de ficheros de los dispositivos sata/ata/usb linux encontrados.
#@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
disk = ALLDISKS[int(args[0])-1]
# Error si el fichero no existe.
disk = ALLDISKS[arg_disk-1]
if not os.path.exists(disk):
ogRaiseError(OG_ERR_NOTFOUND, args[0])
ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
return
# Actualizar caché de configuración y mostrar dispositivo.
with open(CACHEFILE, 'a') as f:
f.write(f"{args[0]}:{disk}\n")
print(disk)
elif len(args) == 2:
# Error si los 2 parámetros no son números positivos.
if not args[0].isdigit() or int(args[0]) <= 0 or not args[1].isdigit() or int(args[1]) <= 0:
ogRaiseError(OG_ERR_FORMAT, f"{args[0]} {args[1]}")
return
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)
f.write(f"{arg_disk}:{disk}\n")
return disk
# arg_disk and arg_part are set: there are several possibilities
disk = ALLDISKS[arg_disk-1]
if not os.path.exists(disk):
ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
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):
# Variables locales
DISK = SIZE = None