summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose M. Guisado <jguisado@soleta.eu>2022-07-05 17:11:26 +0200
committerJose M. Guisado <jguisado@soleta.eu>2022-08-24 09:48:35 +0200
commit3550da73e6da062d69dd0d7f1f6889b684abb15d (patch)
tree56d75bcc720468d6bebe889d21e06148e7322033
parent74a61d6a7d71fa0bfb2a762bad7f754f33f63895 (diff)
image_create: partial integration into pythonv1.2.2
Integrates some parts of this operation into native code, eg: the md5 checksum computation. Wraps non native processes and commands using the subprocess module. For example, legacy.py stores bash commands pending integration. Supports python >=3.6, expected until more modern ogLives are put into production environments.
-rw-r--r--src/live/ogOperations.py105
-rw-r--r--src/utils/fs.py28
-rw-r--r--src/utils/legacy.py55
3 files changed, 162 insertions, 26 deletions
diff --git a/src/live/ogOperations.py b/src/live/ogOperations.py
index 1b2c3dd..439d1d7 100644
--- a/src/live/ogOperations.py
+++ b/src/live/ogOperations.py
@@ -6,9 +6,13 @@
# Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
+import hashlib
import logging
import os
import subprocess
+import shlex
+
+from subprocess import Popen, PIPE
import fdisk
@@ -16,9 +20,10 @@ from src.ogClient import ogClient
from src.ogRest import ThreadState
from src.live.partcodes import GUID_MAP
+from src.utils.legacy import *
from src.utils.net import ethtool
from src.utils.menu import generate_menu
-from src.utils.fs import mount_mkdir, umount, get_usedperc
+from src.utils.fs import *
from src.utils.probe import os_probe, cache_probe
from src.utils.disk import get_disks
from src.utils.cache import generate_cache_txt
@@ -30,6 +35,8 @@ class OgLiveOperations:
def __init__(self, config):
self._url = config['opengnsys']['url']
self._url_log = config['opengnsys']['url_log']
+ self._smb_user = config['samba']['user']
+ self._smb_pass = config['samba']['pass']
def _restartBrowser(self, url):
try:
@@ -91,6 +98,25 @@ class OgLiveOperations:
part_setup['filesystem'] = 'CACHE'
part_setup['code'] = 'ca'
+ def _compute_md5(self, path, bs=2**20):
+ m = hashlib.md5()
+ with open(path, 'rb') as f:
+ while True:
+ buf = f.read(bs)
+ if not buf:
+ break
+ m.update(buf)
+ return m.hexdigest()
+
+ def _write_md5_file(self, path):
+ if not os.path.exists(path):
+ logging.error('Invalid path in _write_md5_file')
+ raise ValueError('Invalid image path when computing md5 checksum')
+ filename = path + ".full.sum"
+ dig = self._compute_md5(path)
+ with open(filename, 'w') as f:
+ f.write(dig)
+
def poweroff(self):
logging.info('Powering off client')
if os.path.exists('/scripts/oginit'):
@@ -262,17 +288,20 @@ class OgLiveOperations:
return output.decode('utf-8')
def image_create(self, path, request, ogRest):
- disk = request.getDisk()
- partition = request.getPartition()
+ disk = int(request.getDisk())
+ partition = int(request.getPartition())
name = request.getName()
repo = request.getRepo()
cmd_software = f'{ogClient.OG_PATH}interfaceAdm/InventarioSoftware {disk} ' \
f'{partition} {path}'
- cmd_create_image = f'{ogClient.OG_PATH}interfaceAdm/CrearImagen {disk} ' \
- f'{partition} {name} {repo}'
+ image_path = f'/opt/opengnsys/images/{name}.img'
self._restartBrowser(self._url_log)
+ if ogChangeRepo(repo).returncode != 0:
+ logging.error('ogChangeRepo could not change repository to %s', repo)
+ raise ValueError(f'Error: Cannot change repository to {repo}')
+
try:
ogRest.proc = subprocess.Popen([cmd_software],
stdout=subprocess.PIPE,
@@ -287,31 +316,57 @@ class OgLiveOperations:
return
try:
- ogRest.proc = subprocess.Popen([cmd_create_image],
- stdout=subprocess.PIPE,
- shell=True,
- executable=OG_SHELL)
- ogRest.proc.communicate()
- except:
- logging.error('Exception when running "image create" subprocess')
- raise ValueError('Error: Incorrect command value')
+ diskname = get_disks()[disk-1]
+ cxt = fdisk.Context(f'/dev/{diskname}', details=True)
+ pa = None
- if ogRest.proc.returncode != 0:
- logging.warn('Image creation failed')
- raise ValueError('Error: Image creation failed')
+ for i, p in enumerate(cxt.partitions):
+ if (p.partno + 1) == partition:
+ pa = cxt.partitions[i]
- with open('/tmp/image.info') as file_info:
- line = file_info.readline().rstrip()
+ if pa is None:
+ logging.error('Target partition not found')
+ raise ValueError('Target partition number not found')
- image_info = {}
+ padev = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE)
+ fstype = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_FSTYPE)
+ if not fstype:
+ logging.error('No filesystem detected. Aborting image creation.')
+ raise ValueError('Target partition has no filesystem present')
- (image_info['clonator'],
- image_info['compressor'],
- image_info['filesystem'],
- image_info['datasize'],
- image_info['clientname']) = line.split(':', 5)
+ cambiar_acceso(user=self._smb_user, pwd=self._smb_pass)
+ ogReduceFs(disk, partition)
+
+ cmd1 = shlex.split(f'partclone.{fstype} -I -C --clone -s {padev} -O -')
+ cmd2 = shlex.split(f'lzop -1 -fo {image_path}')
+
+ logfile = open('/tmp/command.log', 'wb', 0)
+
+ p1 = Popen(cmd1, stdout=PIPE, stderr=logfile)
+ p2 = Popen(cmd2, stdin=p1.stdout)
+ p1.stdout.close()
+
+ try:
+ retdata = p2.communicate()
+ except OSError as e:
+ logging.error('Unexpected error when running partclone and lzop commands')
+ finally:
+ logfile.close()
+ p2.terminate()
+ p1.poll()
+
+ logging.info(f'partclone process exited with code {p1.returncode}')
+ logging.info(f'lzop process exited with code {p2.returncode}')
+
+ if ogExtendFs(disk, partition) != 0:
+ logging.warn('Error extending filesystem after image creation')
+
+ image_info = ogGetImageInfo(image_path)
+ except:
+ logging.error('Exception when running "image create" subprocess')
+ raise ValueError('Error: Incorrect command value')
- os.remove('/tmp/image.info')
+ self._write_md5_file(f'/opt/opengnsys/images/{name}.img')
self._restartBrowser(self._url)
diff --git a/src/utils/fs.py b/src/utils/fs.py
index 191fb86..cd58383 100644
--- a/src/utils/fs.py
+++ b/src/utils/fs.py
@@ -6,10 +6,11 @@
# Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
+import logging
import os
import subprocess
-from subprocess import DEVNULL
+from subprocess import DEVNULL, PIPE
import psutil
@@ -74,3 +75,28 @@ def get_usedperc(mountpoint):
except FileNotFoundError:
return '0'
return str(perc)
+
+
+def ogReduceFs(disk, part):
+ """
+ Bash function 'ogReduceFs' wrapper
+ """
+ proc = subprocess.run(f'ogReduceFs {disk} {part}',
+ shell=True, stdout=PIPE)
+
+ if proc.returncode == 0:
+ subprocess.run(f'ogUnmount {disk} {part}',
+ shell=True, stdout=PIPE)
+ return proc.stdout.decode().replace('\n', '')
+
+ logging.warn(f'ogReduceFS exited with code {proc.returncode}')
+ return None
+
+
+def ogExtendFs(disk, part):
+ """
+ Bash function 'ogExtendFs' wrapper
+ """
+ proc = subprocess.run(f'ogExtendFs {disk} {part}',
+ shell=True)
+ return proc.returncode
diff --git a/src/utils/legacy.py b/src/utils/legacy.py
new file mode 100644
index 0000000..7ea2bd6
--- /dev/null
+++ b/src/utils/legacy.py
@@ -0,0 +1,55 @@
+import ipaddress
+import os
+import subprocess
+import shlex
+
+from subprocess import PIPE
+
+def ogGetImageInfo(path):
+ """
+ Bash function 'ogGetImageInfo' wrapper (client/engine/Image.lib)
+ """
+ proc = subprocess.run(f'ogGetImageInfo {path}',
+ stdout=PIPE, shell=True,
+ encoding='utf-8')
+
+ if proc.stdout.count(':') != 3:
+ return ''
+
+ image_info = {}
+ (image_info['clonator'],
+ image_info['compressor'],
+ image_info['filesystem'],
+ image_info['datasize']) = proc.stdout.rstrip().split(':', 4)
+ image_info['clientname'] = os.getenv('HOSTNAME')
+ return image_info
+
+
+def cambiar_acceso(mode='rw', user='opengnsys', pwd='og'):
+ """
+ 'CambiarAcceso' wrapper (admin/Interface/CambiarAcceso)
+ """
+ if mode not in ['rw', 'ro']:
+ raise ValueError('Invalid remount mode option')
+
+ cmd = shlex.split(f'mount -o remount,{mode},username={user},password={pwd} /opt/opengnsys/images')
+ ret = True
+ try:
+ subprocess.run(cmd, check=True)
+ except CalledProcessError:
+ ret = False
+ finally:
+ return ret
+
+
+def ogChangeRepo(ip):
+ """
+ Bash function 'ogGetImageInfo' wrapper (client/engine/Net.lib)
+ """
+ try:
+ ipaddr = ipaddress.ip_address(ip)
+ except ValueError as e:
+ raise
+
+ return subprocess.run(f'ogChangeRepo {ipaddr}',
+ shell=True)