Add scripts to run VMTests and convert images
run_vmtests will run the VMTests after build_image
convert_image will convert the image to VMDK or VHD
make_ova will create a ova image based on VMDK
BUG=b/173643442, b/171738548
TEST=manually test
RELEASE_NOTE=None
Change-Id: I99865424dcaefd1d588f3df7009d70cf86f71c95
Reviewed-on: https://cos-review.googlesource.com/c/third_party/platform/crosutils/+/10662
Reviewed-by: Robert Kolchmeyer <rkolchmeyer@google.com>
Reviewed-by: Varsha Teratipally <teratipally@google.com>
Tested-by: Roy Yang <royyang@google.com>
diff --git a/cos/README.md b/cos/README.md
new file mode 100644
index 0000000..a4895f0
--- /dev/null
+++ b/cos/README.md
@@ -0,0 +1,8 @@
+This folder contains image utilities from Container-Optimized OS(COS) team
+to fullfill the functionalities to support image formwat convertion between
+different platfroms:
+
+* COS on vSphere
+* COS on AWS
+* COS on AZure
+* COS on Borg
diff --git a/cos/convert_image.sh b/cos/convert_image.sh
new file mode 100755
index 0000000..bae8e40
--- /dev/null
+++ b/cos/convert_image.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# convert_image.sh --board=[board] --image_type=[type] --image_format=[format]
+#
+# This script converts a board's image(base, test, dev) to the specified format
+# like vmdk, vhd so that the image can be used by platform other than GCP.
+
+SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
+. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
+
+# Script must be run inside the chroot.
+restart_in_chroot_if_needed "$@"
+
+DEFINE_string board "${DEFAULT_BOARD}" \
+ "The board to build an image for."
+DEFINE_string image_type "base" \
+ "Image type to process, base, test or dev."
+DEFINE_string image_format "" \
+ "Image format to be converted to, vmdk or vhd."
+DEFINE_string image_dir "" "Path to the folder to store netboot images."
+
+# Parse command line.
+FLAGS "$@" || exit 1
+eval set -- "${FLAGS_ARGV}"
+
+. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
+. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
+
+switch_to_strict_mode
+
+set -x
+# build_packages artifact output.
+SYSROOT="${GCLIENT_ROOT}/chroot/build/${FLAGS_board}"
+# build_image artifact output.
+
+IMAGE_DIR="${CHROOT_TRUNK_DIR}"/src/build/images/"${FLAGS_board}"/latest
+if [ -n "${FLAGS_image_dir}" ]; then
+ IMAGE_DIR=${FLAGS_image_dir}
+fi
+IMAGE_TYPE=${FLAGS_image_type}
+
+case ${FLAGS_image_format} in
+ "vmdk")
+ qemu-img convert -p -o subformat=streamOptimized -O vmdk\
+ ${IMAGE_DIR}/chromiumos_${IMAGE_TYPE}_image.bin \
+ ${IMAGE_DIR}/chromiumos_${IMAGE_TYPE}_image.vmdk
+ ;;
+
+ "vhd")
+ qemu-img convert -f raw -o subformat=fixed,force_size -O vpc \
+ ${IMAGE_DIR}/chromiumos_${IMAGE_TYPE}_image.bin \
+ ${IMAGE_DIR}/chromiumos_${IMAGE_TYPE}_image.vhd
+ ;;
+
+ *)
+ ;;
+esac
diff --git a/cos/make_ova.sh b/cos/make_ova.sh
new file mode 100755
index 0000000..8791597
--- /dev/null
+++ b/cos/make_ova.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+#
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+#
+# This scripts creates .ova file from given disk image and OVA template.
+#
+
+set -o xtrace
+set -o errexit
+set -o nounset
+
+TEMPLATE_PATH=/usr/share/make-ova/template.ovf
+
+usage() {
+ echo "Usage: $0 -d disk.vmdk \
+ -p product-name -n image-name \
+ -o output-file [-t template.ovf]"
+}
+
+while getopts ":d:p:n:t:o:h" arg; do
+ case $arg in
+ d) DISK_FILE=$OPTARG ;;
+ p) PRODUCT_NAME=$OPTARG ;;
+ n) IMAGE_NAME=$OPTARG ;;
+ t) TEMPLATE_PATH=$OPTARG ;;
+ o) OUTPUT_FILE=$OPTARG ;;
+ h)
+ usage
+ exit 0
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+: "${DISK_FILE?Missing -d DISK_FILE value}"
+: "${PRODUCT_NAME?Missing -p PRODUCT_NAME value}"
+: "${IMAGE_NAME?Missing -n IMAGE_NAME value}"
+: "${TEMPLATE_PATH?Missing -t TEMPLATE_PATH value}"
+: "${OUTPUT_FILE?Missing -o OUTPUT_FILE value}"
+
+if [[ ! -f ${TEMPLATE_PATH} ]]; then
+ echo "Cannot find template at ${TEMPLATE_PATH}"
+ exit 1
+fi
+
+XML_NS=(
+ -N 'x=http://schemas.dmtf.org/ovf/envelope/1'
+ -N 'ovf=http://schemas.dmtf.org/ovf/envelope/1'
+ -N 'vssd=http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData'
+)
+
+WORK_DIR=$(mktemp -d)
+trap 'rm -rf "${WORK_DIR}"' EXIT
+
+# xmlstar does not support multiple updates at once, and we need to provide
+# namespaces to every invocation, so disable quoting warning.
+# shellcheck disable=SC2086
+xmlstarlet ed ${XML_NS[*]} \
+ --update '//x:VirtualSystem/@ovf:id' --value "${IMAGE_NAME}" \
+ "${TEMPLATE_PATH}" \
+ | xmlstarlet ed ${XML_NS[*]} \
+ --update '//x:VirtualSystem/x:Name' --value "${IMAGE_NAME}" \
+ | xmlstarlet ed ${XML_NS[*]} \
+ --update '//vssd:VirtualSystemIdentifier' --value "${IMAGE_NAME}" \
+ > "${WORK_DIR}/tmp.ovf"
+
+# Add a disk image to temporary .ovf
+cot --force add-disk "${DISK_FILE}" "${WORK_DIR}/tmp.ovf" \
+ -o "${WORK_DIR}/image.ovf" \
+ -f vmdisk1 -t harddisk -c scsi
+
+# Add product information and convert .ovf to .ova
+cot --force edit-product "${WORK_DIR}/image.ovf" \
+ -o "${OUTPUT_FILE}" \
+ --product "${PRODUCT_NAME}"
+
diff --git a/cos/run_vmtests.sh b/cos/run_vmtests.sh
new file mode 100755
index 0000000..b968973
--- /dev/null
+++ b/cos/run_vmtests.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# run_vmtests.sh --board=[board]
+#
+# This script builds and runs VMTests for a given board.
+
+SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
+. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
+
+# Script must be run inside the chroot.
+restart_in_chroot_if_needed "$@"
+
+DEFINE_string board "${DEFAULT_BOARD}" \
+ "The board to build an image for."
+DEFINE_string image_type "base" \
+ "Image type to process, base, test or dev."
+DEFINE_string image_dir "" "Path to the folder to store netboot images."
+
+# Parse command line.
+FLAGS "$@" || exit 1
+eval set -- "${FLAGS_ARGV}"
+
+. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
+. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
+
+switch_to_strict_mode
+
+set -x
+# build_packages artifact output.
+SYSROOT="${GCLIENT_ROOT}/chroot/build/${FLAGS_board}"
+# build_image artifact output.
+
+IMAGE_DIR="${CHROOT_TRUNK_DIR}"/src/build/images/"${FLAGS_board}"/latest
+if [ -n "${FLAGS_image_dir}" ]; then
+ IMAGE_DIR=${FLAGS_image_dir}
+fi
+
+cros_run_vm_test --board ${BOARD} \
+ --image-path ${IMAGE_DIR}/chromiumos_${IMAGE_TYPE}_image.bin \
+ --private-key ${IMAGE_DIR}/id_rsa \
+ --test_that-args=--model=ad_hoc_model \
+ --copy-on-write \
+ --start-vm \
+ --autotest 'suite:smoke'
diff --git a/cos/template.ovf b/cos/template.ovf
new file mode 100644
index 0000000..cdc96df
--- /dev/null
+++ b/cos/template.ovf
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <References>
+ </References>
+ <DiskSection>
+ <Info>Virtual disk information</Info>
+ </DiskSection>
+ <NetworkSection>
+ <Info>The list of logical networks</Info>
+ <Network ovf:name="VM Network">
+ <Description>The VM Network network</Description>
+ </Network>
+ </NetworkSection>
+ <VirtualSystem ovf:id="__NAME__">
+ <Info>A virtual machine</Info>
+ <Name>__NAME__</Name>
+ <OperatingSystemSection ovf:id="94" vmw:osType="genericLinuxGuest">
+ <Info>The kind of installed guest operating system</Info>
+ <Description>Other Linux</Description>
+ </OperatingSystemSection>
+
+ <ProductSection ovf:required="false">
+ <Info>Cloud-Init customization</Info>
+ <Product>__PRODUCT_REPLACED_BY_COT__</Product>
+ <Property ovf:key="instance-id" ovf:type="string" ovf:userConfigurable="true" ovf:value="id-ovf">
+ <Label>A Unique Instance ID for this instance</Label>
+ <Description>Specifies the instance id. This is required and used to determine if the machine should take "first boot" actions</Description>
+ </Property>
+ <Property ovf:key="hostname" ovf:type="string" ovf:userConfigurable="true" ovf:value="ubuntuguest">
+ <Description>Specifies the hostname for the appliance</Description>
+ </Property>
+ <Property ovf:key="seedfrom" ovf:type="string" ovf:userConfigurable="true">
+ <Label>Url to seed instance data from</Label>
+ <Description>This field is optional, but indicates that the instance should 'seed' user-data and meta-data from the given url. If set to 'http://tinyurl.com/sm-' is given, meta-data will be pulled from http://tinyurl.com/sm-meta-data and user-data from http://tinyurl.com/sm-user-data. Leave this empty if you do not want to seed from a url.</Description>
+ </Property>
+ <Property ovf:key="public-keys" ovf:type="string" ovf:userConfigurable="true" ovf:value="">
+ <Label>ssh public keys</Label>
+ <Description>This field is optional, but indicates that the instance should populate the default user's 'authorized_keys' with this value</Description>
+ </Property>
+ <Property ovf:key="user-data" ovf:type="string" ovf:userConfigurable="true" ovf:value="">
+ <Label>Encoded user-data</Label>
+ <Description>In order to fit into a xml attribute, this value is base64 encoded . It will be decoded, and then processed normally as user-data.</Description>
+ <!-- The following represents '#!/bin/sh\necho "hi world"'
+ ovf:value="IyEvYmluL3NoCmVjaG8gImhpIHdvcmxkIgo="
+ -->
+ </Property>
+ <Property ovf:key="password" ovf:type="string" ovf:userConfigurable="true" ovf:value="">
+ <Label>Default User's password</Label>
+ <Description>If set, the default user's password will be set to this value to allow password based login. The password will be good for only a single login. If set to the string 'RANDOM' then a random password will be generated, and written to the console.</Description>
+ </Property>
+ </ProductSection>
+
+ <VirtualHardwareSection ovf:transport="com.vmware.guestInfo">
+ <Info>Virtual hardware requirements</Info>
+ <System>
+ <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
+ <vssd:InstanceID>0</vssd:InstanceID>
+ <vssd:VirtualSystemIdentifier>__NAME__</vssd:VirtualSystemIdentifier>
+ <vssd:VirtualSystemType>vmx-13</vssd:VirtualSystemType>
+ </System>
+ <Item>
+ <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
+ <rasd:Description>Number of Virtual CPUs</rasd:Description>
+ <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
+ <rasd:InstanceID>1</rasd:InstanceID>
+ <rasd:ResourceType>3</rasd:ResourceType>
+ <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
+ <rasd:Description>Memory Size</rasd:Description>
+ <rasd:ElementName>1024MB of memory</rasd:ElementName>
+ <rasd:InstanceID>2</rasd:InstanceID>
+ <rasd:ResourceType>4</rasd:ResourceType>
+ <rasd:VirtualQuantity>1024</rasd:VirtualQuantity>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Description>SCSI Controller</rasd:Description>
+ <rasd:ElementName>SCSI Controller 0</rasd:ElementName>
+ <rasd:InstanceID>3</rasd:InstanceID>
+ <rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType>
+ <rasd:ResourceType>6</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>1</rasd:Address>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>VirtualIDEController 1</rasd:ElementName>
+ <rasd:InstanceID>4</rasd:InstanceID>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item>
+ <rasd:Address>0</rasd:Address>
+ <rasd:Description>IDE Controller</rasd:Description>
+ <rasd:ElementName>VirtualIDEController 0</rasd:ElementName>
+ <rasd:InstanceID>5</rasd:InstanceID>
+ <rasd:ResourceType>5</rasd:ResourceType>
+ </Item>
+ <Item ovf:required="false">
+ <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+ <rasd:ElementName>VirtualVideoCard</rasd:ElementName>
+ <rasd:InstanceID>6</rasd:InstanceID>
+ <rasd:ResourceType>24</rasd:ResourceType>
+ <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/>
+ <vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/>
+ </Item>
+ <Item ovf:required="false">
+ <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
+ <rasd:ElementName>VirtualVMCIDevice</rasd:ElementName>
+ <rasd:InstanceID>7</rasd:InstanceID>
+ <rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
+ <rasd:ResourceType>1</rasd:ResourceType>
+ <vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false"/>
+ </Item>
+ <Item>
+ <rasd:AddressOnParent>7</rasd:AddressOnParent>
+ <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
+ <rasd:Connection>VM Network</rasd:Connection>
+ <rasd:Description>VMXNET3 ethernet adapter on "VM Network"</rasd:Description>
+ <rasd:ElementName>GigabitEthernet1</rasd:ElementName>
+ <rasd:InstanceID>11</rasd:InstanceID>
+ <rasd:ResourceSubType>VMXNET3</rasd:ResourceSubType>
+ <rasd:ResourceType>10</rasd:ResourceType>
+ <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
+ </Item>
+ <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>
+ <vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="preset"/>
+ <vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="preset"/>
+ <vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="checkpoint"/>
+ <vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="preset"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.afterPowerOn" vmw:value="true"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.afterResume" vmw:value="true"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.beforeGuestShutdown" vmw:value="true"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.beforeGuestStandby" vmw:value="true"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
+ <vmw:Config ovf:required="false" vmw:key="tools.toolsUpgradePolicy" vmw:value="manual"/>
+ </VirtualHardwareSection>
+ </VirtualSystem>
+</Envelope>