You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

455 lines
13 KiB

  1. #!/bin/sh
  2. set -e
  3. # Parse input parameters
  4. usage() {
  5. echo "Usage: $0 --release|-r <jessie|wheezy> [options]
  6. Options are:
  7. --minimal|-m
  8. --debootstrap-url|-u <debootstrap-mirror> (default: http://httpredir.debian.org/debian)
  9. --sources.list-mirror|-s <source-list-mirror> (default: http://httpredir.debian.org/debian)
  10. --extra-packages|-e <package>,<package>,...
  11. --hook-script|-hs <hook-script>
  12. --image-size|-is <image-size> (default: 2G)
  13. --automatic-resize|-ar
  14. --automatic-resize-space|-ars <suplementary-space> (default: 50M)
  15. --login|-l <userlogin> (default: debian)
  16. --password|-p <root-password> (dangerous option: avoid it if possible)
  17. For more info: man $0"
  18. exit 1
  19. }
  20. EXTRA=yes
  21. for i in $@ ; do
  22. case "${1}" in
  23. "--extra-packages"|"-e")
  24. if [ -z "${2}" ] ; then
  25. echo "No parameter defining the extra packages"
  26. usage
  27. fi
  28. EXTRA_PACKAGES=${2}
  29. shift
  30. shift
  31. ;;
  32. "--debootstrap-url"|"-u")
  33. if [ -z "${2}" ] ; then
  34. echo "No parameter defining the debootstrap URL"
  35. usage
  36. fi
  37. DEB_MIRROR=${2}
  38. shift
  39. shift
  40. ;;
  41. "--minimal"|"-m")
  42. EXTRA=no
  43. shift
  44. ;;
  45. "--automatic-resize"|"-ar")
  46. AUTOMATIC_RESIZE=yes
  47. shift
  48. ;;
  49. "--automatic-resize-space"|"-ars")
  50. if [ -z "${2}" ] ; then
  51. echo "No parameter defining the suplementary space"
  52. usage
  53. fi
  54. AUTOMATIC_RESIZE_SPACE=${2}
  55. shift
  56. shift
  57. ;;
  58. "--image-size"|"-is")
  59. if [ -z "${2}" ] ; then
  60. echo "No parameter defining the image size"
  61. usage
  62. fi
  63. IMAGE_SIZE=${2}
  64. shift
  65. shift
  66. ;;
  67. "--hook-script"|"-hs")
  68. if [ -z "${2}" ] ; then
  69. echo "No parameter defining the hook script"
  70. usage
  71. fi
  72. if ! [ -x "${2}" ] ; then
  73. echo "Hook script not executable"
  74. fi
  75. HOOK_SCRIPT=${2}
  76. shift
  77. shift
  78. ;;
  79. "--sources.list-mirror"|"-s")
  80. if [ -z "${2}" ] ; then
  81. echo "No parameter defining the hook script"
  82. usage
  83. fi
  84. SOURCE_LIST_MIRROR=${2}
  85. shift
  86. shift
  87. ;;
  88. "--release"|"-r")
  89. if [ "${2}" = "wheezy" ] || [ "${2}" = "jessie" ] || [ "${2}" = "stretch" ] || [ "${2}" = "buster" ] ; then
  90. RELEASE=${2}
  91. shift
  92. shift
  93. else
  94. echo "Release not recognized."
  95. usage
  96. fi
  97. ;;
  98. "--login"|"-l")
  99. if [ -z "${2}" ] ; then
  100. echo "No parameter defining the user login"
  101. usage
  102. fi
  103. USER_LOGIN=${2}
  104. shift
  105. shift
  106. ;;
  107. "--password"|"-p")
  108. if [ -z "${2}" ] ; then
  109. echo "No parameter defining the root password"
  110. fi
  111. ROOT_PASSWORD=${2}
  112. shift
  113. shift
  114. ;;
  115. *)
  116. ;;
  117. esac
  118. done
  119. if [ -z "${RELEASE}" ] ; then
  120. echo "Release not recognized: please specify the -r parameter."
  121. usage
  122. fi
  123. case "${RELEASE}" in
  124. "wheezy")
  125. RELEASE_NUM=7
  126. ;;
  127. "jessie")
  128. RELEASE_NUM=8
  129. ;;
  130. "stretch")
  131. RELEASE_NUM=9
  132. ;;
  133. "buster")
  134. RELEASE_NUM=10
  135. ;;
  136. esac
  137. if [ -z "${DEB_MIRROR}" ] ; then
  138. DEB_MIRROR=http://httpredir.debian.org/debian
  139. fi
  140. if [ -z "${EXTRA_PACKAGES}" ] ; then
  141. EXTRA_PACKAGES=bash-completion,joe,most,screen,less,vim,bzip2,nano
  142. fi
  143. if [ -z "${SOURCE_LIST_MIRROR}" ] ; then
  144. SOURCE_LIST_MIRROR=http://httpredir.debian.org/debian
  145. fi
  146. if [ -z "${IMAGE_SIZE}" ] ; then
  147. IMAGE_SIZE=2
  148. fi
  149. if [ -z "${AUTOMATIC_RESIZE_SPACE}" ] ; then
  150. AUTOMATIC_RESIZE_SPACE=50
  151. fi
  152. if [ -z "${USER_LOGIN}" ] ; then
  153. USER_LOGIN=debian
  154. fi
  155. NEEDED_PACKAGES=sudo,adduser,locales,extlinux,openssh-server,linux-image-amd64,euca2ools,file,kbd,aptitude
  156. if [ "${RELEASE}" = "wheezy" ] ; then
  157. # These are needed by cloud-init and friends, and since we don't want backports of them,
  158. # but just normal packages from Wheezy, we resolve dependencies by hand, prior to using
  159. # apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot
  160. NEEDED_PACKAGES=${NEEDED_PACKAGES},python,python-paramiko,python-argparse,python-cheetah,python-configobj,python-oauth,python-software-properties,python-yaml,python-boto,python-prettytable,initramfs-tools,python-requests,acpid,acpi-support-base
  161. else
  162. NEEDED_PACKAGES=${NEEDED_PACKAGES},cloud-init,cloud-utils,cloud-initramfs-growroot,dbus,libpam-systemd
  163. fi
  164. if [ ${EXTRA} = "no" ] ; then
  165. PKG_LIST=${NEEDED_PACKAGES}
  166. else
  167. PKG_LIST=${NEEDED_PACKAGES},${EXTRA_PACKAGES}
  168. fi
  169. if ! [ `whoami` = "root" ] ; then
  170. echo "You have to be root to run this script"
  171. exit 1
  172. fi
  173. FILE_NAME=debian-${RELEASE}-${RELEASE_NUM}.0.0-1-amd64
  174. AMI_NAME=${FILE_NAME}.raw
  175. QCOW2_NAME=${FILE_NAME}.qcow2
  176. rm -f ${AMI_NAME}
  177. set -x
  178. ######################################
  179. ### Prepare the HDD (format, ext.) ###
  180. ######################################
  181. PARTED=/sbin/parted
  182. rm -f $AMI_NAME
  183. qemu-img create ${AMI_NAME} ${IMAGE_SIZE}G
  184. #dd if=/dev/null bs=1M seek=1024 of=${AMI_NAME}
  185. ${PARTED} -s ${AMI_NAME} mktable msdos
  186. ${PARTED} -s -a optimal ${AMI_NAME} mkpart primary ext3 1Mi 100%
  187. ${PARTED} -s ${AMI_NAME} set 1 boot on
  188. install-mbr ${AMI_NAME}
  189. RESULT_KPARTX=`kpartx -asv ${AMI_NAME} 2>&1`
  190. if echo "${RESULT_KPARTX}" | grep "^add map" ; then
  191. LOOP_DEVICE=`echo ${RESULT_KPARTX} | cut -d" " -f3`
  192. echo "kpartx mounted using: ${LOOP_DEVICE}"
  193. else
  194. echo "It seems kpartx didn't mount the image correctly: exiting."
  195. exit 1
  196. fi
  197. cleanup(){
  198. error=$?
  199. [ ! -d "${MOUNT_DIR}" ] && return
  200. echo
  201. echo "error $error, umounting $MOUNT_DIR"
  202. chroot ${MOUNT_DIR} umount /proc || true
  203. chroot ${MOUNT_DIR} umount /sys || true
  204. umount ${MOUNT_DIR}
  205. rmdir ${MOUNT_DIR}
  206. kpartx -d ${AMI_NAME}
  207. exit $error
  208. }
  209. trap "cleanup" EXIT TERM INT
  210. # We first use ext2, THEN convert to ext3, because that's so much faster this way.
  211. mkfs.ext2 /dev/mapper/${LOOP_DEVICE}
  212. # No fsck because of X days without checks
  213. tune2fs -i 0 /dev/mapper/${LOOP_DEVICE}
  214. MOUNT_DIR=`mktemp -d -t build-debimg.XXXXXX`
  215. mount -o loop /dev/mapper/${LOOP_DEVICE} ${MOUNT_DIR}
  216. debootstrap --verbose \
  217. --include=${PKG_LIST} \
  218. ${RELEASE} ${MOUNT_DIR} ${DEB_MIRROR}
  219. ############################
  220. ### Customize the distro ###
  221. ############################
  222. ### Customize: access to the VM ###
  223. # # # # # # # # # # # # # # # # # #
  224. # Setup default root password to what has been set on the command line
  225. if [ -n "${ROOT_PASSWORD}" ] ; then
  226. chroot ${MOUNT_DIR} sh -c "echo root:${ROOT_PASSWORD} | chpasswd"
  227. fi
  228. # Otherwise, we have a huge backdoor, since the root password
  229. # is always the same.
  230. sed -i "s/PermitRootLogin yes/PermitRootLogin without-password/" ${MOUNT_DIR}/etc/ssh/sshd_config
  231. # Add a default user which is used by cloud-init by default
  232. chroot ${MOUNT_DIR} adduser --gecos Cloud-init-user --disabled-password --quiet ${USER_LOGIN}
  233. # Adds the "debian" user to sudoers, since that is the way
  234. # cloud-init grant access
  235. mkdir -p ${MOUNT_DIR}/etc/sudoers.d
  236. echo "${USER_LOGIN} ALL = NOPASSWD: ALL" >${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
  237. chmod 0440 ${MOUNT_DIR}/etc/sudoers.d/debian-cloud-init
  238. ### Customize: misc stuff ###
  239. # # # # # # # # # # # # # # #
  240. # Setup fstab
  241. echo "# /etc/fstab: static file system information.
  242. proc /proc proc nodev,noexec,nosuid 0 0
  243. /dev/vda1 / ext3 errors=remount-ro 0 1
  244. " > ${MOUNT_DIR}/etc/fstab
  245. chroot ${MOUNT_DIR} mount /proc || true
  246. echo "# disable pc speaker
  247. blacklist pcspkr" >${MOUNT_DIR}/etc/modprobe.d/blacklist.conf
  248. #echo "# Required for cinder hotplug
  249. #acpiphp
  250. #pci_hotplug
  251. #" >>${MOUNT_DIR}/etc/modules
  252. # Enable bash-completion by default
  253. if [ ${EXTRA} = "yes" ] ; then
  254. echo "# enable bash completion in interactive shells
  255. if ! shopt -oq posix; then
  256. if [ -f /usr/share/bash-completion/bash_completion ]; then
  257. . /usr/share/bash-completion/bash_completion
  258. elif [ -f /etc/bash_completion ]; then
  259. . /etc/bash_completion
  260. fi
  261. fi" >>${MOUNT_DIR}/etc/bash.bashrc
  262. # No clear for the tty1 console
  263. if [ "${RELEASE}" = "wheezy" ] ; then
  264. sed -i "s#1:2345:respawn:/sbin/getty 38400 tty1#1:2345:respawn:/sbin/getty --noclear 38400 tty1#" ${MOUNT_DIR}/etc/inittab
  265. else
  266. echo ForwardToConsole=yes >> ${MOUNT_DIR}/etc/systemd/journald.conf
  267. fi
  268. chroot ${MOUNT_DIR} apt-get install -y locales-all
  269. fi
  270. # Turn off console blanking which is *very* annoying
  271. # and increase KEYBOARD_DELAY because it can be annoying
  272. # over network.
  273. sed -i s/^BLANK_TIME=.*/BLANK_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
  274. sed -i s/^POWERDOWN_TIME=.*/POWERDOWN_TIME=0/ ${MOUNT_DIR}/etc/kbd/config
  275. sed -i 's/^[ \t#]KEYBOARD_DELAY=.*/KEYBOARD_DELAY=1000/' ${MOUNT_DIR}/etc/kbd/config
  276. rm -f ${MOUNT_DIR}/etc/ssh/ssh_host_*
  277. rm -f ${MOUNT_DIR}/etc/udev/rules.d/70-persistent-net.rules
  278. rm -f ${MOUNT_DIR}/lib/udev/write_net_rules
  279. # Setup networking (eg: DHCP by default)
  280. echo "# This file describes the network interfaces available on your system
  281. # and how to activate them. For more information, see interfaces(5).
  282. # The loopback network interface
  283. auto lo
  284. iface lo inet loopback
  285. # The normal eth0
  286. auto eth0
  287. iface eth0 inet dhcp
  288. # Maybe the VM has 2 NICs?
  289. allow-hotplug eth1
  290. iface eth1 inet dhcp
  291. # Maybe the VM has 3 NICs?
  292. allow-hotplug eth2
  293. iface eth2 inet dhcp
  294. " > ${MOUNT_DIR}/etc/network/interfaces
  295. # Setup the default hostname (will be set by cloud-init
  296. # at boot time anyway)
  297. echo "debian.example.com" >${MOUNT_DIR}/etc/hostname
  298. # This should be a correct default everywhere
  299. echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE} main
  300. deb-src ${SOURCE_LIST_MIRROR} ${RELEASE} main" >${MOUNT_DIR}/etc/apt/sources.list
  301. if [ "${RELEASE}" = "wheezy" ] || [ "${RELEASE}" = "jessie" ] ; then
  302. echo "deb ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
  303. deb-src ${SOURCE_LIST_MIRROR} ${RELEASE}-updates main
  304. deb http://security.debian.org/ ${RELEASE}/updates main
  305. deb-src http://security.debian.org/ ${RELEASE}/updates main
  306. " >>${MOUNT_DIR}/etc/apt/sources.list
  307. fi
  308. if [ "${RELEASE}" = "wheezy" ] ; then
  309. echo "deb ${SOURCE_LIST_MIRROR} wheezy-backports main
  310. deb-src ${SOURCE_LIST_MIRROR} wheezy-backports main
  311. " >>${MOUNT_DIR}/etc/apt/sources.list
  312. fi
  313. chroot ${MOUNT_DIR} apt-get update
  314. chroot ${MOUNT_DIR} apt-get upgrade -y
  315. # Setup cloud-init, cloud-utils and cloud-initramfs-growroot
  316. # These are only available from backports in Wheezy
  317. if [ "${RELEASE}" = "wheezy" ] ; then
  318. chroot ${MOUNT_DIR} apt-get -t wheezy-backports install cloud-init cloud-utils cloud-initramfs-growroot -y
  319. fi
  320. # For OpenStack, we would like to use Ec2 and no other API
  321. echo "# to update this file, run dpkg-reconfigure cloud-init
  322. datasource_list: [ConfigDrive, Openstack, Ec2]" >${MOUNT_DIR}/etc/cloud/cloud.cfg.d/90_dpkg.cfg
  323. # Needed to have automatic mounts of /dev/vdb
  324. echo "mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
  325. echo "manage_etc_hosts: true" >>${MOUNT_DIR}/etc/cloud/cloud.cfg
  326. # Set the cloud init default user (required for the keypair to be put in the right home directory)
  327. sed -i "s/name: debian/name: ${USER_LOGIN}/" ${MOUNT_DIR}/etc/cloud/cloud.cfg
  328. # Setting-up initramfs
  329. chroot ${MOUNT_DIR} update-initramfs -u
  330. rm ${MOUNT_DIR}/var/cache/apt/archives/*.deb
  331. # Set console for emergency and rescue shells
  332. SYSTEMD_DIR="${MOUNT_DIR}/etc/systemd/system/"
  333. for service in emergency.service rescue.service ; do
  334. mkdir "${SYSTEMD_DIR}/${service}.d"
  335. echo '[Service]
  336. ExecStart=
  337. ExecStart=-/bin/sh -c "/sbin/sulogin /dev/tty0; /bin/systemctl --fail --no-block default"' > "${SYSTEMD_DIR}/${service}.d/console.conf"
  338. done
  339. ###########################
  340. ### Setting-up extlinux ###
  341. ###########################
  342. UUID=`blkid -o value -s UUID /dev/mapper/${LOOP_DEVICE}`
  343. mkdir -p ${MOUNT_DIR}/boot/extlinux
  344. echo "default linux
  345. timeout 1
  346. label linux
  347. kernel /vmlinuz
  348. append initrd=/initrd.img root=/dev/vda1 console=tty0 console=ttyS0,115200 ro" > ${MOUNT_DIR}/boot/extlinux/extlinux.conf
  349. extlinux --install ${MOUNT_DIR}/boot/extlinux
  350. ###################
  351. ### HOOK SCRIPT ###
  352. ###################
  353. if [ -x ${HOOK_SCRIPT} ] ; then
  354. export BODI_CHROOT_PATH=${MOUNT_DIR}
  355. export BODI_RELEASE=${RELEASE}
  356. ${HOOK_SCRIPT}
  357. fi
  358. ##########################
  359. ### Unmount everything ###
  360. ##########################
  361. cleanup(){
  362. # refine cleanup everything was ok
  363. echo "Finished."
  364. }
  365. sync
  366. chroot ${MOUNT_DIR} umount /proc || true
  367. umount ${MOUNT_DIR}
  368. # Run FSCK so that resize can work
  369. tune2fs -j /dev/mapper/${LOOP_DEVICE}
  370. fsck.ext3 -f /dev/mapper/${LOOP_DEVICE} || true
  371. if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
  372. resize2fs -M /dev/mapper/${LOOP_DEVICE}
  373. FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
  374. WANTED_SIZE=`expr $FS_BLOCKS '*' 4 '/' 1024 + ${AUTOMATIC_RESIZE_SPACE}` # Add ${AUTOMATIC_RESIZE_SPACE}M
  375. resize2fs /dev/mapper/${LOOP_DEVICE} ${WANTED_SIZE}M
  376. FINAL_FS_BLOCKS=`tune2fs -l /dev/mapper/${LOOP_DEVICE} | awk '/Block count/{print $3}'`
  377. FINAL_IMG_SIZE=`expr '(' $FINAL_FS_BLOCKS + 258 ')' '*' 4 '/' 1024` # some blocks for mbr and multiple block size (4k)
  378. fi
  379. sync
  380. kpartx -d ${AMI_NAME}
  381. rmdir ${MOUNT_DIR}
  382. if [ "${AUTOMATIC_RESIZE}" = "yes" ] ; then
  383. # Rebuild a smaller partition table
  384. parted -s ${AMI_NAME} rm 1
  385. parted -s ${AMI_NAME} mkpart primary ext3 1Mi ${FINAL_IMG_SIZE}Mi
  386. parted -s ${AMI_NAME} set 1 boot on
  387. # Add 2M for the 1M at the beginning of the partition and some additionnal space
  388. truncate -s `expr 3 + ${FINAL_IMG_SIZE}`M ${AMI_NAME}
  389. install-mbr ${AMI_NAME}
  390. fi
  391. QEMU_VERSION=`qemu-img --help | head -n 1 | cut -d" " -f3 | cut -d"," -f1`
  392. if dpkg --compare-versions ${QEMU_VERSION} gt 1.0 ; then
  393. OTHER_QEMU_IMG_OPTIONS=" -o compat=0.10"
  394. else
  395. OTHER_QEMU_IMG_OPTIONS=""
  396. fi
  397. qemu-img convert -c -f raw ${AMI_NAME}${OTHER_QEMU_IMG_OPTIONS} -O qcow2 ${QCOW2_NAME}