summaryrefslogtreecommitdiffstats
path: root/src/utils/tiptorrent.py
blob: 1aa1a6a7b8ff51aa9bfb7d840254831eb59c5dd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#
# Copyright (C) 2023 Soleta Networks <info@soleta.eu>
#
# 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 *

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'Error fetching checksum file from {url}: {e.reason}') from e
    except urllib.error.HTTPError as e:
        raise OgError(f'Error fetching checksum file via HTTP from {url}: {e.reason}') from e
    return r


def _tip_read_csum(image_checksum_path):
    try:
        with open(image_checksum_path, 'r') as f:
            checksum = f.read().strip("\n")
    except OSError as e:
        return "unavailable"

    return checksum


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'{OG_CACHE_IMAGE_PATH}{image_name}.img'

    if not os.path.exists(image_path):
        raise OgError(f'Invalid image path {image_path} for tiptorrent checksum writing')

    logging.info('Calculating checksum...')
    logging.info('*DO NOT REBOOT OR POWEROFF* the client during this time')

    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'{OG_CACHE_IMAGE_PATH}{image_name}.img'
    if not os.path.exists(image_path):
        logging.error(f'File {image_path} does not exist')
        return False
    if not os.path.exists(f"{image_path}.full.sum"):
        logging.error(f'File {image_path}.full.sum does not exist in repository {tip_addr}')
        return False

    cache_csum = _tip_read_csum(f"{image_path}.full.sum")
    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')
        logging.warn(f'Server reports checksum {remote_csum} but local checksum is {cache_csum}')

    return ret


def tip_client_get(tip_addr, image_name):
    """
    """
    image_path = f"{OG_CACHE_IMAGE_PATH}{image_name}.img"
    if os.path.exists(image_path):
        os.unlink(image_path)
    if os.path.exists(f"{image_path}.full.sum"):
        os.unlink(f"{image_path}.full.sum")

    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=OG_CACHE_IMAGE_PATH)
        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:
        tip_write_csum(image_name)