Update ISO installer tiers and backend deployment
This commit is contained in:
1 parent
79784a6743
commit
56668f7bdc
38 files changed
+7206
-175
No files matched your search
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Nexus One AI — Starter Tier ISO Builder
|
||||
# Hardware target: compact workstation (1× RTX 5090, 64 GB RAM, 2 TB NVMe)
|
||||
#
|
||||
# Usage:
|
||||
# cd ~/aipackage
|
||||
# bash autoinstall/build-iso-starter.sh
|
||||
#
|
||||
# Output: autoinstall/cezen-ai-starter-ubuntu2204.iso
|
||||
# Flash to USB:
|
||||
# diskutil unmountDisk /dev/diskN
|
||||
# sudo dd if=cezen-ai-starter-ubuntu2204.iso of=/dev/diskN bs=4m status=progress
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PACKAGE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
WORK_DIR="/tmp/cezen-iso-starter-work"
|
||||
ORIGINAL_ISO="/tmp/ubuntu-22.04.5-live-server-amd64.iso"
|
||||
OUTPUT_ISO="$SCRIPT_DIR/cezen-ai-starter-ubuntu2204.iso"
|
||||
UBUNTU_URL="https://releases.ubuntu.com/22.04.5/ubuntu-22.04.5-live-server-amd64.iso"
|
||||
TIER="starter"
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ Nexus One AI — ISO Builder [STARTER TIER] ║"
|
||||
echo "║ RTX 5090 · 64 GB RAM · 2 TB NVMe · 1–5 users ║"
|
||||
echo "╚══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# ── Install build tools ────────────────────────
|
||||
echo "→ Installing build tools..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq xorriso wget isolinux rsync
|
||||
echo "✓ Tools ready"
|
||||
|
||||
# ── Download Ubuntu ISO ────────────────────────
|
||||
if [ -f "$ORIGINAL_ISO" ]; then
|
||||
echo "✓ Ubuntu ISO already downloaded"
|
||||
else
|
||||
echo "→ Downloading Ubuntu 22.04.5 Server ISO (~1.8 GB)..."
|
||||
wget --show-progress -O "$ORIGINAL_ISO" "$UBUNTU_URL"
|
||||
echo "✓ Downloaded"
|
||||
fi
|
||||
|
||||
# ── Extract ISO ────────────────────────────────
|
||||
echo "→ Extracting ISO..."
|
||||
rm -rf "$WORK_DIR"
|
||||
mkdir -p "$WORK_DIR"
|
||||
xorriso -osirrox on \
|
||||
-indev "$ORIGINAL_ISO" \
|
||||
-extract / "$WORK_DIR" 2>/dev/null
|
||||
chmod -R u+w "$WORK_DIR"
|
||||
echo "✓ Extracted"
|
||||
|
||||
# ── Inject Starter autoinstall files ──────────
|
||||
echo "→ Injecting Starter autoinstall config..."
|
||||
mkdir -p "$WORK_DIR/nocloud"
|
||||
cp "$SCRIPT_DIR/user-data-starter" "$WORK_DIR/nocloud/user-data"
|
||||
cp "$SCRIPT_DIR/meta-data" "$WORK_DIR/nocloud/meta-data"
|
||||
echo "✓ user-data-starter and meta-data injected"
|
||||
|
||||
# ── Online installer mode ──────────────────────
|
||||
# The installed system pulls the current package from cgit on first boot. This
|
||||
# keeps the ISO small and avoids shipping stale backend/portal code.
|
||||
echo "✓ Online installer mode: package will be pulled from cgit on first boot"
|
||||
|
||||
# ── Patch GRUB ────────────────────────────────
|
||||
echo "→ Patching GRUB config..."
|
||||
GRUB_CFG="$WORK_DIR/boot/grub/grub.cfg"
|
||||
cp "$GRUB_CFG" "$GRUB_CFG.orig"
|
||||
|
||||
sed -i "s/set timeout=.*/set timeout=5/" "$GRUB_CFG"
|
||||
sed -i "s/set timeout_style=.*/set timeout_style=countdown/" "$GRUB_CFG"
|
||||
sed -i '/^\s*linux.*vmlinuz/s|---|autoinstall ds=nocloud\\;s=/cdrom/nocloud/ ---|' "$GRUB_CFG"
|
||||
|
||||
# Update GRUB title to reflect Starter tier
|
||||
sed -i 's/Install Ubuntu Server/Install Nexus One AI — Starter Tier/' "$GRUB_CFG" || true
|
||||
echo "✓ GRUB patched"
|
||||
|
||||
# ── Extract MBR and EFI boot data ─────────────
|
||||
echo "→ Extracting boot data from original ISO..."
|
||||
MBR_TEMPLATE=$(mktemp)
|
||||
EFI_IMG=$(mktemp)
|
||||
dd if="$ORIGINAL_ISO" bs=1 count=432 of="$MBR_TEMPLATE" 2>/dev/null
|
||||
|
||||
EFI_LINE=$(fdisk -l "$ORIGINAL_ISO" 2>/dev/null | grep "EFI")
|
||||
echo " EFI partition info: $EFI_LINE"
|
||||
EFI_START=$(echo "$EFI_LINE" | awk '{print $2}')
|
||||
EFI_SIZE=$(echo "$EFI_LINE" | awk '{print $4}')
|
||||
|
||||
if [ -z "$EFI_START" ] || [ -z "$EFI_SIZE" ]; then
|
||||
echo "ERROR: Could not detect EFI partition in ISO."
|
||||
echo "Run: fdisk -l $ORIGINAL_ISO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dd if="$ORIGINAL_ISO" bs=512 skip="$EFI_START" count="$EFI_SIZE" \
|
||||
of="$EFI_IMG" 2>/dev/null
|
||||
echo "✓ EFI partition extracted (start=$EFI_START, size=$EFI_SIZE)"
|
||||
|
||||
# ── Repack ISO (pass 1) ────────────────────────
|
||||
echo "→ Repacking ISO (pass 1)..."
|
||||
xorriso -as mkisofs \
|
||||
-r \
|
||||
-V "CezenAI_Starter_2204" \
|
||||
-o "$OUTPUT_ISO" \
|
||||
--grub2-mbr "$MBR_TEMPLATE" \
|
||||
-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 "/boot/grub/i386-pc/eltorito.img" \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--grub2-boot-info \
|
||||
-eltorito-alt-boot \
|
||||
-e "--interval:appended_partition_2:::" \
|
||||
-no-emul-boot \
|
||||
"$WORK_DIR"
|
||||
|
||||
# ── Refresh md5sum.txt and repack (pass 2) ────
|
||||
echo "→ Refreshing md5sum.txt..."
|
||||
FINAL_DIR=$(mktemp -d)
|
||||
VERIFY_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK_DIR" "$MBR_TEMPLATE" "$EFI_IMG" "$FINAL_DIR" "$VERIFY_DIR"' EXIT
|
||||
|
||||
xorriso -osirrox on -indev "$OUTPUT_ISO" -extract / "$FINAL_DIR" >/dev/null 2>&1
|
||||
chmod -R u+w "$FINAL_DIR"
|
||||
(
|
||||
cd "$FINAL_DIR"
|
||||
rm -f md5sum.txt
|
||||
find . -type f \
|
||||
! -path './md5sum.txt' \
|
||||
! -path './boot.catalog' \
|
||||
-print0 \
|
||||
| sort -z \
|
||||
| xargs -0 md5sum > md5sum.txt
|
||||
)
|
||||
echo "✓ md5sum.txt refreshed"
|
||||
|
||||
echo "→ Repacking ISO (pass 2)..."
|
||||
xorriso -as mkisofs \
|
||||
-r \
|
||||
-V "CezenAI_Starter_2204" \
|
||||
-o "$OUTPUT_ISO" \
|
||||
--grub2-mbr "$MBR_TEMPLATE" \
|
||||
-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 "/boot/grub/i386-pc/eltorito.img" \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--grub2-boot-info \
|
||||
-eltorito-alt-boot \
|
||||
-e "--interval:appended_partition_2:::" \
|
||||
-no-emul-boot \
|
||||
"$FINAL_DIR"
|
||||
|
||||
# ── Verify output ISO ──────────────────────────
|
||||
echo "→ Verifying rebuilt ISO manifest..."
|
||||
xorriso -osirrox on -indev "$OUTPUT_ISO" -extract / "$VERIFY_DIR" >/dev/null 2>&1
|
||||
chmod -R u+w "$VERIFY_DIR"
|
||||
(
|
||||
cd "$VERIFY_DIR"
|
||||
md5sum -c md5sum.txt >/tmp/cezen-iso-md5check-starter.log 2>&1 || {
|
||||
echo "ERROR: Rebuilt ISO failed its own md5sum.txt verification."
|
||||
sed -n '1,40p' /tmp/cezen-iso-md5check-starter.log
|
||||
exit 1
|
||||
}
|
||||
)
|
||||
echo "✓ Output ISO manifest verified"
|
||||
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ Done! Starter Tier ISO ready. ║"
|
||||
echo "╚══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
ls -lh "$OUTPUT_ISO"
|
||||
echo ""
|
||||
echo "→ Transfer to MacBook:"
|
||||
echo " scp user@server:~/aipackage/autoinstall/cezen-ai-starter-ubuntu2204.iso ."
|
||||
echo ""
|
||||
echo "→ Flash to USB (macOS):"
|
||||
echo " diskutil list # find USB e.g. /dev/disk4"
|
||||
echo " diskutil unmountDisk /dev/disk4"
|
||||
echo " sudo dd if=cezen-ai-starter-ubuntu2204.iso of=/dev/disk4 bs=4m status=progress"
|
||||
echo ""
|
||||
echo "→ Post-flash: boot the workstation from USB."
|
||||
echo " Unattended install completes in ~10 min."
|
||||
echo " First-boot wizard runs on tty1 — set IP, org name, admin password."
|
||||
echo " Then run: sudo bash /opt/aipackage/install.sh --tier starter"
|
||||
+65
-26
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Cezen AI Suite — Custom ISO Builder
|
||||
# Nexus One AI — Custom ISO Builder
|
||||
# Runs directly on Ubuntu 22.04 (run on the server)
|
||||
#
|
||||
# Usage:
|
||||
@@ -20,7 +20,7 @@ OUTPUT_ISO="$SCRIPT_DIR/cezen-ai-ubuntu2204.iso"
|
||||
UBUNTU_URL="https://releases.ubuntu.com/22.04.5/ubuntu-22.04.5-live-server-amd64.iso"
|
||||
|
||||
echo "╔══════════════════════════════════════════╗"
|
||||
echo "║ Cezen AI — ISO Builder ║"
|
||||
echo "║ Nexus One AI — ISO Builder ║"
|
||||
echo "╚══════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
@@ -56,15 +56,10 @@ cp "$SCRIPT_DIR/user-data" "$WORK_DIR/nocloud/user-data"
|
||||
cp "$SCRIPT_DIR/meta-data" "$WORK_DIR/nocloud/meta-data"
|
||||
echo "✓ user-data and meta-data injected"
|
||||
|
||||
# Keep the installer payload on the ISO so first boot does not depend on a
|
||||
# private Git server being reachable before the setup UI can start.
|
||||
echo "→ Bundling Cezen AI installer payload..."
|
||||
mkdir -p "$WORK_DIR/cezen-aipackage"
|
||||
rsync -a --delete \
|
||||
--exclude 'autoinstall/cezen-ai-ubuntu2204.iso' \
|
||||
--exclude '*.iso' \
|
||||
"$PACKAGE_DIR/" "$WORK_DIR/cezen-aipackage/"
|
||||
echo "✓ Installer payload bundled"
|
||||
# Keep this as an online installer ISO. The installed system pulls the current
|
||||
# Nexus One AI package from cgit during first boot, which keeps the ISO small and
|
||||
# avoids shipping stale backend/portal code inside the image.
|
||||
echo "✓ Online installer mode: package will be pulled from cgit on first boot"
|
||||
|
||||
# ── Patch GRUB ────────────────────────────────
|
||||
echo "→ Patching GRUB config..."
|
||||
@@ -82,20 +77,6 @@ sed -i "s/set timeout_style=.*/set timeout_style=countdown/" "$GRUB_CFG"
|
||||
sed -i '/^\s*linux.*vmlinuz/s|---|autoinstall ds=nocloud\\;s=/cdrom/nocloud/ ---|' "$GRUB_CFG"
|
||||
echo "✓ GRUB patched"
|
||||
|
||||
# ── Refresh installer checksum manifest ─────────────────
|
||||
echo "→ Refreshing md5sum.txt..."
|
||||
(
|
||||
cd "$WORK_DIR"
|
||||
rm -f md5sum.txt
|
||||
find . -type f \
|
||||
! -path './md5sum.txt' \
|
||||
! -path './boot.catalog' \
|
||||
-print0 \
|
||||
| sort -z \
|
||||
| xargs -0 md5sum > md5sum.txt
|
||||
)
|
||||
echo "✓ md5sum.txt refreshed"
|
||||
|
||||
# ── Extract MBR and EFI partition from original ISO ────
|
||||
echo "→ Extracting boot data from original ISO..."
|
||||
MBR_TEMPLATE=$(mktemp)
|
||||
@@ -119,7 +100,7 @@ dd if="$ORIGINAL_ISO" bs=512 skip="$EFI_START" count="$EFI_SIZE" \
|
||||
echo "✓ EFI partition extracted (start=$EFI_START, size=$EFI_SIZE)"
|
||||
|
||||
# ── Repack ISO ─────────────────────────────────
|
||||
echo "→ Repacking ISO (this takes ~2 minutes)..."
|
||||
echo "→ Repacking ISO (pass 1)..."
|
||||
xorriso -as mkisofs \
|
||||
-r \
|
||||
-V "Cezen_AI_Ubuntu2204" \
|
||||
@@ -141,6 +122,64 @@ xorriso -as mkisofs \
|
||||
-no-emul-boot \
|
||||
"$WORK_DIR"
|
||||
|
||||
echo "→ Refreshing md5sum.txt from pass-1 ISO contents..."
|
||||
FINAL_DIR=$(mktemp -d)
|
||||
VERIFY_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK_DIR" "$MBR_TEMPLATE" "$EFI_IMG" "$FINAL_DIR" "$VERIFY_DIR"' EXIT
|
||||
xorriso -osirrox on \
|
||||
-indev "$OUTPUT_ISO" \
|
||||
-extract / "$FINAL_DIR" >/dev/null 2>&1
|
||||
chmod -R u+w "$FINAL_DIR"
|
||||
(
|
||||
cd "$FINAL_DIR"
|
||||
rm -f md5sum.txt
|
||||
find . -type f \
|
||||
! -path './md5sum.txt' \
|
||||
! -path './boot.catalog' \
|
||||
-print0 \
|
||||
| sort -z \
|
||||
| xargs -0 md5sum > md5sum.txt
|
||||
)
|
||||
echo "✓ md5sum.txt refreshed"
|
||||
|
||||
echo "→ Repacking ISO (pass 2 with final manifest)..."
|
||||
xorriso -as mkisofs \
|
||||
-r \
|
||||
-V "Cezen_AI_Ubuntu2204" \
|
||||
-o "$OUTPUT_ISO" \
|
||||
--grub2-mbr "$MBR_TEMPLATE" \
|
||||
-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 "/boot/grub/i386-pc/eltorito.img" \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--grub2-boot-info \
|
||||
-eltorito-alt-boot \
|
||||
-e "--interval:appended_partition_2:::" \
|
||||
-no-emul-boot \
|
||||
"$FINAL_DIR"
|
||||
|
||||
# ── Verify output ISO integrity manifest ─────────────────
|
||||
echo "→ Verifying rebuilt ISO manifest..."
|
||||
xorriso -osirrox on \
|
||||
-indev "$OUTPUT_ISO" \
|
||||
-extract / "$VERIFY_DIR" >/dev/null 2>&1
|
||||
chmod -R u+w "$VERIFY_DIR"
|
||||
(
|
||||
cd "$VERIFY_DIR"
|
||||
md5sum -c md5sum.txt >/tmp/cezen-iso-md5check.log 2>&1 || {
|
||||
echo "ERROR: Rebuilt ISO failed its own md5sum.txt verification."
|
||||
sed -n '1,40p' /tmp/cezen-iso-md5check.log
|
||||
exit 1
|
||||
}
|
||||
)
|
||||
echo "✓ Output ISO manifest verified"
|
||||
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ Done! ║"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Cezen AI Suite — First Boot Setup Wizard
|
||||
# Nexus One AI — First Boot Setup Wizard
|
||||
# Runs on first boot after OS install via systemd service.
|
||||
# Uses whiptail for the TUI.
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
@@ -8,6 +8,7 @@ set -e
|
||||
|
||||
AIPACKAGE_DIR="/opt/aipackage"
|
||||
LOG_FILE="/var/log/cezen-setup.log"
|
||||
export TERM="${TERM:-linux}"
|
||||
exec > >(tee -a "$LOG_FILE") 2>&1
|
||||
|
||||
detect_iface() {
|
||||
@@ -20,28 +21,28 @@ IFACE="${IFACE:-$(ip -o link show | awk -F': ' '$2 !~ /lo|docker|br-|veth/ {prin
|
||||
# ── Colors / terminal setup ────────────────────────────────
|
||||
export NEWT_COLORS='
|
||||
root=,black
|
||||
window=white,navy
|
||||
border=white,navy
|
||||
title=white,navy
|
||||
button=black,cyan
|
||||
actbutton=white,red
|
||||
checkbox=white,navy
|
||||
actcheckbox=black,cyan
|
||||
entry=white,navy
|
||||
label=white,navy
|
||||
listbox=white,navy
|
||||
actlistbox=black,cyan
|
||||
textbox=white,navy
|
||||
acttextbox=black,cyan
|
||||
window=black,white
|
||||
border=white,black
|
||||
title=black,white
|
||||
button=black,white
|
||||
actbutton=white,blue
|
||||
checkbox=black,white
|
||||
actcheckbox=white,blue
|
||||
entry=black,white
|
||||
label=black,white
|
||||
listbox=black,white
|
||||
actlistbox=white,blue
|
||||
textbox=black,white
|
||||
acttextbox=white,blue
|
||||
'
|
||||
|
||||
TITLE=" Cezen AI Suite — Server Setup "
|
||||
TITLE=" Nexus One AI — Server Setup "
|
||||
H=20
|
||||
W=70
|
||||
|
||||
# ── Welcome ────────────────────────────────────────────────
|
||||
whiptail --title "$TITLE" \
|
||||
--msgbox "\nWelcome to the Cezen AI Suite installer.\n\nThis wizard will configure your network and install the AI stack.\n\nMake sure this server is connected to the internet before continuing." \
|
||||
--msgbox "\nWelcome to the Nexus One AI installer.\n\nThis wizard will configure your network and install the AI stack.\n\nMake sure this server is connected to the internet before continuing." \
|
||||
$H $W
|
||||
|
||||
# ════════════════════════════════════════════════════════════
|
||||
@@ -135,10 +136,11 @@ fi
|
||||
|
||||
TIER=$(whiptail --title "$TITLE" \
|
||||
--menu "\nStep 2 of 3: Select AI Package Tier\n\nChoose the tier that matches your hardware:" \
|
||||
$H $W 3 \
|
||||
"entry" "Entry — 3× NVIDIA L40S (48GB each) · Up to 20 users" \
|
||||
"mid" "Mid — RTX Pro 6000 BW (96GB each) · Up to 50 users" \
|
||||
"advanced" "Advanced — HGX H200 (141GB each) · 200+ users" \
|
||||
$H $W 4 \
|
||||
"starter" "Starter — 1× RTX 5090 / 32GB VRAM · Small team" \
|
||||
"basic" "Entry — 1× NVIDIA RTX Pro 6000 (96GB) · Up to 20 users" \
|
||||
"pro" "Pro — 2× RTX 5090 / RTX Pro class · Up to 100 users" \
|
||||
"max" "Max — 4–8× H100/H200/A100 class · 100+ users" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
# ════════════════════════════════════════════════════════════
|
||||
@@ -177,7 +179,7 @@ whiptail --title "$TITLE" \
|
||||
clear
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════╗"
|
||||
echo "║ Cezen AI Suite — Installing... ║"
|
||||
echo "║ Nexus One AI — Installing... ║"
|
||||
echo "║ Check progress: journalctl -f ║"
|
||||
echo "╚══════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
+11
-15
@@ -85,10 +85,11 @@ autoinstall:
|
||||
# mirrors instead of the custom ISO content.
|
||||
- sed -i 's/^deb cdrom:/# deb cdrom:/' /target/etc/apt/sources.list || true
|
||||
|
||||
# Install the Cezen AI payload from the ISO first. Fall back to Git only
|
||||
# when building from older media that does not contain /cdrom/cezen-aipackage.
|
||||
# Pull the Nexus One AI installer from cgit. The ISO intentionally does not
|
||||
# bundle the full package, keeping the image small and the installed code
|
||||
# current at deployment time.
|
||||
- mkdir -p /target/opt/aipackage
|
||||
- cp -a /cdrom/cezen-aipackage/. /target/opt/aipackage/ || git clone https://cgit.cezentech.com/jinojose/aipackage.git /target/opt/aipackage
|
||||
- git clone https://cgit.cezentech.com/jinojose/aipackage.git /target/opt/aipackage
|
||||
|
||||
# Deploy the console setup wizard
|
||||
- mkdir -p /target/opt/cezen
|
||||
@@ -104,23 +105,18 @@ autoinstall:
|
||||
- |
|
||||
cat > /target/etc/systemd/system/cezen-setup.service << 'EOF'
|
||||
[Unit]
|
||||
Description=Cezen AI Suite — Console Setup Wizard
|
||||
Description=Nexus One AI — Console Setup Wizard
|
||||
After=cloud-final.service cloud-init.target network-online.target
|
||||
Wants=cloud-init.target network-online.target
|
||||
Conflicts=getty@tty1.service
|
||||
ConditionPathExists=!/opt/cezen/.setup-done
|
||||
OnFailure=getty@tty1.service
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
ExecStartPre=-/usr/bin/systemctl stop getty@tty1.service
|
||||
ExecStart=/bin/bash -lc 'clear >/dev/tty1 2>/dev/null || true; exec /opt/cezen/firstboot-setup.sh'
|
||||
StandardInput=tty-force
|
||||
StandardOutput=tty
|
||||
StandardError=tty
|
||||
TTYPath=/dev/tty1
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/cezen
|
||||
ExecStart=/bin/bash -lc 'clear >/dev/tty1 2>/dev/null || true; /usr/bin/openvt -c 1 -f -w -- env TERM=linux /opt/cezen/firstboot-setup.sh'
|
||||
StandardOutput=journal+console
|
||||
StandardError=journal+console
|
||||
Restart=no
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
#cloud-config
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Nexus One AI — Starter Tier Autoinstall
|
||||
# Hardware target: compact workstation (Mini-ITX / SFF)
|
||||
# GPU: 1× NVIDIA RTX 5090 (32 GB GDDR7)
|
||||
# RAM: 64 GB DDR5
|
||||
# Storage: 1× 2 TB NVMe SSD (single drive — simple LVM)
|
||||
# Network: 2.5 GbE (single interface)
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
autoinstall:
|
||||
version: 1
|
||||
|
||||
# ── Locale & keyboard ──────────────────────────
|
||||
locale: en_IN.UTF-8
|
||||
keyboard:
|
||||
layout: us
|
||||
|
||||
# ── Network: DHCP during install; static config applied post-install ──
|
||||
network:
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
any-en:
|
||||
dhcp4: true
|
||||
match:
|
||||
name: "en*"
|
||||
any-eth:
|
||||
dhcp4: true
|
||||
match:
|
||||
name: "eth*"
|
||||
|
||||
# ── Storage: single 2 TB NVMe, simple LVM ─────
|
||||
# Starter workstations have one drive — no RAID needed.
|
||||
storage:
|
||||
layout:
|
||||
name: lvm
|
||||
match:
|
||||
size: largest
|
||||
|
||||
# ── Identity ──────────────────────────────────
|
||||
identity:
|
||||
hostname: cezenai-starter
|
||||
username: cezen
|
||||
# Default password: cezen@123 (change via first-boot wizard)
|
||||
password: "$6$I5VA.42G1xTeVhCv$KCLzqIKg/kbNHZyiTEMAY4FZsJMDDwoS90k6Ffb9VEwmcK.wuzlJNe3ceiEfLrzYzXEvqjYsLc7klAbeGPGab."
|
||||
|
||||
# ── SSH ───────────────────────────────────────
|
||||
ssh:
|
||||
install-server: true
|
||||
allow-pw: true
|
||||
|
||||
# ── Base packages ─────────────────────────────
|
||||
packages:
|
||||
- git
|
||||
- curl
|
||||
- wget
|
||||
- python3
|
||||
- whiptail
|
||||
- openssh-server
|
||||
- nvme-cli # NVMe health / SMART monitoring
|
||||
|
||||
# ── Late commands ─────────────────────────────
|
||||
late-commands:
|
||||
# Expand LVM to fill the full 2 TB NVMe
|
||||
- lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv || true
|
||||
- resize2fs /dev/ubuntu-vg/ubuntu-lv || true
|
||||
|
||||
# Passwordless sudo for cezen (needed by install.sh + first-boot wizard)
|
||||
- echo "cezen ALL=(ALL) NOPASSWD:ALL" > /target/etc/sudoers.d/cezen
|
||||
- chmod 440 /target/etc/sudoers.d/cezen
|
||||
|
||||
# Replace installer netplan with simple DHCP target config.
|
||||
# The first-boot wizard will switch to static if desired.
|
||||
- rm -f /target/etc/netplan/50-cloud-init.yaml /target/etc/netplan/00-installer-config.yaml || true
|
||||
- |
|
||||
cat > /target/etc/netplan/99-cezen-dhcp.yaml << 'EOF'
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
any-en:
|
||||
dhcp4: true
|
||||
match:
|
||||
name: "en*"
|
||||
any-eth:
|
||||
dhcp4: true
|
||||
match:
|
||||
name: "eth*"
|
||||
EOF
|
||||
|
||||
# Disable cdrom APT source
|
||||
- sed -i 's/^deb cdrom:/# deb cdrom:/' /target/etc/apt/sources.list || true
|
||||
|
||||
# Pull the Nexus One AI installer from cgit. The ISO intentionally does not
|
||||
# bundle the full package, keeping the image small and the installed code
|
||||
# current at deployment time.
|
||||
- mkdir -p /target/opt/aipackage
|
||||
- git clone https://cgit.cezentech.com/jinojose/aipackage.git /target/opt/aipackage
|
||||
|
||||
# Write tier marker — used by install.sh and the portal branding system
|
||||
- mkdir -p /target/opt/cezen
|
||||
- echo "starter" > /target/opt/cezen/tier
|
||||
|
||||
# Deploy first-boot TUI wizard
|
||||
- cp /target/opt/aipackage/autoinstall/firstboot-setup.sh /target/opt/cezen/firstboot-setup.sh
|
||||
- chmod +x /target/opt/cezen/firstboot-setup.sh
|
||||
|
||||
# Set hostname
|
||||
- echo "cezenai-starter" > /target/etc/hostname
|
||||
- sed -i 's/aiserver/cezenai-starter/g' /target/etc/hosts || true
|
||||
|
||||
# Systemd service: run first-boot wizard on tty1 once
|
||||
- |
|
||||
cat > /target/etc/systemd/system/cezen-setup.service << 'EOF'
|
||||
[Unit]
|
||||
Description=Nexus One AI — Console Setup Wizard (Starter)
|
||||
After=cloud-final.service cloud-init.target network-online.target
|
||||
Wants=cloud-init.target network-online.target
|
||||
ConditionPathExists=!/opt/cezen/.setup-done
|
||||
OnFailure=getty@tty1.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/cezen
|
||||
ExecStart=/bin/bash -lc 'clear >/dev/tty1 2>/dev/null || true; /usr/bin/openvt -c 1 -f -w -- env TERM=linux CEZEN_TIER=starter /opt/cezen/firstboot-setup.sh'
|
||||
StandardOutput=journal+console
|
||||
StandardError=journal+console
|
||||
Restart=no
|
||||
|
||||
[Install]
|
||||
WantedBy=cloud-init.target
|
||||
EOF
|
||||
|
||||
- curtin in-target -- systemctl enable ssh
|
||||
- curtin in-target -- systemctl enable cezen-setup.service
|
||||
|
||||
user-data:
|
||||
disable_root: false
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cezen AI Suite — First Boot Web Setup Server
|
||||
Nexus One AI — First Boot Web Setup Server
|
||||
Serves on port 80. Access from any browser on the same network.
|
||||
"""
|
||||
import os, json, subprocess, threading, time, socket, ipaddress
|
||||
@@ -123,7 +123,7 @@ HTML = r"""<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cezen AI Suite — Server Setup</title>
|
||||
<title>Nexus One AI — Server Setup</title>
|
||||
<style>
|
||||
:root {
|
||||
--navy: #1B2A4A;
|
||||
@@ -317,19 +317,24 @@ HTML = r"""<!DOCTYPE html>
|
||||
<h2>Select AI Package Tier</h2>
|
||||
<p class="desc">Choose the tier that matches your GPU hardware.</p>
|
||||
<div class="tier-grid">
|
||||
<div class="tier-card" id="tier-entry" onclick="selectTier('entry')">
|
||||
<div class="tier-card" id="tier-starter" onclick="selectTier('starter')">
|
||||
<div class="tier-name">Starter</div>
|
||||
<div class="tier-gpu">1× RTX 5090 / 32GB VRAM</div>
|
||||
<div class="tier-users">Small team deployment</div>
|
||||
</div>
|
||||
<div class="tier-card" id="tier-basic" onclick="selectTier('basic')">
|
||||
<div class="tier-name">Entry</div>
|
||||
<div class="tier-gpu">3× NVIDIA L40S</div>
|
||||
<div class="tier-gpu">1× NVIDIA RTX Pro 6000 (96GB)</div>
|
||||
<div class="tier-users">Up to 20 concurrent users</div>
|
||||
</div>
|
||||
<div class="tier-card" id="tier-mid" onclick="selectTier('mid')">
|
||||
<div class="tier-name">Mid</div>
|
||||
<div class="tier-gpu">3× RTX Pro 6000</div>
|
||||
<div class="tier-users">Up to 50 concurrent users</div>
|
||||
<div class="tier-card" id="tier-pro" onclick="selectTier('pro')">
|
||||
<div class="tier-name">Pro</div>
|
||||
<div class="tier-gpu">2× RTX 5090 / RTX Pro class</div>
|
||||
<div class="tier-users">Up to 100 concurrent users</div>
|
||||
</div>
|
||||
<div class="tier-card" id="tier-advanced" onclick="selectTier('advanced')">
|
||||
<div class="tier-name">Advanced</div>
|
||||
<div class="tier-gpu">8× HGX H200</div>
|
||||
<div class="tier-card" id="tier-max" onclick="selectTier('max')">
|
||||
<div class="tier-name">Max</div>
|
||||
<div class="tier-gpu">4–8× H100/H200/A100 class</div>
|
||||
<div class="tier-users">200+ concurrent users</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -369,7 +374,7 @@ HTML = r"""<!DOCTYPE html>
|
||||
|
||||
<div class="progress-wrap" id="progress-wrap">
|
||||
<div class="card">
|
||||
<h2>Installing Cezen AI Suite...</h2>
|
||||
<h2>Installing Nexus One AI...</h2>
|
||||
<div class="progress-bar-bg"><div class="progress-bar" id="progress-bar"></div></div>
|
||||
<p id="progress-label" style="font-size:13px;color:var(--muted);margin-bottom:12px">Starting...</p>
|
||||
<div class="log-box" id="log-box"></div>
|
||||
@@ -379,7 +384,7 @@ HTML = r"""<!DOCTYPE html>
|
||||
<div id="done-screen" class="hidden">
|
||||
<div class="done-icon">✅</div>
|
||||
<h2>Installation Complete!</h2>
|
||||
<p>Your Cezen AI Suite is ready.</p>
|
||||
<p>Your Nexus One AI is ready.</p>
|
||||
<div class="services card" style="margin-top:24px;text-align:left">
|
||||
<div class="summary-row"><span class="key">Open WebUI</span><span class="val badge">:3001</span></div>
|
||||
<div class="summary-row"><span class="key">JupyterLab</span><span class="val badge">:8888</span></div>
|
||||
@@ -400,7 +405,7 @@ HTML = r"""<!DOCTYPE html>
|
||||
<script>
|
||||
// ── State ──────────────────────────────────────────────────
|
||||
let netMode = 'dhcp';
|
||||
let selectedTier = 'entry';
|
||||
let selectedTier = 'basic';
|
||||
let tools = {
|
||||
ollama: { name: 'Ollama + Open WebUI', desc: 'LLM inference & chat', icon: '🤖', on: true },
|
||||
jupyterlab: { name: 'JupyterLab', desc: 'Notebook environment', icon: '📓', on: true },
|
||||
@@ -418,7 +423,7 @@ window.onload = () => {
|
||||
document.getElementById('current-ip').textContent = d.ip || 'unknown';
|
||||
});
|
||||
renderTools();
|
||||
selectTier('entry');
|
||||
selectTier('basic');
|
||||
};
|
||||
|
||||
// ── Navigation ─────────────────────────────────────────────
|
||||
@@ -467,7 +472,7 @@ function applyStaticIP() {
|
||||
// ── Tier ───────────────────────────────────────────────────
|
||||
function selectTier(t) {
|
||||
selectedTier = t;
|
||||
['entry','mid','advanced'].forEach(x =>
|
||||
['starter','basic','pro','max'].forEach(x =>
|
||||
document.getElementById('tier-'+x).classList.toggle('selected', x===t));
|
||||
}
|
||||
|
||||
@@ -707,7 +712,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
elif path == "/api/install":
|
||||
global install_proc
|
||||
tier = body.get("tier", "entry")
|
||||
tier = body.get("tier", "basic")
|
||||
skip = body.get("skip_tools", [])
|
||||
if not install_status["running"]:
|
||||
t = threading.Thread(target=run_install, args=(tier, skip), daemon=True)
|
||||
@@ -752,7 +757,7 @@ def show_console_banner(ip):
|
||||
try:
|
||||
with open("/etc/issue", "w") as f:
|
||||
f.write(f"Ubuntu 22.04.5 LTS \\n \\l\n\n")
|
||||
f.write(f" \033[1;36mCezen AI Suite Setup:\033[0m http://{ip} | http://cezenai.local\n\n")
|
||||
f.write(f" \033[1;36mNexus One AI Setup:\033[0m http://{ip} | http://cezenai.local\n\n")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in new issue
Block a user