# # Copyright (C) 2023 Soleta Networks # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the # Free Software Foundation; either version 3 of the License, or # (at your option) any later version. import hashlib import logging import os import shlex import shutil import subprocess import urllib.request from src.log import OgError from src.utils.cache import mount_cache def _compute_md5(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 tip_fetch_csum(tip_addr, image_name): """ """ url = f'http://{tip_addr}:9999/{image_name}.img.full.sum' try: with urllib.request.urlopen(f'{url}') as resp: r = resp.readline().rstrip().decode('utf-8') except urllib.error.URLError as e: raise OgError(f'URL error when fetching checksum: {e.reason}') from e except urllib.error.HTTPError as e: raise OgError(f'HTTP Error when fetching checksum: {e.reason}') from e return r def tip_write_csum(image_name): if not mount_cache(): raise OgError(f'Failed to checksum {image_name}: cache partition is not available') image_path = f'/opt/opengnsys/cache/opt/opengnsys/images/{image_name}.img' if not os.path.exists(image_path): raise OgError(f'Invalid image path {image_path} for tiptorrent checksum writing') filename = image_path + ".full.sum" csum = _compute_md5(image_path) try: with open(filename, 'w') as f: f.write(csum) except OSError as e: raise OgError(f'Could not write {filename}, reported: {e}') from e return csum 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 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) if cache_csum == remote_csum: ret = True logging.info(f'Checksum is OK for {image_name}.img') else: ret = False logging.warn(f'Checksum mismatch for {image_name}.img') return ret def tip_client_get(tip_addr, image_name): """ """ logging.info(f'Fetching image {image_name} from tiptorrent server at {tip_addr}') logging.info('*DO NOT REBOOT OR POWEROFF* the client during this time') cmd = f'tiptorrent-client {tip_addr} {image_name}.img' logfile = open('/tmp/command.log', 'wb', 0) try: proc = subprocess.Popen(shlex.split(cmd), stdout=logfile, cwd='/opt/opengnsys/cache/opt/opengnsys/images/') proc.communicate() except OSError as e: raise OgError('Unexpected error running tiptorrent subprocess: {e}') from e finally: logfile.close() if proc.returncode != 0: 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') tip_write_csum(image_name)