#! /bin/bash -e
function error( ) {
echo " **** ERROR aborted. Status of last operation: $? " 1>& 2
exit 1
}
trap error HUP INT QUIT TERM ERR
# log LEVEL "Text"
# LEVEL: 0=normal 1=verbose 2=more-verbose
function echon( ) {
if test " $1 " = = "-n" ; then
shift
echo -n " $* "
else
echo " $* "
fi
}
function log( ) {
l = $1
shift
if test " $1 " = = "-n" ; then
shift
n = "-n"
else
n = ""
fi
if test $q -eq 1; then
return
elif test $l -eq 0 -o $l -eq 1 -a $v -eq 1 -o $l -eq 2 -a $vv -eq 1; then
if test $l -gt 0; then
echon $n " $l → $* "
else
echon $n " $* "
fi
fi
}
################################################################################
# Arguments
################################################################################
q = 0
v = 0
vv = 0
while test $# -gt 0; do
case " $1 " in
( -q) q = 1; v = 0; vv = 0; ;
( -v) q = 0; v = 1; vv = 0; ;
( -vv) q = 0; v = 1; vv = 1; ;
( -h| --help)
echo " SYNOPSIS: $0 [-q|-v|-vv] "
echo " -q quiet"
echo " -v verbose"
echo " -vv more verbose"
exit 0; ;
esac
shift
done
################################################################################
################################################################################
# CONFIGURATION you can overwrite any of the variables when you start the script
# e.g. HW="x86_64 noarch" VERSION=11.4 sudo susebootstrap.sh
# Setup OS sources
#
# OS: name of the OS, used for chroot pathname
OS = ${ OS :- "opensuse" }
log 2 " OS= $OS "
#
# VERSION: see e.g. http://download.opensuse.org/distribution/
VERSION = ${ VERSION :- $(
case " $OS " in
opensuse) echo "openSUSE-current" ; ;
fedora) echo " $( wget -O- http://mirror.switch.ch/ftp/mirror/fedora/linux/releases 2>/dev/null | html2 | sed -n 's,.*/a/@href=\([0-9][0-9]*\)/,\1,gp' | uniq | tail -1) " ; ;
centos) echo " $( wget -O- http://mirror.switch.ch/ftp/mirror/centos 2>/dev/null | sed -n 's,.*href="\([0-9][0-9]*\)/".*,\1,pg' | uniq | tail -1) "
esac ) }
log 2 " VERSION= $VERSION "
#
# HW: list of hardware subdirs in openSUSE, e.g. "x86_64 noarch"
HW = ${ HW :- $(
case " $OS " in
opensuse) echo "i586 noarch" ; ;
fedora) echo "i686 noarch" ; ;
centos) echo "x86_64 noarch" ; ;
esac ) }
log 2 " HW= $HW "
#
# BASEARCH: Base architecture, e.g. for i686 and i566 BASEARCH is i386
BASEARCH = ${ BASEARCH :- $(
case " ${ HW %% * } " in
i?86) echo "i386" ; ;
*) echo " ${ HW %% * } " ; ;
esac ) }
log 2 " BASEARCH= $BASEARCH "
#
# URL: where to download packages from
URL = ${ URL :- $(
case " $OS " in
opensuse)
case " $VERSION " in
( tumbleweed) echo "http://download.opensuse.org/tumbleweed/repo/oss/suse" ; ;
( *) if wget -qO- http://download.opensuse.org/distribution/${ VERSION } ; then
echo " http://download.opensuse.org/distribution/ ${ VERSION } /repo/oss/suse "
else
echo " http://download.opensuse.org/distribution/leap/ ${ VERSION } /repo/oss/suse "
fi ; ;
esac ; ;
fedora)
if test " $VERSION " -gt 20; then
echo " http://mirror.switch.ch/ftp/mirror/fedora/linux/releases/ ${ VERSION } /Server/ ${ BASEARCH } /os "
else
echo " http://mirror.switch.ch/ftp/mirror/fedora/linux/releases/ ${ VERSION } /Fedora/ ${ BASEARCH } /os "
fi ; ;
centos) echo " http://mirror.switch.ch/ftp/mirror/centos/ ${ VERSION } /os/ ${ BASEARCH } " ; ;
esac ) }
log 2 " URL= $URL "
# Setup installation pathes
#
# BASE: Base path to the chroots on your system
BASE = ${ BASE :- /var/chroot }
log 2 " BASE= $BASE "
#
# SCHROOTCONF: Configurationfile for schroot
SCHROOTCONF = ${ SCHROOTCONF :- ${ OS } - ${ VERSION } _ ${ BASEARCH } }
log 2 " SCHROOTCONF= $SCHROOTCONF "
#
# ROOT: Full path to the chroot directory
ROOT = ${ ROOT :- ${ BASE } / ${ SCHROOTCONF } }
log 2 " ROOT= $ROOT "
#
# PACKAGES: Where to store package lists path+filename-prefix
PACKAGES = ${ PACKAGES :- ${ ROOT } /var/tmp/packagelist }
log 2 " PACKAGES= $PACKAGES "
#
# PKGDIR: Where to store downloaded packages, relative to ${ROOT}
PKGDIR = ${ PKGDIR :- /var/tmp/packages }
log 2 " PKGDIR= $PKGDIR "
#
# SCHROOTUSER: Name of user or list of user that will use schroot
SCHROOTUSER = ${ SCHROOTUSER :- ${ USER } }
log 2 " SCHROOTUSER= $SCHROOTUSER "
#
# CHROOT: Command for starting chroot, more possibilities, see below
CHROOT = " schroot -c ${ SCHROOTCONF } -d / -u root "
log 2 " CHROOT= $CHROOT "
#
# YUM: Set Fedora / CentOS repository tool
if test " $OS " = "fedora" && test " ${ VERSION } " -gt "21" ; then
YUM = ${ YUM :- "dnf" }
else
YUM = ${ YUM :- "yum" }
fi
log 2 " YUM= $YUM "
#
# PKGS: Base packages to install for minimal system
PKGS = ${ PKGS :- $(
case " $OS " in
opensuse) echo "bash rpm zypper" ; ;
fedora)
echo "bash rpm fedora-release"
if test " ${ VERSION } " -gt "21" ; then
echo "dnf"
else
echo "yum"
fi
; ;
centos)
echo "bash rpm yum centos-release"
; ;
esac ) }
log 2 " PKGS= $PKGS "
#
#
if [ -z " ${ CHROOT } " ] ; then
echo "**** ERROR: Variable CHROOT must be set." 1>& 2
exit 1
fi
# ADDITIONAL_DEFINITIONS: Add additional definitions to /etc/schroot/chroot.d
# Example: ADDITIONAL_DEFINITIONS="setup.fstab=jenkins/fstab"
ADDITIONAL_DEFINITIONS = ${ ADDITIONAL_DEFINITIONS :- }
log 2 " ADDITIONAL_DEFINITIONS= $ADDITIONAL_DEFINITIONS "
################################################################################
################################################################################
# Main Part
################################################################################
# 1. Download basic packages
# 2. Unpack them into a chroot-path
# 3. Tweak the minimal system
# 4. Setup basic /dev
# 5. Properly install all the packages within chroot
# 6. configure basic system
# 7. Done, now chroot to the path and install whatever you need
################################################################################
log 0 " Install ${ OS } ${ VERSION } for ${ BASEARCH } in ${ ROOT } "
################################################################################
# Preparation of path for chroot and packages
test -d ${ ROOT } ${ PKGDIR } || mkdir -p ${ ROOT } ${ PKGDIR }
################################################################################
################################################################################
# 0. Setup schroot configuration
cat > /etc/schroot/chroot.d/${ SCHROOTCONF } <<EOF
[ ${ SCHROOTCONF } ]
description = ${ OS } -${ VERSION } ${ BASEARCH }
directory = ${ ROOT }
users = ${ SCHROOTUSER }
root-groups= root
root-users= ${ SCHROOTUSER }
type = directory
${ ADDITIONAL_DEFINITIONS }
EOF
################################################################################
################################################################################
# 0. Search for dependencies to install
# requires: rpm xml2 schroot
log 0 " ... read repository meta data"
test -d ${ PACKAGES } || mkdir -p ${ PACKAGES }
test -f ${ PACKAGES } /repomd.xml || \
wget -q -O ${ PACKAGES } /repomd.xml ${ URL } /repodata/repomd.xml
datapath = $( xml2 < ${ PACKAGES } /repomd.xml \
| awk -F= ' BEGIN { x = 0}
x = = 1 && $1 = = "/repomd/data/location/@href" { print $2 ; exit}
$1 = = "/repomd/data/@type" && $2 = = "primary" { x = 1} ' )
dataname = ${ PACKAGES } /${ datapath ##*/ }
log 2 " dataname= ${ dataname } from ${ URL } / ${ datapath } "
log 0 " ... read repository details"
test -f ${ dataname } || \
wget -q -O ${ dataname } ${ URL } /${ datapath }
log 0 " ... recursively extract dependencies"
oldPKGS = ""
if [ ! -f ${ PACKAGES } /packages ] ; then
test ! -f ${ PACKAGES } /install || rm ${ PACKAGES } /install
while [ " $PKGS " != " $oldPKGS " ] ; do
oldPKGS = $PKGS
log 0 " ... extract dependencies: scan $( echo " $PKGS " | wc -w) " \
"packages"
log 0 " ... extract dependencies: find requirements"
PKGS = ${ PKGS % }
chkinstpkgs = $( echo $PKGS | sed -e 's/[^-A-Za-z0-9_ ]/./g' | tr ' ' '|' )
deps = $(
(
echo " $PKGS " | tr ' ' '\n' ;
zcat $dataname | xml2 | awk -F= '
BEGIN {
x = 0
}
x = = 1 &&
( $1 = = "/metadata/package/format/rpm:requires/rpm:entry/@name" ||
( $1 = = "/metadata/package/format/rpm:provides/rpm:entry/@name" &&
$2 ~/^( '${chkinstpkgs}' ) $/ ) ) {
print $2
}
$1 = = "/metadata/package/name" && $2 ~/^( '${chkinstpkgs}' ) $/ {
x = 1
}
$1 = = "/metadata/package/name" && $2 !~/^( '${chkinstpkgs}' ) $/ {
x = 0
}
$1 = = "/metadata/package/arch" && $2 !~/^( '${HW// /|}' ) $/ {
x = 0
} '
) | \
sed -e 's/[^-A-Za-z0-9_]/./g' | \
sort | uniq | tr '\n ' '||' | \
sed -e 's/^|//' -e 's/|$//'
)
log 0 " ... extract dependencies: extend packagelist by providers"
PKGS = $(
zcat $dataname | xml2 | awk -F= '
BEGIN {
matcher = " ^(' ${ deps } ') $" #(|\\(.*\\))$"
}
$1 = = "/metadata/package/name" && $2 ~matcher {
name = $2
tmp = $2
gsub( /[ ^-A-Za-z0-9_] /, "." , tmp)
sub( "\\|" tmp "\\|" , "|" , matcher)
sub( "^" tmp ")\\|" , "" , matcher)
sub( "\\|" tmp " $" , "" , matcher)
print name
}
$1 = = "/metadata/package/name" && $2 !~matcher {
name = $2
}
( $1 = = "/metadata/package/format/rpm:provides/rpm:entry/@name" ||
$1 = = "/metadata/package/format/file" ) &&
$2 ~matcher {
tmp = $2
gsub( /[ ^-A-Za-z0-9_] /, "." , tmp)
sub( "\\|" tmp "\\|" , "|" , matcher)
sub( "^" tmp ")\\|" , "" , matcher)
sub( "\\|" tmp " $" , "" , matcher)
print name
}
END {
} ' | sort | uniq | tr ' \n ' ' '
)
( ( ++pkgcnt) ) ###
log 2 " temp packages: $PKGS "
log 2 " temp dependencies: $deps "
done
else
log 2 "packages already evaluated"
PKGS = $( <${ PACKAGES } /packages)
fi
PKGS = ${ PKGS % }
echo ${ PKGS } > ${ PACKAGES } /packages
log 1 " final packages: $PKGS "
log 0 " ... extract dependencies: find files to download and install"
if [ -f ${ PACKAGES } /install ] ; then
log 2 "installations already evaluated"
INSTALL = $( <${ PACKAGES } /install)
else
chkinstpkgs = $( echo $PKGS | sed -e 's/[^-A-Za-z0-9_ ]/./g' | tr ' ' '|' )
INSTALL = $(
zcat $dataname | xml2 | awk -F= '
BEGIN {
x = 0
}
x = = 1 && $1 = = "/metadata/package/location/@href" {
print $2
}
$1 = = "/metadata/package/name" && $2 ~/^( '${chkinstpkgs}' ) $/ {
x = 1
}
$1 = = "/metadata/package/name" && $2 !~/^( '${chkinstpkgs}' ) $/ {
x = 0
}
$1 = = "/metadata/package/arch" && $2 !~/^( '${HW// /|}' ) $/ {
x = 0
} ' | sort | uniq | tr ' \n ' ' '
)
echo $INSTALL > ${ PACKAGES } /install
fi
log 1 " installations: ${ INSTALL } "
log 0 " ... download and install $( echo ${ INSTALL } | wc -w) files "
################################################################################
################################################################################
# 1. Download basic packages
# We know the logical package names, find matching package files
# on server
# INST: Will be filled with a list of package files to download and
# install
for p in $INSTALL ; do # for all packages to install
log 0 " ... download $p "
test -f ${ ROOT } ${ PKGDIR } /${ p ##*/ } || \
wget -q -O ${ ROOT } ${ PKGDIR } /${ p ##*/ } ${ URL } /$p
INST = " $INST ${ p ##*/ } "
done
################################################################################
################################################################################
# 2. Unpack them into a chroot-path
# Extract all downloaded packages in the chroot directory
cd ${ ROOT }
for p in $INST ; do # for all downloaded packages
# just extract the file structure withing the package without running
# pre-/post-install scripts
# this is necessary for a minimal basic system to chroot in
log 0 " ... unpack $p "
rpm2cpio ${ ROOT } ${ PKGDIR } /$p | cpio -dim --quiet 2> /dev/null
done
cd - > /dev/null
################################################################################
################################################################################
# 3. Tweak the minimal system
log 0 " ... setup system"
#
# Do some system setup tweaks
#
# Create minimal /etc/passwd and /etc/group with user "root"
test -e ${ ROOT } /etc/passwd || \
echo 'root:x:0:0:root:/root:/bin/bash' > ${ ROOT } /etc/passwd
test -e ${ ROOT } /etc/group || \
( \
echo 'root:x:0:' ; \
echo 'utmp:x:0:' \
) > ${ ROOT } /etc/group
test -e ${ ROOT } /etc/shadow || touch ${ ROOT } /etc/shadow
test -e ${ ROOT } /etc/gshadow || touch ${ ROOT } /etc/gshadow
test -e ${ ROOT } /etc/fstab || touch ${ ROOT } /etc/fstab
#
# copy /etc/resolv.conf into chroot to be able to access internet
test -e ${ ROOT } /etc/resolv.conf || \
cp /etc/resolv.conf ${ ROOT } /etc/
################################################################################
# 4. Setup basic /dev
log 0 " ... setup prepare minimal /dev with /dev/null and /dev/zero"
if [ -n " ${ CHROOT //*schroot*/ } " ] ; then
# only if chroot is not schroot, schroot creates /dev
test -d ${ ROOT } /dev || ${ CHROOT } mkdir ${ ROOT } /dev
test -e ${ ROOT } /dev/null || \
( \
${ CHROOT } mknod -m 666 /dev/null c 1 3 && \
${ CHROOT } chown root:root /dev/null
)
test -e ${ ROOT } /dev/zero || \
( \
${ CHROOT } mknod -m 666 /dev/zero c 1 5 && \
${ CHROOT } chown root:root /dev/zero \
)
fi
################################################################################
################################################################################
# 5. Properly install all the package packages within chroot
# now chroot into the new system and properly install all downloaded packages,
# this executes triggers and maintains package database
log 0 " ... install all packages"
all_success = 1
if ! ${ CHROOT } -- rpm -i --nodeps --force ${ PKGDIR } /*.rpm > /dev/null 2> /dev/null; then
for p in ${ ROOT } ${ PKGDIR } /*.rpm; do
log 0 -n " ... install ${ p # ${ ROOT } } "
if ${ CHROOT } -- rpm -i --nodeps --force ${ p # ${ ROOT } } \
> /dev/null 2> /dev/null; then
log 0 " success"
else
all_success = 0
log 0 " error"
fi
done
fi
################################################################################
################################################################################
# System Dependent Part
################################################################################
case " $OS " in
( opensuse)
########################################################################
# 6. Add an archive source to package manager
# add installation source as repository
log 0 " ... setup package manager"
${ CHROOT } -- zypper -q ar ${ URL } repo-oss
#
# setup hardware architecture in zypper, necessary different from host
${ CHROOT } -- perl -pi \
-e 's#^\#? *arch = .*$#arch = ' ${ HW %% * } '#g' \
/etc/zypp/zypp.conf
#
# basic initialisation, first update - bug with key in 12.2
log 0 " ... basic initialisation of package manager"
${ CHROOT } -- zypper -q -n --gpg-auto-import-keys update || \
${ CHROOT } -- zypper -q -n --no-gpg-checks update
${ CHROOT } -- zypper -q -n --gpg-auto-import-keys dist-upgrade || \
${ CHROOT } -- zypper -q -n --no-gpg-checks dist-upgrade
#
# install some basic packages
log 0 " ... install more basic packages"
${ CHROOT } -- zypper -q -n --gpg-auto-import-keys \
install openSUSE-release
########################################################################
; ;
( fedora)
########################################################################
# 6. Install basic packages
log 0 " ... setup package manager using $YUM "
test -d ${ ROOT } /etc/rpm || mkdir -p ${ ROOT } /etc/rpm
sed -i 's,$basearch,' ${ BASEARCH } ',g' \
$( grep -lr '$basearch' ${ ROOT } /etc/yum.repos.d)
echo " ${ HW %% * } - ${ OS } -linux " > ${ ROOT } /etc/rpm/platform
if test " ${ VERSION } " -gt "16" ; then # hack for fedora 17 and above
log 0 " ... convert fedora 17+ filesystem"
${ 0 %/* } /convertfs-fedora-17.sh ${ ROOT }
fi
log 0 " ... upgrade base system"
${ CHROOT } -- ${ YUM } -y -q upgrade || \
( ${ CHROOT } -- bash -c 'sed -i "s|^#baseurl|baseurl| ; s|^mirrorlist|#mirrorlist|" /etc/yum.repos.d/*' && \
${ CHROOT } -- ${ YUM } -y -q upgrade )
log 0 " ... install more basic packages"
for g in "Base" "Minimal Install" ; do
if ${ CHROOT } -- ${ YUM } -y -q grouplist | grep -q " $g " ; then
log 2 " install $g "
case " $VERSION " in
( *-Fedora)
${ CHROOT } -- ${ YUM } -y -q groups mark install " $g "
; ;
( *)
${ CHROOT } -- ${ YUM } -y -q groupupdate " $g "
; ;
esac
fi
done
########################################################################
; ;
( centos)
########################################################################
# 6. Install basic packages
log 0 " ... setup package manager using $YUM "
${ CHROOT } -- ln -s /sbin/chkconfig /usr/sbin/chkconfig
test -d ${ ROOT } /etc/rpm || mkdir -p ${ ROOT } /etc/rpm
echo " ${ HW %% * } - ${ OS } -linux " > ${ ROOT } /etc/rpm/platform
if test " ${ VERSION } " -gt "6" ; then # hack for fedora 17 and above
log 0 " ... convert fedora 17+ filesystem"
${ 0 %/* } /convertfs-fedora-17.sh ${ ROOT }
fi
log 0 " ... upgrade base system"
${ CHROOT } -- ${ YUM } -y -q upgrade || \
( ${ CHROOT } -- bash -c 'sed -i "s|^#baseurl|baseurl| ; s|^mirrorlist|#mirrorlist|" /etc/yum.repos.d/*' && \
${ CHROOT } -- ${ YUM } -y -q upgrade )
log 0 " ... install more basic packages"
for g in "Base" "Minimal Install" ; do
if ${ CHROOT } -- ${ YUM } -y -q grouplist | grep -q " $g " ; then
log 2 " install $g "
${ CHROOT } -- ${ YUM } -y -q groups mark install " $g "
${ CHROOT } -- ${ YUM } -y -q upgrade
log 2 " update $g "
${ CHROOT } -- ${ YUM } -y -q groupupdate " $g "
fi
done
########################################################################
; ;
esac
################################################################################
################################################################################
# 7. Done - use your chroot to work or install more packages
log 0 "**** SUCCESS done."
log 0
log 0 "########################################################################"
log 0 "Use your new chroot:"
log 0 " > ${ CHROOT } -- bash "
log 0 " > [... work in your chroot ...]"
log 0 " > exit"
log 0 "########################################################################"
log 0 " ... cleanup chroot"
################################################################################