image_to_live.sh: move script here
This is the only repo left that uses this script, so move it here.
This is the same as the src/scripts/ file, so not doing style review.
BUG=None
TEST=precq passes
Change-Id: Ie5c19524503749a04655d1aa13eabec7b54af171
Reviewed-on: https://chromium-review.googlesource.com/1493351
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/au_test_harness/real_au_worker.py b/au_test_harness/real_au_worker.py
index b6fa0a2..8a6a74c 100644
--- a/au_test_harness/real_au_worker.py
+++ b/au_test_harness/real_au_worker.py
@@ -30,7 +30,7 @@
proxy_port=None, payload_signing_key=None):
"""Updates a remote image using image_to_live.sh."""
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
- cmd = ['%s/image_to_live.sh' % constants.CROSUTILS_DIR,
+ cmd = [os.path.join(constants.CROSTESTUTILS_DIR, 'image_to_live.sh'),
'--remote=%s' % self.remote,
stateful_change_flag,
'--verify',
@@ -43,7 +43,7 @@
proxy_port=None):
"""Updates a remote image using image_to_live.sh."""
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
- cmd = ['%s/image_to_live.sh' % constants.CROSUTILS_DIR,
+ cmd = [os.path.join(constants.CROSTESTUTILS_DIR, 'image_to_live.sh'),
'--payload=%s' % update_path,
'--remote=%s' % self.remote,
stateful_change_flag,
diff --git a/au_test_harness/vm_au_worker.py b/au_test_harness/vm_au_worker.py
index ca56787..c24b1b6 100644
--- a/au_test_harness/vm_au_worker.py
+++ b/au_test_harness/vm_au_worker.py
@@ -87,7 +87,7 @@
'--ssh-port=%s' % self._ssh_port,
self.graphics_flag,
'--host-cmd', '--',
- '%s/image_to_live.sh' % constants.CROSUTILS_DIR,
+ os.path.join(constants.CROSTESTUTILS_DIR, 'image_to_live.sh'),
'--ssh_port=%s' % self._ssh_port,
'--noupdate_hostkey',
'--update_log=%s' % os.path.join(log_directory, 'update_engine.log'),
diff --git a/image_to_live.sh b/image_to_live.sh
new file mode 100755
index 0000000..13e0cd1
--- /dev/null
+++ b/image_to_live.sh
@@ -0,0 +1,567 @@
+#!/bin/bash
+
+# Copyright (c) 2012 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.
+
+# Script to update an image onto a live running ChromiumOS instance.
+. $(dirname "$(readlink -f "$0")")/outside_chroot_common.sh || exit 1
+
+. "${SCRIPT_ROOT}/common.sh" ||
+ (echo "Unable to load common.sh" && false) ||
+ exit 1
+
+. "${SCRIPT_ROOT}/remote_access.sh" || die "Unable to load remote_access.sh"
+
+# Flags to control image_to_live.
+DEFINE_boolean ignore_hostname ${FLAGS_TRUE} \
+ "Ignore existing AU hostname on running instance use this hostname."
+DEFINE_boolean ignore_version ${FLAGS_TRUE} \
+ "Ignore existing version on running instance and always update."
+DEFINE_string netdev "eth0" \
+ "The network device to use for figuring out hostname. \
+ This is useful on hosts with multiple NICs."
+DEFINE_string server_log "dev_server.log" \
+ "Path to log for the devserver."
+DEFINE_boolean update "${FLAGS_TRUE}" \
+ "Perform update of root partition."
+DEFINE_boolean update_hostkey ${FLAGS_TRUE} \
+ "Update your known_hosts with the new remote instance's key."
+DEFINE_string update_log "update_engine.log" \
+ "Path to log for the update_engine."
+DEFINE_string update_url "" "Full url of an update image."
+DEFINE_boolean verify ${FLAGS_TRUE} "Verify image on device after update."
+DEFINE_integer repeat 1 "Number of times to run image_to_live."
+
+# Flags for devserver.
+DEFINE_string archive_dir "" \
+ "Deprecated."
+DEFINE_string board "" "Override the board reported by the target"
+DEFINE_integer devserver_port 8080 \
+ "Port to use for devserver."
+DEFINE_string image "" \
+ "Path to the image file to update with, xbuddy paths accepted." i
+DEFINE_string payload "" \
+ "Update with this update payload, ignoring specified images."
+DEFINE_string proxy_port "" \
+ "Have the client request from this proxy instead of devserver."
+DEFINE_string src_image "" \
+ "Create a delta update by passing in the image on the remote machine."
+DEFINE_boolean update_stateful ${FLAGS_TRUE} \
+ "Perform update of stateful partition e.g. /var /usr/local."
+DEFINE_boolean reboot_after_update ${FLAGS_TRUE} \
+ "Reboot after update applied for the update to take effect."
+
+# Flags for stateful update.
+DEFINE_string stateful_update_flag "" \
+ "Flag to pass to stateful update e.g. old, clean, etc." s
+
+DEPRECATION_WARNING="
+!!! You are using a deprecated script !!!
+
+Please use 'cros flash' in the future. See 'cros flash -h' for the details.
+More information available in the link below.
+https://sites.google.com/a/chromium.org/dev/chromium-os/build/cros-flash
+"
+
+FLAGS_HELP="
+Usage: $0 --remote=[target_ip] [--image=[...]] ...
+The remote flag is required to specify a ChromeOS machine to reimage.
+The image flag can be a path to a local image or an XBuddy path.
+For example:
+ $0 --remote=172.0.0.0 --image=./some/path/to/chromium_test_image.bin
+ Would reimage device at 172.0.0.0 with that locally available image.
+ $0 --remote=172.0.0.0 --image='xbuddy:remote/parrot/latest/dev'
+ Uses the latest developer parrot image available on Google Storage.
+ $0 --remote=172.0.0.0 --image='xbuddy:release'
+ Uses the latest test image available on Google Storage.
+ $0 --remote=172.0.0.0 --image='xbuddy:'
+ Uses the latest locally built image for the device board.
+Please see http://goo.gl/6WdLrD for XBuddy documentation."
+
+UPDATER_BIN="/usr/bin/update_engine_client"
+UPDATER_IDLE="UPDATE_STATUS_IDLE"
+UPDATER_NEED_REBOOT="UPDATE_STATUS_UPDATED_NEED_REBOOT"
+UPDATER_UPDATE_CHECK="UPDATE_STATUS_CHECKING_FOR_UPDATE"
+UPDATER_DOWNLOADING="UPDATE_STATUS_DOWNLOADING"
+
+IMAGE_PATH=""
+UPDATE_PATH=""
+ROOTFS_MOUNTPT=""
+STATEFUL_MOUNTPT=""
+
+kill_all_devservers() {
+ # Using ! here to avoid exiting with set -e is insufficient, so use
+ # || true instead.
+ sudo pkill -f devserver\.py || true
+}
+
+cleanup() {
+ if [ -z "${FLAGS_update_url}" ]; then
+ kill_all_devservers
+ fi
+ cleanup_remote_access
+ sudo rm -rf "${TMP}" || true
+ if [ ! -z "${ROOTFS_MOUNTPT}" ]; then
+ rm -rf "${ROOTFS_MOUNTPT}"
+ fi
+ if [ ! -z "${STATEFUL_MOUNTPT}" ]; then
+ rm -rf "${STATEFUL_MOUNTPT}"
+ fi
+}
+
+remote_reboot_sh() {
+ rm -f "${TMP_KNOWN_HOSTS}"
+ remote_sh "$@"
+}
+
+# Returns the hostname of this machine.
+# It tries to find the ipaddress using ifconfig, however, it will
+# default to $HOSTNAME on failure. We try to use the ip address first as
+# some targets may have dns resolution issues trying to contact back
+# to us.
+get_hostname() {
+ local hostname
+ # Try to parse ifconfig for ip address. Use sudo, because not all distros
+ # allow a common user to call ifconfig.
+ # TODO(zbehan): What if target is not connected via eth0? Update over wifi?
+ # Dedicated usb NIC? Perhaps this detection should be done in the target,
+ # which will get the return address in one way or another. Or maybe we should
+ # just open a ssh tunnel and use localhost.
+ hostname=$(/sbin/ifconfig ${FLAGS_netdev} |
+ grep 'inet addr' |
+ cut -f2 -d':' |
+ cut -f1 -d' ')
+
+ # Fallback to $HOSTNAME if that failed
+ [ -z "${hostname}" ] && hostname=${HOSTNAME}
+
+ echo ${hostname}
+}
+
+is_xbuddy_path() {
+ [[ "${FLAGS_image}" == xbuddy:* ]]
+}
+
+# Starts the devserver and returns the update url to use to get the update.
+start_dev_server() {
+ kill_all_devservers
+ local devserver_flags="--board=${FLAGS_board} --port=${FLAGS_devserver_port}"
+ # Parse devserver flags.
+ if [ -n "${FLAGS_image}" ]; then
+ if is_xbuddy_path; then
+ info "Image flag is an xBuddy path to an image."
+ else
+ info "Forcing the devserver to serve a local image."
+ devserver_flags="${devserver_flags} --pregenerate_update \
+ --image=$(reinterpret_path_for_chroot ${FLAGS_image})"
+ IMAGE_PATH="${FLAGS_image}"
+ fi
+ elif [ -n "${FLAGS_archive_dir}" ]; then
+ echo "archive_dir flag is deprecated. Use --image."
+ exit 1
+ else
+ # IMAGE_PATH should be the newest image and learn the board from
+ # the target.
+ learn_board
+ IMAGE_PATH="${IMAGES_DIR}/${FLAGS_board}/latest"
+ if [[ ! -L ${IMAGE_PATH} ]]; then
+ die "No image found; have you run build_image?"
+ fi
+ IMAGE_PATH="${IMAGE_PATH}/chromiumos_image.bin"
+ devserver_flags="${devserver_flags} \
+ --image=$(reinterpret_path_for_chroot ${IMAGE_PATH})"
+ fi
+
+ if [ -n "${FLAGS_payload}" ]; then
+ devserver_flags="${devserver_flags} \
+ --payload=$(reinterpret_path_for_chroot ${FLAGS_payload})"
+ fi
+
+ if [ -n "${FLAGS_proxy_port}" ]; then
+ devserver_flags="${devserver_flags} \
+ --proxy_port=${FLAGS_proxy_port}"
+ fi
+
+ if [ -n "${FLAGS_src_image}" ]; then
+ devserver_flags="${devserver_flags} \
+ --src_image=\"$(reinterpret_path_for_chroot ${FLAGS_src_image})\""
+ fi
+ # Remove any extra whitespace between words in flags.
+ devserver_flags=$(echo ${devserver_flags} | sed 's/ \+/ /g')
+ info "Starting devserver with flags ${devserver_flags}"
+
+ # Clobber dev_server log in case image_to_live is run with sudo previously.
+ if [ -f "${FLAGS_server_log}" ]; then
+ sudo rm -f "${FLAGS_server_log}"
+ fi
+
+ # Need to inherit environment variables to discover gsutil credentials.
+ if cros_sdk -- cros_workon --host list |
+ grep chromeos-base/cros-devutils &> /dev/null; then
+ info "cros_workon for devserver detected. Running devserver from source."
+ cros_sdk -- sudo -E ../platform/dev/devserver.py ${devserver_flags} \
+ > ${FLAGS_server_log} 2>&1 &
+ else
+ cros_sdk -- sudo -E start_devserver ${devserver_flags} \
+ > ${FLAGS_server_log} 2>&1 &
+ fi
+
+ info "Waiting on devserver to start. " \
+ "Be patient as the server generates the update before starting."
+ until netstat -lnp 2>&1 | grep :${FLAGS_devserver_port} > /dev/null; do
+ sleep 5
+ if ! pgrep -f "devserver" > /dev/null; then
+ tail -n 10 "${FLAGS_server_log}"
+ die "Devserver failed, see dev_server.log -- snippet above"
+ fi
+ done
+ if is_xbuddy_path; then
+ local devserver_url=$(echo $(get_devserver_url) | sed "s#/update##")
+ local xbuddy_path="xbuddy/${FLAGS_image##*xbuddy:}?for_update=true"
+ info "Xbuddy image detected. Using xbuddy RPC: " ${xbuddy_path}
+ UPDATE_PATH="$(curl -f "${devserver_url}/${xbuddy_path}")"
+ if [ -z "${UPDATE_PATH}" ]; then
+ tail -n 10 "${FLAGS_server_log}"
+ die "XBuddy failed to stage the image specified by ${FLAGS_image}."
+ fi
+ info "XBuddy returned uri to use for update: ${UPDATE_PATH}."
+ if [[ "${UPDATE_PATH}" != *"/update"* ]]; then
+ die "XBuddy did not return a valid update uri."
+ fi
+ fi
+}
+
+# Copies stateful update script which fetches the newest stateful update
+# from the dev server and prepares the update. chromeos_startup finishes
+# the update on next boot.
+run_stateful_update() {
+ local dev_url="${UPDATE_PATH}"
+ local stateful_url=""
+ local stateful_update_args=""
+
+ # Parse stateful update flag.
+ if [ -n "${FLAGS_stateful_update_flag}" ]; then
+ stateful_update_args="${stateful_update_args} \
+ --stateful_change ${FLAGS_stateful_update_flag}"
+ fi
+
+ # Assume users providing an update url are using an archive_dir path.
+ stateful_url=$(echo ${dev_url} | sed -e "s/update/static/")
+
+ info "Starting stateful update using URL ${stateful_url}"
+
+ # Copy over update script and run update.
+ local chroot_path="${SCRIPTS_DIR}/../../chroot"
+ local stateful_update_script="/usr/bin/stateful_update"
+
+ remote_cp_to "${chroot_path}/${stateful_update_script}" "/tmp"
+ remote_sh "mount -o remount,exec /tmp"
+ remote_sh "/tmp/stateful_update ${stateful_update_args} ${stateful_url}"
+}
+
+get_update_args() {
+ if [ -z "${1}" ]; then
+ die "No url provided for update."
+ fi
+
+ local update_args="--omaha_url=${1}"
+
+ info "Omaha URL: " ${update_args}
+
+ if [[ ${FLAGS_ignore_version} -eq ${FLAGS_TRUE} ]]; then
+ info "Forcing update independent of the current version"
+ update_args="--update ${update_args}"
+ fi
+
+ echo "${update_args}"
+}
+
+get_devserver_url() {
+ local devserver_url=""
+ local port=${FLAGS_devserver_port}
+
+ if [[ -n ${FLAGS_proxy_port} ]]; then
+ port=${FLAGS_proxy_port}
+ fi
+
+ if [ ${FLAGS_ignore_hostname} -eq ${FLAGS_TRUE} ]; then
+ if [ -z "${FLAGS_update_url}" ]; then
+ devserver_url="http://$(get_hostname):${port}/update"
+ else
+ devserver_url="${FLAGS_update_url}"
+ fi
+ fi
+
+ echo "${devserver_url}"
+}
+
+truncate_update_log() {
+ remote_sh "> /var/log/update_engine.log"
+}
+
+get_update_log() {
+ remote_sh "cat /var/log/update_engine.log"
+ echo "${REMOTE_OUT}" > "${FLAGS_update_log}"
+}
+
+# Used to store the current update status of the remote update engine.
+REMOTE_UPDATE_STATUS=
+
+# Returns ${1} reported by the update client e.g. PROGRESS, CURRENT_OP.
+get_var_from_remote_status() {
+ echo "${REMOTE_UPDATE_STATUS}" |
+ grep ${1} |
+ cut -f 2 -d =
+}
+
+# Updates the remote status variable for the update engine.
+update_remote_status() {
+ remote_sh "${UPDATER_BIN} --status 2> /dev/null"
+ REMOTE_UPDATE_STATUS="${REMOTE_OUT}"
+}
+
+# Both updates the remote status and gets the given variables.
+get_update_var() {
+ update_remote_status
+ get_var_from_remote_status "${1}"
+}
+
+# Returns the current status / progress of the update engine.
+# This is expected to run in its own thread.
+status_thread() {
+ local timeout=5
+
+ info "Devserver handling ping. Check ${FLAGS_server_log} for more info."
+ sleep ${timeout}
+
+ update_remote_status
+ local current_state=""
+ local next_state="$(get_var_from_remote_status CURRENT_OP)"
+
+ # For current status, only print out status changes.
+ # For download, show progress.
+ # Finally if no status change print out .'s to keep dev informed.
+ while [ "${current_state}" != "${UPDATER_NEED_REBOOT}" ] && \
+ [ "${current_state}" != "${UPDATER_IDLE}" ]; do
+ if [ "${current_state}" != "${next_state}" ]; then
+ info "State of updater has changed to: ${next_state}"
+ elif [ "${next_state}" = "${UPDATER_DOWNLOADING}" ]; then
+ echo "Download progress $(get_var_from_remote_status PROGRESS)"
+ else
+ echo -n "."
+ fi
+
+ sleep ${timeout}
+ current_state="${next_state}"
+ update_remote_status
+ next_state="$(get_var_from_remote_status CURRENT_OP)"
+ done
+}
+
+# Pings the update_engine to see if it responds or a max timeout is reached.
+# Returns 1 if max timeout is reached.
+wait_until_update_engine_is_ready() {
+ local wait_timeout=1
+ local max_timeout=60
+ local time_elapsed=0
+ while ! get_update_var CURRENT_OP > /dev/null; do
+ sleep ${wait_timeout}
+ time_elapsed=$(( time_elapsed + wait_timeout ))
+ echo -n "."
+ if [ ${time_elapsed} -gt ${max_timeout} ]; then
+ return 1
+ fi
+ done
+}
+
+# Runs the autoupdate.
+run_auto_update() {
+ # Truncate the update log so our log file is clean.
+ truncate_update_log
+
+ local update_args="$(get_update_args "${UPDATE_PATH}")"
+ info "Waiting to initiate contact with the update_engine."
+ wait_until_update_engine_is_ready || die "Could not contact update engine."
+
+ info "Starting update using args ${update_args}"
+
+ # Sets up secondary threads to track the update progress and logs
+ status_thread &
+ local status_thread_pid=$!
+ trap "kill ${status_thread_pid}; cleanup" EXIT INT TERM
+
+ # Actually run the update. This is a blocking call.
+ remote_sh "${UPDATER_BIN} ${update_args}"
+
+ # Clean up secondary threads.
+ kill ${status_thread_pid} 2> /dev/null || warn "Failed to kill status thread"
+ trap cleanup EXIT INT TERM
+
+ local update_status="$(get_update_var CURRENT_OP)"
+ if [ "${update_status}" = ${UPDATER_NEED_REBOOT} ]; then
+ info "Autoupdate was successful."
+ return 0
+ else
+ warn "Autoupdate was unsuccessful. Status returned was ${update_status}."
+ return 1
+ fi
+}
+
+verify_image() {
+ info "Verifying image."
+ if [ -n "${FLAGS_update_url}" ]; then
+ warn "Verify is not compatible with setting an update url."
+ return
+ fi
+
+ if is_xbuddy_path; then
+ warn "Verify is not currently compatible with xbuddy."
+ return
+ fi
+
+ ROOTFS_MOUNTPT=$(mktemp -d)
+ STATEFUL_MOUNTPT=$(mktemp -d)
+ "${SCRIPTS_DIR}/mount_gpt_image.sh" --from "$(dirname "${IMAGE_PATH}")" \
+ --image "$(basename ${IMAGE_PATH})" \
+ --rootfs_mountpt="${ROOTFS_MOUNTPT}" \
+ --stateful_mountpt="${STATEFUL_MOUNTPT}" \
+ --read_only
+
+ local lsb_release=$(cat ${ROOTFS_MOUNTPT}/etc/lsb-release)
+ info "Verifying image with release:"
+ echo ${lsb_release}
+
+ "${SCRIPTS_DIR}/mount_gpt_image.sh" --unmount \
+ --rootfs_mountpt="${ROOTFS_MOUNTPT}" \
+ --stateful_mountpt="${STATEFUL_MOUNTPT}"
+
+ remote_sh "cat /etc/lsb-release"
+ info "Remote image reports:"
+ echo ${REMOTE_OUT}
+
+ if [ "${lsb_release}" = "${REMOTE_OUT}" ]; then
+ info "Update was successful and image verified as ${lsb_release}."
+ return 0
+ else
+ warn "Image verification failed."
+ return 1
+ fi
+}
+
+find_root_dev() {
+ remote_sh "rootdev -s"
+ echo ${REMOTE_OUT}
+}
+
+run_once() {
+ if [ "$(get_update_var CURRENT_OP)" != "${UPDATER_IDLE}" ]; then
+ warn "Machine is in a bad state. Resetting the update_engine."
+ remote_sh "${UPDATER_BIN} --reset_status 2> /dev/null"
+ fi
+
+ local initial_root_dev=$(find_root_dev)
+ UPDATE_PATH="$(get_devserver_url)"
+ if [ -z "${FLAGS_update_url}" ]; then
+ # Start local devserver if no update url specified.
+ start_dev_server
+ fi
+
+ local update_pid
+ if [ ${FLAGS_update} -eq ${FLAGS_TRUE} ]; then
+ run_auto_update &
+ update_pid=$!
+ fi
+
+ local stateful_pid
+ local stateful_tmp_file
+ if [ ${FLAGS_update_stateful} -eq ${FLAGS_TRUE} ]; then
+ stateful_tmp_file=$(mktemp)
+ run_stateful_update &> "${stateful_tmp_file}" &
+ stateful_pid=$!
+ fi
+
+ if [ -n "${update_pid}" ] && ! wait ${update_pid}; then
+ warn "Update failed. " \
+ "Dumping update_engine.log for debugging and/or bug reporting."
+ get_update_log
+ tail -n 200 "${FLAGS_update_log}" >&2
+ die "Update was not successful."
+ fi
+
+ if [ -n "${stateful_pid}" ]; then
+ local stateful_success=0
+ if ! wait ${stateful_pid}; then
+ stateful_success=1
+ fi
+ cat "${stateful_tmp_file}"
+ rm "${stateful_tmp_file}"
+ if [ ${stateful_success} -ne 0 ]; then
+ die "Stateful update was not successful."
+ fi
+ fi
+
+ if [ ${FLAGS_reboot_after_update} -eq ${FLAGS_FALSE} ]; then
+ echo "Not rebooting because of --noreboot_after_update"
+ print_time_elapsed
+ return 0
+ fi
+
+ remote_reboot
+
+ if [[ ${FLAGS_update_hostkey} -eq ${FLAGS_TRUE} ]]; then
+ local known_hosts="${HOME}/.ssh/known_hosts"
+ cp "${known_hosts}" "${known_hosts}~"
+ grep -v "^${FLAGS_remote} " "${known_hosts}" > "${TMP}/new_known_hosts"
+ cat "${TMP}/new_known_hosts" "${TMP_KNOWN_HOSTS}" > "${known_hosts}"
+ chmod 0640 "${known_hosts}"
+ info "New updated in ${known_hosts}, backup made."
+ fi
+
+ remote_sh "grep ^CHROMEOS_RELEASE_DESCRIPTION= /etc/lsb-release"
+ if [ ${FLAGS_verify} -eq ${FLAGS_TRUE} ]; then
+ verify_image
+
+ if [ "${initial_root_dev}" == "$(find_root_dev)" ]; then
+ # At this point, the software version didn't change, but we didn't
+ # switch partitions either. Means it was an update to the same version
+ # that failed.
+ die "The root partition did NOT change. The update failed."
+ fi
+ else
+ local release_description=$(echo ${REMOTE_OUT} | cut -d '=' -f 2)
+ info "Update was successful and rebooted to $release_description"
+ fi
+
+ print_time_elapsed
+}
+
+main() {
+ echo "$DEPRECATION_WARNING"
+
+ cd "${SCRIPTS_DIR}"
+
+ FLAGS "$@" || exit 1
+ eval set -- "${FLAGS_ARGV}"
+
+ set -e
+
+ trap cleanup EXIT INT TERM
+
+ TMP=$(mktemp -d /tmp/image_to_live.XXXX)
+
+ remote_access_init
+
+ for i in $(seq 1 ${FLAGS_repeat}); do
+ echo "Iteration: " $i of ${FLAGS_repeat}
+ run_once
+ if [ ${FLAGS_repeat} -gt 1 ]; then
+ remote_sh "${UPDATER_BIN} --reset_status 2> /dev/null"
+ fi
+ done
+
+ command_completed
+ exit 0
+}
+
+main $@
diff --git a/lib/constants.py b/lib/constants.py
index bb87e33..25c4866 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -10,7 +10,9 @@
_TEST_LIB_PATH = os.path.realpath(__file__)
-CROS_PLATFORM_ROOT = os.path.join(os.path.dirname(_TEST_LIB_PATH), '..', '..')
+CROSTESTUTILS_DIR = os.path.dirname(_TEST_LIB_PATH)
+
+CROS_PLATFORM_ROOT = os.path.join(CROSTESTUTILS_DIR, '..', '..')
DEFAULT_CHROOT_DIR = 'chroot'
diff --git a/outside_chroot_common.sh b/outside_chroot_common.sh
index 3cc4f2e..4d60ea3 100644
--- a/outside_chroot_common.sh
+++ b/outside_chroot_common.sh
@@ -12,5 +12,6 @@
exit 1
fi
-SCRIPT_ROOT="$(dirname "$(readlink -f "$0")")/../../scripts"
+CROSTESTUTILS_DIR="$(dirname "$(readlink -f "$0")")"
+SCRIPT_ROOT="${CROSTESTUTILS_DIR}/../../scripts"