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.

358 lines
12 KiB

  1. #!/bin/bash
  2. # create-arista-veos-image.sh
  3. # HS-Fulda - sebastian.rieger@informatik.hs-fulda.de
  4. #
  5. # changelog:
  6. # V1.1 added injection of config defined in VM Maestro using config-drivex
  7. # V1.1.1 fixed device mapping of extracted partitions, fixed problems with stale swi directory
  8. # V1.1.2 rc.eos now supports e1000 and virtio as vnic types (virtio is supported in vEOS >=4.14.5F)
  9. # V1.2 added dynamic handling of device mapping of extacted partitions
  10. # V1.21 checking whether it safe to unmount working directories
  11. # V1.3 added support to delete existing image with the same name and generating the default nova flavor
  12. # V1.3.1 added support for newer glance releases (e.g. kilo) used in VIRL 1.0.0
  13. # V1.3.2 changed the extension of the bootloader iso to match the size of the partitions to be injected
  14. # V1.4 support for variable VEOS image sizes (as requested by @Jade_Deane to use custom VEOS images)
  15. # V1.4.1 fixed check for existing images
  16. # V1.5 fixed detection of missing loop files for Ubuntu 16.04 in VIRL >=1.3, added default subtype creation
  17. # usage
  18. if [ ! $# -eq 3 ] ; then
  19. echo -e "usage: $0 <Aboot-veos-serial-version.iso> <vEOS-version.vmdk> <new glance image name>, e.g.:\n"
  20. echo "$0 Aboot-veos-serial-2.0.8.iso vEOS-4.13.4F.vmdk vEOS"
  21. exit -1
  22. fi
  23. # sudo check
  24. if [ ! $UID -eq 0 ] ; then
  25. echo "Insufficient privileges. Please consider using sudo -s."
  26. exit -1
  27. fi
  28. ABOOT_SERIAL_ISO=$1
  29. ABOOT_SERIAL_ISO_BASENAME=$(basename -s .iso $1)
  30. VEOS_VMDK=$2
  31. VEOS_VMDK_BASENAME=$(basename -s .vmdk $2)
  32. GLANCE_IMAGE_NAME=$3
  33. GLANCE_IMAGE_RELEASE=$VEOS_VMDK_BASENAME-$ABOOT_SERIAL_ISO_BASENAME
  34. TMP_NAME="vEOS-$GLANCE_IMAGE_RELEASE"
  35. TIMESTAMP=$(date +%Y%m%d%H%M%S)
  36. function safe_unmount() {
  37. echo -n "Unmounting $1..."
  38. RETRY=0
  39. until umount $1 &>/dev/null
  40. do
  41. echo -n "."
  42. sleep 1
  43. RETRY=$((RETRY+1))
  44. if [ "$RETRY" -ge "5" ] ; then
  45. echo
  46. echo "ERROR: unable to unmount working directory $1"
  47. exit 1
  48. fi
  49. done
  50. echo
  51. return 0
  52. }
  53. # check for an existing image with the same name and offer to delete it prior to creating a new one
  54. CHECK_FOR_EXISTING_IMAGE=$(glance --os-image-api-version 1 image-show $GLANCE_IMAGE_NAME 2>&1)
  55. if [ $? == 0 ] ; then
  56. glance --os-image-api-version 1 image-show $GLANCE_IMAGE_NAME
  57. echo
  58. echo
  59. read -r -p "There is already an image with the same name in glance. Do you want to overwrite it? [y/N] " RESPONSE
  60. if [[ $RESPONSE =~ ^([yY][eE][sS]|[yY])$ ]] ; then
  61. echo "Deleting existing image $GLANCE_IMAGE_NAME..."
  62. echo "==========================================================="
  63. glance --os-image-api-version 1 image-delete $GLANCE_IMAGE_NAME
  64. else
  65. echo "An image with the same name already exists. Either delete this image or choose another name."
  66. exit 1
  67. fi
  68. fi
  69. echo
  70. echo "Creating vEOS image..."
  71. echo "==========================================================="
  72. # create a copy of Aboot bootloader and extend it to 3G
  73. echo "Creating a copy of Aboot bootloader in $TMP_NAME.raw..."
  74. cp $1 $TMP_NAME.raw
  75. echo
  76. echo "Extracting partitions from vEOS vmdk..."
  77. echo "==========================================================="
  78. # convert vmdk to raw and extract two partitions in it
  79. echo "Converting vmdk image to raw..."
  80. qemu-img convert -O raw $2 $VEOS_VMDK_BASENAME.raw
  81. LOOPDEV=$(kpartx -av $VEOS_VMDK_BASENAME.raw)
  82. LOOPDEV_PART1=$(echo "$LOOPDEV" | sed '1q;d' | cut -d " " -f 3)
  83. LOOPDEV_PART2=$(echo "$LOOPDEV" | sed '2q;d' | cut -d " " -f 3)
  84. echo "Output from kpartx: $LOOPDEV"
  85. echo "PART1: $LOOPDEV_PART1"
  86. echo "PART2: $LOOPDEV_PART2"
  87. echo -n "waiting for loop devs from kpartx"
  88. LOOP_DEV_RETRIES=0
  89. until dd if=/dev/mapper/$LOOPDEV_PART1 of=/dev/null bs=1k count=1 &>/dev/null && dd if=/dev/mapper/$LOOPDEV_PART2 of=/dev/null bs=1k count=1 &>/dev/null ; do
  90. echo -n "."
  91. LOOP_DEV_RETRIES=$(expr $LOOP_DEV_RETRIES + 1)
  92. if [ $LOOP_DEV_RETRIES -eq 10 ]; then
  93. echo
  94. echo "ERROR: timeout waiting for loop devs from kaprtx"
  95. exit
  96. fi
  97. sleep 1
  98. done
  99. echo
  100. echo "using dd to extract partitions..."
  101. dd if=/dev/mapper/$LOOPDEV_PART1 of=$VEOS_VMDK_BASENAME-p1.raw
  102. dd if=/dev/mapper/$LOOPDEV_PART2 of=$VEOS_VMDK_BASENAME-p2.raw
  103. LOOP_DEV_DEL_RETRIES=0
  104. echo "removing loop devs from kpartx"
  105. until kpartx -vd $VEOS_VMDK_BASENAME.raw ; do
  106. echo -n "."
  107. LOOP_DEV_DEL_RETRIES=$(expr $LOOP_DEV_DEL_RETRIES + 1)
  108. if [ $LOOP_DEV_DEL_RETRIES -eq 10 ]; then
  109. echo
  110. echo "ERROR: timeout waiting for loop dev removal from kaprtx"
  111. exit
  112. fi
  113. sleep 1
  114. done
  115. echo
  116. echo
  117. echo "Injecting rc.eos startup script to get switch config..."
  118. echo "==========================================================="
  119. # inject rc.eos script in first partition of the image, to get switch config defined in VM Maestro (config-drive)
  120. mkdir swi-$TIMESTAMP
  121. echo "loop mounting image..."
  122. mount -o loop $VEOS_VMDK_BASENAME-p1.raw swi-$TIMESTAMP
  123. cd swi-$TIMESTAMP
  124. echo "injecting rc.eos..."
  125. cat << EOF > rc.eos
  126. #!/bin/sh
  127. #
  128. # startup script to get node configs from VM Maestro
  129. #
  130. echo "Getting switch config from config drive..."
  131. echo "=========================================="
  132. mkdir /config-drive
  133. mount /dev/sdb1 /config-drive
  134. echo "Getting ip address for ma1 via dhcp..."
  135. echo "=========================================="
  136. MANAGEMENT_INTERFACE="ma1"
  137. ip link show \$MANAGEMENT_INTERFACE
  138. if [ \$? -ne 0 ]; then
  139. # if using virtio ma1 will not be up during Eos Init 1, hence we use eth0
  140. MANAGEMENT_INTERFACE="eth0"
  141. fi
  142. dhclient -r \$MANAGEMENT_INTERFACE
  143. dhclient -1 -v \$MANAGEMENT_INTERFACE >/mnt/flash/dhclient.log
  144. IP=\$(ip addr show \$MANAGEMENT_INTERFACE | grep inet | tr -s ' ' | cut -d ' ' -f 3 | sed s/"\/"/"\\\\\\\\\/"/g)
  145. echo \$IP
  146. sed s/"! ip of ma1 configured on launch"/"ip address \$IP"/g /config-drive/veos_config.txt >/mnt/flash/startup-config.tmp
  147. cat /mnt/flash/startup-config.tmp
  148. echo
  149. echo "Copying switch config from config drive..."
  150. echo "=========================================="
  151. cp /mnt/flash/startup-config.tmp /mnt/flash/startup-config
  152. EOF
  153. chmod 755 rc.eos
  154. cd ..
  155. echo "unmounting image..."
  156. safe_unmount swi-$TIMESTAMP
  157. rm -rf swi-$TIMESTAMP
  158. echo
  159. echo "Injecting new partitions from vEOS vmdk in Aboot image..."
  160. echo "==========================================================="
  161. echo "$VEOS_VMDK_BASENAME.raw (original vEOS vmdk layout):"
  162. fdisk -l $VEOS_VMDK_BASENAME.raw
  163. echo "$TMP_NAME.raw (original ABoot bootloader layout):"
  164. fdisk -l $TMP_NAME.raw
  165. # calulate size of the two partitions
  166. PART1_START=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw1" | tr -s " " | cut -d ' ' -f 3)
  167. PART1_END=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw1" | tr -s " " | cut -d ' ' -f 4)
  168. PART1_LENGTH=$(expr $PART1_END - $PART1_START)
  169. echo "raw1 start=$PART1_START,end=$PART1_END,length=$PART1_LENGTH"
  170. PART2_START=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw2" | tr -s " " | cut -d ' ' -f 2)
  171. PART2_END=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw2" | tr -s " " | cut -d ' ' -f 3)
  172. PART2_LENGTH=$(expr $PART2_END - $PART2_START)
  173. echo "raw2 start=$PART2_START,end=$PART2_END,length=$PART2_LENGTH"
  174. # extend the bootloader iso to be able to append the two partitions
  175. EXTENSION_SIZE=$(ls -lk $VEOS_VMDK_BASENAME.raw | tr -s " " | cut -d " " -f 5)
  176. echo "extending image $TMP_NAME.raw to +$EXTENSION_SIZE"
  177. truncate -s +$EXTENSION_SIZE $TMP_NAME.raw
  178. echo "appending partitions from vmdk in the bootloader iso in $TMP_NAME.raw..."
  179. # append the two partitions from vmdk in the bootloader iso
  180. echo -e "n
  181. p
  182. +$PART1_LENGTH
  183. t
  184. 2
  185. c
  186. a
  187. 2
  188. n
  189. p
  190. +$PART2_LENGTH
  191. t
  192. 3
  193. 12
  194. w" | fdisk $TMP_NAME.raw
  195. echo "$TMP_NAME.raw (new combined image layout):"
  196. fdisk -l $TMP_NAME.raw
  197. # copy the partitions from vEOS vmdk to new image
  198. LOOPDEV=$(kpartx -av $TMP_NAME.raw)
  199. LOOPDEV_PART2=$(echo "$LOOPDEV" | sed '2q;d' | cut -d " " -f 3)
  200. LOOPDEV_PART3=$(echo "$LOOPDEV" | sed '3q;d' | cut -d " " -f 3)
  201. echo "Output from kpartx: $LOOPDEV"
  202. echo "PART1: $LOOPDEV_PART1"
  203. echo "PART2: $LOOPDEV_PART2"
  204. echo -n "waiting for loop devs from kpartx"
  205. LOOP_DEV_RETRIES=0
  206. until dd if=/dev/mapper/$LOOPDEV_PART2 of=/dev/null bs=1k count=1 &>/dev/null && dd if=/dev/mapper/$LOOPDEV_PART3 of=/dev/null bs=1k count=1 &>/dev/null ; do
  207. echo -n "."
  208. LOOP_DEV_RETRIES=$(expr $LOOP_DEV_RETRIES + 1)
  209. if [ $LOOP_DEV_RETRIES -eq 10 ]; then
  210. echo
  211. echo "ERROR: timeout waiting for loop devs from kaprtx"
  212. exit
  213. fi
  214. sleep 1
  215. done
  216. echo
  217. echo "copying the partitions from vEOS vmdk to new image..."
  218. dd if=$VEOS_VMDK_BASENAME-p1.raw of=/dev/mapper/$LOOPDEV_PART2
  219. dd if=$VEOS_VMDK_BASENAME-p2.raw of=/dev/mapper/$LOOPDEV_PART3
  220. LOOP_DEV_DEL_RETRIES=0
  221. echo "removing loop devs from kpartx"
  222. until kpartx -vd $TMP_NAME.raw ; do
  223. echo -n "."
  224. LOOP_DEV_DEL_RETRIES=$(expr $LOOP_DEV_DEL_RETRIES + 1)
  225. if [ $LOOP_DEV_DEL_RETRIES -eq 10 ]; then
  226. echo
  227. echo "ERROR: timeout waiting for loop dev removal from kaprtx"
  228. exit
  229. fi
  230. sleep 1
  231. done
  232. echo
  233. echo
  234. echo "Convert new image to qcow2..."
  235. echo "==========================================================="
  236. # convert raw to qcow2
  237. qemu-img convert -O qcow2 $TMP_NAME.raw $TMP_NAME.qcow2
  238. echo
  239. echo "Cleaning up..."
  240. echo "==========================================================="
  241. #cleanup
  242. rm $TMP_NAME.raw
  243. rm $VEOS_VMDK_BASENAME-p1.raw
  244. rm $VEOS_VMDK_BASENAME-p2.raw
  245. rm $VEOS_VMDK_BASENAME.raw
  246. echo
  247. echo "Importing image into glance..."
  248. echo "==========================================================="
  249. glance image-create --container-format bare --disk-format qcow2 --visibility public --name $GLANCE_IMAGE_NAME \
  250. --file $TMP_NAME.qcow2 --property hw_disk_bus=ide --property serial=1 \
  251. --property hw_vif_model=e1000 --property hw_cdrom_type=ide --property release="$GLANCE_IMAGE_RELEASE" --property subtype=IOSv --property config_disk_type=disk
  252. # create default flavor
  253. CHECKING_FOR_EXISTING_FLAVOR=$(nova flavor-show $GLANCE_IMAGE_NAME.small 2>&1)
  254. if [ $? == 1 ]; then
  255. echo "Creating default flavor $GLANCE_IMAGE_NAME.small..."
  256. echo "==========================================================="
  257. nova flavor-create --is-public true $GLANCE_IMAGE_NAME.small auto 1024 0 1
  258. else
  259. echo "Default flavor $GLANCE_IMAGE_NAME.small already exists..."
  260. fi
  261. CHECKING_FOR_EXISTING_FLAVOR=$(nova flavor-show $GLANCE_IMAGE_NAME.medium 2>&1)
  262. if [ $? == 1 ]; then
  263. echo "Creating default flavor $GLANCE_IMAGE_NAME.medium..."
  264. echo "==========================================================="
  265. nova flavor-create --is-public true $GLANCE_IMAGE_NAME.medium auto 2048 0 1
  266. else
  267. echo "Default flavor $GLANCE_IMAGE_NAME.medium already exists..."
  268. fi
  269. # create deault subtype
  270. cat << EOF > dynamic-subtype-$GLANCE_IMAGE_NAME.json.default
  271. {
  272. "dynamic-subtypes": [
  273. {
  274. "plugin_name": "$GLANCE_IMAGE_NAME",
  275. "cli_serial": 1,
  276. "plugin_desc": "Arista vEOS",
  277. "baseline_image": "$GLANCE_IMAGE_NAME",
  278. "hw_ram": 1024,
  279. "hw_vm_extra": "",
  280. "interface_wrap": 7,
  281. "config_disk_type": "disk",
  282. "interface_pattern": "Ethernet{0}",
  283. "gui_visible": true,
  284. "config_file": "/veos_config.txt",
  285. "interface_first": 1,
  286. "gui_icon": "iosvl2",
  287. "plugin_base": "generic",
  288. "vnc_available": false,
  289. "interface_management": "Management1",
  290. "interface_range": 7,
  291. "baseline_flavor": "$GLANCE_IMAGE_NAME.medium"
  292. }
  293. ]
  294. }
  295. EOF
  296. CHECKING_FOR_EXISTING_SUBTYPE=$(virl_uwm_client subtype-info 2>&1 | grep $GLANCE_IMAGE_NAME)
  297. if [ $? == 1 ]; then
  298. echo "Creating default subtype $GLANCE_IMAGE_NAME..."
  299. echo "==========================================================="
  300. virl_uwm_client subtype-import --dynamic-subtypes @dynamic-subtype-$GLANCE_IMAGE_NAME.json.default
  301. else
  302. echo "Default subtype $GLANCE_IMAGE_NAME already exists..."
  303. fi
  304. rm dynamic-subtype-$GLANCE_IMAGE_NAME.json.default
  305. echo
  306. echo "Image creation successful."
  307. echo "==========================================================="
  308. echo
  309. echo "You can import and use the subtype $GLANCE_IMAGE_NAME in VM Maestro..."
  310. #testing:
  311. #
  312. # nova boot --image "Arista vEOS Disk" --flavor m1.small veos --nic net-id=abc7ad47-55fd-4396-8d31-91dd4d41a18a --nic net-id=abc7ad47-55fd-4396-8d31-91dd4d41a18a --nic net-id=abc7ad47-55fd-4396-8d31-91dd4d41a18a --nic net-id=abc7ad47-55fd-4396-8d31-91dd4d41a18a --nic net-id=abc7ad47-55fd-4396-8d31-91dd4d41a18a
  313. #
  314. # using VM Maestro, the image can be chosen as "VM image", e.g., for an IOSv or IOSvL2 node