Initial commit: MAAS-deployable Proxmox VE images with cluster automation
Builds a Proxmox VE image that MAAS can deploy to bare metal, plus first-boot
automation that configures the node and joins it to a Proxmox cluster with no
manual steps.
The image starts from the official Debian cloud image and installs proxmox-ve
on top of it, rather than capturing a raw disk from the Proxmox ISO. That keeps
MAAS in control of partitioning, networking, SSH keys and cloud-init, and makes
moving between Proxmox releases a variable change instead of a rewrite.
Contents:
* Makefile driving the whole flow: build, verify, preseed, upload
* customize-proxmox.sh, run inside the Packer build VM, which layers Proxmox
onto the Debian cloud image and resets the pmxcfs node identity so one image
can produce many nodes
* pve-maas-init, a first-boot state machine covering /etc/hosts, node-unique
identifiers, the root password, vmbr0 conversion, cluster create/join and
the local-lvm thin pool; each stage is resumable across reboots
* curtin-hooks, which stops curtin installing a kernel over APT and pins
interface names by MAC so they match what MAAS recorded at commissioning
* a MAAS curtin preseed template and cloud-init examples
* deploy-cluster.sh, which builds a whole cluster through the MAAS API
* verify-image.sh, 22 static checks on the produced tarball
Cluster identity lives entirely in deploy-time cloud-init user-data, so a single
image and preseed can build any number of independent clusters.
Verified end to end against MAAS 3.7.2: proxmox-ve 9.2.0 / pve-manager 9.2.11 /
kernel 7.0.14-15-pve, deployed to two machines that formed a quorate cluster with
local-lvm on both, with no manual intervention.
The README documents four failure modes found along the way that all fail
silently: curtin rejecting "kernel: null", pvenetcommit overwriting the network
configuration at boot, interface renaming leaving the link down, and a systemd
ordering cycle that made systemd delete the service's start job.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
114
overlay/curtin/curtin-hooks
Executable file
114
overlay/curtin/curtin-hooks
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# curtin-hooks - MAAS/curtin kurulum kancasi (Proxmox VE imaji icin)
|
||||
#
|
||||
# Curtin, hedef sistemde /curtin/curtin-hooks varsa dahili curthooks yerine
|
||||
# bu script'i calistirir (curtin.util.run_hook_if_exists).
|
||||
#
|
||||
# Amac: curtin'in APT uzerinden cekirdek kurmaya calismasini engellemek.
|
||||
# Proxmox cekirdegi (proxmox-default-kernel) imajin icinde hazir gelir;
|
||||
# curtin'in onu yeniden kurmasi gereksizdir ve dugumun deploy sirasinda
|
||||
# download.proxmox.com'a erisebilmesini zorunlu kilar (izole aglarda calismaz).
|
||||
#
|
||||
# Not: Bunu preseed'de "kernel: null" ile yapmak mumkun degil - MAAS ile
|
||||
# gelen curtin surumlerinin bir kismi bu degeri desteklemiyor ve
|
||||
# install_kernel icinde AttributeError ile patliyor. Kancayi kullanmak
|
||||
# curtin surumunden bagimsiz olarak calisir.
|
||||
#
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from curtin.commands import curthooks
|
||||
from curtin.config import load_command_config
|
||||
from curtin.util import load_command_environment
|
||||
|
||||
|
||||
def disable_kernel_install():
|
||||
"""curtin'in cekirdek kurulum adimini etkisiz hale getir.
|
||||
|
||||
Fonksiyon adi curtin surumune gore degisiyor (install_kernel ->
|
||||
curthook_install_kernel), ikisini de yakaliyoruz.
|
||||
"""
|
||||
disabled = []
|
||||
for name in ("install_kernel", "curthook_install_kernel"):
|
||||
if hasattr(curthooks, name):
|
||||
setattr(curthooks, name, lambda *args, **kwargs: None)
|
||||
disabled.append(name)
|
||||
print("curtin-hooks: cekirdek kurulumu devre disi: %s" % (disabled or "hicbiri bulunamadi"))
|
||||
|
||||
|
||||
def pin_interface_names(config, target):
|
||||
"""MAAS'in bildigi arayuz adlarini hedef sistemde MAC uzerinden sabitle.
|
||||
|
||||
MAAS commissioning'i Ubuntu ephemeral ortaminda yapiyor ve arayuzu orada
|
||||
gordugu adla (or. enp6s18) kaydediyor. Deploy edilen Debian 13 / Proxmox
|
||||
ise udev'in farkli isimlendirme semasi yuzunden ayni karti ens18 diye
|
||||
adlandirabiliyor. Bu durumda cloud-init acilista arayuzu yeniden
|
||||
adlandirmaya calisiyor, su hatayi aliyor:
|
||||
|
||||
Failed to rename devices: [busy] Error renaming
|
||||
mac=... from ens18 to enp6s18
|
||||
|
||||
...ve arayuzu KAPALI birakiyor; dugum agini tamamen kaybediyor.
|
||||
|
||||
Cozum: MAC -> ad eslemesini systemd .link dosyasi olarak yaziyoruz, boylece
|
||||
udev karti en bastan MAAS'in bekledigi adla olusturuyor ve yeniden
|
||||
adlandirmaya hic gerek kalmiyor.
|
||||
"""
|
||||
net = config.get("network") or {}
|
||||
pairs = []
|
||||
|
||||
# netplan / v2 bicimi
|
||||
for name, cfg in (net.get("ethernets") or {}).items():
|
||||
cfg = cfg or {}
|
||||
mac = (cfg.get("match") or {}).get("macaddress")
|
||||
if mac:
|
||||
pairs.append((cfg.get("set-name") or name, mac))
|
||||
|
||||
# curtin v1 bicimi
|
||||
for entry in net.get("config") or []:
|
||||
if not isinstance(entry, dict) or entry.get("type") != "physical":
|
||||
continue
|
||||
if entry.get("name") and entry.get("mac_address"):
|
||||
pairs.append((entry["name"], entry["mac_address"]))
|
||||
|
||||
if not pairs:
|
||||
print("curtin-hooks: arayuz adi sabitlenmedi (ag yapilandirmasi bos)")
|
||||
return
|
||||
|
||||
link_dir = os.path.join(target, "etc/systemd/network")
|
||||
os.makedirs(link_dir, exist_ok=True)
|
||||
for name, mac in pairs:
|
||||
path = os.path.join(link_dir, "10-maas-%s.link" % name)
|
||||
with open(path, "w") as fh:
|
||||
fh.write(
|
||||
"# MAAS tarafindan beklenen arayuz adi - pve-maas imaji\n"
|
||||
"# curtin-hooks tarafindan olusturuldu.\n"
|
||||
"[Match]\n"
|
||||
"MACAddress=%s\n"
|
||||
"\n"
|
||||
"[Link]\n"
|
||||
"Name=%s\n" % (mac.lower(), name)
|
||||
)
|
||||
print("curtin-hooks: arayuz adi sabitlendi %s -> %s" % (mac, name))
|
||||
|
||||
|
||||
def cleanup():
|
||||
"""Kancayi hedef sistemden kaldir - iz birakma."""
|
||||
curtin_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
shutil.rmtree(curtin_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
state = load_command_environment()
|
||||
config = load_command_config(None, state)
|
||||
target = state["target"]
|
||||
|
||||
disable_kernel_install()
|
||||
curthooks.builtin_curthooks(config, target, state)
|
||||
pin_interface_names(config, target)
|
||||
cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
overlay/etc/pve-maas/conf.d/.keep
Normal file
0
overlay/etc/pve-maas/conf.d/.keep
Normal file
98
overlay/etc/pve-maas/pve-maas.conf
Normal file
98
overlay/etc/pve-maas/pve-maas.conf
Normal file
@@ -0,0 +1,98 @@
|
||||
# /etc/pve-maas/pve-maas.conf
|
||||
#
|
||||
# pve-maas-init varsayilan yapilandirmasi. BU DOSYAYI DUZENLEMEYIN.
|
||||
# Dugume ozel ayarlari MAAS deploy sirasinda cloud-init ile
|
||||
# /etc/pve-maas/conf.d/*.conf altina yazin; oradaki degerler bunlari ezer.
|
||||
#
|
||||
# Ornek (cloud-init user-data):
|
||||
# write_files:
|
||||
# - path: /etc/pve-maas/conf.d/50-cluster.conf
|
||||
# permissions: "0600"
|
||||
# content: |
|
||||
# PVE_ROOT_PASSWORD='...'
|
||||
# PVE_CLUSTER_MODE=join
|
||||
# PVE_CLUSTER_PEER=192.0.2.11
|
||||
# PVE_CLUSTER_PEER_PASSWORD='...'
|
||||
|
||||
# ---------------------------------------------------------------- genel
|
||||
# Tum ilk acilis otomasyonunu kapatmak icin false yapin.
|
||||
PVE_ENABLED=true
|
||||
|
||||
# Bos birakilirsa hostname -f / resolv.conf'tan turetilir.
|
||||
PVE_FQDN=
|
||||
|
||||
# ---------------------------------------------------------------- ag
|
||||
# MAAS'in yapilandirdigi arayuz vmbr0 koprusune donusturulsun mu?
|
||||
# Bond/VLAN gibi karmasik topolojilerde false yapip /etc/network/interfaces
|
||||
# dosyasini cloud-init ile kendiniz yazin.
|
||||
PVE_NET_MANAGE=true
|
||||
|
||||
PVE_NET_BRIDGE=vmbr0
|
||||
|
||||
# Bos ise varsayilan rotayi tasiyan arayuz kullanilir.
|
||||
PVE_NET_UPLINK=
|
||||
|
||||
# auto | static | dhcp (auto: MAAS'in yazdigi yapilandirmadan tespit edilir)
|
||||
PVE_NET_MODE=auto
|
||||
|
||||
# reboot | reload | none
|
||||
# reboot : yapilandirmayi yazip bir kez yeniden baslatir (en guvenilir)
|
||||
# reload : ifreload -a ile canli uygular (yeniden baslatma yok)
|
||||
# none : sadece yazar, uygulamaz
|
||||
PVE_NET_APPLY=reboot
|
||||
|
||||
# Koprude VLAN farkindaligi (bridge-vlan-aware yes, vids 2-4094)
|
||||
PVE_NET_VLAN_AWARE=false
|
||||
|
||||
# /etc/network/interfaces sonuna eklenecek ham metin (ek kopruler vb.)
|
||||
PVE_NET_EXTRA=
|
||||
|
||||
# ---------------------------------------------------------------- root parolasi
|
||||
# root@pam web arayuzu girisi ve baska dugumlerin bu dugume katilmasi icin gerekli.
|
||||
# Duz metin yerine hash tercih edin: openssl passwd -6
|
||||
PVE_ROOT_PASSWORD=
|
||||
PVE_ROOT_PASSWORD_HASH=
|
||||
|
||||
# ---------------------------------------------------------------- kume
|
||||
# none | create | join
|
||||
PVE_CLUSTER_MODE=none
|
||||
|
||||
# create icin
|
||||
PVE_CLUSTER_NAME=
|
||||
|
||||
# join icin: mevcut bir kume dugumunun IP/hostname'i ve root@pam parolasi
|
||||
PVE_CLUSTER_PEER=
|
||||
PVE_CLUSTER_PEER_PASSWORD=
|
||||
PVE_CLUSTER_PEER_PASSWORD_FILE=
|
||||
|
||||
# Sertifika parmak izi. Bos ve DISCOVER=true ise dugumden okunur (TOFU).
|
||||
# Guvenlik icin degeri onceden vermeniz onerilir:
|
||||
# openssl s_client -connect NODE:8006 </dev/null 2>/dev/null | \
|
||||
# openssl x509 -noout -fingerprint -sha256 | cut -d= -f2
|
||||
PVE_CLUSTER_FINGERPRINT=
|
||||
PVE_CLUSTER_FINGERPRINT_DISCOVER=true
|
||||
|
||||
# Corosync baglantilari (ayri bir kume agi kullaniyorsaniz)
|
||||
PVE_CLUSTER_LINK0=
|
||||
PVE_CLUSTER_LINK1=
|
||||
PVE_CLUSTER_NODEID=
|
||||
PVE_CLUSTER_VOTES=
|
||||
|
||||
# Kume dugumunun erisilebilir olmasi icin beklenecek sure (saniye) ve deneme sayisi
|
||||
PVE_CLUSTER_WAIT=900
|
||||
PVE_CLUSTER_RETRIES=5
|
||||
|
||||
# Katilimdan sonra conf.d icindeki parolalari sil
|
||||
PVE_CLUSTER_WIPE_SECRETS=true
|
||||
|
||||
# ---------------------------------------------------------------- depolama
|
||||
# auto | off | <vg-adi>
|
||||
# auto: en cok bos alani olan VG secilir
|
||||
PVE_THINPOOL=auto
|
||||
PVE_THINPOOL_NAME=data
|
||||
PVE_THINPOOL_STORAGE=local-lvm
|
||||
PVE_THINPOOL_MIN_GB=16
|
||||
|
||||
# Ayri bir disk verilirse o diskten yeni bir VG olusturulur.
|
||||
PVE_THINPOOL_DISK=
|
||||
PVE_THINPOOL_VG=pve
|
||||
44
overlay/etc/systemd/system/pve-maas-init.service
Normal file
44
overlay/etc/systemd/system/pve-maas-init.service
Normal file
@@ -0,0 +1,44 @@
|
||||
[Unit]
|
||||
Description=Proxmox VE post-deployment configuration for MAAS
|
||||
Documentation=file:/etc/pve-maas/pve-maas.conf
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
ConditionPathExists=!/var/lib/pve-maas/complete
|
||||
|
||||
# DIKKAT - burada cloud-init servislerine "After=" YAZMAYIN.
|
||||
#
|
||||
# After=cloud-final.service + WantedBy=multi-user.target birlesimi bu imajda
|
||||
# siralama dongusu olusturuyor ve systemd dongoyu kirmak icin BU servisin
|
||||
# baslatma isini siliyor:
|
||||
#
|
||||
# multi-user.target: Found ordering cycle on pve-maas-init.service/start
|
||||
# multi-user.target: Found dependency on cloud-final.service/start
|
||||
# multi-user.target: Found dependency on multi-user.target/start
|
||||
# Job pve-maas-init.service/start deleted to break ordering cycle
|
||||
#
|
||||
# Bunun yerine script kendi icinde "cloud-init status --wait" ile bekliyor;
|
||||
# boylece hem conf.d dosyalari yazilmis oluyor hem de MAAS deploy sinyalini
|
||||
# almis oluyor, systemd bagimlilik grafigine dokunmadan.
|
||||
|
||||
[Service]
|
||||
# Type=simple (oneshot DEGIL) - bu onemli.
|
||||
#
|
||||
# Type=oneshot ile servis bitene kadar multi-user.target ulasilmis sayilmiyor.
|
||||
# cloud-final.service ise "After=multi-user.target" ile siralanmis. Script
|
||||
# icinde cloud-init'i bekledigimiz icin bu bir kilitlenme uretiyordu:
|
||||
#
|
||||
# cloud-final.service waiting (multi-user.target bekliyor)
|
||||
# pve-maas-init.service running (cloud-init bekliyor)
|
||||
# multi-user.target waiting (pve-maas-init bekliyor)
|
||||
#
|
||||
# Type=simple ile servis hemen "basladi" sayilir, multi-user.target acilir,
|
||||
# cloud-final calisir ve beklememiz doner. Asama takibi zaten
|
||||
# /var/lib/pve-maas/*.done dosyalariyla yapiliyor.
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/pve-maas-init
|
||||
TimeoutStartSec=0
|
||||
StandardOutput=journal+console
|
||||
StandardError=journal+console
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
636
overlay/usr/local/sbin/pve-maas-init
Executable file
636
overlay/usr/local/sbin/pve-maas-init
Executable file
@@ -0,0 +1,636 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# pve-maas-init - MAAS ile deploy edilen Proxmox VE dugumunu ilk acilista yapilandirir.
|
||||
#
|
||||
# Asamalar (her biri /var/lib/pve-maas/<asama>.done ile bir kez calisir):
|
||||
# hosts - hostname/FQDN'i yonetim IP'sine bagla (pvecm bunu ister)
|
||||
# identity - dugume ozel kimlikleri yeniden uret (iSCSI IQN vb.)
|
||||
# rootpw - root@pam parolasini ayarla (web arayuzu ve kume katilimi icin)
|
||||
# network - MAAS'in verdigi arayuzu vmbr0 koprusune donustur
|
||||
# cluster - kume olustur veya mevcut kumeye katil
|
||||
# storage - bos alanda LVM-thin havuzu olustur (local-lvm)
|
||||
#
|
||||
# Yapilandirma: /etc/pve-maas/pve-maas.conf ve /etc/pve-maas/conf.d/*.conf
|
||||
# (conf.d icerigi MAAS deploy sirasinda cloud-init write_files ile yazilir)
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
CONF_DIR=/etc/pve-maas
|
||||
STATE_DIR=/var/lib/pve-maas
|
||||
LOG_TAG=pve-maas-init
|
||||
|
||||
mkdir -p "$STATE_DIR" "$CONF_DIR/conf.d"
|
||||
|
||||
log() { echo "[$LOG_TAG] $*"; logger -t "$LOG_TAG" -- "$*" 2>/dev/null || true; }
|
||||
warn() { echo "[$LOG_TAG] UYARI: $*" >&2; logger -t "$LOG_TAG" -p user.warning -- "UYARI: $*" 2>/dev/null || true; }
|
||||
die() { echo "[$LOG_TAG] HATA: $*" >&2; logger -t "$LOG_TAG" -p user.err -- "HATA: $*" 2>/dev/null || true; exit 1; }
|
||||
|
||||
done_flag() { echo "$STATE_DIR/$1.done"; }
|
||||
is_done() { [ -e "$(done_flag "$1")" ]; }
|
||||
mark_done() { date -Is > "$(done_flag "$1")"; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yapilandirmayi yukle
|
||||
# ---------------------------------------------------------------------------
|
||||
load_config() {
|
||||
# shellcheck disable=SC1091
|
||||
[ -r "$CONF_DIR/pve-maas.conf" ] && . "$CONF_DIR/pve-maas.conf"
|
||||
local f
|
||||
for f in "$CONF_DIR"/conf.d/*.conf; do
|
||||
[ -r "$f" ] || continue
|
||||
log "yapilandirma yukleniyor: $f"
|
||||
# shellcheck disable=SC1090
|
||||
. "$f"
|
||||
done
|
||||
|
||||
# Varsayilanlar
|
||||
PVE_ENABLED="${PVE_ENABLED:-true}"
|
||||
PVE_FQDN="${PVE_FQDN:-}"
|
||||
|
||||
PVE_NET_MANAGE="${PVE_NET_MANAGE:-true}"
|
||||
PVE_NET_BRIDGE="${PVE_NET_BRIDGE:-vmbr0}"
|
||||
PVE_NET_UPLINK="${PVE_NET_UPLINK:-}"
|
||||
PVE_NET_MODE="${PVE_NET_MODE:-auto}" # auto | static | dhcp
|
||||
PVE_NET_APPLY="${PVE_NET_APPLY:-reboot}" # reboot | reload | none
|
||||
PVE_NET_VLAN_AWARE="${PVE_NET_VLAN_AWARE:-false}"
|
||||
PVE_NET_EXTRA="${PVE_NET_EXTRA:-}"
|
||||
|
||||
PVE_ROOT_PASSWORD="${PVE_ROOT_PASSWORD:-}"
|
||||
PVE_ROOT_PASSWORD_HASH="${PVE_ROOT_PASSWORD_HASH:-}"
|
||||
|
||||
PVE_CLUSTER_MODE="${PVE_CLUSTER_MODE:-none}" # none | create | join
|
||||
PVE_CLUSTER_NAME="${PVE_CLUSTER_NAME:-}"
|
||||
PVE_CLUSTER_PEER="${PVE_CLUSTER_PEER:-}"
|
||||
PVE_CLUSTER_PEER_PASSWORD="${PVE_CLUSTER_PEER_PASSWORD:-}"
|
||||
PVE_CLUSTER_PEER_PASSWORD_FILE="${PVE_CLUSTER_PEER_PASSWORD_FILE:-}"
|
||||
PVE_CLUSTER_FINGERPRINT="${PVE_CLUSTER_FINGERPRINT:-}"
|
||||
PVE_CLUSTER_FINGERPRINT_DISCOVER="${PVE_CLUSTER_FINGERPRINT_DISCOVER:-true}"
|
||||
PVE_CLUSTER_LINK0="${PVE_CLUSTER_LINK0:-}"
|
||||
PVE_CLUSTER_LINK1="${PVE_CLUSTER_LINK1:-}"
|
||||
PVE_CLUSTER_NODEID="${PVE_CLUSTER_NODEID:-}"
|
||||
PVE_CLUSTER_VOTES="${PVE_CLUSTER_VOTES:-}"
|
||||
PVE_CLUSTER_WAIT="${PVE_CLUSTER_WAIT:-900}"
|
||||
PVE_CLUSTER_RETRIES="${PVE_CLUSTER_RETRIES:-5}"
|
||||
PVE_CLUSTER_WIPE_SECRETS="${PVE_CLUSTER_WIPE_SECRETS:-true}"
|
||||
|
||||
PVE_THINPOOL="${PVE_THINPOOL:-auto}" # auto | off | <vg-adi>
|
||||
PVE_THINPOOL_NAME="${PVE_THINPOOL_NAME:-data}"
|
||||
PVE_THINPOOL_STORAGE="${PVE_THINPOOL_STORAGE:-local-lvm}"
|
||||
PVE_THINPOOL_MIN_GB="${PVE_THINPOOL_MIN_GB:-16}"
|
||||
PVE_THINPOOL_DISK="${PVE_THINPOOL_DISK:-}"
|
||||
PVE_THINPOOL_VG="${PVE_THINPOOL_VG:-pve}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yardimcilar
|
||||
# ---------------------------------------------------------------------------
|
||||
primary_iface() {
|
||||
if [ -n "$PVE_NET_UPLINK" ]; then echo "$PVE_NET_UPLINK"; return; fi
|
||||
ip -4 -o route show default 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="dev") {print $(i+1); exit}}'
|
||||
}
|
||||
|
||||
primary_ip() {
|
||||
ip -4 -o route get 1.1.1.1 2>/dev/null \
|
||||
| awk '{for(i=1;i<=NF;i++) if($i=="src") {print $(i+1); exit}}'
|
||||
}
|
||||
|
||||
node_fqdn() {
|
||||
local host fqdn dom
|
||||
host="$(hostname -s)"
|
||||
if [ -n "$PVE_FQDN" ]; then echo "$PVE_FQDN"; return; fi
|
||||
fqdn="$(hostname -f 2>/dev/null || true)"
|
||||
if [ -n "$fqdn" ] && [ "$fqdn" != "$host" ]; then echo "$fqdn"; return; fi
|
||||
dom="$(awk '/^(search|domain)[[:space:]]/{print $2; exit}' /etc/resolv.conf 2>/dev/null || true)"
|
||||
if [ -n "$dom" ]; then echo "${host}.${dom}"; else echo "$host"; fi
|
||||
}
|
||||
|
||||
wait_for_cloud_init() {
|
||||
command -v cloud-init >/dev/null 2>&1 || return 0
|
||||
log "cloud-init'in bitmesi bekleniyor"
|
||||
# Unit'te After=cloud-final.service kullanamiyoruz (siralama dongusu),
|
||||
# bu yuzden beklemeyi burada yapiyoruz. Hata donmesi normal olabilir.
|
||||
timeout 900 cloud-init status --wait >/dev/null 2>&1
|
||||
log "cloud-init durumu: $(cloud-init status 2>/dev/null | head -1)"
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_network() {
|
||||
local i ip
|
||||
for ((i = 0; i < 120; i += 5)); do
|
||||
ip="$(primary_ip)"
|
||||
[ -n "$ip" ] && { log "yonetim IP'si: ${ip}"; return 0; }
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_pmxcfs() {
|
||||
local i
|
||||
for i in $(seq 1 60); do
|
||||
[ -e /etc/pve/local ] && return 0
|
||||
sleep 2
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: hosts
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_hosts() {
|
||||
is_done hosts && return 0
|
||||
local host fqdn ip
|
||||
host="$(hostname -s)"
|
||||
fqdn="$(node_fqdn)"
|
||||
ip="$(primary_ip)"
|
||||
[ -n "$ip" ] || { warn "yonetim IP'si bulunamadi, /etc/hosts atlaniyor"; return 0; }
|
||||
|
||||
log "/etc/hosts guncelleniyor: ${ip} ${fqdn} ${host}"
|
||||
cp -a /etc/hosts "$STATE_DIR/hosts.orig" 2>/dev/null || true
|
||||
|
||||
local keep
|
||||
keep="$(grep -vE "^[[:space:]]*(127\.0\.0\.1|127\.0\.1\.1|::1|ff02::[12])[[:space:]]" /etc/hosts 2>/dev/null \
|
||||
| grep -vE "[[:space:]]${host}([[:space:]]|\$)" || true)"
|
||||
|
||||
{
|
||||
echo "127.0.0.1 localhost.localdomain localhost"
|
||||
echo "${ip} ${fqdn} ${host}"
|
||||
[ -n "$keep" ] && echo "$keep"
|
||||
echo
|
||||
echo "# IPv6"
|
||||
echo "::1 localhost ip6-localhost ip6-loopback"
|
||||
echo "ff02::1 ip6-allnodes"
|
||||
echo "ff02::2 ip6-allrouters"
|
||||
} > /etc/hosts
|
||||
|
||||
if command -v postconf >/dev/null 2>&1 && [ -f /etc/postfix/main.cf ]; then
|
||||
postconf -e "myhostname = ${fqdn}" >/dev/null 2>&1 || true
|
||||
systemctl try-restart postfix >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# cloud-init manage_etc_hosts=true ise her acilista /etc/hosts'u
|
||||
# 127.0.1.1 satiriyla geri yazar ve duzeltmemizi bozar.
|
||||
cat > /etc/cloud/cloud.cfg.d/99-pve-maas-hosts.cfg <<'EOF'
|
||||
# /etc/hosts artik pve-maas-init tarafindan yonetiliyor.
|
||||
manage_etc_hosts: false
|
||||
EOF
|
||||
|
||||
hostname -f >/dev/null 2>&1 || warn "hostname -f hala cozulmuyor"
|
||||
|
||||
# pmxcfs dugum kimligini ve sertifikalari dogru hostname/IP ile yenile.
|
||||
if systemctl list-unit-files pve-cluster.service >/dev/null 2>&1; then
|
||||
log "pve servisleri yeniden baslatiliyor (dogru hostname ile)"
|
||||
systemctl restart pve-cluster >/dev/null 2>&1 || true
|
||||
systemctl restart pvedaemon pveproxy pvestatd >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
mark_done hosts
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: identity - dugume ozel kimlikler
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_identity() {
|
||||
is_done identity && return 0
|
||||
if [ ! -s /etc/iscsi/initiatorname.iscsi ] && command -v iscsi-iname >/dev/null 2>&1; then
|
||||
log "iSCSI initiator adi uretiliyor"
|
||||
mkdir -p /etc/iscsi
|
||||
echo "InitiatorName=$(iscsi-iname)" > /etc/iscsi/initiatorname.iscsi
|
||||
chmod 0600 /etc/iscsi/initiatorname.iscsi
|
||||
systemctl try-restart iscsid open-iscsi >/dev/null 2>&1 || true
|
||||
fi
|
||||
mark_done identity
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: rootpw
|
||||
# ---------------------------------------------------------------------------
|
||||
stage_rootpw() {
|
||||
is_done rootpw && return 0
|
||||
if [ -n "$PVE_ROOT_PASSWORD_HASH" ]; then
|
||||
log "root parolasi (hash) ayarlaniyor"
|
||||
usermod -p "$PVE_ROOT_PASSWORD_HASH" root
|
||||
elif [ -n "$PVE_ROOT_PASSWORD" ]; then
|
||||
log "root parolasi ayarlaniyor"
|
||||
echo "root:${PVE_ROOT_PASSWORD}" | chpasswd
|
||||
else
|
||||
warn "PVE_ROOT_PASSWORD tanimli degil; root@pam ile web arayuzune girilemez"
|
||||
warn "ve bu dugum baska bir dugumun kumeye katilmasi icin hedef olamaz."
|
||||
mark_done rootpw
|
||||
return 0
|
||||
fi
|
||||
mark_done rootpw
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: network - MAAS arayuzunu vmbr0'a cevir
|
||||
# ---------------------------------------------------------------------------
|
||||
detect_net_mode() {
|
||||
[ "$PVE_NET_MODE" != "auto" ] && { echo "$PVE_NET_MODE"; return; }
|
||||
if grep -rqsE '^\s*(dhcp4|dhcp6)\s*:\s*(true|yes)' /etc/netplan/ 2>/dev/null; then
|
||||
echo dhcp; return
|
||||
fi
|
||||
if grep -rqsE '^\s*iface\s+\S+\s+inet6?\s+dhcp' /etc/network/interfaces.d/ 2>/dev/null; then
|
||||
echo dhcp; return
|
||||
fi
|
||||
echo static
|
||||
}
|
||||
|
||||
neutralize_foreign_netconf() {
|
||||
log "MAAS/cloud-init ag yapilandirmasi devre disi birakiliyor"
|
||||
printf 'network: {config: disabled}\n' > /etc/cloud/cloud.cfg.d/99-pve-maas-disable-network.cfg
|
||||
|
||||
local d
|
||||
d=/etc/network/interfaces.d/disabled-by-pve-maas
|
||||
mkdir -p "$d"
|
||||
find /etc/network/interfaces.d -maxdepth 1 -type f -print0 2>/dev/null \
|
||||
| xargs -0 -r -I{} mv {} "$d/"
|
||||
|
||||
if compgen -G "/etc/netplan/*.yaml" >/dev/null || compgen -G "/etc/netplan/*.yml" >/dev/null; then
|
||||
mkdir -p /etc/netplan/disabled-by-pve-maas
|
||||
mv /etc/netplan/*.y*ml /etc/netplan/disabled-by-pve-maas/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if systemctl is-enabled systemd-networkd >/dev/null 2>&1; then
|
||||
systemctl disable --now systemd-networkd systemd-networkd.socket >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Proxmox statik /etc/resolv.conf bekler.
|
||||
if [ -L /etc/resolv.conf ]; then
|
||||
local ns search
|
||||
ns="$(resolvectl dns 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9a-fA-F:.]+$' | sort -u || true)"
|
||||
[ -z "$ns" ] && ns="$(awk '/^nameserver/{print $2}' /etc/resolv.conf | sort -u || true)"
|
||||
search="$(awk '/^(search|domain)[[:space:]]/{$1=""; print substr($0,2); exit}' /etc/resolv.conf || true)"
|
||||
if [ -n "$ns" ]; then
|
||||
rm -f /etc/resolv.conf
|
||||
{
|
||||
[ -n "$search" ] && echo "search ${search}"
|
||||
echo "$ns" | while read -r s; do [ -n "$s" ] && echo "nameserver $s"; done
|
||||
} > /etc/resolv.conf
|
||||
systemctl disable --now systemd-resolved >/dev/null 2>&1 || true
|
||||
else
|
||||
warn "nameserver bulunamadi, /etc/resolv.conf oldugu gibi birakiliyor"
|
||||
fi
|
||||
fi
|
||||
|
||||
systemctl enable networking.service >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
stage_network() {
|
||||
is_done network && return 0
|
||||
[ "$PVE_NET_MANAGE" = "true" ] || { log "ag yonetimi kapali (PVE_NET_MANAGE=false)"; mark_done network; return 0; }
|
||||
|
||||
local br uplink
|
||||
br="$PVE_NET_BRIDGE"
|
||||
uplink="$(primary_iface)"
|
||||
|
||||
if ip link show "$br" >/dev/null 2>&1 && [ "$uplink" = "$br" ]; then
|
||||
log "$br zaten mevcut ve varsayilan rotayi tasiyor, ag donusumu atlaniyor"
|
||||
mark_done network
|
||||
return 0
|
||||
fi
|
||||
[ -n "$uplink" ] || { warn "uplink arayuzu bulunamadi, ag donusumu atlaniyor"; mark_done network; return 0; }
|
||||
|
||||
local mode cidr gw cidr6 gw6
|
||||
mode="$(detect_net_mode)"
|
||||
cidr="$(ip -4 -o addr show dev "$uplink" scope global 2>/dev/null | awk '{print $4; exit}')"
|
||||
gw="$(ip -4 -o route show default dev "$uplink" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="via") {print $(i+1); exit}}')"
|
||||
cidr6="$(ip -6 -o addr show dev "$uplink" scope global -deprecated 2>/dev/null | awk '{print $4; exit}')"
|
||||
gw6="$(ip -6 -o route show default dev "$uplink" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="via") {print $(i+1); exit}}')"
|
||||
|
||||
if [ "$mode" = "static" ] && [ -z "$cidr" ]; then
|
||||
warn "$uplink uzerinde IPv4 adresi yok, DHCP'ye dusuluyor"
|
||||
mode=dhcp
|
||||
fi
|
||||
|
||||
log "ag donusumu: ${uplink} -> ${br} (mod=${mode} adres=${cidr:-dhcp} gw=${gw:-yok})"
|
||||
cp -a /etc/network/interfaces "$STATE_DIR/interfaces.orig" 2>/dev/null || true
|
||||
neutralize_foreign_netconf
|
||||
|
||||
{
|
||||
echo "# /etc/network/interfaces - pve-maas-init tarafindan olusturuldu"
|
||||
echo "# Kaynak yedegi: $STATE_DIR/interfaces.orig"
|
||||
echo
|
||||
echo "auto lo"
|
||||
echo "iface lo inet loopback"
|
||||
echo
|
||||
echo "iface ${uplink} inet manual"
|
||||
echo
|
||||
echo "auto ${br}"
|
||||
if [ "$mode" = "dhcp" ]; then
|
||||
echo "iface ${br} inet dhcp"
|
||||
else
|
||||
echo "iface ${br} inet static"
|
||||
echo " address ${cidr}"
|
||||
[ -n "$gw" ] && echo " gateway ${gw}"
|
||||
fi
|
||||
echo " bridge-ports ${uplink}"
|
||||
echo " bridge-stp off"
|
||||
echo " bridge-fd 0"
|
||||
[ "$PVE_NET_VLAN_AWARE" = "true" ] && {
|
||||
echo " bridge-vlan-aware yes"
|
||||
echo " bridge-vids 2-4094"
|
||||
}
|
||||
if [ -n "$cidr6" ]; then
|
||||
echo
|
||||
echo "iface ${br} inet6 static"
|
||||
echo " address ${cidr6}"
|
||||
[ -n "$gw6" ] && echo " gateway ${gw6}"
|
||||
fi
|
||||
if [ -n "$PVE_NET_EXTRA" ]; then
|
||||
echo
|
||||
echo "# PVE_NET_EXTRA"
|
||||
printf '%s\n' "$PVE_NET_EXTRA"
|
||||
fi
|
||||
echo
|
||||
echo "source /etc/network/interfaces.d/*"
|
||||
} > /etc/network/interfaces
|
||||
|
||||
# Proxmox'un pvenetcommit.service'i acilista /etc/network/interfaces.new
|
||||
# dosyasini interfaces uzerine tasiyor. Kalmis bir .new dosyasi yeni
|
||||
# yazdigimiz yapilandirmayi yeniden baslatmada ezer.
|
||||
rm -f /etc/network/interfaces.new
|
||||
|
||||
mark_done network
|
||||
|
||||
case "$PVE_NET_APPLY" in
|
||||
reload)
|
||||
log "ifreload -a ile uygulaniyor"
|
||||
ip -4 addr flush dev "$uplink" 2>/dev/null || true
|
||||
ip -6 addr flush dev "$uplink" scope global 2>/dev/null || true
|
||||
if ! ifreload -a; then
|
||||
warn "ifreload basarisiz, yedek rota ekleniyor"
|
||||
[ -n "$gw" ] && ip route replace default via "$gw" dev "$br" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
reboot)
|
||||
log "ag yapilandirmasi icin yeniden baslatiliyor; kalan asamalar sonraki acilista surecek"
|
||||
# Bu servis hala calisirken 'systemctl reboot' cagirmak systemd islem
|
||||
# kuyrugunda "transaction is destructive" hatasina yol acabilir; bu
|
||||
# yuzden reboot'u ayri bir gecici unit uzerinden tetikliyoruz.
|
||||
systemd-run --no-block --unit=pve-maas-reboot --on-active=5 \
|
||||
/bin/systemctl reboot \
|
||||
|| shutdown -r +1 "pve-maas-init: ag yapilandirmasi uygulanacak"
|
||||
exit 0
|
||||
;;
|
||||
none)
|
||||
log "PVE_NET_APPLY=none: yapilandirma yazildi, uygulanmadi"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: cluster
|
||||
# ---------------------------------------------------------------------------
|
||||
peer_fingerprint() {
|
||||
local peer="$1"
|
||||
openssl s_client -connect "${peer}:8006" -servername "${peer}" </dev/null 2>/dev/null \
|
||||
| openssl x509 -noout -fingerprint -sha256 2>/dev/null \
|
||||
| cut -d= -f2
|
||||
}
|
||||
|
||||
wait_for_peer() {
|
||||
local peer="$1" timeout="$2" i
|
||||
log "kume dugumu bekleniyor: ${peer}:8006 (en fazla ${timeout}s)"
|
||||
for ((i = 0; i < timeout; i += 5)); do
|
||||
if timeout 4 bash -c "exec 3<>/dev/tcp/${peer}/8006" 2>/dev/null; then
|
||||
log "${peer}:8006 erisilebilir"
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
cluster_password() {
|
||||
if [ -n "$PVE_CLUSTER_PEER_PASSWORD_FILE" ] && [ -r "$PVE_CLUSTER_PEER_PASSWORD_FILE" ]; then
|
||||
head -1 "$PVE_CLUSTER_PEER_PASSWORD_FILE"
|
||||
else
|
||||
printf '%s' "$PVE_CLUSTER_PEER_PASSWORD"
|
||||
fi
|
||||
}
|
||||
|
||||
wipe_cluster_secrets() {
|
||||
[ "$PVE_CLUSTER_WIPE_SECRETS" = "true" ] || return 0
|
||||
local f
|
||||
for f in "$CONF_DIR"/conf.d/*.conf; do
|
||||
[ -r "$f" ] || continue
|
||||
grep -q 'PVE_CLUSTER_PEER_PASSWORD\|PVE_ROOT_PASSWORD' "$f" || continue
|
||||
log "kimlik bilgileri temizleniyor: $f"
|
||||
sed -i -E "s/^([[:space:]]*(PVE_CLUSTER_PEER_PASSWORD|PVE_ROOT_PASSWORD)(_HASH)?=).*/\1'<silindi>'/" "$f"
|
||||
done
|
||||
if [ -n "$PVE_CLUSTER_PEER_PASSWORD_FILE" ] && [ -f "$PVE_CLUSTER_PEER_PASSWORD_FILE" ]; then
|
||||
shred -u "$PVE_CLUSTER_PEER_PASSWORD_FILE" 2>/dev/null \
|
||||
|| rm -f "$PVE_CLUSTER_PEER_PASSWORD_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_quorum() {
|
||||
local i
|
||||
for ((i = 0; i < 180; i += 5)); do
|
||||
if pvecm status 2>/dev/null | grep -qE '^Quorate:[[:space:]]+Yes'; then
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
stage_cluster() {
|
||||
is_done cluster && return 0
|
||||
[ "$PVE_CLUSTER_MODE" = "none" ] && { log "kume islemi yok (PVE_CLUSTER_MODE=none)"; mark_done cluster; return 0; }
|
||||
|
||||
wait_for_pmxcfs || { warn "/etc/pve baglanmadi, kume asamasi erteleniyor"; return 1; }
|
||||
|
||||
if [ -f /etc/pve/corosync.conf ]; then
|
||||
log "dugum zaten bir kumenin parcasi, kume asamasi atlaniyor"
|
||||
mark_done cluster
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -a opts=()
|
||||
[ -n "$PVE_CLUSTER_LINK0" ] && opts+=(--link0 "$PVE_CLUSTER_LINK0")
|
||||
[ -n "$PVE_CLUSTER_LINK1" ] && opts+=(--link1 "$PVE_CLUSTER_LINK1")
|
||||
[ -n "$PVE_CLUSTER_NODEID" ] && opts+=(--nodeid "$PVE_CLUSTER_NODEID")
|
||||
[ -n "$PVE_CLUSTER_VOTES" ] && opts+=(--votes "$PVE_CLUSTER_VOTES")
|
||||
|
||||
case "$PVE_CLUSTER_MODE" in
|
||||
create)
|
||||
[ -n "$PVE_CLUSTER_NAME" ] || { warn "PVE_CLUSTER_NAME bos, kume olusturulamiyor"; return 1; }
|
||||
log "kume olusturuluyor: ${PVE_CLUSTER_NAME}"
|
||||
if pvecm create "$PVE_CLUSTER_NAME" "${opts[@]}"; then
|
||||
wait_for_quorum && log "kume olusturuldu ve quorate"
|
||||
mark_done cluster
|
||||
else
|
||||
warn "pvecm create basarisiz"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
join)
|
||||
[ -n "$PVE_CLUSTER_PEER" ] || { warn "PVE_CLUSTER_PEER bos, katilim yapilamiyor"; return 1; }
|
||||
local pw fp
|
||||
pw="$(cluster_password)"
|
||||
[ -n "$pw" ] || { warn "kume dugumunun root parolasi verilmedi (PVE_CLUSTER_PEER_PASSWORD)"; return 1; }
|
||||
|
||||
wait_for_peer "$PVE_CLUSTER_PEER" "$PVE_CLUSTER_WAIT" \
|
||||
|| { warn "kume dugumu ${PVE_CLUSTER_PEER} zaman asimina ugradi"; return 1; }
|
||||
|
||||
fp="$PVE_CLUSTER_FINGERPRINT"
|
||||
if [ -z "$fp" ] && [ "$PVE_CLUSTER_FINGERPRINT_DISCOVER" = "true" ]; then
|
||||
fp="$(peer_fingerprint "$PVE_CLUSTER_PEER")"
|
||||
warn "parmak izi otomatik alindi (TOFU, dogrulanmadi): ${fp}"
|
||||
fi
|
||||
[ -n "$fp" ] || { warn "kume dugumunun sertifika parmak izi alinamadi"; return 1; }
|
||||
|
||||
local i rc=1
|
||||
for ((i = 1; i <= PVE_CLUSTER_RETRIES; i++)); do
|
||||
log "kumeye katiliniyor (deneme ${i}/${PVE_CLUSTER_RETRIES}): ${PVE_CLUSTER_PEER}"
|
||||
# pvecm add etkilesimli parola sorar; API ucu ile etkilesimsiz calisiyoruz.
|
||||
if pvesh create /cluster/config/join \
|
||||
--hostname "$PVE_CLUSTER_PEER" \
|
||||
--password "$pw" \
|
||||
--fingerprint "$fp" \
|
||||
"${opts[@]}"; then
|
||||
rc=0
|
||||
break
|
||||
fi
|
||||
warn "katilim basarisiz, 30s sonra tekrar denenecek"
|
||||
sleep 30
|
||||
done
|
||||
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
warn "kumeye katilim ${PVE_CLUSTER_RETRIES} denemede basarisiz"
|
||||
return 1
|
||||
fi
|
||||
|
||||
systemctl restart pve-cluster pvedaemon pveproxy pvestatd >/dev/null 2>&1 || true
|
||||
if wait_for_quorum; then
|
||||
log "kumeye katilim tamamlandi ve quorate"
|
||||
else
|
||||
warn "katilim yapildi ama quorum saglanamadi, kume durumunu kontrol edin"
|
||||
fi
|
||||
mark_done cluster
|
||||
wipe_cluster_secrets
|
||||
;;
|
||||
*)
|
||||
warn "gecersiz PVE_CLUSTER_MODE=${PVE_CLUSTER_MODE}"
|
||||
mark_done cluster
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Asama: storage - LVM-thin havuzu
|
||||
# ---------------------------------------------------------------------------
|
||||
pick_vg() {
|
||||
if [ "$PVE_THINPOOL" != "auto" ] && [ "$PVE_THINPOOL" != "off" ]; then
|
||||
echo "$PVE_THINPOOL"; return
|
||||
fi
|
||||
vgs --noheadings --nosuffix --units b -o vg_name,vg_free 2>/dev/null \
|
||||
| awk '{gsub(/^ +/,""); print $2, $1}' | sort -rn | head -1 | awk '{print $2}'
|
||||
}
|
||||
|
||||
stage_storage() {
|
||||
is_done storage && return 0
|
||||
[ "$PVE_THINPOOL" = "off" ] && { log "thin havuz kapali"; mark_done storage; return 0; }
|
||||
|
||||
wait_for_pmxcfs || { warn "/etc/pve baglanmadi, storage asamasi erteleniyor"; return 1; }
|
||||
command -v lvs >/dev/null 2>&1 || { warn "lvm2 yok"; mark_done storage; return 0; }
|
||||
|
||||
local vg="$PVE_THINPOOL_VG"
|
||||
|
||||
if [ -n "$PVE_THINPOOL_DISK" ]; then
|
||||
if [ ! -b "$PVE_THINPOOL_DISK" ]; then
|
||||
warn "PVE_THINPOOL_DISK=${PVE_THINPOOL_DISK} blok aygiti degil"
|
||||
mark_done storage; return 0
|
||||
fi
|
||||
if ! pvs "$PVE_THINPOOL_DISK" >/dev/null 2>&1; then
|
||||
log "PV/VG olusturuluyor: ${PVE_THINPOOL_DISK} -> ${vg}"
|
||||
pvcreate -ff -y "$PVE_THINPOOL_DISK" || { warn "pvcreate basarisiz"; mark_done storage; return 0; }
|
||||
vgcreate "$vg" "$PVE_THINPOOL_DISK" || { warn "vgcreate basarisiz"; mark_done storage; return 0; }
|
||||
fi
|
||||
else
|
||||
vg="$(pick_vg)"
|
||||
fi
|
||||
|
||||
[ -n "$vg" ] || { log "uygun VG bulunamadi, thin havuz atlaniyor"; mark_done storage; return 0; }
|
||||
|
||||
local pool="$PVE_THINPOOL_NAME"
|
||||
if lvs "${vg}/${pool}" >/dev/null 2>&1; then
|
||||
log "${vg}/${pool} zaten var"
|
||||
else
|
||||
local free_b free_g
|
||||
free_b="$(vgs --noheadings --nosuffix --units b -o vg_free "$vg" 2>/dev/null | tr -d ' ')"
|
||||
free_g=$(( ${free_b:-0} / 1024 / 1024 / 1024 ))
|
||||
if [ "$free_g" -lt "$PVE_THINPOOL_MIN_GB" ]; then
|
||||
log "${vg} icinde yalnizca ${free_g}GiB bos alan var (gereken ${PVE_THINPOOL_MIN_GB}GiB), thin havuz atlaniyor"
|
||||
mark_done storage
|
||||
return 0
|
||||
fi
|
||||
log "thin havuz olusturuluyor: ${vg}/${pool} (~${free_g}GiB)"
|
||||
if ! lvcreate --type thin-pool -l 95%FREE -n "$pool" "$vg"; then
|
||||
warn "lvcreate basarisiz"
|
||||
mark_done storage
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local storage="$PVE_THINPOOL_STORAGE" node
|
||||
node="$(hostname -s)"
|
||||
if pvesm status --storage "$storage" >/dev/null 2>&1; then
|
||||
log "storage '${storage}' zaten tanimli"
|
||||
local nodes
|
||||
nodes="$(awk -v s="$storage" '$1=="lvmthin:" && $2==s {f=1; next} /^[a-z]+:/{f=0} f && $1=="nodes"{print $2}' /etc/pve/storage.cfg 2>/dev/null)"
|
||||
if [ -n "$nodes" ] && ! echo ",$nodes," | grep -q ",${node},"; then
|
||||
log "'${storage}' dugum listesine ${node} ekleniyor"
|
||||
pvesm set "$storage" --nodes "${nodes},${node}" || warn "pvesm set basarisiz"
|
||||
fi
|
||||
else
|
||||
log "storage tanimlaniyor: ${storage} (lvmthin ${vg}/${pool})"
|
||||
local -a sopts=(--vgname "$vg" --thinpool "$pool" --content images,rootdir)
|
||||
[ -f /etc/pve/corosync.conf ] && sopts+=(--nodes "$node")
|
||||
pvesm add lvmthin "$storage" "${sopts[@]}" || warn "pvesm add basarisiz"
|
||||
fi
|
||||
|
||||
mark_done storage
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
load_config
|
||||
|
||||
if [ "$PVE_ENABLED" != "true" ]; then
|
||||
log "PVE_ENABLED=false, hicbir sey yapilmiyor"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "baslatiliyor (hostname=$(hostname -s))"
|
||||
|
||||
wait_for_cloud_init
|
||||
load_config # cloud-init conf.d dosyalarini yeni yazmis olabilir
|
||||
|
||||
if ! wait_for_network; then
|
||||
warn "yonetim IP'si bulunamadi; sonraki acilista tekrar denenecek"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local failed=0
|
||||
stage_hosts || failed=1
|
||||
stage_identity || failed=1
|
||||
stage_rootpw || failed=1
|
||||
stage_network || failed=1
|
||||
stage_cluster || failed=1
|
||||
stage_storage || failed=1
|
||||
|
||||
if [ "$failed" -eq 0 ] \
|
||||
&& is_done hosts && is_done identity && is_done rootpw \
|
||||
&& is_done network && is_done cluster && is_done storage; then
|
||||
date -Is > "$STATE_DIR/complete"
|
||||
log "tum asamalar tamamlandi"
|
||||
else
|
||||
warn "bazi asamalar tamamlanmadi; servis sonraki acilista tekrar denenecek"
|
||||
warn "ayrintilar: journalctl -u pve-maas-init"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user