Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XenServer 8 and XCP-ng 8.x support #290

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Read more about how [custom images](https://maas.io/docs/how-to-customise-images
| Windows 2025 | Beta | >= 3.3 |
| Windows 10 | Beta | >= 3.3 |
| Windows 11 | Beta | >= 3.3 |
| XenServer 8 | Beta | >= 3.3 |
| XCP-ng 8.x | Beta | >= 3.3 |

### Maturity level

Expand Down
49 changes: 49 additions & 0 deletions xenserver8/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/make -f

include ../scripts/check.mk

PACKER ?= packer
PACKER_LOG ?= 0
ISO ?= XenServer8_2024-12-09.iso
TIMEOUT ?= 1h
ARCH ?= x86_64
HEADLESS ?= false

ifeq ($(wildcard /usr/share/OVMF/OVMF_CODE.fd),)
OVMF_SFX ?= _4M
else
OVMF_SFX ?=
endif

export PACKER_LOG

.PHONY: all clean

all: xenserver8-lvm.dd.gz

$(eval $(call check_packages_deps))

lint:
packer validate .
packer fmt -check -diff .

format:
packer fmt .

OVMF_VARS.fd: /usr/share/OVMF/OVMF_VARS${OVMF_SFX}.fd
cp -v $< ${ARCH}_VARS.fd

SIZE_VARS.fd:
truncate -s 2m ${ARCH}_VARS.fd

xenserver8-lvm.dd.gz: check-deps clean OVMF_VARS.fd SIZE_VARS.fd
${PACKER} init xenserver8.pkr.hcl && ${PACKER} build \
-var architecture=${ARCH} \
-var headless=${HEADLESS} \
-var ovmf_suffix=${OVMF_SFX} \
-var "xenserver8_iso_path=${ISO}" \
-var timeout=${TIMEOUT} \
xenserver8.pkr.hcl

clean:
${RM} -rf *.fd output-xenserver8 xenserver8-lvm.dd.gz
84 changes: 84 additions & 0 deletions xenserver8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# XenServer 8 Packer Template for MAAS

## Introduction

The Packer template in this directory creates a XenServer 8 AMD64 image for use with MAAS.

This template is also compatible with [XCP-ng](https://xcp-ng.org/) which is the Open Source equivalent.

## Prerequisites (to create the image)

* A machine running Ubuntu 22.04+ with the ability to run KVM virtual machines.
* qemu-utils, libnbd-bin, nbdkit and fuse2fs
* [Packer](https://www.packer.io/intro/getting-started/install.html), v1.11.0 or newer
* The [XenServer 8 ISO](https://www.xenserver.com/downloads)

## Requirements (to deploy the image)

* [MAAS](https://maas.io) 3.3+
* [Curtin](https://launchpad.net/curtin) 22.1+

## Customizing the Image

The deployment image may be customized by modifying the http/xenserver8.xml.pkrtpl.hcl answer file.
See the [XenServer Answer file reference](https://docs.xenserver.com/en-us/xenserver/8/install/advanced-install#create-an-answer-file-for-unattended-installation) for more information.

For XCP-ng, see the [Answer file page](https://docs.xcp-ng.org/appendix/answerfile/).

## Building an image

You can easily build the image using the Makefile:

```shell
make ISO=/PATH/TO/XenServer8_2024-12-09.iso
```

Alternatively you can manually run packer. Your current working directory must
be in packer-maas/xenserver8, where this file is located. Once in packer-maas/xenserver8
you can generate an image with:

```shell
packer init
PACKER_LOG=1 packer build -var 'xenserver8_iso_path=/PATH/TO/XenServer8_2024-12-09.iso'.
```

The installation process non-interactive. Note this image only supports UEFI boot mode.

## Network Device Name Compatibility Note

Both XenServer and XCP-ng ship with a custom Linux kernel 4.19 which uses the traditional
NIC naming schema. This requires commissioning and deployment using the following
kernel paramaters on target machines on MAAS:

```
net.ifnames=0 biosdevname=0
```

For additional hardware support details, refer to the [HCL Page](https://hcl.xenserver.com/).

### Makefile Parameters

#### HEADLESS

Defaults to true. Set to false in order to see the VM during the build process.

### ISO

The path to the installation ISO image for XenSever or XCP-ng.

#### TIMEOUT

The timeout to apply when building the image. The default value is set to 1h.

## Uploading an image to MAAS

```shell
maas $PROFILE boot-resources create \
name='custom/xenserver8' title='XenServer 8' \
architecture='amd64/generic' filetype='ddgz' \
base_image='rhel/8' content@=xenserver8-lvm.dd.gz
```

## Default Username

The default username is ```centos```.
90 changes: 90 additions & 0 deletions xenserver8/curtin/curtin-hooks
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
# curtin-hooks - Curtin installation hooks for XenServer 8
#
# Copyright (C) 2024 Canonical
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import os
import shutil
import platform

from curtin import distro, util
from curtin.config import load_command_config
from curtin.log import LOG
from curtin.paths import target_path
from curtin.util import load_command_environment, ChrootableTarget
from curtin.commands import curthooks

def run_hook_in_target(target, hook):
"""Look for "hook" in "target" and run in a chroot"""
target_hook = target_path(target, '/curtin/' + hook)
if os.path.isfile(target_hook):
LOG.debug("running %s" % target_hook)
with ChrootableTarget(target=target) as in_chroot:
in_chroot.subp(['/curtin/' + hook])
return True
return False

def curthook(cfg, target, state):
"""Configure network and bootloader"""
LOG.info('Running curtin builtin curthooks')
state_etcd = os.path.split(state['fstab'])[0]
machine = platform.machine()

distro_info = distro.get_distroinfo(target=target)
if not distro_info:
raise RuntimeError('Failed to determine target distro')
osfamily = distro_info.family
LOG.info('Configuring target system for distro: %s osfamily: %s',
distro_info.variant, osfamily)

sources = cfg.get('sources', {})
dd_image = len(util.get_dd_images(sources)) > 0

curthooks.disable_overlayroot(cfg, target)
curthooks.disable_update_initramfs(cfg, target, machine)

curthooks.apply_networking(target, state)
curthooks.handle_pollinate_user_agent(cfg, target)

run_hook_in_target(target, 'install-custom-packages')

# set cloud-init maas datasource
if cfg.get('cloudconfig'):
curthooks.handle_cloudconfig(
cfg['cloudconfig'],
base_dir=target_path(target,
'etc/cloud/cloud.cfg.d'))

run_hook_in_target(target, 'setup-bootloader')

def cleanup():
"""Remove curtin-hooks so its as if we were never here."""
curtin_dir = os.path.dirname(__file__)
shutil.rmtree(curtin_dir)


def main():
state = load_command_environment()
config = load_command_config(None, state)
target = state['target']

curthook(config, target, state)
cleanup()


if __name__ == "__main__":
main()
50 changes: 50 additions & 0 deletions xenserver8/curtin/install-custom-packages
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash

log_file="/var/log/post-install.log"

if [ -f /etc/yum/pluginconf.d/ptoken.conf ]; then
sed -i s/1/0/g /etc/yum/pluginconf.d/ptoken.conf
fi

mkdir -pv /etc/pki/rpm-gpg/ >> $log_file
curl -o /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 https://vault.centos.org/RPM-GPG-KEY-CentOS-7 >> $log_file

mkdir -pv /etc/yum.repos.d/ >> $log_file

cat <<EOF >/etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-$releasever - Base
baseurl=https://vault.centos.org/7.9.2009/os/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[updates]
name=CentOS-$releasever - Updates
baseurl=https://vault.centos.org/7.9.2009/updates/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[extras]
name=CentOS-$releasever - Extras
baseurl=https://vault.centos.org/7.9.2009/extras/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[centosplus]
name=CentOS-$releasever - CentOSPlus
baseurl=https://vault.centos.org/7.9.2009/centosplus/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=0
EOF

yum -y install cloud-init python-oauthlib >> $log_file
yum clean all >> $log_file

# This is no longer required
rm -v /etc/yum.repos.d/CentOS-Base.repo >> $log_file

exit 0
25 changes: 25 additions & 0 deletions xenserver8/curtin/setup-bootloader
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# Update initrd
#for k in $(find /boot/ -type f -name vmlinuz-* | awk -F 'vmlinuz-' '{print $2}'); do dracut --no-hostonly -f /boot/initrd-${k}.img ${k}; done

# Generate the GRUB config file
grub-mkconfig -o /boot/grub/grub.cfg

# Install GRUB and update the configuration
if [ -d /sys/firmware/efi/efivars/ ]; then
# Mount /boot/efi first if not mounted
if [ ! -d /boot/efi/EFI ]; then
mount -L $(blkid | grep vfat | grep -oP 'LABEL="[^"]*"' | cut -d'"' -f2) /boot/efi/
fi

grub_dev="/dev/$(lsblk -r | grep 'part /$' | awk '{print $1}' | sed s/[0-9]//g)"
grub_part_num="$(lsblk -r | grep 'part /boot/efi' | awk '{print $1}' | sed s/[a-z]//g)"

mkdir -p /boot/efi/EFI/BOOT
mkdir -p /boot/efi/boot/grub

efibootmgr --create --disk ${grub_dev} --part ${grub_part_num} --label "XenServer8" --loader /EFI/xenserver/grubx64.efi
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=xenserver
fi
exit 0
3 changes: 3 additions & 0 deletions xenserver8/http/xenserver8.post.sh.pkrtpl.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

shutdown 0
14 changes: 14 additions & 0 deletions xenserver8/http/xenserver8.xml.pkrtpl.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<installation srtype="ext">
<primary-disk>vda</primary-disk>
<guest-disk>vda</guest-disk>
<keymap>us</keymap>
<root-password>mypassword</root-password>
<source type="local" />
<post-install-script type="url">
http://10.0.2.2:8100/xenserver8.post.sh
</post-install-script>
<admin-interface name="eth0" proto="dhcp" />
<timezone>America/LosAngeles</timezone>
</installation>

45 changes: 45 additions & 0 deletions xenserver8/post.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh -x
PACKER_OUTPUT=output-${SOURCE:-qemu}/packer-${SOURCE:-qemu}

TMP_DIR=$(mktemp -d /tmp/packer-maas-XXXX)

ROOT_DIR="${TMP_DIR}/root"

qemu-nbd --socket="${TMP_DIR}"/qemu-img.sock \
--format="${IMG_FMT}" \
--shared=10 \
"${PACKER_OUTPUT}" &
sleep 5

mkdir -pv "${ROOT_DIR}"

DEV=${TMP_DIR}/p1

mkdir -pv "${DEV}"

nbdfuse "${DEV}" \
--command nbdkit -s nbd \
socket="${TMP_DIR}"/qemu-img.sock \
--filter=partition partition="1" &

retries=0
until [ -f "${DEV}/nbd" ]; do
sleep 1
if ((++retries > 10)); then
return 1
fi
done

echo "Mounting ${DEV}/nbd under ${ROOT_DIR}..."
fuse2fs "${DEV}"/nbd "${ROOT_DIR}" -o fakeroot

echo 'Adding curtin-hooks to image...'
cp -rv curtin "$ROOT_DIR"
sync

echo "Unmount and Clean-up $ROOT_DIR..."
umount $DEV/nbd
sleep 3
umount $DEV
rm -rv $ROOT_DIR
echo 'Done'
Loading
Loading