]> git.proxmox.com Git - mirror_lxc.git/blob - templates/lxc-centos.in
Merge pull request #101 from ahippo/master
[mirror_lxc.git] / templates / lxc-centos.in
1 #!/bin/bash
2
3 #
4 # template script for generating centos container for LXC
5
6 #
7 # lxc: linux Container library
8
9 # Authors:
10 # Daniel Lezcano <daniel.lezcano@free.fr>
11 # Ramez Hanna <rhanna@informatiq.org>
12 # Fajar A. Nugraha <github@fajar.net>
13 # Michael H. Warfield <mhw@WittsEnd.com>
14
15 # This library is free software; you can redistribute it and/or
16 # modify it under the terms of the GNU Lesser General Public
17 # License as published by the Free Software Foundation; either
18 # version 2.1 of the License, or (at your option) any later version.
19
20 # This library is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 # Lesser General Public License for more details.
24
25 # You should have received a copy of the GNU Lesser General Public
26 # License along with this library; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
29 #Configurations
30 arch=$(arch)
31 cache_base=@LOCALSTATEDIR@/cache/lxc/centos/$arch
32 default_path=@LXCPATH@
33 # We really need something better here!
34 root_password=root
35
36 lxc_network_type=veth
37 lxc_network_link=lxcbr0
38
39 # is this centos?
40 # Alow for weird remixes like the Raspberry Pi
41 #
42 # Use the Mitre standard CPE identifier for the release ID if possible...
43 # This may be in /etc/os-release or /etc/system-release-cpe. We
44 # should be able to use EITHER. Give preference to /etc/os-release for now.
45
46 if [ -e /etc/os-release ]
47 then
48 # This is a shell friendly configuration file. We can just source it.
49 # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME
50 . /etc/os-release
51 echo "Host CPE ID from /etc/os-release: ${CPE_NAME}"
52 fi
53
54 if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ]
55 then
56 CPE_NAME=$(head -n1 /etc/system-release-cpe)
57 CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:*]\)')
58 if [ "${CPE_URI}" != "cpe:/o" ]
59 then
60 CPE_NAME=
61 else
62 echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}"
63 # Probably a better way to do this but sill remain posix
64 # compatible but this works, shrug...
65 # Must be nice and not introduce convenient bashisms here.
66 ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)')
67 VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)')
68 fi
69 fi
70
71 if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ]
72 then
73 centos_host_ver=${VERSION_ID}
74 is_centos=true
75 elif [ -e /etc/redhat-release ]
76 then
77 # Only if all other methods fail, try to parse the redhat-release file.
78 centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release )
79 if [ "$centos_host_ver" != "" ]
80 then
81 is_centos=true
82 fi
83 fi
84
85 # Map a few architectures to their generic Centos repository archs.
86 #
87 # CentOS currently doesn't support ARM but it's copied here from
88 # the Fedora template for completeness and that it will in the future.
89 #
90 # The two ARM archs are a bit of a guesstimate for the v5 and v6
91 # archs. V6 should have hardware floating point (Rasberry Pi).
92 # The "arm" arch is safer (no hardware floating point). So
93 # there may be cases where we "get it wrong" for some v6 other
94 # than RPi.
95 case "$arch" in
96 i686) arch=i386 ;;
97 armv3l|armv4l|armv5l) arch=arm ;;
98 armv6l|armv7l|armv8l) arch=armhfp ;;
99 esac
100
101 force_mknod()
102 {
103 # delete a device node if exists, and create a new one
104 rm -f $2 && mknod -m $1 $2 $3 $4 $5
105 }
106
107 configure_centos()
108 {
109
110 # disable selinux in centos
111 mkdir -p $rootfs_path/selinux
112 echo 0 > $rootfs_path/selinux/enforce
113
114 # Also kill it in the /etc/selinux/config file if it's there...
115 if [[ -f $rootfs_path/etc/selinux/config ]]
116 then
117 sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config
118 fi
119
120 # Nice catch from Dwight Engen in the Oracle template.
121 # Wantonly plagerized here with much appreciation.
122 if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then
123 mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig
124 ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled
125 fi
126
127 # This is a known problem and documented in RedHat bugzilla as relating
128 # to a problem with auditing enabled. This prevents an error in
129 # the container "Cannot make/remove an entry for the specified session"
130 sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login
131 sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd
132
133 # configure the network using the dhcp
134 cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
135 DEVICE=eth0
136 BOOTPROTO=dhcp
137 ONBOOT=yes
138 HOSTNAME=${UTSNAME}
139 NM_CONTROLLED=no
140 TYPE=Ethernet
141 MTU=${MTU}
142 EOF
143
144 # set the hostname
145 cat <<EOF > ${rootfs_path}/etc/sysconfig/network
146 NETWORKING=yes
147 HOSTNAME=${UTSNAME}
148 EOF
149
150 # set minimal hosts
151 cat <<EOF > $rootfs_path/etc/hosts
152 127.0.0.1 localhost $name
153 EOF
154
155 # set minimal fstab
156 cat <<EOF > $rootfs_path/etc/fstab
157 /dev/root / rootfs defaults 0 0
158 none /dev/shm tmpfs nosuid,nodev 0 0
159 EOF
160
161 # create lxc compatibility init script
162 if [ "$release" = "6" ]; then
163 cat <<EOF > $rootfs_path/etc/init/lxc-sysinit.conf
164 start on startup
165 env container
166
167 pre-start script
168 if [ "x$container" != "xlxc" -a "x$container" != "xlibvirt" ]; then
169 stop;
170 fi
171 initctl start tty TTY=console
172 rm -f /var/lock/subsys/*
173 rm -f /var/run/*.pid
174 telinit 3
175 exit 0;
176 end script
177 EOF
178 elif [ "$release" = "5" ]; then
179 cat <<EOF > $rootfs_path/etc/rc.d/lxc.sysinit
180 #! /bin/bash
181 rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/*
182 rm -rf {/,/var}/tmp/*
183 echo "/dev/root / rootfs defaults 0 0" > /etc/mtab
184 exit 0
185 EOF
186 chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit
187 sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab
188 sed -i 's|^1:|co:2345:respawn:/sbin/mingetty console\n1:|' $rootfs_path/etc/inittab
189 sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab
190 fi
191
192 dev_path="${rootfs_path}/dev"
193 rm -rf $dev_path
194 mkdir -p $dev_path
195 mknod -m 666 ${dev_path}/null c 1 3
196 mknod -m 666 ${dev_path}/zero c 1 5
197 mknod -m 666 ${dev_path}/random c 1 8
198 mknod -m 666 ${dev_path}/urandom c 1 9
199 mkdir -m 755 ${dev_path}/pts
200 mkdir -m 1777 ${dev_path}/shm
201 mknod -m 666 ${dev_path}/tty c 5 0
202 mknod -m 666 ${dev_path}/tty0 c 4 0
203 mknod -m 666 ${dev_path}/tty1 c 4 1
204 mknod -m 666 ${dev_path}/tty2 c 4 2
205 mknod -m 666 ${dev_path}/tty3 c 4 3
206 mknod -m 666 ${dev_path}/tty4 c 4 4
207 mknod -m 600 ${dev_path}/console c 5 1
208 mknod -m 666 ${dev_path}/full c 1 7
209 mknod -m 600 ${dev_path}/initctl p
210 mknod -m 666 ${dev_path}/ptmx c 5 2
211
212 echo "setting root passwd to $root_password"
213 echo "root:$root_password" | chroot $rootfs_path chpasswd
214
215 # This will need to be enhanced for CentOS 7 when systemd
216 # comes into play... /\/\|=mhw=|\/\/
217
218 return 0
219 }
220
221 configure_centos_init()
222 {
223 sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit
224 sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit
225 if [ "$release" = "6" ]; then
226 chroot ${rootfs_path} chkconfig udev-post off
227 fi
228 chroot ${rootfs_path} chkconfig network on
229 }
230
231 download_centos()
232 {
233
234 # check the mini centos was not already downloaded
235 INSTALL_ROOT=$cache/partial
236 mkdir -p $INSTALL_ROOT
237 if [ $? -ne 0 ]; then
238 echo "Failed to create '$INSTALL_ROOT' directory"
239 return 1
240 fi
241
242 # download a mini centos into a cache
243 echo "Downloading centos minimal ..."
244 YUM="yum --installroot $INSTALL_ROOT -y --nogpgcheck"
245 PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils"
246
247 # use temporary repository definition
248 REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo
249 mkdir -p $(dirname $REPO_FILE)
250 cat <<EOF > $REPO_FILE
251 [base]
252 name=CentOS-$release - Base
253 mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$arch&repo=os
254
255 [updates]
256 name=CentOS-$release - Updates
257 mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$arch&repo=updates
258 EOF
259
260 # create minimal device nodes, needed for "yum install" and "yum update" process
261 mkdir -p $INSTALL_ROOT/dev
262 force_mknod 666 $INSTALL_ROOT/dev/null c 1 3
263 force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9
264
265 $YUM install $PKG_LIST
266
267 if [ $? -ne 0 ]; then
268 echo "Failed to download the rootfs, aborting."
269 return 1
270 fi
271
272 # use same nameservers as hosts, needed for "yum update later"
273 cp /etc/resolv.conf $INSTALL_ROOT/etc/
274
275 # check whether rpmdb is under $HOME
276 if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then
277 echo "Fixing rpmdb location ..."
278 mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/
279 rm -rf $INSTALL_ROOT/$HOME/.rpmdb
280 chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null
281 fi
282
283 # check whether rpmdb version is correct
284 chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null
285 ret=$?
286
287 # if "rpm -q" doesn't work due to rpmdb version difference,
288 # then we need to redo the process using the newly-installed yum
289 if [ $ret -gt 0 ]; then
290 echo "Reinstalling packages ..."
291 mv $REPO_FILE $REPO_FILE.tmp
292 mkdir $INSTALL_ROOT/etc/yum.repos.disabled
293 mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/
294 mv $REPO_FILE.tmp $REPO_FILE
295 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc
296 cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/
297 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev
298 mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3
299 mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9
300 mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum
301 cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/
302 chroot $INSTALL_ROOT $YUM install $PKG_LIST
303 if [ $? -ne 0 ]; then
304 echo "Failed to download the rootfs, aborting."
305 return 1
306 fi
307 mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp
308 rm -rf $INSTALL_ROOT
309 mv $INSTALL_ROOT.tmp $INSTALL_ROOT
310 fi
311
312 rm -f $REPO_FILE
313 rm -rf $INSTALL_ROOT/var/cache/yum/*
314
315 mv "$INSTALL_ROOT" "$cache/rootfs"
316 echo "Download complete."
317
318 return 0
319 }
320
321 copy_centos()
322 {
323
324 # make a local copy of the mini centos
325 echo -n "Copying rootfs to $rootfs_path ..."
326 #cp -a $cache/rootfs-$arch $rootfs_path || return 1
327 # i prefer rsync (no reason really)
328 mkdir -p $rootfs_path
329 rsync -a $cache/rootfs/ $rootfs_path/
330 return 0
331 }
332
333 update_centos()
334 {
335 YUM="chroot $cache/rootfs yum -y --nogpgcheck"
336 $YUM update
337 if [ $? -ne 0 ]; then
338 return 1
339 fi
340 $YUM clean packages
341 }
342
343 install_centos()
344 {
345 mkdir -p /var/lock/subsys/
346 (
347 flock -x 200
348 if [ $? -ne 0 ]; then
349 echo "Cache repository is busy."
350 return 1
351 fi
352
353 echo "Checking cache download in $cache/rootfs ... "
354 if [ ! -e "$cache/rootfs" ]; then
355 download_centos
356 if [ $? -ne 0 ]; then
357 echo "Failed to download 'centos base'"
358 return 1
359 fi
360 else
361 echo "Cache found. Updating..."
362 update_centos
363 if [ $? -ne 0 ]; then
364 echo "Failed to update 'centos base', continuing with last known good cache"
365 else
366 echo "Update finished"
367 fi
368 fi
369
370 echo "Copy $cache/rootfs to $rootfs_path ... "
371 copy_centos
372 if [ $? -ne 0 ]; then
373 echo "Failed to copy rootfs"
374 return 1
375 fi
376
377 return 0
378
379 ) 200>/var/lock/subsys/lxc-centos
380
381 return $?
382 }
383
384 copy_configuration()
385 {
386
387 mkdir -p $config_path
388 cat <<EOF >> $config_path/config
389 lxc.utsname = $utsname
390 lxc.tty = 4
391 lxc.pts = 1024
392 lxc.rootfs = $rootfs_path
393 lxc.mount = $config_path/fstab
394 lxc.cap.drop = sys_module mac_admin mac_override sys_time
395
396 lxc.autodev = $auto_dev
397
398 # example simple networking setup, uncomment to enable
399 #lxc.network.type = $lxc_network_type
400 #lxc.network.flags = up
401 #lxc.network.link = $lxc_network_link
402 #lxc.network.name = eth0
403 # additional example for veth network type, static MAC address,
404 # and persistent veth device name on host side
405 #lxc.network.hwaddr = 00:16:3e:77:52:20
406 #lxc.network.veth.pair = v-$name-e0
407
408 #cgroups
409 lxc.cgroup.devices.deny = a
410 # /dev/null and zero
411 lxc.cgroup.devices.allow = c 1:3 rwm
412 lxc.cgroup.devices.allow = c 1:5 rwm
413 # consoles
414 lxc.cgroup.devices.allow = c 5:1 rwm
415 lxc.cgroup.devices.allow = c 5:0 rwm
416 lxc.cgroup.devices.allow = c 4:0 rwm
417 lxc.cgroup.devices.allow = c 4:1 rwm
418 # /dev/{,u}random
419 lxc.cgroup.devices.allow = c 1:9 rwm
420 lxc.cgroup.devices.allow = c 1:8 rwm
421 lxc.cgroup.devices.allow = c 136:* rwm
422 lxc.cgroup.devices.allow = c 5:2 rwm
423 # rtc
424 lxc.cgroup.devices.allow = c 254:0 rwm
425 EOF
426
427 cat <<EOF > $config_path/fstab
428 proc proc proc nodev,noexec,nosuid 0 0
429 devpts dev/pts devpts defaults 0 0
430 sysfs sys sysfs defaults 0 0
431 EOF
432
433 if [ $? -ne 0 ]; then
434 echo "Failed to add configuration"
435 return 1
436 fi
437
438 return 0
439 }
440
441 clean()
442 {
443
444 if [ ! -e $cache ]; then
445 exit 0
446 fi
447
448 # lock, so we won't purge while someone is creating a repository
449 (
450 flock -x 200
451 if [ $? != 0 ]; then
452 echo "Cache repository is busy."
453 exit 1
454 fi
455
456 echo -n "Purging the download cache for centos-$release..."
457 rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
458 exit 0
459
460 ) 200>/var/lock/subsys/lxc-centos
461 }
462
463 usage()
464 {
465 cat <<EOF
466 usage:
467 $1 -n|--name=<container_name>
468 [-p|--path=<path>] [-c|--clean] [-R|--release=<CentOS_release>] [-A|--arch=<arch of the container>]
469 [-h|--help]
470 Mandatory args:
471 -n,--name container name, used to as an identifier for that container from now on
472 Optional args:
473 -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in that case
474 -c,--clean clean the cache
475 -R,--release Centos release for the new container. if the host is Centos, then it will defaultto the host's release.
476 --fqdn fully qualified domain name (FQDN) for DNS and system naming
477 -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64]
478 -h,--help print this help
479 EOF
480 return 0
481 }
482
483 options=$(getopt -o hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,fqdn: -- "$@")
484 if [ $? -ne 0 ]; then
485 usage $(basename $0)
486 exit 1
487 fi
488 eval set -- "$options"
489
490 while true
491 do
492 case "$1" in
493 -h|--help) usage $0 && exit 0;;
494 -p|--path) path=$2; shift 2;;
495 --rootfs) rootfs=$2; shift 2;;
496 -n|--name) name=$2; shift 2;;
497 -c|--clean) clean=$2; shift 2;;
498 -R|--release) release=$2; shift 2;;
499 --fqdn) utsname=$2; shift 2;;
500 --) shift 1; break ;;
501 *) break ;;
502 esac
503 done
504
505 if [ ! -z "$clean" -a -z "$path" ]; then
506 clean || exit 1
507 exit 0
508 fi
509
510 if [ -z "${utsname}" ]; then
511 utsname=${name}
512 fi
513
514 # This follows a standard "resolver" convention that an FQDN must have
515 # at least two dots or it is considered a local relative host name.
516 # If it doesn't, append the dns domain name of the host system.
517 #
518 # This changes one significant behavior when running
519 # "lxc_create -n Container_Name" without using the
520 # --fqdn option.
521 #
522 # Old behavior:
523 # utsname and hostname = Container_Name
524 # New behavior:
525 # utsname and hostname = Container_Name.Domain_Name
526
527 if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
528 if [ -n "$(dnsdomainname)" ]; then
529 utsname=${utsname}.$(dnsdomainname)
530 fi
531 fi
532
533 type yum >/dev/null 2>&1
534 if [ $? -ne 0 ]; then
535 echo "'yum' command is missing"
536 exit 1
537 fi
538
539 if [ -z "$path" ]; then
540 path=$default_path/$name
541 fi
542
543 if [ -z "$release" ]; then
544 if [ "$is_centos" -a "$centos_host_ver" ]; then
545 release=$centos_host_ver
546 else
547 echo "This is not a centos host and release missing, defaulting to 6 use -R|--release to specify release"
548 release=6
549 fi
550 fi
551
552 # CentOS 7 and above should run systemd. We need autodev enabled to keep
553 # systemd from causing problems.
554 if [ $release -gt 6 ]; then
555 auto_dev="1"
556 else
557 auto_dev="0"
558 fi
559
560 if [ "$(id -u)" != "0" ]; then
561 echo "This script should be run as 'root'"
562 exit 1
563 fi
564
565
566 if [ -z "$rootfs_path" ]; then
567 rootfs_path=$path/rootfs
568 # check for 'lxc.rootfs' passed in through default config by lxc-create
569 if grep -q '^lxc.rootfs' $path/config 2>/dev/null ; then
570 rootfs_path=`grep 'lxc.rootfs =' $path/config | awk -F= '{ print $2 }'`
571 fi
572 fi
573 config_path=$default_path/$name
574 cache=$cache_base/$release
575
576 revert()
577 {
578 echo "Interrupted, so cleaning up"
579 lxc-destroy -n $name
580 # maybe was interrupted before copy config
581 rm -rf $path
582 rm -rf $default_path/$name
583 echo "exiting..."
584 exit 1
585 }
586
587 trap revert SIGHUP SIGINT SIGTERM
588
589 copy_configuration
590 if [ $? -ne 0 ]; then
591 echo "failed write configuration file"
592 exit 1
593 fi
594
595 install_centos
596 if [ $? -ne 0 ]; then
597 echo "failed to install centos"
598 exit 1
599 fi
600
601 configure_centos
602 if [ $? -ne 0 ]; then
603 echo "failed to configure centos for a container"
604 exit 1
605 fi
606
607 configure_centos_init
608
609 if [ ! -z $clean ]; then
610 clean || exit 1
611 exit 0
612 fi
613 echo "container rootfs and config created, default root password is '$root_password'"
614 echo "edit the config file to check/enable networking setup"