src: log backtrace in unhandled error cases

Log an error message in known error cases and log a backtrace
otherwise.

Define a new error type OgError to be used in all the 'raise'
blocks to define the error message to log. The exception
propagates until it reaches send_internal_server_error() where
the exception type is checked. If the type is OgError we log
the exception message. Logs the backtrace for other types.

The initial error implementation printed a backtrace everytime
an error ocurred. The next iteration changed it to only print
a backtrace in a very particular case but ended up omiting too
much information such as syntax errors or unknown error context.
The actual implementation only logs the cases we already cover in
the codebase and logs a bracktrace in the others, enabling a
better debugging experience.
master
Alejandro Sirgo Rica 2024-04-02 12:51:34 +02:00
parent c5ccc3c7e2
commit dfde363aa6
15 changed files with 110 additions and 93 deletions

View File

@ -20,7 +20,7 @@ except ImportError:
from src.ogClient import *
from src.log import configure_logging
from src.log import *
def send_event_dgram(msg, ip='127.0.0.1', port=55885):
@ -92,7 +92,7 @@ def main():
client = ogClient(config=CONFIG)
client.connect()
client.run()
except Exception as e:
except OgError as e:
logging.critical(e)
if __name__ == "__main__":

View File

@ -35,6 +35,7 @@ from src.utils.uefi import *
from src.utils.boot import *
from src.utils.sw_inventory import get_package_set
from src.utils.hw_inventory import get_hardware_inventory, legacy_list_hardware_inventory
from src.log import OgError
OG_SHELL = '/bin/bash'
@ -52,7 +53,7 @@ class OgLiveOperations:
proc = subprocess.call(["pkill", "-9", "browser"])
proc = subprocess.Popen(["browser", "-qws", url])
except Exception as e:
raise RuntimeError('Cannot restart browser') from e
raise OgError('Cannot restart browser') from e
def _refresh_payload_disk(self, cxt, part_setup, num_disk):
part_setup['disk'] = str(num_disk)
@ -119,7 +120,7 @@ class OgLiveOperations:
def _write_md5_file(self, path):
if not os.path.exists(path):
raise ValueError(f'Invalid image path {path} when computing md5 checksum')
raise OgError(f'Invalid image path {path} when computing md5 checksum')
filename = path + ".full.sum"
dig = self._compute_md5(path)
try:
@ -144,12 +145,12 @@ class OgLiveOperations:
r = shutil.copy(src, dst)
tip_write_csum(image_name)
except Exception as e:
raise RuntimeError(f'Error copying image {image_name} to cache. Reported: {e}') from e
raise OgError(f'Error copying image {image_name} to cache. Reported: {e}') from e
def _restore_image_unicast(self, repo, name, devpath, cache=False):
if ogChangeRepo(repo, smb_user=self._smb_user, smb_pass=self._smb_pass) != 0:
self._restartBrowser(self._url)
raise ValueError(f'Cannot change repository to {repo}')
raise OgError(f'Cannot change repository to {repo}')
logging.debug(f'restore_image_unicast: name => {name}')
if cache:
image_path = f'/opt/opengnsys/cache/opt/opengnsys/images/{name}.img'
@ -162,7 +163,7 @@ class OgLiveOperations:
def _restore_image_tiptorrent(self, repo, name, devpath):
if not os.path.exists(OG_CACHE_PATH):
raise RuntimeError('No cache partition is mounted')
raise OgError('No cache partition is mounted')
image_path = f'/opt/opengnsys/cache/opt/opengnsys/images/{name}.img'
try:
@ -172,11 +173,11 @@ class OgLiveOperations:
except:
self._restartBrowser(self._url)
if (not os.path.exists(image_path)):
raise RuntimeError(f'Image file {image_path} does not exist')
raise OgError(f'Image file {image_path} does not exist')
if (not tip_check_csum(repo, name)):
raise RuntimeError(f'checksum file {name}.full.sum is missing in repository {repo}')
raise OgError(f'checksum file {name}.full.sum is missing in repository {repo}')
raise RuntimeError(f'Unexpected error when restoring image file {image_path}')
raise OgError(f'Unexpected error when restoring image file {image_path}')
self._restore_image(image_path, devpath)
@ -188,7 +189,7 @@ class OgLiveOperations:
cmd_pc = shlex.split(f'partclone.restore -d0 -C -I -o {devpath}')
if not os.path.exists(image_path):
raise RuntimeError(f'Image not found at {image_path} during image restore')
raise OgError(f'Image not found at {image_path} during image restore')
with open('/tmp/command.log', 'wb', 0) as logfile:
proc_lzop = subprocess.Popen(cmd_lzop,
@ -245,7 +246,7 @@ class OgLiveOperations:
executable=OG_SHELL)
(output, error) = ogRest.proc.communicate()
except Exception as e:
raise RuntimeError(f'Error when running "shell run" subprocess: {e}') from e
raise OgError(f'Error when running "shell run" subprocess: {e}') from e
if ogRest.proc.returncode != 0:
logging.warn('Non zero exit code when running: %s', ' '.join(cmds))
@ -269,9 +270,9 @@ class OgLiveOperations:
partdev = get_partition_device(int(disk), int(partition))
mountpoint = partdev.replace('dev', 'mnt')
if not mount_mkdir(partdev, mountpoint):
raise RuntimeError(f'Error mounting {partdev} at {mountpoint}')
raise OgError(f'Error mounting {partdev} at {mountpoint}')
if not os.path.ismount(mountpoint):
raise RuntimeError(f'Invalid mountpoint {mountpoint} for software inventory')
raise OgError(f'Invalid mountpoint {mountpoint} for software inventory')
self._restartBrowser(self._url_log)
pkgset = get_package_set(mountpoint)
@ -294,7 +295,7 @@ class OgLiveOperations:
try:
inventory = get_hardware_inventory()
except Exception as e:
raise RuntimeError(f'Error while running hardware inventory. {e}') from e
raise OgError(f'Error while running hardware inventory. {e}') from e
finally:
self._restartBrowser(self._url)
@ -321,7 +322,7 @@ class OgLiveOperations:
elif table_type == 'GPT':
cxt.create_disklabel('gpt')
else:
raise ValueError(f'Unsupported partition scheme {table_type}, only MSDOS and GPT are supported')
raise OgError(f'Unsupported partition scheme {table_type}, only MSDOS and GPT are supported')
logging.info(f'Setting up partition layout to {table_type}')
@ -415,7 +416,7 @@ class OgLiveOperations:
if ogChangeRepo(repo, smb_user=self._smb_user, smb_pass=self._smb_pass) != 0:
self._restartBrowser(self._url)
raise RuntimeError(f'Cannot change image repository to {repo}')
raise OgError(f'Cannot change image repository to {repo}')
if ogRest.terminated:
return
@ -431,25 +432,25 @@ class OgLiveOperations:
if pa is None:
self._restartBrowser(self._url)
raise RuntimeError(f'Target partition /dev/{diskname} not found')
raise OgError(f'Target partition /dev/{diskname} not found')
padev = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE)
fstype = cxt.partition_to_string(pa, fdisk.FDISK_FIELD_FSTYPE)
if not fstype:
raise RuntimeError(f'No filesystem detected in {padev}. Aborting image creation')
raise OgError(f'No filesystem detected in {padev}. Aborting image creation')
if change_access(user=self._smb_user, pwd=self._smb_pass) == -1:
raise RuntimeError('remount of /opt/opengnsys/images has failed')
raise OgError('remount of /opt/opengnsys/images has failed')
if os.access(f'/opt/opengnsys/images', os.R_OK | os.W_OK) == False:
raise RuntimeError('Cannot access /opt/opengnsys/images in read and write mode, check permissions')
raise OgError('Cannot access /opt/opengnsys/images in read and write mode, check permissions')
if os.access(f'{image_path}', os.R_OK) == True:
logging.info(f'image file {image_path} already exists, updating.')
copy_windows_efi_bootloader(disk, partition)
if ogReduceFs(disk, partition) == -1:
raise ValueError(f'Failed to shrink {fstype} filesystem in {padev}')
raise OgError(f'Failed to shrink {fstype} filesystem in {padev}')
cmd1 = shlex.split(f'partclone.{fstype} -I -C --clone -s {padev} -O -')
cmd2 = shlex.split(f'lzop -1 -fo {image_path}')
@ -469,7 +470,7 @@ class OgLiveOperations:
try:
retdata = p2.communicate()
except OSError as e:
raise OSError(f'Unexpected error when running partclone and lzop commands: {e}') from e
raise OgError(f'Unexpected error when running partclone and lzop commands: {e}') from e
finally:
logfile.close()
p2.terminate()
@ -481,7 +482,7 @@ class OgLiveOperations:
ogExtendFs(disk, partition)
if os.access(f'{image_path}', os.R_OK) == False:
raise RuntimeError(f'Cannot access partclone image file {image_path}')
raise OgError(f'Cannot access partclone image file {image_path}')
image_info = ogGetImageInfo(image_path)
except Exception as e:
@ -492,7 +493,7 @@ class OgLiveOperations:
shutil.move(f'{image_path}.ant', image_path)
self._restartBrowser(self._url)
raise RuntimeError(f'Failed to create image for {fstype} filesystem in device {padev}: {e}') from e
raise OgError(f'Failed to create image for {fstype} filesystem in device {padev}: {e}') from e
try:
st = os.stat(image_path)
@ -510,7 +511,7 @@ class OgLiveOperations:
image_info.mtime = mtime
if self._write_md5_file(f'/opt/opengnsys/images/{name}.img') == -1:
raise ValueError(f'Cannot write {name}.full.sum file')
raise OgError(f'Cannot write {name}.full.sum file')
self._restartBrowser(self._url)

View File

@ -7,6 +7,7 @@
# (at your option) any later version.
import fdisk
from src.log import OgError
GPT_PARTTYPES = {
'LINUX-SWAP': '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F',
@ -43,15 +44,15 @@ def get_gpt_parttype(cxt, ptype_str):
def get_parttype(cxt, ptype_str):
if not cxt:
raise RuntimeError('No libfdisk context')
raise OgError('No libfdisk context')
if not cxt.label or cxt.label.name not in ['dos', 'gpt']:
raise RuntimeError('Unknown libfdisk label')
raise OgError('Unknown libfdisk label')
if type(ptype_str) != str:
raise RuntimeError('Invalid partition type')
raise OgError('Invalid partition type')
if cxt.label.name == 'dos':
return get_dos_parttype(cxt, ptype_str)
elif cxt.label.name == 'gpt':
return get_gpt_parttype(cxt, ptype_str)
else:
raise RuntimeError(f'Invalid partition label \'{cxt.label.name}\'')
raise OgError(f'Invalid partition label \'{cxt.label.name}\'')

View File

@ -11,6 +11,10 @@ import logging.config
import os
class OgError(Exception):
pass
def _default_logging_linux():
from src.utils.net import getifaddr
logconfig = {
@ -125,7 +129,7 @@ def configure_logging(mode, level):
elif mode == 'live':
logconfig = _default_logging_live()
else:
raise ValueError(f'Logging mode {mode} not supported')
raise OgError(f'Logging mode {mode} not supported')
logconfig['loggers']['']['level'] = level

View File

@ -16,6 +16,7 @@ from io import StringIO
from src.restRequest import *
from src.ogRest import *
from src.log import OgError
from enum import Enum
class State(Enum):
@ -31,7 +32,7 @@ class ogClient:
self.mode = self.CONFIG['opengnsys']['mode']
if self.mode not in {'virtual', 'live', 'linux', 'windows'}:
raise ValueError(f'Invalid ogClient mode: {self.mode}.')
raise OgError(f'Invalid ogClient mode: {self.mode}.')
if self.mode in {'linux', 'windows'}:
self.event_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.event_sock.setblocking(0)
@ -190,4 +191,4 @@ class ogClient:
message = event_sock.recv(4096).decode('utf-8').rstrip()
self.send_event_hint(message)
else:
raise ValueError(f'Invalid ogClient run state: {str(state)}.')
raise OgError(f'Invalid ogClient run state: {str(state)}.')

View File

@ -19,6 +19,7 @@ import logging
from logging.handlers import SysLogHandler
from src.restRequest import *
from src.log import OgError
class ThreadState(Enum):
@ -263,13 +264,13 @@ class ogRest():
from src.windows.ogOperations import OgWindowsOperations
self.operations = OgWindowsOperations()
else:
raise ValueError(f'Ogrest mode \'{self.mode}\'not supported')
raise OgError(f'Ogrest mode \'{self.mode}\'not supported')
def send_internal_server_error(self, client, exc=None):
if isinstance(exc, AssertionError):
logging.exception(exc)
else:
if isinstance(exc, OgError):
logging.error(exc)
else:
logging.exception(exc)
response = restResponse(ogResponses.INTERNAL_ERR, seq=client.seq)
client.send(response.get())
self.state = ThreadState.IDLE

View File

@ -8,6 +8,7 @@
import logging
import os
from src.log import OgError
def get_grub_boot_params(mountpoint, device):
@ -35,7 +36,7 @@ def get_vmlinuz_path(mountpoint):
target_file = file
if not target_file:
raise FileNotFoundError(f'vmlinuz not found in {initrd_dir}')
raise OgError(f'vmlinuz not found in {initrd_dir}')
return os.path.join(linuz_dir, target_file)
@ -48,6 +49,6 @@ def get_initrd_path(mountpoint):
target_file = file
if not target_file:
raise FileNotFoundError(f'initrd not found in {initrd_dir}')
raise OgError(f'initrd not found in {initrd_dir}')
return os.path.join(initrd_dir, target_file)

View File

@ -17,13 +17,14 @@ from src.utils.disk import get_partition_device, get_efi_partition
from src.utils.bios import *
from src.utils.uefi import *
from src.utils.fs import *
from src.log import OgError
def _boot_bios_linux(disk, part, mountpoint):
logging.info(f'Booting Linux system')
if not get_linux_distro_id(mountpoint) == 'ubuntu':
raise NotImplementedError(f'{os_probe(mountpoint)} detected, only Ubuntu is supported for legacy BIOS boot')
raise OgError(f'{os_probe(mountpoint)} detected, only Ubuntu is supported for legacy BIOS boot')
kernel_path = get_vmlinuz_path(mountpoint)
initrd_path = get_initrd_path(mountpoint)
@ -39,7 +40,7 @@ def _boot_bios_linux(disk, part, mountpoint):
subprocess.run(shlex.split(kexec_cmd), check=True, text=True)
subprocess.run(shlex.split(kexec_reboot_cmd), check=True, text=True)
except OSError as e:
raise OSError(f'Error processing kexec: {e}') from e
raise OgError(f'Error processing kexec: {e}') from e
def _boot_bios_windows(disk, part, mountpoint):
logging.info(f'Booting Windows system')
@ -52,7 +53,7 @@ def _boot_bios_windows(disk, part, mountpoint):
with open(f'{mountpoint}/ogboot.secondboot', 'w') as f:
f.write('\0' * (3072))
except OSError as e:
raise OSError(f'Could not create ogboot files in Windows partition: {e}') from e
raise OgError(f'Could not create ogboot files in Windows partition: {e}') from e
def _boot_uefi_windows(disk, part, mountpoint):
logging.info(f'Booting windows system')
@ -60,7 +61,7 @@ def _boot_uefi_windows(disk, part, mountpoint):
esp, esp_disk, esp_part_number = get_efi_partition(disk, enforce_gpt=True)
esp_mountpoint = esp.replace('dev', 'mnt')
if not mount_mkdir(esp, esp_mountpoint):
raise RuntimeError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
raise OgError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
loader_paths = [f'{esp_mountpoint}/EFI/{bootlabel}/Boot/bootmgfw.efi',
f'{esp_mountpoint}/EFI/Microsoft/Boot/bootmgfw.efi']
@ -71,7 +72,7 @@ def _boot_uefi_windows(disk, part, mountpoint):
logging.info(f'Found bootloader at ESP partition: {loader}')
break
else:
raise RuntimeError(f'Unable to locate Windows EFI bootloader bootmgfw.efi')
raise OgError(f'Unable to locate Windows EFI bootloader bootmgfw.efi')
efibootmgr_delete_bootentry(bootlabel)
efibootmgr_create_bootentry(esp_disk, esp_part_number, loader, bootlabel)
@ -85,7 +86,7 @@ def _boot_uefi_linux(disk, part, mountpoint):
esp, esp_disk, esp_part_number = get_efi_partition(disk, enforce_gpt=False)
esp_mountpoint = esp.replace('dev', 'mnt')
if not mount_mkdir(esp, esp_mountpoint):
raise RuntimeError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
raise OgError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
loader_paths = [f'{esp_mountpoint}/EFI/{bootlabel}/Boot/shimx64.efi',
f'{esp_mountpoint}/EFI/ubuntu/shimx64.efi']
@ -96,7 +97,7 @@ def _boot_uefi_linux(disk, part, mountpoint):
logging.info(f'Found bootloader at ESP partition: {loader}')
break
else:
raise RuntimeError(f'Unable to locate Linux EFI bootloader shimx64.efi')
raise OgError(f'Unable to locate Linux EFI bootloader shimx64.efi')
efibootmgr_delete_bootentry(bootlabel)
efibootmgr_create_bootentry(esp_disk, esp_part_number, loader, bootlabel)
@ -109,7 +110,7 @@ def boot_os_at(disk, part):
device = get_partition_device(disk, part)
mountpoint = device.replace('dev', 'mnt')
if not mount_mkdir(device, mountpoint):
raise RuntimeError(f'Cannot probe OS family. Unable to mount {device} into {mountpoint}')
raise OgError(f'Cannot probe OS family. Unable to mount {device} into {mountpoint}')
is_uefi = is_uefi_supported()
if is_uefi:
@ -130,6 +131,6 @@ def boot_os_at(disk, part):
elif not is_uefi and os_family == OSFamily.LINUX:
_boot_bios_linux(disk, part, mountpoint)
else:
raise RuntimeError(f'Unknown OS family {os_family}')
raise OgError(f'Unknown OS family {os_family}')
finally:
umount(mountpoint)

View File

@ -8,6 +8,7 @@
import os
import logging
from src.log import OgError
import fdisk
@ -29,7 +30,7 @@ def get_partition_device(disknum, partnum):
"""
disk_index = disknum - 1
if disk_index < 0 or disk_index >= len(get_disks()):
raise ValueError(f'Invalid disk number {disknum}, {len(get_disks())} disks available.')
raise OgError(f'Invalid disk number {disknum}, {len(get_disks())} disks available.')
disk = get_disks()[disk_index]
cxt = fdisk.Context(f'/dev/{disk}')
@ -38,7 +39,7 @@ def get_partition_device(disknum, partnum):
if pa.partno == partnum - 1:
return cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE)
raise ValueError(f'No such partition with disk index {disknum} and partition index {partnum}')
raise OgError(f'No such partition with disk index {disknum} and partition index {partnum}')
def get_efi_partition(disknum, enforce_gpt):
@ -55,16 +56,16 @@ def get_efi_partition(disknum, enforce_gpt):
"""
disk_index = disknum - 1
if disk_index < 0 or disk_index >= len(get_disks()):
raise ValueError(f'Invalid disk number {disknum} when trying to find ESP, {len(get_disks())} disks available.')
raise OgError(f'Invalid disk number {disknum} when trying to find ESP, {len(get_disks())} disks available.')
disk = get_disks()[disk_index]
cxt = fdisk.Context(f'/dev/{disk}')
if enforce_gpt and cxt.label == fdisk.FDISK_DISKLABEL_DOS:
raise RuntimeError(f'Windows EFI System requires GPT partition scheme, but /dev/{disk} has DOS partition scheme')
raise OgError(f'Windows EFI System requires GPT partition scheme, but /dev/{disk} has DOS partition scheme')
for pa in cxt.partitions:
logging.info(f'Checking partition "{pa.type.name}"...')
if pa.type.name == 'EFI System':
return cxt.partition_to_string(pa, fdisk.FDISK_FIELD_DEVICE), f'/dev/{disk}', pa.partno + 1
raise RuntimeError(f'Cannot find "EFI System" partition at /dev/{disk}')
raise OgError(f'Cannot find "EFI System" partition at /dev/{disk}')

View File

@ -10,6 +10,7 @@ import logging
import os
import subprocess
import shlex
from src.log import OgError
from subprocess import DEVNULL, PIPE, STDOUT
@ -148,12 +149,12 @@ def mkfs(fs, disk, partition, label=None):
}
if fs not in fsdict:
raise ValueError(f'mkfs failed, unsupported target filesystem {fs}')
raise OgError(f'mkfs failed, unsupported target filesystem {fs}')
try:
partdev = get_partition_device(disk, partition)
except ValueError as e:
raise ValueError(f'mkfs aborted: {e}') from e
raise OgError(f'mkfs aborted: {e}') from e
fsdict[fs](partdev, label)
@ -249,11 +250,11 @@ def _reduce_ntfsresize(partdev):
data_split = output_data.split(pattern)
# If we fail to match pattern in the split then data_split will contain [output_data]
if len(data_split) == 1:
raise ValueError(f'nfsresize: failed to find: {pattern}')
raise OgError(f'nfsresize: failed to find: {pattern}')
value_str = data_split[1].split(' ')[0]
if not value_str.isdigit() or value_str.startswith('-'):
raise ValueError(f'nfsresize: failed to parse numeric value at {pattern}')
raise OgError(f'nfsresize: failed to parse numeric value at {pattern}')
return int(value_str)
try:
@ -307,11 +308,11 @@ def _extend_resize2fs(partdev):
cmd = shlex.split(f'resize2fs -f {partdev}')
proc = subprocess.run(cmd)
if proc.returncode != 0:
raise RuntimeError(f'Error growing ext4 filesystem at {partdev}')
raise OgError(f'Error growing ext4 filesystem at {partdev}')
def _extend_ntfsresize(partdev):
cmd = shlex.split(f'ntfsresize -f {partdev}')
proc = subprocess.run(cmd, input=b'y')
if proc.returncode != 0:
raise RuntimeError(f'Error growing ntfs filesystem at {partdev}')
raise OgError(f'Error growing ntfs filesystem at {partdev}')

View File

@ -10,6 +10,7 @@ import json
import os.path
import shlex
import subprocess
from src.log import OgError
from collections import namedtuple
from enum import Enum, auto
@ -55,16 +56,16 @@ class HardwareInventory():
def add_element(self, elem):
if elem.type not in HardwareType:
raise ValueError(f'Unsupported hardware type, received {elem.type}')
raise OgError(f'Unsupported hardware type, received {elem.type}')
if not elem.name:
raise ValueError('Empty hardware element name')
raise OgError('Empty hardware element name')
self.elements.append(elem)
def _bytes_to_human(size):
suffixes = ['B', 'MiB', 'GiB', 'TiB']
if type(size) is not int:
raise TypeError(f'Invalid type in _bytes_to_human, got: {size} {type(size)}')
raise OgError(f'Invalid type in _bytes_to_human, got: {size} {type(size)}')
for exponent, suffix in enumerate(suffixes, start=1):
conv = size / (1024**exponent)
if conv < 1024:
@ -250,7 +251,7 @@ def legacy_hardware_element(element):
represented as "vga=Foo"
"""
if type(element) is not HardwareElement:
raise TypeError('Invalid hardware element type')
raise OgError('Invalid hardware element type')
elif element.type is HardwareType.MULTIMEDIA:
nemonic = 'mul'
elif element.type is HardwareType.BOOTMODE:
@ -297,7 +298,7 @@ def get_hardware_inventory():
if type(j) is list:
root = j[0]
if type(root) is not dict:
raise ValueError('Invalid lshw json output')
raise OgError('Invalid lshw json output')
inventory = HardwareInventory()
_fill_computer_model(inventory, root)

View File

@ -16,6 +16,7 @@ import shutil
from subprocess import PIPE, DEVNULL, STDOUT, CalledProcessError
from src.utils.fs import umount
from src.log import OgError
class ImageInfo:
@ -75,9 +76,9 @@ def image_info_from_partclone(partclone_output):
fill_imageinfo(line, image_info)
if not image_info.datasize:
raise ValueError("Missing device size from partclone.info output")
raise OgError("Missing device size from partclone.info output")
elif not image_info.filesystem:
raise ValueError("Missing filesystem from partclone.info output")
raise OgError("Missing filesystem from partclone.info output")
return image_info
@ -100,7 +101,7 @@ def run_lzop_partcloneinfo(image_path):
p2_out, p2_err = p2.communicate()
if p2.returncode != 0:
raise ValueError(f'Unable to process image {image_path}')
raise OgError(f'Unable to process image {image_path}')
return p2_out
@ -174,7 +175,7 @@ def ogChangeRepo(ip, smb_user='opengnsys', smb_pass='og'):
try:
ipaddr = ipaddress.ip_address(ip)
except ValueError as e:
raise ValueError(f'Invalid IP address {ip} received')
raise OgError(f'Invalid IP address {ip} received') from e
mounted = False
with open('/etc/mtab') as f:
@ -209,7 +210,7 @@ def restoreImageCustom(repo_ip, image_name, disk, partition, method):
"""
"""
if not shutil.which('restoreImageCustom'):
raise OSError('restoreImageCustom not found')
raise OgError('restoreImageCustom not found')
cmd = f'restoreImageCustom {repo_ip} {image_name} {disk} {partition} {method}'
with open('/tmp/command.log', 'wb', 0) as logfile:
@ -220,7 +221,7 @@ def restoreImageCustom(repo_ip, image_name, disk, partition, method):
shell=True,
check=True)
except OSError as e:
raise OSError(f'Error processing restoreImageCustom: {e}') from e
raise OgError(f'Error processing restoreImageCustom: {e}') from e
return proc.returncode
@ -240,6 +241,6 @@ def configureOs(disk, partition):
check=True)
out = proc.stdout
except OSError as e:
raise OSError(f'Error processing configureOsCustom: {e}') from e
raise OgError(f'Error processing configureOsCustom: {e}') from e
return out

View File

@ -13,6 +13,7 @@ import shlex
import shutil
import subprocess
import urllib.request
from src.log import OgError
def _compute_md5(path, bs=2**20):
m = hashlib.md5()
@ -33,9 +34,9 @@ def tip_fetch_csum(tip_addr, image_name):
with urllib.request.urlopen(f'{url}') as resp:
r = resp.readline().rstrip().decode('utf-8')
except urllib.error.URLError as e:
raise urllib.error.URLError(f'URL error when fetching checksum: {e.reason}') from e
raise OgError(f'URL error when fetching checksum: {e.reason}') from e
except urllib.error.HTTPError as e:
raise urllib.error.URLError(f'HTTP Error when fetching checksum: {e.reason}') from e
raise OgError(f'HTTP Error when fetching checksum: {e.reason}') from e
return r
@ -46,7 +47,7 @@ def tip_write_csum(image_name):
image_path = f'/opt/opengnsys/cache/opt/opengnsys/images/{image_name}.img'
if not os.path.exists(image_path):
raise RuntimeError(f'Invalid image path {image_path} for tiptorrent checksum writing')
raise OgError(f'Invalid image path {image_path} for tiptorrent checksum writing')
filename = image_path + ".full.sum"
csum = _compute_md5(image_path)
@ -62,7 +63,7 @@ def tip_check_csum(tip_addr, image_name):
logging.info(f'Verifying checksum for {image_name}.img, please wait...')
image_path = f'/opt/opengnsys/cache/opt/opengnsys/images/{image_name}.img'
if not os.path.exists(image_path):
raise RuntimeError(f'Invalid image path {image_path} for tiptorrent image csum comparison')
raise OgError(f'Invalid image path {image_path} for tiptorrent image csum comparison')
cache_csum = _compute_md5(image_path)
remote_csum = tip_fetch_csum(tip_addr, image_name)
@ -85,12 +86,12 @@ def tip_client_get(tip_addr, image_name):
cwd='/opt/opengnsys/cache/opt/opengnsys/images/')
proc.communicate()
except OSError as e:
raise OSError('Unexpected error running tiptorrent subprocess: {e}') from e
raise OgError('Unexpected error running tiptorrent subprocess: {e}') from e
finally:
logfile.close()
if proc.returncode != 0:
raise RuntimeError(f'Error fetching image {image_name} via tiptorrent')
raise OgError(f'Error fetching image {image_name} via tiptorrent')
else:
logging.info('Calculating checksum...')
logging.info('*DO NOT REBOOT OR POWEROFF* the client during this time')

View File

@ -15,6 +15,7 @@ import shutil
from src.utils.disk import *
from src.utils.fs import *
from src.utils.probe import *
from src.log import OgError
import fdisk
@ -25,14 +26,14 @@ def _find_bootentry(entries, label):
if entry['description'] == label:
return entry
else:
raise NameError('Boot entry {label} not found')
raise OgError('Boot entry {label} not found')
def _strip_boot_prefix(entry):
try:
num = entry['name'][4:]
except:
raise KeyError('Unable to strip "Boot" prefix from boot entry')
raise OgError('Unable to strip "Boot" prefix from boot entry')
return num
@ -56,7 +57,7 @@ def is_uefi_supported():
def run_efibootmgr_json():
if _check_efibootmgr_json() is False:
raise RuntimeError(f'{EFIBOOTMGR_BIN} not available')
raise OgError(f'{EFIBOOTMGR_BIN} not available')
proc = subprocess.run([EFIBOOTMGR_BIN, '--json'], capture_output=True, text=True)
dict_json = json.loads(proc.stdout)
@ -99,14 +100,14 @@ def efibootmgr_create_bootentry(disk, part, loader, label, add_to_bootorder=True
try:
proc = subprocess.run(shlex.split(efibootmgr_cmd), check=True, text=True)
except OSError as e:
raise OSError(f'Unexpected error adding boot entry to nvram. UEFI firmware might be buggy') from e
raise OgError(f'Unexpected error adding boot entry to nvram. UEFI firmware might be buggy') from e
def copy_windows_efi_bootloader(disk, partition):
device = get_partition_device(disk, partition)
mountpoint = device.replace('dev', 'mnt')
if not mount_mkdir(device, mountpoint):
raise RuntimeError(f'Cannot probe OS family. Unable to mount {device} into {mountpoint}')
raise OgError(f'Cannot probe OS family. Unable to mount {device} into {mountpoint}')
os_family = get_os_family(mountpoint)
is_uefi = is_uefi_supported()
@ -119,7 +120,7 @@ def copy_windows_efi_bootloader(disk, partition):
esp_mountpoint = esp.replace('dev', 'mnt')
if not mount_mkdir(esp, esp_mountpoint):
umount(mountpoint)
raise RuntimeError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
raise OgError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')
loader_paths = [f'{esp_mountpoint}/EFI/{bootlabel}/Boot/bootmgfw.efi',
f'{esp_mountpoint}/EFI/Microsoft/Boot/bootmgfw.efi']
@ -131,7 +132,7 @@ def copy_windows_efi_bootloader(disk, partition):
logging.info(f'Found bootloader at ESP partition: {loader}')
break
else:
raise RuntimeError(f'Unable to locate Windows EFI bootloader bootmgfw.efi')
raise OgError(f'Unable to locate Windows EFI bootloader bootmgfw.efi')
loader_dir = os.path.dirname(loader)
destination_dir = f'{mountpoint}/ogBoot'
@ -139,12 +140,12 @@ def copy_windows_efi_bootloader(disk, partition):
try:
shutil.rmtree(destination_dir)
except Exception as e:
raise OSError(f'Failed to delete {destination_dir}: {e}') from e
raise OgError(f'Failed to delete {destination_dir}: {e}') from e
logging.info(f'Copying {loader_dir} into {destination_dir}')
try:
shutil.copytree(loader_dir, destination_dir)
except Exception as e:
raise OSError(f'Failed to copy {loader_dir} into {destination_dir}: {e}') from e
raise OgError(f'Failed to copy {loader_dir} into {destination_dir}: {e}') from e
finally:
umount(mountpoint)
umount(esp_mountpoint)

View File

@ -23,6 +23,7 @@ import math
import sys
import enum
import time
from src.log import OgError
class OgVM:
DEFAULT_CPU = 'host'
@ -97,7 +98,7 @@ class OgQMP:
self.sock.connect((self.ip, self.port))
except socket.error as err:
if err.errno == errno.ECONNREFUSED:
raise RuntimeError('Cannot connect to QEMU')
raise OgError('Cannot connect to QEMU')
elif err.errno == errno.EINPROGRESS:
pass
@ -114,11 +115,11 @@ class OgQMP:
pass
if 'QMP' not in out:
raise RuntimeError('Cannot handshake QEMU')
raise OgError('Cannot handshake QEMU')
out = self.talk(str({"execute": "qmp_capabilities"}))
if 'return' not in out:
raise RuntimeError('Cannot handshake QEMU')
raise OgError('Cannot handshake QEMU')
def disconnect(self):
try:
@ -143,9 +144,9 @@ class OgQMP:
try:
self.sock.send(bytes(data, 'utf-8'))
except Exception as e:
raise RuntimeError('Cannot talk to QEMU') from e
raise OgError('Cannot talk to QEMU') from e
else:
raise RuntimeError('Timeout when talking to QEMU')
raise OgError('Timeout when talking to QEMU')
return self.recv(timeout=timeout)
@ -158,9 +159,9 @@ class OgQMP:
out = self.sock.recv(4096).decode('utf-8')
out = json.loads(out)
except socket.error as err:
raise RuntimeError('Cannot talk to QEMU') from err
raise OgError('Cannot talk to QEMU') from err
else:
raise RuntimeError('Timeout when talking to QEMU')
raise OgError('Timeout when talking to QEMU')
return out
class OgVirtualOperations: