#!/usr/bin/env python3
#
# lmn-appliance
# thomas@linuxmuster.net
# 20260618
#

import configparser
import getopt
import os
import subprocess
import sys
import urllib.request

# check for root user and go on
if not os.geteuid() == 0:
    sys.exit('Please start the script as root user.\n You can use the command "sudo -i" to achieve this.')

lvmvols_default = 'var:10,linbo:40,global:10,default-school:100%FREE'

# help
def usage(rc):
    print('\nInstalls linuxmuster.net package repo, downloads and installs linuxmuster-prepare package.')
    print('If profile option is set it initially prepares a linuxmuster.net ubuntu appliance.\n')
    print('\nUsage: lmn-appliance [options]')
    print('\n[options] are:\n')
    print('-t, --hostname=<hostname>   : Hostname to apply (optional, works only with')
    print('                              server profile).')
    print('-n, --ipnet=<ip/bitmask>    : Ip address and bitmask assigned to the host')
    print('                              (optional, default is 10.0.0.x/16, depending')
    print('                              on the profile).')
    print('-p, --profile=<profile>     : Host profile to apply, mandatory. Expected')
    print('                              values are "server" or "ubuntu".')
    print('                              Profile name is also used as hostname, except for')
    print('                              "server" if set with -t.')
    print('-w, --swapsize=<size>       : Swapfile size in GB (default "2").')
    print('-f, --firewall=<firewallip> : Firewall/gateway ip (default *.254).')
    print('-g, --gateway=<ip>          : Gateway ip address (default is firewall ip).')
    print('-d, --domain=<domainname>   : Domainname (default linuxmuster.lan).')
    print('-u, --unattended            : Unattended mode, do not ask, use defaults.')
    print('-b, --reboot                : Reboots finally (only in unattended mode).')
    print('-h, --help                  : Print this help.')
    print('\nNote on lvm volumes:')
    print('- Default value for volumes is:')
    print('  "' + lvmvols_default + '".')
    print('- At least "linbo", "global" and "default-school" must be given.')
    print('- "nn%FREE" means "use nn% of the remaining free space of the device for this volume".')
    print('  Example: "linbo:50%FREE,global:10,default-school:100%FREE" means "linbo" takes 50% of')
    print('  the volume, "global" takes 10G and "default-school" finally takes all of the rest.')
    print('- "global" and "default-school" are mounted with quota options.')
    sys.exit(rc)


# get cli args
try:
    opts, args = getopt.getopt(sys.argv[1:], "bd:f:g:hl:n:p:t:uv:w:", [
        "reboot", "domain=", "firewall=", "gateway=", "help", "swapsize=", "ipnet=", "profile=", "hostname=", "unattended"])
except getopt.GetoptError as err:
    # print help information and exit:
    print(err)  # will print something like "option -a not recognized"
    usage(2)

# default values
unattended = ''
reboot = ''
profile = ''
hostname = ''
domainname = ''
firewallip = ''
gateway = ''
ipnet = ''
volumes = ''
pvdevice = ''
swapsize = ''
profile_list = ['server', 'ubuntu']

# evaluate options
for o, a in opts:
    if o in ("-u", "--unattended"):
        unattended = ' -u'
    elif o in ("-b", "--reboot"):
        reboot = ' -b'
    elif o in ("-p", "--profile"):
        profile = a
    elif o in ("-t", "--hostname"):
        hostname = ' -t ' + a
    elif o in ("-w", "--swapsize"):
        swapsize = ' -w ' + a
    elif o in ("-d", "--domain"):
        domainname = ' -d ' + a
    elif o in ("-n", "--ipnet"):
        ipnet = ' -n ' + a
    elif o in ("-f", "--firewall"):
        firewallip = ' -f ' + a
    elif o in ("-g", "--gateway"):
        gateway = ' -g ' + a
    elif o in ("-h", "--help"):
        usage(0)
    else:
        assert False, "unhandled option"
        usage(1)

if profile != '' and profile not in profile_list:
    print('\nUnknown profile: ' + profile + '.')
    usage(1)

# repo data
lmn_url = 'https://deb.linuxmuster.net/'
lmnkey_remote = lmn_url + 'pub.gpg'
lmnkey_local = '/usr/share/keyrings/linuxmuster.net.gpg'
lmn_list_pre = '/etc/apt/sources.list.d/lmn.'
lmn74_def = 'Types: deb\nURIs: ' + lmn_url + '\nSuites: lmn74\nComponents: main\nSigned-By: ' + lmnkey_local
lmn74_sources = lmn_list_pre + 'sources'
lmn73_def = 'deb [arch=amd64 signed-by=' + lmnkey_local + '] ' + lmn_url + ' lmn73 main'
lmn73_list = lmn_list_pre + 'list'
pkgname = 'linuxmuster-prepare'


# return content of text file
def readTextfile(tfile):
    if not os.path.isfile(tfile):
        return False, None
    try:
        infile = open(tfile, 'r')
        content = infile.read()
        infile.close()
        return True, content
    except:
        print('Cannot read ' + tfile + '!')
        return False, None


# write textfile
def writeTextfile(tfile, content, flag):
    try:
        outfile = open(tfile, flag)
        outfile.write(content)
        outfile.close()
        return True
    except:
        print('Failed to write ' + tfile + '!')
        return False


# get os release
def os_release():
    # read data
    rc, content = readTextfile('/etc/os-release')
    # insert a section
    content = "[OS]\n" + content
    content = content.replace('"', '')
    # read the os data
    os_data = configparser.ConfigParser()
    os_data.read_string(content)
    return os_data['OS']['ID'], os_data['OS']['VERSION_ID']


# check ubuntu version
id, version = os_release()
if id != 'ubuntu':
    print(id + ' is not the expected os.')
    sys.exit(1)
if version != '26.04' and version != '24.04':
    print(version + ' is not the expected version.')
    sys.exit(1)
print('## install linuxmuster.net repos')


# install repo key
urllib.request.urlretrieve(lmnkey_remote, lmnkey_local)


# install repos
if version == '26.04':
    writeTextfile(lmn74_sources, lmn74_def, 'w')
else:
    writeTextfile(lmn73_list, lmn73_def, 'w')

# get updates
print('## install software updates')
subprocess.call('apt clean', shell=True)
subprocess.call('apt update', shell=True)
subprocess.call('DEBIAN_FRONTEND=noninteractive apt -y dist-upgrade', shell=True)


# get lmn packages
print('## install ' + pkgname + ' package')
subprocess.call('DEBIAN_FRONTEND=noninteractive echo -e "y\ny\ny\n" | apt -y install ' + pkgname, shell=True)


# invoke prepare script if profile is set
if profile != '':
    print('## invoke lmn-prepare')
    profile = ' -p ' + profile
    subprocess.call('lmn-prepare -i' + unattended + reboot + profile
        + hostname + domainname + firewallip + gateway + ipnet + swapsize, shell=True)
