mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-06 05:52:19 +08:00
支持部署KVM XFCE桌面系统
This commit is contained in:
@@ -28,6 +28,7 @@ type ImageInfo struct {
|
||||
Downloading bool `json:"downloading"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
ManualPath string `json:"manual_path,omitempty"`
|
||||
Desktop string `json:"desktop,omitempty"`
|
||||
}
|
||||
|
||||
var imageDownloadsMu sync.Mutex
|
||||
@@ -140,6 +141,7 @@ func HandleImages(w http.ResponseWriter, r *http.Request) {
|
||||
Downloading: downloading,
|
||||
SizeBytes: size,
|
||||
ManualPath: manualPath,
|
||||
Desktop: t.Desktop,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -336,7 +338,7 @@ func HandleEnabledImages(w http.ResponseWriter, r *http.Request) {
|
||||
if downloaded, _ := kvm.ImageDownloadedInfo(t.ID); enabledSet[t.ID] && downloaded {
|
||||
result = append(result, map[string]string{
|
||||
"id": t.ID, "name": t.Name, "distro": t.Distro, "release": t.Release, "arch": t.Arch,
|
||||
"description": t.Description, "type": config.VirtualizationKVM,
|
||||
"description": t.Description, "type": config.VirtualizationKVM, "desktop": t.Desktop,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,13 +371,21 @@ func (m *Manager) defineContainer(id int, vmName string, cfg lxc.ContainerConfig
|
||||
}
|
||||
xml = windowsDomainXML(vmName, int(cfg.VCPU), cfg.RAMMB, diskPath, ImagePath(image.ID), unattendPath, mac, cfg.IOSpeedMBps, cfg.NetworkBWMbps)
|
||||
} else {
|
||||
if image.Desktop != "" {
|
||||
if cfg.RAMMB < 2048 {
|
||||
cfg.RAMMB = 2048
|
||||
}
|
||||
if cfg.DiskGB < 20 {
|
||||
cfg.DiskGB = 20
|
||||
}
|
||||
}
|
||||
if err := createOverlayDisk(ImagePath(image.ID), diskPath, cfg.DiskGB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := createSeedISO(seedPath, vmName, cfg.Name, sshPassword, mac, ipv6); err != nil {
|
||||
if err := createSeedISO(seedPath, vmName, cfg.Name, sshPassword, mac, ipv6, *image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xml = domainXML(vmName, int(cfg.VCPU), cfg.RAMMB, diskPath, seedPath, mac, cfg.IOSpeedMBps, cfg.NetworkBWMbps)
|
||||
xml = domainXML(vmName, int(cfg.VCPU), cfg.RAMMB, diskPath, seedPath, mac, cfg.IOSpeedMBps, cfg.NetworkBWMbps, image.Desktop != "")
|
||||
}
|
||||
xmlPath := filepath.Join(m.instanceDir(vmName), "domain.xml")
|
||||
if err := os.WriteFile(xmlPath, []byte(xml), 0644); err != nil {
|
||||
@@ -706,7 +714,7 @@ func (m *Manager) ApplyContainerLimits(c *config.Container) error {
|
||||
xml = windowsDomainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, winISO, unattendISO, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps)
|
||||
} else {
|
||||
seedPath := filepath.Join(m.instanceDir(c.VirshName()), "seed.iso")
|
||||
xml = domainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, seedPath, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps)
|
||||
xml = domainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, seedPath, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps, isKVMDesktopTemplate(c.Template))
|
||||
}
|
||||
xmlPath := filepath.Join(m.instanceDir(c.VirshName()), "domain.xml")
|
||||
if err := os.WriteFile(xmlPath, []byte(xml), 0644); err != nil {
|
||||
@@ -731,7 +739,7 @@ func (m *Manager) ensureDomainDefinition(c *config.Container) error {
|
||||
xml = windowsDomainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, winISO, unattendISO, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps)
|
||||
} else {
|
||||
seedPath := filepath.Join(m.instanceDir(c.VirshName()), "seed.iso")
|
||||
xml = domainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, seedPath, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps)
|
||||
xml = domainXML(c.VirshName(), int(c.VCPU), c.RAMMB, c.DiskImage, seedPath, c.MACAddress, c.IOSpeedMBps, c.NetworkBWMbps, isKVMDesktopTemplate(c.Template))
|
||||
}
|
||||
if err := os.WriteFile(xmlPath, []byte(xml), 0644); err != nil {
|
||||
return err
|
||||
@@ -1668,8 +1676,11 @@ func shellQuoteWindows(value string) string {
|
||||
return `"` + strings.ReplaceAll(value, `"`, `\"`) + `"`
|
||||
}
|
||||
|
||||
func createSeedISO(seedPath, instanceID, hostname, password, mac, ipv6 string) error {
|
||||
func createSeedISO(seedPath, instanceID, hostname, password, mac, ipv6 string, image Image) error {
|
||||
guestSetup := kvmSSHSetupScript(password)
|
||||
if desktopSetup := kvmDesktopSetupScript(image); desktopSetup != "" {
|
||||
guestSetup += "\n" + desktopSetup
|
||||
}
|
||||
if strings.TrimSpace(ipv6) != "" {
|
||||
guestSetup += "\n" + kvmIPv6SetupScript(ipv6)
|
||||
}
|
||||
@@ -1743,7 +1754,12 @@ func indentScript(script string, spaces int) string {
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func domainXML(name string, vcpu int, ramMB int, diskPath, seedPath, mac string, ioSpeedMBps int, networkBWMbps int) string {
|
||||
func isKVMDesktopTemplate(templateID string) bool {
|
||||
image := FindImage(templateID)
|
||||
return image != nil && image.Desktop != ""
|
||||
}
|
||||
|
||||
func domainXML(name string, vcpu int, ramMB int, diskPath, seedPath, mac string, ioSpeedMBps int, networkBWMbps int, desktop bool) string {
|
||||
if vcpu < 1 {
|
||||
vcpu = 1
|
||||
}
|
||||
@@ -1767,6 +1783,12 @@ func domainXML(name string, vcpu int, ramMB int, diskPath, seedPath, mac string,
|
||||
<outbound average='%d'/>
|
||||
</bandwidth>`, averageKiB, averageKiB)
|
||||
}
|
||||
video := "<video><model type='virtio'/></video>"
|
||||
input := ""
|
||||
if desktop {
|
||||
video = "<video><model type='qxl' ram='65536' vram='65536' heads='1' primary='yes'/></video>"
|
||||
input = "\n\t <input type='tablet' bus='usb'/>"
|
||||
}
|
||||
return fmt.Sprintf(`<domain type='kvm'>
|
||||
<name>%s</name>
|
||||
%s
|
||||
@@ -1810,10 +1832,10 @@ func domainXML(name string, vcpu int, ramMB int, diskPath, seedPath, mac string,
|
||||
<memballoon model='virtio'>
|
||||
<stats period='10'/>
|
||||
</memballoon>
|
||||
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'/>
|
||||
<video><model type='virtio'/></video>
|
||||
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'/>%s
|
||||
%s
|
||||
</devices>
|
||||
</domain>`, xmlEscape(name), domainUUIDXML(name), ramMB, ramMB, vcpu, vcpu, xmlEscape(diskPath), iotune, xmlEscape(seedPath), xmlEscape(mac), bandwidth)
|
||||
</domain>`, xmlEscape(name), domainUUIDXML(name), ramMB, ramMB, vcpu, vcpu, xmlEscape(diskPath), iotune, xmlEscape(seedPath), xmlEscape(mac), bandwidth, input, video)
|
||||
}
|
||||
|
||||
func windowsDomainXML(name string, vcpu int, ramMB int, diskPath, winISOPath, unattendISOPath, mac string, ioSpeedMBps int, networkBWMbps int) string {
|
||||
@@ -2263,6 +2285,64 @@ fi
|
||||
`
|
||||
}
|
||||
|
||||
func kvmDesktopSetupScript(image Image) string {
|
||||
if strings.ToLower(strings.TrimSpace(image.Desktop)) != "xfce" {
|
||||
return ""
|
||||
}
|
||||
packages := ""
|
||||
switch image.Distro {
|
||||
case "ubuntu":
|
||||
packages = "xubuntu-desktop"
|
||||
case "debian":
|
||||
packages = "task-xfce-desktop"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
return `if command -v apt-get >/dev/null 2>&1; then
|
||||
{
|
||||
exec >>/var/log/clicd-desktop-setup.log 2>&1
|
||||
echo "CLICD XFCE setup started at $(date -Is)"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export APT_LISTCHANGES_FRONTEND=none
|
||||
apt-get update || true
|
||||
apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold ` + packages + ` || apt-get install -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold xfce4 lightdm lightdm-gtk-greeter dbus-x11 xorg || true
|
||||
if command -v useradd >/dev/null 2>&1 && ! id clicd >/dev/null 2>&1; then
|
||||
useradd -m -s /bin/bash clicd || true
|
||||
fi
|
||||
if command -v chpasswd >/dev/null 2>&1 && id clicd >/dev/null 2>&1; then
|
||||
printf 'clicd:%s\n' "$ROOT_PASSWORD" | chpasswd || true
|
||||
fi
|
||||
usermod -aG sudo clicd >/dev/null 2>&1 || true
|
||||
usermod -aG autologin clicd >/dev/null 2>&1 || true
|
||||
if id clicd >/dev/null 2>&1; then
|
||||
printf 'startxfce4\n' >/home/clicd/.xsession || true
|
||||
chown clicd:clicd /home/clicd/.xsession >/dev/null 2>&1 || true
|
||||
fi
|
||||
mkdir -p /etc/lightdm/lightdm.conf.d
|
||||
cat >/etc/lightdm/lightdm.conf.d/50-clicd-autologin.conf <<'EOF'
|
||||
[Seat:*]
|
||||
autologin-user=clicd
|
||||
autologin-user-timeout=0
|
||||
user-session=xfce
|
||||
greeter-session=lightdm-gtk-greeter
|
||||
EOF
|
||||
if [ -x /usr/sbin/lightdm ]; then
|
||||
printf '/usr/sbin/lightdm\n' >/etc/X11/default-display-manager || true
|
||||
fi
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
systemctl set-default graphical.target >/dev/null 2>&1 || true
|
||||
systemctl enable display-manager.service >/dev/null 2>&1 || true
|
||||
systemctl enable lightdm.service >/dev/null 2>&1 || true
|
||||
systemctl restart lightdm.service >/dev/null 2>&1 || systemctl start lightdm.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
apt-get clean || true
|
||||
echo "CLICD XFCE setup finished at $(date -Is)"
|
||||
} || true
|
||||
fi
|
||||
`
|
||||
}
|
||||
|
||||
func qemuGuestPing(name string) error {
|
||||
out, err := exec.Command("virsh", "qemu-agent-command", name, `{"execute":"guest-ping"}`).CombinedOutput()
|
||||
if err != nil {
|
||||
|
||||
@@ -12,6 +12,7 @@ type Image struct {
|
||||
Arch string `json:"arch"`
|
||||
Description string `json:"description"`
|
||||
URL string `json:"url"`
|
||||
Desktop string `json:"desktop,omitempty"`
|
||||
}
|
||||
|
||||
func GetImages() []Image {
|
||||
@@ -22,6 +23,13 @@ func GetImages() []Image {
|
||||
Description: "Ubuntu 24.04 LTS cloud image for KVM",
|
||||
URL: "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img",
|
||||
},
|
||||
{
|
||||
ID: "kvm-ubuntu-noble-xfce", Name: "Ubuntu 24.04 XFCE KVM",
|
||||
Distro: "ubuntu", Release: "noble", Arch: "amd64",
|
||||
Description: "Ubuntu 24.04 LTS cloud image with XFCE desktop provisioned via cloud-init",
|
||||
URL: "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img",
|
||||
Desktop: "xfce",
|
||||
},
|
||||
{
|
||||
ID: "kvm-ubuntu-jammy", Name: "Ubuntu 22.04 KVM",
|
||||
Distro: "ubuntu", Release: "jammy", Arch: "amd64",
|
||||
@@ -34,6 +42,13 @@ func GetImages() []Image {
|
||||
Description: "Debian 12 generic cloud image for KVM",
|
||||
URL: "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2",
|
||||
},
|
||||
{
|
||||
ID: "kvm-debian-bookworm-xfce", Name: "Debian 12 XFCE KVM",
|
||||
Distro: "debian", Release: "bookworm", Arch: "amd64",
|
||||
Description: "Debian 12 generic cloud image with XFCE desktop provisioned via cloud-init",
|
||||
URL: "https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2",
|
||||
Desktop: "xfce",
|
||||
},
|
||||
{
|
||||
ID: "kvm-debian-bullseye", Name: "Debian 11 KVM",
|
||||
Distro: "debian", Release: "bullseye", Arch: "amd64",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
Reference in New Issue
Block a user