mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-13 23:12:50 +00:00

Signed-off-by: Johannes Kastl <git@ojkastl.de> Acked-by: Stéphane Graber <stgraber@ubuntu.com>
498 lines
15 KiB
Bash
498 lines
15 KiB
Bash
#!/bin/bash
|
|
|
|
#
|
|
# template script for generating OpenSUSE container for LXC
|
|
#
|
|
|
|
#
|
|
# lxc: linux Container library
|
|
|
|
# Authors:
|
|
# Daniel Lezcano <daniel.lezcano@free.fr>
|
|
# Frederic Crozat <fcrozat@suse.com>
|
|
# Michael H. Warfield <mhw@WittsEnd.com>
|
|
# Johannes Kastl <mail@ojkastl.de>
|
|
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
|
|
# This library 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
|
|
# Lesser General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
# Detect use under userns (unsupported)
|
|
for arg in "$@"; do
|
|
[ "$arg" = "--" ] && break
|
|
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
|
|
echo "This template can't be used for unprivileged containers." 1>&2
|
|
echo "You may want to try the \"download\" template instead." 1>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Make sure the usual locations are in PATH
|
|
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
|
|
|
|
configure_opensuse()
|
|
{
|
|
rootfs=$1
|
|
hostname=$2
|
|
|
|
# set first network adapter as dhcp. This is the most common config.
|
|
cat <<EOF > $rootfs/etc/sysconfig/network/ifcfg-eth0
|
|
STARTMODE='auto'
|
|
BOOTPROTO='dhcp'
|
|
EOF
|
|
|
|
# create empty fstab
|
|
touch $rootfs/etc/fstab
|
|
|
|
# set the hostname
|
|
cat <<EOF > $rootfs/etc/HOSTNAME
|
|
$hostname
|
|
EOF
|
|
# ensure /etc/hostname is available too
|
|
ln -s -f HOSTNAME $rootfs/etc/hostname
|
|
|
|
# do not use hostname from HOSTNAME variable
|
|
cat <<EOF >> $rootfs/etc/sysconfig/cron
|
|
unset HOSTNAME
|
|
EOF
|
|
|
|
# set minimal hosts
|
|
cat <<EOF > $rootfs/etc/hosts
|
|
127.0.0.1 localhost $hostname
|
|
EOF
|
|
|
|
# disable yast->bootloader in container
|
|
cat <<EOF > $rootfs/etc/sysconfig/bootloader
|
|
LOADER_TYPE=none
|
|
LOADER_LOCATION=none
|
|
EOF
|
|
|
|
# set /dev/console as securetty
|
|
cat << EOF >> $rootfs/etc/securetty
|
|
console
|
|
EOF
|
|
|
|
cat <<EOF >> $rootfs/etc/sysconfig/boot
|
|
# disable root fsck
|
|
ROOTFS_FSCK="0"
|
|
ROOTFS_BLKDEV="/dev/null"
|
|
EOF
|
|
|
|
|
|
# remove pointless services in a container
|
|
ln -s /dev/null $rootfs/etc/systemd/system/proc-sys-fs-binfmt_misc.automount
|
|
ln -s /dev/null $rootfs/etc/systemd/system/console-shell.service
|
|
ln -s /dev/null $rootfs/etc/systemd/system/systemd-vconsole-setup.service
|
|
sed -e 's/ConditionPathExists=.*//' /usr/lib/systemd/system/getty@.service > $rootfs/etc/systemd/system/getty@.service
|
|
ln -s getty@.service $rootfs/etc/systemd/system/getty@tty1.service
|
|
ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@console.service
|
|
ln -s -f ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty1.service
|
|
ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty2.service
|
|
ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty3.service
|
|
ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty4.service
|
|
|
|
# copy host poweroff target as sigpwr target to make shutdown work
|
|
# see https://wiki.archlinux.org/index.php/Linux_Containers#Container_cannot_be_shutdown_if_using_systemd
|
|
cp /usr/lib/systemd/system/poweroff.target $rootfs/usr/lib/systemd/system/sigpwr.target
|
|
|
|
touch $rootfs/etc/sysconfig/kernel
|
|
|
|
echo "Please change root-password !"
|
|
echo "root:root" | chpasswd -R $rootfs
|
|
|
|
return 0
|
|
}
|
|
|
|
download_opensuse()
|
|
{
|
|
cache=$1
|
|
arch=$2
|
|
|
|
if [ ! -x /usr/bin/build ]; then
|
|
echo "Could not create openSUSE template :"
|
|
echo "you need to install \"build\" package"
|
|
return 1
|
|
fi
|
|
|
|
# check the mini opensuse was not already downloaded
|
|
mkdir -p "$cache/partial-$arch"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to create '$cache/partial-$arch' directory"
|
|
return 1
|
|
fi
|
|
|
|
# download a mini opensuse into a cache
|
|
echo "Downloading opensuse minimal ..."
|
|
mkdir -p "$cache/partial-$arch-packages"
|
|
zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/distribution/$DISTRO/repo/oss/ repo-oss || return 1
|
|
zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1
|
|
zypper --quiet --root $cache/partial-$arch-packages --non-interactive --gpg-auto-import-keys update || return 1
|
|
zypper --root $cache/partial-$arch-packages --non-interactive in --auto-agree-with-licenses --download-only zypper lxc patterns-openSUSE-base bash iputils sed tar rsyslog || return 1
|
|
cat > $cache/partial-$arch-packages/opensuse.conf << EOF
|
|
Preinstall: aaa_base bash coreutils diffutils
|
|
Preinstall: filesystem fillup glibc grep insserv-compat perl-base
|
|
Preinstall: libbz2-1 libgcc_s1 libncurses5 pam
|
|
Preinstall: permissions libreadline6 rpm sed tar libz1 libselinux1
|
|
Preinstall: liblzma5 libcap2 libacl1 libattr1
|
|
Preinstall: libpopt0 libelf1 liblua5_1
|
|
Preinstall: libpcre1
|
|
|
|
RunScripts: aaa_base
|
|
|
|
Support: zypper
|
|
Support: patterns-openSUSE-base
|
|
Support: lxc
|
|
Support: ncurses-utils
|
|
Support: iputils
|
|
Support: udev
|
|
Support: netcfg
|
|
Support: dhcpcd hwinfo insserv-compat module-init-tools openSUSE-release openssh
|
|
Support: pwdutils rpcbind sysconfig
|
|
|
|
Ignore: rpm:suse-build-key,build-key
|
|
Ignore: systemd:systemd-presets-branding
|
|
EOF
|
|
|
|
if [ $DISTRO = "13.2" ]
|
|
then
|
|
echo "Support: python3-base" >> $cache/partial-$arch-packages/opensuse.conf
|
|
fi
|
|
|
|
if [ "$arch" = "i686" ]; then
|
|
mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/
|
|
for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i586/*" ; do
|
|
ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/
|
|
done
|
|
mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686
|
|
for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/update/i586/*" ; do
|
|
ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686/
|
|
done
|
|
fi
|
|
|
|
# openSUSE 13.2 has no noarch directory in update
|
|
[ -d $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch ] || mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch
|
|
|
|
CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:/usr/lib/build" /usr/lib/build/init_buildsystem --clean --configdir /usr/lib/build/configs --cachedir $cache/partial-$arch-cache --repository $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/$arch --repository $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/noarch --repository $cache/partial-$arch-packages/var/cache/zypp/packages/update/$arch --repository $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch || return 1
|
|
chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/distribution/$DISTRO/repo/oss repo-oss || return 1
|
|
chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar http://download.opensuse.org/update/$DISTRO/ update || return 1
|
|
# really clean the image
|
|
rm -fr $cache/partial-$arch/{.build,.guessed_dist,.srcfiles*,installed-pkg}
|
|
rm -fr $cache/partial-$arch/dev
|
|
# make sure we have a minimal /dev
|
|
mkdir -p "$cache/partial-$arch/dev"
|
|
mknod -m 666 $cache/partial-$arch/dev/null c 1 3
|
|
mknod -m 666 $cache/partial-$arch/dev/zero c 1 5
|
|
# create mtab symlink
|
|
rm -f $cache/partial-$arch/etc/mtab
|
|
ln -sf /proc/self/mounts $cache/partial-$arch/etc/mtab
|
|
|
|
# ensure /var/run and /run are symlinked
|
|
rm -fr $cache/partial-$arch/var/run
|
|
ln -s -f ../run $cache/partial-$arch/var/run
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to download the rootfs, aborting."
|
|
return 1
|
|
fi
|
|
|
|
rm -fr "$cache/partial-$arch-packages"
|
|
mv "$1/partial-$arch" "$1/rootfs-$arch"
|
|
echo "Download complete."
|
|
|
|
return 0
|
|
}
|
|
|
|
copy_opensuse()
|
|
{
|
|
cache=$1
|
|
arch=$2
|
|
rootfs=$3
|
|
|
|
# make a local copy of the mini opensuse
|
|
echo "Copying rootfs to $rootfs ..."
|
|
mkdir -p $rootfs
|
|
rsync -Ha $cache/rootfs-$arch/ $rootfs/ || return 1
|
|
return 0
|
|
}
|
|
|
|
install_opensuse()
|
|
{
|
|
cache="@LOCALSTATEDIR@/cache/lxc/opensuse/$DISTRO"
|
|
rootfs=$1
|
|
mkdir -p @LOCALSTATEDIR@/lock/subsys/
|
|
(
|
|
flock -x 9
|
|
if [ $? -ne 0 ]; then
|
|
echo "Cache repository is busy."
|
|
return 1
|
|
fi
|
|
|
|
echo "Checking cache download in $cache/rootfs-$arch ... "
|
|
if [ ! -e "$cache/rootfs-$arch" ]; then
|
|
download_opensuse $cache $arch
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to download 'opensuse base'"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
echo "Copy $cache/rootfs-$arch to $rootfs ... "
|
|
copy_opensuse $cache $arch $rootfs
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to copy rootfs"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse
|
|
|
|
return $?
|
|
}
|
|
|
|
# Generate a random hardware (MAC) address composed of FE followed by
|
|
# 5 random bytes...
|
|
create_hwaddr()
|
|
{
|
|
openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/'
|
|
}
|
|
|
|
copy_configuration()
|
|
{
|
|
path=$1
|
|
rootfs=$2
|
|
name=$3
|
|
|
|
grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "
|
|
lxc.rootfs = $rootfs_path
|
|
" >> $path/config
|
|
|
|
# The following code is to create static MAC addresses for each
|
|
# interface in the container. This code will work for multiple
|
|
# interfaces in the default config. It will also strip any
|
|
# hwaddr stanzas out of the default config since we can not share
|
|
# MAC addresses between containers.
|
|
#
|
|
# This code is largely mimiced from the Fedora Template.
|
|
mv $path/config $path/config.def
|
|
while read LINE
|
|
do
|
|
# This should catch variable expansions from the default config...
|
|
if expr "${LINE}" : '.*\$' > /dev/null 2>&1
|
|
then
|
|
LINE=$(eval "echo \"${LINE}\"")
|
|
fi
|
|
|
|
# There is a tab and a space in the regex bracket below!
|
|
# Seems that \s doesn't work in brackets.
|
|
KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=')
|
|
|
|
if [[ "${KEY}" != "lxc.network.hwaddr" ]]
|
|
then
|
|
echo "${LINE}" >> $path/config
|
|
|
|
if [[ "${KEY}" == "lxc.network.link" ]]
|
|
then
|
|
echo "lxc.network.hwaddr = $(create_hwaddr)" >> $path/config
|
|
fi
|
|
fi
|
|
done < $path/config.def
|
|
|
|
rm -f $path/config.def
|
|
|
|
if [ -e "@LXCTEMPLATECONFIG@/opensuse.common.conf" ]; then
|
|
echo "
|
|
# Include common configuration
|
|
lxc.include = @LXCTEMPLATECONFIG@/opensuse.common.conf
|
|
" >> $path/config
|
|
fi
|
|
|
|
# Append things which require expansion here...
|
|
cat <<EOF >> $path/config
|
|
lxc.arch = $arch
|
|
lxc.utsname = $name
|
|
|
|
lxc.mount = $path/fstab
|
|
|
|
# When using LXC with apparmor, uncomment the next line to run unconfined:
|
|
#lxc.aa_profile = unconfined
|
|
|
|
# example simple networking setup, uncomment to enable
|
|
#lxc.network.type = $lxc_network_type
|
|
#lxc.network.flags = up
|
|
#lxc.network.link = $lxc_network_link
|
|
#lxc.network.name = eth0
|
|
# Additional example for veth network type
|
|
# static MAC address,
|
|
#lxc.network.hwaddr = 00:16:3e:77:52:20
|
|
# persistent veth device name on host side
|
|
# Note: This may potentially collide with other containers of same name!
|
|
#lxc.network.veth.pair = v-$name-e0
|
|
|
|
EOF
|
|
|
|
cat <<EOF > $path/fstab
|
|
proc proc proc nodev,noexec,nosuid 0 0
|
|
sysfs sys sysfs defaults 0 0
|
|
tmpfs run tmpfs mode=0755,nodev,nosuid 0 0
|
|
EOF
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to add configuration"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
clean()
|
|
{
|
|
cache="@LOCALSTATEDIR@/cache/lxc/opensuse"
|
|
|
|
if [ ! -e $cache ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# lock, so we won't purge while someone is creating a repository
|
|
(
|
|
flock -x 9
|
|
if [ $? != 0 ]; then
|
|
echo "Cache repository is busy."
|
|
exit 1
|
|
fi
|
|
|
|
echo -n "Purging the download cache..."
|
|
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
|
|
exit 0
|
|
) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse
|
|
}
|
|
|
|
usage()
|
|
{
|
|
cat <<EOF
|
|
$1 -h|--help -p|--path=<path> -r|--release nn.n --clean
|
|
Please give the release as 12.3, 13.1, 13.2 etc.
|
|
If no release is given, openSUSE 12.3 is installed.
|
|
EOF
|
|
return 0
|
|
}
|
|
|
|
# Make arch a global. This may become configurable?
|
|
arch=$(uname -m)
|
|
|
|
options=$(getopt -o hp:n:r:c -l help,rootfs:,path:,name:,release:,clean -- "$@")
|
|
if [ $? -ne 0 ]; then
|
|
usage $(basename $0)
|
|
exit 1
|
|
fi
|
|
eval set -- "$options"
|
|
|
|
while true
|
|
do
|
|
case "$1" in
|
|
-h|--help) usage $0 && exit 0;;
|
|
-p|--path) path=$2; shift 2;;
|
|
--rootfs) rootfs=$2; shift 2;;
|
|
-n|--name) name=$2; shift 2;;
|
|
-r|--release) DISTRO=$2; shift 2;;
|
|
-c|--clean) clean=$2; shift 2;;
|
|
--) shift 1; break ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
|
|
if [ ! -z "$clean" -a -z "$path" ]; then
|
|
clean || exit 1
|
|
exit 0
|
|
fi
|
|
|
|
type zypper > /dev/null
|
|
if [ $? -ne 0 ]; then
|
|
echo "'zypper' command is missing"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$path" ]; then
|
|
echo "'path' parameter is required"
|
|
exit 1
|
|
fi
|
|
|
|
if grep -q Harlequin /etc/os-release || grep -q Tumbleweed /etc/os-release ; then
|
|
echo "Building containers on openSUSE 13.2 or Tumbleweed is broken at the moment. We are working on this problem."
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$DISTRO" ]; then
|
|
echo ""
|
|
echo "No release selected, using openSUSE 12.3"
|
|
DISTRO=12.3
|
|
else
|
|
echo ""
|
|
case "$DISTRO" in
|
|
12.3)
|
|
echo "Selected openSUSE 12.3"
|
|
;;
|
|
|
|
13.1)
|
|
echo "Selected openSUSE 13.1"
|
|
;;
|
|
|
|
13.2)
|
|
echo "Selected openSUSE 13.2"
|
|
;;
|
|
|
|
*)
|
|
echo "You have chosen an invalid release, quitting..."
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
|
|
if [ "$(id -u)" != "0" ]; then
|
|
echo "This script should be run as 'root'"
|
|
exit 1
|
|
fi
|
|
|
|
# detect rootfs
|
|
config="$path/config"
|
|
if [ -z "$rootfs" ]; then
|
|
if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
|
|
rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config)
|
|
else
|
|
rootfs=$path/rootfs
|
|
fi
|
|
fi
|
|
|
|
install_opensuse $rootfs
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to install opensuse"
|
|
exit 1
|
|
fi
|
|
|
|
configure_opensuse $rootfs $name
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed to configure opensuse for a container"
|
|
exit 1
|
|
fi
|
|
|
|
copy_configuration $path $rootfs $name
|
|
if [ $? -ne 0 ]; then
|
|
echo "failed write configuration file"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -z $clean ]; then
|
|
clean || exit 1
|
|
exit 0
|
|
fi
|