1427 lines
50 KiB
Python
1427 lines
50 KiB
Python
#/**
|
|
#@file DiskLib.py
|
|
#@brief Librería o clase Disk
|
|
#@class Disk
|
|
#@brief Funciones para gestión de discos y particiones.
|
|
#@warning License: GNU GPLv3+
|
|
#*/
|
|
|
|
import filecmp
|
|
import subprocess
|
|
import shutil
|
|
import os
|
|
import re
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
import ogGlobals
|
|
import SystemLib
|
|
import CacheLib
|
|
import FileSystemLib
|
|
import InventoryLib
|
|
|
|
# Función ficticia para lanzar parted con timeout, evitando cuelgues del programa.
|
|
def parted(*args):
|
|
parted_path = shutil.which("parted")
|
|
if parted_path:
|
|
try:
|
|
result = subprocess.run(
|
|
[parted_path] + list(args),
|
|
timeout=3,
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
return result.stdout
|
|
except subprocess.TimeoutExpired:
|
|
return "Error: Command 'parted' timed out"
|
|
else:
|
|
return "Error: 'parted' command not found"
|
|
|
|
|
|
#/**
|
|
# ogCreatePartitions int_ndisk str_parttype:int_partsize ...
|
|
#@brief Define el conjunto de particiones de un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param str_parttype mnemónico del tipo de partición
|
|
#@param int_partsize tamaño de la partición (en KB)
|
|
#@return (nada, por determinar)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o partición no detectado (no es un dispositivo).
|
|
#@exception OG_ERR_PARTITION error en partición o en tabla de particiones.
|
|
#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize
|
|
#@attention Pueden definirse particiones vacías de tipo \c EMPTY
|
|
#@attention No puede definirse partición de cache y no se modifica si existe.
|
|
#@note Requisitos: sfdisk, parted, partprobe, awk
|
|
#@todo Definir atributos (arranque, oculta) y tamaños en MB, GB, etc.
|
|
#*/ ##
|
|
def ogCreatePartitions(*args):
|
|
# Variables locales
|
|
ND = DISK = PTTYPE = PART = SECTORS = START = SIZE = TYPE = CACHEPART = None
|
|
IODISCO = IOSIZE = CACHESIZE = EXTSTART = EXTSIZE = NVME_PREFIX = tmpsfdisk = None
|
|
|
|
# Si se solicita, mostrar ayuda.
|
|
if len(args) == 1 and args[0] == "help":
|
|
SystemLib.ogHelp('ogCreatePartitions', 'ogCreatePartitions int_ndisk str_parttype:int_partsize ...',
|
|
'ogCreatePartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000')
|
|
return
|
|
|
|
# Error si no se reciben al menos 2 parámetros.
|
|
if len(args) < 2:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT)
|
|
return
|
|
|
|
# Nº total de sectores, para evitar desbordamiento (evitar redondeo).
|
|
ND = args[0]
|
|
DISK = ogDiskToDev(ND)
|
|
if DISK is None:
|
|
return
|
|
|
|
PTTYPE = ogGetPartitionTableType(ND) or "MSDOS" # Por defecto para discos vacíos.
|
|
|
|
if PTTYPE == "GPT":
|
|
ogCreateGptPartitions(*args)
|
|
return
|
|
elif PTTYPE != "MSDOS":
|
|
SystemLib.ogRaiseError(OG_ERR_PARTITION, PTTYPE)
|
|
return
|
|
|
|
SECTORS = ogGetLastSector(ND)
|
|
|
|
# Se recalcula el nº de sectores del disco 1, si existe partición de caché.
|
|
CACHEPART = CacheLib.ogFindCache()
|
|
if CACHEPART and ND == CACHEPART.split()[0]:
|
|
CACHESIZE = int(CacheLib.ogGetCacheSize()) * 2
|
|
|
|
# Sector de inicio (la partición 1 empieza en el sector 63).
|
|
IODISCO = ogDiskToDev(ND)
|
|
IOSIZE = subprocess.getoutput(f"fdisk -l {IODISCO} | awk '/I\\/O/ {{print $4}}'")
|
|
|
|
if IOSIZE == "4096":
|
|
START = 4096
|
|
SECTORS -= 8192
|
|
if CACHESIZE:
|
|
SECTORS = SECTORS - CACHESIZE + 2048 - (SECTORS - CACHESIZE) % 2048 - 1
|
|
else:
|
|
START = 63
|
|
if CACHESIZE:
|
|
SECTORS -= CACHESIZE
|
|
|
|
PART = 1
|
|
|
|
# Fichero temporal de entrada para "sfdisk"
|
|
tmpsfdisk = f"/tmp/sfdisk{os.getpid()}"
|
|
try:
|
|
with open(tmpsfdisk, 'w') as f:
|
|
f.write("unit: sectors\n\n")
|
|
|
|
NVME_PREFIX = "p" if "nvme" in DISK else ""
|
|
|
|
# Generar fichero de entrada para "sfdisk" con las particiones.
|
|
args = args[1:] # Shift
|
|
while args:
|
|
# Conservar los datos de la partición de caché.
|
|
if f"{ND} {PART}" == CACHEPART and CACHESIZE:
|
|
with open(tmpsfdisk, 'a') as f:
|
|
f.write(f"{DISK}{NVME_PREFIX}{PART} : start={SECTORS + 1}, size={CACHESIZE}, Id=ca\n")
|
|
PART += 1
|
|
|
|
# Leer formato de cada parámetro - Tipo:Tamaño
|
|
TYPE = args[0].split(":")[0]
|
|
SIZE = int(args[0].split(":")[1])
|
|
|
|
# Obtener identificador de tipo de partición válido.
|
|
ID = ogTypeToId(TYPE, "MSDOS")
|
|
with open(tmpsfdisk, 'a') as f:
|
|
f.write(f"{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, Id={ID}\n")
|
|
START += SIZE
|
|
SECTORS -= SIZE
|
|
PART += 1
|
|
args = args[1:]
|
|
|
|
# Ejecutar "sfdisk" con el fichero temporal.
|
|
subprocess.run(["sfdisk", DISK], input=open(tmpsfdisk, 'r').read(), text=True)
|
|
subprocess.run(["partprobe", DISK])
|
|
|
|
finally:
|
|
# Eliminar fichero temporal.
|
|
if os.path.exists(tmpsfdisk):
|
|
os.remove(tmpsfdisk)
|
|
|
|
if CACHESIZE:
|
|
CacheLib.ogMountCache()
|
|
return 0
|
|
|
|
|
|
#/**
|
|
# ogCreateGptPartitions int_ndisk str_parttype:int_partsize ...
|
|
#@brief Define el conjunto de particiones de un disco GPT
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param str_parttype mnemónico del tipo de partición
|
|
#@param int_partsize tamaño de la partición (en KB)
|
|
#@return (nada, por determinar)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o partición no detectado (no es un dispositivo).
|
|
#@exception OG_ERR_PARTITION error en partición o en tabla de particiones.
|
|
#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize
|
|
#@attention Pueden definirse particiones vacías de tipo \c EMPTY
|
|
#@attention No puede definirse partición de caché y no se modifica si existe.
|
|
#@note Requisitos: sfdisk, parted, partprobe, awk
|
|
#@todo Definir atributos (arranque, oculta) y tamaños en MB, GB, etc.
|
|
#*/ ##
|
|
def ogCreateGptPartitions(*args):
|
|
# Variables locales
|
|
ND = DISK = PART = SECTORS = ALIGN = START = SIZE = TYPE = CACHEPART = CACHESIZE = DELOPTIONS = OPTIONS = None
|
|
|
|
# Si se solicita, mostrar ayuda.
|
|
if len(args) == 1 and args[0] == "help":
|
|
SystemLib.ogHelp('ogCreateGptPartitions', 'ogCreateGptPartitions int_ndisk str_parttype:int_partsize ...',
|
|
'ogCreateGptPartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000')
|
|
return
|
|
|
|
# Error si no se reciben menos de 2 parámetros.
|
|
if len(args) < 2:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT)
|
|
return
|
|
|
|
# Nº total de sectores, para evitar desbordamiento (evitar redondeo).
|
|
ND = args[0]
|
|
DISK = ogDiskToDev(ND)
|
|
if DISK is None:
|
|
return
|
|
|
|
# Se calcula el ultimo sector del disco (total de sectores usables)
|
|
SECTORS = ogGetLastSector(ND)
|
|
|
|
# Se recalcula el nº de sectores del disco si existe partición de caché.
|
|
CACHEPART = CacheLib.ogFindCache()
|
|
if ND == CACHEPART.split()[0]:
|
|
CACHESIZE = int(ogGetCacheSize()) * 2
|
|
if CACHESIZE:
|
|
SECTORS -= CACHESIZE
|
|
|
|
# Si el disco es GPT empieza en el sector 2048 por defecto, pero podria cambiarse
|
|
ALIGN = subprocess.getoutput(f"sgdisk -D {DISK} 2>/dev/null")
|
|
START = ALIGN
|
|
PART = 1
|
|
|
|
# Leer parámetros con definición de particionado.
|
|
args = args[1:] # Shift
|
|
while args:
|
|
# Si PART es la cache, nos la saltamos y seguimos con el siguiente numero para conservar los datos de la partición de caché.
|
|
if f"{ND} {PART}" == CACHEPART and CACHESIZE:
|
|
PART += 1
|
|
|
|
# Leer formato de cada parámetro - Tipo:Tamaño
|
|
TYPE = args[0].split(":")[0]
|
|
SIZE = int(args[0].split(":")[1])
|
|
|
|
# Error si la partición es extendida (no válida en discos GPT).
|
|
if TYPE == "EXTENDED":
|
|
SystemLib.ogRaiseError(OG_ERR_PARTITION, "EXTENDED")
|
|
return
|
|
|
|
# Comprobar si existe la particion actual, capturamos su tamaño para ver si cambio o no
|
|
PARTSIZE = ogGetPartitionSize(ND, PART)
|
|
# En sgdisk no se pueden redimensionar las particiones, es necesario borrarlas y volver a crealas
|
|
if PARTSIZE:
|
|
DELOPTIONS += f" -d{PART}"
|
|
|
|
# Creamos la particion
|
|
# Obtener identificador de tipo de partición válido.
|
|
ID = ogTypeToId(TYPE, "GPT")
|
|
if TYPE != "CACHE" and ID:
|
|
OPTIONS += f" -n{PART}:{START}:+{SIZE} -t{PART}:{ID} "
|
|
|
|
START += SIZE
|
|
|
|
# Error si se supera el nº total de sectores.
|
|
if START > SECTORS:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT, f"{START//2} > {SECTORS//2}")
|
|
return
|
|
|
|
PART += 1
|
|
args = args[1:]
|
|
|
|
# Desmontar los sistemas de archivos del disco antes de realizar las operaciones.
|
|
ogUnmountAll(ND)
|
|
if CACHESIZE:
|
|
CacheLib.ogUnmountCache()
|
|
|
|
# Si la tabla de particiones no es valida, volver a generarla.
|
|
ogCreatePartitionTable(ND)
|
|
|
|
# Definir particiones y notificar al kernel.
|
|
# Borramos primero las particiones y luego creamos las nuevas
|
|
subprocess.run(["sgdisk"] + DELOPTIONS.split() + OPTIONS.split() + [DISK], stderr=subprocess.DEVNULL)
|
|
subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL)
|
|
|
|
if CACHESIZE:
|
|
CacheLib.ogMountCache()
|
|
return 0
|
|
|
|
|
|
#/**
|
|
# ogCreatePartitionTable int_ndisk [str_tabletype]
|
|
#@brief Genera una tabla de particiones en caso de que no sea valida, si es valida no hace nada.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param str_tabletype tipo de tabla de particiones (opcional)
|
|
#@return (por determinar)
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
|
|
#@note tabletype: { MSDOS, GPT }, MSDOS por defecto
|
|
#@note Requisitos: fdisk, gdisk, parted
|
|
#*/ ##
|
|
def ogCreatePartitionTable (disk, createptt=None):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
|
|
pttype = ogGetPartitionTableType (disk) or 'MSDOS' # Por defecto para discos vacíos.
|
|
createptt = createptt or pttype
|
|
CREATE = None
|
|
|
|
# Si la tabla actual y la que se indica son iguales, se comprueba si hay que regenerarla.
|
|
if createptt == pttype:
|
|
if 'GPT' == pttype:
|
|
try:
|
|
result = subprocess.run (['sgdisk', '-p', DISK])
|
|
if result.returncode:
|
|
CREATE = 'GPT'
|
|
except subprocess.CalledProcessError:
|
|
CREATE = 'GPT'
|
|
elif 'MSDOS' == pttype:
|
|
try:
|
|
result = subprocess.run (['parted', '-s', DISK, 'print'])
|
|
if result.returncode:
|
|
CREATE = 'MSDOS'
|
|
except subprocess.CalledProcessError:
|
|
CREATE = 'MSDOS'
|
|
else:
|
|
CREATE = createptt.upper()
|
|
|
|
# Dependiendo del valor de CREATE, creamos la tabla de particiones en cada caso.
|
|
if 'GPT' == CREATE:
|
|
if 'MSDOS' == pttype:
|
|
subprocess.run (['sgdisk', '-go', DISK])
|
|
else:
|
|
subprocess.run (['gdisk', DISK], input='2\nw\nY\n', text=True)
|
|
subprocess.run (['partprobe', DISK])
|
|
elif 'MSDOS' == CREATE:
|
|
if 'GPT' == pttype:
|
|
subprocess.run (['sgdisk', '-Z', DISK])
|
|
# Crear y borrar una partición para que la tabla se genere bien.
|
|
subprocess.run (['fdisk', DISK], input='o\nn\np\n\n\n\nd\n\nw', text=True)
|
|
subprocess.run (['partprobe', DISK])
|
|
|
|
return None
|
|
|
|
|
|
#/**
|
|
# ogDeletePartitionTable ndisk
|
|
#@brief Borra la tabla de particiones del disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return la informacion propia del fdisk
|
|
#*/ ##
|
|
def ogDeletePartitionTable (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
|
|
PTTYPE = ogGetPartitionTableType (disk)
|
|
if 'GPT' == PTTYPE:
|
|
subprocess.run (['sgdisk', '--clear', DISK])
|
|
elif 'MSDOS' == PTTYPE:
|
|
subprocess.run (['fdisk', DISK], input='o\nw', text=True)
|
|
return
|
|
|
|
#/**
|
|
# ogDevToDisk path_device | LABEL="str_label" | UUID="str_uuid"
|
|
#@brief Devuelve el nº de orden de dicso (y partición) correspondiente al nombre de fichero de dispositivo o a la etiqueta o UUID del sistema de archivos asociado.
|
|
#@param path_device Camino del fichero de dispositivo.
|
|
#@param str_label etiqueta de sistema de archivos.
|
|
#@param str_uuid UUID de sistema de archivos.
|
|
#@return int_ndisk (para dispositivo de disco)
|
|
#@return int_ndisk int_npartition (para dispositivo de partición).
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Dispositivo no detectado.
|
|
#@note Solo se acepta en cada llamada 1 de los 3 tipos de parámetros.
|
|
#*/ ##
|
|
def ogDevToDisk(arg_dev):
|
|
CACHEFILE = "/var/cache/disks.cfg"
|
|
|
|
if '=' in arg_dev:
|
|
# arg_dev is "FOO=bar"
|
|
cmd = None
|
|
if arg_dev.startswith("LABEL="): cmd = ['blkid', '-L', arg_dev[6:]]
|
|
elif arg_dev.startswith("PARTLABEL="): cmd = ['realpath', '/dev/disk/by-partlabel/'+arg_dev[11:]]
|
|
elif arg_dev.startswith("PARTUUID="): cmd = ['realpath', '/dev/disk/by-partuuid/'+arg_dev[10:]]
|
|
elif arg_dev.startswith("UUID="): cmd = ['blkid', '-U', arg_dev[5:]]
|
|
if not cmd:
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, arg_dev)
|
|
return
|
|
DEV = subprocess.run (cmd, capture_output=True, text=True).stdout.strip()
|
|
else:
|
|
# arg_dev is "/dev/something"
|
|
DEV = arg_dev
|
|
|
|
if not os.path.exists(DEV):
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev)
|
|
return
|
|
|
|
# Error si no es fichero de bloques o directorio (para LVM).
|
|
m = os.stat (DEV, follow_symlinks=True).st_mode
|
|
if not stat.S_ISBLK (m) and not stat.S_ISDIR (m):
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev)
|
|
return
|
|
|
|
# Buscar en fichero de caché de discos.
|
|
PART = None
|
|
if os.path.exists(CACHEFILE):
|
|
with open(CACHEFILE, 'r') as f:
|
|
for line in f:
|
|
parts = line.strip().split(':')
|
|
if len(parts) == 2 and parts[1] == DEV:
|
|
PART = parts[0]
|
|
break
|
|
|
|
if PART: return PART
|
|
|
|
# Si no se encuentra, procesa todos los discos para devolver su nº de orden y de partición.
|
|
disks = ogDiskToDev()
|
|
n = 1
|
|
for d in disks:
|
|
NVME_PREFIX = "p" if "nvme" in d else ""
|
|
if DEV.startswith(d):
|
|
return f"{n} {DEV[len(d) + len(NVME_PREFIX):]}"
|
|
n += 1
|
|
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_dev)
|
|
return
|
|
|
|
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":
|
|
parts[0].replace ('!', '/')
|
|
ret.append(f"/dev/{parts[0]}")
|
|
return ret
|
|
|
|
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
|
|
|
|
def _getMPath():
|
|
ret = alldisks2remove = []
|
|
try:
|
|
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":
|
|
alldisks2remove.append (f"/dev/{line.split()[2]}")
|
|
except FileNotFoundError:
|
|
pass
|
|
except subprocess.CalledProcessError:
|
|
pass
|
|
return ret, alldisks2remove
|
|
|
|
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]
|
|
|
|
#/**
|
|
# 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:
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, f"{arg_disk} {arg_part}")
|
|
return
|
|
|
|
try:
|
|
if arg_disk != None:
|
|
arg_disk = int (arg_disk)
|
|
except:
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_FORMAT, arg_disk)
|
|
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:
|
|
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])))
|
|
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()
|
|
for d in ALLDISKS_to_remove:
|
|
if d in ALLDISKS: ALLDISKS.remove (d)
|
|
|
|
ZFSVOLS = _getAllZFSVols()
|
|
ALLDISKS += ZFSVOLS
|
|
|
|
# No params: return all disks
|
|
if arg_disk is None:
|
|
return ALLDISKS
|
|
|
|
# arg_disk is set: return it
|
|
if arg_part is None:
|
|
if arg_disk > len (ALLDISKS):
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
|
|
return
|
|
disk = ALLDISKS[arg_disk-1]
|
|
if not os.path.exists(disk):
|
|
SystemLib.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"{arg_disk}:{disk}\n")
|
|
return disk
|
|
|
|
# arg_disk and arg_part are set: there are several possibilities
|
|
if arg_disk > len (ALLDISKS):
|
|
SystemLib.ogRaiseError([], ogGlobals.OG_ERR_NOTFOUND, arg_disk)
|
|
return
|
|
disk = ALLDISKS[arg_disk-1]
|
|
if not os.path.exists(disk):
|
|
SystemLib.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:
|
|
SystemLib.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
|
|
|
|
|
|
#/**
|
|
# ogGetDiskSize int_ndisk
|
|
#@brief Muestra el tamaño en KB de un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return int_size - Tamaño en KB del disco.
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@note Requisitos: sfdisk, awk
|
|
#*/ ##
|
|
def ogGetDiskSize (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
|
|
bn = os.path.basename (DISK)
|
|
SIZE = None
|
|
with open ('/proc/partitions', 'r') as fd:
|
|
while True:
|
|
l = fd.readline()
|
|
if not l: break
|
|
items = l.split()
|
|
if len(items) < 4: continue
|
|
if items[3] == bn:
|
|
SIZE = int (items[2])
|
|
break
|
|
if not SIZE:
|
|
vgs_out = subprocess.run (['vgs', '--noheadings', '--units=B', '-o', 'dev_size', DISK], capture_output=True, text=True).stdout
|
|
items = vgs_out.split()
|
|
SIZE = int (items[0]) // 1024
|
|
|
|
return SIZE
|
|
|
|
|
|
#/**
|
|
# ogGetDiskType path_device
|
|
#@brief Muestra el tipo de disco (real, RAID, meta-disco, USB, etc.).
|
|
#@param path_device Dispositivo
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco no detectado o no es un dispositivo de bloques.
|
|
#@note Requisitos: udevadm
|
|
#*/ ##
|
|
def ogGetDiskType (dev):
|
|
DEV = os.path.basename (dev)
|
|
|
|
bn = os.path.basename (DEV)
|
|
MAJOR = None
|
|
with open ('/proc/partitions', 'r') as fd:
|
|
while True:
|
|
l = fd.readline()
|
|
if not l: break
|
|
items = l.split()
|
|
if len(items) < 4: continue
|
|
if items[3] == bn:
|
|
MAJOR = items[0]
|
|
break
|
|
|
|
TYPE = None
|
|
with open ('/proc/devices', 'r') as fd:
|
|
within_block_section = False
|
|
while True:
|
|
l = fd.readline()
|
|
if not l: break
|
|
|
|
if 'Block' in l:
|
|
within_block_section = True
|
|
continue
|
|
if not within_block_section:
|
|
continue
|
|
|
|
items = l.split()
|
|
if len(items) < 2: continue
|
|
if items[0] == MAJOR:
|
|
TYPE = items[1].upper()
|
|
break
|
|
|
|
# Devolver mnemónico del driver de dispositivo.
|
|
if 'SD' == TYPE:
|
|
TYPE = 'DISK'
|
|
udevadm_out = subprocess.run (['udevadm', 'info', '-q', 'property', dev], capture_output=True, text=True).stdout
|
|
for l in udevadm_out.splitlines():
|
|
if 'ID_BUS=usb' == l[0:10]:
|
|
TYPE = 'USB'
|
|
elif 'BLKEXT' == TYPE:
|
|
TYPE = 'NVM'
|
|
elif 'SR' == TYPE or TYPE.startswith ('IDE'):
|
|
TYPE = 'CDROM' # FIXME Comprobar discos IDE.
|
|
elif 'MD' == TYPE or TYPE.startswith ('CCISS'):
|
|
TYPE = 'RAID'
|
|
elif 'DEVICE-MAPPER' == TYPE:
|
|
TYPE = 'MAPPER' # FIXME Comprobar LVM y RAID.
|
|
|
|
return TYPE
|
|
|
|
|
|
#/**
|
|
# ogGetEsp
|
|
#@brief Devuelve números de disco y partición para la partición EFI (ESP).
|
|
#*/ ##
|
|
def ogGetEsp():
|
|
devices = subprocess.run (['blkid', '-o', 'device'], capture_output=True, text=True).stdout.splitlines()
|
|
devices.sort()
|
|
for d in devices:
|
|
# Previene error para /dev/loop0
|
|
PART = ogDevToDisk (d)
|
|
if not PART: continue
|
|
|
|
# En discos NVMe blkid devuelve una salida del tipo:
|
|
# >/dev/loop0
|
|
# >/dev/nvme0n1
|
|
# >/dev/nvme0n1p1
|
|
# al analizar la particion nvme0n1, PART solo tiene un argumento y hace que ogGetPartitionId lance un error
|
|
if len (PART) > 1:
|
|
disk, par = PART.split()
|
|
if ogGetPartitionId (disk, par) == ogTypeToId ('EFI', 'GPT'):
|
|
return PART
|
|
return None
|
|
|
|
|
|
#/**
|
|
# ogGetLastSector int_ndisk [int_npart]
|
|
#@brief Devuelve el último sector usable del disco o de una partición.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npart nº de orden de la partición (opcional)
|
|
#@return Último sector usable.
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o partición no corresponde con un dispositivo.
|
|
#@note Requisitos: sfdisk, sgdisk
|
|
#*/ ##
|
|
def ogGetLastSector (disk, par=None):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
env = os.environ
|
|
env['LANG'] = 'C'
|
|
last = None
|
|
if par:
|
|
PART = ogDiskToDev (disk, par)
|
|
if not PART: return None
|
|
sgdisk_out = subprocess.run (['sgdisk', '-p', DISK], env=env, capture_output=True, text=True).stdout
|
|
for l in sgdisk_out.splitlines():
|
|
items = l.split()
|
|
if len(items) < 3: continue
|
|
if str (par) != items[0]: continue
|
|
last = int (items[2])
|
|
break
|
|
|
|
else:
|
|
sgdisk_out = subprocess.run (['sgdisk', '-p', DISK], env=env, capture_output=True, text=True).stdout
|
|
for l in sgdisk_out.splitlines():
|
|
if 'last usable sector' not in l: continue
|
|
items = l.split()
|
|
last = int (items[-1])
|
|
|
|
return last
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionActive int_ndisk
|
|
#@brief Muestra que particion de un disco esta marcada como de activa.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
|
|
#@note Requisitos: parted
|
|
#@todo Queda definir formato para atributos (arranque, oculta, ...).
|
|
#*/ ##
|
|
def ogGetPartitionActive (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if DISK is None: return
|
|
|
|
if 'LANG' in os.environ:
|
|
lang = os.environ['LANG']
|
|
|
|
ret = None
|
|
os.environ['LANG'] = 'C'
|
|
lines = subprocess.run (['parted', '--script', '--machine', DISK, 'print'], capture_output=True, text=True).stdout.splitlines()
|
|
for line in lines:
|
|
parts = line.split (':')
|
|
if len (parts) < 6: continue
|
|
if 'boot' in parts[6]:
|
|
ret = parts[0]
|
|
break
|
|
|
|
if lang is None:
|
|
del os.environ['LANG']
|
|
else:
|
|
os.environ['LAMG'] = lang
|
|
|
|
return ret
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionId int_ndisk int_npartition
|
|
#@brief Devuelve el mnemónico con el tipo de partición.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return Identificador de tipo de partición.
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o partición no corresponde con un dispositivo.
|
|
#@note Requisitos: sfdisk
|
|
#*/ ##
|
|
def ogGetPartitionId (disk, par):
|
|
DISK = ogDiskToDev (disk)
|
|
if DISK is None: return
|
|
fsid = None
|
|
|
|
pttype = ogGetPartitionTableType (disk)
|
|
if 'GPT' == pttype:
|
|
lines = subprocess.run (['sgdisk', '-p', DISK], capture_output=True, text=True).stdout.splitlines()
|
|
start_index = next (i for i, line in enumerate(lines) if 'Number' in line)
|
|
for l in lines[start_index:]:
|
|
idx, start, end, sz, sz_units, code, *rest = l.split()
|
|
if idx == str(par):
|
|
fsid = code
|
|
break
|
|
if fsid == '8300' and f'{disk} {par}' == CacheLib.ogFindCache():
|
|
fsid = 'CA00'
|
|
elif 'MSDOS' == pttype:
|
|
fsid = subprocess.run (['sfdisk', '--part-type', DISK, par], capture_output=True, text=True).stdout.strip()
|
|
elif 'LVM' == pttype:
|
|
fsid = '10000'
|
|
elif 'ZPOOL' == pttype:
|
|
fsid = '10010'
|
|
|
|
return fsid
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionSize int_ndisk int_npartition
|
|
#@brief Muestra el tamano en KB de una particion determinada.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return int_partsize - Tamaño en KB de la partición.
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@note Requisitos: sfdisk, awk
|
|
#*/ ##
|
|
def ogGetPartitionSize (disk, par):
|
|
PART = ogDiskToDev (disk, par)
|
|
if PART is None: return
|
|
|
|
sz = subprocess.run (['partx', '-gbo', 'SIZE', PART], capture_output=True, text=True).stdout.strip()
|
|
if sz: return int (int (sz) / 1024)
|
|
|
|
sz = subprocess.run (['lvs', '--noheadings', '-o', 'lv_size', '--units', 'k', PART], capture_output=True, text=True).stdout.strip
|
|
if sz: return int (sz)
|
|
|
|
return FileSystemLib.ogGetFsSize (disk, par)
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionsNumber int_ndisk
|
|
#@brief Detecta el numero de particiones del disco duro indicado.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return Devuelve el numero paritiones del disco duro indicado
|
|
#@warning Salidas de errores no determinada
|
|
#@attention Requisitos: parted
|
|
#@note Notas sin especificar
|
|
#*/ ##
|
|
def ogGetPartitionsNumber (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
out = 0
|
|
|
|
pttype = ogGetPartitionTableType (disk)
|
|
if pttype in ['GPT', 'MSDOS']:
|
|
partx_out = subprocess.run (['partx', '-gso', 'NR', DISK], capture_output=True, text=True).stdout
|
|
lines = partx_out.splitlines()
|
|
if len(lines):
|
|
out = lines[-1].strip()
|
|
elif 'LVM' == pttype:
|
|
lvs_out = subprocess.run (['lvs', '--noheadings', DISK], capture_output=True, text=True).stdout
|
|
lines = lvs_out.splitlines()
|
|
out = len (lines)
|
|
elif 'ZPOOL' == pttype:
|
|
if subprocess.run (['zpool', 'list']).returncode:
|
|
subprocess.run (['modprobe', 'zfs'])
|
|
subprocess.run (['zpool', 'import', '-f', '-R', '/mnt', '-N', '-a'])
|
|
blkid = subprocess.run (['blkid', '-s', 'LABEL', '-o', 'value', DISK], capture_output=True, text=True).stdout
|
|
zfs_out = subprocess.run (['zfs', 'list', '-Hp', '-o', 'name,canmount,mountpoint', '-r', blkid], capture_output=True, text=True).stdout
|
|
out = 0
|
|
for l in zfs_out.splitlines():
|
|
items = l.split()
|
|
if len(items) < 3: continue
|
|
if 'on' == items[1] and 'none' != items[2]: out += 1
|
|
|
|
return int (out)
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionTableType int_ndisk
|
|
#@brief Devuelve el tipo de tabla de particiones del disco (GPT o MSDOS)
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return str_tabletype - Tipo de tabla de paritiones
|
|
#@warning Salidas de errores no determinada
|
|
#@note tabletype = { MSDOS, GPT }
|
|
#@note Requisitos: blkid, parted, vgs
|
|
#*/ ##
|
|
def ogGetPartitionTableType (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if DISK is None: return
|
|
|
|
m = os.stat (DISK, follow_symlinks=True).st_mode
|
|
if stat.S_ISBLK (m):
|
|
lines = subprocess.run (['parted', '--script', '--machine', DISK, 'print'], capture_output=True, text=True).stdout.splitlines()
|
|
for l in lines:
|
|
elems = l.split (':')
|
|
if DISK == elems[0]:
|
|
type = elems[5].upper()
|
|
break
|
|
|
|
# Comprobar si es volumen lógico.
|
|
if os.path.isdir (DISK) and 0 == subprocess.run (['vgs', DISK]).returncode:
|
|
type = 'LVM'
|
|
|
|
# Comprobar si es pool de ZFS.
|
|
if not type or 'UNKNOWN' == type:
|
|
if 'zfs' in subprocess.run (['blkid', '-s', 'TYPE', DISK], capture_output=True, text=True).stdout:
|
|
type = 'ZPOOL'
|
|
|
|
return type
|
|
|
|
|
|
#/**
|
|
# ogGetPartitionType int_ndisk int_npartition
|
|
#@brief Devuelve el mnemonico con el tipo de partición.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return Mnemonico
|
|
#@note Mnemonico: valor devuelto por ogIdToType.
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
|
|
#*/ ##
|
|
def ogGetPartitionType (disk, par):
|
|
ID = ogGetPartitionId (disk, par)
|
|
if ID is None: return None
|
|
|
|
return ogIdToType (ID)
|
|
|
|
|
|
#/**
|
|
# ogHidePartition int_ndisk int_npartition
|
|
#@brief Oculta un apartición visible.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@exception OG_ERR_PARTITION tipo de partición no reconocido.
|
|
#*/ ##
|
|
def ogHidePartition (disk, par):
|
|
PART = ogDiskToDev (disk, par)
|
|
if not PART: return None
|
|
|
|
TYPE = ogGetPartitionType (disk, par)
|
|
if 'NTFS' == TYPE: NEWTYPE = 'HNTFS'
|
|
elif 'FAT32' == TYPE: NEWTYPE = 'HFAT32'
|
|
elif 'FAT16' == TYPE: NEWTYPE = 'HFAT16'
|
|
elif 'FAT12' == TYPE: NEWTYPE = 'HFAT12'
|
|
elif 'WINDOWS' == TYPE: NEWTYPE = 'WIN-RESERV'
|
|
else:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE)
|
|
return None
|
|
|
|
ogSetPartitionType (disk, par, NEWTYPE)
|
|
|
|
|
|
#/**
|
|
# ogIdToType int_idpart
|
|
#@brief Devuelve el identificador correspondiente a un tipo de partición.
|
|
#@param int_idpart identificador de tipo de partición.
|
|
#@return str_parttype mnemónico de tipo de partición.
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#*/ ##
|
|
def ogIdToType (ID):
|
|
# Obtener valor hexadecimal de 4 caracteres rellenado con 0 por delante.
|
|
ID = ID.zfill(4).lower()
|
|
|
|
id2type = {
|
|
'0000': 'EMPTY',
|
|
'0001': 'FAT12',
|
|
'0005': 'EXTENDED',
|
|
'000f': 'EXTENDED',
|
|
'0006': 'FAT16',
|
|
'000e': 'FAT16',
|
|
'0007': 'NTFS',
|
|
'000b': 'FAT32',
|
|
'000c': 'FAT32',
|
|
'0011': 'HFAT12',
|
|
'0012': 'COMPAQDIAG',
|
|
'0016': 'HFAT16',
|
|
'001e': 'HFAT16',
|
|
'0017': 'HNTFS',
|
|
'001b': 'HFAT32',
|
|
'001c': 'HFAT32',
|
|
'0042': 'WIN-DYNAMIC',
|
|
'0082': 'LINUX-SWAP',
|
|
'8200': 'LINUX-SWAP',
|
|
'0083': 'LINUX',
|
|
'8300': 'LINUX',
|
|
'008e': 'LINUX-LVM',
|
|
'8E00': 'LINUX-LVM',
|
|
'00a5': 'FREEBSD',
|
|
'a503': 'FREEBSD',
|
|
'00a6': 'OPENBSD',
|
|
'00a7': 'CACHE', # (compatibilidad con Brutalix)
|
|
'00af': 'HFS',
|
|
'af00': 'HFS',
|
|
'00be': 'SOLARIS-BOOT',
|
|
'be00': 'SOLARIS-BOOT',
|
|
'00bf': 'SOLARIS',
|
|
'bf00145': 'SOLARIS',
|
|
'00ca': 'CACHE',
|
|
'ca00': 'CACHE',
|
|
'00da': 'DATA',
|
|
'00ee': 'GPT',
|
|
'00ef': 'EFI',
|
|
'ef00': 'EFI',
|
|
'00fb': 'VMFS',
|
|
'00fd': 'LINUX-RAID',
|
|
'fd00': 'LINUX-RAID',
|
|
'0700': 'WINDOWS',
|
|
'0c01': 'WIN-RESERV',
|
|
'7f00': 'CHROMEOS-KRN',
|
|
'7f01': 'CHROMEOS',
|
|
'7f02': 'CHROMEOS-RESERV',
|
|
'8301': 'LINUX-RESERV',
|
|
'a500': 'FREEBSD-DISK',
|
|
'a501': 'FREEBSD-BOOT',
|
|
'a502': 'FREEBSD-SWAP',
|
|
'ab00': 'HFS-BOOT',
|
|
'af01': 'HFS-RAID',
|
|
'bf02': 'SOLARIS-SWAP',
|
|
'bf03': 'SOLARIS-DISK',
|
|
'ef01': 'MBR',
|
|
'ef02': 'BIOS-BOOT',
|
|
'10000': 'LVM-LV',
|
|
'10010': 'ZFS-VOL',
|
|
}
|
|
if ID in id2type:
|
|
return id2type[ID]
|
|
return 'UNKNOWN'
|
|
|
|
|
|
# ogIsDiskLocked int_ndisk
|
|
#@brief Comprueba si un disco está bloqueado por una operación de uso exclusivo.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return Código de salida: 0 - bloqueado, 1 - sin bloquear o error.
|
|
#@note Los ficheros de bloqueo se localizan en \c /var/lock/dev, siendo \c dev el dispositivo de la partición o de su disco, sustituyendo el carácter "/" por "-".
|
|
#*/ ##
|
|
def ogIsDiskLocked (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return False
|
|
return os.path.isfile (f'/var/lock/lock{DISK.replace("/", "-")}')
|
|
|
|
|
|
#/**
|
|
# ogListPartitions int_ndisk
|
|
#@brief Lista las particiones definidas en un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return str_parttype:int_partsize ...
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@note Requisitos: \c parted \c awk
|
|
#@attention El nº de partición se indica por el orden de los párametros \c parttype:partsize
|
|
#@attention Las tuplas de valores están separadas por espacios.
|
|
#*/ ##
|
|
def ogListPartitions (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
|
|
p = []
|
|
NPARTS = ogGetPartitionsNumber (disk)
|
|
for PART in range (1, NPARTS + 1):
|
|
t = ogGetPartitionType (disk, PART)
|
|
TYPE = ogGetPartitionType (disk, PART) or 'EMPTY'
|
|
SIZE = ogGetPartitionSize (disk, PART) or 0
|
|
p.append (f'{TYPE}:{SIZE}')
|
|
|
|
return p
|
|
|
|
|
|
#/**
|
|
# ogListPrimaryPartitions int_ndisk
|
|
#@brief Metafunción que lista las particiones primarias no vacías de un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@see ogListPartitions
|
|
#*/ ##
|
|
def ogListPrimaryPartitions (disk):
|
|
PTTYPE = ogGetPartitionTableType (disk)
|
|
if not PTTYPE: return None
|
|
|
|
PARTS = ogListPartitions (disk)
|
|
if not PARTS: return None
|
|
|
|
if 'GPT' == PTTYPE:
|
|
res = []
|
|
for idx in range (len(PARTS),0,-1):
|
|
item = PARTS[idx-1]
|
|
if 0==len(res) and 'EMPTY:0' == item: continue
|
|
res.insert (0, item)
|
|
return res
|
|
elif 'MSDOS' == PTTYPE:
|
|
return PARTS[0:4]
|
|
|
|
|
|
#/**
|
|
# ogListLogicalPartitions int_ndisk
|
|
#@brief Metafunción que lista las particiones lógicas de una tabla tipo MSDOS.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@see ogListPartitions
|
|
#*/ ##
|
|
def ogListLogicalPartitions (disk):
|
|
PTTYPE = ogGetPartitionTableType (disk)
|
|
if not PTTYPE: return None
|
|
|
|
PARTS = ogListPartitions (disk)
|
|
if not PARTS: return None
|
|
|
|
return PARTS[4:]
|
|
|
|
|
|
#/**
|
|
# ogLockDisk int_ndisk
|
|
#@brief Genera un fichero de bloqueo para un disco en uso exlusivo.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
|
|
#@note El fichero de bloqueo se localiza en \c /var/lock/disk, siendo \c disk el dispositivo del disco, sustituyendo el carácter "/" por "-".
|
|
#*/ ##
|
|
def ogLockDisk (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
open (f'/var/lock/lock{DISK.replace("/", "-")}', 'a').close()
|
|
|
|
|
|
#/**
|
|
# ogSetPartitionActive int_ndisk int_npartition
|
|
#@brief Establece cual es la partición activa de un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return (nada).
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo.
|
|
#@note Requisitos: parted
|
|
#*/ ##
|
|
def ogSetPartitionActive (disk, par):
|
|
if InventoryLib.ogIsEfiActive():
|
|
SystemLib.ogEcho (['session', 'log'], 'warning', f'EFI: {ogGlobals.lang.MSG_DONTUSE} ogSetPartitionActive')
|
|
return
|
|
|
|
DISK = ogDiskToDev (disk)
|
|
if DISK is None: return
|
|
|
|
PART = ogDiskToDev (disk, par)
|
|
if PART is None: return
|
|
|
|
subprocess.run (["parted", "-s", DISK, "set", par, "boot", "on"])
|
|
return
|
|
|
|
|
|
#/**
|
|
# ogSetPartitionId int_ndisk int_npartition hex_partid
|
|
#@brief Cambia el identificador de la partición.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@param hex_partid identificador de tipo de partición
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo.
|
|
#@exception OG_ERR_OUTOFLIMIT Valor no válido.
|
|
#@exception OG_ERR_PARTITION Error al cambiar el id. de partición.
|
|
#@attention Requisitos: fdisk, sgdisk
|
|
#*/ ##
|
|
def ogSetPartitionId (disk, par, id):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
|
|
PART = ogDiskToDev (disk, par)
|
|
if not PART: return None
|
|
|
|
# Error si el id. de partición no es hexadecimal.
|
|
if not re.match ('^[0-9A-Fa-f]+$', id):
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, id)
|
|
return None
|
|
|
|
# Elección del tipo de partición.
|
|
PTTYPE = ogGetPartitionTableType (disk)
|
|
if 'GPT' == PTTYPE:
|
|
p = subprocess.run (['sgdisk', f'-t{par}:{id.upper()}', DISK])
|
|
elif 'MSDOS' == PTTYPE:
|
|
p = subprocess.run (['sfdisk', '--id', DISK, par, id.upper()])
|
|
else:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_OUTOFLIMIT, f'{disk},{PTTYPE}')
|
|
return None
|
|
|
|
# MSDOS) Correcto si fdisk sin error o con error pero realiza Syncing
|
|
if 0 == p.returncode:
|
|
subprocess.run (['partprobe', DISK])
|
|
return True
|
|
else:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{part},{id}')
|
|
return None
|
|
|
|
|
|
#/**
|
|
# ogSetPartitionSize int_ndisk int_npartition int_size
|
|
#@brief Muestra el tamano en KB de una particion determinada.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@param int_size tamaño de la partición (en KB)
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@note Requisitos: sfdisk, awk
|
|
#@todo Compruebar que el tamaño sea numérico positivo y evitar que pueda solaparse con la siguiente partición.
|
|
#*/ ##
|
|
def ogSetPartitionSize(*args):
|
|
# Variables locales
|
|
DISK = None
|
|
PART = None
|
|
SIZE = None
|
|
|
|
# Si se solicita, mostrar ayuda.
|
|
if len(args) == 1 and args[0] == "help":
|
|
SystemLib.ogHelp('ogSetPartitionSize', 'ogSetPartitionSize int_ndisk int_npartition int_size', 'ogSetPartitionSize 1 1 10000000')
|
|
return
|
|
|
|
# Error si no se reciben 3 parámetros.
|
|
if len(args) != 3:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT)
|
|
return
|
|
|
|
# Obtener el tamaño de la partición.
|
|
DISK = ogDiskToDev(args[0])
|
|
if DISK is None:
|
|
return
|
|
PART = ogDiskToDev(args[0], args[1])
|
|
if PART is None:
|
|
return
|
|
# Convertir tamaño en KB a sectores de 512 B.
|
|
SIZE = int(args[2]) * 2
|
|
# Redefinir el tamaño de la partición.
|
|
subprocess.run(["sfdisk", "-f", "-uS", f"-N{args[1]}", DISK], input=f",{SIZE}", text=True, stderr=subprocess.DEVNULL)
|
|
subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL)
|
|
return
|
|
|
|
|
|
#/**
|
|
# ogSetPartitionType int_ndisk int_npartition str_type
|
|
#@brief Cambia el identificador de la partición.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@param str_type mnemónico de tipo de partición
|
|
#@return (nada)
|
|
#@attention Requisitos: fdisk, sgdisk
|
|
#*/ ##
|
|
def ogSetPartitionType(*args):
|
|
# Variables locales
|
|
DISK = None
|
|
PART = None
|
|
PTTYPE = None
|
|
TYPE = None
|
|
|
|
# Si se solicita, mostrar ayuda.
|
|
if len(args) == 1 and args[0] == "help":
|
|
SystemLib.ogHelp('ogSetPartitionType', 'ogSetPartitionType int_ndisk int_npartition str_type', 'ogSetPartitionType 1 1 NTFS')
|
|
return
|
|
|
|
# Error si no se reciben 3 parámetros.
|
|
if len(args) != 3:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT)
|
|
return
|
|
|
|
# Sustituye nº de disco por su dispositivo.
|
|
DISK = ogDiskToDev(args[0])
|
|
if DISK is None:
|
|
return
|
|
PART = ogDiskToDev(args[0], args[1])
|
|
if PART is None:
|
|
return
|
|
|
|
# Elección del tipo de partición.
|
|
PTTYPE = ogGetPartitionTableType(args[0])
|
|
if PTTYPE is None:
|
|
return
|
|
TYPE = args[2]
|
|
ID = ogTypeToId(TYPE, PTTYPE)
|
|
if ID is None:
|
|
SystemLib.ogRaiseError(OG_ERR_FORMAT, f"{TYPE},{PTTYPE}")
|
|
return
|
|
|
|
ogSetPartitionId(args[0], args[1], ID)
|
|
return
|
|
|
|
|
|
#/**
|
|
# ogTypeToId str_parttype [str_tabletype]
|
|
#@brief Devuelve el identificador correspondiente a un tipo de partición.
|
|
#@param str_parttype mnemónico de tipo de partición.
|
|
#@param str_tabletype mnemónico de tipo de tabla de particiones (MSDOS por defecto).
|
|
#@return int_idpart identificador de tipo de partición.
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@note tabletype = { MSDOS, GPT }, (MSDOS, por defecto)
|
|
#*/ ##
|
|
#ogTypeToId ('LINUX') => "83"
|
|
#ogTypeToId ('LINUX', 'MSDOS') => "83"
|
|
def ogTypeToId (type, pttype='MSDOS'):
|
|
data = {
|
|
'GPT': {
|
|
'EMPTY': '0',
|
|
'WINDOWS': '0700',
|
|
'NTFS': '0700',
|
|
'EXFAT': '0700',
|
|
'FAT32': '0700',
|
|
'FAT16': '0700',
|
|
'FAT12': '0700',
|
|
'HNTFS': '0700',
|
|
'HFAT32': '0700',
|
|
'HFAT16': '0700',
|
|
'HFAT12': '0700',
|
|
'WIN-RESERV': '0C01',
|
|
'CHROMEOS-KRN': '7F00',
|
|
'CHROMEOS': '7F01',
|
|
'CHROMEOS-RESERV': '7F02',
|
|
'LINUX-SWAP': '8200',
|
|
'LINUX': '8300',
|
|
'EXT2': '8300',
|
|
'EXT3': '8300',
|
|
'EXT4': '8300',
|
|
'REISERFS': '8300',
|
|
'REISER4': '8300',
|
|
'XFS': '8300',
|
|
'JFS': '8300',
|
|
'LINUX-RESERV': '8301',
|
|
'LINUX-LVM': '8E00',
|
|
'FREEBSD-DISK': 'A500',
|
|
'FREEBSD-BOOT': 'A501',
|
|
'FREEBSD-SWAP': 'A502',
|
|
'FREEBSD': 'A503',
|
|
'HFS-BOOT': 'AB00',
|
|
'HFS': 'AF00',
|
|
'HFS+': 'AF00',
|
|
'HFSPLUS': 'AF00',
|
|
'HFS-RAID': 'AF01',
|
|
'SOLARIS-BOOT': 'BE00',
|
|
'SOLARIS': 'BF00',
|
|
'SOLARIS-SWAP': 'BF02',
|
|
'SOLARIS-DISK': 'BF03',
|
|
'CACHE': 'CA00',
|
|
'EFI': 'EF00',
|
|
'LINUX-RAID': 'FD00',
|
|
},
|
|
'MSDOS': {
|
|
'EMPTY': '0',
|
|
'FAT12': '1',
|
|
'EXTENDED': '5',
|
|
'FAT16': '6',
|
|
'WINDOWS': '7',
|
|
'NTFS': '7',
|
|
'EXFAT': '7',
|
|
'FAT32': 'b',
|
|
'HFAT12': '11',
|
|
'HFAT16': '16',
|
|
'HNTFS': '17',
|
|
'HFAT32': '1b',
|
|
'LINUX-SWAP': '82',
|
|
'LINUX': '83',
|
|
'EXT2': '83',
|
|
'EXT3': '83',
|
|
'EXT4': '83',
|
|
'REISERFS': '83',
|
|
'REISER4': '83',
|
|
'XFS': '83',
|
|
'JFS': '83',
|
|
'LINUX-LVM': '8e',
|
|
'FREEBSD': 'a5',
|
|
'OPENBSD': 'a6',
|
|
'HFS': 'af',
|
|
'HFS+': 'af',
|
|
'SOLARIS-BOOT': 'be',
|
|
'SOLARIS': 'bf',
|
|
'CACHE': 'ca',
|
|
'DATA': 'da',
|
|
'GPT': 'ee',
|
|
'EFI': 'ef',
|
|
'VMFS': 'fb',
|
|
'LINUX-RAID': 'fd',
|
|
},
|
|
'LVM': {
|
|
'LVM-LV': '10000',
|
|
},
|
|
'ZVOL': {
|
|
'ZFS-VOL': '10010',
|
|
},
|
|
}
|
|
|
|
if pttype.upper() not in data: return None
|
|
if type.upper() not in data[pttype.upper()]: return None
|
|
return data[pttype.upper()][type.upper()]
|
|
|
|
|
|
#/**
|
|
# ogUnhidePartition int_ndisk int_npartition
|
|
#@brief Hace visible una partición oculta.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@param int_npartition nº de orden de la partición
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND disco o particion no detectado (no es un dispositivo).
|
|
#@exception OG_ERR_PARTITION tipo de partición no reconocido.
|
|
#*/ ##
|
|
def ogUnhidePartition (disk, par):
|
|
PART = ogDiskToDev (disk, par)
|
|
if not PART: return None
|
|
|
|
TYPE = ogGetPartitionType (disk, par)
|
|
if 'HNTFS' == TYPE: NEWTYPE = 'NTFS'
|
|
elif 'HFAT32' == TYPE: NEWTYPE = 'FAT32'
|
|
elif 'HFAT16' == TYPE: NEWTYPE = 'FAT16'
|
|
elif 'HFAT12' == TYPE: NEWTYPE = 'FAT12'
|
|
elif 'WIN-RESERV' == TYPE: NEWTYPE = 'WINDOWS'
|
|
else:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE)
|
|
return None
|
|
|
|
ogSetPartitionType (disk, par, NEWTYPE)
|
|
|
|
|
|
#/**
|
|
# ogUnlockDisk int_ndisk
|
|
#@brief Elimina el fichero de bloqueo para un disco.
|
|
#@param int_ndisk nº de orden del disco
|
|
#@return (nada)
|
|
#@exception OG_ERR_FORMAT Formato incorrecto.
|
|
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
|
|
#@note El fichero de bloqueo se localiza en \c /var/lock/disk, siendo \c disk el dispositivo del disco, sustituyendo el carácter "/" por "-".
|
|
#*/ ##
|
|
def ogUnlockDisk (disk):
|
|
DISK = ogDiskToDev (disk)
|
|
if not DISK: return None
|
|
os.remove (f'/var/lock/lock{DISK.replace("/", "-")}')
|
|
|
|
|
|
#/**
|
|
# ogUpdatePartitionTable
|
|
#@brief Fuerza al kernel releer la tabla de particiones de los discos duros
|
|
#@param no requiere
|
|
#@return informacion propia de la herramienta
|
|
#@note Requisitos: \c partprobe
|
|
#@warning pendiente estructurar la funcion a opengnsys
|
|
#*/ ##
|
|
def ogUpdatePartitionTable():
|
|
for disk in ogDiskToDev():
|
|
subprocess.run(["partprobe", disk])
|