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.

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