Шаблоны операционных систем (шаблоны ОС) — наборы образов диска и скриптов установки, состоящие из дистрибутива и других необходимых для установки операционной системы данных. Используются для удобства установки операционных систем на виртуальные машины.

В статье описаны принципы создания собственного шаблона ОС.

Шаблон ОС представляет из себя XML-файл metainfo.xml и набор файлов для установки. В metainfo.xml описаны механизмы, которые будут использованы при установке.

Подготовленные нами шаблоны доступны в репозитории.

Для создания шаблона необходимо создать директорию [NFS-хранилище]/<НАЗВАНИЕ_ШАБЛОНА>. Внутри должен обязательно содержаться файл metainfo.xml с XML-описанием шаблона. Также в этой директории должны находиться все файлы, которые указаны в параметрах шаблона.

В описании шаблона необходимо добавить:

<date>2013-04-14 15:37:21</date>
<install_result>ok</install_result>
BASH


Данные строки являются флагом, который сигнализирует, что шаблон уже установлен. В примерах эти поля не указаны, т.к. описание представлено в формате, который используется для шаблонов в репозитории.

После проделанных действий шаблон отобразится в меню добавления шаблонов ОС. Его можно добавить и использовать. Все изменения, которые осуществляются в файлах шаблона, применяются сразу.

Принципы создания собственного репозитория шаблонов ОС см. в статье Создание собственного репозитория шаблонов ОС.

Список параметров


Параметры

  • <kernel>Имя файла</kernel> — ядро Linux;

  • <initrd>Имя файла</initrd> — initrd;

  • <kernelcommand>Параметры</kernelcommand> — параметры загрузки ядра. Пример:

<kernelcommand>lang=en_US keymap=us ks=($OSINSTALLINFO_HTTP) method=http://mirror.yandex.ru/centos/6/os/x86_64/ initrd=initrd ip=($IP) netmask=($NETMASK) gateway=($GATEWAY) dns=($NAMESERVER)</kernelcommand>
BASH
  • <installdrive>Имя файла</installdrive> — дополнительный диск, который присоединяется к виртуальной машине. Представляет из себя любой файл, увеличенный до 1 мегабайта. В файле можно использовать макросы. Подробнее см. в статье Макросы шаблонов ОС. Такой способ рекомендуем для установки FreeBSD 9;
  • <installcfg>Имя файла</installcfg> — файл, который будет отдан при запросе по URL (макрос $OSINSTALLINFO_HTTP). В файле можно использовать макросы;
  • <image>Имя файла</image> — ISO-образ, который будет примонтирован как CD-ROM;
  • <tempipv4>yes</tempipv4> — опция, которая в значении "yes" определяет, что требуется временный IPv4-адрес. используется при установке ОС с основным IPv6-адресом;
    <sharedir>Имя директории</sharedir> — директория внутри шаблона, файлы из которой будут доступны по HTTP-протоколу. Узнать ссылку можно с помощью макросов;
  • <support><elem version='5.1.0'>vmmgr</elem><elem>dcimgr</elem></support> — краткое наименование панелей управления, которые поддерживают шаблон. version — минимальная версия панели управления;
  • <rebootcount>1</rebootcount> — количество перезагрузок, после которых VMmanager начинает считать, что операционная система установилась;
  • <illegal_password_characters>Запрещенные символы</illegal_password_characters> — символы которые запрещено использовать в пароле;
  • <sshpublickey>yes</sshpublickey> — опция поддержки добавления публичных SSH-ключей;
  • <up_mem_on_install>512</up_mem_on_install> — оперативная память, которую может использовать виртуальная машина при установке ОС. После установки количество памяти возвращается к основному значению;
  • <virtionet>yes</virtionet> — опция поддержки работы с virtio сетью;
  • <virtiodisk>yes</virtiodisk> —опция поддержки работы с virtio дисками;
  • <chpasswd_method>mount.linux</chpasswd_method> — метод смены пароля. Реализован метод mount.linux для linux-шаблонов;
  • <loaderefi64>pxelinux.efi</loaderefi64> — поддержка загрузки через UEFI;
  • <ipxeconf>ipxe.conf</ipxeconf> — конфигурационный файл для загрузки через iPXE;
  • <ipxeconf type="tftp"> — опция загрузки конфигурационного файла ipxe.conf по протоколу tftp. В конфигурационный файл dhcpd.conf добавляется строка вида "tftp://<адрес_панели_управления>/.../ipxe.conf".


Все пути в параметрах указываются относительно директории с шаблоном.

Ограничения

В теге limit описываются необходимые ограничения шаблона. При этом в VMmanager нельзя будет создать виртуальную машину, если она не удовлетворяет ограничениям. Если лимиты не заданы, то проверка не производится.

  • ipv4 — поддержка IPv4 как основной IP-адрес. Указывается значение "yes" или "no";
  • ipv6 — поддержка IPv6 как основной IP-адрес. Указывается значение "yes" или "no";
  • mem — минимально необходимый объём оперативной памяти в MiB;
  • disk — минимально необходимый объём основного диска в MB.

Пример:

<limit>
	<elem name="ipv4">yes</elem>
	<elem name="ipv6">no</elem>
	<elem name="mem">512</elem>
	<elem name="disk">1500</elem>
</limit>
BASH

Примеры шаблонов


kickstart (CentOS, Fedora, RedHat)

Рекомендуется использовать для шаблонов ОС CentOS, Fedora, RedHat. Подробнее о технологии kickstart см. в статье Википедии. Она позволяет гибко настраивать параметры установки. Для загрузки указывается kernel и initrd, а в команде ядра ссылка на файл ответов.

metainfo.xml

<?xml version="1.0"?>
<doc>
  <osname>CentOS-6-amd64</osname>
  <support>
    <elem>VMmgr</elem>
    <elem>IFXmgr</elem>
  </support>
  <rebootcount>1</rebootcount>
  <kernel>vmlinuz</kernel>
  <initrd>initrd.img</initrd>
  <type>ostemplate</type>
  <loader>pxelinux.0</loader>
  <pxelinuxcfg>pxelinux.conf</pxelinuxcfg>
  <tempipv4>yes</tempipv4>
  <kernelcommand>lang=en_US keymap=us ks=($OSINSTALLINFO_HTTPv4) ksdevice=link method=http://mirror.yandex.ru/centos/6/os/x86_64/ ip=($IPv4) netmask=($NETMASKv4) gateway=($GATEWAYv4) dns=($NAMESERVERv4) roxy=($HTTPPROXYv4) text</kernelcommand>
  <installcfg>install.cfg</installcfg>
  <limit>
    <elem name="ipv4">yes</elem>
    <elem name="ipv6">yes</elem>
    <elem name="mem">512</elem>
    <elem name="disk">2000</elem>
  </limit>
 </doc>
BASH

install.cfg

%pre

#!/bin/sh

for disk in `ls -la /dev/sd?`; do
    dd if=/dev/zero of=$disk bs=512 count=32
done

SWSIZE=$(grep MemTotal /proc/meminfo  | awk '{print int($2/1024)+1}')
if [ ${SWSIZE} -gt 2048 ]; then
    SWSIZE=2048
fi

if [ `cat /proc/scsi/scsi | grep -wc ATA` -eq 2 ]; then
     set $(fdisk -l 2>/dev/null | grep -vi mapper | grep Disk | grep dev |  sed 's/://' | awk '{print $2}' | tr '\n' ' ')
     HD1=$1
    HD2=$2
    if [ -b ${HD2} ]; then
        SIZE1=$(fdisk -l "${HD1}"  2>/dev/null  | grep Disk | grep dev |  sed 's/://'|awk '{print $5}')
        SIZE2=$(fdisk -l "${HD2}"  2>/dev/null  | grep Disk | grep dev |  sed 's/://'|awk '{print $5}')
        if [ -n "${SIZE1}" ] && [ -n "${SIZE2}" ] && [ "${SIZE1}" = "${SIZE2}" ]; then
            USE_MIRROR=yes
        fi
    fi
fi

if [ -n "${USE_MIRROR}" ]; then
cat > /tmp/part-include << EOF
part raid.11 --size=256 --asprimary --ondisk=sda
part raid.12 --size=${SWSIZE} --asprimary --ondisk=sda 
part raid.13 --size=1 --grow --asprimary --ondisk=sda 
part raid.21 --size=256 --asprimary --ondisk=sdb
part raid.22 --size=${SWSIZE} --asprimary --ondisk=sdb 
part raid.23 --size=1 --grow --asprimary --ondisk=sdb
raid /boot --fstype ext4 --device md0 --level=RAID1 raid.11 raid.21
raid swap --fstype swap --device md1 --level=RAID1 raid.12 raid.22
raid / --fstype ext4 --device md2 --level=RAID1 raid.13 raid.23
EOF
else
cat > /tmp/part-include << EOF
part /boot --fstype ext4 --size=256 --asprimary --ondisk=sda
part swap --size=${SWSIZE} --asprimary --ondisk=sda
part / --fstype ext4 --size=1 --grow --asprimary --ondisk=sda
EOF
fi

%end
auth --useshadow --enablemd5
# Crete partition map
bootloader --location=mbr
zerombr
clearpart --all --initlabel
firstboot --disable
# Disk partitioning information
%include /tmp/part-include
# System keyboard
keyboard us
# System language
lang en_US.UTF-8
# Installation logging level
logging --level=info
# Use NFS installation media
url --url http://mirror.yandex.ru/centos/6/os/x86_64/
#Root password
rootpw ($PASS)
# SELinux configuration
selinux --disabled
# Text installation
text
# System timezone
timezone --utc Europe/Moscow

# Network
network --bootproto=static --ip=($IPv4) --netmask=($NETMASK)  --gateway=($GATEWAYv4) --nameserver=($NAMESERVERv4) --hostname=($HOSTNAME) --device=link

# Install OS instead of upgrade
install
%packages
@core
%end

%post
# Настройка сети
#echo "NETWORKING=yes" > /etc/sysconfig/network
#echo "HOSTNAME=($HOSTNAME)"
ETHDEV=$(ip route show | grep default | grep -Eo 'dev\ .+\ ' | awk '{print $2}')
HWADDR=$(cat /etc/sysconfig/network-scripts/ifcfg-eth0 | awk -F= '/HWADDR/ {print $2}' | sed 's/"//g')
UUID=$(cat /etc/sysconfig/network-scripts/ifcfg-eth0 | awk -F= '/UUID/ {print $2}' | sed 's/"//g')

if [ -n "($IPv6)" ]; then
cat > /etc/sysconfig/network << EOF
NETWORKING=yes
NETWORKING_IPV6=yes
HOSTNAME=($HOSTNAME)
IPV6_DEFAULTGW=($GATEWAY)
EOF

cat > /etc/sysconfig/network-scripts/ifcfg-${ETHDEV} << EOF
DEVICE="${ETHDEV}"
BOOTPROTO="static"
DNS1="($NAMESERVER)"
HWADDR="${HWADDR}"
IPV6ADDR="($IPv6)/($NETMASKv6)"
IPV6INIT="yes"
IPV6_AUTOCONF="no"
IPV6_DEFAULTGW="($GATEWAY)"
NM_CONTROLLED="yes"
ONBOOT="yes"
TYPE="Ethernet"
UUID="${UUID}"
EOF

fi
%end

%post --nochroot
wget -O /dev/null --no-check-certificate "($FINISHv4)"
# Reboot after installation
%end

reboot
BASH

FreeBSD 9

Установка производится за счёт модифицированного образа установочного диска. В основе лежит образ bootonly.iso. Модифицируется файл /etc/rc.local который загружается после загрузки ОС с диска.

Чтобы изменить настройки установленной системы (пароль, сеть, пакеты и т.п.) и при этом пользователям не приходилось самостоятельно модифицировать образ используется следующий механизм: к виртуальной машине присоединяется дополнительный диск, который представляет из себя shell-скрипт, увеличенный до 1 мегабайта. После запуска ОС с диска запускается rc.local, который считывает все данные со второго жесткого диска и кладет их в файл /tmp/install.sh После чего запускает install.sh. В install.sh могут быть описаны любые действия.

Шаблон ОС включает в себя скрипт install.sh. В скрипте можно использовать макросы, вместо которых VMmanager подставит значения.

metainfo.xml

<?xml version="1.0"?>
<doc>
  <osname>FreeBSD-9-amd64</osname>
  <support>
    <elem>VMmgr</elem>
    <elem>IFXmgr</elem>
  </support>
  <image>freebsd.iso</image>
  <rebootcount>1</rebootcount>
  <type>ostemplate</type>
  <installdrive>freebsdinstall.sh</installdrive>
  <loader>pxelinux.0</loader>
  <pxelinuxcfg>pxelinux.conf</pxelinuxcfg>
  <installcfg>freebsdinstall.sh</installcfg>
  <initrd>freebsd.iso</initrd>
  <kernel>memdisk</kernel>
  <kernelcommand>iso raw</kernelcommand>
  <limit>
    <elem name="ipv4">yes</elem>
    <elem name="ipv6">yes</elem>
    <elem name="mem">512</elem>
    <elem name="disk">1500</elem>
  </limit>
</doc>
BASH

freebsdinstall.sh

#!/bin/sh

WGET="fetch -o- -q "

if [ "$START_BSD_INSTALL" != "yes" ]; then
export START_BSD_INSTALL="yes"
bsdinstall ../../../$0

exit 0
fi

clear
echo "Begin Installation at $(date)" | tee -a $BSDINSTALL_LOG

2>>$BSDINSTALL_LOG

error() {
	${WGET} "($FAILURL)?error=$1"
	exit 1
} 

rm -rf $BSDINSTALL_TMPETC
mkdir $BSDINSTALL_TMPETC

echo "Setting hostname..."
HOSTNAME=($HOSTNAME)
echo "hostname=\"$HOSTNAME\"" > $BSDINSTALL_TMPETC/rc.conf.hostname
hostname -s "$HOSTNAME"

echo "Configuring interfaces"
echo "Detecting..."
INTERFACES="${IFACE}"
if [ -z "${INTERFACES}" ]; then
	for IF in `ifconfig -l`; do
       test "$IF" = "lo0" && continue
       INTERFACES="$IF"
	done
fi
if [ -z "$INTERFACES" ]; then
       dialog --backtitle 'FreeBSD Installer' \
           --title 'Network Configuration Error' \
           --msgbox 'No network interfaces present to configure.' 0 0
       exit 1
fi

if [ -n "($IPv6)" ]; then
	echo "Configuring IPv6"
	export http_proxy="($HTTPPROXYv6)"
	export HTTP_PROXY="($HTTPPROXYv6)"
	ifconfig $INTERFACES inet6 ($IPv6) prefixlen ($NETMASKv6)
	route add -inet6 default ($GATEWAYv6)
	echo "nameserver ($NAMESERVERv6)" >> /etc/resolv.conf

	cat >> $BSDINSTALL_TMPETC/rc.conf.network << EOF
ipv6_defaultrouter="($GATEWAYv6)"
ifconfig_${INTERFACES}_ipv6="inet6 ($IPv6) prefixlen ($NETMASKv6)" 
sshd_enable="YES"
ipv6_all_interfaces="YES"
EOF
	echo "nameserver ($NAMESERVERv6)" >> $BSDINSTALL_TMPETC/resolv.conf
elif [ -n "($IPv4)" ]; then
	echo "Configuring IPv4"
	export http_proxy="($HTTPPROXYv4)"
	export HTTP_PROXY="($HTTPPROXYv4)"
	ifconfig $INTERFACES inet ($IPv4) netmask ($NETMASKv4)
	route add default ($GATEWAYv4)
	echo "nameserver ($NAMESERVERv4)" >> /etc/resolv.conf
	echo "name_servers=($NAMESERVERv4)" >> /etc/resolvconf.conf

	cat >> $BSDINSTALL_TMPETC/rc.conf.network << EOF
defaultrouter="($GATEWAYv4)"
ifconfig_${INTERFACES}="inet ($IPv4) netmask ($NETMASKv4)" 
sshd_enable="YES"
EOF

	echo "nameserver ($NAMESERVERv4)" >> $BSDINSTALL_TMPETC/resolv.conf
fi
sleep 5

echo "Setting files for download"
if [ "#$(uname -m)" = "#amd64" ]; then
       export DISTRIBUTIONS="base.txz kernel.txz lib32.txz"
else
        export DISTRIBUTIONS="base.txz kernel.txz"
fi

FETCH_DISTRIBUTIONS=""
for dist in $DISTRIBUTIONS; do
       if [ ! -f $BSDINSTALL_DISTDIR/$dist ]; then
               FETCH_DISTRIBUTIONS="$FETCH_DISTRIBUTIONS $dist"
       fi
done
FETCH_DISTRIBUTIONS=`echo $FETCH_DISTRIBUTIONS` # Trim white space

MIRROR="http://mirror.yandex.ru/freebsd/snapshots"
BSDVER="9.1-STABLE"

BSDINSTALL_DISTSITE="$MIRROR/`uname -m`/`uname -p`/${BSDVER}"

export BSDINSTALL_DISTSITE


echo "Detecting disks"
rm $PATH_FSTAB
touch $PATH_FSTAB

DISKS=`/sbin/sysctl -n kern.disks`
if [ -n ${SKIP_HD} ]; then
	DISKS=$(echo ${DISKS} | sed "s/${SKIP_HD}[ ]\{0,\}//")
fi


HD=${HD:-empty}
if [ $HD = "empty" ]; then
 HD=`echo $DISKS | xargs -n1 echo | grep ar | sort -tr -k2 -n | head -1`
 if [ -z "$HD" ]; then
   HD=`echo $DISKS | xargs -n1 echo | grep ad | sort -td -k2 -n | head -1`
   if [ -z "$HD" ]; then
     HD=`echo $DISKS | xargs -n1 echo | grep da | sort -ta -k2 -n | head -1`
     if [ -z "$HD" ]; then
       HD=`echo $DISKS | /usr/bin/cut -d ' ' -f1`
       if [ -z "$HD" ]; then
         exit 1
       fi
     fi
   fi
 fi
else
 if [ ! -b /dev/$HD ]; then
   # Hard disk device with this name not found
   # Terminate install with error
   error "disknodetect"
 fi
fi

DISK_TYPE=$(echo ${HD} | sed -e 's/[0-9]\{1,\}.*$//g')
echo "Disk type: ${DISK_TYPE}"

if [ $(echo ${DISKS} | grep -Eo "${DISK_TYPE}[0-9]" | wc -l) -eq 2 ]; then
# Mirror
	HDDS=$(echo ${DISKS} | grep -Eo "${DISK_TYPE}[0-9]")
	echo "${HDDS}"
	set ${HDDS}
	HD1=$1
	HD2=$2
	echo "Disk1: ${HD1}"
	echo "Disk2: ${HD2}"
	SIZE1=$(grep -Eio "^${HD1}: [0-9]{1,}(mb|gb|tb|b) " /var/run/dmesg.boot | awk '{print $2}')
	SIZE2=$(grep -Eio "^${HD2}: [0-9]{1,}(mb|gb|tb|b) " /var/run/dmesg.boot | awk '{print $2}')

	grep -Eio "^${HD1}: [0-9]{1,}(mb|gb|tb|b) " /var/run/dmesg.boot
	echo "Disk1 size: ${SIZE1}"
	grep -Eio "^${HD2}: [0-9]{1,}(mb|gb|tb|b) " /var/run/dmesg.boot
	echo "Disk2 size: ${SIZE2}"

	if [ -n "${SIZE1}" ] && [ -n "${SIZE2}" ] && [ "${SIZE1}" = "${SIZE2}" ]; then
		echo "This is miroor system"
		GMIRROR=yes
	else
		echo "Disks size is differ"
		echo "This is single system"
		HDDS=${HD}
	fi
else
# single
	echo "This is single system"
	HDDS=${HD}
fi

#exec 1>&2
echo "Formatting disks"

clear_disks() {
	# $1 — disk
	# $2 — disk-label-ending
        gpart delete -i 2 $1
        gpart delete -i 3 $1
        gpart delete -i 4 $1
        gpart delete -i 5 $1
        gpart delete -i 6 $1
        echo "destroy" | tee -a $BSDINSTALL_LOG
        gpart destroy -F $1
        echo "create GPT" | tee -a $BSDINSTALL_LOG
        gpart create -s GPT $1
        echo "add boot" | tee -a $BSDINSTALL_LOG
        gpart add -t freebsd-boot -l gpboot$2 -s 256K $1
        echo "set bootcode" | tee -a $BSDINSTALL_LOG
        gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 $1
        echo "add swap" >> $BSDINSTALL_LOG
        SWSIZE=$(sysctl -n hw.realmem | awk '{print int($1/1024/1024)+1}')
		if [ ${SWSIZE} -gt 2048 ]; then
			SWSIZE=2048
		fi
        gpart add -t freebsd-swap -l swap$2 -s ${SWSIZE}M $1
        echo "add root" | tee -a $BSDINSTALL_LOG

        # calculating size (not neded now)
        #let "ROOT_PART = ($VOL_SIZE_M) — 1"
        #echo "$ROOT_PART"
        #gpart add -t freebsd-ufs -l rootfs -s "$ROOT_PART"M $HD
        gpart add -t freebsd-ufs -l rootfs$2 $1 

}

if [ -n "${GMIRROR}" ]; then
	clear_disks ${HD1} ${HD1}
	clear_disks ${HD2} ${HD2}
else
	clear_disks ${HD}
fi

if [ -n "${GMIRROR}" ]; then
	echo "Creating mirror"
	gmirror load
	gmirror label -v rootfs /dev/gpt/rootfs${HD1} /dev/gpt/rootfs${HD2}
	gmirror label -v swap /dev/gpt/swap${HD1} /dev/gpt/swap${HD2}

	echo "newfs root" | tee -a $BSDINSTALL_LOG
	newfs -U /dev/mirror/rootfs

	echo "mount" | tee -a $BSDINSTALL_LOG
	mount /dev/mirror/rootfs $BSDINSTALL_CHROOT

	echo "# Device Mountpoint FStype Options Dump Pass#" > $PATH_FSTAB
	echo "/dev/mirror/swap none swap sw 0 0"    >> $PATH_FSTAB
	echo "/dev/mirror/rootfs / ufs rw 1 1" >> $PATH_FSTAB
else

	echo "newfs root" | tee -a $BSDINSTALL_LOG
	newfs -U /dev/gpt/rootfs

	echo "mount" | tee -a $BSDINSTALL_LOG
	mount /dev/gpt/rootfs $BSDINSTALL_CHROOT

	echo "# Device Mountpoint FStype Options Dump Pass#" > $PATH_FSTAB
	echo "/dev/gpt/swap none swap sw 0 0"    >> $PATH_FSTAB
	echo "/dev/gpt/rootfs / ufs rw 1 1" >> $PATH_FSTAB
fi

if [ ! -z "$FETCH_DISTRIBUTIONS" ]; then
       ALL_DISTRIBUTIONS="$DISTRIBUTIONS"

       # Download to a directory in the new system as scratch space
       BSDINSTALL_FETCHDEST="$BSDINSTALL_CHROOT/usr/freebsd-dist"
       mkdir -p "$BSDINSTALL_FETCHDEST" || error

       export DISTRIBUTIONS="$FETCH_DISTRIBUTIONS"
       # Try to use any existing distfiles
       if [ -d $BSDINSTALL_DISTDIR ]; then
               DISTDIR_IS_UNIONFS=1
               mount_nullfs -o union "$BSDINSTALL_FETCHDEST" "$BSDINSTALL_DISTDIR"
       else
               export DISTRIBUTIONS="MANIFEST $ALL_DISTRIBUTIONS"
               export BSDINSTALL_DISTDIR="$BSDINSTALL_FETCHDEST"
       fi

       export FTP_PASSIVE_MODE=YES
       bsdinstall distfetch || error fetch
       export DISTRIBUTIONS="$ALL_DISTRIBUTIONS"
        clear
fi

bsdinstall checksum || error checksummincorrect
bsdinstall distextract || error distextractfail
clear

echo "Setting password"
PASSWORD="($PASS)"

echo $PASSWORD | pw -V "$BSDINSTALL_CHROOT/etc" usermod root -h0

echo "Configuring services"
echo "PermitRootLogin yes" >> "$BSDINSTALL_CHROOT/etc/ssh/sshd_config"

#echo sshd_enable=\"YES\" >> $BSDINSTALL_TMPETC/rc.conf.services
echo dumpdev=\"AUTO\" >> $BSDINSTALL_TMPETC/rc.conf.services

if [ -n "${GMIRROR}" ]; then
	echo geom_mirror_load="YES" >> $BSDINSTALL_CHROOT/boot/loader.conf
fi

echo "Installing configs"
bsdinstall config  || error

echo "Installing vim"
chroot $BSDINSTALL_CHROOT pkg_add -r vim-lite
sed -i "" -E 's/EDITOR(.*)vi/EDITOR\1vim/g' $BSDINSTALL_CHROOT/root/.cshrc
cat >> $BSDINSTALL_CHROOT/root/.vimrc << EOF
set nocompatible
set encoding=utf8mb4
syntax on
EOF

cp $BSDINSTALL_LOG $BSDINSTALL_CHROOT/root/
dmesg > $BSDINSTALL_CHROOT/root/dmesg
if [ ! -z "$BSDINSTALL_FETCHDEST" ]; then
       [ "$BSDINSTALL_FETCHDEST" != "$BSDINSTALL_DISTDIR" ] && \
           umount "$BSDINSTALL_DISTDIR"
       rm -rf "$BSDINSTALL_FETCHDEST"
fi

echo "Installation complete"
$WGET "($FINISH)"

echo "Installation Completed at $(date)" | tee -a $BSDINSTALL_LOG
cp $BSDINSTALL_LOG $BSDINSTALL_CHROOT/root

BASH

preseed (Debian, Ubuntu)

Рекомендуется использовать для шаблонов ОС Debian, Ubuntu.

<?xml version="1.0"?>
<doc>
  <osname>Debian-7-amd64</osname>
  <support>
    <elem>IFXmgr</elem>
    <elem>VMmgr</elem>
  </support>
  <rebootcount>1</rebootcount>
  <kernel>linux</kernel>
  <initrd>initrd.gz</initrd>
  <type>ostemplate</type>
  <loader>pxelinux.0</loader>
  <pxelinuxcfg>pxelinux.conf</pxelinuxcfg>
  <tempipv4>yes</tempipv4>
  <kernelcommand>url=($OSINSTALLINFO_HTTPv4) language=en debian-installer/country=RU locale=en_US keyboard-configuration/xkb-keymap=us console-keymaps-at/keymap=us interface=auto netcfg/disable_dhcp=true netcfg/get_ipaddress=($IPv4) netcfg/get_netmask=($NETMASKv4) netcfg/get_gateway=($GATEWAYv4) netcfg/get_nameservers=($NAMESERVERv4) hostname=($HOSTNAME) domain=($HOSTNAME)</kernelcommand>
  <installcfg>install.cfg</installcfg>
  <limit>
    <elem name="ipv4">yes</elem>
    <elem name="ipv6">yes</elem>
    <elem name="mem">512</elem>
    <elem name="disk">1000</elem>
  </limit>
</doc>

BASH


install.cfg

d-i keyboard-configuration/xkb-keymap select us

# Mirrors
#d-i mirror/protocol string ftp
#d-i mirror/country string manual
#d-i mirror/ftp/hostname string ftp.ru.debian.org
#d-i mirror/ftp/directory string /debian
#d-i mirror/ftp/proxy string
d-i mirror/country string manual
d-i mirror/http/hostname string mirror.yandex.ru
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string ($HTTPPROXYv4)

d-i passwd/make-user boolean false

d-i passwd/root-password password ($PASS)
d-i passwd/root-password-again password ($PASS)

d-i clock-setup/utc boolean true

d-i time/zone string Europe/Moscow

d-i preseed/early_command string \
	anna-install parted-udeb 

# Partitioning
d-i partman/early_command string \
for DISK in $(list-devices disk); do \
    dd if=/dev/zero of=${DISK} bs=512 count=1; \
    parted -s ${DISK} mklabel gpt; \
done; \
set $(list-devices disk); \
let numb=$#/2; \
DISKA=$1; \
DISKB=$2; \
if [ -b "${DISKB}" ]; then \
    SIZE1=$(fdisk -l "${DISKA}" 2>/dev/null|grep Disk|grep dev|cut -d' ' -f5); \
    SIZE2=$(fdisk -l "${DISKB}" 2>/dev/null|grep Disk|grep dev|cut -d' ' -f5); \
	if [ -n ${SIZE1} ] && [ -n ${SIZE2} ] && [ "${SIZE1}" = "${SIZE2}" ]; then \
	    USE_MIRROR=yes; \
	else \
	    USE_MIRROR=no; \
	fi; \
fi; \
if [ "#${USE_MIRROR}" = "#yes" ]; then \
	debconf-set partman-auto/disk "$DISKA $DISKB";\
	debconf-set partman-auto/method "raid";\
	debconf-set partman-auto/expert_recipe "multiraid :: \  100 50 100 raid $primary{ } method{ raid } . \  128 512 100% raid method{ raid } . \  1024 10000 1000000000 raid method{ raid } . ";\
	debconf-set partman-auto-raid/recipe "1 2 0 ext2 /boot ${DISKA}1#${DISKB}1 . \  1 2 0 swap — ${DISKA}5#${DISKB}5 . \  1 2 0 ext4 / ${DISKA}6#${DISKB}6 . ";\
	debconf-set grub-installer/bootdev "$DISKA $DISKB";\
else \
	debconf-set partman-auto/disk "$DISKA";\
	debconf-set partman-auto/method "regular";\
	debconf-set partman-auto/expert_recipe "boot-root :: \  100 50 100 ext2 $primary{ } $bootable{ } method{ format } format{ } use_filesystem{ } filesystem{ ext2 } mountpoint{ /boot } . \  128 512 100% linux-swap method{ swap } format{ } . \  1024 10000 1000000000 ext4 method{ format } format{ } use_filesystem{ } filesystem{ ext4 } mountpoint{ / } . ";\
	debconf-set grub-installer/bootdev "$DISKA";\
fi 


#d-i partman-auto/method string regular

#d-i partman-auto/expert_recipe string \
# boot-root :: \
# 40 50 100 ext2 \
# $primary{ } $bootable{ } \
# method{ format } format{ } \
# use_filesystem{ } filesystem{ ext2 } \
# mountpoint{ /boot } \
# . \
# 500 10000 1000000000 ext4 \
# method{ format } format{ } \
# use_filesystem{ } filesystem{ ext4 } \
# mountpoint{ / } \
# . \
# 64 512 300% linux-swap \
# method{ swap } format{ } \
# .

# Force overwrite partitions

d-i partman-partitioning/choose_label       select gpt
d-i partman-partitioning/confirm_new_label  boolean true
d-i partman-partitioning/unknown_label  boolean true
d-i partman/exception_handler   select  Yes
partman-partitioning    partman-partitioning/choose_label       select gpt
partman-partitioning    partman-partitioning/confirm_new_label  boolean true
partman-partitioning    partman-partitioning/unknown_label  boolean true
partman-base    partman/exception_handler   select  Yes

d-i partman-auto/purge_lvm_from_device boolean true
d-i partman-lvm/device_remove_lvm boolean true 
d-i partman-md/device_remove_md boolean true
d-i partman-md/confirm boolean true
d-i partman-md/confirm_nooverwrite boolean true
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

d-i partman/mount_style select traditional

# Apt

#d-i base-installer/install-recommends boolean true

#d-i base-installer/kernel/linux/initramfs-generators string initramfs-tools
#d-i base-installer/kernel/image string linux-image-amd64

d-i apt-setup/contrib boolean true
#d-i apt-setup/use_mirror boolean true

# Packages
d-i apt-setup/services-select multiselect security, volatile
tasksel tasksel/first multiselect standard
d-i pkgsel/include string openssh-server vim wget

popularity-contest popularity-contest/participate boolean false

# Grub
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true

d-i finish-install/keep-consoles boolean true

d-i preseed/late_command string \
	in-target rm -f /etc/apt/apt.conf ;\
	ETHDEV=$(ip route show | grep default | grep -Eo 'dev\ .+\ ' | cut -d' ' -f2) ;\
	if [ -n "($IPv6)" ]; then \
		echo "# The loopback network interface" > /target/etc/network/interfaces ;\
		echo "auto lo" >> /target/etc/network/interfaces ;\
		echo "iface lo inet loopback" >> /target/etc/network/interfaces ;\
		echo "" >> /target/etc/network/interfaces ;\
		echo "# The primary network interface" >> /target/etc/network/interfaces ;\
		echo "allow-hotplug ${ETHDEV}" >> /target/etc/network/interfaces ;\
		echo "iface ${ETHDEV} inet6 static" >> /target/etc/network/interfaces ;\
		echo -e "\taddress ($IPv6)" >> /target/etc/network/interfaces ;\
		echo -e "\tnetmask ($NETMASKv6)" >> /target/etc/network/interfaces ;\
		echo -e "\tgateway ($GATEWAYv6)" >> /target/etc/network/interfaces ;\
		echo -e "\tdns-nameservers ($NAMESERVERv6)" >> /target/etc/network/interfaces ;\
		echo "nameserver ($NAMESERVERv6)" > /target/etc/resolv.conf ;\
		sed -i "s/($IPv4)/($IPv6)/" /etc/hosts ;\
		sed -i "s/($IPv4)/($IPv6)/" /target/etc/hosts ;\
		echo "# The loopback network interface" > /etc/network/interfaces ;\
		echo "auto lo" >> /etc/network/interfaces ;\
		echo "iface lo inet loopback" >> /etc/network/interfaces ;\
		echo "" >> /etc/network/interfaces ;\
		echo "# The primary network interface" >> /etc/network/interfaces ;\
		echo "allow-hotplug ${ETHDEV}" >> /etc/network/interfaces ;\
		echo "iface ${ETHDEV} inet6 static" >> /etc/network/interfaces ;\
		echo -e "\taddress ($IPv6)" >> /etc/network/interfaces ;\
		echo -e "\tnetmask ($NETMASKv6)" >> /etc/network/interfaces ;\
		echo -e "\tgateway ($GATEWAYv6)" >> /etc/network/interfaces ;\
		echo -e "\tdns-nameservers ($NAMESERVERv6)" >> /etc/network/interfaces ;\
		echo "nameserver ($NAMESERVERv6)" > /etc/resolv.conf ;\
	fi ;\
	in-target wget --no-check-certificate "($FINISHv4)"

d-i finish-install/reboot_in_progress note

BASH

Windows

Разворачивается готовый образ установленной операционной системы, у которой стоит запуск скрипта C:\vmmgr\firstrun.cmd в автозагрузке. Этот скрипт с помощью dd считывает данные из одномегабайтного виртуального диска, в котором находится скрипт, полученный от VMmanager. Автозапуск рекомендуется настраивать через Task Scheduler.

Скрипты автозагрузки Windows

Скрипты нужно расположить в C:\vmmgr\

firstrun.cmd

@echo off
powershell.exe C:\vmmgr\firstrun.ps1 >NUL
BASH

firstrun.ps1

$ddout = C:\vmmgr\ddwrap.cmd
$ddout
$list = $ddout | select-string "size is 1048576 bytes" -Context 3,0
$list
$context = $list.Context.PreContext | select-string Part
$str = $context.Line
$str
C:\vmmgr\dd.exe if=$str of=C:\vmmgr\vmmgr.cmd bs=512 count=2048
C:\vmmgr\vmmgr.cmd
BASH

ddwrap.cmd

c:\vmmgr\dd.exe --list 2>&1
BASH

При использовании наших скриптов загрузки, необходимо разрешить в PowerShell выполнение неподписанных скриптов. Для этого нужно зайти в PowerShell и выполнить команду:

Set-ExecutionPolicy RemoteSigned
BASH

Также необходимо скопировать в C:\vmmgr\ утилиту dd. Утилита доступна по ссылке.

Файлы шаблона

metainfo.xml

<?xml version="1.0"?>
<doc>
  <osname>Windows-Server-2012</osname>
  <limit>
    <elem name="ipv4">yes</elem>
    <elem name="ipv6">no</elem>
    <elem name="mem">1024</elem>
    <elem name="disk">10000</elem>
  </limit>
  <support>
    <elem>VMmgr</elem>
  </support>
  <hddimage>win_ser_2012_str.hddimage</hddimage>
  <rebootcount>1</rebootcount>
  <type>ostemplate</type>
  <installdrive>win_ser_2012_str.cmd</installdrive>
  <install_result>ok</install_result>
</doc>
BASH

Обратите внимание!

Перед строкой <?xml version="1.0"?> не должно быть пробелов. 

В скрипте установки необходимо раскомментировать строки для активации WIndows и указать свой ключ.

win_ser_2012_str.cmd

@echo off
set logfile=C:\setup.log 
echo ------Set static IP-------------------------------------[%DATE%-%TIME%]      > %logfile%
echo --- IP/NM=($IP)/($NETMASK)  GW=($GATEWAY)      >> %logfile%
echo --- NS1=($NAMESERVER)                                                   >> %logfile%
netsh interface ip set address    "Ethernet" static ($IP) ($NETMASK) ($GATEWAY)  >> %logfile%
netsh interface ip add dnsservers "Ethernet" ($NAMESERVER)          >> %logfile%
echo --- set Administartor password >> %logfile%
net user Administrator ($PASS)  >> %logfile%
set extendfile=C:\vmmgr\extend.txt
echo --- resize disk >> %logfile%
echo select volume 1 > %extendfile%
echo extend noerr >> %extendfile%
diskpart.exe /s %extendfile% >> %logfile%

#Uncomment the following 3 lines and enter your activation key.
#echo activate windows >> %logfile%
#cscript %windir%\system32\slmgr.vbs -ipk XXXXX-XXXXX-XXXXX-XXXXX-XXXX
#cscript %windir%\system32\slmgr.vbs -ato

echo remove task vmmgr_firstrun >> %logfile%
schtasks /Delete /TN "vmmgr_firstrun" /F  >> %logfile%
echo restart OS >> %logfile%
echo ------END--------------------------------------------------[%DATE%-%TIME%]     >> %logfile%

echo RMDIR /s /Q  C:\vmmgr >> c:\del.cmd
echo shutdown /r >> c:\del.cmd
cmd /c c:\del.cmd
BASH

CentOS с разворачиванием из файла


Можно сделать Linux шаблон, устанавливающийся не через файл ответов, а разворачивающийся из готового образа диска. Для этого необходимо, чтобы файловая система ОС поддерживала "растягивание на лету", то есть изменение размера на смонтированной ФС (например, ext4).

Для примера используется CentOS 6.

Создание образа диска

Для создания образа нужно создать виртуальную машину с CentOS с небольшим размером диска (2GB).

В данной машине можно установить нужное ПО, сделать нужные настройки.

Разбивка диска:

/dev/vda1 — /boot
/dev/vda2 — swap
/dev/vda3 — / 
BASH

Также нужно добавить в /etc/rc.local команду запуска скрипта установки, который будет подключен через опцию installdrive

echo "sh /dev/sda" >> /etc/rc.local
BASH

В debian файл /etc/rc.local заканчивается строкой exit 0 и команду запуска скрипта установки нужно добавлять не в конец файла, а до строки exit.

Если поддержки virtio нет, то установочный диск будет /dev/sdb.

После нужно сделать "дамп диска" в файл в директорию шаблона, например:

Для LVM

dd if=/dev/MyLvm/VMNAME of=centos_hdd.image
BASH

Для формата RAW/Qcow2

dd if=ПУТЬ_К_ФАЙЛУ of=centos_hdd.image
BASH

Скрипт установки

Пример скрипта установки для CentOS. Для Debian скрипт будет отличаться. Название файла указывается в параметре installdrive.

#!/bin/sh
 
clean_files() {
        rm -f /etc/ssh/*key*
        sed -r -i '/sh \/dev\/sda/d' /etc/rc.local
}
 
 
disk_format() {
        fdisk /dev/vda <<EOF                                                                                                                                                                 
c                                                                                                                                                                                            
d                                                                                                                                                                                            
3                                                                                                                                                                                            
n                                                                                                                                                                                            
p                                                                                                                                                                                            
3                                                                                                                                                                                            
                                                                                                                                                                                             
                                                                                                                                                                                             
w                                                                                                                                                                                            
EOF
}
 
resize_fs() {
        cat >> /etc/init.d/resize_fs << EOF
#!/bin/bash
#
# Resize fs
#
### BEGIN INIT INFO
# Default-Start:        1 2 3 4 5
# Default-Stop:         0 6
# Required-Start:
# Required-Stop?
# Short-Description:    Resize root filesystem
# Description:          Resize root filesystem
# Provides:             resize_fs
### END INIT INFO
 
. /etc/init.d/functions
 
case "\$1" in
        start|reload)
                resize2fs /dev/root
                chkconfig --del resize_fs
                rm -f /etc/init.d/resize_fs
                exit 0
                ;;
        *)
                echo "service resize_fs start"
                exit 2
esac
 
EOF
chmod +x /etc/init.d/resize_fs
chkconfig --add resize_fs
}
 
network_configure() {
        IPv4=($IPv4)
        NETMASK=($NETMASKv4)
        GATEWAYv4=($GATEWAYv4)
        IPv6=($IPv6)
        PREFIX=($NETMASKv6)
        GATEWAYv6=($GATEWAYv6)
        HOSTNAME=($HOSTNAME)
 
        if [ -n "${GATEWAYv4}" ]; then
                GATEWAY=${GATEWAYv4}
        else
                GATEWAY=${GATEWAYv6}
        fi
 
        # Removing udev rules
        rm -f /etc/udev/rules.d/70-persistent-net.rules
 
 
        # Configuring network
        sed -r -i '/HOSTNAME=.+/d; /GATEWAY=.+/d' /etc/sysconfig/network
cat >> /etc/sysconfig/network << EOF
HOSTNAME=${HOSTNAME}
GATEWAY=${GATEWAY}
EOF
 
        ip link set eth1 name eth0
        HWADDR=$(ip link show eth0 | awk '/link\/ether/ {print $2}' | tr [:lower:] [:upper:])
 
        cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
DEVICE="eth0"
BOOTPROTO="static"
DNS1="($NAMESERVER)"
HWADDR="${HWADDR}"
NM_CONTROLLED="yes"
ONBOOT="yes"
TYPE="Ethernet"
EOF
 
        if [ -n "${IPv4}" ]; then
                cat >> /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
GATEWAY="${GATEWAYv4}"
IPADDR="${IPv4}"
NETMASK="${NETMASK}"
EOF
 
        else
                cat >> /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF
IPV6INIT="yes"
IPV6ADDR="${IPv6}/${PREFIX}"
IPV6_DEFAULTGW="${GATEWAYv6}"
EOF
 
        fi
 
        ifup eth0
}
 
clean_files
disk_format
resize_fs
echo "($PASS)" | passwd --stdin root
network_configure
wget -q -O /dev/null --no-check-certificate "($FINISH)"
reboot
BASH

Обратите внимание!

 В скрипте указаны команды для утилиты fdisk. Они будут корректны, если внутри VM, с которой делается образ, сделана разбивка диска на vda1, vda2, vda3. 

Если устанавливать CentOS из нашего шаблона, то разбивка диска будет на vda1 и vda2. В этом случае команду для fdisk нужно изменить.

Данный скрипт:

  • изменит размер раздела;
  • выставит пароль;
  • добавит растягивание файловой системы при следующем запуске. Сразу же после изменения размера раздела сделать это нельзя, так как ядро ОС узнает об изменении разметки только при перезагрузке;
  • удалит из /etc/rc.local запуск себя.

metainfo.xml

<?xml version="1.0"?>
<doc>
  <osname>CentOS-6-amd64-fromdisk</osname>
  <limit>
    <elem name="ipv4">yes</elem>
    <elem name="ipv6">yes</elem>
    <elem name="mem">512</elem>
    <elem name="disk">2000</elem>
  </limit>
  <support>
    <elem version="5.4.0">VMmgr</elem>
  </support>
  <hddimage>centos_hdd.image</hddimage>
  <rebootcount>1</rebootcount>
  <type>ostemplate</type>
  <installdrive>install.sh</installdrive>
  <virtiodisk>yes</virtiodisk>
  <virtionet>yes</virtionet>
  <date>2013-09-20 12:59:19</date>
  <install_result>ok</install_result>
</doc>
BASH

Обратите внимание!

Имя файла образа должно оканчиваться на _hdd.image. Например, centos_hdd.image.