diff --git a/.gitignore b/.gitignore
index 2a7412f..fe28f31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*.iso
-*.swp
\ No newline at end of file
+*.swp
+*.iso.sha256sum
\ No newline at end of file
diff --git a/README.md b/README.md
index e84dd36..be64014 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,13 @@
# Potos iso building
-To create your own Linux Client based on Potos, you need to create a [config](config) directory according to the scaffolding in this repo (and the explanation in section [Config](#Config)). To build the iso yourself, you can choose between one of the following possibilities:
- * [Plain Docker](#Build iso - Docker plain)
- * [Docker Compose](#Build iso - Docker compose)
+## Structure
+This repo contains the iso builder for Potos.
+ * [`container`](container) contains the source for the docker image `ghcr.io/projectpotos/potos-iso-builder`
+ * [`config`](container) contains an example config
+ * [`output`](output) is the directory where using the default commands the final iso is stored into
-## Build iso - Docker plain
-1. Clone this repository with `git clone` or download & unzip it.
-2. Adjust the files in [`config`](./config) to your client
-3. Run `docker run -it -v $(pwd)/config:/config -v $(pwd)/output:/output ghcr.io/projectpotos/potos-iso-builder:latest`
-
-## Build iso - Docker compose
-
-1. Clone this repository with `git clone` or download & unzip it.
-2. Adjust the files in [`config`](./config) to your client
-3. Run `docker-compose up` in the main directory
-
-## Build iso - Github Workflow
-1. Create a new repo containing the `config` directory adjusted for your client
-2. Create the following file as workflow file under `.github/workflows/isobuild.yml`
-```
-name: Build Iso
-on:
- workflow_dispatch:
- pull_request:
-jobs:
- build:
- name: Build Iso
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v3
- - name: Run the build process with Docker
- uses: addnab/docker-run-action@v3
- with:
- image: ghcr.io/projectpotos/potos-iso-builder:latest
- run: ./build-iso
- options: -v ${{ github.workspace }}:/config -v /output:/output
- - name: Save iso
- uses: actions/upload-artifact@v3
- with:
- name: potos-iso
- path: /output/*.iso
- retention-days: 1
-```
-
-## Build your own container image
-
-If you wnat to build the build container image yourself, all the container relevant files are in [`container`](./container)
-1. Execute `docker build container/` to build the container
-2. Execute `docker build container/ -t iso-build` to build the container and assign a tag
-
-Then to build the client, adjust the image to be used in the commands above.
-
-## Config
-
-### logo.png
-Place your client logo in this file, if you want some customization during the setup dialogs
-
-### config.yml
-The iso build is configured using [YAML](https://en.wikipedia.org/wiki/YAML) based `config.yml` file. The file has to be located within the configuration directory mounted as volume into the build container.
-
-> **CONVENTION**
-> The *dot-notation* of a config-key like `client_name.long` means `long` property within the `client_name` section. All *dot-notation* references are absolut.
-
-| Variable | Type | Default | Comment |
-|---|---|---|---|
-| client_name.long | *string* | Potos Linux Client | Define the Name of your Linux Client, e.g. "My Linux Client". |
-| client_name.short | *string, lowercase, short, regex `^([0-9a-z]{1,32})$`* | potos | Define a short name of your Linux Client. Use lowercase. Will be used for example for the log folder /var/log/$POTOS_CLIENT_SHORTNAME |
-| disk_encryption.enable | *boolean --> `true`\|`false`* | false | To enable autoinstall feature with disk encryption (except: /boot). You have to enter the defined password at first boot after the installation. |
-| disk_encryption.init_password | *string* | install | The autoinstall feature with disk encryption (except: /boot) needs a predefined decryption password. You have to enter this password at first boot after the installation. |
-| specs.url | *string, URL, trailing slash* | https://github.com/projectpotos/ | The URL to your Git Account that holds your own Potos Specs Repository. Make sure you have the trailing slash included. |
-| specs.repo | *string, part of the URL* | ansible-specs-potos | The name of your own Potos Git Specs Repository, without *.git* at the End. |
-| specs.branch | *string* | main | Define the branch of your specs.repo. Typical values are `main`, `master`, `develop` |
-| initial_hostname | *string* | potoshostname01 | Your Linux Client based on Potos will use this predefined hostname at the installation and first boot. |
-| initial_user.username | *string* | admin | An initial username is required. Will have full sudo (root) permission. Can be removed later on. |
-| initial_user.password | *string* | admin *hashed* | The password in form of a hash. Create your own with `echo -n yourpasswordhere \| mkpasswd --method=SHA-512 --stdin` . |
-| environment | *string* | production | Possible values are `production` and `develop`. The installation in `develop` mode is more verbose. |
-| first_boot_ansible.runtype | *string* | setup | Run type of the first ansible run |
-| full_unattended_install | *boolean --> `true`\|`false`* | false | Disable security question before overwrite of disk and user input from iso side to allow a fully unattended installation |
-| input.iso_filename | *string* | ubuntu-22.04.1-live-server-amd64.iso | Name of the local iso file (needs to correspond with content of the SHA256SUMS file) |
-| input.iso_url | *string* | https://releases.ubuntu.com/22.04/ubuntu-22.04.1-live-server-amd64.iso | Where to download the iso file if it doesn't exist locally |
-| input.sha256_filename | *string* | SHA256SUMS | Name of the SHA256SUMS file |
-| input.sha256_url | *string* | https://releases.ubuntu.com/22.04/SHA256SUMS | Where to download the SHA256SUMS file if it doesn't exist locally |
-| output.version | *string* | current date in yyyymmddd | What string should be used as Version identifier |
-| output.filename | *string* | `client_name.short`-installer-`environment`.iso | How the iso in the output directory should be named |
-| preinstall_packages | *list of strings* | - python3-virtualenv
- linux-generic-hwe-22.04
- ubuntu-desktop
- plymouth-theme-ubuntu-logo
- ldap-utils
- yad | What packages should be installed with autoinstall. * `python3-virtualenv`: python with virtualenv is required to install ansible within it * `linux-generic-hwe-22.04`: install hwe kernel * `ubuntu-desktop`: install gnome desktop * `plymouth-theme-ubuntu-logo`: install plymouth-theme * `ldap-utils`: ldap utils used for all the ldap integration things * `yad`: used for graphical dialogs during setup |
-
-# Potos iso installation
-
-Boot from the previously generated Potos .iso image in your virtual or physical hardware and follow the instruction. The client is entirely defined by the `specs` repo,
+# Documentation
+To see the entire documentation go to [potos.dev](https://potos.dev)
+ * [How to build an ISO](https://potos.dev/guide/iso-build/how-to-build.html)
+ * [ISO build config](https://potos.dev/guide/iso-build/config.html)
diff --git a/config/config.yml b/config/config.yml
index 3272a30..7cb0a25 100644
--- a/config/config.yml
+++ b/config/config.yml
@@ -19,18 +19,7 @@ environment: "production"
first_boot_ansible:
runtype: "setup"
full_unattended_install: false
-input:
- iso_filename: "ubuntu-22.04.1-live-server-amd64.iso"
- iso_url: "https://releases.ubuntu.com/22.04/ubuntu-22.04.1-live-server-amd64.iso"
- iso_sha256_filename: "SHA256SUMS"
- iso_sha256_url: "https://releases.ubuntu.com/22.04/SHA256SUMS"
+os: "jammy"
output:
- version: "22.04"
+ version: "%Y%m%d"
iso_filename: "potos-installer.iso"
-preinstall_packages:
- - python3-virtualenv
- - linux-generic-hwe-22.04
- - ubuntu-desktop
- - plymouth-theme-ubuntu-logo
- - ldap-utils
- - yad
diff --git a/container/Dockerfile b/container/Dockerfile
index 3ab0a11..456fbaa 100644
--- a/container/Dockerfile
+++ b/container/Dockerfile
@@ -3,13 +3,13 @@ FROM ubuntu:22.04
WORKDIR /potos-iso
# Install ISO creation depencies
-RUN apt update && apt install -y gfxboot p7zip-full xorriso wget curl libhtml-parser-perl cpio whois python3 python3-pip fdisk
-RUN pip3 install j2cli
-RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && chmod +x /usr/local/bin/yq
+RUN apt update && apt install -y gfxboot p7zip-full xorriso wget curl libhtml-parser-perl cpio whois python3 python3-pip fdisk squashfs-tools
+COPY requirements.txt .
+RUN pip3 install -r requirements.txt
# Create config directory
RUN mkdir /config
ADD . /potos-iso
-CMD ["./build-iso"]
+CMD ["/usr/bin/python3", "build-iso.py"]
diff --git a/container/autoinstall-user-data.j2 b/container/autoinstall-user-data.j2
index 6fa74fe..08ce3f9 100644
--- a/container/autoinstall-user-data.j2
+++ b/container/autoinstall-user-data.j2
@@ -7,9 +7,9 @@ autoinstall:
- arches: [default]
uri: "http://archive.ubuntu.com/ubuntu"
identity:
- hostname: {{ POTOS_INITIAL_HOSTNAME | default('potoshostname01') }}
- password: {{ POTOS_INITIAL_PASSWORD_HASH | default('$6$L36BiUuVCSipvlO8$oGI0C.LXZegkbftFkVDXXaasTM6zs9LM71BkqZToKw5aOZ7Yr70pkzH3P9Xz5R.n0ULJ0Zf8v5ZQ/eH8flDR7/') }}
- username: {{ POTOS_INITIAL_USERNAME | default('admin') }}
+ hostname: {{ config['initial_hostname'] | default('potoshostname01') }}
+ password: {{ config['initial_user']['password'] | default('$6$L36BiUuVCSipvlO8$oGI0C.LXZegkbftFkVDXXaasTM6zs9LM71BkqZToKw5aOZ7Yr70pkzH3P9Xz5R.n0ULJ0Zf8v5ZQ/eH8flDR7/') }}
+ username: {{ config['initial_user']['username'] | default('admin') }}
ssh:
allow-pw: true
authorized-keys: []
@@ -53,7 +53,7 @@ autoinstall:
size: 1GB
preserve: false
number: 2
-{% if POTOS_DISK_ENCRYPTION_ENABLE is defined and POTOS_DISK_ENCRYPTION_ENABLE == 'true' %}
+{% if config['disk_encryption']['enable'] is defined and config['disk_encryption']['enable'] == 'true' %}
- id: partition_crypt
type: partition
device: disk_primary
@@ -62,7 +62,7 @@ autoinstall:
number: 3
- id: dm-crypt_0
volume: partition_crypt
- key: {{ POTOS_DISK_ENCRYPTION_INITIAL_PASSWORD | default('install') }}
+ key: {{ config['disk_encryption']['init_password'] | default('install') }}
preserve: false
type: dm_crypt
- id: lvm_volgroup_0
@@ -132,8 +132,7 @@ autoinstall:
path: /
type: mount
packages:
-{% set packages = POTOS_PRE_INSTALL_PACKAGES.split('\n') %}
-{% for package in packages %}
+{% for package in config['packages']['preinstall'] %}
- {{ package }}
{% endfor %}
late-commands:
@@ -150,10 +149,10 @@ autoinstall:
- cp /cdrom/setup/default-netplan.yml /target/etc/netplan/01-network-manager-all.yaml
- cp /cdrom/setup/gnome-sudo /target/etc/sudoers.d/01_gnome-initial-setup
- mkdir -p /target/etc/potos/ && chown 0:0 /target/etc/potos/ && chmod 0700 /target/etc/potos/
-{% if POTOS_GIT_SPECS_SSH_KEY != "" %}
+{% if config['specs']['ssh_key'] != "" %}
- cp /cdrom/setup/specs_key /target/etc/potos/specs_key && chown 0:0 /target/etc/potos/specs_key && chmod 0400 /target/etc/potos/specs_key
{% endif %}
-{% if POTOS_GIT_SPECS_ANSIBLE_VAULT != "" %}
+{% if config['specs']['ansible_vault_key_file'] != "" %}
- cp /cdrom/setup/ansible_vault_key /target/etc/potos/ansible_vault_key && chown 0:0 /target/etc/potos/ansible_vault_key && chmod 0500 /target/etc/potos/ansible_vault_key
{% endif %}
- cp -r /cdrom/setup /target/setup
diff --git a/container/build-iso.py b/container/build-iso.py
new file mode 100755
index 0000000..022984c
--- /dev/null
+++ b/container/build-iso.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python3
+
+import yaml
+import os
+import shutil
+import jinja2
+from datetime import date
+import subprocess
+from pprint import pprint
+
+# define jinja2 environment
+j2 = jinja2.Environment(
+ loader=jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__)))
+)
+
+# get config inkl default values
+config = {}
+with open("/config/config.yml", "r") as f:
+ try:
+ ymlconfig = yaml.safe_load(f)
+ config['client_name'] = {}
+ config['client_name']['long'] = ymlconfig.get("client_name",{}).get('long',"Potos Linux Client")
+ config['client_name']['short'] = ymlconfig.get("client_name",{}).get('short',"potos")
+ config['disk_encryption'] = {}
+ config['disk_encryption']['enable'] = ymlconfig.get("disk_encryption",{}).get('enable',False)
+ config['disk_encryption']['init_password'] = ymlconfig.get("disk_encryption",{}).get('init_password',"install")
+ config['specs'] = {}
+ config['specs']['url'] = ymlconfig.get("specs",{}).get('url',"https://github.com/projectpotos/")
+ config['specs']['repo'] = ymlconfig.get("specs",{}).get('repo',"ansible-specs-potos")
+ config['specs']['branch'] = ymlconfig.get("specs",{}).get('branch',"main")
+ config['specs']['ssh_key'] = ymlconfig.get("specs",{}).get('ssh_key',"")
+ config['specs']['ansible_vault_key_file'] = ymlconfig.get("specs",{}).get('ansible_vault_key_file',"")
+ config['initial_hostname'] = ymlconfig.get("initial_hostname","potoshostname01")
+ config['initial_user'] = {}
+ config['initial_user']['username'] = ymlconfig.get("initial_user",{}).get('username',"admin")
+ config['initial_user']['password'] = ymlconfig.get("initial_user",{}).get('password',"$6$L36BiUuVCSipvlO8$oGI0C.LXZegkbftFkVDXXaasTM6zs9LM71BkqZToKw5aOZ7Yr70pkzH3P9Xz5R.n0ULJ0Zf8v5ZQ/eH8flDR7/")
+ config['environment'] = ymlconfig.get("environment","production")
+ config['first_boot_ansible'] = {}
+ config['first_boot_ansible']['runtype'] = ymlconfig.get("first_boot_ansible",{}).get('runtype',"setup")
+ config['full_unattended_install'] = ymlconfig.get("full_unattended_install", False)
+ config['os'] = ymlconfig.get("os","jammy")
+ config['output'] = {}
+ config['output']['version'] = date.today().strftime(ymlconfig.get("output",{}).get('version',"%Y%m%d"))
+ config['output']['iso_filename'] = ymlconfig.get("output",{}).get('iso_filename',"%s-installer.iso"%(config['client_name']['short']))
+ config['isolinux'] = {}
+ config['isolinux']['txtBackgroundColor'] = ymlconfig.get("isolinux",{}).get('txtBackgroundColor', "0xCCCCCC")
+ config['isolinux']['txtForegroundColor'] = ymlconfig.get("isolinux",{}).get('txtForegroundColor', "0xFFFFFF")
+ config['isolinux']['screenColor'] = ymlconfig.get("isolinux",{}).get('screenColor', "0x161B21")
+ except yaml.YAMLError as e:
+ print(e)
+ exit(1)
+
+TMP_DIR = "iso"
+REQIREMENTS = ["7z", "gfxboot", "xorriso", "wget", "curl", "sha256sum"]
+
+# switch iso by selected os
+if config['os'] == "jammy":
+ config['input'] = {}
+ config['input']['iso_filename'] = "ubuntu-22.04.1-live-server-amd64.iso"
+ config['input']['iso_url'] = (
+ "https://releases.ubuntu.com/22.04/ubuntu-22.04.1-live-server-amd64.iso"
+ )
+ config['input']['sha256_filename'] = "SHA256SUMS"
+ config['input']['sha256_url'] = "https://releases.ubuntu.com/22.04/SHA256SUMS"
+ config['packages'] = {}
+ config['packages']['preinstall'] = [
+ "python3-virtualenv",
+ "linux-generic-hwe-22.04",
+ "ubuntu-desktop",
+ "plymouth-theme-ubuntu-logo",
+ "ldap-utils",
+ "yad",
+ ]
+elif config['os'] == "focal":
+ config['input'] = {}
+ config['input']['iso_filename'] = "ubuntu-20.04.5-live-server-amd64.iso"
+ config['input']['iso_url'] = (
+ "https://releases.ubuntu.com/20.04/ubuntu-20.04.5-live-server-amd64.iso"
+ )
+ config['input']['sha256_filename'] = "SHA256SUMS"
+ config['input']['sha256_url'] = "https://releases.ubuntu.com/20.04/SHA256SUMS"
+ config['packages'] = {}
+ config['packages']['preinstall'] = [
+ "python3-virtualenv",
+ "linux-generic-hwe-20.04",
+ "ubuntu-desktop",
+ "plymouth-theme-ubuntu-logo",
+ "ldap-utils",
+ "yad",
+ ]
+else:
+ print("Invalid base os: %s"%(config['os']))
+ print("Currently supported os are:")
+ print(" * Ubuntu: 'jammy', 'focal'")
+ exit(1)
+
+######
+# Start with iso build
+######
+
+# Print config info
+if config['environment'] == "develop":
+ print(
+ "*** config.environment is %s, going to print some more informations for you:"%(
+ config['environment'],
+ )
+ )
+ pprint(config)
+
+print(
+ "*** Going to build an ISO for %s (%s)"%(config['client_name']['long'], config['environment'])
+)
+
+# Check if required software is installed else install
+for r in REQIREMENTS:
+ if os.system("which %s > /dev/null"%(r)) != 0:
+ print("%s is missing!"%(r))
+ exit(1)
+
+# If iso dir somehow exists, remove it
+if os.path.exists(TMP_DIR) and os.path.isdir(TMP_DIR):
+ shutil.rmtree(TMP_DIR)
+elif os.path.exists(TMP_DIR):
+ print("ERROR: %s exists but is not a directory"%(TMP_DIR))
+ exit(1)
+
+# If iso and checksum not exist, download them
+if not os.path.exists(config['input']['iso_filename']):
+ if (
+ os.system(
+ "wget -nv --output-document='%s' %s"%(
+ config['input']['iso_filename'],
+ config['input']['iso_url'],
+ )
+ )
+ != 0
+ ):
+ print("ERROR: %s could not be downloaded!"%(config['input']['iso_url']))
+
+if not os.path.exists(config['input']['sha256_filename']):
+ if (
+ os.system(
+ "wget -nv --output-document='%s' %s"%(
+ config['input']['sha256_filename'],
+ config['input']['sha256_url'],
+ )
+ )
+ != 0
+ ):
+ print("ERROR: %s could not be downloaded!"%(config['input']['sha256_url']))
+
+# Fail if no checksum exists
+if (
+ os.system("sha256sum --ignore-missing --quiet -c %s"%(config['input']['sha256_filename']))
+ != 0
+):
+ print("ERROR: sha256sum check failed!")
+ exit(1)
+
+# extract iso into temporary directory
+if os.system("7z x '%s' -o'%s'"%(config['input']['iso_filename'], TMP_DIR)) != 0:
+ print("ERROR: could not extract iso file")
+ exit(2)
+
+# remove no longer needed stuff
+if os.path.exists(os.path.join(TMP_DIR, "preseed")):
+ shutil.rmtree(os.path.join(TMP_DIR, "preseed"))
+
+# make directory for files during autoinstall
+os.mkdir(os.path.join(TMP_DIR, "setup"))
+
+# copy Netplan into iso
+shutil.copy("default-netplan.yml", os.path.join(TMP_DIR, "setup/default-netplan.yml"))
+
+# copy Gnome initial setup sudoers rule into iso
+shutil.copy("gnome-sudo", os.path.join(TMP_DIR, "setup/gnome-sudo"))
+
+# template autoinstall files
+os.mkdir(os.path.join(TMP_DIR, "nocloud-uefi"))
+with open(os.path.join(TMP_DIR, "nocloud-uefi/meta-data"), "w") as f:
+ f.write(
+ j2.get_template("autoinstall-meta-data.j2").render(
+ config=config, autoinstall_type="uefi"
+ )
+ )
+with open(os.path.join(TMP_DIR, "nocloud-uefi/user-data"), "w") as f:
+ f.write(
+ j2.get_template("autoinstall-user-data.j2").render(
+ config=config, autoinstall_type="uefi"
+ )
+ )
+
+os.mkdir(os.path.join(TMP_DIR, "nocloud-bios"))
+with open(os.path.join(TMP_DIR, "nocloud-bios/meta-data"), "w") as f:
+ f.write(
+ j2.get_template("autoinstall-meta-data.j2").render(
+ config=config, autoinstall_type="bios"
+ )
+ )
+with open(os.path.join(TMP_DIR, "nocloud-bios/user-data"), "w") as f:
+ f.write(
+ j2.get_template("autoinstall-user-data.j2").render(
+ config=config, autoinstall_type="bios"
+ )
+ )
+
+# create version file
+with open(
+ os.path.join(TMP_DIR, "setup/%s-version" % (config['client_name']['short'])), "w"
+) as f:
+ f.write(
+ "%s %s (%s)\n"%(
+ config['client_name']['short'],
+ config['output']['version'] ,
+ date.today().strftime("%Y%m%d-%H%M"),
+ )
+ )
+
+# remove boot config
+shutil.rmtree(os.path.join(TMP_DIR, "[BOOT]"))
+
+# template grub config
+if not os.path.exists(os.path.join(TMP_DIR, "boot/grub")):
+ os.makedir(os.path.join(TMP_DIR, "boot/grub"))
+with open(os.path.join(TMP_DIR, "boot/grub/grub.cfg"), "w") as f:
+ f.write(j2.get_template("grub.cfg.j2").render(config=config))
+
+# copy logo into iso
+if os.path.exists("/config/logo.png"):
+ shutil.copy("/config/logo.png", os.path.join(TMP_DIR, "setup/logo.png"))
+else:
+ shutil.copy("default/logo.png", os.path.join(TMP_DIR, "setup/logo.png"))
+
+# copy ssh deploy key for specs repo into image
+if config['specs']['ssh_key'] != "" and os.path.exists(os.path.join("/config/", config['specs']['ssh_key'])):
+ shutil.copy(
+ os.path.join("/config/", config['specs']['ssh_key']),
+ os.path.join(TMP_DIR, "setup/specs_key"),
+ )
+
+# copy ansible-vault key for specs repo into image
+if config['specs']['ansible_vault_key_file'] != "" and os.path.exists(
+ os.path.join("/config/", config['specs']['ansible_vault_key_file'])
+):
+ shutil.copy(
+ os.path.join("/config/", config['specs']['ansible_vault_key_file']),
+ os.path.join(TMP_DIR, "setup/ansible_vault_key"),
+ )
+
+# template diverse files for firstboot
+
+with open(os.path.join(TMP_DIR, "setup/firstboot-gui.sh"), 'w') as f:
+ f.write(j2.get_template("firstboot-gui.sh.j2").render(config=config))
+os.chmod(os.path.join(TMP_DIR, "setup/firstboot-gui.sh"), 0o555)
+with open(os.path.join(TMP_DIR, "setup/finish.sh"), 'w') as f:
+ f.write(j2.get_template("finish.sh.j2").render(config=config))
+os.chmod(os.path.join(TMP_DIR, "setup/finish.sh"), 0o555)
+with open(os.path.join(TMP_DIR, "setup/change-keyboard-layout"), 'w') as f:
+ f.write(j2.get_template("change-keyboard-layout.j2").render(config=config))
+os.chmod(os.path.join(TMP_DIR, "setup/change-keyboard-layout"), 0o555)
+
+
+
+# adjust md5sum checks
+os.system("sed -i 's|$%s/|./|g' '%s/md5sum.txt'" % (TMP_DIR, TMP_DIR))
+
+# Get efi partition infos
+iso_bs = subprocess.check_output(
+ "fdisk -l '%s' | grep 'Sector size' | tr -s ' ' | cut -d ' ' -f4"
+ % (config['input']['iso_filename']), shell=True
+).decode("utf-8").rstrip()
+iso_efi_skip = subprocess.check_output(
+ "fdisk -l '%s' | grep 'EFI' | tr -s ' ' | cut -d ' ' -f2" % (config['input']['iso_filename']), shell=True
+).decode("utf-8").rstrip()
+iso_efi_size = subprocess.check_output(
+ "fdisk -l '%s' | grep 'EFI' | tr -s ' ' | cut -d ' ' -f4" % (config['input']['iso_filename']), shell=True
+).decode("utf-8").rstrip()
+
+# define MBR
+MBR_FILE = "boot_hybrid.img"
+
+# write original MBR to MBR file
+os.system("dd if='%s' bs=1 count=432 of='%s'" % (config['input']['iso_filename'], MBR_FILE))
+
+# write original efi.img to file
+os.system(
+ "dd if='%s' bs='%s' skip='%s' count='%s' of=efi.img"
+ % (config['input']['iso_filename'], iso_bs, iso_efi_skip, iso_efi_size)
+)
+
+# as default try to use eltorito boot image
+boot_image = "/boot/grub/i386-pc/eltorito.img"
+
+# if eltorito is not available in the iso, fall back to isolinux
+if not os.path.exists(os.path.join(TMP_DIR, boot_image)):
+ with open(os.path.join(TMP_DIR, "isolinux/txt.cfg"), "w") as f:
+ f.write(j2.get_template("isolinux.cfg.j2").render(config=config))
+ if os.path.exists("/config/splash.pcx"):
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --add-files /config/splash.pcx"
+ % (TMP_DIR)
+ )
+ else:
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --add-files default/splash.pcx"
+ % (TMP_DIR)
+ )
+ if os.path.exists("/config/access.pcx"):
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --add-files /config/access.pcx"
+ % (TMP_DIR)
+ )
+ else:
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --add-files default/access.pcx"
+ % (TMP_DIR)
+ )
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --change-config background='%s'"
+ % (TMP_DIR, config['isolinux']['txtBackgroundColor'])
+ )
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --change-config foreground='%s'"
+ % (TMP_DIR, config['isolinux']['txtForegroundColor'])
+ )
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --change-config screen-colour='%s'"
+ % (TMP_DIR, config['isolinux']['screenColor'])
+ )
+ os.system("gfxboot -a '%s/isolinux/bootlogo' --default-language en_US" % (TMP_DIR))
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --rm-config hidden-timeout" % (TMP_DIR)
+ )
+ os.system(
+ "gfxboot -a '%s/isolinux/bootlogo' --change-config hidden-timeout=1" % (TMP_DIR)
+ )
+ boot_image = "isolinux/isolinux.bin"
+
+os.system(
+ """xorriso -as mkisofs -r -V '%s' \
+-o 'output.iso' \
+--grub2-mbr '%s' \
+-partition_offset 16 \
+--mbr-force-bootable \
+-append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b efi.img \
+-appended_part_as_gpt \
+-iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
+-c '/boot.catalog' \
+-b '%s' \
+-no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \
+-eltorito-alt-boot \
+-e '--interval:appended_partition_2:::' \
+-no-emul-boot \
+'%s'"""
+ % (config['client_name']['short'], MBR_FILE, boot_image, TMP_DIR)
+)
+
+# remove temp directory
+shutil.rmtree(TMP_DIR)
+
+# remove mbr
+os.remove(MBR_FILE)
+
+# Move iso to output directory
+shutil.move("output.iso", os.path.join("/output/", config['output']['iso_filename']))
+
+# Generate checksum
+with open("/output/%s.sha256sum" % (config['output']['iso_filename']), "w") as f:
+ f.write(
+ subprocess.check_output("sha256sum '/output/%s'" % (config['output']['iso_filename']), shell=True).decode("utf-8").rstrip()
+ )
+
+print("Done!")
diff --git a/container/change-keyboard-layout.j2 b/container/change-keyboard-layout.j2
index 39a3d9f..c95000e 100644
--- a/container/change-keyboard-layout.j2
+++ b/container/change-keyboard-layout.j2
@@ -22,4 +22,4 @@ gsettings set org.gnome.desktop.input-sources mru-sources "[('xkb', '${KEYBOARD_
# Write this settings permamently, keep them after reboot, as root user
sudo sed -i 's/XKBLAYOUT=\".*"/XKBLAYOUT=\"'${KEYBOARD_LAYOUT}'\"/g' /etc/default/keyboard
-echo "${KEYBOARD_LAYOUT}" > /tmp/{{ POTOS_CLIENT_SHORTNAME }}_keyboardlayout
+echo "${KEYBOARD_LAYOUT}" > /tmp/{{ config['client_name']['short'] }}_keyboardlayout
diff --git a/container/default/access.pcx b/container/default/access.pcx
new file mode 100644
index 0000000..9e93dc0
Binary files /dev/null and b/container/default/access.pcx differ
diff --git a/container/logo.png b/container/default/logo.png
similarity index 100%
rename from container/logo.png
rename to container/default/logo.png
diff --git a/container/default/splash.pcx b/container/default/splash.pcx
new file mode 100644
index 0000000..b5cd4d9
Binary files /dev/null and b/container/default/splash.pcx differ
diff --git a/container/finish.sh.j2 b/container/finish.sh.j2
index eb29492..1463a2e 100755
--- a/container/finish.sh.j2
+++ b/container/finish.sh.j2
@@ -12,17 +12,17 @@ ANSIBLE_GIT_URL='https://github.com/projectpotos/ansible-plays-potos.git'
ANSIBLE_GIT_BRANCH='main'
mkdir -p "/etc/potos"
-mkdir -p "/var/log/{{ POTOS_CLIENT_SHORTNAME }}"
+mkdir -p "/var/log/{{ config['client_name']['short']}}"
cat > /etc/potos/specs_repo.yml < /var/log/{{ POTOS_CLIENT_SHORTNAME }}/setup.log" &
+sudo mkdir -m 755 -p /var/log/{{ config['client_name']['short'] }}
+sudo touch /var/log/{{ config['client_name']['short'] }}/setup.log
+sudo -E bash -c "/setup/finish.sh &> /var/log/{{ config['client_name']['short'] }}/setup.log" &
FINISH_CMD_PID=${!}
sudo chown gnome-initial-setup /dev/tty2
-sudo tail -f /var/log/{{ POTOS_CLIENT_SHORTNAME }}/setup.log | tee /dev/tty2 | yad --fullscreen --no-buttons --title "{{ POTOS_CLIENT_NAME }} Setup" \
+sudo tail -f /var/log/{{ config['client_name']['short'] }}/setup.log | tee /dev/tty2 | yad --fullscreen --no-buttons --title "{{ config['client_name']['long'] }} Setup" \
--progress --enable-log --log-expanded --log-on-top --log-height 500 \
--text 'Please wait until the setup is finished' &
@@ -91,7 +82,7 @@ if [[ ${FINISH_CMD_RC} -ne 0 ]]; then
esac
fi
-sudo cat /var/log/{{ POTOS_CLIENT_SHORTNAME }}/setup.log | yad --fullscreen --title "{{ POTOS_CLIENT_NAME }} Setup" \
+sudo cat /var/log/{{ config['client_name']['short'] }}/setup.log | yad --fullscreen --title "{{ config['client_name']['long'] }} Setup" \
--borders 20 --align center --button gtk-ok \
--button "Shutdown":"sudo systemctl halt" \
--text-info --tail \
diff --git a/container/grub.cfg.j2 b/container/grub.cfg.j2
index 5db1c54..c67c679 100644
--- a/container/grub.cfg.j2
+++ b/container/grub.cfg.j2
@@ -4,14 +4,15 @@ loadfont unicode
set menu_color_normal=white/black
set menu_color_highlight=black/light-gray
+set gfxmode=640x480
grub_platform
if [ "$grub_platform" = "efi" ]; then
-menuentry "Install {{ POTOS_CLIENT_NAME }}" {
+menuentry "Install {{ config['client_name']['long'] }}" {
set gfxpayload=keep
linux /casper/vmlinuz fsck.mode=skip autoinstall ds=nocloud\;s=/cdrom/nocloud-uefi/ ---
initrd /casper/initrd
- {% if POTOS_FULL_UNATTENDED is not defined or POTOS_FULL_UNATTENDED != 'true' -%}
+ {% if config['full_unattended_install'] is not defined or config['full_unattended_install'] != 'true' -%}
echo WARNING: This will erase your hard drive. Press Enter to confirm.
read dummy
{%- endif %}
@@ -24,11 +25,11 @@ menuentry 'UEFI Firmware Settings' {
}
#grub_platform set to bios expected
else
-menuentry "Install {{ POTOS_CLIENT_NAME }}" {
+menuentry "Install {{ config['client_name']['long'] }}" {
set gfxpayload=keep
linux /casper/vmlinuz fsck.mode=skip autoinstall ds=nocloud\;s=/cdrom/nocloud-bios/ ---
initrd /casper/initrd
- {% if POTOS_FULL_UNATTENDED is not defined or POTOS_FULL_UNATTENDED != 'true' -%}
+ {% if config['full_unattended_install'] is not defined or config['full_unattended_install'] != 'true' -%}
echo WARNING: This will erase your hard drive. Press Enter to confirm.
read dummy
{%- endif %}
diff --git a/container/isolinux.cfg.j2 b/container/isolinux.cfg.j2
new file mode 100644
index 0000000..4d40bc8
--- /dev/null
+++ b/container/isolinux.cfg.j2
@@ -0,0 +1,8 @@
+default prod
+label prod
+ menu label ^Install {{ config['client_name']['long'] }}
+ kernel /casper/vmlinuz
+ append initrd=/casper/initrd quiet fsck.mode=skip autoinstall ds=nocloud;s=/cdrom/nocloud-bios/ ---
+label hd
+ menu label ^Boot from first hard disk
+ localboot 0x80
diff --git a/container/requirements.txt b/container/requirements.txt
new file mode 100644
index 0000000..9c893db
--- /dev/null
+++ b/container/requirements.txt
@@ -0,0 +1,2 @@
+pyyaml
+Jinja2