forked from neil/lxc-templates
1496 lines
49 KiB
Plaintext
1496 lines
49 KiB
Plaintext
|
#!/bin/bash
|
||
|
|
||
|
#
|
||
|
# template script for generating fedora container for LXC
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# lxc: linux Container library
|
||
|
|
||
|
# Authors:
|
||
|
# Daniel Lezcano <daniel.lezcano@free.fr>
|
||
|
# Ramez Hanna <rhanna@informatiq.org>
|
||
|
# Michael H. Warfield <mhw@WittsEnd.com>
|
||
|
|
||
|
# 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
|
||
|
|
||
|
#Configurations
|
||
|
default_path=@LXCPATH@
|
||
|
|
||
|
# Some combinations of the tuning knobs below do not exactly make sense.
|
||
|
# but that's ok.
|
||
|
#
|
||
|
# If the "root_password" is non-blank, use it, else set a default.
|
||
|
# This can be passed to the script as an environment variable and is
|
||
|
# set by a shell conditional assignment. Looks weird but it is what it is.
|
||
|
#
|
||
|
# If the root password contains a ding ($) then try to expand it.
|
||
|
# That will pick up things like ${name} and ${RANDOM}.
|
||
|
# If the root password contains more than 3 consecutive X's, pass it as
|
||
|
# a template to mktemp and take the result.
|
||
|
#
|
||
|
# If root_display_password = yes, display the temporary root password at exit.
|
||
|
# If root_store_password = yes, store it in the configuration directory
|
||
|
# If root_prompt_password = yes, invoke "passwd" to force the user to change
|
||
|
# the root password after the container is created.
|
||
|
# If root_expire_password = yes, you will be prompted to change the root
|
||
|
# password at the first login.
|
||
|
#
|
||
|
# These are conditional assignments... The can be overridden from the
|
||
|
# preexisting environment variables...
|
||
|
#
|
||
|
# Make sure this is in single quotes to defer expansion to later!
|
||
|
# :{root_password='Root-${name}-${RANDOM}'}
|
||
|
: ${root_password='Root-${name}-XXXXXX'}
|
||
|
|
||
|
# Now, it doesn't make much sense to display, store, and force change
|
||
|
# together. But, we gotta test, right???
|
||
|
: ${root_display_password='no'}
|
||
|
: ${root_store_password='yes'}
|
||
|
# Prompting for something interactive has potential for mayhem
|
||
|
# with users running under the API... Don't default to "yes"
|
||
|
: ${root_prompt_password='no'}
|
||
|
|
||
|
# Expire root password? Default to yes, but can be overridden from
|
||
|
# the environment variable
|
||
|
: ${root_expire_password='yes'}
|
||
|
|
||
|
# These are only going into comments in the resulting config...
|
||
|
lxc_network_type=veth
|
||
|
lxc_network_link=lxcbr0
|
||
|
|
||
|
# is this fedora?
|
||
|
# Alow for weird remixes like the Raspberry Pi
|
||
|
#
|
||
|
# Use the Mitre standard CPE identifier for the release ID if possible...
|
||
|
# This may be in /etc/os-release or /etc/system-release-cpe. We
|
||
|
# should be able to use EITHER. Give preference to /etc/os-release for now.
|
||
|
|
||
|
# 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
|
||
|
|
||
|
if [ -e /etc/os-release ]
|
||
|
then
|
||
|
# This is a shell friendly configuration file. We can just source it.
|
||
|
# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME
|
||
|
. /etc/os-release
|
||
|
echo "Host CPE ID from /etc/os-release: ${CPE_NAME}"
|
||
|
fi
|
||
|
|
||
|
if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ]
|
||
|
then
|
||
|
CPE_NAME=$(head -n1 /etc/system-release-cpe)
|
||
|
CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)')
|
||
|
if [ "${CPE_URI}" != "cpe:/o" ]
|
||
|
then
|
||
|
CPE_NAME=
|
||
|
else
|
||
|
echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
|
||
|
# Probably a better way to do this but sill remain posix
|
||
|
# compatible but this works, shrug...
|
||
|
# Must be nice and not introduce convenient bashisms here.
|
||
|
ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
|
||
|
VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [ "${CPE_NAME}" != "" -a "${ID}" = "fedora" -a "${VERSION_ID}" != "" ]
|
||
|
then
|
||
|
fedora_host_ver=${VERSION_ID}
|
||
|
is_fedora=true
|
||
|
elif [ -e /etc/redhat-release ]
|
||
|
then
|
||
|
# Only if all other methods fail, try to parse the redhat-release file.
|
||
|
fedora_host_ver=$( sed -e '/^Fedora /!d' -e 's/Fedora.*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release )
|
||
|
if [ "$fedora_host_ver" != "" ]
|
||
|
then
|
||
|
is_fedora=true
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
configure_fedora()
|
||
|
{
|
||
|
|
||
|
# disable selinux in fedora
|
||
|
mkdir -p $rootfs_path/selinux
|
||
|
echo 0 > $rootfs_path/selinux/enforce
|
||
|
|
||
|
# Also kill it in the /etc/selinux/config file if it's there...
|
||
|
if [[ -f $rootfs_path/etc/selinux/config ]]
|
||
|
then
|
||
|
sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config
|
||
|
fi
|
||
|
|
||
|
# Nice catch from Dwight Engen in the Oracle template.
|
||
|
# Wantonly plagerized here with much appreciation.
|
||
|
if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then
|
||
|
mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig
|
||
|
ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled
|
||
|
fi
|
||
|
|
||
|
# This is a known problem and documented in RedHat bugzilla as relating
|
||
|
# to a problem with auditing enabled. This prevents an error in
|
||
|
# the container "Cannot make/remove an entry for the specified session"
|
||
|
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login
|
||
|
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd
|
||
|
|
||
|
if [ -f ${rootfs_path}/etc/pam.d/crond ]
|
||
|
then
|
||
|
sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond
|
||
|
fi
|
||
|
|
||
|
# In addition to disabling pam_loginuid in the above config files
|
||
|
# we'll also disable it by linking it to pam_permit to catch any
|
||
|
# we missed or any that get installed after the container is built.
|
||
|
#
|
||
|
# Catch either or both 32 and 64 bit archs.
|
||
|
if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ]
|
||
|
then
|
||
|
( cd ${rootfs_path}/lib/security/
|
||
|
mv pam_loginuid.so pam_loginuid.so.disabled
|
||
|
ln -s pam_permit.so pam_loginuid.so
|
||
|
)
|
||
|
fi
|
||
|
|
||
|
if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ]
|
||
|
then
|
||
|
( cd ${rootfs_path}/lib64/security/
|
||
|
mv pam_loginuid.so pam_loginuid.so.disabled
|
||
|
ln -s pam_permit.so pam_loginuid.so
|
||
|
)
|
||
|
fi
|
||
|
|
||
|
# Set default localtime to the host localtime if not set...
|
||
|
if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ]
|
||
|
then
|
||
|
# if /etc/localtime is a symlink, this should preserve it.
|
||
|
cp -a /etc/localtime ${rootfs_path}/etc/localtime
|
||
|
fi
|
||
|
|
||
|
# Deal with some dain bramage in the /etc/init.d/halt script.
|
||
|
# Trim it and make it our own and link it in before the default
|
||
|
# halt script so we can intercept it. This also preventions package
|
||
|
# updates from interferring with our interferring with it.
|
||
|
#
|
||
|
# There's generally not much in the halt script that useful but what's
|
||
|
# in there from resetting the hardware clock down is generally very bad.
|
||
|
# So we just eliminate the whole bottom half of that script in making
|
||
|
# ourselves a copy. That way a major update to the init scripts won't
|
||
|
# trash what we've set up.
|
||
|
#
|
||
|
# This is mostly for legacy distros since any modern systemd Fedora
|
||
|
# release will not have this script so we won't try to intercept it.
|
||
|
if [ -f ${rootfs_path}/etc/init.d/halt ]
|
||
|
then
|
||
|
sed -e '/hwclock/,$d' \
|
||
|
< ${rootfs_path}/etc/init.d/halt \
|
||
|
> ${rootfs_path}/etc/init.d/lxc-halt
|
||
|
|
||
|
echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt
|
||
|
chmod 755 ${rootfs_path}/etc/init.d/lxc-halt
|
||
|
|
||
|
# Link them into the rc directories...
|
||
|
(
|
||
|
cd ${rootfs_path}/etc/rc.d/rc0.d
|
||
|
ln -s ../init.d/lxc-halt S00lxc-halt
|
||
|
cd ${rootfs_path}/etc/rc.d/rc6.d
|
||
|
ln -s ../init.d/lxc-halt S00lxc-reboot
|
||
|
)
|
||
|
fi
|
||
|
|
||
|
# configure the network using the dhcp
|
||
|
cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
|
||
|
DEVICE=eth0
|
||
|
BOOTPROTO=dhcp
|
||
|
ONBOOT=yes
|
||
|
HOSTNAME=${utsname}
|
||
|
DHCP_HOSTNAME=\`hostname\`
|
||
|
NM_CONTROLLED=no
|
||
|
TYPE=Ethernet
|
||
|
MTU=${MTU}
|
||
|
EOF
|
||
|
|
||
|
# set the hostname
|
||
|
cat <<EOF > ${rootfs_path}/etc/sysconfig/network
|
||
|
NETWORKING=yes
|
||
|
HOSTNAME=${utsname}
|
||
|
EOF
|
||
|
|
||
|
# set hostname on systemd Fedora systems
|
||
|
if [ $release -gt 14 ]; then
|
||
|
echo "${utsname}" > ${rootfs_path}/etc/hostname
|
||
|
fi
|
||
|
|
||
|
# set minimal hosts
|
||
|
cat <<EOF > $rootfs_path/etc/hosts
|
||
|
127.0.0.1 localhost.localdomain localhost $utsname
|
||
|
::1 localhost6.localdomain6 localhost6
|
||
|
EOF
|
||
|
|
||
|
# These mknod's really don't make any sense with modern releases of
|
||
|
# Fedora with systemd, devtmpfs, and autodev enabled. They are left
|
||
|
# here for legacy reasons and older releases with upstart and sysv init.
|
||
|
dev_path="${rootfs_path}/dev"
|
||
|
rm -rf $dev_path
|
||
|
mkdir -p $dev_path
|
||
|
mknod -m 666 ${dev_path}/null c 1 3
|
||
|
mknod -m 666 ${dev_path}/zero c 1 5
|
||
|
mknod -m 666 ${dev_path}/random c 1 8
|
||
|
mknod -m 666 ${dev_path}/urandom c 1 9
|
||
|
mkdir -m 755 ${dev_path}/pts
|
||
|
mkdir -m 1777 ${dev_path}/shm
|
||
|
mknod -m 666 ${dev_path}/tty c 5 0
|
||
|
mknod -m 666 ${dev_path}/tty0 c 4 0
|
||
|
mknod -m 666 ${dev_path}/tty1 c 4 1
|
||
|
mknod -m 666 ${dev_path}/tty2 c 4 2
|
||
|
mknod -m 666 ${dev_path}/tty3 c 4 3
|
||
|
mknod -m 666 ${dev_path}/tty4 c 4 4
|
||
|
mknod -m 600 ${dev_path}/console c 5 1
|
||
|
mknod -m 666 ${dev_path}/full c 1 7
|
||
|
mknod -m 600 ${dev_path}/initctl p
|
||
|
mknod -m 666 ${dev_path}/ptmx c 5 2
|
||
|
|
||
|
# setup console and tty[1-4] for login. note that /dev/console and
|
||
|
# /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and
|
||
|
# /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks.
|
||
|
# lxc will maintain these links and bind mount ptys over /dev/lxc/*
|
||
|
# since lxc.tty.dir is specified in the config.
|
||
|
|
||
|
# allow root login on console, tty[1-4], and pts/0 for libvirt
|
||
|
echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty
|
||
|
echo "lxc/console" >>${rootfs_path}/etc/securetty
|
||
|
echo "lxc/tty1" >>${rootfs_path}/etc/securetty
|
||
|
echo "lxc/tty2" >>${rootfs_path}/etc/securetty
|
||
|
echo "lxc/tty3" >>${rootfs_path}/etc/securetty
|
||
|
echo "lxc/tty4" >>${rootfs_path}/etc/securetty
|
||
|
echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty
|
||
|
echo "pts/0" >>${rootfs_path}/etc/securetty
|
||
|
|
||
|
if [ ${root_display_password} = "yes" ]
|
||
|
then
|
||
|
echo "Setting root password to '$root_password'"
|
||
|
fi
|
||
|
if [ ${root_store_password} = "yes" ]
|
||
|
then
|
||
|
touch ${config_path}/tmp_root_pass
|
||
|
chmod 600 ${config_path}/tmp_root_pass
|
||
|
echo ${root_password} > ${config_path}/tmp_root_pass
|
||
|
echo "Storing root password in '${config_path}/tmp_root_pass'"
|
||
|
fi
|
||
|
|
||
|
echo "root:$root_password" | chroot $rootfs_path chpasswd
|
||
|
|
||
|
if [ ${root_expire_password} = "yes" ]
|
||
|
then
|
||
|
# Also set this password as expired to force the user to change it!
|
||
|
chroot $rootfs_path passwd -e root
|
||
|
fi
|
||
|
|
||
|
# specifying this in the initial packages doesn't always work.
|
||
|
# Even though it should have...
|
||
|
echo "installing fedora-release package"
|
||
|
mount -o bind /dev ${rootfs_path}/dev
|
||
|
mount -t proc proc ${rootfs_path}/proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf ${rootfs_path}/etc/
|
||
|
# Rebuild the rpm database based on the target rpm version...
|
||
|
rm -f ${rootfs_path}/var/lib/rpm/__db*
|
||
|
chroot ${rootfs_path} rpm --rebuilddb
|
||
|
chroot ${rootfs_path} yum -y install fedora-release
|
||
|
|
||
|
if [[ ! -e ${rootfs_path}/sbin/NetworkManager ]]
|
||
|
then
|
||
|
# NetworkManager has not been installed. Use the
|
||
|
# legacy chkconfig command to enable the network startup
|
||
|
# scripts in the container.
|
||
|
chroot ${rootfs_path} chkconfig network on
|
||
|
fi
|
||
|
|
||
|
umount ${rootfs_path}/proc
|
||
|
umount ${rootfs_path}/dev
|
||
|
|
||
|
# silence some needless startup errors
|
||
|
touch ${rootfs_path}/etc/fstab
|
||
|
|
||
|
# give us a console on /dev/console
|
||
|
sed -i 's/ACTIVE_CONSOLES=.*$/ACTIVE_CONSOLES="\/dev\/console \/dev\/tty[1-4]"/' \
|
||
|
${rootfs_path}/etc/sysconfig/init
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
configure_fedora_init()
|
||
|
{
|
||
|
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit
|
||
|
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit
|
||
|
# don't mount devpts, for pete's sake
|
||
|
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.sysinit
|
||
|
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit
|
||
|
chroot ${rootfs_path} chkconfig udev-post off
|
||
|
chroot ${rootfs_path} chkconfig network on
|
||
|
|
||
|
if [ -d ${rootfs_path}/etc/init ]
|
||
|
then
|
||
|
# This is to make upstart honor SIGPWR. Should do no harm
|
||
|
# on systemd systems and some systems may have both.
|
||
|
cat <<EOF >${rootfs_path}/etc/init/power-status-changed.conf
|
||
|
# power-status-changed - shutdown on SIGPWR
|
||
|
#
|
||
|
start on power-status-changed
|
||
|
|
||
|
exec /sbin/shutdown -h now "SIGPWR received"
|
||
|
EOF
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
configure_fedora_systemd()
|
||
|
{
|
||
|
rm -f ${rootfs_path}/etc/systemd/system/default.target
|
||
|
touch ${rootfs_path}/etc/fstab
|
||
|
chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service
|
||
|
chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
|
||
|
# Make systemd honor SIGPWR
|
||
|
chroot ${rootfs_path} ln -s /usr/lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target
|
||
|
|
||
|
# if desired, prevent systemd from over-mounting /tmp with tmpfs
|
||
|
if [ $masktmp -eq 1 ]; then
|
||
|
chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/tmp.mount
|
||
|
fi
|
||
|
|
||
|
#dependency on a device unit fails it specially that we disabled udev
|
||
|
# sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service
|
||
|
#
|
||
|
# Actually, the After=dev-%i.device line does not appear in the
|
||
|
# Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left
|
||
|
# over from an earlier version and it's not doing any harm. We do need
|
||
|
# to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are
|
||
|
# started on the ttys in the container. Lets do it in an override copy of
|
||
|
# the service so it can still pass rpm verifies and not be automatically
|
||
|
# updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/
|
||
|
|
||
|
sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \
|
||
|
-e 's/After=dev-%i.device/After=/' \
|
||
|
< ${rootfs_path}/lib/systemd/system/getty\@.service \
|
||
|
> ${rootfs_path}/etc/systemd/system/getty\@.service
|
||
|
# Setup getty service on the 4 ttys we are going to allow in the
|
||
|
# default config. Number should match lxc.tty
|
||
|
( cd ${rootfs_path}/etc/systemd/system/getty.target.wants
|
||
|
for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done )
|
||
|
}
|
||
|
|
||
|
### BEGIN Bootstrap Environment Code... Michael H. Warfield /\/\|=mhw=|\/\/
|
||
|
|
||
|
# Ok... Heads up. If you're reading these comments, you're either a
|
||
|
# template owner or someone wondering how the hell I did this (or, worse,
|
||
|
# someone in the future trying to maintain it). This code is slightly
|
||
|
# "evil coding bastard" code with one significant hack / dirty trick
|
||
|
# that you would probably miss just reading the code below. I'll mark
|
||
|
# it out with comments.
|
||
|
#
|
||
|
# Because of what this code does, it deserves a lot of comments so people
|
||
|
# can understand WHY I did it this way...
|
||
|
#
|
||
|
# Ultimate Objective - Build a Fedora container on a host system which does
|
||
|
# not have a (complete compatible) version of rpm and/or yum. That basically
|
||
|
# means damn near any distro other than Fedora and Ubuntu (which has rpm and
|
||
|
# yum available). Only requirements for this function are rsync and
|
||
|
# squashfs available to the kernel. If you don't have those, why are you
|
||
|
# even attempting to build containers?
|
||
|
#
|
||
|
# Challenge for this function - Bootstrap a Fedora install bootstrap
|
||
|
# run time environment which has all the pieces to run rpm and yum and
|
||
|
# from which we can build targets containers even where the host system
|
||
|
# has no support for rpm, yum, or fedora.
|
||
|
#
|
||
|
# Steps:
|
||
|
# Stage 0 - Download a Fedora LiveOS squashfs core (netinst core).
|
||
|
# Stage 1 - Extract filesystem from Stage 0 and update to full rpm & yum
|
||
|
# Stage 2 - Use Stage 1 to build a rootfs with python, rpm, and yum.
|
||
|
#
|
||
|
# Stage 2 becomes our bootstrap file system which can be cached
|
||
|
# and then used to build other arbitrary vesions of Fedora of a
|
||
|
# given architecture. Note that this only has to run once for
|
||
|
# Fedora on a given architecture since rpm and yum can build other
|
||
|
# versions. We'll arbitrarily pick Fedora 20 to build this. This
|
||
|
# will need to change as time goes on.
|
||
|
|
||
|
# Programmers Note... A future fall back may be to download the netinst
|
||
|
# iso image instead of the LiveOS squasfs image and work from that.
|
||
|
# That may be more general but will introduce another substep
|
||
|
# (mounting the iso) to the stage0 setup.
|
||
|
|
||
|
# This system is designed to be as autonomous as possible so all whitelists
|
||
|
# and controls are self-contained.
|
||
|
|
||
|
# Initial testing - Whitelist nobody. Build for everybody...
|
||
|
# Initial deployment - Whitelist Fedora.
|
||
|
# Long term - Whitelist Fedora, Debian, Ubuntu, CentOs, Scientific, and NST.
|
||
|
|
||
|
# List of distros which do not (should not) need a bootstrap (but we will test
|
||
|
# for rpm and yum none the less... OS SHOULD be taken from CPE values but
|
||
|
# Debian / Ubuntu doesn't support CPE yet.
|
||
|
|
||
|
# BOOTSTRAP_WHITE_LIST=""
|
||
|
BOOTSTRAP_WHITE_LIST="fedora"
|
||
|
# BOOTSTRAP_WHITE_LIST="fedora debian ubuntu centos scientific sl nst"
|
||
|
|
||
|
BOOTSTRAP=0
|
||
|
BOOTSTRAP_DIR=
|
||
|
BOOTSTRAP_CHROOT=
|
||
|
|
||
|
fedora_get_bootstrap()
|
||
|
{
|
||
|
echo "Bootstrap Environment testing..."
|
||
|
|
||
|
WHITE_LISTED=1
|
||
|
|
||
|
# We need rpm. No rpm - not possible to white list...
|
||
|
if ! which rpm > /dev/null 2>&1
|
||
|
then
|
||
|
WHITE_LISTED=0
|
||
|
fi
|
||
|
|
||
|
# We need yum No yum - not possible to white list...
|
||
|
if ! which yum > /dev/null 2>&1
|
||
|
then
|
||
|
WHITE_LISTED=0
|
||
|
fi
|
||
|
|
||
|
if [[ ${WHITE_LISTED} != 0 ]]
|
||
|
then
|
||
|
for OS in ${BOOTSTRAP_WHITE_LIST}
|
||
|
do
|
||
|
if [[ ${ID} = ${OS} ]]
|
||
|
then
|
||
|
echo "
|
||
|
OS ${ID} is whitelisted. Installation Bootstrap Environment not required.
|
||
|
"
|
||
|
return 0;
|
||
|
fi
|
||
|
done
|
||
|
fi
|
||
|
|
||
|
echo "
|
||
|
Fedora Installation Bootstrap Build..."
|
||
|
|
||
|
if ! which rsync > /dev/null 2>&1
|
||
|
then
|
||
|
echo "
|
||
|
Unable to locate rsync. Cravely bailing out before even attempting to build
|
||
|
an Installation Bootstrap Please install rsync and then rerun this process.
|
||
|
"
|
||
|
|
||
|
return 255
|
||
|
fi
|
||
|
|
||
|
[[ -d ${cache_base} ]] || mkdir -p ${cache_base}
|
||
|
|
||
|
cd ${cache_base}
|
||
|
|
||
|
# We know we don't have a cache directory of this version or we
|
||
|
# would have never reached this code to begin with. But we may
|
||
|
# have another Fedora cache directory from which we could run...
|
||
|
# We'll give a preference for close matches preferring higher over
|
||
|
# lower - which makes for really ugly code...
|
||
|
|
||
|
# Is this a "bashism" that will need cleaning up????
|
||
|
BOOTSTRAP_LIST="$(( $release + 1 ))/rootfs $(( $release - 1 ))/rootfs \
|
||
|
$(( $release + 2 ))/rootfs $(( $release - 2 ))/rootfs \
|
||
|
$(( $release + 3 ))/rootfs $(( $release - 3 ))/rootfs \
|
||
|
bootstrap"
|
||
|
|
||
|
for bootstrap in ${BOOTSTRAP_LIST}
|
||
|
do
|
||
|
if [[ -d ${bootstrap} ]]
|
||
|
then
|
||
|
echo "
|
||
|
Existing Bootstrap found. Testing..."
|
||
|
|
||
|
mount -o bind /dev ${bootstrap}/dev
|
||
|
mount -t proc proc ${bootstrap}/proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf ${bootstrap}/etc/
|
||
|
rm -f ${bootstrap}/var/lib/rpm/__db*
|
||
|
chroot ${bootstrap} rpm --rebuilddb
|
||
|
chroot ${bootstrap} yum -y update
|
||
|
RC=$?
|
||
|
umount ${bootstrap}/proc
|
||
|
umount ${bootstrap}/dev
|
||
|
|
||
|
if [[ 0 == ${RC} ]]
|
||
|
then
|
||
|
BOOTSTRAP=1
|
||
|
BOOTSTRAP_DIR="${cache_base}/${bootstrap}"
|
||
|
BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} "
|
||
|
BOOTSTRAP_INSTALL_ROOT=/run/install
|
||
|
|
||
|
echo "
|
||
|
Functional Installation Bootstrap exists and appears to be completed.
|
||
|
Will use existing Bootstrap: ${BOOTSTRAP_DIR}
|
||
|
"
|
||
|
return 0
|
||
|
fi
|
||
|
echo "
|
||
|
Installation Bootstrap in ${BOOTSTRAP_DIR} exists
|
||
|
but appears to be non-functional. Skipping... It should be removed.
|
||
|
"
|
||
|
fi
|
||
|
done
|
||
|
|
||
|
TMP_BOOTSTRAP_DIR=$( mktemp -d --tmpdir=${cache_base} bootstrap_XXXXXX )
|
||
|
|
||
|
cd ${TMP_BOOTSTRAP_DIR}
|
||
|
|
||
|
mkdir squashfs stage0 stage1 bootstrap
|
||
|
|
||
|
### Stage 0 setup.
|
||
|
# Download the LiveOS squashfs image
|
||
|
# mount image to "squashfs"
|
||
|
# mount contained LiveOS to stage0
|
||
|
|
||
|
# We're going to use the archives.fedoraproject.org mirror for the initial stages...
|
||
|
# 1 - It's generally up to date and complete
|
||
|
# 2 - It's has high bandwidth access
|
||
|
# 3 - It supports rsync and wildcarding (and we need both)
|
||
|
# 4 - Not all the mirrors carry the LiveOS images
|
||
|
|
||
|
if [[ ! -f ../LiveOS/squashfs.img ]]
|
||
|
then
|
||
|
echo "
|
||
|
Downloading stage 0 LiveOS squashfs file system from archives.fedoraproject.org...
|
||
|
Have a beer or a cup of coffee. This will take a bit (~300MB).
|
||
|
"
|
||
|
sleep 3 # let him read it...
|
||
|
|
||
|
# Right now, we are using Fedora 20 for the inial bootstrap.
|
||
|
# We could make this the "current" Fedora rev (F > 15).
|
||
|
|
||
|
rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/LiveOS .
|
||
|
|
||
|
if [[ 0 == $? ]]
|
||
|
then
|
||
|
echo "Download of squashfs image complete."
|
||
|
mv LiveOS ..
|
||
|
else
|
||
|
echo "
|
||
|
Download of squashfs image failed.
|
||
|
"
|
||
|
return 255
|
||
|
fi
|
||
|
else
|
||
|
echo "Using cached stage 0 LiveOS squashfs file system."
|
||
|
fi
|
||
|
|
||
|
mount -o loop ../LiveOS/squashfs.img squashfs
|
||
|
|
||
|
if [[ $? != 0 ]]
|
||
|
then
|
||
|
echo "
|
||
|
Mount of LiveOS squashfs image failed! You mush have squashfs support
|
||
|
available to mount image. Unable to continue. Correct and retry
|
||
|
process later! LiveOS image not removed. Process may be rerun
|
||
|
without penalty of downloading LiveOS again. If LiveOS is corrupt,
|
||
|
remove ${cache_base}/LiveOS before rerunning to redownload.
|
||
|
"
|
||
|
return 255
|
||
|
fi
|
||
|
|
||
|
mount -o loop squashfs/LiveOS/rootfs.img stage0
|
||
|
|
||
|
if [[ $? != 0 ]]
|
||
|
then
|
||
|
echo "
|
||
|
Mount of LiveOS stage0 rootfs image failed! LiveOS download may be corrupt.
|
||
|
Remove ${cache_base}/LiveOS to force a new download or
|
||
|
troubleshoot cached image and then rerun process.
|
||
|
"
|
||
|
return 255
|
||
|
fi
|
||
|
|
||
|
|
||
|
### Stage 1 setup.
|
||
|
# Copy stage0 (which is ro) to stage1 area (rw) for modification.
|
||
|
# Unmount stage0 mounts - we're done with stage 0 at this point.
|
||
|
# Download our rpm and yum rpm packages.
|
||
|
# Force install of rpm and yum into stage1 image (dirty hack!)
|
||
|
|
||
|
echo "Stage 0 complete, building Stage 1 image...
|
||
|
This will take a couple of minutes. Patience..."
|
||
|
|
||
|
echo "Creating Stage 1 r/w copy of r/o Stage 0 squashfs image from LiveOS."
|
||
|
|
||
|
rsync -aAHS stage0/. stage1/
|
||
|
|
||
|
umount stage0
|
||
|
umount squashfs
|
||
|
|
||
|
cd stage1
|
||
|
|
||
|
# Setup stage1 image with pieces to run installs...
|
||
|
|
||
|
|
||
|
mount -o bind /dev dev
|
||
|
mount -t proc proc proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf etc/
|
||
|
|
||
|
mkdir run/install
|
||
|
|
||
|
echo "Updating Stage 1 image with full rpm and yum packages"
|
||
|
|
||
|
# Retrieve our 2 rpm packages we need to force down the throat
|
||
|
# of this LiveOS image we're camped out on. This is the beginning
|
||
|
# of the butt ugly hack. Look close or you may missing it...
|
||
|
|
||
|
rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/r/rpm-[0-9]* \
|
||
|
${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/y/yum-[0-9]* .
|
||
|
|
||
|
# And here it is...
|
||
|
# The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?!
|
||
|
chroot . rpm -ivh --nodeps rpm-* yum-*
|
||
|
# Did you catch it?
|
||
|
|
||
|
# The LiveOS image contains rpm (but not rpmdb) and yum (but not
|
||
|
# yummain.py - What the hell good does yum do with no
|
||
|
# yummain.py?!?! - Sigh...). It contains all the supporting
|
||
|
# pieces but the rpm database has not be initialized and it
|
||
|
# doesn't know all the dependences (seem to) have been met.
|
||
|
# So we do a "--nodeps" rpm install in the chrooted environment
|
||
|
# to force the installation of the full rpm and yum packages.
|
||
|
#
|
||
|
# For the purists - Yes, I know the rpm database is wildly out
|
||
|
# of whack now. That's why this is a butt ugly hack / dirty trick.
|
||
|
# But, this is just the stage1 image that we are going to discard as
|
||
|
# soon as the stage2 image is built, so we don't care. All we care
|
||
|
# is that the stage2 image ends up with all the pieces it need to
|
||
|
# run yum and rpm and that the stage2 rpm database is coherent.
|
||
|
#
|
||
|
# NOW we can really go to work!
|
||
|
|
||
|
### Stage 2 setup.
|
||
|
# Download our Fedora Release rpm packages.
|
||
|
# Install fedora-release into bootstrap to initialize fs and databases.
|
||
|
# Install rpm, and yum into bootstrap image using yum
|
||
|
|
||
|
echo "Stage 1 creation complete. Building stage 2 Installation Bootstrap"
|
||
|
|
||
|
mount -o bind ../bootstrap run/install
|
||
|
rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/f/fedora-release-20* .
|
||
|
|
||
|
# The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?!
|
||
|
chroot . rpm --root /run/install --nodeps -ivh fedora-release-*
|
||
|
|
||
|
# yum will take $basearch from host, so force the arch we want
|
||
|
sed -i "s|\$basearch|$basearch|" ./run/install/etc/yum.repos.d/*
|
||
|
|
||
|
chroot . yum -y --nogpgcheck --installroot /run/install install python rpm yum
|
||
|
|
||
|
umount run/install
|
||
|
umount proc
|
||
|
umount dev
|
||
|
|
||
|
# That's it! We should now have a viable installation BOOTSTRAP in
|
||
|
# bootstrap We'll do a yum update in that to verify and then
|
||
|
# move it to the cache location before cleaning up.
|
||
|
|
||
|
cd ../bootstrap
|
||
|
mount -o bind /dev dev
|
||
|
mount -t proc proc proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf etc/
|
||
|
|
||
|
# yum will take $basearch from host, so force the arch we want
|
||
|
sed -i "s|\$basearch|$basearch|" ./etc/yum.repos.d/*
|
||
|
|
||
|
chroot . yum -y update
|
||
|
|
||
|
RC=$?
|
||
|
|
||
|
umount proc
|
||
|
umount dev
|
||
|
|
||
|
cd ..
|
||
|
|
||
|
if [[ ${RC} != 0 ]]
|
||
|
then
|
||
|
echo "
|
||
|
Build of Installation Bootstrap failed. Temp directory
|
||
|
not removed so it can be investigated.
|
||
|
"
|
||
|
return 255
|
||
|
fi
|
||
|
|
||
|
# We know have a working run time environment in rootfs...
|
||
|
mv bootstrap ..
|
||
|
cd ..
|
||
|
rm -rf ${TMP_BOOTSTRAP_DIR}
|
||
|
|
||
|
echo "
|
||
|
Build of Installation Bootstrap complete! We now return you to your
|
||
|
normally scheduled template creation.
|
||
|
"
|
||
|
|
||
|
BOOTSTRAP=1
|
||
|
BOOTSTRAP_DIR="${cache_base}/bootstrap"
|
||
|
BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} "
|
||
|
BOOTSTRAP_INSTALL_ROOT=/run/install
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
|
||
|
fedora_bootstrap_mounts()
|
||
|
{
|
||
|
if [[ ${BOOTSTRAP} -ne 1 ]]
|
||
|
then
|
||
|
return 0
|
||
|
fi
|
||
|
|
||
|
BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} "
|
||
|
|
||
|
echo "Mounting Bootstrap mount points"
|
||
|
|
||
|
[[ -d ${BOOTSTRAP_DIR}/run/install ]] || mkdir -p ${BOOTSTRAP_DIR}/run/install
|
||
|
|
||
|
mount -o bind ${INSTALL_ROOT} ${BOOTSTRAP_DIR}/run/install
|
||
|
mount -o bind /dev ${BOOTSTRAP_DIR}/dev
|
||
|
mount -t proc proc ${BOOTSTRAP_DIR}/proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf ${BOOTSTRAP_DIR}/etc/
|
||
|
}
|
||
|
|
||
|
fedora_bootstrap_umounts()
|
||
|
{
|
||
|
if [[ ${BOOTSTRAP} -ne 1 ]]
|
||
|
then
|
||
|
return 0
|
||
|
fi
|
||
|
|
||
|
umount ${BOOTSTRAP_DIR}/proc
|
||
|
umount ${BOOTSTRAP_DIR}/dev
|
||
|
umount ${BOOTSTRAP_DIR}/run/install
|
||
|
}
|
||
|
|
||
|
|
||
|
# This is the code to create the initial roofs for Fedora. It may
|
||
|
# require a run time environment by calling the routines above...
|
||
|
|
||
|
download_fedora()
|
||
|
{
|
||
|
|
||
|
# check the mini fedora was not already downloaded
|
||
|
INSTALL_ROOT=$cache/partial
|
||
|
mkdir -p $INSTALL_ROOT
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to create '$INSTALL_ROOT' directory"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
# download a mini fedora into a cache
|
||
|
echo "Downloading fedora minimal ..."
|
||
|
|
||
|
# These will get changed if it's decided that we need a
|
||
|
# boostrap environment (can not build natively). These
|
||
|
# are the defaults for the non-boostrap (native) mode.
|
||
|
|
||
|
BOOTSTRAP_INSTALL_ROOT=${INSTALL_ROOT}
|
||
|
BOOTSTRAP_CHROOT=
|
||
|
BOOTSTRAP_DIR=
|
||
|
|
||
|
PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils fedora-release"
|
||
|
MIRRORLIST_URL="http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$release&arch=$basearch"
|
||
|
|
||
|
if [[ ${release} -lt 17 ]]
|
||
|
then
|
||
|
# The reflects the move of db_dump and db_load from db4_utils to
|
||
|
# libdb_utils in Fedora 17 and above and it's inclusion as a dep...
|
||
|
# Prior to Fedora 11, we need to explicitly include it!
|
||
|
PKG_LIST="${PKG_LIST} db4-utils"
|
||
|
fi
|
||
|
|
||
|
if [[ ${release} -ge 21 ]]
|
||
|
then
|
||
|
# Since Fedora 21, a separate fedora-repos package is needed.
|
||
|
# Before, the information was conained in fedora-release.
|
||
|
PKG_LIST="${PKG_LIST} fedora-repos"
|
||
|
fi
|
||
|
|
||
|
DOWNLOAD_OK=no
|
||
|
|
||
|
# We're splitting the old loop into two loops plus a directory retrival.
|
||
|
# First loop... Try and retrive a mirror list with retries and a slight
|
||
|
# delay between attempts...
|
||
|
for trynumber in 1 2 3 4; do
|
||
|
[ $trynumber != 1 ] && echo "Trying again..."
|
||
|
# This code is mildly "brittle" in that it assumes a certain
|
||
|
# page format and parsing HTML. I've done worse. :-P
|
||
|
MIRROR_URLS=$(curl -s -S -f "$MIRRORLIST_URL" | sed -e '/^http:/!d' -e '2,6!d')
|
||
|
if [ $? -eq 0 ] && [ -n "$MIRROR_URLS" ]
|
||
|
then
|
||
|
break
|
||
|
fi
|
||
|
|
||
|
echo "Failed to get a mirror on try $trynumber"
|
||
|
sleep 3
|
||
|
done
|
||
|
|
||
|
# This will fall through if we didn't get any URLS above
|
||
|
for MIRROR_URL in ${MIRROR_URLS}
|
||
|
do
|
||
|
if [ "$release" -gt "16" ]; then
|
||
|
RELEASE_URL="$MIRROR_URL/Packages/f"
|
||
|
else
|
||
|
RELEASE_URL="$MIRROR_URL/Packages/"
|
||
|
fi
|
||
|
|
||
|
echo "Fetching release rpm name from $RELEASE_URL..."
|
||
|
# This code is mildly "brittle" in that it assumes a certain directory
|
||
|
# page format and parsing HTML. I've done worse. :-P
|
||
|
RELEASE_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-release-${release}-/!d" -e 's/.*<a href=\"//' -e 's/\">.*//' )
|
||
|
if [ $? -ne 0 -o "${RELEASE_RPM}" = "" ]; then
|
||
|
echo "Failed to identify fedora release rpm."
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
echo "Fetching fedora release rpm from ${RELEASE_URL}/${RELEASE_RPM}......"
|
||
|
curl -L -f "${RELEASE_URL}/${RELEASE_RPM}" > ${INSTALL_ROOT}/${RELEASE_RPM}
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to download fedora release rpm ${RELEASE_RPM}."
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
# F21 and newer need fedora-repos in addition to fedora-release.
|
||
|
if [ "$release" -ge "21" ]; then
|
||
|
echo "Fetching repos rpm name from $RELEASE_URL..."
|
||
|
REPOS_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-repos-${release}-/!d" -e 's/.*<a href=\"//' -e 's/\">.*//' )
|
||
|
if [ $? -ne 0 -o "${REPOS_RPM}" = "" ]; then
|
||
|
echo "Failed to identify fedora repos rpm."
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
echo "Fetching fedora repos rpm from ${RELEASE_URL}/${REPOS_RPM}..."
|
||
|
curl -L -f "${RELEASE_URL}/${REPOS_RPM}" > ${INSTALL_ROOT}/${REPOS_RPM}
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to download fedora repos rpm ${RELEASE_RPM}."
|
||
|
continue
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
|
||
|
DOWNLOAD_OK=yes
|
||
|
break
|
||
|
done
|
||
|
|
||
|
if [ $DOWNLOAD_OK != yes ]; then
|
||
|
echo "Aborting"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
mkdir -p ${INSTALL_ROOT}/var/lib/rpm
|
||
|
|
||
|
if ! fedora_get_bootstrap
|
||
|
then
|
||
|
echo "Fedora Bootstrap setup failed"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
fedora_bootstrap_mounts
|
||
|
|
||
|
${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --initdb
|
||
|
|
||
|
# The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?!
|
||
|
${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --nodeps -ivh ${BOOTSTRAP_INSTALL_ROOT}/${RELEASE_RPM}
|
||
|
|
||
|
# F21 and newer need fedora-repos in addition to fedora-release...
|
||
|
# Note that fedora-release and fedora-system have a mutual dependency.
|
||
|
# So installing the reops package after the release package we can
|
||
|
# spare one --nodeps.
|
||
|
if [ "$release" -ge "21" ]; then
|
||
|
${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} -ivh ${BOOTSTRAP_INSTALL_ROOT}/${REPOS_RPM}
|
||
|
fi
|
||
|
|
||
|
# yum will take $basearch from host, so force the arch we want
|
||
|
sed -i "s|\$basearch|$basearch|" ${BOOTSTRAP_DIR}/${BOOTSTRAP_INSTALL_ROOT}/etc/yum.repos.d/*
|
||
|
|
||
|
${BOOTSTRAP_CHROOT}yum --installroot ${BOOTSTRAP_INSTALL_ROOT} -y --nogpgcheck install ${PKG_LIST}
|
||
|
|
||
|
RC=$?
|
||
|
|
||
|
if [[ ${BOOTSTRAP} -eq 1 ]]
|
||
|
then
|
||
|
# Here we have a bit of a sticky problem. We MIGHT have just installed
|
||
|
# this template cache using versions of yum and rpm in the bootstrap
|
||
|
# chroot that use a different database version than the target version.
|
||
|
# That can be a very big problem. Solution is to rebuild the rpmdatabase
|
||
|
# with the target database now that we are done building the cache. In the
|
||
|
# vast majority of cases, this is a do-not-care with no harm done if we
|
||
|
# didn't do it. But it catches several corner cases with older unsupported
|
||
|
# releases and it really doesn't cost us a lot of time for a one shot
|
||
|
# install that will never be done again for this rev.
|
||
|
#
|
||
|
# Thanks and appreciation to Dwight Engen and the Oracle template for the
|
||
|
# database rewrite hint!
|
||
|
|
||
|
echo "Fixing up rpm databases"
|
||
|
|
||
|
# Change to our target install directory (if we're not already
|
||
|
# there) just to simplify some of the logic to follow...
|
||
|
cd ${INSTALL_ROOT}
|
||
|
|
||
|
rm -f var/lib/rpm/__db*
|
||
|
# Programmers Note (warning):
|
||
|
#
|
||
|
# Pay careful attention to the following commands! It
|
||
|
# crosses TWO chroot boundaries linked by a bind mount!
|
||
|
# In the bootstrap case, that's the bind mount of ${INSTALL_ROOT}
|
||
|
# to the ${BOOTSTRAP_CHROOT}/run/install directory! This is
|
||
|
# a deliberate hack across that bind mount to do a database
|
||
|
# translation between two environments, neither of which may
|
||
|
# be the host environment! It's ugly and hard to follow but,
|
||
|
# if you don't understand it, don't mess with it! The pipe
|
||
|
# is in host space between the two chrooted environments!
|
||
|
# This is also why we cd'ed into the INSTALL_ROOT directory
|
||
|
# in advance of this loop, so everything is relative to the
|
||
|
# current working directory and congruent with the same working
|
||
|
# space in both chrooted environments. The output into the new
|
||
|
# db is also done in INSTALL_ROOT space but works in either host
|
||
|
# space or INSTALL_ROOT space for the mv, so we don't care. It's
|
||
|
# just not obvious what's happening in the db_dump and db_load
|
||
|
# commands...
|
||
|
#
|
||
|
for db in var/lib/rpm/* ; do
|
||
|
${BOOTSTRAP_CHROOT} db_dump ${BOOTSTRAP_INSTALL_ROOT}/$db | chroot . db_load $db.new
|
||
|
mv $db.new $db
|
||
|
done
|
||
|
# finish up by rebuilding the database...
|
||
|
# This should be redundant but we do it for completeness and
|
||
|
# any corner cases I may have missed...
|
||
|
mount -t proc proc proc
|
||
|
mount -o bind /dev dev
|
||
|
chroot . rpm --rebuilddb
|
||
|
umount dev
|
||
|
umount proc
|
||
|
fi
|
||
|
|
||
|
fedora_bootstrap_umounts
|
||
|
|
||
|
if [ ${RC} -ne 0 ]; then
|
||
|
echo "Failed to download the rootfs, aborting."
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
mv "$INSTALL_ROOT" "$cache/rootfs"
|
||
|
echo "Download complete."
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
copy_fedora()
|
||
|
{
|
||
|
|
||
|
# make a local copy of the minifedora
|
||
|
echo -n "Copying rootfs to $rootfs_path ..."
|
||
|
#cp -a $cache/rootfs-$basearch $rootfs_path || return 1
|
||
|
# i prefer rsync (no reason really)
|
||
|
mkdir -p $rootfs_path
|
||
|
rsync -SHaAX $cache/rootfs/ $rootfs_path/
|
||
|
echo
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
update_fedora()
|
||
|
{
|
||
|
mount -o bind /dev ${cache}/rootfs/dev
|
||
|
mount -t proc proc ${cache}/rootfs/proc
|
||
|
# Always make sure /etc/resolv.conf is up to date in the target!
|
||
|
cp /etc/resolv.conf ${cache}/rootfs/etc/
|
||
|
chroot ${cache}/rootfs yum -y update
|
||
|
umount ${cache}/rootfs/proc
|
||
|
umount ${cache}/rootfs/dev
|
||
|
}
|
||
|
|
||
|
install_fedora()
|
||
|
{
|
||
|
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 ... "
|
||
|
if [ ! -e "$cache/rootfs" ]; then
|
||
|
download_fedora
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to download 'fedora base'"
|
||
|
return 1
|
||
|
fi
|
||
|
else
|
||
|
echo "Cache found. Updating..."
|
||
|
update_fedora
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to update 'fedora base', continuing with last known good cache"
|
||
|
else
|
||
|
echo "Update finished"
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
echo "Copy $cache/rootfs to $rootfs_path ... "
|
||
|
copy_fedora
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to copy rootfs"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
return 0
|
||
|
) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora
|
||
|
|
||
|
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()
|
||
|
{
|
||
|
mkdir -p $config_path
|
||
|
|
||
|
grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "
|
||
|
lxc.rootfs.path = $rootfs_path
|
||
|
" >> $config_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.
|
||
|
mv $config_path/config $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.net.0.hwaddr" ]]
|
||
|
then
|
||
|
echo "${LINE}" >> $config_path/config
|
||
|
|
||
|
if [[ "${KEY}" == "lxc.net.0.link" ]]
|
||
|
then
|
||
|
echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config
|
||
|
fi
|
||
|
fi
|
||
|
done < $config_path/config.def
|
||
|
|
||
|
rm -f $config_path/config.def
|
||
|
|
||
|
if [ -e "@LXCTEMPLATECONFIG@/fedora.common.conf" ]; then
|
||
|
echo "
|
||
|
# Include common configuration
|
||
|
lxc.include = @LXCTEMPLATECONFIG@/fedora.common.conf
|
||
|
" >> $config_path/config
|
||
|
fi
|
||
|
|
||
|
# Append things which require expansion here...
|
||
|
cat <<EOF >> $config_path/config
|
||
|
lxc.arch = $arch
|
||
|
lxc.uts.name = $utsname
|
||
|
|
||
|
# When using LXC with apparmor, uncomment the next line to run unconfined:
|
||
|
#lxc.apparmor.profile = unconfined
|
||
|
|
||
|
# example simple networking setup, uncomment to enable
|
||
|
#lxc.net.0.type = $lxc_network_type
|
||
|
#lxc.net.0.flags = up
|
||
|
#lxc.net.0.link = $lxc_network_link
|
||
|
#lxc.net.0.name = eth0
|
||
|
# Additional example for veth network type
|
||
|
# static MAC address,
|
||
|
#lxc.net.0.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.net.0.veth.pair = v-$name-e0
|
||
|
|
||
|
EOF
|
||
|
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "Failed to add configuration"
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
clean()
|
||
|
{
|
||
|
|
||
|
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 for Fedora-$release..."
|
||
|
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
|
||
|
exit 0
|
||
|
) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora
|
||
|
}
|
||
|
|
||
|
usage()
|
||
|
{
|
||
|
cat <<EOF
|
||
|
usage:
|
||
|
$1 -n|--name=<container_name>
|
||
|
[-p|--path=<path>] [-c|--clean] [-R|--release=<Fedora_release>]
|
||
|
[--fqdn=<network name of container>] [-a|--arch=<arch of the container>]
|
||
|
[--mask-tmp]
|
||
|
[-h|--help]
|
||
|
Mandatory args:
|
||
|
-n,--name container name, used to as an identifier for that container
|
||
|
Optional args:
|
||
|
-p,--path path to where the container will be created,
|
||
|
defaults to @LXCPATH@.
|
||
|
--rootfs path for actual rootfs.
|
||
|
-c,--clean clean the cache
|
||
|
-R,--release Fedora release for the new container.
|
||
|
Defaults to host's release if the host is Fedora.
|
||
|
--fqdn fully qualified domain name (FQDN) for DNS and system naming
|
||
|
-a,--arch Define what arch the container will be [i686,x86_64]
|
||
|
--mask-tmp Prevent systemd from over-mounting /tmp with tmpfs.
|
||
|
-h,--help print this help
|
||
|
EOF
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,arch:,fqdn:,mask-tmp -- "$@")
|
||
|
if [ $? -ne 0 ]; then
|
||
|
usage $(basename $0)
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
arch=$(uname -m)
|
||
|
masktmp=0
|
||
|
|
||
|
eval set -- "$options"
|
||
|
while true
|
||
|
do
|
||
|
case "$1" in
|
||
|
-h|--help) usage $0 && exit 0;;
|
||
|
-p|--path) path=$2; shift 2;;
|
||
|
--rootfs) rootfs_path=$2; shift 2;;
|
||
|
-n|--name) name=$2; shift 2;;
|
||
|
-c|--clean) clean=1; shift 1;;
|
||
|
-R|--release) release=$2; shift 2;;
|
||
|
-a|--arch) newarch=$2; shift 2;;
|
||
|
--fqdn) utsname=$2; shift 2;;
|
||
|
--mask-tmp) masktmp=1; shift 1;;
|
||
|
--) shift 1; break ;;
|
||
|
*) break ;;
|
||
|
esac
|
||
|
done
|
||
|
|
||
|
if [ ! -z "$clean" -a -z "$path" ]; then
|
||
|
clean || exit 1
|
||
|
exit 0
|
||
|
fi
|
||
|
|
||
|
basearch=${arch}
|
||
|
# Map a few architectures to their generic Fedora repository archs.
|
||
|
# The two ARM archs are a bit of a guesstimate for the v5 and v6
|
||
|
# archs. V6 should have hardware floating point (Rasberry Pi).
|
||
|
# The "arm" arch is safer (no hardware floating point). So
|
||
|
# there may be cases where we "get it wrong" for some v6 other
|
||
|
# than RPi.
|
||
|
case "$arch" in
|
||
|
i686) basearch=i386 ;;
|
||
|
armv3l|armv4l|armv5l) basearch=arm ;;
|
||
|
armv6l|armv7l|armv8l) basearch=armhfp ;;
|
||
|
*) ;;
|
||
|
esac
|
||
|
|
||
|
mirrorurl="archives.fedoraproject.org::fedora-archive"
|
||
|
case "$basearch" in
|
||
|
ppc64|s390x) mirrorurl="archives.fedoraproject.org::fedora-secondary" ;;
|
||
|
*) ;;
|
||
|
esac
|
||
|
|
||
|
# Somebody wants to specify an arch. This is very limited case.
|
||
|
# i386/i586/i686 on i386/x86_64
|
||
|
# - or -
|
||
|
# x86_64 on x86_64
|
||
|
if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ]
|
||
|
then
|
||
|
case "${newarch}" in
|
||
|
i386|i586|i686)
|
||
|
if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ]
|
||
|
then
|
||
|
# Make the arch a generic x86 32 bit...
|
||
|
arch=${newarch}
|
||
|
basearch=i386
|
||
|
else
|
||
|
basearch=bad
|
||
|
fi
|
||
|
;;
|
||
|
*)
|
||
|
basearch=bad
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
if [ "${basearch}" = "bad" ]
|
||
|
then
|
||
|
echo "You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!"
|
||
|
exit 1
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# Allow the cache base to be set by environment variable
|
||
|
cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/fedora/$basearch
|
||
|
|
||
|
# Let's do something better for the initial root password.
|
||
|
# It's not perfect but it will defeat common scanning brute force
|
||
|
# attacks in the case where ssh is exposed. It will also be set to
|
||
|
# expired, forcing the user to change it at first login.
|
||
|
if [ "${root_password}" = "" ]
|
||
|
then
|
||
|
root_password=Root-${name}-${RANDOM}
|
||
|
else
|
||
|
# If it's got a ding in it, try and expand it!
|
||
|
if [ $(expr "${root_password}" : '.*$.') != 0 ]
|
||
|
then
|
||
|
root_password=$(eval echo "${root_password}")
|
||
|
fi
|
||
|
|
||
|
# If it has more than 3 consecutive X's in it, feed it
|
||
|
# through mktemp as a template.
|
||
|
if [ $(expr "${root_password}" : '.*XXXX') != 0 ]
|
||
|
then
|
||
|
root_password=$(mktemp -u ${root_password})
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [ -z "${utsname}" ]; then
|
||
|
utsname=${name}
|
||
|
fi
|
||
|
|
||
|
# This follows a standard "resolver" convention that an FQDN must have
|
||
|
# at least two dots or it is considered a local relative host name.
|
||
|
# If it doesn't, append the dns domain name of the host system.
|
||
|
#
|
||
|
# This changes one significant behavior when running
|
||
|
# "lxc_create -n Container_Name" without using the
|
||
|
# --fqdn option.
|
||
|
#
|
||
|
# Old behavior:
|
||
|
# utsname and hostname = Container_Name
|
||
|
# New behavior:
|
||
|
# utsname and hostname = Container_Name.Domain_Name
|
||
|
|
||
|
if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
|
||
|
if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then
|
||
|
utsname=${utsname}.$(dnsdomainname)
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
needed_pkgs=""
|
||
|
|
||
|
type curl >/dev/null 2>&1
|
||
|
if [ $? -ne 0 ]; then
|
||
|
needed_pkgs="curl $needed_pkgs"
|
||
|
fi
|
||
|
type openssl >/dev/null 2>&1
|
||
|
if [ $? -ne 0 ]; then
|
||
|
needed_pkgs="openssl $needed_pkgs"
|
||
|
fi
|
||
|
|
||
|
if [ -n "$needed_pkgs" ]; then
|
||
|
echo "Missing commands: $needed_pkgs"
|
||
|
echo "Please install these using \"sudo yum install $needed_pkgs\""
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
if [ -z "$path" ]; then
|
||
|
path=$default_path/$name
|
||
|
fi
|
||
|
|
||
|
if [ -z "$release" ]; then
|
||
|
if [ "$is_fedora" -a "$fedora_host_ver" ]; then
|
||
|
release=$fedora_host_ver
|
||
|
else
|
||
|
echo "This is not a fedora host and release missing, defaulting to 22 use -R|--release to specify release"
|
||
|
release=22
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [ "$(id -u)" != "0" ]; then
|
||
|
echo "This script should be run as 'root'"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
if [ -z "$rootfs_path" ]; then
|
||
|
rootfs_path=$path/rootfs
|
||
|
# check for 'lxc.rootfs.path' passed in through default config by lxc-create
|
||
|
if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then
|
||
|
rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \
|
||
|
-e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config)
|
||
|
fi
|
||
|
fi
|
||
|
config_path=$path
|
||
|
cache=$cache_base/$release
|
||
|
|
||
|
revert()
|
||
|
{
|
||
|
echo "Interrupted, so cleaning up"
|
||
|
lxc-destroy -n $name
|
||
|
# maybe was interrupted before copy config
|
||
|
rm -rf $path
|
||
|
echo "exiting..."
|
||
|
exit 1
|
||
|
}
|
||
|
|
||
|
trap revert SIGHUP SIGINT SIGTERM
|
||
|
|
||
|
copy_configuration
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "failed write configuration file"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
install_fedora
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "failed to install fedora"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
configure_fedora
|
||
|
if [ $? -ne 0 ]; then
|
||
|
echo "failed to configure fedora for a container"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
# If the systemd configuration directory exists - set it up for what we need.
|
||
|
if [ -d ${rootfs_path}/etc/systemd/system ]
|
||
|
then
|
||
|
configure_fedora_systemd
|
||
|
fi
|
||
|
|
||
|
# This configuration (rc.sysinit) is not inconsistent with the systemd stuff
|
||
|
# above and may actually coexist on some upgraded systems. Let's just make
|
||
|
# sure that, if it exists, we update this file, even if it's not used...
|
||
|
if [ -f ${rootfs_path}/etc/rc.sysinit ]
|
||
|
then
|
||
|
configure_fedora_init
|
||
|
fi
|
||
|
|
||
|
if [ ! -z "$clean" ]; then
|
||
|
clean || exit 1
|
||
|
exit 0
|
||
|
fi
|
||
|
echo "
|
||
|
Container rootfs and config have been created.
|
||
|
Edit the config file to check/enable networking setup.
|
||
|
"
|
||
|
|
||
|
if [[ -d ${cache_base}/bootstrap ]]
|
||
|
then
|
||
|
echo "You have successfully built a Fedora container and cache. This cache may
|
||
|
be used to create future containers of various revisions. The directory
|
||
|
${cache_base}/bootstrap contains a bootstrap
|
||
|
which may no longer needed and can be removed.
|
||
|
"
|
||
|
fi
|
||
|
|
||
|
if [[ -e ${cache_base}/LiveOS ]]
|
||
|
then
|
||
|
echo "A LiveOS directory exists at ${cache_base}/LiveOS.
|
||
|
This is only used in the creation of the bootstrap run-time-environment
|
||
|
and may be removed.
|
||
|
"
|
||
|
fi
|
||
|
|
||
|
if [ ${root_display_password} = "yes" ]
|
||
|
then
|
||
|
echo "The temporary password for root is: '$root_password'
|
||
|
|
||
|
You may want to note that password down before starting the container.
|
||
|
"
|
||
|
fi
|
||
|
|
||
|
if [ ${root_store_password} = "yes" ]
|
||
|
then
|
||
|
echo "The temporary root password is stored in:
|
||
|
|
||
|
'${config_path}/tmp_root_pass'
|
||
|
"
|
||
|
fi
|
||
|
|
||
|
if [ ${root_prompt_password} = "yes" ]
|
||
|
then
|
||
|
echo "Invoking the passwd command in the container to set the root password.
|
||
|
|
||
|
chroot ${rootfs_path} passwd
|
||
|
"
|
||
|
chroot ${rootfs_path} passwd
|
||
|
else
|
||
|
if [ ${root_expire_password} = "yes" ]
|
||
|
then
|
||
|
if ( mountpoint -q -- "${rootfs_path}" )
|
||
|
then
|
||
|
echo "To reset the root password, you can do:
|
||
|
|
||
|
lxc-start -n ${name}
|
||
|
lxc-attach -n ${name} -- passwd
|
||
|
lxc-stop -n ${name}
|
||
|
"
|
||
|
else
|
||
|
echo "
|
||
|
The root password is set up as "expired" and will require it to be changed
|
||
|
at first login, which you should do as soon as possible. If you lose the
|
||
|
root password or wish to change it without starting the container, you
|
||
|
can change it from the host by running the following command (which will
|
||
|
also reset the expired flag):
|
||
|
|
||
|
chroot ${rootfs_path} passwd
|
||
|
"
|
||
|
fi
|
||
|
fi
|
||
|
fi
|