refs #1150 add restoreImage.py

code-review
Natalia Serrano 2024-11-19 12:03:33 +01:00
parent b755de21be
commit b71e631fb1
7 changed files with 527 additions and 301 deletions

View File

@ -8,6 +8,7 @@ from pathlib import Path
import ogGlobals
import SystemLib
import CacheLib
import FileSystemLib
def parted(*args):
parted_path = shutil.which("parted")
@ -707,32 +708,28 @@ def ogGetPartitionId(*args):
print(ID)
return
def ogGetPartitionSize(*args):
# Variables locales
PART = None
SIZE = None
# Si se solicita, mostrar ayuda.
if len(args) == 1 and args[0] == "help":
SystemLib.ogHelp('ogGetPartitionSize', 'ogGetPartitionSize int_ndisk int_npartition', 'ogGetPartitionSize 1 1 => 10000000')
return
#/**
# 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
# Error si no se reciben 2 parámetros.
if len(args) != 2:
SystemLib.ogRaiseError(OG_ERR_FORMAT)
return
sz = subprocess.run (['partx', '-gbo', 'SIZE', PART], capture_output=True, text=True).stdout.strip()
if sz: return int (int (sz) / 1024)
# Devolver tamaño de partición, del volumen lógico o del sistema de archivos (para ZFS).
PART = ogDiskToDev(args[0], args[1])
if PART is None:
return
SIZE = subprocess.getoutput(f"partx -gbo SIZE {PART} 2>/dev/null | awk '{{print int($1/1024)}}'")
if not SIZE:
SIZE = subprocess.getoutput(f"lvs --noheadings -o lv_size --units k {PART} | awk '{{printf \"%d\",$0}}'")
if not SIZE:
SIZE = ogGetFsSize(args[0], args[1])
print(SIZE or 0)
return
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)
def ogGetPartitionsNumber(*args):
# Variables locales

View File

@ -6,6 +6,7 @@ import ogGlobals
import SystemLib
import DiskLib
import CacheLib
import FileSystemLib
def ogCheckFs(int_ndisk, int_nfilesys):
# Si se solicita, mostrar ayuda.
@ -171,170 +172,144 @@ def ogExtendFs():
ogUnlock(int(sys.argv[1]), int(sys.argv[2]))
return ERRCODE
def ogFormat(int_ndisk, int_nfilesys):
if int_nfilesys.lower() == "cache":
ogFormatCache()
#/**
# ogFormat int_ndisk int_nfilesys | CACHE
#@see ogFormatFs ogFormatCache
#*/ ##
def ogFormat (disk, par=None, fs=None, label=None):
if disk.lower() == "cache":
return CacheLib.ogFormatCache()
else:
ogFormatFs(int_ndisk, int_nfilesys)
return ogFormatFs (disk, par)
def ogFormatFs(int_ndisk, int_nfilesys, str_label=None):
# Si se solicita, mostrar ayuda.
if str_label == "help":
ogHelp("ogFormatFs", "ogFormatFs int_ndisk int_nfilesys [str_label]", "ogFormatFs 1 1", "ogFormatFs 1 1 EXT4", "ogFormatFs 1 1 \"DATA\"", "ogFormatFs 1 1 EXT4 \"DATA\"")
#/**
# ogFormatFs int_ndisk int_nfilesys [type_fstype] [str_label]
#@brief Formatea un sistema de ficheros según el tipo de su partición.
#@param int_ndisk nº de orden del disco
#@param int_nfilesys nº de orden del sistema de archivos
#@param type_fstype mnemónico de sistema de ficheros a formatear (opcional al reformatear)
#@param str_label etiqueta de volumen (opcional)
#@return (por determinar)
#@exception OG_ERR_FORMAT Formato de ejecución incorrecto.
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
#@exception OG_ERR_PARTITION Partición no accesible o desconocida.
#@note Requisitos: mkfs*
#@warning No formatea particiones montadas ni bloqueadas.
#@todo Definir salidas.
#*/ ##
def ogFormatFs (disk, par, type=None, label=None):
PART = DiskLib.ogDiskToDev (disk, par)
if not PART: return
if ogIsMounted (disk, par):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_DONTFORMAT, f'{ogGlobals.lang.MSG_MOUNT}: {disk} {par}')
return None
if ogIsLocked (disk, par):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f"{disk} {par}")
return None
if not type:
type = ogGetFsType (disk, par)
if not type:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{disk} {par} ...")
return None
data = {
'EXT2': { 'PROG': 'mkfs.ext2', 'PARAMS': '-F' },
'EXT3': { 'PROG': 'mkfs.ext3', 'PARAMS': '-F' },
'EXT4': { 'PROG': 'mkfs.ext4', 'PARAMS': '-F' },
'BTRFS': { 'PROG': 'mkfs.btrfs', 'PARAMS': '-f' },
'REISERFS': { 'PROG': 'mkfs.reiserfs', 'PARAMS': '-f', 'LABELPARAM': '-l' },
'REISER4': { 'PROG': 'mkfs.reiser4', 'PARAMS': '-f', 'INPUT': 'y\n' },
'XFS': { 'PROG': 'mkfs.xfs', 'PARAMS': '-f' },
'JFS': { 'PROG': 'mkfs.jfs', 'INPUT': 'y\n' },
'F2FS': { 'PROG': 'mkfs.f2fs', 'LABELPARAM': '-l' },
'NILFS2': { 'PROG': 'mkfs.nilfs2', 'PARAMS': '-f' },
'LINUX-SWAP': { 'PROG': 'mkswap' },
'NTFS': { 'PROG': 'mkntfs', 'PARAMS': '-f' },
'EXFAT': { 'PROG': 'mkfs.exfat', 'LABELPARAM': '-n' },
'FAT32': { 'PROG': 'mkdosfs', 'PARAMS': '-F 32', 'LABELPARAM': '-n' },
'FAT16': { 'PROG': 'mkdosfs', 'PARAMS': '-F 16', 'LABELPARAM': '-n' },
'FAT12': { 'PROG': 'mkdosfs', 'PARAMS': '-F 12', 'LABELPARAM': '-n' },
'HFS': { 'PROG': 'mkfs.hfs' },
'HFSPLUS': { 'PROG': 'mkfs.hfsplus', 'LABELPARAM': '-v' },
'UFS': { 'PROG': 'mkfs.ufs', 'PARAMS': '-O 2' },
}
if type not in data:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par} {type}")
return
# Error si no se reciben entre 2 y 4 parámetros.
if not (2 <= len(sys.argv) <= 4):
ogRaiseError(OG_ERR_FORMAT)
d = data[type]
prog = d['PROG']
params = d['PARAMS'] if 'PARAMS' in d else ''
labelparam = d['LABELPARAM'] if 'LABELPARAM' in d else ''
input = d['INPUT'] if 'INPUT' in d else ''
if label == "CACHE":
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{ogGlobals.lang.MSG_RESERVEDVALUE}: CACHE")
return
if label:
params = f"{params} {labelparam or '-L'} {label}"
# Obtener fichero de dispositivo.
PART = ogDiskToDev(int_ndisk, int_nfilesys)
if not PART:
return
# Error si la partición está montada o bloqueada.
if ogIsMounted(int_ndisk, int_nfilesys):
ogRaiseError(OG_ERR_DONTFORMAT, f"{MSG_MOUNT}: {int_ndisk} {int_nfilesys}")
return
if ogIsLocked(int_ndisk, int_nfilesys):
ogRaiseError(OG_ERR_LOCKED, f"{int_ndisk} {int_nfilesys}")
return
# Si no se indica el tipo de sistema de archivos, intentar obtenerlo.
TYPE = ogGetFsType(int_ndisk, int_nfilesys)
if not TYPE:
TYPE = sys.argv[3]
# Error, si no especifica el tipo de sistema de archivos a formatear.
if not TYPE:
ogRaiseError(OG_ERR_FORMAT, f"{int_ndisk} {int_nfilesys} ...")
return
# Elegir tipo de formato.
if TYPE == "EXT2":
PROG = "mkfs.ext2"
PARAMS = "-F"
elif TYPE == "EXT3":
PROG = "mkfs.ext3"
PARAMS = "-F"
elif TYPE == "EXT4":
PROG = "mkfs.ext4"
PARAMS = "-F"
elif TYPE == "BTRFS":
PROG = "mkfs.btrfs"
PARAMS = "-f"
elif TYPE == "REISERFS":
PROG = "mkfs.reiserfs"
PARAMS = "-f"
LABELPARAM = "-l"
elif TYPE == "REISER4":
PROG = "mkfs.reiser4"
PARAMS = "-f <<<\"y\""
elif TYPE == "XFS":
PROG = "mkfs.xfs"
PARAMS = "-f"
elif TYPE == "JFS":
PROG = "mkfs.jfs"
PARAMS = "<<<\"y\""
elif TYPE == "F2FS":
PROG = "mkfs.f2fs"
LABELPARAM = "-l"
elif TYPE == "NILFS2":
PROG = "mkfs.nilfs2"
PARAMS = "-f"
elif TYPE == "LINUX-SWAP":
PROG = "mkswap"
elif TYPE == "NTFS":
PROG = "mkntfs"
PARAMS = "-f"
elif TYPE == "EXFAT":
PROG = "mkfs.exfat"
LABELPARAM = "-n"
elif TYPE == "FAT32":
PROG = "mkdosfs"
PARAMS = "-F 32"
LABELPARAM = "-n"
elif TYPE == "FAT16":
PROG = "mkdosfs"
PARAMS = "-F 16"
LABELPARAM = "-n"
elif TYPE == "FAT12":
PROG = "mkdosfs"
PARAMS = "-F 12"
LABELPARAM = "-n"
elif TYPE == "HFS":
PROG = "mkfs.hfs"
elif TYPE == "HFSPLUS":
PROG = "mkfs.hfsplus"
LABELPARAM = "-v"
elif TYPE == "UFS":
PROG = "mkfs.ufs"
PARAMS = "-O 2"
else:
ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys} {TYPE}")
return
# Etiquetas de particion.
if not str_label:
if sys.argv[4] == "CACHE":
ogRaiseError(OG_ERR_FORMAT, f"{MSG_RESERVEDVALUE}: CACHE")
return
if len(sys.argv) >= 4:
PARAMS = f"{PARAMS} {LABELPARAM or '-L'} {sys.argv[4]}"
else:
PARAMS = f"{PARAMS} {LABELPARAM or '-L'} {str_label}"
# Formatear en modo uso exclusivo (desmontar siempre).
ogLock(int_ndisk, int_nfilesys)
ogLock (disk, par)
subprocess.run (['umount', PART])
try:
subprocess.run([PROG, PARAMS, PART], capture_output=True)
ERRCODE = 0
if input:
errcode = subprocess.run ([prog, params, PART])
else:
errcode = subprocess.run ([prog, params, PART], input=input, text=True)
except FileNotFoundError:
ogRaiseError(OG_ERR_NOTEXEC, PROG)
ERRCODE = OG_ERR_NOTEXEC
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, prog)
errcode = ogGlobals.OG_ERR_NOTEXEC
except:
ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys}")
ERRCODE = OG_ERR_PARTITION
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f"{disk} {par}")
errcode = ogGlobals.OG_ERR_PARTITION
ogUnlock(int_ndisk, int_nfilesys)
return ERRCODE
ogUnlock (disk, par)
return errcode
def ogGetFsSize(int_ndisk, int_npartition, str_unit=None):
# Si se solicita, mostrar ayuda.
if str_unit == "help":
ogHelp("ogGetFsSize", "ogGetFsSize int_ndisk int_npartition [str_unit]", "ogGetFsSize 1 1", "ogGetFsSize 1 1 KB")
return
# Error si no se reciben 2 o 3 parámetros.
if not (2 <= len(sys.argv) <= 3):
ogRaiseError(OG_ERR_FORMAT)
return
#/**
# ogGetFsSize int_ndisk int_npartition [str_unit]
#@brief Muestra el tamanio del sistema de archivos indicado, permite definir la unidad de medida, por defecto GB
#@param int_ndisk nº de orden del disco
#@param int_npartition nº de orden de la partición
#@param str_unit unidad (opcional, por defecto: KB)
#@return float_size - Tamaño del sistema de archivos
#@note str_unit = { KB, MB, GB, TB }
#@exception OG_ERR_FORMAT Formato incorrecto.
#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositivo.
#*/ ##
# Obtener unidad y factor de medida.
UNIT = str_unit or "KB"
FACTOR = 1
if UNIT.upper() == "MB":
FACTOR = 1024
elif UNIT.upper() == "GB":
FACTOR = 1024 * 1024
elif UNIT.upper() == "TB":
FACTOR = 1024 * 1024 * 1024
elif UNIT.upper() != "KB":
ogRaiseError(OG_ERR_FORMAT, f"{UNIT} != {{ KB, MB, GB, TB }}")
def ogGetFsSize (disk, par, unit=None):
u = unit or "KB"
factor = 1
if u.upper() == "MB":
factor = 1024
elif u.upper() == "GB":
factor = 1024 * 1024
elif u.upper() == "TB":
factor = 1024 * 1024 * 1024
elif u.upper() != "KB":
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f"{unit} != {{ KB, MB, GB, TB }}")
return
# Obtener el tamaño del sistema de archivo (si no está formateado; tamaño = 0).
MNTDIR = ogMount(int_ndisk, int_npartition)
if MNTDIR:
result = subprocess.run(["df", "-BK", MNTDIR], capture_output=True, text=True)
VALUE = result.stdout.split("\n")[1].split()[1]
SIZE = float(VALUE) / FACTOR
mnt = FileSystemLib.ogMount (disk, par)
if mnt:
result = subprocess.run(["df", "-BK", mnt], capture_output=True, text=True)
val = result.stdout.split("\n")[1].split()[1]
val = val.replace ('K', '')
sz = int (val) / factor
else:
SIZE = 0
sz = 0
return int (sz)
# Devolver el tamaño (quitar decimales si son 0).
return int(SIZE) if SIZE.is_integer() else SIZE
#/**
# ogGetFsType int_ndisk int_nfilesys
@ -461,26 +436,24 @@ def ogIsLocked(disk, par):
def ogIsPartitionLocked(disk, par):
DISK = DiskLib.ogDiskToDev(disk)
PART = DiskLib.ogDiskToDev(disk, par)
print (f'nati: ogIsPartitionLocked: DISK ({DISK}) PART ({PART})')
if not PART: return
LOCKDISK = f"/var/lock/lock{DISK.replace('/', '-')}"
LOCKPART = f"/var/lock/lock{PART.replace('/', '-')}"
return os.path.isfile(LOCKDISK) or os.path.isfile(LOCKPART)
rc = os.path.isfile(LOCKDISK) or os.path.isfile(LOCKPART)
print (f'nati: ogIsPartitionLocked: LOCKDISK ({LOCKDISK}) LOCKPART ({LOCKPART}) rc ({rc})')
return rc
def ogIsMounted(int_ndisk, int_nfilesys):
# Si se solicita, mostrar ayuda.
if len(sys.argv) == 3 and sys.argv[2] == "help":
ogHelp("ogIsMounted", "ogIsMounted int_ndisk int_nfilesys", "ogIsMounted 1 1")
return
# Error si no se reciben 2 parámetros.
if len(sys.argv) != 3:
ogRaiseError(OG_ERR_FORMAT)
return
# Obtener punto de montaje.
MNTDIR = ogGetMountPoint(int_ndisk, int_nfilesys)
return bool(MNTDIR)
#/**
# ogIsMounted int_ndisk int_nfilesys
#@brief Comprueba si un sistema de archivos está montado.
#@param int_ndisk nº de orden del disco
#@param int_nfilesys nº de orden del sistema de archivos
#@return Código de salida: 0 - montado, 1 - sin montar o error.
#*/ ##
def ogIsMounted (disk, par):
return bool (ogGetMountPoint (disk, par))
#/**
@ -523,28 +496,31 @@ def ogIsWritable(int_ndisk, int_nfilesys):
options = result.stdout.strip().split(",")
return "rw" in options
def ogLock(int_ndisk, int_nfilesys):
ogLockPartition(int_ndisk, int_nfilesys)
def ogLockPartition(int_ndisk, int_npartition):
# Si se solicita, mostrar ayuda.
if len(sys.argv) == 3 and sys.argv[2] == "help":
ogHelp("ogLockPartition", "ogLockPartition int_ndisk int_npartition", "ogLockPartition 1 1")
return
#/**
# ogLock int_ndisk int_npartition
#@see ogLockPartition
#*/
def ogLock(disk, par):
return ogLockPartition(disk, par)
# Error si no se reciben 2 parámetros.
if len(sys.argv) != 3:
ogRaiseError(OG_ERR_FORMAT)
return
#/**
# ogLockPartition int_ndisk int_npartition
#@brief Genera un fichero de bloqueo para una partición en uso exlusivo.
#@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 corresponden con un dispositivo.
#@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-".
#*/ ##
def ogLockPartition (disk, par):
PART = DiskLib.ogDiskToDev (disk, par)
if not PART: return
# Obtener partición.
PART = ogDiskToDev(int_ndisk, int_npartition)
if not PART:
return
# Crear archivo de bloqueo exclusivo.
LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}"
open(LOCKFILE, 'a').close()
open(LOCKFILE, 'w').close()
return True
#/**
@ -580,7 +556,6 @@ def ogMountFirstFs(int_ndisk):
#@exception OG_ERR_NOTFOUND Disco o particion no corresponden con un dispositivo.
#@exception OG_ERR_PARTITION Tipo de particion desconocido o no se puede montar.
#*/ ##
def ogMountFs (disk, par):
dev = DiskLib.ogDiskToDev (disk, par)
if not dev: return
@ -589,6 +564,7 @@ def ogMountFs (disk, par):
if mntdir: return mntdir
if ogIsLocked (disk, par):
print (f'nati: ogMountFs: is locked disk ({disk}) par ({par})')
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f"{ogGlobals.lang.MSG_PARTITION}, {disk} {par}")
return
@ -727,26 +703,28 @@ def ogReduceFs(int_ndisk, int_nfilesys):
# Devuelve tamaño del sistema de ficheros.
return ogGetFsSize(int_ndisk, int_nfilesys)
def ogUnlock(int_ndisk, int_npartition):
ogUnlockPartition(int_ndisk, int_npartition)
def ogUnlockPartition(int_ndisk, int_npartition):
# Si se solicita, mostrar ayuda.
if len(sys.argv) == 3 and sys.argv[2] == "help":
ogHelp("ogUnlockPartition", "ogUnlockPartition int_ndisk int_npartition", "ogUnlockPartition 1 1")
return
#/**
# ogUnlock int_ndisk int_npartition
#@see ogUnlockPartition
#*/ ##
def ogUnlock (disk, par):
return ogUnlockPartition (disk, par)
# Error si no se reciben 2 parámetros.
if len(sys.argv) != 3:
ogRaiseError(OG_ERR_FORMAT)
return
#/**
# ogUnlockPartition int_ndisk int_npartition
#@brief Elimina el fichero de bloqueo para una particion.
#@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 corresponden con un dispositivo.
#@note El fichero de bloqueo se localiza en \c /var/lock/part, siendo \c part el dispositivo de la partición, sustituyendo el carácter "/" por "-".
#*/ ##
def ogUnlockPartition (disk, par):
PART = DiskLib.ogDiskToDev (disk, par)
if not PART: return
# Obtener partición.
PART = ogDiskToDev(int_ndisk, int_npartition)
if not PART:
return
# Borrar archivo de bloqueo exclusivo.
LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}"
os.remove(LOCKFILE)
@ -756,7 +734,7 @@ def ogUnlockPartition(int_ndisk, int_npartition):
#@see ogUnmountFs
#*/ ##
def ogUnmount (disk, par):
ogUnmountFs (disk, par)
return ogUnmountFs (disk, par)
#/**
# ogUnmountFs int_ndisk int_nfilesys
@ -793,8 +771,10 @@ def ogUnmountFs(disk, par):
os.remove(MNTDIR)
except:
pass
return True
else:
SystemLib.ogEcho ([], "warning", f'{ogGlobals.lang.MSG_DONTMOUNT}: "{disk},{par}"')
return True
#/**

View File

@ -267,6 +267,17 @@ def ogRestoreImageSyntax (imgfile, part, tool=None, level=None):
#@note repo = { REPO, CACHE }
#@exception OG_ERR_FORMAT formato incorrecto.
#*/ ##
#ogIsImageLocked ('/opt/opengnsys/images/aula1/win7.img')
#ogIsImageLocked ('REPO', '/aula1/win7.img')
def ogIsImageLocked (container=None, imgfile=None):
if container and imgfile:
p = FileLib.ogGetPath (src=container, file=f'{imgfile}.lock')
elif imgfile:
p = FileLib.ogGetPath (file=f'{imgfile}.lock')
else:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'{container} {imgfile}')
return
return os.path.exists (p)
#/**
@ -315,6 +326,68 @@ def ogRestoreImageSyntax (imgfile, part, tool=None, level=None):
#@exception OG_ERR_IMGSIZEPARTITION 30 Tamaño de la particion es menor al tamaño de la imagen.
#@todo Comprobar incongruencias partición-imagen, control de errores, definir parámetros, caché/repositorio, etc.
#*/ ##
#ogRestoreImage ('REPO', '/aula1/win7', '1', '1')
def ogRestoreImage (repo, imgpath, disk, par):
PART = DiskLib.ogDiskToDev (disk, par)
if not PART:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {disk} {par}')
return None
imgtype = 'img'
imgfile = FileLib.ogGetPath (repo, f'{imgpath}.{imgtype}')
if not os.path.exists (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f' {disk} {par}')
return None
if not ogGetImageInfo (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f' {repo} {imgpath}')
return None
# Error si la imagen no cabe en la particion.
imgsize = ogGetImageSize (repo, imgpath)
if not imgsize:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f' {repo} {imgpath}')
return None
if not FileSystemLib.ogMount (disk, par):
FileSystemLib.ogFormat (disk, par)
partsize = DiskLib.ogGetPartitionSize (disk, par)
if float (imgsize) > float (partsize):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMGSIZEPARTITION, f' {partsize} < {imgsize}')
return None
if ogIsImageLocked (container=None, imgfile=imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_IMAGE} {repo}, {imgpath}.{imgtype}')
return None
if FileSystemLib.ogIsLocked (disk, par):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_LOCKED, f'{ogGlobals.lang.MSG_PARTITION} {disk}, {par}')
return None
program = ogRestoreImageSyntax (imgfile, PART)
if not FileSystemLib.ogUnmount (disk, par):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f' {disk} {par}')
return None
if not FileSystemLib.ogLock (disk, par):
print (f'not FileSystemLib.ogLock()')
return None
rc = None
try:
print (f'nati: ogRestoreImage: running ({program})')
p = subprocess.run (program, shell=True, capture_output=True, text=True)
rc = p.returncode
print (f'nati: ogRestoreImage: rc ({rc}) stdout ({p.stdout}) stderr ({p.stderr})')
if not rc:
SystemLib.ogRaiseError ([], ogGlobalsOG_ERR_IMAGE, f'{imgfile}, {disk}, {par}')
except:
pass
finally:
FileSystemLib.ogUnlock (disk, par)
return rc
#/**
@ -516,6 +589,14 @@ def ogGetImageInfo (imgfile):
#@note ogGetImageProgram REPO imagenA -> partclone
#*/ ##
#ogGetImageProgram ('REPO', 'prueba') ==> 'PARTCLONE'
def ogGetImageProgram (container, filename):
imgfile = FileLib.ogGetPath (container, f'{filename}.img')
if not os.path.exists (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile)
return None
i = ogGetImageInfo (imgfile)
return i.split (':')[0]
#/**
# ogGetImageCompressor str_REPO str_imagen
@ -529,6 +610,15 @@ def ogGetImageInfo (imgfile):
#@note ogGetImageCompressor REPO imagenA -> lzop
#*/ ##
#ogGetImageCompressor ('REPO', 'prueba') ==> 'LZOP'
def ogGetImageCompressor (container, filename):
imgfile = FileLib.ogGetPath (container, f'{filename}.img')
if not os.path.exists (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile)
return None
i = ogGetImageInfo (imgfile)
return i.split (':')[1]
#/**
# ogGetImageType str_REPO str_imagen
@ -544,7 +634,7 @@ def ogGetImageInfo (imgfile):
#ogGetImageType ('REPO', 'imgprueba') ==> 'NTFS'
#ogGetImageType ('CACHE', 'testimg') ==> 'EXTFS'
def ogGetImageType (repo, imgname):
imgfile = FileLib.ogGetPath (src=repo, file=imgname)
imgfile = FileLib.ogGetPath (src=repo, file=f'{imgname}.img')
if not os.path.exists (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile)
return None
@ -565,6 +655,15 @@ def ogGetImageType (repo, imgname):
#@exception OG_ERR_NOTFOUND fichero no encontrado.
#@note ogGetImagesize REPO imagenA -> 56432234 > Kb
#*/ ##
#ogGetImageSize ('REPO', 'prueba') ==> '5642158'
def ogGetImageSize (repo, imgname):
imgfile = FileLib.ogGetPath (src=repo, file=f'{imgname}.img')
if not os.path.exists (imgfile):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, imgfile)
return None
i = ogGetImageInfo (imgfile)
return i.split (':')[3]
#/**

View File

@ -40,7 +40,7 @@ def ogChangeRepo(ip_repo, og_unit=None):
try:
mount = subprocess.run (['mount'], capture_output=True, text=True).stdout
ro = not not list (filter (lambda line: re.search (r'ogimages.*\bro,', line), mount.splitlines()))
ro = bool (list (filter (lambda line: re.search (r'ogimages.*\bro,', line), mount.splitlines())))
current_repo = ogGetRepoIp()
new_repo = current_repo if ip_repo.upper() == "REPO" else ip_repo
@ -48,7 +48,7 @@ def ogChangeRepo(ip_repo, og_unit=None):
subprocess.run(["umount", ogGlobals.OGIMG], check=True)
SystemLib.ogEcho (['session', 'log'], 'info', f'{ogGlobals.lang.MSG_HELP_ogChangeRepo} {new_repo}')
SystemLib.ogEcho (['session', 'log'], None, f'{ogGlobals.lang.MSG_HELP_ogChangeRepo} {new_repo}')
options = _ogConnect_options()
if not _ogConnect (new_repo, ogprotocol, 'ogimages', ogGlobals.OGIMG, options, ro):
_ogConnect (current_repo, ogprotocol, 'ogimages', ogGlobals.OGIMG, options, ro)
@ -61,7 +61,7 @@ def ogChangeRepo(ip_repo, og_unit=None):
SystemLib.ogEcho(
["session", "log"],
'info',
None,
f"Repository successfully changed to {new_repo}".strip(),
)
@ -164,28 +164,30 @@ def ogGetHostname():
#@note Usa las variables utilizadas por el initrd "/etc/net-ethX.conf
#*/ ##
def ogGetIpAddress():
IP = ""
if len(sys.argv) >= 2 and sys.argv[1] == "help":
SystemLib.ogHelp("ogGetIpAddress", "ogGetIpAddress", "ogGetIpAddress => 192.168.0.10")
return
if "IPV4ADDR" in os.environ:
IP = os.environ["IPV4ADDR"]
else:
# Obtener direcciones IP.
if "DEVICE" in os.environ:
IP = subprocess.run(["ip", "-o", "address", "show", "up", "dev", os.environ["DEVICE"]], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split()
else:
IP = subprocess.run(["ip", "-o", "address", "show", "up"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split()
ip = os.environ["IPV4ADDR"]
if '/' in ip: ip = ip.split ('/')[0]
return ip
IP = [addr.split("/")[0] for addr in IP if "inet" in addr]
extra_args = []
if "DEVICE" in os.environ:
extra_args = [ "dev", os.environ["DEVICE"] ]
ipas = subprocess.run (['ip', '-json', 'address', 'show', 'up'] + extra_args, capture_output=True, text=True).stdout
# Mostrar solo la primera.
if IP:
print(IP[0])
ipasj = json.loads (ipas)
addresses = []
for e in ipasj:
if 'lo' == e['ifname']: continue
if 'addr_info' not in e: continue
addrs = e['addr_info']
for a in addrs:
if 'inet' != a['family']: continue
addresses.append ({ 'local': a['local'], 'prefixlen': a['prefixlen'] })
if 1 != len (addresses):
raise Exception ('more than one local IP address found')
return addresses[0]
return 0
#/**
# ogGetMacAddress

View File

@ -246,9 +246,6 @@ def _clientip():
addresses = []
for e in ipasj:
if 'lo' == e['ifname']: continue
if 'vboxnet' in e['ifname']: continue
if 'br-' in e['ifname']: continue
if 'tun' in e['ifname']: continue
if 'addr_info' not in e: continue
addrs = e['addr_info']
for a in addrs:
@ -642,7 +639,7 @@ def ogMcastReceiverPartition (disk, par, sess, tool, compressor):
#*/ ##
## now ogCore takes this responsibility
def ogMcastRequest (img, proto):
return
return True
##########################################
@ -754,7 +751,7 @@ def ogTorrentStart (disk=None, par=None, container=None, torrentfile=None, torre
if 'peer' == mode:
print ('Donwloading Torrent as peer')
# Creamos el fichero de resumen por defecto
with open (f'{source}.bf', 'w') as fd: pass
open (f'{source}.bf', 'w').close()
# ctorrent controla otro fichero -b ${SOURCE}.bfog
subprocess.run (['ctorrent', '-f', '-c', '-X', f'sleep {time}; kill -2 $(pidof ctorrent)', '-C', '100', source, '-s', target, '-b', f'{source}.bfog'])
elif 'leecher' == mode:
@ -763,7 +760,7 @@ def ogTorrentStart (disk=None, par=None, container=None, torrentfile=None, torre
elif 'seeder' == mode:
print ('MODE seeder ctorrent')
# Creamos el fichero de resumen por defecto
with open (f'{source}.bf', 'w') as fd: pass
open (f'{source}.bf', 'w').close()
# ctorrent controla otro fichero -b ${SOURCE}.bfog
subprocess.run (['ctorrent', '-f', '-c', '-X', f'sleep {time}; kill -2 $(pidof ctorrent)', '-C', '100', source, '-s', target, '-b', f'{source}.bfog'])
else:

View File

@ -2,10 +2,14 @@ import subprocess
import datetime
from zoneinfo import ZoneInfo
import sys
import os
import os
import shutil
import inspect
## for ogExecAndLog
from io import StringIO
from contextlib import redirect_stdout, redirect_stderr
import ogGlobals
import StringLib
@ -14,9 +18,9 @@ import StringLib
#OGLOGSESSION, OGLOGCOMMAND, OGLOGFILE, OG_ERR_LOCKED, OG_ERR_PARTITION, OG_ERR_FORMAT, OG_ERR_NOTEXEC, OG_ERR_NOTFOUND
def _logtype2logfile (t):
if 'log' == t: return ogGlobals.OGLOGFILE
elif 'command' == t: return ogGlobals.OGLOGCOMMAND
elif 'session' == t: return ogGlobals.OGLOGSESSION
if 'log' == t.lower(): return ogGlobals.OGLOGFILE
elif 'command' == t.lower(): return ogGlobals.OGLOGCOMMAND
elif 'session' == t.lower(): return ogGlobals.OGLOGSESSION
else: raise Exception (f'unknown log type ({t})')
#/**
# ogEcho [str_logtype ...] [str_loglevel] "str_message" ...
@ -51,58 +55,88 @@ def ogEcho (logtypes, loglevel, msg):
else:
raise Exception (f'unknown loglevel ({loglevel})')
def ogExecAndLog(*args):
# Variables locales
ISCOMMAND = False
ISLOG = False
ISSESSION = False
COMMAND = ""
CONTINUE = 1
FILES = ""
REDIREC = ""
FUNCNAME = ogExecAndLog.__name__
# Si se solicita, mostrar ayuda.
if len(args) > 0 and args[0] == "help":
ogHelp(f"{FUNCNAME} str_logfile ... str_command ...",
f"{FUNCNAME} COMMAND ls -al /")
#/**
# ogExecAndLog str_logfile ... str_command ...
#@brief Ejecuta un comando y guarda su salida en fichero de registro.
#@param str_logfile fichero de registro (pueden ser varios).
#@param str_command comando y comandos a ejecutar.
#@return Salida de ejecución del comando.
#@note str_logfile = { LOG, SESSION, COMMAND }
#*/
#ogHelp (str_logfile ... str_command ...",
#ogHelp ([], ogMyLib.ogSomeMethod, *args, **kwargs)
#ogHelp ('command', ogMyLib.ogSomeMethod, *args, **kwargs)
#ogHelp (['command'], ogMyLib.ogSomeMethod, *args, **kwargs)
#ogHelp (['log', 'command'], ogMyLib.ogSomeMethod, *args, **kwargs)
def ogExecAndLog (logtypes, fun, *args, **kwargs):
logfiles = ['/dev/stdout']
if type (logtypes) is list:
for l in logtypes:
logtypes = list (map (lambda x: x.lower(), logtypes))
logfiles.append (_logtype2logfile (l))
else: ## string
logtypes = logtypes.lower()
logfiles.append (_logtype2logfile (logtypes))
if not fun:
ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'no function provided')
return
# Procesar parámetros.
while CONTINUE:
arg = args.pop(0).lower()
if arg == "command":
ISCOMMAND = True
continue
elif arg == "log":
ISLOG = True
continue
elif arg == "session":
ISSESSION = True
continue
else:
COMMAND = " ".join(args)
CONTINUE = 0
## the original bash code does something like this:
#if [ $ISCOMMAND ]; then
# > $OGLOGCOMMAND
# REDIREC="2>&1"
#fi
#eval $COMMAND $REDIREC | tee -a $FILES
# Error si no se recibe un comando que ejecutar.
if not COMMAND:
ogRaiseError(OG_ERR_FORMAT)
return
## a hybrid bash/python pseudocode would end up being like the following:
#if 'command' in logtypes:
# rm $OGLOGCOMMAND
# touch $OGLOGCOMMAND
#
#if 'command' in logtypes:
# ## redirect both stdout and stderr
# eval $COMMAND 2>&1 | tee -a $FILES
#else:
# ## redirect stdout only
# eval $COMMAND | tee -a $FILES
# Componer lista de ficheros de registro.
if ISCOMMAND:
FILES = OGLOGCOMMAND
open(FILES, "w").close()
REDIREC = "2>&1"
if ISLOG:
FILES += " " + OGLOGFILE
if ISSESSION:
FILES += " " + OGLOGSESSION
import time
sout = serr = ''
if 'command' in logtypes:
os.unlink (ogGlobals.OGLOGCOMMAND)
open (ogGlobals.OGLOGCOMMAND, 'w').close()
print ('nati: ogExecAndLog: about to redirect stdout and stderr')
time.sleep (1) ## nati
with redirect_stdout (StringIO()) as r_stdout, redirect_stderr (StringIO()) as r_stderr:
rc = fun (*args, **kwargs)
sout = r_stdout.getvalue()
serr = r_stderr.getvalue()
print (f'nati: ogExecAndLog: end of redirections, rc ({rc}) sout ({sout}) serr ({serr})')
time.sleep (1) ## nati
else:
print ('nati: ogExecAndLog: about to redirect stdout only')
time.sleep (1)
with redirect_stdout (StringIO()) as r_stdout:
rc = fun (*args, **kwargs)
sout = r_stdout.getvalue()
print (f'nati: ogExecAndLog: end of redirections, rc ({rc}) sout ({sout})')
time.sleep (1) ## nati
# Ejecutar comando.
subprocess.call(f"{COMMAND} {REDIREC} | tee -a {FILES}", shell=True)
# Salida de error del comando ejecutado.
return subprocess.PIPESTATUS[0]
if sout or serr:
print ('nati: ogExecAndLog: sout or serr are true')
time.sleep (1) ## nati
for f in logfiles:
print (f'nati: ogExecAndLog: logging to logfile ({f})')
with open (f, 'a') as fd:
if sout: fd.write (f"ogExecAndLog: {fun.__name__} stdout:\n{sout}")
else: fd.write (f"ogExecAndLog: {fun.__name__} stdout: (none)\n")
if serr: fd.write (f"ogExecAndLog: {fun.__name__} stderr:\n{serr}")
else: fd.write (f"ogExecAndLog: {fun.__name__} stderr: (none)\n")
print (f'nati: ogExecAndLog: returning rc ({rc})')
return rc
#/**
# ogGetCaller
@ -211,14 +245,14 @@ def ogRaiseError (logtypes, code, msg):
CODE = ogGlobals.OG_ERR_GENERIC
call_stack = [i[3] for i in inspect.stack()]
if len (call_stack) < 3: return ## shouldn't happen
if len (call_stack) < 2: return ## shouldn't happen
call_stack.pop() ## remove '<module>'
call_stack.pop(0) ## remove 'ogRaiseError'
str_call_stack = ' '.join (call_stack)
if code == ogGlobals.OG_ERR_FORMAT or \
StringLib.ogCheckStringInGroup (str_call_stack, ogGlobals.NODEBUGFUNCTIONS) or \
not StringLib.ogCheckStringInGroup (call_stack[0], ogGlobals.NODEBUGFUNCTIONS):
not (len(call_stack)>0 and StringLib.ogCheckStringInGroup (call_stack[0], ogGlobals.NODEBUGFUNCTIONS)):
ogEcho (logtypes, "error", f"{str_call_stack.replace(' ', '<-')}: {MSG}")
return code

View File

@ -0,0 +1,117 @@
#!/usr/bin/python3
#/**
#@file restoreImage
#@brief Script de ejemplo para restaurar una imagen.
#@param $1 Repositorio (CACHE, REPO o dirección IP)
#@param $2 Nombre canónico de la imagen (sin extensión)
#@param $3 Número de disco
#@param $4 Número de particion
#@param $5 Protocolo (UNICAST, UNICAST-DIRECT, MULTICAST o MULTICAST-DIRECT)
#@param $6 Opciones del protocolo
#@exception OG_ERR_FORMAT 1 formato incorrecto.
#@exception OG_ERR_NOTFOUND 2 cambio de repositorio: repositorio no encontrado
#@exception OG_ERR_NOTFOUND 2 fichero de imagen o partición no detectados.
#@exception $OG_ERR_MCASTRECEIVERFILE 57 Error en la recepción Multicast de un fichero
#@exception $OG_ERR_PROTOCOLJOINMASTER 60 Error en la conexión de una sesión Unicast|Multicast con el Master
#**/
import os
import os.path
import sys
import re
import time
import ogGlobals
import SystemLib
import NetLib
import StringLib
import FileLib
import ImageLib
import ProtocolLib
t0 = time.time()
prog = os.path.basename (sys.argv[0])
#Load engine configurator from engine.cfg file.
#Carga el configurador del engine desde el fichero engine.cfg
# Valores por defecto: #IMGPROG="partclone" ; #IMGCOMP="lzop" ; #IMGEXT="img" #IMGREDUCE="TRUE"
## (ogGlobals se encarga)
# Clear temporary file used as log track by httpdlog
# Limpia los ficheros temporales usados como log de seguimiento para httpdlog
open (ogGlobals.OGLOGCOMMAND, 'w').close()
if SystemLib.ogGetCaller() not in ['deployImage', 'restoreImageCustom']:
open (ogGlobals.OGLOGSESSION, 'w').close()
SystemLib.ogEcho (['log', 'session'], None, f'[1] {ogGlobals.lang.MSG_SCRIPTS_START} {prog} ({sys.argv})')
# Procesar parámetros de entrada
print (f'argv ({sys.argv}) len ({len (sys.argv)})')
if len (sys.argv) < 6:
SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST opciones protocolo]')
sys.exit (1)
_, repo, imgname, disk, par, *other = sys.argv
proto = other[0].upper() if len (other) > 0 else 'UNICAST'
protoopt = other[1] if len (other) > 1 else ''
repo = repo.upper()
# Si MCASTWAIT menos que tiempo de espera del servidor lo aumento
MCASTWAIT = ogGlobals.MCASTWAIT
if ':' in protoopt:
port, wait = protoopt.split (':')
else:
port, wait = ('', '')
if proto.startswith ('MULTICAST') and re.match (r'^-?\d+$', wait):
if int (MCASTWAIT or 0) < int (wait):
MCASTWAIT = int (wait) + 5
imgtype = 'img'
print (f'repo ({repo}) imgname ({imgname}) disk ({disk}) par ({par}) proto ({proto}) protoopt ({protoopt}) MCASTWAIT ({MCASTWAIT})')
# Si es una ip y es igual a la del equipo restaura desde cache
if repo == NetLib.ogGetIpAddress(): repo = 'CACHE'
# Si es una ip y es distinta a la del recurso samba cambiamos de REPO.
if StringLib.ogCheckIpAddress (repo) or 'REPO' == repo:
# Si falla el cambio -> salimos con error repositorio no valido
if not NetLib.ogChangeRepo (repo):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, repo)
sys.exit (1)
REPO = 'REPO'
# Comprobar que existe la imagen del origen.
imgfile = FileLib.ogGetPath (repo, f'{imgname}.{imgtype}')
imgdir = FileLib.ogGetParentPath (repo, imgname)
print (f'imgfile ({imgfile}) imgdir ({imgdir})')
if not imgfile or not imgdir:
SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_NOTFOUND, f'{repo}, {imgname}')
sys.exit (1)
# Procesar protocolos de transferencia.
retval = 0
if proto in ['UNICAST', 'UNICAST-DIRECT']:
SystemLib.ogEcho (['log', 'session'], None, f'[40] ogRestoreImage {repo} {imgname} {disk} {par} UNICAST')
retval = SystemLib.ogExecAndLog ('command', ImageLib.ogRestoreImage, repo, imgname, disk, par)
elif proto in ['MULTICAST', 'MULTICAST-DIRECT']:
tool = ImageLib.ogGetImageProgram ('REPO', imgname)
if not tool:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'could not get tool used for image {imgname}')
sys.exit (1)
compress = ImageLib.ogGetImageCompressor ('REPO', imgname)
if not compress:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'could not get compressor used for image {imgname}')
sys.exit (1)
SystemLib.ogEcho (['log', 'session'], None, f'[40] ogMcastReceiverPartition {disk} {par} {port} {tool} {compress}')
if not ProtocolLib.ogMcastRequest (f'{imgname}.img', protoopt):
sys.exit (1)
retval = SystemLib.ogExecAndLog ('command', ProtocolLib.ogMcastReceiverPartition, disk, par, port, tool, compress)
else:
SystemLib.ogRaiseError ('session', ogGlobals.OG_ERR_FORMAT, f'{ogGlobals.lang.MSG_FORMAT}: {prog} REPO|CACHE imagen ndisco nparticion [ UNICAST|MULTICAST opciones protocolo]')
sys.exit (1)
t = time.time() - t0
SystemLib.ogEcho (['log', 'session'], None, f'[100] Duracion de la operacion {int (t/60)}m {int (t%60)}s')
# Código de salida del comando prinicpal de restauración.
sys.exit (retval)