#/** #@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 (disk, parts): ND = disk DISK = ogDiskToDev (ND) if not DISK: return None PTTYPE = ogGetPartitionTableType (disk) if not PTTYPE: PTTYPE = 'MSDOS' # Por defecto para discos vacíos. if 'GPT' == PTTYPE: return ogCreateGptPartitions (disk, parts) elif 'MSDOS' != PTTYPE: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, PTTYPE) return None # Se calcula el ultimo sector del disco (total de sectores usables) SECTORS = ogGetLastSector (disk) # Se recalcula el nº de sectores del disco si existe partición de caché. CACHESIZE = 0 CACHEPART = CacheLib.ogFindCache() if CACHEPART: cache_disk, cache_part = CACHEPART.split() if ND == cache_disk: CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 # Sector de inicio (la partición 1 empieza en el sector 63). IODISCO = ogDiskToDev (disk) IOSIZE = 0 fdisk_out = subprocess.run (['fdisk', '-l', IODISCO], capture_output=True, text=True).stdout for l in fdisk_out.splitlines(): if 'I/O' not in l: continue items = l.split() if len(items) < 4: continue IOSIZE = items[3] break if '4096' == IOSIZE: START = 4096 SECTORS -= 8192 if CACHESIZE: SECTORS = SECTORS - CACHESIZE + 2048 - (SECTORS - CACHESIZE) % 2048 - 1 else: START = 63 if CACHESIZE: SECTORS -= CACHESIZE PART = 1 sfdisk_input = 'unit: sectors\n\n' NVME_PREFIX = 'p' if 'nvme' in DISK else '' for p in parts: # Conservar los datos de la partición de caché. if f'{ND} {PART}' == CACHEPART and CACHESIZE: sfdisk_input += 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, SIZE = p.split (':') try: SIZE = int (SIZE) except ValueError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) return None # Obtener identificador de tipo de partición válido. ID = ogTypeToId (TYPE, 'MSDOS') if 'CACHE' == TYPE or not ID: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) return None # Comprobar tamaño numérico y convertir en sectores de 512 B. SIZE *= 2 # Comprobar si la partición es extendida. EXTSTART = EXTSIZE = 0 if 5 == ID: if PART > 4: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') return None # El inicio de la primera partición logica es el de la extendida más 4x512 EXTSTART = START+2048 EXTSIZE = SIZE-2048 # Incluir particiones lógicas dentro de la partición extendida. if 5 == PART: if not EXTSTART: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, '') return None START = EXTSTART SECTORS = EXTSTART+EXTSIZE # Generar datos para la partición. # En el caso de que la partición sea EMPTY no se crea nada if 'EMPTY' != TYPE: sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, Id={ID}\n' START += SIZE # Error si se supera el nº total de sectores. if '4096' == IOSIZE and PART > 4: START += 2048 if START > SECTORS: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') return None PART += 1 # Si no se indican las 4 particiones primarias, definirlas como vacías, conservando la partición de caché. while PART <= 4: if f'{ND} {PART}' == CACHEPART and CACHESIZE: sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start={SECTORS+1}, size={CACHESIZE}, Id=ca\n' else: sfdisk_input += f'{DISK}{NVME_PREFIX}{PART} : start=0, size=0, Id=0\n' PART += 1 # Si se define partición extendida sin lógicas, crear particion 5 vacía. if 5 == PART and EXTSTART: sfdisk_input += f'{DISK}5 : start={EXTSTART}, SIZE={EXTSIZE}, Id=0\n' # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. FileSystemLib.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. subprocess.run (['sfdisk', DISK], input=sfdisk_input, capture_output=True, text=True) subprocess.run (['partprobe', DISK]) if CACHESIZE: CacheLib.ogMountCache() return True #/** # 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 (disk, parts): ND = disk DISK = ogDiskToDev (ND) if not DISK: return None # Se calcula el ultimo sector del disco (total de sectores usables) SECTORS = ogGetLastSector (disk) # Se recalcula el nº de sectores del disco si existe partición de caché. CACHESIZE = 0 CACHEPART = CacheLib.ogFindCache() if CACHEPART: cache_disk, cache_part = CACHEPART.split() if ND == cache_disk: CACHESIZE = int (CacheLib.ogGetCacheSize()) * 2 if CACHESIZE: SECTORS -= CACHESIZE # Si el disco es GPT empieza en el sector 2048 por defecto, pero podria cambiarse ALIGN = int (subprocess.run (['sgdisk', '-D', DISK], capture_output=True, text=True).stdout) START = ALIGN PART = 1 # Leer parámetros con definición de particionado. DELOPTIONS = [] OPTIONS = [] for p in parts: # 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, SIZE = p.split (':') try: SIZE = int (SIZE) except ValueError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, SIZE) return None # Error si la partición es extendida (no válida en discos GPT). if 'EXTENDED' == TYPE: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'EXTENDED') return None # 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.append (f'-d{PART}') # Creamos la particion # Obtener identificador de tipo de partición válido. ID = ogTypeToId (TYPE, 'GPT') if 'CACHE' == TYPE or not ID: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, TYPE) return None # Comprobar tamaño numérico y convertir en sectores de 512 B. SIZE *= 2 # SIZE debe ser múltiplo de ALIGN, si no gdisk lo mueve automáticamente. SIZE = (SIZE // ALIGN) * ALIGN # Generar datos para la partición. # En el caso de que la partición sea EMPTY no se crea nada if 'EMPTY' != TYPE: OPTIONS += [f'-n{PART}:{START}:+{SIZE}', f'-t{PART}:{ID}'] START += SIZE # Error si se supera el nº total de sectores. if START > SECTORS: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{START//2} > {SECTORS//2}') return None PART += 1 # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. FileSystemLib.ogUnmountAll (ND) if CACHESIZE: CacheLib.ogUnmountCache() # Si la tabla de particiones no es valida, volver a generarla. ogCreatePartitionTable (ND, 'GPT') # Definir particiones y notificar al kernel. subprocess.run (['sgdisk'] + DELOPTIONS + OPTIONS + [DISK]) subprocess.run (['partprobe', DISK]) if CACHESIZE: CacheLib.ogMountCache() return True #/** # 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], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if result.returncode: CREATE = 'GPT' except subprocess.CalledProcessError: CREATE = 'GPT' elif 'MSDOS' == pttype: try: result = subprocess.run (['parted', '-s', DISK, 'print'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 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 (disk, par, size): DISK = ogDiskToDev (disk) if not DISK: return None PART = ogDiskToDev (disk, par) if not PART: return None # Convertir tamaño en KB a sectores de 512 B. SIZE = 2 * int (size) # Redefinir el tamaño de la partición. p = subprocess.run (['sfdisk', '-f', '-uS', f'-N{par}', DISK], input=f',{SIZE}', text=True) if p.returncode: ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk},{par}') return None subprocess.run (['partprobe', DISK]) #/** # 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 (disk, par, t): DISK = ogDiskToDev (disk) if not DISK: return None PART = ogDiskToDev (disk, par) if not PART: return None PTTYPE = ogGetPartitionTableType (disk) if not PTTYPE: return None ID = ogTypeToId (t, PTTYPE) if not ID: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{t},{PTTYPE}') return None ogSetPartitionId (disk, par, ID) #/** # 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])