refs #1101 add ogMcastSyntax()

pull/1/head
Natalia Serrano 2024-11-12 19:00:27 +01:00
parent 8e2f35f89e
commit 6e30bc1bdd
6 changed files with 233 additions and 14 deletions

View File

@ -425,7 +425,7 @@ def ogGetMountPoint(disk, par):
#@return Código de salida: True - formateado, False - sin formato o error.
#*/ ##
def ogIsFormated(disk, part):
PART = ogDiskToDev(disk, part)
PART = DiskLib.ogDiskToDev (disk, part)
if not PART:
return

View File

@ -100,7 +100,7 @@ def ogCreateImageSyntax (dev, imgfile, tool='partclone', level='gzip'):
'bzip': ' | bzip -c > ',
}.get (level, ' > ')
print (f'param1 ({param1}) param2 ({param2}) param3 ({param3}) imgfile ({imgfile})')
#print (f'param1 ({param1}) param2 ({param2}) param3 ({param3}) imgfile ({imgfile})')
if param1: return f'{param1} {param2} {param3} {imgfile}'
@ -148,14 +148,14 @@ def ogRestoreImageSyntax (imgfile, part, tool=None, level=None):
3: ' bzip -dc ',
'bzip': ' bzip -dc ',
}.get (level, '')
print (f'tool ({tool}) level ({level}) compressor ({compressor})')
#print (f'tool ({tool}) level ({level}) compressor ({compressor})')
if compressor == '':
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'Compressor no valid {level}')
## original bash code is broken: 'return' is never called
#return
mbuffer = '| mbuffer -q -m 40M ' if shutil.which ('mbuffer') else ' '
print (f'mbuffer ({mbuffer})')
#print (f'mbuffer ({mbuffer})')
if 'ntfsclone' == tool:
tool = f'| ntfsclone --restore-image --overwrite {part} -'
elif 'partimage' == tool:
@ -169,7 +169,7 @@ def ogRestoreImageSyntax (imgfile, part, tool=None, level=None):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'Tools imaging no valid {tool}')
## original bash code is broken: 'return' is never called
#return
print (f'tool ({tool})')
#print (f'tool ({tool})')
return f'{compressor} {imgfile} {mbuffer} {tool}'.strip()
@ -202,7 +202,7 @@ def ogGetImageInfo (imgfile):
## original bash idiom is: $($COMPRESSOR -dc $IMGFILE 2>/dev/null | head -n 40 > $FILEHEAD) || ogRaiseError
## the purpose of which I can't fully comprehend
print (f'shelling out "{compressor} -dc {imgfile} |head -n 40 > {filehead}"')
#print (f'shelling out "{compressor} -dc {imgfile} |head -n 40 > {filehead}"')
if subprocess.run (f'{compressor} -dc {imgfile} |head -n 40 > {filehead}', shell=True).returncode:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_IMAGE, f'Image format is not valid {imgfile}')
return
@ -256,7 +256,7 @@ def ogGetImageInfo (imgfile):
if False == imgdetect and not os.path.exists ('/dev/loop2'):
filehead_contents = Path (filehead).read_bytes()
if b'ntfsclone-image' in filehead_contents:
print (f'shelling out "cat {filenead} | ntfsclone --restore --overwrite /dev/loop2 - 2>&1"')
#print (f'shelling out "cat {filenead} | ntfsclone --restore --overwrite /dev/loop2 - 2>&1"')
ntfscloneinfo = subprocess.run (f'cat {filenead} | ntfsclone --restore --overwrite /dev/loop2 - 2>&1', shell=True, capture_output=True, text=True).stdout
#ntfscloneinfo = subprocess.run (['cat', '/tmp/foo-ntfsclone'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout
## sacado de claude 3 haiku

View File

@ -247,8 +247,8 @@ def ogGetRepoIp():
return None
if 'filesystems' not in j: return None
source = j['filesystems']['source']
fstype = j['filesystems']['fstype']
source = j['filesystems'][0]['source']
fstype = j['filesystems'][0]['fstype']
if 'nfs' == fstype: return source.split(":")[0]
elif 'cifs' == fstype: return source.split("/")[2]

View File

@ -1,8 +1,15 @@
#!/usr/bin/python3
import subprocess
import re
import json
import ogGlobals
import SystemLib
import ImageLib
import FileSystemLib
import StringLib
import NetLib
#/**
#@file ProtocolLib.py
@ -42,11 +49,11 @@ def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
raise TypeError ('missing required argument: "device"')
if tool is None:
raise TypeError ('missing required argument: "tool"')
if tool not in ['partclone', 'PARTCLONE', 'partimage', 'PARTIMAGE', 'ntfsclone', 'NTFSCLONE']:
if tool.lower() not in ['partclone', 'partimage', 'ntfsclone']:
raise TypeError (f'argument "tool" has unsupported value "{tool}"')
if level is None:
raise TypeError ('missing required argument: "level"')
if level not in ['lzop', 'gzip', 'LZOP', 'GZIP', '0', '1']:
if level.lower() not in ['lzop', 'gzip', '0', '1']:
raise TypeError (f'argument "level" has unsupported value "{level}"')
elif 'SENDFILE' == op or 'RECEIVERFILE' == op:
if file is None:
@ -70,7 +77,7 @@ def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
address += f'-O {session[i]}:{portbase}'
else:
address = f'{session[1]}:{portbase}'
print (f'nati mode ({mode}) address ({address})')
#print (f'nati mode ({mode}) address ({address})')
if 'SENDPARTITION' == op:
syn = ImageLib.ogCreateImageSyntax (device, ' ', tool, level)
@ -78,7 +85,7 @@ def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
## otherwise, param2 in ImageLib.ogCreateImageSyntax() is not '| mbuffer' but empty
## and then parts[2] is out of range
parts = syn.split ('|')
print (f'syn ({syn}) parts ({parts})')
#print (f'syn ({syn}) parts ({parts})')
prog1 = f'{parts[0]}|{parts[2]}'.strip()
prog1 = prog1.replace ('>', '').strip()
return f'{prog1} | mbuffer {address}'
@ -140,6 +147,28 @@ def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
#*/ ##
#
def _clientip():
ipas = subprocess.run (['ip', '-json', 'address', 'show', 'up'], capture_output=True, text=True).stdout
ipasj = json.loads (ipas)
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:
if 'inet' != a['family']: continue
addresses.append ({ 'local': a['local'], 'prefixlen': a['prefixlen'] })
return addresses
def _binary_ip (ip):
for l in subprocess.run (['ipcalc', '--nocolor', ip ], capture_output=True, text=True).stdout.splitlines():
if 'Address' not in l: continue
match = re.search (r'^(Address:)\s+(\S+)\s+(.*$)', l).group(3).replace (' ', '').replace ('.', '')
break
return match
#/**
# ogMcastSyntax
@ -158,6 +187,168 @@ def ogUcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
#*/ ##
#
#ogMcastSyntax SENDPARTITION 9000:full-duplex|half-duplex|broadcast:239.194.17.36:80M:50:60 device tools level
#ogMcastSyntax RECEIVERPARTITION 9000 device tools level
#ogMcastSyntax RECEIVERPARTITION 9000:172.17.88.161:40:120 device tools level
#ogMcastSyntax SENDFILE 9000:full-duplex|half-duplex|broadcast:239.194.17.36:80M:50:60 file
#ogMcastSyntax RECEIVERFILE 9000 file
#ogMcastSyntax RECEIVERFILE 9000:172.17.88.161:40:120 file
def ogMcastSyntax (op, sess, file=None, device=None, tool=None, level=None):
if 'SENDPARTITION' == op or 'RECEIVERPARTITION' == op:
if device is None:
raise TypeError ('missing required argument: "device"')
if tool is None:
raise TypeError ('missing required argument: "tool"')
if tool.lower() not in ['partclone', 'partimage', 'ntfsclone']:
raise TypeError (f'argument "tool" has unsupported value "{tool}"')
if level is None:
raise TypeError ('missing required argument: "level"')
if level.lower() not in ['lzop', 'gzip', '0', '1']:
raise TypeError (f'argument "level" has unsupported value "{level}"')
elif 'SENDFILE' == op or 'RECEIVERFILE' == op:
if file is None:
raise TypeError ('missing required argument: "file"')
else:
raise TypeError ('first parameter should match (SEND|RECEIVER)(PARTITION|FILE), eg. "SENDFILE"')
if 'SEND' in op: mode = 'server'
else: mode = 'client'
try:
isudpcast = subprocess.run (['udp-receiver', '--help'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout
except subprocess.CalledProcessError:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTEXEC, 'upd-cast no existe')
return
session = sess.split (':')
PERROR = 0
if 'server' == mode:
if 6 != len (session):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'parametros session de servidor multicast no completa')
PERROR = 2
elif 'client' == mode:
if 4 < len (session):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'parametros session de cliente multicast no completa')
PERROR = 2
mbuffer = " --pipe 'mbuffer -q -m 20M' "
portbase = int (session[0])
if portbase not in range (9000, 9100, 2):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession portbase {portbase}')
PERROR = 3
if 'server' == mode:
method, address, bitrate, nclients, maxtime = session[1:]
if method.lower() not in ['full-duplex', 'half-duplex', 'broadcast']:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession method {method}')
PERROR = 4
if not StringLib.ogCheckIpAddress (address):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession address {address}')
PERROR = 5
## the original regex has a backslash: ^[0-9]{1,3}\M$
## not sure why
if not re.search (r'^[0-9]{1,3}M$', bitrate):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession bitrate {bitrate}')
PERROR = 6
if not re.search (r'^[0-9]{1,10}$', nclients):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession nclients {nclients}')
PERROR = 7
if not re.search (r'^[0-9]{1,10}$', maxtime):
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, f'McastSession maxtime {maxtime}')
PERROR = 8
cerror = '8x8/128'
syntaxserver = f'udp-sender {mbuffer} --nokbd --portbase {portbase} --{method} --mcast-data-address {address} --fec {cerror} --max-bitrate {bitrate} --ttl 16 --min-clients {nclients} --max-wait {maxtime} --autostart {maxtime} --log /tmp/mcast.log'
if PERROR:
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_MCASTSYNTAXT, f' {PERROR}')
return
if 'client' == mode:
other = session[1:]
#print (f'session ({session}) other ({other})')
serveraddress = other[0] if len (other) > 0 else ''
starttimeout = other[1] if len (other) > 1 else ''
receivertimeout = other[2] if len (other) > 2 else ''
## serveraddres
if StringLib.ogCheckIpAddress (serveraddress):
serveraddress = f' --mcast-rdv-address {serveraddress}'
else:
repoip = NetLib.ogGetRepoIp()
clientip = _clientip()
#print (f'repoip ({repoip}) clientip ({clientip})')
if 1 != len (clientip):
raise Exception ('more than one local IP address found')
c = clientip[0]
#print (f'c ({c})')
clientip = c['local']
mascara = c['prefixlen']
#print (f'clientip ({clientip}) mascara ({mascara})')
ripbt = _binary_ip (repoip)
ipbt = _binary_ip (clientip)
reposubred = ripbt[0:mascara]
clientsubred = ipbt[0:mascara]
#print (f'ripbt ({ripbt})')
#print (f'ipbt ({ipbt})')
#print (f'reposubred ({reposubred})')
#print (f'clientsubred ({clientsubred})')
if reposubred == clientsubred: serveraddress = ' '
else: serveraddress = f' --mcast-rdv-address {repoip}'
## starttimeout
if re.search (r'^[0-9]{1,10}$', starttimeout):
if 0 == starttimeout: starttimeout = ' '
else: starttimeout = f' --start-timeout {starttimeout}'
else:
starttimeout = f' --start-timeout {ogGlobals.MCASTERRORSESSION}'
if 'start-timeout' not in isudpcast: starttimeout = ' '
## receivertimeout
if re.search (r'^[0-9]{1,10}$', receivertimeout):
if 0 == receivertimeout: receivertimeout = ' '
else: receivertimeout = f' --receive-timeout {receivertimeout}'
else:
receivertimeout = f' --receive-timeout {ogGlobals.MCASTWAIT}'
if 'receive-timeout' not in isudpcast: receivertimeout = ' '
syntaxclient = f'udp-receiver {mbuffer} --portbase {portbase} {serveraddress} {starttimeout} {receivertimeout} --log /tmp/mcast.log'
if 'SENDPARTITION' == op:
syn = ImageLib.ogCreateImageSyntax (device, ' ', tool, level)
## REQUIRES package mbuffer to be installed!!
## otherwise, param2 in ImageLib.ogCreateImageSyntax() is not '| mbuffer' but empty
## and then parts[2] is out of range
parts = syn.split ('|')
#print (f'syn ({syn}) parts ({parts})')
prog1 = f'{parts[0]}|{parts[2]}'.strip()
prog1 = prog1.replace ('>', '').strip()
return f'{prog1} | {syntaxserver}'
elif 'RECEIVERPARTITION' == op:
syn = ImageLib.ogRestoreImageSyntax (' ', device, tool, level)
parts = syn.split ('|')
compressor = parts[0].strip()
tools = parts[-1].strip()
return f'{syntaxclient} | {compressor} | {tools} '
elif 'SENDFILE' == op:
return f'{syntaxserver} --file {file}'
elif 'RECEIVERFILE' == op:
return f'{syntaxclient} --file {file}'
else:
raise Exception (f'unknown op ({op})--this should not happen')

View File

@ -26,6 +26,18 @@ def ogCheckStringInReg(element, regex):
return re.match(regex, element) is not None
#/**
# ogCheckIpAddress
#@brief Función para determinar si una cadena es una dirección ipv4 válida
#@param 1 string de la ip a comprobar
#@return 0 si es una dirección válida
#@return 1 si NO es una dirección válida
#@exception OG_ERR_FORMAT formato incorrecto.
#@note
#@todo
#*/ ##
def ogCheckIpAddress(ip):
"""
Función para determinar si una cadena es una dirección ipv4 válida.

View File

@ -30,7 +30,23 @@ TZ='Europe/Madrid'
## engine.cfg
OGLOGSESSION='/tmp/session.log'
OGLOGCOMMAND='/tmp/command.log'
NODEBUGFUNCTIONS='ogCreateImageSyntax ogGetHivePath ogGetOsType ogRestoreImageSyntax ogUnmountAll ogUnmountCache'
#OGWINCHKDISK=True #Hacer chkdisk tras la clonacion
#ACTIONCACHEFULL=None #Que hacer cuando la cache no tenga espacio libre. [ NONE | FORMAT ] ]
#RESTOREPROTOCOLNOTCACHE=None #Que protocolo de restauracion usar en el caso de que no exista cache o no exista espacio sufiente. [NONE | UNICAST | MULTICAST].NONE retorna error
#IMGPROG='partclone'
#IMGCOMP='lzop'
#IMGEXT='img'
#IMGREDUCE=True
#OGWINREDUCE=True #Al enviar particion reducir el sistema de archivos previamente.
MCASTERRORSESSION=120 #timeout (segundos) para abortar la sesion de multicast si no contacta con el servidor de multicast. Valor asignado a 0, utiliza los valores por defecto de udp-cast
MCASTWAIT=30 # timeout (segundos) para abortar la la transferencia si se interrumpe. Valor asignado a 0, utiliza los valores por defecto de udp-cast
#CREATESPEED=100000*4 # Factor para calcular el time-out al crear la imagen. 100000k -> 4s
#FACTORSYNC=120 # Factor de compresion para las imagenes (windos en ext4).
#BACKUP=False # Realizar copia de seguridad antes de crear la imagen.
#IMGFS='EXT4' # Sistema de archivo de la imagenes sincronizadas. EXT4 o BTRFS
#OGSLEEP=20 # Tiempo de sleep antes de realizar el reboot
NODEBUGFUNCTIONS='ogCreateImageSyntax ogGetHivePath ogGetOsType ogRestoreImageSyntax ogUnmountAll ogUnmountCache' # Funciones que no deben mostrar salida de avisos si son llamadas por otras funciones.
#DEFAULTSPEED=''
## /engine.cfg