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.

307 lines
10 KiB

9 years ago
  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.11 fixed device mapping of extracted partitions, fixed problems with stale swi directory
  8. # V1.12 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.31 added support for newer glance releases (e.g. kilo) used in VIRL 1.0.0
  13. # V1.32 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.5 automatic import of subtype and creation of flavors based on image name,
  16. # short instructions on how to use the image and hint to default node configuration,
  17. # removed detection of existing image as new VIRL version supports multiple versions
  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-2.0.8.iso vEOS-4.13.4F.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. echo
  55. echo "Creating vEOS image..."
  56. echo "==========================================================="
  57. # create a copy of Aboot bootloader and extend it to 3G
  58. cp $1 $TMP_NAME.raw
  59. echo
  60. echo "Extracting partitions from vEOS vmdk..."
  61. echo "==========================================================="
  62. # convert vmdk to raw and extract two partitions in it
  63. qemu-img convert -O raw $2 $VEOS_VMDK_BASENAME.raw
  64. LOOPDEV=$(kpartx -av $VEOS_VMDK_BASENAME.raw)
  65. LOOPDEV_PART1=$(echo "$LOOPDEV" | sed '1q;d' | cut -d " " -f 3)
  66. LOOPDEV_PART2=$(echo "$LOOPDEV" | sed '2q;d' | cut -d " " -f 3)
  67. dd if=/dev/mapper/$LOOPDEV_PART1 of=$VEOS_VMDK_BASENAME-p1.raw
  68. dd if=/dev/mapper/$LOOPDEV_PART2 of=$VEOS_VMDK_BASENAME-p2.raw
  69. kpartx -d $VEOS_VMDK_BASENAME.raw
  70. echo
  71. echo "Injecting rc.eos startup script to get switch config..."
  72. echo "==========================================================="
  73. # inject rc.eos script in first partition of the image, to get switch config defined in VM Maestro (config-drive)
  74. mkdir swi-$TIMESTAMP
  75. mount -o loop $VEOS_VMDK_BASENAME-p1.raw swi-$TIMESTAMP
  76. cd swi-$TIMESTAMP
  77. cat << EOF > rc.eos
  78. #!/bin/sh
  79. #
  80. # startup script to get node configs from VM Maestro
  81. #
  82. echo "Getting switch config from config drive..."
  83. echo "=========================================="
  84. mkdir /config-drive
  85. mount /dev/sdb1 /config-drive
  86. echo "Getting ip address for ma1 via dhcp..."
  87. echo "=========================================="
  88. MANAGEMENT_INTERFACE="ma1"
  89. ip link show \$MANAGEMENT_INTERFACE
  90. if [ \$? -ne 0 ]; then
  91. # if using virtio ma1 will not be up during Eos Init 1, hence we use eth0
  92. MANAGEMENT_INTERFACE="eth0"
  93. fi
  94. dhclient -r \$MANAGEMENT_INTERFACE
  95. dhclient -1 -v \$MANAGEMENT_INTERFACE >/mnt/flash/dhclient.log
  96. IP=\$(ip addr show \$MANAGEMENT_INTERFACE | grep inet | tr -s ' ' | cut -d ' ' -f 3 | sed s/"\/"/"\\\\\\\\\/"/g)
  97. echo \$IP
  98. sed s/"! ip of ma1 configured on launch"/"ip address \$IP"/g /config-drive/veos_config.txt >/mnt/flash/startup-config.tmp
  99. cat /mnt/flash/startup-config.tmp
  100. echo
  101. echo "Copying switch config from config drive..."
  102. echo "=========================================="
  103. cp /mnt/flash/startup-config.tmp /mnt/flash/startup-config
  104. EOF
  105. chmod 755 rc.eos
  106. cd ..
  107. safe_unmount swi-$TIMESTAMP
  108. rm -rf swi-$TIMESTAMP
  109. echo
  110. echo "Injecting new partitions from vEOS vmdk in Aboot image..."
  111. echo "==========================================================="
  112. # calulate size of the two partitions
  113. PART1_START=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw1" | tr -s " " | cut -d ' ' -f 3)
  114. PART1_END=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw1" | tr -s " " | cut -d ' ' -f 4)
  115. PART1_LENGTH=$(expr $PART1_END - $PART1_START)
  116. PART2_START=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw2" | tr -s " " | cut -d ' ' -f 2)
  117. PART2_END=$(fdisk -l $VEOS_VMDK_BASENAME.raw | grep "\.raw2" | tr -s " " | cut -d ' ' -f 3)
  118. PART2_LENGTH=$(expr $PART2_END - $PART2_START)
  119. # extend the bootloader iso to be able to append the two partitions
  120. EXTENSION_SIZE=$(ls -lk $VEOS_VMDK_BASENAME.raw | tr -s " " | cut -d " " -f 5)
  121. truncate -s +$EXTENSION_SIZE $TMP_NAME.raw
  122. # append the two partitions from vmdk in the bootloader iso
  123. echo -e "n
  124. p
  125. +$PART1_LENGTH
  126. t
  127. 2
  128. c
  129. a
  130. 2
  131. n
  132. p
  133. +$PART2_LENGTH
  134. t
  135. 3
  136. 12
  137. w" | fdisk $TMP_NAME.raw >/dev/null
  138. # copy the partitions from vEOS vmdk to new image
  139. LOOPDEV=$(kpartx -av $TMP_NAME.raw)
  140. LOOPDEV_PART2=$(echo "$LOOPDEV" | sed '2q;d' | cut -d " " -f 3)
  141. LOOPDEV_PART3=$(echo "$LOOPDEV" | sed '3q;d' | cut -d " " -f 3)
  142. dd if=$VEOS_VMDK_BASENAME-p1.raw of=/dev/mapper/$LOOPDEV_PART2
  143. dd if=$VEOS_VMDK_BASENAME-p2.raw of=/dev/mapper/$LOOPDEV_PART3
  144. kpartx -d $TMP_NAME.raw
  145. echo
  146. echo "Convert new image to qcow2..."
  147. echo "==========================================================="
  148. # convert raw to qcow2
  149. qemu-img convert -O qcow2 $TMP_NAME.raw $TMP_NAME.qcow2
  150. echo
  151. echo "Cleaning up..."
  152. echo "==========================================================="
  153. #cleanup
  154. rm $TMP_NAME.raw
  155. rm $VEOS_VMDK_BASENAME-p1.raw
  156. rm $VEOS_VMDK_BASENAME-p2.raw
  157. rm $VEOS_VMDK_BASENAME.raw
  158. echo
  159. echo "Importing image into glance..."
  160. echo "==========================================================="
  161. glance image-create --container-format bare --disk-format qcow2 --visibility public --name $GLANCE_IMAGE_NAME \
  162. --file $TMP_NAME.qcow2 --property hw_disk_bus=ide --property serial=1 \
  163. --property hw_vif_model=e1000 --property hw_cdrom_type=ide --property release="$GLANCE_IMAGE_RELEASE" --property subtype=IOSv --property config_disk_type=disk
  164. # create default flavor
  165. CHECKING_FOR_EXISTING_FLAVOR=$(nova flavor-show vEOS.small 2>&1)
  166. if [ $? == 1 ]; then
  167. echo "Creating default flavor vEOS.small..."
  168. echo "==========================================================="
  169. nova flavor-create --is-public true vEOS.small auto 1024 0 1
  170. fi
  171. CHECKING_FOR_EXISTING_FLAVOR=$(nova flavor-show $GLANCE_IMAGE_NAME.medium 2>&1)
  172. if [ $? == 1 ]; then
  173. echo "Creating default flavor $GLANCE_IMAGE_NAME.medium..."
  174. echo "==========================================================="
  175. nova flavor-create --is-public true $GLANCE_IMAGE_NAME.medium auto 2048 0 1
  176. fi
  177. CHECKING_FOR_EXISTING_FLAVOR=$(nova flavor-show $GLANCE_IMAGE_NAME.small 2>&1)
  178. if [ $? == 1 ]; then
  179. echo "Creating default flavor $GLANCE_IMAGE_NAME.small..."
  180. echo "==========================================================="
  181. nova flavor-create --is-public true $GLANCE_IMAGE_NAME.small auto 1024 0 1
  182. fi
  183. #CHECKING_FOR_EXISTING_SUBTYPE=$(virl_uwm_client subtype-export 2>/dev/null | grep -Po '"'"plugin_desc"'"\s*:\s*"\K([^"]*)')
  184. CHECKING_FOR_EXISTING_SUBTYPE=$(virl_uwm_client subtype-export 2>/dev/null | grep "\"plugin_name\": \"$GLANCE_IMAGE_NAME\",")
  185. if [ $? == 1 ]; then
  186. # create default subtype
  187. cat << EOF > $TIMESTAMP.default-subtype
  188. {
  189. "dynamic-subtypes": [
  190. {
  191. "interface_range": 22,
  192. "config_disk_type": "disk",
  193. "baseline_image": "$GLANCE_IMAGE_NAME",
  194. "baseline_flavor": "$GLANCE_IMAGE_NAME.medium",
  195. "hw_vm_extra": "",
  196. "plugin_desc": "Arista vEOS",
  197. "interface_pattern": "Ethernet{0}",
  198. "interface_management": "Management1",
  199. "gui_visible": true,
  200. "cli_serial": 1,
  201. "hw_ram": 1024,
  202. "plugin_name": "$GLANCE_IMAGE_NAME",
  203. "interface_first": 1,
  204. "config_file": "/veos_config.txt",
  205. "gui_icon": "iosvl2",
  206. "plugin_base": "generic"
  207. }
  208. ]
  209. }
  210. EOF
  211. echo "Creating default subtype vEOS..."
  212. echo "==========================================================="
  213. virl_uwm_client subtype-import --dynamic-subtypes @$TIMESTAMP.default-subtype
  214. rm $TIMESTAMP.default-subtype
  215. fi
  216. echo
  217. echo "Creation of the image finished successfully"
  218. echo "==========================================================="
  219. echo "You can import the default Subtype in VM Maestro using"
  220. echo "\"File->Preferences->Node Subtypes->Restore Defaults\""
  221. echo "followed by clicking on \"Fetch from Server\". Afterwards,"
  222. echo "vEOS should appear in the \"Nodes\" section of VM Maestro's"
  223. echo "Topology Palette. The following default config can be used"
  224. echo "by pasting it into the Configuration tab after clicking on"
  225. echo "each vEOS node created in your topology:"
  226. echo -e "
  227. ! device: veos-1 (vEOS, EOS-4.14.2F)
  228. !
  229. ! boot system flash:/vEOS.swi
  230. !
  231. transceiver qsfp default-mode 4x10G
  232. !
  233. hostname veos-1
  234. !
  235. spanning-tree mode mstp
  236. !
  237. no aaa root
  238. !
  239. username admin role network-admin secret 5 \$1\$93LlZesx\$MSqS1D/8NGTSY724FGx1K0
  240. username cisco role network-admin secret 5 \$1\$rQS0W9wP\$ZUzVG2XoGCCZCJopFp1aV/
  241. !
  242. interface Ethernet1
  243. !
  244. interface Ethernet2
  245. !
  246. interface Management1
  247. ! ip of ma1 configured on launch
  248. !
  249. no ip routing
  250. !
  251. !
  252. "
  253. echo "Of course you modify the config (name, version) to your needs."
  254. echo "Otherwise, vEOS will will start with an empty configuration and"
  255. echo "go into Zero-Touch-Provisioning (ZTP) mode. You should also disable"
  256. echo "\"Auto-generate the configuration based on these attributes\" in"
  257. echo "the AutoNetkit tab of the node, as AutoNetkit is only supported"
  258. echo "for the Cisco images in VIRL."
  259. #testing:
  260. #
  261. # 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
  262. #
  263. # using VM Maestro, the image can be chosen as "VM image", e.g., for an IOSv or IOSvL2 node