[lxc-devel] [PATCH] add yum plugin to repatch rootfs on yum update

Dwight Engen dwight.engen at oracle.com
Thu Mar 27 20:46:38 UTC 2014


oracle-template: Split patching rootfs vs one time setup into separate
shell functions so the template can be run with --patch.

oracle-template: Update to install the yum plugin and itself (as lxc-patch)
into a container. The plugin just runs lxc-patch --patch <path> so it is
fairly generic, but in this case it is running a copy of the template inside
the container.

Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
---
 config/Makefile.am      |   2 +-
 config/yum/Makefile.am  |   6 ++
 config/yum/lxc-patch.py |  56 +++++++++++++
 configure.ac            |   1 +
 templates/lxc-oracle.in | 205 +++++++++++++++++++++++++++++++-----------------
 5 files changed, 197 insertions(+), 73 deletions(-)
 create mode 100644 config/yum/Makefile.am
 create mode 100644 config/yum/lxc-patch.py

diff --git a/config/Makefile.am b/config/Makefile.am
index 9515965..e40f842 100644
--- a/config/Makefile.am
+++ b/config/Makefile.am
@@ -1 +1 @@
-SUBDIRS = apparmor bash etc init templates
+SUBDIRS = apparmor bash etc init templates yum
diff --git a/config/yum/Makefile.am b/config/yum/Makefile.am
new file mode 100644
index 0000000..fb9c3bd
--- /dev/null
+++ b/config/yum/Makefile.am
@@ -0,0 +1,6 @@
+yumpluginsdir=$(datadir)/lxc
+
+yumplugins_SCRIPTS = \
+	lxc-patch.py
+
+EXTRA_DIST = $(yumplugins_SCRIPTS)
diff --git a/config/yum/lxc-patch.py b/config/yum/lxc-patch.py
new file mode 100644
index 0000000..94854f4
--- /dev/null
+++ b/config/yum/lxc-patch.py
@@ -0,0 +1,56 @@
+# Yum plugin to re-patch container rootfs after a yum update is done
+#
+# Copyright (C) 2012 Oracle
+#
+# Authors:
+# Dwight Engen <dwight.engen at oracle.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+import os
+from fnmatch import fnmatch
+from yum.plugins import TYPE_INTERACTIVE
+from yum.plugins import PluginYumExit
+
+requires_api_version = '2.0'
+plugin_type = (TYPE_INTERACTIVE,)
+
+def posttrans_hook(conduit):
+    pkgs = []
+    patch_required = False
+
+    # If we aren't root, we can't have updated anything
+    if os.geteuid():
+        return
+
+    # See what packages have files that were patched
+    confpkgs = conduit.confString('main', 'packages')
+    if not confpkgs:
+        return
+
+    tmp = confpkgs.split(",")
+    for confpkg in tmp:
+        pkgs.append(confpkg.strip())
+
+    conduit.info(2, "lxc-patch: checking if updated pkgs need patching...")
+    ts = conduit.getTsInfo()
+    for tsmem in ts.getMembers():
+        for pkg in pkgs:
+            if fnmatch(pkg, tsmem.po.name):
+                patch_required = True
+    if patch_required:
+        conduit.info(2, "lxc-patch: patching container...")
+        os.spawnlp(os.P_WAIT, "lxc-patch", "lxc-patch", "--patch", "/")
diff --git a/configure.ac b/configure.ac
index 20d409f..ee49386 100644
--- a/configure.ac
+++ b/configure.ac
@@ -587,6 +587,7 @@ AC_CONFIG_FILES([
 	config/templates/ubuntu.common.conf
 	config/templates/ubuntu.lucid.conf
 	config/templates/ubuntu.userns.conf
+	config/yum/Makefile
 
 	doc/Makefile
 	doc/api/Makefile
diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in
index a82b81f..92361f8 100644
--- a/templates/lxc-oracle.in
+++ b/templates/lxc-oracle.in
@@ -69,9 +69,44 @@ can_chcon()
 }
 
 # fix up the container_rootfs
-container_rootfs_configure()
+container_rootfs_patch()
 {
-    echo "Configuring container for Oracle Linux $container_release_major.$container_release_minor"
+    echo "Patching container rootfs $container_rootfs for Oracle Linux $container_release_major.$container_release_minor"
+
+    # copy ourself into the container to be used to --patch the rootfs when
+    # yum update on certain packages is done. we do this here instead of in
+    # container_rootfs_configure() in case the patching done in this function
+    # is updated in the future, we can inject the updated version of ourself
+    # into older containers.
+    if [ $container_rootfs != "/" ]; then
+        cp -f `readlink -f $0` $container_rootfs/usr/bin/lxc-patch
+        if [ $container_release_major -lt "6" ]; then
+            mkdir -p $container_rootfs/usr/lib/yum-plugins
+            cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/lib/yum-plugins
+        fi
+        if [ $container_release_major = "6" ]; then
+            mkdir -p $container_rootfs/usr/share/yum-plugins
+            cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/share/yum-plugins
+        fi
+        mkdir -p $container_rootfs/etc/yum/pluginconf.d
+        cat <<EOF > $container_rootfs/etc/yum/pluginconf.d/lxc-patch.conf
+[main]
+enabled=1
+packages=initscripts,iptables,selinux-policy,readahead,udev,util-linux-ng
+EOF
+    fi
+
+    if [ $container_release_major = "4" ]; then
+        # yum plugin type of TYPE_INTERFACE works in all releases but gives a
+        # deprecation warning on major > 4, so we default to TYPE_INTERACTIVE
+        # and fix it up here
+        sed -i 's|TYPE_INTERACTIVE|TYPE_INTERFACE|' $container_rootfs/usr/lib/yum-plugins/lxc-patch.py
+        if [ -f $container_rootfs/etc/yum.repos.d/ULN-Base.repo ]; then
+            mv $container_rootfs/etc/yum.repos.d/ULN-Base.repo \
+               $container_rootfs/etc/yum.repos.d/ULN-Base.repo.lxc-disabled
+        fi
+        echo "plugins = 1" >>$container_rootfs/etc/yum.conf
+    fi
 
     # "disable" selinux in the guest. The policy in the container isn't
     # likely to match the hosts (unless host == guest exactly) and the
@@ -114,48 +149,26 @@ container_rootfs_configure()
         sed -i 's|session[ \t]*required[ \t]*/lib/security/\$ISA/pam_limits.so|#session required /lib/security/$ISA/pam_limits.so|' $container_rootfs/etc/pam.d/system-auth
     fi
 
-    # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest
-    # will report its name and be resolv'able by the hosts dnsmasq
-    cat <<EOF > $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
-DEVICE=eth0
-BOOTPROTO=dhcp
-ONBOOT=yes
-HOSTNAME=$name
-DHCP_HOSTNAME=$name
-NM_CONTROLLED=no
-TYPE=Ethernet
-EOF
-
     # avoid error in ol5 attempting to copy non-existent resolv.conf
     if [ $container_release_major = "5" ]; then
         sed -i 's|resolv.conf.predhclient|resolv.conf.predhclient 2>/dev/null|' $container_rootfs/sbin/dhclient-script
     fi
 
-    # set the hostname
-    cat <<EOF > $container_rootfs/etc/sysconfig/network
-NETWORKING=yes
-NETWORKING_IPV6=no
-HOSTNAME=$name
-EOF
-
     # disable interactive ovmd asking questions
     if [ -f $container_rootfs/etc/sysconfig/ovmd ]; then
         sed -i 's|INITIAL_CONFIG=yes|INITIAL_CONFIG=no|' $container_rootfs/etc/sysconfig/ovmd
     fi
 
-    # set minimal hosts
-    echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts
+    # disable disabling of ipv4 forwarding and defrag on shutdown since
+    # we mount /proc/sys ro
+    if [ $container_release_major = "5" ]; then
+        sed -i 's|-f /proc/sys/net/ipv4/ip_forward|-w /proc/sys/net/ipv4/ip_forward|' $container_rootfs/etc/rc.d/init.d/network
+        sed -i 's|-f /proc/sys/net/ipv4/ip_always_defrag|-w /proc/sys/net/ipv4/ip_always_defrag|' $container_rootfs/etc/rc.d/init.d/network
+    fi
 
     # disable ipv6 on ol6
     rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global
 
-    # this file has to exist for libvirt/Virtual machine monitor to boot the container
-    touch $container_rootfs/etc/mtab
-
-    # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt
-    cat <<EOF > $container_rootfs/etc/fstab
-EOF
-
     # remove module stuff for iptables it just shows errors that are not
     # relevant in a container
     if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then
@@ -182,17 +195,6 @@ EOF
         sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/NetworkManager 2>/dev/null
     fi
 
-    # sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC, so make sure to mount /dev/shm (normally done by dracut initrd) as tmpfs
-    if [ $container_release_major = "4" -o $container_release_major = "5" ]; then
-        echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.sysinit
-        echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.d/rc.sysinit
-    fi
-
-    if [ $container_release_major = "6" ]; then
-        sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit
-        sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit
-    fi
-
     # no need to attempt to mount /
     sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit
     sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit
@@ -231,8 +233,12 @@ EOF
     sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.d/rc.sysinit
 
     # dont start lvm
-    sed -i 's|action $"Setting up Logical Volume Management:"|#action $"Setting up Logical Volume Management:"|' $container_rootfs/etc/rc.sysinit
-    sed -i 's|action $"Setting up Logical Volume Management:"|/bin/true #action $"Setting up Logical Volume Management:"|' $container_rootfs/etc/rc.d/rc.sysinit
+    if [ $container_release_major -lt "6" -a -f $container_rootfs/sbin/lvm.static ]; then
+        mv $container_rootfs/sbin/lvm.static $container_rootfs/sbin/lvm.static.lxc-disabled
+    fi
+    if [ $container_release_major = "6" ]; then
+        touch $container_rootfs/.nolvm
+    fi
 
     # fix assumptions that plymouth is available
     sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit
@@ -241,6 +247,80 @@ EOF
     rm -f $container_rootfs/etc/init/quit-plymouth.conf
     rm -f $container_rootfs/etc/init/splash-manager.conf
 
+    # dont try to unmount /dev/lxc devices
+    sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt
+
+    # don't try to unmount swap
+    sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt
+
+    # there might be other services that are useless but the below set is a good start
+    # some of these might not exist in the image, so we silence chkconfig complaining
+    # about the service file not being found
+    for service in \
+        acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd	\
+        ip6tables irqbalance iscsi iscsid isdn kdump kudzu		\
+        lm_sensors lvm2-monitor mdmonitor microcode_ctl			\
+        ntpd pcmcia postfix sendmail udev-post xfs ;
+    do
+        chroot $container_rootfs chkconfig 2>/dev/null $service off
+    done
+
+    for service in rsyslog ;
+    do
+        chroot $container_rootfs chkconfig 2>/dev/null $service on
+    done
+
+    # ensure /dev/ptmx refers to the newinstance devpts of the container, or
+    # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512)
+    rm -f $container_rootfs/dev/ptmx
+    ln -s pts/ptmx $container_rootfs/dev/ptmx
+}
+
+container_rootfs_configure()
+{
+    container_rootfs_patch
+    echo "Configuring container for Oracle Linux $container_release_major.$container_release_minor"
+
+    # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest
+    # will report its name and be resolv'able by the hosts dnsmasq
+    cat <<EOF > $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
+DEVICE=eth0
+BOOTPROTO=dhcp
+ONBOOT=yes
+HOSTNAME=$name
+DHCP_HOSTNAME=$name
+NM_CONTROLLED=no
+TYPE=Ethernet
+EOF
+
+    # set the hostname
+    cat <<EOF > $container_rootfs/etc/sysconfig/network
+NETWORKING=yes
+NETWORKING_IPV6=no
+HOSTNAME=$name
+EOF
+
+    # set minimal hosts
+    echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts
+
+    # this file has to exist for libvirt/Virtual machine monitor to boot the container
+    touch $container_rootfs/etc/mtab
+
+    # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt
+    cat <<EOF > $container_rootfs/etc/fstab
+EOF
+
+    # sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC, so make sure to mount /dev/shm (normally done by dracut initrd) as tmpfs
+    if [ $container_release_major = "4" -o $container_release_major = "5" ]; then
+        echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.sysinit
+        echo "mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.d/rc.sysinit
+    fi
+
+    if [ $container_release_major = "6" ]; then
+        sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit
+        sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit
+    fi
+
     # 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.
@@ -262,12 +342,6 @@ EOF
         sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf
     fi
 
-    # dont try to unmount /dev/lxc devices
-    sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt
-
-    # don't try to unmount swap
-    sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt
-
     # start a getty on /dev/console, /dev/tty[1-4]
     if [ $container_release_major = "4" -o $container_release_major = "5" ]; then
         sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/inittab
@@ -312,23 +386,6 @@ exec init 0
 EOF
     fi
 
-    # there might be other services that are useless but the below set is a good start
-    # some of these might not exist in the image, so we silence chkconfig complaining
-    # about the service file not being found
-    for service in \
-        acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd	\
-        ip6tables irqbalance iscsi iscsid isdn kdump kudzu		\
-        lm_sensors lvm2-monitor mdmonitor microcode_ctl			\
-        ntpd pcmcia postfix sendmail udev-post xfs ;
-    do
-        chroot $container_rootfs chkconfig 2>/dev/null $service off
-    done
-
-    for service in rsyslog ;
-    do
-        chroot $container_rootfs chkconfig 2>/dev/null $service on
-    done
-
     # create required devices. note that /dev/console will be created by lxc
     # or libvirt itself to be a symlink to the right pty.
     # take care to not nuke /dev in case $container_rootfs isn't set
@@ -365,11 +422,6 @@ EOF
         done
     fi
 
-    # ensure /dev/ptmx refers to the newinstance devpts of the container, or
-    # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512)
-    rm -f $container_rootfs/dev/ptmx
-    ln -s pts/ptmx $container_rootfs/dev/ptmx
-
     # start with a clean /var/log/messages
     rm -f $container_rootfs/var/log/messages
 
@@ -631,6 +683,7 @@ usage()
   -r|--rpms=<rpm name>    additional rpms to install into container
   -u|--url=<url>          replace yum repo url (ie. local yum mirror)
   -t|--templatefs=<path>  copy/clone rootfs at path instead of downloading
+  -P|--patch=<path>       only patch the rootfs at path for use as a container
   -h|--help
 
 Release is of the format "major.minor", for example "5.8", "6.3", or "6.latest"
@@ -638,7 +691,7 @@ EOF
     return 0
 }
 
-options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs: -- "$@")
+options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch: -- "$@")
 if [ $? -ne 0 ]; then
     usage $(basename $0)
     exit 1
@@ -658,6 +711,7 @@ do
         -r|--rpms)		user_pkgs=$2; shift 2;;
         -u|--url)		repourl=$2; shift 2;;
         -t|--templatefs)	template_rootfs=$2; shift 2;;
+        --patch)		patch_rootfs=$2; shift 2;;
         --)             	shift 1; break ;;
         *)              	break ;;
     esac
@@ -669,6 +723,13 @@ if [ "$(id -u)" != "0" ]; then
     exit 1
 fi
 
+if [ -n "$patch_rootfs" ]; then
+    container_rootfs="$patch_rootfs"
+    container_release_get $container_rootfs
+    container_rootfs_patch
+    exit 0
+fi
+
 if [ -z $name ]; then
     echo "Container name must be given"
     usage
-- 
1.8.5.3



More information about the lxc-devel mailing list