Шаблоны операционных систем (шаблоны ОС) — наборы образов диска и скриптов установки, состоящие из дистрибутива и других необходимых для установки операционной системы данных. Используются для удобства установки операционных систем на виртуальные машины.
В статье описаны принципы создания собственного шаблона ОС.
Шаблон ОС представляет из себя XML-файл metainfo.xml и набор файлов для установки. В metainfo.xml описаны механизмы, которые будут использованы при установке.
Подготовленные нами шаблоны доступны в репозитории.
Для создания шаблона необходимо создать директорию [NFS-хранилище]/<НАЗВАНИЕ_ШАБЛОНА>. Внутри должен обязательно содержаться файл metainfo.xml с XML-описанием шаблона. Также в этой директории должны находиться все файлы, которые указаны в параметрах шаблона.
В описании шаблона необходимо добавить:
<date>2013-04-14 15:37:21</date>
<install_result>ok</install_result>
Данные строки являются флагом, который сигнализирует, что шаблон уже установлен. В примерах эти поля не указаны, т.к. описание представлено в формате, который используется для шаблонов в репозитории.
После проделанных действий шаблон отобразится в меню добавления шаблонов ОС. Его можно добавить и использовать. Все изменения, которые осуществляются в файлах шаблона, применяются сразу.
Принципы создания собственного репозитория шаблонов ОС см. в статье Создание собственного репозитория шаблонов ОС.
Список параметров
Параметры
- <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>
- <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>
Примеры шаблонов
kickstart (CentOS, Fedora, RedHat)
Рекомендуется использовать для шаблонов ОС CentOS, Fedora, RedHat. Подробнее о технологии kickstart см. в статье Википедии. Она позволяет гибко настраивать параметры установки. Для загрузки указывается kernel и initrd, а в команде ядра ссылка на файл ответов.
<?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>
%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
FreeBSD 9
Установка производится за счёт модифицированного образа установочного диска. В основе лежит образ bootonly.iso. Модифицируется файл /etc/rc.local который загружается после загрузки ОС с диска.
Чтобы изменить настройки установленной системы (пароль, сеть, пакеты и т.п.) и при этом пользователям не приходилось самостоятельно модифицировать образ используется следующий механизм: к виртуальной машине присоединяется дополнительный диск, который представляет из себя shell-скрипт, увеличенный до 1 мегабайта. После запуска ОС с диска запускается rc.local, который считывает все данные со второго жесткого диска и кладет их в файл /tmp/install.sh После чего запускает install.sh. В install.sh могут быть описаны любые действия.
Шаблон ОС включает в себя скрипт install.sh. В скрипте можно использовать макросы, вместо которых VMmanager подставит значения.
<?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>
#!/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
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>
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
Windows
Разворачивается готовый образ установленной операционной системы, у которой стоит запуск скрипта C:\vmmgr\firstrun.cmd в автозагрузке. Этот скрипт с помощью dd считывает данные из одномегабайтного виртуального диска, в котором находится скрипт, полученный от VMmanager. Автозапуск рекомендуется настраивать через Task Scheduler.
Скрипты автозагрузки Windows
Скрипты нужно расположить в C:\vmmgr\
@echo off
powershell.exe C:\vmmgr\firstrun.ps1 >NUL
$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
c:\vmmgr\dd.exe --list 2>&1
При использовании наших скриптов загрузки, необходимо разрешить в PowerShell выполнение неподписанных скриптов. Для этого нужно зайти в PowerShell и выполнить команду:
Set-ExecutionPolicy RemoteSigned
Также необходимо скопировать в C:\vmmgr\ утилиту dd. Утилита доступна по ссылке.
Файлы шаблона
<?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>
не должно быть пробелов. В скрипте установки необходимо раскомментировать строки для активации WIndows и указать свой ключ.
@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
CentOS с разворачиванием из файла
Можно сделать Linux шаблон, устанавливающийся не через файл ответов, а разворачивающийся из готового образа диска. Для этого необходимо, чтобы файловая система ОС поддерживала "растягивание на лету", то есть изменение размера на смонтированной ФС (например, ext4).
Для примера используется CentOS 6.
Создание образа диска
Для создания образа нужно создать виртуальную машину с CentOS с небольшим размером диска (2GB).
В данной машине можно установить нужное ПО, сделать нужные настройки.
Разбивка диска:
/dev/vda1 — /boot
/dev/vda2 — swap
/dev/vda3 — /
Также нужно добавить в /etc/rc.local команду запуска скрипта установки, который будет подключен через опцию installdrive
echo "sh /dev/sda" >> /etc/rc.local
Если поддержки virtio нет, то установочный диск будет /dev/sdb.
После нужно сделать "дамп диска" в файл в директорию шаблона, например:
dd if=/dev/MyLvm/VMNAME of=centos_hdd.image
dd if=ПУТЬ_К_ФАЙЛУ of=centos_hdd.image
Скрипт установки
Пример скрипта установки для 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
В скрипте указаны команды для утилиты fdisk. Они будут корректны, если внутри VM, с которой делается образ, сделана разбивка диска на vda1, vda2, vda3.
Если устанавливать CentOS из нашего шаблона, то разбивка диска будет на vda1 и vda2. В этом случае команду для fdisk нужно изменить.
Данный скрипт:
- изменит размер раздела;
- выставит пароль;
- добавит растягивание файловой системы при следующем запуске. Сразу же после изменения размера раздела сделать это нельзя, так как ядро ОС узнает об изменении разметки только при перезагрузке;
- удалит из /etc/rc.local запуск себя.
<?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>