summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Shi <dshi@google.com>2020-10-06 22:18:35 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-10-06 22:18:35 +0000
commit2b68c2c808eb5561738114f1e7a4dcfba11f0dff (patch)
tree44844efda6427314e40c57463b95f55c6c7f22c2
parent94c0c115c2049a6cebb79318d3a685312d8e82d0 (diff)
parent25d86869b0ecbb4d2f042e12705ab3622c999cd8 (diff)
downloadframework-2b68c2c808eb5561738114f1e7a4dcfba11f0dff.tar.gz
Remove vtf related configs am: 376757b0ba am: 647713d162 am: 43e5702f3d am: 25d86869b0
Original change: https://android-review.googlesource.com/c/platform/test/framework/+/1449375 Change-Id: Ia1020dfb78438091861e16d87a551f4262c769dd
-rw-r--r--PREUPLOAD.cfg8
-rw-r--r--README.md4
-rw-r--r--build/Android.mk152
-rw-r--r--build/package.mk50
-rw-r--r--build/tasks/list/vtslab_apk_package_list.mk18
-rw-r--r--build/tasks/list/vtslab_bin_package_list.mk24
-rw-r--r--build/tasks/list/vtslab_lib_package_list.mk18
-rw-r--r--build/utils/vtslab_package_utils.mk50
-rw-r--r--build/vtf.mk48
-rw-r--r--harnesses/Android.bp20
-rw-r--r--harnesses/README.md15
-rw-r--r--harnesses/cts-tradefed/tradefed-cts-prebuilt.jarbin211065 -> 0 bytes
-rw-r--r--harnesses/host_controller/__init__.py0
-rw-r--r--harnesses/host_controller/acloud/__init__.py0
-rw-r--r--harnesses/host_controller/acloud/acloud_client.py203
-rw-r--r--harnesses/host_controller/acloud/acloud_config.py103
-rw-r--r--harnesses/host_controller/build/README.md12
-rw-r--r--harnesses/host_controller/build/__init__.py0
-rw-r--r--harnesses/host_controller/build/build_flasher.py364
-rw-r--r--harnesses/host_controller/build/build_flasher_test.py135
-rw-r--r--harnesses/host_controller/build/build_info.py72
-rw-r--r--harnesses/host_controller/build/build_provider.py323
-rw-r--r--harnesses/host_controller/build/build_provider_ab.py94
-rw-r--r--harnesses/host_controller/build/build_provider_gcs.py113
-rw-r--r--harnesses/host_controller/build/build_provider_local_fs.py40
-rw-r--r--harnesses/host_controller/build/build_provider_pab.py728
-rw-r--r--harnesses/host_controller/build/build_provider_pab_test.py317
-rw-r--r--harnesses/host_controller/build/build_provider_test.py156
-rw-r--r--harnesses/host_controller/campaigns/__init__.py0
-rw-r--r--harnesses/host_controller/campaigns/campaign_common.py755
-rw-r--r--harnesses/host_controller/campaigns/campaign_test.py75
-rw-r--r--harnesses/host_controller/campaigns/cts.py26
-rw-r--r--harnesses/host_controller/campaigns/gts.py26
-rw-r--r--harnesses/host_controller/campaigns/sts.py26
-rw-r--r--harnesses/host_controller/campaigns/testdata/__init__.py0
-rw-r--r--harnesses/host_controller/campaigns/testdata/default_testcase.py141
-rw-r--r--harnesses/host_controller/campaigns/vts.py26
-rw-r--r--harnesses/host_controller/command_processor/__init__.py0
-rw-r--r--harnesses/host_controller/command_processor/base_command_processor.py135
-rw-r--r--harnesses/host_controller/command_processor/command_acloud.py78
-rw-r--r--harnesses/host_controller/command_processor/command_adb.py86
-rw-r--r--harnesses/host_controller/command_processor/command_build.py249
-rw-r--r--harnesses/host_controller/command_processor/command_config.py440
-rw-r--r--harnesses/host_controller/command_processor/command_config_local.py166
-rw-r--r--harnesses/host_controller/command_processor/command_copy.py46
-rw-r--r--harnesses/host_controller/command_processor/command_device.py354
-rw-r--r--harnesses/host_controller/command_processor/command_device_test.py149
-rw-r--r--harnesses/host_controller/command_processor/command_dut.py141
-rw-r--r--harnesses/host_controller/command_processor/command_dut_test.py159
-rw-r--r--harnesses/host_controller/command_processor/command_exit.py63
-rw-r--r--harnesses/host_controller/command_processor/command_fastboot.py100
-rw-r--r--harnesses/host_controller/command_processor/command_fetch.py204
-rw-r--r--harnesses/host_controller/command_processor/command_flash.py210
-rw-r--r--harnesses/host_controller/command_processor/command_gsispl.py153
-rw-r--r--harnesses/host_controller/command_processor/command_info.py45
-rw-r--r--harnesses/host_controller/command_processor/command_lease.py59
-rw-r--r--harnesses/host_controller/command_processor/command_list.py84
-rw-r--r--harnesses/host_controller/command_processor/command_password.py46
-rw-r--r--harnesses/host_controller/command_processor/command_release.py267
-rw-r--r--harnesses/host_controller/command_processor/command_repack.py147
-rw-r--r--harnesses/host_controller/command_processor/command_repack_test.py172
-rw-r--r--harnesses/host_controller/command_processor/command_reproduce.py301
-rw-r--r--harnesses/host_controller/command_processor/command_reproduce_test.py353
-rw-r--r--harnesses/host_controller/command_processor/command_request.py66
-rw-r--r--harnesses/host_controller/command_processor/command_retry.py301
-rw-r--r--harnesses/host_controller/command_processor/command_sheet.py421
-rw-r--r--harnesses/host_controller/command_processor/command_sheet_test.py328
-rw-r--r--harnesses/host_controller/command_processor/command_shell.py59
-rw-r--r--harnesses/host_controller/command_processor/command_sleep.py53
-rw-r--r--harnesses/host_controller/command_processor/command_test.py226
-rw-r--r--harnesses/host_controller/command_processor/command_upload.py346
-rw-r--r--harnesses/host_controller/command_processor/command_upload_test.py312
-rw-r--r--harnesses/host_controller/common.py212
-rw-r--r--harnesses/host_controller/console.py920
-rw-r--r--harnesses/host_controller/console_argument_parser.py60
-rw-r--r--harnesses/host_controller/console_test.py263
-rwxr-xr-xharnesses/host_controller/gsi/change_security_patch_ver.sh288
-rw-r--r--harnesses/host_controller/invocation_thread.py169
-rw-r--r--harnesses/host_controller/invocation_thread_test.py148
-rw-r--r--harnesses/host_controller/main.py227
-rw-r--r--harnesses/host_controller/script/pip_requirements.txt13
-rwxr-xr-xharnesses/host_controller/script/release.sh17
-rw-r--r--harnesses/host_controller/tfc/__init__.py0
-rw-r--r--harnesses/host_controller/tfc/api_message.py46
-rw-r--r--harnesses/host_controller/tfc/command_attempt.py137
-rw-r--r--harnesses/host_controller/tfc/command_task.py39
-rw-r--r--harnesses/host_controller/tfc/device_info.py105
-rw-r--r--harnesses/host_controller/tfc/request.py74
-rw-r--r--harnesses/host_controller/tfc/tfc_client.py176
-rw-r--r--harnesses/host_controller/tfc/tfc_client_test.py130
-rw-r--r--harnesses/host_controller/tfc_host_controller.py140
-rw-r--r--harnesses/host_controller/tfc_host_controller_test.py92
-rw-r--r--harnesses/host_controller/tradefed/__init__.py0
-rw-r--r--harnesses/host_controller/tradefed/remote_client.py135
-rw-r--r--harnesses/host_controller/tradefed/remote_client_test.py179
-rw-r--r--harnesses/host_controller/tradefed/remote_operation.py172
-rw-r--r--harnesses/host_controller/utils/__init__.py0
-rw-r--r--harnesses/host_controller/utils/gcp/__init__.py0
-rw-r--r--harnesses/host_controller/utils/gcp/gcs_utils.py104
-rw-r--r--harnesses/host_controller/utils/gsi/__init__.py0
-rw-r--r--harnesses/host_controller/utils/gsi/img_utils.py51
-rw-r--r--harnesses/host_controller/utils/ipc/__init__.py0
-rw-r--r--harnesses/host_controller/utils/ipc/file_lock.py119
-rw-r--r--harnesses/host_controller/utils/ipc/file_lock_semaphore.py124
-rw-r--r--harnesses/host_controller/utils/ipc/file_lock_semaphore_test.py78
-rw-r--r--harnesses/host_controller/utils/ipc/shared_dict.py66
-rw-r--r--harnesses/host_controller/utils/parser/__init__.py0
-rw-r--r--harnesses/host_controller/utils/parser/pb2_utils.py81
-rw-r--r--harnesses/host_controller/utils/parser/result_utils.py129
-rw-r--r--harnesses/host_controller/utils/parser/xml_utils.py43
-rw-r--r--harnesses/host_controller/utils/usb/__init__.py0
-rw-r--r--harnesses/host_controller/utils/usb/usb_utils.py112
-rw-r--r--harnesses/host_controller/vti_interface/__init__.py0
-rw-r--r--harnesses/host_controller/vti_interface/vti_endpoint_client.py452
-rw-r--r--host_setup/fabfile.py386
-rwxr-xr-xhost_setup/host_setup.sh96
-rwxr-xr-xscript/run-unittest.sh64
-rw-r--r--tools/host_controller/Android.bp18
-rwxr-xr-xtools/host_controller/make_screen20
-rwxr-xr-xtools/host_controller/run49
120 files changed, 0 insertions, 16195 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
deleted file mode 100644
index 1204431..0000000
--- a/PREUPLOAD.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[Builtin Hooks]
-clang_format = true
-
-[Builtin Hooks Options]
-clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,java
-
-[Hook Scripts]
-test_framework_unittests = ${REPO_ROOT}/test/framework/script/run-unittest.sh
diff --git a/README.md b/README.md
deleted file mode 100644
index 60cfc7e..0000000
--- a/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Android Vendor Test Suite (VTS) Lab
-
-VTS Lab is an open source test serving infrastructure that can be used
-to streamline VTS and CTS-on-GSI (General System Image) tests.
diff --git a/build/Android.mk b/build/Android.mk
deleted file mode 100644
index 82b1883..0000000
--- a/build/Android.mk
+++ /dev/null
@@ -1,152 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-VTF_MK := $(LOCAL_PATH)/vtf.mk
-VTF_PACKAGE_MK := test/vts/tools/build/tasks/framework/vtf_package.mk
-
-include $(CLEAR_VARS)
-include $(LOCAL_PATH)/tasks/list/vtslab_apk_package_list.mk
-include $(LOCAL_PATH)/tasks/list/vtslab_bin_package_list.mk
-include $(LOCAL_PATH)/tasks/list/vtslab_lib_package_list.mk
-
-VTSLAB_OUT_ROOT := $(HOST_OUT)/vtslab
-VTSLAB_TESTCASES_OUT := $(VTSLAB_OUT_ROOT)/android-vtslab/testcases
-VTSLAB_TOOLS_OUT := $(VTSLAB_OUT_ROOT)/android-vtslab/tools
-VTSLAB_BIN_LIB_OUT := $(VTSLAB_OUT_ROOT)/android-vtslab/
-VTSLAB_TIMESTAMP := $(shell git -C $(LOCAL_PATH) log -s -n 1 --format="%cd" \
- --date=format:"%Y%m%d_%H%M%S" 2>/dev/null)
-VTSLAB_SHORTHASH := $(shell git -C $(LOCAL_PATH) rev-parse --short HEAD 2>/dev/null)
-VTSLAB_VERSION := $(VTSLAB_TIMESTAMP):$(VTSLAB_SHORTHASH)
-
-# Packaging rule for android-vtslab.zip
-test_suite_name := vtslab
-test_suite_readme := test/framework/README.md
-
-include $(LOCAL_PATH)/package.mk
-include $(LOCAL_PATH)/utils/vtslab_package_utils.mk
-
-# TODO: instead of an alias, deprecate vtslab build target
-.PHONY: lab
-lab: vtslab
-.PHONY: vtslab
-vtslab: $(compatibility_zip)
-$(call dist-for-goals, vtslab, $(compatibility_zip))
-
-# Packaging rule for android-vtslab.zip's testcases dir (DATA subdir).
-vtslab_apk_modules := \
- $(vtslab_apk_packages) \
-
-vtslab_apk_modules_copy_pairs := \
- $(call target-native-copy-pairs,$(vtslab_apk_modules),$(VTSLAB_TESTCASES_OUT))
-
-# host controller files.
-host_hc_files := \
- $(call find-files-in-subdirs,test/framework/harnesses/host_controller,"*.py" -and -type f,.) \
-
-host_hc_copy_pairs := \
- $(foreach f,$(host_hc_files),\
- test/framework/harnesses/host_controller/$(f):$(VTSLAB_TESTCASES_OUT)/host_controller/$(f))
-
-# gsi security patch scripts.
-host_hc_gsispl_files := \
- $(call find-files-in-subdirs,test/framework/harnesses/host_controller/gsi,"*.sh" -and -type f,.) \
-
-host_hc_gsispl_copy_pairs := \
- $(foreach f,$(host_hc_gsispl_files),\
- test/framework/harnesses/host_controller/gsi/$(f):$(VTSLAB_BIN_LIB_OUT)/bin/$(f))
-
-# host controller scripts.
-host_hc_extra_copy_pairs := \
- test/framework/tools/host_controller/run:$(VTSLAB_TOOLS_OUT)/run \
- test/framework/tools/host_controller/make_screen:$(VTSLAB_TOOLS_OUT)/make_screen \
- test/vts/script/diagnose.py:$(VTSLAB_BIN_LIB_OUT)/bin/diagnose.py \
- test/vts/script/pip_requirements.txt:$(VTSLAB_BIN_LIB_OUT)/bin/pip_requirements.txt \
- test/vts/script/setup.sh:$(VTSLAB_BIN_LIB_OUT)/bin/setup.sh \
-
-host_acloud_files := \
- $(call find-files-in-subdirs,tools/acloud,"*.py" -and -type f,.) \
- $(call find-files-in-subdirs,tools/acloud,"*.config" -and -type f,.)
-
-host_acloud_copy_pairs := \
- $(foreach f,$(host_acloud_files),\
- tools/acloud/$(f):$(VTSLAB_TESTCASES_OUT)/acloud/$(f))
-
-host_vti_dashboard_proto_files := \
- $(call find-files-in-subdirs,test/vti/dashboard/proto,"*.py" -and -type f,.)
-
-host_vti_test_serving_proto_files := \
- $(call find-files-in-subdirs,test/vti/test_serving/proto,"*.py" -and -type f,.)
-
-host_vti_copy_pairs := \
- $(foreach f,$(host_vti_test_serving_proto_files),\
- test/vti/test_serving/proto/$(f):$(VTSLAB_TESTCASES_OUT)/vti/test_serving/proto/$(f)) \
- $(foreach f,$(host_vti_dashboard_proto_files),\
- test/vti/dashboard/proto/$(f):$(VTSLAB_TESTCASES_OUT)/vti/dashboard/proto/$(f)) \
- test/vti/dashboard/__init__.py:$(VTSLAB_TESTCASES_OUT)/vti/dashboard/__init__.py \
- test/vti/test_serving/__init__.py:$(VTSLAB_TESTCASES_OUT)/vti/test_serving/__init__.py \
-
-$(VTSLAB_TESTCASES_OUT)/vti/__init__.py:
- @mkdir -p $(VTSLAB_TESTCASES_OUT)/vti
- @touch $(VTSLAB_TESTCASES_OUT)/vti/__init__.py
-
-host_vti_extra_copy_pairs := \
- $(VTSLAB_TESTCASES_OUT)/vti/__init__.py \
-
-vts_host_python_files := \
- $(call find-files-in-subdirs,test/vts,"*.py" -and -type f,.)
-
-vts_host_python_copy_pairs := \
- $(foreach f,$(vts_host_python_files),\
- test/vts/$(f):$(VTSLAB_TESTCASES_OUT)/vts/$(f))
-
-# Packaging rule for host-controller's dependencies
-host_hc_bin_lib := \
- $(vtslab_bin_packages) \
- $(vtslab_lib_packages) \
-
-host_hc_bin_lib_copy_pairs := \
- $(call host-native-copy-pairs,$(host_hc_bin_lib),$(VTSLAB_BIN_LIB_OUT))
-
-vtslab_copy_pairs := \
- $(call copy-many-files,$(vtslab_apk_modules_copy_pairs)) \
- $(call copy-many-files,$(host_hc_copy_pairs)) \
- $(call copy-many-files,$(host_hc_gsispl_copy_pairs)) \
- $(call copy-many-files,$(host_hc_extra_copy_pairs)) \
- $(call copy-many-files,$(host_acloud_copy_pairs)) \
- $(call copy-many-files,$(host_vti_copy_pairs)) \
- $(call copy-many-files,$(vts_host_python_copy_pairs)) \
- $(call copy-many-files,$(host_hc_bin_lib_copy_pairs)) \
- $(host_vti_extra_copy_pairs) \
-
-$(compatibility_zip): $(vtslab_copy_pairs) $(VTSLAB_TESTCASES_OUT)/version.txt
-
-$(VTSLAB_TESTCASES_OUT)/version.txt:
- @rm -f $@
- @echo $(VTSLAB_VERSION) > $@
-
-# for VTF (Vendor Test Framework)
-VTF_OUT_ROOT := $(HOST_OUT)/vtf
-VTF_TESTCASES_OUT := $(VTF_OUT_ROOT)/android-vtf/testcases
-VTF_TOOLS_OUT := $(VTF_OUT_ROOT)/android-vtf/tools
-VTF_EXTRA_SCRIPTS := vtf
-
-include $(VTF_PACKAGE_MK)
-include $(VTF_MK)
-
-# clears local vars
-VTF_MK :=
-VTF_PACKAGE_MK :=
diff --git a/build/package.mk b/build/package.mk
deleted file mode 100644
index d04b159..0000000
--- a/build/package.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Package up a compatibility test suite in a zip file.
-#
-# Input variables:
-# test_suite_name: the name of this test suite eg. cts
-# test_suite_readme: the path to a README file for this test suite
-# test_suite_prebuilt_tools: the set of prebuilt tools to be included directly
-# in the 'tools' subdirectory of the test suite.
-# test_suite_tools: the set of tools for this test suite
-#
-# Output variables:
-# compatibility_zip: the path to the output zip file.
-
-out_dir := $(HOST_OUT)/$(test_suite_name)/android-$(test_suite_name)
-test_artifacts := $(COMPATIBILITY.$(test_suite_name).FILES)
-test_tools := $(test_suite_readme)
-test_tools += $(test_suite_tools)
-
-compatibility_zip := $(out_dir).zip
-$(compatibility_zip): PRIVATE_NAME := android-$(test_suite_name)
-$(compatibility_zip): PRIVATE_OUT_DIR := $(out_dir)
-$(compatibility_zip): PRIVATE_TOOLS := $(test_tools) $(test_suite_prebuilt_tools)
-$(compatibility_zip): PRIVATE_SUITE_NAME := $(test_suite_name)
-$(compatibility_zip): $(test_artifacts) $(test_tools) $(test_suite_prebuilt_tools) $(SOONG_ZIP) | $(ADB) $(ACP)
-# Make dir structure
- $(hide) mkdir -p $(PRIVATE_OUT_DIR)/tools $(PRIVATE_OUT_DIR)/testcases
-# Copy tools
- $(hide) $(ACP) -fp $(PRIVATE_TOOLS) $(PRIVATE_OUT_DIR)/tools
- $(hide) find $(dir $@)/$(PRIVATE_NAME) | sort >$@.list
- $(hide) $(SOONG_ZIP) -d -o $@ -C $(dir $@) -l $@.list
-
-# Reset all input variables
-test_suite_name :=
-test_suite_dynamic_config :=
-test_suite_readme :=
-test_suite_prebuilt_tools :=
-test_suite_tools :=
diff --git a/build/tasks/list/vtslab_apk_package_list.mk b/build/tasks/list/vtslab_apk_package_list.mk
deleted file mode 100644
index fc842aa..0000000
--- a/build/tasks/list/vtslab_apk_package_list.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-vtslab_apk_packages := \
- WifiUtil \
diff --git a/build/tasks/list/vtslab_bin_package_list.mk b/build/tasks/list/vtslab_bin_package_list.mk
deleted file mode 100644
index 664c7fe..0000000
--- a/build/tasks/list/vtslab_bin_package_list.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-vtslab_bin_packages := \
- aapt \
- adb \
- fastboot \
- img2simg \
- mke2fs \
- mkuserimg_mke2fs \
- simg2img \
diff --git a/build/tasks/list/vtslab_lib_package_list.mk b/build/tasks/list/vtslab_lib_package_list.mk
deleted file mode 100644
index 5f4740c..0000000
--- a/build/tasks/list/vtslab_lib_package_list.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-vtslab_lib_packages := \
- libc++ \
diff --git a/build/utils/vtslab_package_utils.mk b/build/utils/vtslab_package_utils.mk
deleted file mode 100644
index e4ea608..0000000
--- a/build/utils/vtslab_package_utils.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# $(1): List of target native files to copy.
-# $(2): Copy destination directory.
-# Evaluates to a list of ":"-separated pairs src:dst.
-define target-native-copy-pairs
-$(foreach m,$(1),\
- $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\
- $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\
- $(foreach i, $(_built_files),\
- $(eval bui_ins := $(subst :,$(space),$(i)))\
- $(eval ins := $(word 2,$(bui_ins)))\
- $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\
- $(eval bui := $(word 1,$(bui_ins)))\
- $(eval my_copy_dest := $(patsubst data/%,DATA/%,\
- $(patsubst system/%,DATA/%,\
- $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))))\
- $(bui):$(2)/$(my_copy_dest))))
-endef
-
-# $(1): List of host native files to copy.
-# $(2): Copy destination directory.
-# Evaluates to a list of ":"-separated pairs src:dst.
-define host-native-copy-pairs
-$(foreach m,$(1),\
- $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\
- $(ALL_MODULES.$(m)$(HOST_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\
- $(foreach i, $(_built_files),\
- $(eval bui_ins := $(subst :,$(space),$(i)))\
- $(eval ins := $(word 2,$(bui_ins)))\
- $(if $(filter $(HOST_OUT)/% $(HOST_CROSS_OUT)/%,$(ins)),\
- $(eval bui := $(word 1,$(bui_ins)))\
- $(eval my_copy_dest := $(patsubst $(HOST_OUT)/%,%,\
- $(patsubst $(HOST_CROSS_OUT)/%,%,$(ins))))\
- $(bui):$(2)/$(my_copy_dest))))
-endef
diff --git a/build/vtf.mk b/build/vtf.mk
deleted file mode 100644
index 069b6b2..0000000
--- a/build/vtf.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Build rules for VTF (Vendor Test Framework).
-LOCAL_PATH := $(call my-dir)
-
-vtf_tradefed_modules := \
- compatibility-common-util-tests \
- compatibility-host-util \
- compatibility-host-util-tests \
- compatibility-tradefed-tests \
- host-libprotobuf-java-full \
- loganalysis \
- tradefed \
- vts-tradefed \
- vts10-tradefed \
- vts10-tradefed-tests \
-
-vtf_tradefed_copy_pairs := \
- $(foreach f,$(vtf_tradefed_modules),\
- $(HOST_OUT)/framework/$(f).jar:$(VTF_TOOLS_OUT)/$(f).jar)
-
-vtf_tradefed_additional_deps_copy_pairs := \
- test/vts/tools/vts-tradefed/etc/vts10-tradefed:$(VTF_TOOLS_OUT)/vts10-tradefed
-
-vtf_tradefed_additional_deps_copy_pairs += \
- $(foreach f,$(VTF_COPY_VTF_BINARY),\
- test/vts/tools/vts-tradefed/etc/$(f):$(VTF_TESTCASES_OUT)/$(f))
-
-vtf_package_copy_pairs := \
- $(call copy-many-files,$(vtf_tradefed_copy_pairs)) \
- $(call copy-many-files,$(vtf_tradefed_additional_deps_copy_pairs)) \
-
-.PHONY: vtf
-vtf: $(vtf_copy_pairs) $(vtf_package_copy_pairs)
diff --git a/harnesses/Android.bp b/harnesses/Android.bp
deleted file mode 100644
index 93e059b..0000000
--- a/harnesses/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-java_import_host {
- name: "tradefed-cts-prebuilt",
- jars: ["cts-tradefed/tradefed-cts-prebuilt.jar"],
-}
diff --git a/harnesses/README.md b/harnesses/README.md
deleted file mode 100644
index a8c5fde..0000000
--- a/harnesses/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-To build a new CTS TF prebuilt binary
-
-`$ lunch aosp_arm64`
-`$ make cts-tradefed -j
-
-To release to TEST
-`$ cp out/host/linux-x86/testcases/cts-tradefed/cts-tradefed.jar test/framework/harnesses/cts-tradefed/tradefed-cts-prebuilt-staging.jar`
-
-To release to PROD
-`$ cp test/framework/harnesses/cts-tradefed/tradefed-cts-prebuilt-staging.jar test/framework/harnesses/cts-tradefed/tradefed-cts-prebuilt.jar`
-
-To test a test suite which uses that prebuilt binary
-
-`$ make vts -j32`
-
diff --git a/harnesses/cts-tradefed/tradefed-cts-prebuilt.jar b/harnesses/cts-tradefed/tradefed-cts-prebuilt.jar
deleted file mode 100644
index 7e728bf..0000000
--- a/harnesses/cts-tradefed/tradefed-cts-prebuilt.jar
+++ /dev/null
Binary files differ
diff --git a/harnesses/host_controller/__init__.py b/harnesses/host_controller/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/acloud/__init__.py b/harnesses/host_controller/acloud/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/acloud/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/acloud/acloud_client.py b/harnesses/host_controller/acloud/acloud_client.py
deleted file mode 100644
index 5bcd85e..0000000
--- a/harnesses/host_controller/acloud/acloud_client.py
+++ /dev/null
@@ -1,203 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import getpass
-import json
-import logging
-import os
-from os.path import expanduser
-import re
-import shutil
-import tempfile
-
-from acloud.public import acloud_main
-from host_controller.acloud import acloud_config
-from vts.utils.python.common import cmd_utils
-
-DEFAULT_BRANCH = 'git_master'
-DEFAULT_BUILD_TARGET = 'gce_x86_phone-userdebug_fastbuild3c_linux'
-
-#TODO(yuexima): add full support to multiple instances per host
-
-
-class ACloudClient(object):
- '''Helper class to manage access to the acloud module.'''
-
- def __init__(self):
- tmpdir_base = os.path.join(os.getcwd(), "tmp")
- if not os.path.exists(tmpdir_base):
- os.mkdir(tmpdir_base)
- self._tmpdir = tempfile.mkdtemp(dir=tmpdir_base)
-
- def __del__(self):
- """Deletes the temp dir if still set."""
- if self._tmpdir:
- shutil.rmtree(self._tmp_dirpath)
- self._tmpdir = None
-
- def GetCreateCmd(self,
- build_id,
- branch=None,
- build_target=None,
- num=1):
- '''Get acould create command with given build id.
-
- Args:
- build_id: string, build_id.
- branch: string, build branch
- build_target: string, build target
- num: int, number of instances to build
-
- Returns:
- string, acloud create command.
- '''
- if not branch:
- branch = DEFAULT_BRANCH
-
- if not build_target:
- build_target = DEFAULT_BUILD_TARGET
-
- #TODO latest build id (in the caller class of this function).
- #TODO use unique log and tmp file location
- cmd = ('create '
- '--branch {branch} '
- '--build_target {build_target} '
- '--build_id {build_id} '
- '--config_file {config_file} '
- '--report_file {report_file} '
- '--log_file {log_file} '
- '--email {email} '
- '--num {num}').format(
- branch = branch,
- build_target = build_target,
- build_id = build_id,
- config_file = os.path.join(self._tmpdir, 'acloud.config'),
- report_file = os.path.join(self._tmpdir, 'acloud_report.json'),
- log_file = os.path.join(self._tmpdir, 'acloud.log'),
- #TODO use host email address
- email = getpass.getuser() + '@google.com',
- num = num
- )
- return cmd
-
- def GetDeleteCmd(self, instance_names):
- '''Get Acould delete command with given instance names.
-
- Args:
- instance_names: list of string, instance names.
-
- Returns:
- string, acloud create command.
- '''
- cmd = ('delete '
- '--instance_names {instance_names} '
- '--config_file {config_file} '
- '--report_file {report_file} '
- '--log_file {log_file} '
- '--email {email}').format(
- instance_names = ' '.join(instance_names),
- config_file = os.path.join(self._tmpdir, 'acloud.config'),
- report_file = os.path.join(self._tmpdir, 'acloud_report.json'),
- log_file = os.path.join(self._tmpdir, 'acloud.log'),
- email = getpass.getuser() + '@google.com'
- )
- return cmd
-
- def CreateInstance(self, build_id):
- '''Create Acould instance with given build id.
-
- Args:
- build_id: string, build_id.
- '''
- cmd = self.GetCreateCmd(build_id)
- acloud_main.main(cmd.split())
-
- report_file = os.path.join(self._tmpdir, 'acloud_report.json')
- with open(report_file, 'r') as f:
- report = json.load(f)
-
- return report['status']=='SUCCESS'
-
- def PrepareConfig(self, file_path):
- '''Prepare acloud Acloud config file.
-
- Args:
- file_path: string, path to acloud config file.
- '''
- config = acloud_config.ACloudConfig()
- config.Load(file_path)
- if not config.Validate():
- logging.error('Failed to prepare acloud config.')
- return
-
- config.Save(os.path.join(self._tmpdir, 'acloud.config'))
-
- def GetInstanceIP(self):
- '''Get an Acloud instance ip'''
- #TODO support ip addresses when num > 1 (json parser)
- report_file = os.path.join(self._tmpdir, 'acloud_report.json')
-
- with open(report_file, 'r') as f:
- report = json.load(f)
-
- return report['data']['devices'][0]['ip']
-
- def GetInstanceName(self):
- '''Get an Acloud instance name'''
- report_file = os.path.join(self._tmpdir, 'acloud_report.json')
-
- with open(report_file, 'r') as f:
- report = json.load(f)
-
- return report['data']['devices'][0]['instance_name']
-
- def ConnectInstanceToAdb(self, ip=None):
- '''Connect an Acloud instance to adb
-
- Args:
- ip: string, ip address
- '''
- if not ip:
- ip = self.GetInstanceIP()
-
- cmds = [('ssh -i ~/.ssh/acloud_rsa -o UserKnownHostsFile=/dev/null '
- '-o StrictHostKeyChecking=no -L 40000:127.0.0.1:6444 -L '
- '30000:127.0.0.1:5555 -N -f -l root %s') % ip,
- 'adb connect 127.0.0.1:30000']
-
- for cmd in cmds:
- print cmd
- results = cmd_utils.ExecuteShellCommand(cmd)
- if any(results[cmd_utils.EXIT_CODE]):
- logging.error("Fail to execute command: %s\nResult:%s" % (cmd,
- results))
- return
- print 'Acloud instance created and connected to local port 127.0.0.1:30000'
-
- def DeleteInstance(self, instance_names=None):
- '''Delete an Acloud instance
-
- Args:
- instance_names: string, instance name
- '''
- cmd = self.GetDeleteCmd(instance_names)
- acloud_main.main(cmd.split())
-
- report_file = os.path.join(self._tmpdir, 'acloud_report.json')
- with open(report_file, 'r') as f:
- report = json.load(f)
-
- return report['status']=='SUCCESS'
diff --git a/harnesses/host_controller/acloud/acloud_config.py b/harnesses/host_controller/acloud/acloud_config.py
deleted file mode 100644
index 0ceab47..0000000
--- a/harnesses/host_controller/acloud/acloud_config.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-
-
-# In fact, all fields are required. The fields listed below are used
-# to check whether the config class has been properly initialized before
-# generating config file
-REQUIRED_KEYS = [
- 'ssh_public_key_path',
- 'ssh_private_key_path',
- 'project',
- 'client_id',
- 'client_secret'
- ]
-
-
-class ACloudConfig(object):
- '''For ACloud configuration file operations.
-
- Attributes:
- configs: dict of string:string, configuration keys and values.
- has_error: bool, whether error occurred.
- '''
- configs = {}
- has_error = False
-
- def Validate(self):
- '''Validate config class.
-
- Check whether required fields has been set.
- Check whether loading configuration file is success.
-
- Returns:
- bool, True if validated.
- '''
- for key in REQUIRED_KEYS:
- if key not in self.configs:
- logging.error('Required key %s is not '
- 'set for acloud config' % key)
- return False
-
- return not self.has_error
-
- def Load(self, file_path):
- '''Load configs from a file.
-
- Args:
- file_path: string, path to config file.
- '''
- if not os.path.isfile(file_path):
- logging.error('Failed to read acloud config file %s' % file_path)
- self.has_error = True
- return
-
- separator = ': "'
-
- with open(file_path, 'r') as f:
- for line in f:
- line = line.strip()
- # Skip empty line and comments
- if not line or line.startswith('#'):
- continue
-
- idx = line.find(separator)
-
- if idx < 1 or not line.endswith('"'):
- logging.error('Error parsing line %s from '
- 'acloud config file %s' % (line, file_path))
- self.has_error = True
- return
-
- key = line[:idx]
- val = line[len(separator) + idx : -1]
-
- self.configs[key] = val
-
- def Save(self, file_path):
- '''Save config to a file.
-
- Args:
- file_path: string, path to config file.
- '''
- separator = ':'
-
- with open(file_path, 'w') as f:
- for key in self.configs:
- f.write(key + separator + '"%s"' % self.configs[key]) \ No newline at end of file
diff --git a/harnesses/host_controller/build/README.md b/harnesses/host_controller/build/README.md
deleted file mode 100644
index f97f372..0000000
--- a/harnesses/host_controller/build/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# To set up client_secrets.json (once only)
-
-* Go to https://console.cloud.google.com/projectselector/apis/credentials/consent
- and create a new project if needed.
-* Once on the consent screen, set the product name to anything and save.
-* Click "Create credentials" and select "OAuth client ID"
-* Under "Application type", select "Other". Click "Create".
-* Click the download button for the client you just created,
- and save the resulting file at client_secrets.json
-
-# Running unit tests
- python build_provider_pab_test.py \ No newline at end of file
diff --git a/harnesses/host_controller/build/__init__.py b/harnesses/host_controller/build/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/build/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/build/build_flasher.py b/harnesses/host_controller/build/build_flasher.py
deleted file mode 100644
index a920d93..0000000
--- a/harnesses/host_controller/build/build_flasher.py
+++ /dev/null
@@ -1,364 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""Class to flash build artifacts onto devices"""
-
-import hashlib
-import logging
-import os
-import resource
-import sys
-import tempfile
-import time
-
-from host_controller import common
-from vts.utils.python.common import cmd_utils
-from vts.utils.python.controllers import android_device
-
-
-class BuildFlasher(object):
- """Client that manages build flashing.
-
- Attributes:
- device: AndroidDevice, the device associated with the client.
- """
-
- def __init__(self, serial="", customflasher_path=""):
- """Initialize the client.
-
- If serial not provided, find single device connected. Error if
- zero or > 1 devices connected.
-
- Args:
- serial: optional string, serial number for the device.
- customflasher_path: optional string, set device to use specified
- binary to flash a device
- """
- if serial != "":
- self.device = android_device.AndroidDevice(
- serial, device_callback_port=-1)
- else:
- serials = android_device.list_adb_devices()
- if len(serials) == 0:
- serials = android_device.list_fastboot_devices()
- if len(serials) == 0:
- raise android_device.AndroidDeviceError(
- "ADB and fastboot could not find any target devices.")
- if len(serials) > 1:
- logging.info("ADB or fastboot found more than one device: %s",
- serials)
- self.device = android_device.AndroidDevice(
- serials[0], device_callback_port=-1)
- if customflasher_path:
- self.device.SetCustomFlasherPath(customflasher_path)
-
- def SetSerial(self, serial):
- """Sets device serial.
-
- Args:
- serial: string, a device serial.
-
- Returns:
- True if successful; False otherwise.
- """
- if not serial:
- logging.error("no serial is given to BuildFlasher.SetSerial.")
- return False
-
- self.device = android_device.AndroidDevice(
- serial, device_callback_port=-1)
- return True
-
- def FlashGSI(self,
- system_img,
- vbmeta_img=None,
- skip_check=False,
- skip_vbmeta=False):
- """Flash the Generic System Image to the device.
-
- Args:
- system_img: string, path to GSI
- vbmeta_img: string, optional, path to vbmeta image for new devices
- skip_check: boolean, set True to skip adb-based checks when
- the DUT is already running its bootloader.
- skip_vbmeta: bool, whether to skip flashing the vbmeta.img or not.
- If the device has the vbmeta slot then flash vbmeta.img
- even if the skip_vbmeta is set to True.
- """
- if not os.path.exists(system_img):
- raise ValueError("Couldn't find system image at %s" % system_img)
- if not skip_check:
- self.device.adb.wait_for_device()
- if not self.device.isBootloaderMode:
- self.device.log.info(self.device.adb.reboot_bootloader())
- if vbmeta_img is not None:
- if skip_vbmeta == False or self.device.hasVbmetaSlot:
- self.device.log.info(
- self.device.fastboot.flash('vbmeta', vbmeta_img))
- self.device.log.info(self.device.fastboot.erase('system'))
- self.device.log.info(self.device.fastboot.flash('system', system_img))
- self.device.log.info(self.device.fastboot.erase('metadata'))
- self.device.log.info(self.device.fastboot._w())
- self.device.log.info(self.device.fastboot.reboot())
-
- def Flashall(self, directory):
- """Flash all images in a directory to the device using flashall.
-
- Generally the directory is the result of unzipping the .zip from AB.
- Args:
- directory: string, path to directory containing images
- """
- # fastboot flashall looks for imgs in $ANDROID_PRODUCT_OUT
- os.environ['ANDROID_PRODUCT_OUT'] = directory
- self.device.adb.wait_for_device()
- if not self.device.isBootloaderMode:
- self.device.log.info(self.device.adb.reboot_bootloader())
- self.device.log.info(self.device.fastboot.flashall())
-
- def Flash(self, device_images, skip_vbmeta=False):
- """Flash the Generic System Image to the device.
-
- Args:
- device_images: dict, where the key is partition name and value is
- image file path.
- skip_vbmeta: bool, whether to skip flashing the vbmeta.img or not.
-
- Returns:
- True if succesful; False otherwise
- """
- if not device_images:
- logging.warn("Flash skipped because no device image is given.")
- return False
-
- if not self.device.isBootloaderMode:
- self.device.adb.wait_for_device()
- logging.info("rebooting to bootloader")
- self.device.log.info(self.device.adb.reboot_bootloader())
-
- logging.info("checking to flash bootloader.img and radio.img")
- for partition in ["bootloader", "radio"]:
- if partition in device_images:
- image_path = device_images[partition]
- self.device.log.info("fastboot flash %s %s", partition,
- image_path)
- self.device.log.info(
- self.device.fastboot.flash(partition, image_path))
- self.device.log.info("fastboot reboot_bootloader")
- self.device.log.info(self.device.fastboot.reboot_bootloader())
-
- logging.info("starting to flash vendor and other images...")
- full_zipfile = False
- if common.FULL_ZIPFILE in device_images:
- logging.info("fastboot update %s --skip-reboot",
- (device_images[common.FULL_ZIPFILE]))
- self.device.log.info(
- self.device.fastboot.update(device_images[common.FULL_ZIPFILE],
- "--skip-reboot"))
- full_zipfile = True
-
- for partition, image_path in device_images.iteritems():
- if partition in (common.FULL_ZIPFILE, common.FULL_ZIPFILE_DIR,
- "system", "vbmeta", "bootloader", "radio",
- "metadata", "userdata"):
- continue
- if full_zipfile and partition in ("vendor", "boot"):
- logging.info("%s skipped because full zipfile was updated.",
- partition)
- continue
- if not image_path:
- self.device.log.warning("%s image is empty", partition)
- continue
- self.device.log.info("fastboot flash %s %s", partition, image_path)
- self.device.log.info(
- self.device.fastboot.flash(partition, image_path))
-
- logging.info("starting to flash system and other images...")
- if "system" in device_images and device_images["system"]:
- system_img = device_images["system"]
- vbmeta_img = device_images["vbmeta"] if (
- "vbmeta" in device_images
- and device_images["vbmeta"]) else None
- self.FlashGSI(
- system_img,
- vbmeta_img,
- skip_check=True,
- skip_vbmeta=skip_vbmeta)
- else:
- self.device.log.info(self.device.fastboot.reboot())
- return True
-
- def FlashImage(self, device_images, image_partition=None, reboot=False):
- """Flash specified image(s) to the device.
-
- Args:
- device_images: dict, where the key is partition name and value is
- image file path.
- image_partition: string, set to flash only an image in a specified
- partition.
- reboot: boolean, true to reboot the device.
-
- Returns:
- True if successful, False otherwise
- """
- if not device_images:
- logging.warn("Flash skipped because no device image is given.")
- return False
-
- if not self.device.isBootloaderMode:
- self.device.adb.wait_for_device()
- self.device.log.info(self.device.adb.reboot_bootloader())
-
- for partition, image_path in device_images.iteritems():
- if image_partition and image_partition != partition:
- continue
- if partition.endswith(".img"):
- partition = partition[:-4]
- self.device.log.info(
- self.device.fastboot.flash(partition, image_path))
- if reboot:
- self.device.log.info(self.device.fastboot.reboot())
- return True
-
- def WaitForDevice(self, timeout_secs=600):
- """Waits for the device to boot completely.
-
- Args:
- timeout_secs: integer, the maximum timeout value for this
- operation (unit: seconds).
-
- Returns:
- True if device is booted successfully; False otherwise.
- """
- return self.device.waitForBootCompletion(timeout=timeout_secs)
-
- def FlashUsingCustomBinary(self,
- device_images,
- reboot_mode,
- flasher_args,
- timeout_secs_for_reboot=900):
- """Flash the customized image to the device.
-
- Args:
- device_images: dict, where the key is partition name and value is
- image file path.
- reboot_mode: string, decides which mode device will reboot into.
- ("bootloader"/"download").
- flasher_args: list of strings, arguments that will be passed to the
- flash binary.
- timeout_secs_for_reboot: integer, the maximum timeout value for
- reboot to flash-able mode(unit: seconds).
-
- Returns:
- True if successful; False otherwise.
- """
- if not device_images:
- logging.warn("Flash skipped because no device image is given.")
- return False
-
- if not flasher_args:
- logging.error("No arguments.")
- return False
-
- if not self.device.isBootloaderMode:
- self.device.adb.wait_for_device()
- logging.info("rebooting to %s mode", reboot_mode)
- self.device.log.info(self.device.adb.reboot(reboot_mode))
-
- start = time.time()
- while not self.device.customflasher._l():
- if time.time() - start >= timeout_secs_for_reboot:
- logging.error(
- "Timeout while waiting for %s mode boot completion.",
- reboot_mode)
- return False
- time.sleep(1)
-
- flasher_output = self.device.customflasher.ExecCustomFlasherCmd(
- flasher_args[0],
- " ".join(flasher_args[1:] + [device_images["img"]]))
- self.device.log.info(flasher_output)
-
- return True
-
- def RepackageArtifacts(self, device_images, repackage_form):
- """Repackage artifacts into a given format.
-
- Once repackaged, device_images becomes
- {"img": "path_to_repackaged_image"}
-
- Args:
- device_images: dict, where the key is partition name and value is
- image file path.
- repackage_form: string, format to repackage.
-
- Returns:
- True if succesful; False otherwise.
- """
- if not device_images:
- logging.warn("Repackage skipped because no device image is given.")
- return False
-
- if repackage_form == "tar.md5":
- tmp_file_name = next(tempfile._get_candidate_names()) + ".tar"
- tmp_dir_path = os.path.dirname(
- device_images[device_images.keys()[0]])
- for img in device_images:
- if os.path.dirname(device_images[img]) != tmp_dir_path:
- os.rename(device_images[img],
- os.path.join(tmp_dir_path, img))
- device_images[img] = os.path.join(tmp_dir_path, img)
-
- current_dir = os.getcwd()
- os.chdir(tmp_dir_path)
-
- if sys.platform == "linux2":
- tar_cmd = "tar -cf %s %s" % (tmp_file_name, ' '.join(
- (device_images.keys())))
- else:
- logging.error("Unsupported OS for the given repackage form.")
- return False
- logging.info(tar_cmd)
- std_out, std_err, err_code = cmd_utils.ExecuteOneShellCommand(
- tar_cmd)
- if err_code:
- logging.error(std_err)
- return False
-
- hash_md5 = hashlib.md5()
- try:
- with open(tmp_file_name, "rb") as file:
- data_chunk = 0
- chunk_size = resource.getpagesize()
- while data_chunk != b'':
- data_chunk = file.read(chunk_size)
- hash_md5.update(data_chunk)
- hash_ret = hash_md5.hexdigest()
- with open(tmp_file_name, "a") as file:
- file.write("%s %s" % (hash_ret, tmp_file_name))
- except IOError as e:
- logging.error(e.strerror)
- return False
-
- device_images.clear()
- device_images["img"] = os.path.join(tmp_dir_path, tmp_file_name)
-
- os.chdir(current_dir)
- else:
- logging.error(
- "Please specify correct repackage form: --repackage=%s",
- repackage_form)
- return False
-
- return True
diff --git a/harnesses/host_controller/build/build_flasher_test.py b/harnesses/host_controller/build/build_flasher_test.py
deleted file mode 100644
index 21e81b3..0000000
--- a/harnesses/host_controller/build/build_flasher_test.py
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import sys
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller.build import build_flasher
-
-
-class BuildFlasherTest(unittest.TestCase):
- """Tests for Build Flasher"""
-
- @mock.patch(
- "host_controller.build.build_flasher.android_device")
- @mock.patch("host_controller.build.build_flasher.os")
- def testFlashGSIBadPath(self, mock_os, mock_class):
- flasher = build_flasher.BuildFlasher("thisismyserial")
- mock_os.path.exists.return_value = False
- with self.assertRaises(ValueError) as cm:
- flasher.FlashGSI("notexists.img")
- self.assertEqual("Couldn't find system image at notexists.img",
- str(cm.exception))
-
- @mock.patch(
- "host_controller.build.build_flasher.android_device")
- @mock.patch("host_controller.build.build_flasher.os")
- def testFlashGSISystemOnly(self, mock_os, mock_class):
- mock_device = mock.Mock()
- mock_class.AndroidDevice.return_value = mock_device
- flasher = build_flasher.BuildFlasher("thisismyserial")
- mock_os.path.exists.return_value = True
- flasher.FlashGSI("exists.img")
- mock_device.fastboot.erase.assert_any_call('system')
- mock_device.fastboot.flash.assert_any_call('system', 'exists.img')
- mock_device.fastboot.erase.assert_any_call('metadata')
-
- @mock.patch(
- "host_controller.build.build_flasher.android_device")
- def testFlashall(self, mock_class):
- mock_device = mock.Mock()
- mock_class.AndroidDevice.return_value = mock_device
- flasher = build_flasher.BuildFlasher("thisismyserial")
- flasher.Flashall("path/to/dir")
- mock_device.fastboot.flashall.assert_called_with()
-
- @mock.patch(
- "host_controller.build.build_flasher.android_device")
- def testEmptySerial(self, mock_class):
- mock_class.list_adb_devices.return_value = ['oneserial']
- flasher = build_flasher.BuildFlasher(serial="")
- mock_class.AndroidDevice.assert_called_with(
- "oneserial", device_callback_port=-1)
-
- @mock.patch(
- "host_controller.build.build_flasher.android_device")
- def testFlashUsingCustomBinary(self, mock_class):
- """Test for FlashUsingCustomBinary().
-
- Tests if the method passes right args to customflasher
- and the execution path of the method.
- """
- mock_device = mock.Mock()
- mock_class.AndroidDevice.return_value = mock_device
- flasher = build_flasher.BuildFlasher("mySerial", "myCustomBinary")
-
- mock_device.isBootloaderMode = False
- device_images = {"img": "my/image/path"}
- flasher.FlashUsingCustomBinary(device_images, "reboottowhatever",
- ["myarg"])
- mock_device.adb.reboot.assert_called_with("reboottowhatever")
- mock_device.customflasher.ExecCustomFlasherCmd.assert_called_with(
- "myarg", "my/image/path")
-
- mock_device.reset_mock()
- mock_device.isBootloaderMode = True
- device_images["img"] = "my/other/image/path"
- flasher.FlashUsingCustomBinary(device_images, "reboottowhatever",
- ["myarg"])
- mock_device.adb.reboot.assert_not_called()
- mock_device.customflasher.ExecCustomFlasherCmd.assert_called_with(
- "myarg", "my/other/image/path")
-
- @mock.patch("host_controller.build.build_flasher.android_device")
- @mock.patch("host_controller.build.build_flasher.logging")
- @mock.patch("host_controller.build.build_flasher.cmd_utils")
- @mock.patch("host_controller.build.build_flasher.os")
- @mock.patch("host_controller.build.build_flasher.open",
- new_callable=mock.mock_open)
- def testRepackageArtifacts(self, mock_open, mock_os, mock_cmd_utils,
- mock_logger, mock_class):
- """Test for RepackageArtifacts().
-
- Tests if the method executes in correct path regarding
- given arguments.
- """
- mock_device = mock.Mock()
- mock_class.AndroidDevice.return_value = mock_device
- flasher = build_flasher.BuildFlasher("serial")
- device_images = {
- "system.img": "/my/tmp/path/system.img",
- "vendor.img": "/my/tmp/path/vendor.img"
- }
- mock_cmd_utils.ExecuteOneShellCommand.return_value = "", "", 0
- ret = flasher.RepackageArtifacts(device_images, "tar.md5")
- self.assertEqual(ret, True)
- self.assertIsNotNone(device_images["img"])
-
- ret = flasher.RepackageArtifacts(device_images, "incorrect")
- self.assertFalse(ret)
- mock_logger.error.assert_called_with(
- "Please specify correct repackage form: --repackage=%s" ,"incorrect")
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/build/build_info.py b/harnesses/host_controller/build/build_info.py
deleted file mode 100644
index 6be9e95..0000000
--- a/harnesses/host_controller/build/build_info.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import collections
-import logging
-import os
-import shutil
-
-
-class BuildInfo(dict):
- """dict class for fetched device imgs, test suites, etc."""
-
- def __init__(self):
- super(BuildInfo, self).__init__()
-
- def __setitem__(self, key, value):
- """__setitem__ for BuildInfo dict.
-
- Remove pre-fetched file which has the same use in HC
- if the old one has different file name from the new one.
-
- Args:
- key: string, key for the path to the fetched file.
- value: string, path to the newly fetched file.
- """
- if key in self and value != self[key]:
- logging.info("Removing pre-fetched item: %s", self[key])
- try:
- if os.path.isfile(self[key]):
- os.remove(self[key])
- elif os.path.isdir(self[key]):
- shutil.rmtree(self[key])
- else:
- logging.error("%s is not found", self[key])
- except OSError as e:
- logging.error("ERROR: error on file remove %s", e)
-
- super(BuildInfo, self).__setitem__(key, value)
-
- def update(self, other=None, **kwargs):
- """Overrides update() in order to call BuildInfo.__setitem__().
-
- Args:
- other: dict or iterable of key/value pairs. Update self
- using this argument
- **kwargs: The optional attributes.
- """
- if other is not None:
- for k, v in other.items() if isinstance(
- other, collections.Mapping) else other:
- self[k] = v
- for k, v in kwargs.items():
- self[k] = v
-
- dict_keys = self.keys()
- for key in dict_keys:
- if not os.path.exists(self[key]):
- logging.info(
- "Removing path info %s from build info", self.pop(key))
diff --git a/harnesses/host_controller/build/build_provider.py b/harnesses/host_controller/build/build_provider.py
deleted file mode 100644
index 17ba0bd..0000000
--- a/harnesses/host_controller/build/build_provider.py
+++ /dev/null
@@ -1,323 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import re
-import shutil
-import tempfile
-import zipfile
-
-from host_controller import common
-from vts.runners.host import utils
-
-
-class BuildProvider(object):
- """The base class for build provider.
-
- Attributes:
- _IMAGE_FILE_EXTENSIONS: a list of strings which are common image file
- extensions.
- _BASIC_IMAGE_FILE_NAMES: a list of strings which are the image names in
- an artifact zip.
- _CONFIG_FILE_EXTENSION: string, the config file extension.
- _additional_files: a dict containing additionally fetched files that
- custom features may need. The key is the path
- relative to temporary directory and the value is the
- full path.
- _configs: dict where the key is config type and value is the config file
- path.
- _device_images: dict where the key is image file name and value is the
- path.
- _test_suites: dict where the key is test suite type and value is the
- test suite package file path.
- _host_controller_package: dict where the key is a host controller
- package and the value is the path
- to a package file.
- _tmp_dirpath: string, the temp dir path created to keep artifacts.
- _last_fetched_artifact_type: string, stores the type of the last
- artifact fetched.
- """
- _CONFIG_FILE_EXTENSION = ".zip"
- _IMAGE_FILE_EXTENSIONS = [".img", ".bin"]
- _BASIC_IMAGE_FILE_NAMES = ["boot.img", "system.img", "vendor.img"]
-
- def __init__(self):
- self._additional_files = {}
- self._device_images = {}
- self._test_suites = {}
- self._host_controller_package = {}
- self._configs = {}
- self._last_fetched_artifact_type = None
- tempdir_base = os.path.join(os.getcwd(), "tmp")
- if not os.path.exists(tempdir_base):
- os.mkdir(tempdir_base)
- self._tmp_dirpath = tempfile.mkdtemp(dir=tempdir_base)
-
- def __del__(self):
- """Deletes the temp dir if still set."""
- if self._tmp_dirpath:
- shutil.rmtree(self._tmp_dirpath)
- self._tmp_dirpath = None
-
- @property
- def tmp_dirpath(self):
- return self._tmp_dirpath
-
- def CreateNewTmpDir(self):
- return tempfile.mkdtemp(dir=self._tmp_dirpath)
-
- def SetDeviceImage(self, name, path):
- """Sets device image `path` for the specified `name`."""
- self._device_images[name] = path
- self._last_fetched_artifact_type = common._ARTIFACT_TYPE_DEVICE
-
- def _IsFullDeviceImage(self, namelist):
- """Returns true if given namelist list has all common device images."""
- for image_file in self._BASIC_IMAGE_FILE_NAMES:
- if image_file not in namelist:
- return False
- return True
-
- def _IsImageFile(self, file_path):
- """Returns whether a file is an image.
-
- Args:
- file_path: string, the file path.
-
- Returns:
- boolean, whether the file is an image.
- """
- return any(file_path.endswith(ext)
- for ext in self._IMAGE_FILE_EXTENSIONS)
-
- def SetDeviceImageZip(self, path, full_device_images=False):
- """Sets device image(s) using files in a given zip file.
-
- It extracts image files inside the given zip file and selects
- known Android image files.
-
- Args:
- path: string, the path to a zip file.
- """
- dest_path = path + ".dir"
- fetch_type = None
- with zipfile.ZipFile(path, 'r') as zip_ref:
- if full_device_images or self._IsFullDeviceImage(zip_ref.namelist()):
- self.SetDeviceImage(common.FULL_ZIPFILE, path)
- dir_key = common.FULL_ZIPFILE_DIR
- fetch_type = common._ARTIFACT_TYPE_DEVICE
- else:
- self.SetDeviceImage("gsi-zipfile", path)
- dir_key = common.GSI_ZIPFILE_DIR # "gsi-zipfile-dir"
- fetch_type = common._ARTIFACT_TYPE_GSI
- if os.path.exists(dest_path):
- shutil.rmtree(dest_path)
- logging.info("%s %s deleted", dir_key, dest_path)
- zip_ref.extractall(dest_path)
- self.SetFetchedDirectory(dest_path)
- self.SetDeviceImage(dir_key, dest_path)
-
- self._last_fetched_artifact_type = fetch_type
-
- def GetDeviceImage(self, name=None):
- """Returns device image info."""
- if name is None:
- return self._device_images
- return self._device_images[name]
-
- def RemoveDeviceImage(self, name):
- """Removes certain device image info.
-
- Args:
- name: string, the name of the device image file
- that needs to be removed.
- """
- if name in self._device_images:
- self._device_images.pop(name)
-
- def SetTestSuitePackage(self, test_suite, path):
- """Sets test suite package `path` for the specified `type`.
-
- Args:
- test_suite: string, test suite type such as 'vts' or 'cts', etc.
- path: string, the path of a file. if a file is a zip file,
- it's unziped and its main binary is set.
- """
- if re.match("[vcgs]ts", test_suite):
- suite_name = "android-%s" % test_suite
- tradefed_name = "%s-tradefed" % test_suite
- dest_path = os.path.join(self.tmp_dirpath, suite_name)
- if os.path.exists(dest_path):
- shutil.rmtree(dest_path)
- logging.info("test suite %s deleted", dest_path)
- with zipfile.ZipFile(path, 'r') as zip_ref:
- zip_ref.extractall(dest_path)
- bin_path = os.path.join(dest_path, suite_name,
- "tools", tradefed_name)
- os.chmod(bin_path, 0766)
- path = bin_path
- else:
- logging.info("unsupported zip file %s", path)
- self._test_suites[test_suite] = path
- self._last_fetched_artifact_type = common._ARTIFACT_TYPE_TEST_SUITE
-
- def GetTestSuitePackage(self, type=None):
- """Returns test suite package info."""
- if type is None:
- return self._test_suites
- return self._test_suites[type]
-
- def SetHostControllerPackage(self, package_type, path):
- """Sets host controller package `path` for the specified `type`.
-
- Args:
- package_type: string, host controller type such as 'vtslab'.
- path: string, the path of a package file.
- """
- self._host_controller_package[package_type] = path
- self._last_fetched_artifact_type = common._ARTIFACT_TYPE_INFRA
-
- def GetHostControllerPackage(self, package_type=None):
- """Returns host controller package info.
-
- Args:
- package_type: string, key value to self._host_controller_package
- dict.
-
- Returns:
- the whole dict if package_type is None, otherwise a string which is
- the path to the fetched host controller package.
- """
- if package_type is None:
- return self._host_controller_package
- return self._host_controller_package[package_type]
-
- def SetConfigPackage(self, config_type, path):
- """Sets test suite package `path` for the specified `type`.
-
- All valid config files have .zip extension.
-
- Args:
- config_type: string, config type such as 'prod' or 'test'.
- path: string, the path of a config file.
- """
- if path.endswith(self._CONFIG_FILE_EXTENSION):
- dest_path = os.path.join(
- self.tmp_dirpath, os.path.basename(path) + ".dir")
- with zipfile.ZipFile(path, 'r') as zip_ref:
- zip_ref.extractall(dest_path)
- path = dest_path
- else:
- logging.info("unsupported config package file %s", path)
- self._configs[config_type] = path
- self._last_fetched_artifact_type = common._ARTIFACT_TYPE_INFRA
-
- def GetConfigPackage(self, config_type=None):
- """Returns config package info."""
- if config_type is None:
- return self._configs
- return self._configs[config_type]
-
- def SetAdditionalFile(self, rel_path, full_path):
- """Sets the key and value of additionally fetched files.
-
- Args:
- rel_path: the file path relative to temporary directory.
- abs_path: the file path that this process can access.
- """
- self._additional_files[rel_path] = full_path
- self._last_fetched_artifact_type = common._ARTIFACT_TYPE_INFRA
-
- def GetAdditionalFile(self, rel_path=None):
- """Returns the paths to fetched files."""
- if rel_path is None:
- return self._additional_files
- return self._additional_files[rel_path]
-
- def SetFetchedDirectory(self,
- dir_path,
- root_path=None,
- full_device_images=False):
- """Adds every file in a directory to one of the dictionaries.
-
- This method follows symlink to file, but skips symlink to directory.
-
- Args:
- dir_path: string, the directory to find files in.
- root_path: string, the temporary directory that dir_path is in.
- The default value is dir_path.
- """
- for dir_name, file_name in utils.iterate_files(dir_path):
- full_path = os.path.join(dir_name, file_name)
- self.SetFetchedFile(full_path, (root_path
- if root_path else dir_path),
- full_device_images)
-
- def SetFetchedFile(self,
- file_path,
- root_dir=None,
- full_device_images=False,
- set_suite_as=None):
- """Adds a file to one of the dictionaries.
-
- Args:
- file_path: string, the path to the file.
- root_dir: string, the temporary directory that file_path is in.
- The default value is file_path if file_path is a
- directory. Otherwise, the default value is file_path's
- parent directory.
- set_suite_as: string, the test suite name to use for the given
- artifact. Used when the file name does not follow
- the standard "android-*ts.zip" file name pattern.
- """
- file_name = os.path.basename(file_path)
- if os.path.isdir(file_path):
- self.SetFetchedDirectory(file_path, root_dir, full_device_images)
- elif self._IsImageFile(file_path):
- self.SetDeviceImage(file_name, file_path)
- elif re.match("android-[vcgs]ts.zip", file_name):
- test_suite = (file_name.split("-")[-1]).split(".")[0]
- self.SetTestSuitePackage(test_suite, file_path)
- elif file_name == "android-vtslab.zip":
- self.SetHostControllerPackage("vtslab", file_path)
- elif file_name.startswith("vti-global-config"):
- self.SetConfigPackage(
- "prod" if "prod" in file_name else "test", file_path)
- elif set_suite_as:
- self.SetTestSuitePackage(set_suite_as, file_path)
- elif file_path.endswith(".zip"):
- self.SetDeviceImageZip(file_path, full_device_images)
- else:
- rel_path = (os.path.relpath(file_path, root_dir) if root_dir else
- os.path.basename(file_path))
- self.SetAdditionalFile(rel_path, file_path)
-
- def PrintDeviceImageInfo(self):
- """Prints device image info."""
- logging.info(self.GetDeviceImage())
-
- def PrintGetTestSuitePackageInfo(self):
- """Prints test suite package info."""
- logging.info(self.GetTestSuitePackage())
-
- def GetFetchedArtifactType(self):
- """Gets the most recently fetched artifact type.
-
- Returns:
- string, type of the artifact.
- """
- return self._last_fetched_artifact_type
diff --git a/harnesses/host_controller/build/build_provider_ab.py b/harnesses/host_controller/build/build_provider_ab.py
deleted file mode 100644
index 3debdb4..0000000
--- a/harnesses/host_controller/build/build_provider_ab.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-
-from host_controller.build import build_provider
-from vts.utils.python.build.api import artifact_fetcher
-
-
-class BuildProviderAB(build_provider.BuildProvider):
- """A build provider for Android Build (AB)."""
-
- def __init__(self):
- super(BuildProviderAB, self).__init__()
- if 'run_ab_key' in os.environ:
- logging.info(
- "For AB, use the key at %s", os.environ['run_ab_key'])
- self._artifact_fetcher = artifact_fetcher.AndroidBuildClient(
- os.environ['run_ab_key'])
- else:
- self._artifact_fetcher = None
-
- def GetLatestBuildId(self, branch, target):
- """Get the latest build id.
-
- Args:
- branch: string, android branch to pull resource from.
- target: string, build target name.
-
- Returns:
- string, latest build id. None if _artifact_fetcher is not initialized.
- """
- if not self._artifact_fetcher:
- return None
-
- recent_build_ids = self._artifact_fetcher.ListBuildIds(
- branch, target)
-
- return recent_build_ids[0]
-
- def Fetch(self,
- branch,
- target,
- artifact_name,
- build_id="latest",
- full_device_images=False):
- """Fetches Android device artifact file(s) from Android Build.
-
- Args:
- branch: string, android branch to pull resource from.
- target: string, build target name.
- artifact_name: string, file name.
- build_id: string, ID of the build or latest.
-
- Returns:
- a dict containing the device image info.
- a dict containing the test suite package info.
- a dict containing the artifact info.
- """
- fetch_info = {}
- fetch_info["build_id"] = None
-
- if not self._artifact_fetcher:
- return self.GetDeviceImage(), self.GetTestSuitePackage(), fetch_info
-
- if build_id == "latest":
- build_id = self.GetLatestBuildId(branch, target)
- fetch_info["build_id"] = build_id
-
- if "{build_id}" in artifact_name:
- artifact_name = artifact_name.replace("{build_id}", build_id)
-
- dest_filepath = os.path.join(self.tmp_dirpath, artifact_name)
- self._artifact_fetcher.DownloadArtifactToFile(
- branch, target, build_id, artifact_name,
- dest_filepath=dest_filepath)
-
- self.SetFetchedFile(dest_filepath, full_device_images)
-
- return self.GetDeviceImage(), self.GetTestSuitePackage(), fetch_info
diff --git a/harnesses/host_controller/build/build_provider_gcs.py b/harnesses/host_controller/build/build_provider_gcs.py
deleted file mode 100644
index 0dc8f5a..0000000
--- a/harnesses/host_controller/build/build_provider_gcs.py
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import re
-import zipfile
-
-from host_controller.build import build_provider
-from host_controller.utils.gcp import gcs_utils
-from vts.utils.python.common import cmd_utils
-
-_GCLOUD_AUTH_ENV_KEY = "run_gcs_key"
-
-
-class BuildProviderGCS(build_provider.BuildProvider):
- """A build provider for GCS (Google Cloud Storage)."""
-
- def __init__(self):
- super(BuildProviderGCS, self).__init__()
- if _GCLOUD_AUTH_ENV_KEY in os.environ:
- gcloud_path = BuildProviderGCS.GetGcloudPath()
- if gcloud_path is not None:
- auth_cmd = "%s auth activate-service-account --key-file=%s" % (
- gcloud_path, os.environ[_GCLOUD_AUTH_ENV_KEY])
- _, stderr, ret_code = cmd_utils.ExecuteOneShellCommand(
- auth_cmd)
- if ret_code == 0:
- logging.info(stderr)
- else:
- logging.error(stderr)
-
- @staticmethod
- def GetGcloudPath():
- """Returns the gcloud file path if found; None otherwise."""
- sh_stdout, _, ret_code = cmd_utils.ExecuteOneShellCommand(
- "which gcloud")
- if ret_code == 0:
- return sh_stdout.strip()
- else:
- logging.error("`gcloud` doesn't exist on the host; "
- "please install Google Cloud SDK before retrying.")
- return None
-
- def Fetch(self, path, full_device_images=False, set_suite_as=None):
- """Fetches Android device artifact file(s) from GCS.
-
- Args:
- path: string, the path of a directory which keeps artifacts.
- set_suite_as: string, the test suite name to use for the given
- artifact. Used when the file name does not follow
- the standard "android-*ts.zip" file name pattern.
-
- Returns:
- a dict containing the device image info.
- a dict containing the test suite package info.
- a dict containing the info about custom tool files.
- """
- if not path.startswith("gs://"):
- path = "gs://" + re.sub("^/*", "", path)
- path = re.sub("/*$", "", path)
- gsutil_path = gcs_utils.GetGsutilPath()
- if gsutil_path:
- temp_dir_path = self.CreateNewTmpDir()
- # IsGcsFile returns False if path is directory or doesn't exist.
- # cp command returns non-zero if path doesn't exist.
- if not gcs_utils.IsGcsFile(gsutil_path, path):
- dest_path = temp_dir_path
- if "latest.zip" in path:
- gsutil_ls_path = re.sub("latest.zip", "*.zip", path)
- lines_gsutil_ls = gcs_utils.List(gsutil_path, gsutil_ls_path)
- if lines_gsutil_ls:
- lines_gsutil_ls.sort()
- path = lines_gsutil_ls[-1]
- copy_command = "%s cp %s %s" % (gsutil_path, path,
- temp_dir_path)
- else:
- logging.error(
- "There is no file(s) that matches the URL %s.",
- path)
- return (self.GetDeviceImage(),
- self.GetTestSuitePackage(),
- self.GetAdditionalFile())
- else:
- copy_command = "%s cp -r %s/* %s" % (gsutil_path, path,
- temp_dir_path)
- else:
- dest_path = os.path.join(temp_dir_path, os.path.basename(path))
- copy_command = "%s cp %s %s" % (gsutil_path, path,
- temp_dir_path)
-
- _, _, ret_code = cmd_utils.ExecuteOneShellCommand(copy_command)
- if ret_code == 0:
- self.SetFetchedFile(dest_path, temp_dir_path,
- full_device_images, set_suite_as)
- else:
- logging.error("Error in copy file from GCS (code %s).",
- ret_code)
- return (self.GetDeviceImage(), self.GetTestSuitePackage(),
- self.GetAdditionalFile())
diff --git a/harnesses/host_controller/build/build_provider_local_fs.py b/harnesses/host_controller/build/build_provider_local_fs.py
deleted file mode 100644
index afcf429..0000000
--- a/harnesses/host_controller/build/build_provider_local_fs.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.build import build_provider
-
-
-class BuildProviderLocalFS(build_provider.BuildProvider):
- """A build provider for local file system (fs)."""
-
- def __init__(self):
- super(BuildProviderLocalFS, self).__init__()
-
- def Fetch(self, path, full_device_images=False):
- """Fetches Android device artifact file(s) from a local path.
-
- Args:
- path: string, the path of the artifacts.
-
- Returns:
- a dict containing the device image info.
- a dict containing the test suite package info.
- """
- if path.endswith(".tar.md5"):
- self.SetDeviceImage("img", path)
- else:
- self.SetFetchedFile(path, full_device_images=full_device_images)
- return self.GetDeviceImage(), self.GetTestSuitePackage()
diff --git a/harnesses/host_controller/build/build_provider_pab.py b/harnesses/host_controller/build/build_provider_pab.py
deleted file mode 100644
index 9d932b6..0000000
--- a/harnesses/host_controller/build/build_provider_pab.py
+++ /dev/null
@@ -1,728 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""Module to fetch artifacts from Partner Android Build server."""
-
-import argparse
-import getpass
-import httplib2
-import json
-import logging
-import os
-import requests
-import urlparse
-from posixpath import join as path_urljoin
-
-from oauth2client.client import flow_from_clientsecrets
-from oauth2client.file import Storage
-from oauth2client.tools import argparser
-from oauth2client.tools import run_flow
-
-from selenium import webdriver
-from selenium.webdriver.common.by import By
-from selenium.common.exceptions import TimeoutException
-from selenium.webdriver.support import expected_conditions as EC
-from selenium.webdriver.common.keys import Keys
-from selenium.webdriver.chrome.options import Options
-from selenium.webdriver.support.ui import WebDriverWait
-
-from host_controller.build import build_provider
-
-# constants for GET and POST endpoints
-GET = 'GET'
-POST = 'POST'
-
-# timeout seconds for requests
-REQUESTS_TIMEOUT_SECONDS = 60
-
-
-class BuildProviderPAB(build_provider.BuildProvider):
- """Client that manages Partner Android Build downloading.
-
- Attributes:
- BAD_XSRF_CODE: int, error code for bad XSRF token error
- BASE_URL: string, path to PAB entry point
- BUILDARTIFACT_NAME_KEY: string, index in artifact containing name
- BUILD_BUILDID_KEY: string, index in build containing build_id
- BUILD_COMPLETED_STATUS: int, value of 'complete' build
- BUILD_STATUS_KEY: string, index in build object containing status.
- CHROME_DRIVER_LOCATION: string, path to chromedriver
- CHROME_LOCATION: string, path to Chrome browser
- CLIENT_STORAGE: string, path to store credentials.
- DEFAULT_CHUNK_SIZE: int, number of bytes to download at a time.
- DOWNLOAD_URL_KEY: string, index in downloadBuildArtifact containing url
- EMAIL: string, email constant for userinfo JSON
- EXPIRED_XSRF_CODE: int, error code for expired XSRF token error
- GETBUILD_ARTIFACTS_KEY, string, index in build obj containing artifacts
- GMS_DOWNLOAD_URL: string, base url for downloading artifacts.
- LISTBUILD_BUILD_KEY: string, index in listBuild containing builds
- PAB_URL: string, redirect url from Google sign-in to PAB
- PASSWORD: string, password constant for userinfo JSON
- SCOPE: string, URL for which to request access via oauth2.
- SVC_URL: string, path to buildsvc RPC
- XSRF_STORE: string, path to store xsrf token
- _credentials : oauth2client credentials object
- _userinfo_file: location of file containing email and password
- _xsrf : string, XSRF token from PAB website. expires after 7 days.
- """
- _credentials = None
- _userinfo_file = None
- _xsrf = None
- BAD_XSRF_CODE = -32000
- BASE_URL = 'https://partner.android.com'
- BUILDARTIFACT_NAME_KEY = '1'
- BUILD_BUILDID_KEY = '1'
- BUILD_COMPLETED_STATUS = 7
- BUILD_STATUS_KEY = '7'
- CHROME_DRIVER_LOCATION = '/usr/local/bin/chromedriver'
- CHROME_LOCATION = '/usr/bin/google-chrome'
- CLIENT_SECRETS = os.path.join(
- os.path.dirname(__file__), 'client_secrets.json')
- CLIENT_STORAGE = os.path.join(os.path.dirname(__file__), 'credentials')
- DEFAULT_CHUNK_SIZE = 1024
- DOWNLOAD_URL_KEY = '1'
- EMAIL = 'email'
- EXPIRED_XSRF_CODE = -32001
- GETBUILD_ARTIFACTS_KEY = '2'
- GMS_DOWNLOAD_URL = 'https://partnerdash.google.com/build/gmsdownload'
- LISTBUILD_BUILD_KEY = '1'
- PAB_URL = ('https://www.google.com/accounts/Login?&continue='
- 'https://partner.android.com/build/')
- PASSWORD = 'password'
- # need both of these scopes to access PAB downloader
- scopes = ('https://www.googleapis.com/auth/partnerdash',
- 'https://www.googleapis.com/auth/alkali-base')
- SCOPE = ' '.join(scopes)
- SVC_URL = urlparse.urljoin(BASE_URL, 'build/u/0/_gwt/_rpc/buildsvc')
- XSRF_STORE = os.path.join(os.path.dirname(__file__), 'xsrf')
-
- def __init__(self):
- """Creates a temp dir."""
- super(BuildProviderPAB, self).__init__()
-
- def Authenticate(self, userinfo_file=None, noauth_local_webserver=False,
- scopes=SCOPE):
- """Authenticate using OAuth2.
-
- Args:
- userinfo_file: (optional) the path of a JSON file which has
- "email" and "password" string fields.
- noauth_local_webserver: boolean, True if do not (or can not) use
- a local web server.
- scopes: string or iterable of strings, the scopes to request.
- """
- # this should be a JSON file with "email" and "password" string fields
- self._userinfo_file = userinfo_file
- logging.info('Parsing flags, use --noauth_local_webserver'
- ' if running on remote machine')
-
- parser = argparse.ArgumentParser(parents=[argparser])
- flags, unknown = parser.parse_known_args()
- flags.noauth_local_webserver = noauth_local_webserver
- logging.info('Preparing OAuth token')
- flow = flow_from_clientsecrets(self.CLIENT_SECRETS, scope=scopes)
- storage = Storage(self.CLIENT_STORAGE)
- if self._credentials is None:
- self._credentials = storage.get()
- if self._credentials is None or self._credentials.invalid:
- logging.info('Credentials not found, authenticating.')
- self._credentials = run_flow(flow, storage, flags)
-
- if self._credentials.access_token_expired:
- logging.info('Access token expired, refreshing.')
- self._credentials.refresh(http=httplib2.Http())
-
- if self.XSRF_STORE is not None and os.path.isfile(self.XSRF_STORE):
- with open(self.XSRF_STORE, 'r') as handle:
- self._xsrf = handle.read()
-
- def GetXSRFToken(self, email=None, password=None):
- """Get XSRF token. Prompt if email/password not provided.
-
- Args:
- email: string, optional. Gmail account of user logging in
- password: string, optional. Password of user logging in
-
- Returns:
- boolean, whether the token was accessed and stored
-
- Raises:
- ValueError if login fails or userinfo file is malformed.
- """
- if self._userinfo_file is not None:
- with open(self._userinfo_file, 'r') as handle:
- userinfo = json.load(handle)
-
- if self.EMAIL not in userinfo or self.PASSWORD not in userinfo:
- raise ValueError(
- 'Malformed userinfo file: needs email and password')
-
- email = userinfo[self.EMAIL]
- password = userinfo[self.PASSWORD]
-
- chrome_options = Options()
- chrome_options.add_argument("--headless")
-
- driver = webdriver.Chrome(
- chrome_options=chrome_options)
-
- driver.set_window_size(1080, 800)
- wait = WebDriverWait(driver, 10)
-
- driver.get(self.PAB_URL)
-
- query = driver.find_element_by_id("identifierId")
- if email is None:
- email = raw_input("Email: ")
- query.send_keys(email)
- driver.find_element_by_id("identifierNext").click()
-
- pw = wait.until(EC.element_to_be_clickable((By.NAME, "password")))
- pw.clear()
-
- if password is None:
- pw.send_keys(getpass.getpass("Password: "))
- else:
- pw.send_keys(password)
-
- driver.find_element_by_id("passwordNext").click()
-
- try:
- wait.until(EC.title_contains("Partner Android Build"))
- except TimeoutException as e:
- logging.exception(e)
- raise ValueError('Wrong password or non-standard login flow')
-
- self._xsrf = driver.execute_script("return clientConfig.XSRF_TOKEN;")
- with open(self.XSRF_STORE, 'w') as handle:
- handle.write(self._xsrf)
-
- return True
-
- def CallBuildsvc(self, method, params, account_id):
- """Call the buildsvc RPC with given parameters.
-
- Args:
- method: string, name of method to be called in buildsvc
- params: dict, parameters to RPC call
- account_id: int, ID associated with the PAB account.
-
- Returns:
- dict, result from RPC call
-
- Raises:
- ValueError if RPC call returns an error or an unknown response.
- """
- if self._xsrf is None:
- self.GetXSRFToken()
- params = json.dumps(params)
-
- data = {"method": method, "params": params, "xsrf": self._xsrf}
- data = json.dumps(data)
- headers = {}
- self._credentials.apply(headers)
- headers['Content-Type'] = 'application/json'
- headers['x-alkali-account'] = account_id
-
- try:
- response = requests.post(self.SVC_URL, data=data, headers=headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- raise ValueError("Request timeout.")
-
- responseJSON = {}
-
- try:
- responseJSON = response.json()
- except ValueError:
- raise ValueError("Backend error -- check your account ID")
-
- if 'result' in responseJSON:
- return responseJSON['result']
-
- if 'error' in responseJSON and 'code' in responseJSON['error']:
- if responseJSON['error']['code'] == self.BAD_XSRF_CODE:
- raise ValueError(
- "Bad XSRF token -- must be for the same account as your credentials")
- if responseJSON['error']['code'] == self.EXPIRED_XSRF_CODE:
- raise ValueError("Expired XSRF token -- please refresh")
-
- raise ValueError("Unknown response from server -- %s" %
- json.dumps(responseJSON))
-
- def GetBuildList(self,
- account_id,
- branch,
- target,
- page_token="",
- max_results=10,
- internal=True,
- method=GET,
- verify_signed=False):
- """Get the list of builds for a given account, branch and target
- Args:
- account_id: int, ID associated with the PAB account.
- branch: string, branch to pull resource from.
- target: string, "latest" or a specific version.
- page_token: string, token used for pagination
- max_results: maximum build results the build list contains, e.g. 25
- internal: bool, whether to query internal build
- method: 'GET' or 'POST', which endpoint to query
- verify_signed: bool, whether to verify signed build.
-
- Returns:
- list of dicts representing the builds, descending in time
-
- Raises:
- ValueError if build request returns an error or builds not found.
- """
- if method == POST:
- params = {
- "1": branch,
- "2": target,
- "3": page_token,
- "4": max_results,
- "7": int(internal)
- }
-
- result = self.CallBuildsvc("listBuild", params, account_id)
- # in listBuild response, index '1' contains builds
- if self.LISTBUILD_BUILD_KEY in result:
- return result[self.LISTBUILD_BUILD_KEY]
-
- if verify_signed:
- logging.error("verify_signed does not support POST method.")
-
- raise ValueError("Build list not found -- %s" % params)
- elif method == GET:
- headers = {}
- self._credentials.apply(headers)
-
- action = 'list-internal' if internal else 'list'
- # PAB URL format expects something (anything) to be given as buildid
- # and resource, even for action list
- dummy = 'DUMMY'
- url = path_urljoin(self.BASE_URL, 'build', 'builds', action,
- branch, target, dummy,
- dummy) + '?a=' + str(account_id)
- try:
- response = requests.get(url, headers=headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- responseJSON = response.json()
- builds = responseJSON['build']
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- raise ValueError("Request timeout.")
- except ValueError as e:
- logging.exception(e)
- raise ValueError("Backend error -- check your account ID")
-
- if verify_signed:
- for build in builds:
- artifact_name = "signed%2Fsigned-{}-img-{}.zip".format(
- target.split("-")[0], build["build_id"])
- logging.debug("Checking whether the build is signed for "
- "build_target {} and build_id {}".format(
- target, build["build_id"]))
- signed_build_url = self.GetArtifactURL(
- account_id=account_id,
- build_id=build["build_id"],
- target=target,
- artifact_name=artifact_name,
- branch=branch,
- internal=False,
- method=method)
- try:
- self.GetResponseWithURL(signed_build_url)
- logging.debug("The build is signed.")
- build["signed"] = True
- except requests.HTTPError:
- logging.debug("The build is not signed.")
- build["signed"] = False
- except requests.exceptions.Timeout as e:
- logging.debug("Server is not responding.")
- logging.exception(e)
- build["signed"] = False
- return builds
-
- def GetLatestBuildId(self, account_id, branch, target, method=GET):
- """Get the most recent build_id for a given account, branch and target
- Args:
- account_id: int, ID associated with the PAB account.
- branch: string, branch to pull resource from.
- target: string, "latest" or a specific version.
- method: 'GET' or 'POST', which endpoint to query
-
- Returns:
- string, most recent build id
-
- Raises:
- ValueError if complete builds are not found.
- """
- # TODO: support pagination, maybe?
- build_list = self.GetBuildList(account_id=account_id,
- branch=branch,
- target=target,
- method=method)
- if len(build_list) == 0:
- raise ValueError(
- 'No builds found for account_id=%s, branch=%s, target=%s' %
- (account_id, branch, target))
- for build in build_list:
- if method == POST:
- # get build status: 7 = completed build
- if build.get(self.BUILD_STATUS_KEY,
- None) == self.BUILD_COMPLETED_STATUS:
- # return build id (index '1')
- return build[self.BUILD_BUILDID_KEY]
- elif method == GET:
- if build['build_attempt_status'] == "COMPLETE" and build[
- "successful"]:
- return build['build_id']
- raise ValueError(
- 'No complete builds found: %s failed or incomplete builds found' %
- len(build_list))
-
- def GetBuildArtifacts(
- self, account_id, build_id, branch, target, method=POST):
- """Get the list of build artifacts.
-
- For an account, build, target, branch.
-
- Args:
- account_id: int, ID associated with the PAB account.
- build_id: string, ID of the build
- branch: string, branch to pull resource from.
- target: string, "latest" or a specific version.
- method: 'GET' or 'POST', which endpoint to query
-
- Returns:
- list of build artifact objects
-
- Raises:
- NotImplementedError if method is 'GET', which is not supported yet.
- ValueError if build artifacts are not found.
- """
- if method == GET:
- raise NotImplementedError(
- "GetBuildArtifacts not supported with GET")
- params = {"1": build_id, "2": target, "3": branch}
-
- result = self.CallBuildsvc("getBuild", params, account_id)
- # in getBuild response, index '2' contains the artifacts
- if self.GETBUILD_ARTIFACTS_KEY in result:
- return result[self.GETBUILD_ARTIFACTS_KEY]
- if len(result) == 0:
- raise ValueError("Build artifacts not found -- %s" % params)
-
- def GetArtifactURL(self,
- account_id,
- build_id,
- target,
- artifact_name,
- branch,
- internal,
- method=GET):
- """Get the URL for an artifact on the PAB server, using buildsvc.
-
- Args:
- account_id: int, ID associated with the PAB account.
- build_id: string/int, id of the build.
- target: string, "latest" or a specific version.
- artifact_name: string, simple file name (no parent dir or path).
- branch: string, branch to pull resource from.
- internal: int, whether the request is for an internal build artifact
- method: 'GET' or 'POST', which endpoint to query
-
- Returns:
- string, The URL for the resource specified by the parameters
-
- Raises:
- ValueError if given parameters are incorrect or resource not found.
- """
- if method == POST:
- params = {
- "1": str(build_id),
- "2": target,
- "3": artifact_name,
- "4": branch,
- "5": "", # release_candidate_name
- "6": internal
- }
-
- result = self.CallBuildsvc(method='downloadBuildArtifact',
- params=params,
- account_id=account_id)
-
- # in downloadBuildArtifact response, index '1' contains the url
- if self.DOWNLOAD_URL_KEY in result:
- return result[self.DOWNLOAD_URL_KEY]
- if len(result) == 0:
- raise ValueError("Resource not found -- %s" % params)
- elif method == GET:
- headers = {}
- self._credentials.apply(headers)
-
- action = 'get-internal' if internal else 'get'
- get_url = path_urljoin(self.BASE_URL, 'build', 'builds', action,
- branch, target, build_id,
- artifact_name) + '?a=' + str(account_id)
-
- try:
- response = requests.get(get_url, headers=headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- responseJSON = response.json()
- return responseJSON['url']
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- raise ValueError("Request timeout.")
- except ValueError:
- raise ValueError("Backend error -- check your account ID")
-
- def DownloadArtifact(self, download_url, filename):
- """Get artifact from Partner Android Build server.
-
- Args:
- download_url: location of resource that we want to download
- filename: where the artifact gets downloaded locally.
-
- Returns:
- boolean, whether the file was successfully downloaded
- """
- try:
- response = self.GetResponseWithURL(download_url)
- except (requests.HTTPError, requests.exceptions.Timeout) as error:
- logging.exception(error)
- return False
- logging.info('%s now downloading...', download_url)
- with open(filename, 'wb') as handle:
- for block in response.iter_content(self.DEFAULT_CHUNK_SIZE):
- handle.write(block)
- return True
-
- def GetArtifact(self,
- account_id,
- branch,
- target,
- artifact_name,
- build_id='latest',
- method=GET,
- full_device_images=False):
- """Get an artifact for an account, branch, target and name and build id.
-
- If build_id not given, get latest.
-
- Args:
- account_id: int, ID associated with the PAB account.
- branch: string, branch to pull resource from.
- target: string, "latest" or a specific version.
- artifact_name: name of artifact, e.g. aosp_arm64_ab-img-4353141.zip
- ({id} will automatically get replaced with build ID)
- build_id: string, build ID of an artifact to fetch (or 'latest').
- method: 'GET' or 'POST', which endpoint to query.
-
- Returns:
- a dict containing the device image info.
- a dict containing the test suite package info.
- a dict containing the artifact info.
- a dict containing the global config info.
-
- Raises:
- ValueError if artifacts are not found.
- """
- artifact_info = {}
- if build_id == 'latest':
- build_id = self.GetLatestBuildId(account_id=account_id,
- branch=branch,
- target=target,
- method=method)
- logging.info("latest build ID = %s", build_id)
- artifact_info["build_id"] = build_id
-
- if "build_id" in artifact_name:
- artifact_name = artifact_name.format(build_id=build_id)
-
- if method == POST:
- artifacts = self.GetBuildArtifacts(account_id=account_id,
- build_id=build_id,
- branch=branch,
- target=target,
- method=method)
-
- if len(artifacts) == 0:
- raise ValueError(
- "No artifacts found for build_id=%s, branch=%s, target=%s"
- % (build_id, branch, target))
-
- # in build artifact response, index '1' contains the name
- artifact_names = [
- artifact[self.BUILDARTIFACT_NAME_KEY] for artifact in artifacts
- ]
- if artifact_name not in artifact_names:
- raise ValueError("%s not found in artifact list" %
- artifact_name)
-
- url = self.GetArtifactURL(account_id=account_id,
- build_id=build_id,
- target=target,
- artifact_name=artifact_name,
- branch=branch,
- internal=False,
- method=method)
-
- if self.tmp_dirpath:
- artifact_path = os.path.join(self.tmp_dirpath, artifact_name)
- else:
- artifact_path = artifact_name
- self.DownloadArtifact(url, artifact_path)
-
- self.SetFetchedFile(
- artifact_path, full_device_images=full_device_images)
-
- return (self.GetDeviceImage(), self.GetTestSuitePackage(),
- artifact_info, self.GetConfigPackage())
-
- def GetSignedBuildArtifact(self,
- account_id,
- branch,
- target,
- artifact_name,
- build_id='latest',
- method=GET,
- full_device_images=False):
- """Get an signed build artifact from the PAB bulid list.
-
- Args:
- account_id: int, ID associated with the PAB account.
- branch: string, branch to pull resource from.
- target: string, "latest" or a specific version.
- artifact_name: name of artifact, e.g. aosp_arm64_ab-img-4353141.zip
- ({id} will automatically get replaced with build ID)
- build_id: string, build ID of an artifact to fetch (or 'latest').
- method: 'GET' or 'POST', which endpoint to query.
-
- Returns:
- a dict containing the device image info.
- a dict containing the test suite package info.
- a dict containing the artifact info.
- a dict containing the global config info.
- """
- artifact_info = {}
- build_ids = []
- artifact_path = ""
- if build_id == 'latest':
- try:
- build_list = self.GetBuildList(
- account_id=account_id,
- branch=branch,
- target=target,
- method=method)
- for build in build_list:
- build_ids.append(build["build_id"])
- except ValueError as e:
- logging.exception(e)
- else:
- build_ids.append(build_id)
-
- for build_id in build_ids:
- _artifact_name = artifact_name
- if "build_id" in _artifact_name:
- _artifact_name = _artifact_name.format(build_id=build_id)
- _artifact_name = "signed%2Fsigned-" + _artifact_name
- try:
- url = self.GetArtifactURL(
- account_id=account_id,
- build_id=build_id,
- target=target,
- artifact_name=_artifact_name,
- branch=branch,
- internal=False,
- method=method)
- except ValueError as e:
- logging.exception(e)
- continue
-
- if self.tmp_dirpath:
- artifact_path = os.path.join(self.tmp_dirpath, _artifact_name)
- else:
- artifact_path = _artifact_name
- ret = self.DownloadArtifact(url, artifact_path)
-
- if ret:
- artifact_info["build_id"] = build_id
- break
-
- self.SetFetchedFile(
- artifact_path, full_device_images=full_device_images)
-
- return (self.GetDeviceImage(), self.GetTestSuitePackage(),
- artifact_info, self.GetConfigPackage())
-
- def GetResponseWithURL(self, url):
- """Gets the response content from the server connected with the url.
-
- Args:
- url: A string representing the server url.
-
- Returns:
- A Response object received from the server.
-
- Raises:
- requests.HTTPError if response.status_code is not 200.
- requests.exceptions.Timeout if the server does not respond.
- """
- headers = {}
- self._credentials.apply(headers)
- response = requests.get(url, headers=headers, stream=True,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- response.raise_for_status()
-
- return response
-
- def FetchLatestBuiltHCPackage(self, account_id, branch, target):
- """Fetchs the latest <artifact_name> file and return the path.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
-
- Returns:
- path to the fetched file. None if the fetching has failed.
- """
- try:
- listed_builds = self.GetBuildList(
- account_id=account_id,
- branch=branch,
- target=target,
- page_token="",
- max_results=1,
- method="GET")
-
- if listed_builds and len(listed_builds) > 0:
- for listed_build in listed_builds:
- if listed_build["successful"]:
- self.GetArtifact(
- account_id=account_id,
- branch=branch,
- target=target,
- artifact_name="android-vtslab.zip",
- build_id=listed_build["build_id"],
- method="GET")
-
- return self.GetHostControllerPackage("vtslab")
- except ValueError as e:
- logging.exception(e)
diff --git a/harnesses/host_controller/build/build_provider_pab_test.py b/harnesses/host_controller/build/build_provider_pab_test.py
deleted file mode 100644
index 95e83c9..0000000
--- a/harnesses/host_controller/build/build_provider_pab_test.py
+++ /dev/null
@@ -1,317 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import unittest
-from host_controller.build import build_provider_pab
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from requests.models import Response
-
-
-class BuildProviderPABTest(unittest.TestCase):
- """Tests for Partner Android Build client."""
-
- def setUp(self):
- self.client = build_provider_pab.BuildProviderPAB()
- self.client.XSRF_STORE = None
-
- def tearDown(self):
- del self.client
-
- @mock.patch("build_provider_pab.flow_from_clientsecrets")
- @mock.patch("build_provider_pab.run_flow")
- @mock.patch("build_provider_pab.Storage.get")
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- def testAuthenticationNew(self, mock_creds, mock_storage_get, mock_rf,
- mock_ffc):
- mock_creds.invalid = True
- build_provider_pab.flow_from_clientsecrets = mock_ffc
- build_provider_pab.run_flow = mock_rf
- self.client.Authenticate()
- mock_ffc.assert_called_once()
- mock_storage_get.assert_called_once()
- mock_rf.assert_called_once()
-
- @mock.patch("build_provider_pab.flow_from_clientsecrets")
- @mock.patch("build_provider_pab.run_flow")
- @mock.patch("build_provider_pab.Storage.get")
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- def testAuthenticationStale(self, mock_creds, mock_storage_get, mock_rf,
- mock_ffc):
- mock_creds.invalid = False
- mock_creds.access_token_expired = True
- build_provider_pab.flow_from_clientsecrets = mock_ffc
- build_provider_pab.run_flow = mock_rf
- mock_storage_get.return_value = mock_creds
- self.client.Authenticate()
- mock_ffc.assert_called_once()
- mock_storage_get.assert_called_once()
- mock_rf.assert_not_called()
- mock_creds.refresh.assert_called_once()
-
- @mock.patch("build_provider_pab.flow_from_clientsecrets")
- @mock.patch("build_provider_pab.run_flow")
- @mock.patch("build_provider_pab.Storage.get")
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- def testAuthenticationFresh(self, mock_creds, mock_storage_get, mock_rf,
- mock_ffc):
- mock_creds.invalid = False
- mock_creds.access_token_expired = False
- build_provider_pab.flow_from_clientsecrets = mock_ffc
- build_provider_pab.run_flow = mock_rf
- mock_storage_get.return_value = mock_creds
- self.client.Authenticate()
- mock_ffc.assert_called_once()
- mock_storage_get.assert_called_once()
- mock_rf.assert_not_called()
- mock_creds.refresh.assert_not_called()
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.get')
- @mock.patch('__builtin__.open')
- def testDownloadArtifact(self, mock_open, mock_get, mock_creds):
- self.client._credentials = mock_creds
- artifact_url = (
- "https://partnerdash.google.com/build/gmsdownload/"
- "f_companion/label/clockwork.companion_20170906_211311_RC00/"
- "ClockworkCompanionGoogleWithGmsRelease_signed.apk?a=100621237")
- self.client.DownloadArtifact(
- artifact_url, 'ClockworkCompanionGoogleWithGmsRelease_signed.apk')
- self.client._credentials.apply.assert_called_with({})
- mock_get.assert_called_with(
- artifact_url, headers={}, stream=True)
- mock_open.assert_called_with(
- 'ClockworkCompanionGoogleWithGmsRelease_signed.apk', 'wb')
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURL(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{ "result" : {"1": "this_url"}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- url = self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- mock_post.assert_called_with(
- 'https://partner.android.com/build/u/0/_gwt/_rpc/buildsvc',
- data=mock.ANY,
- headers={
- 'Content-Type': 'application/json',
- 'x-alkali-account': 100621237,
- })
- self.assertEqual(url, "this_url")
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURLBackendError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'not JSON'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- expected = "Backend error -- check your account ID"
- self.assertEqual(str(cm.exception), expected)
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURLMissingResultError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"result": {}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- expected = "Resource not found"
- self.assertIn(expected, str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURLInvalidXSRFError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"error": {"code": -32000, "message":"Invalid"}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- self.assertIn('Bad XSRF token', str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURLExpiredXSRFError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"error": {"code": -32001, "message":"Expired"}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- self.assertIn('Expired XSRF token', str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetArtifactURLUnknownError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"some_other_json": "foo"}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetArtifactURL(
- 100621237,
- "4331445",
- "darwin_mac",
- "android-ndk-43345-darwin-x86_64.tar.bz2",
- "aosp-master-ndk",
- 0,
- method='POST')
- self.assertIn('Unknown response from server', str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetBuildListSuccess(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"result": {"1": "foo"}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- result = self.client.GetBuildList(
- 100621237,
- "git_oc-treble-dev",
- "aosp_arm64_ab-userdebug",
- method='POST')
- self.assertEqual(result, "foo")
- mock_post.assert_called_with(
- 'https://partner.android.com/build/u/0/_gwt/_rpc/buildsvc',
- data=mock.ANY,
- headers={
- 'Content-Type': 'application/json',
- 'x-alkali-account': 100621237,
- })
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('requests.post')
- def testGetBuildListError(self, mock_post, mock_creds):
- self.client._xsrf = 'disable'
- response = Response()
- response.status_code = 200
- response._content = b'{"result": {"3": "foo"}}'
- mock_post.return_value = response
- self.client._credentials = mock_creds
- with self.assertRaises(ValueError) as cm:
- self.client.GetBuildList(
- 100621237,
- "git_oc-treble-dev",
- "aosp_arm64_ab-userdebug",
- method='POST')
- self.assertIn('Build list not found', str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('build_provider_pab.BuildProviderPAB.GetBuildList')
- def testGetLatestBuildIdSuccess(self, mock_gbl, mock_creds):
- self.client._xsrf = 'disable'
- mock_gbl.return_value = [{'7': 5, '1': 'bad'}, {'7': 7, '1': 'good'}]
- self.client.GetBuildList = mock_gbl
- result = self.client.GetLatestBuildId(
- 100621237,
- "git_oc-treble-dev",
- "aosp_arm64_ab-userdebug",
- method='POST')
- self.assertEqual(result, 'good')
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('build_provider_pab.BuildProviderPAB.GetBuildList')
- def testGetLatestBuildIdEmpty(self, mock_gbl, mock_creds):
- self.client._xsrf = 'disable'
- mock_gbl.return_value = []
- self.client.GetBuildList = mock_gbl
- with self.assertRaises(ValueError) as cm:
- result = self.client.GetLatestBuildId(
- 100621237,
- "git_oc-treble-dev",
- "aosp_arm64_ab-userdebug",
- method='POST')
- self.assertIn("No builds found for", str(cm.exception))
-
- @mock.patch('build_provider_pab.BuildProviderPAB._credentials')
- @mock.patch('build_provider_pab.BuildProviderPAB.GetBuildList')
- def testGetLatestBuildIdAllBad(self, mock_gbl, mock_creds):
- self.client._xsrf = 'disable'
- mock_gbl.return_value = [{'7': 0}, {'7': 0}]
- self.client.GetBuildList = mock_gbl
- with self.assertRaises(ValueError) as cm:
- result = self.client.GetLatestBuildId(
- 100621237,
- "git_oc-treble-dev",
- "aosp_arm64_ab-userdebug",
- method='POST')
- self.assertEqual(
- "No complete builds found: 2 failed or incomplete builds found",
- str(cm.exception))
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/build/build_provider_test.py b/harnesses/host_controller/build/build_provider_test.py
deleted file mode 100644
index 71b96d1..0000000
--- a/harnesses/host_controller/build/build_provider_test.py
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import shutil
-import tempfile
-import unittest
-import zipfile
-
-from host_controller import common
-from host_controller.build import build_provider
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-
-class BuildProviderTest(unittest.TestCase):
- """Tests for build_provider.
-
- Attributes:
- _build_provider: The BuildProvider object under test.
- _temp_dir: The path to the temporary directory for test files.
- """
-
- def setUp(self):
- """Creates temporary directory."""
- self._build_provider = build_provider.BuildProvider()
- self._temp_dir = tempfile.mkdtemp()
-
- def tearDown(self):
- """Deletes temporary directory."""
- shutil.rmtree(self._temp_dir)
-
- def _CreateFile(self, name):
- """Creates an empty file as test data.
-
- Args:
- name: string, the name of the file.
-
- Returns:
- string, the path to the file.
- """
- path = os.path.join(self._temp_dir, name)
- with open(path, "w"):
- pass
- return path
-
- def _CreateZip(self, name, *paths):
- """Creates a zip file containing empty files.
-
- Args:
- name: string, the name of the zip file.
- *paths: strings, the file paths to create in the zip file.
-
- Returns:
- string, the path to the zip file.
- """
- empty_path = self._CreateFile("empty")
- zip_path = os.path.join(self._temp_dir, name)
- with zipfile.ZipFile(zip_path, "w") as zip_file:
- for path in paths:
- zip_file.write(empty_path, path)
- return zip_path
-
- def _CreateVtsPackage(self):
- """Creates an android-vts.zip containing vts-tradefed.
-
- Returns:
- string, the path to the zip file.
- """
- return self._CreateZip(
- "android-vts.zip",
- os.path.join("android-vts", "tools", "vts-tradefed"))
-
- def _CreateDeviceImageZip(self):
- """Creates a zip containing common device images.
-
- Returns:
- string, the path to the zip file.
- """
- return self._CreateZip(
- "img.zip", "system.img", "vendor.img", "boot.img")
-
- def _CreateProdConfig(self):
- """Creates a zip containing config files.
-
- Returns:
- string, the path to the zip file.
- """
- return self._CreateZip(
- "vti-global-config-prod.zip", os.path.join("test", "prod.config"))
-
- def testSetTestSuitePackage(self):
- """Tests setting a VTS package."""
- vts_path = self._CreateVtsPackage()
- self._build_provider.SetTestSuitePackage("vts", vts_path)
-
- self.assertTrue(
- os.path.exists(self._build_provider.GetTestSuitePackage("vts")))
-
- def testSetDeviceImageZip(self):
- """Tests setting a device image zip."""
- img_path = self._CreateDeviceImageZip()
- self._build_provider.SetDeviceImageZip(img_path)
-
- self.assertEqual(
- img_path,
- self._build_provider.GetDeviceImage(common.FULL_ZIPFILE))
-
- def testSetConfigPackage(self):
- """Tests setting a config package."""
- config_path = self._CreateProdConfig()
- self._build_provider.SetConfigPackage("prod", config_path)
-
- self.assertTrue(
- os.path.exists(self._build_provider.GetConfigPackage("prod")))
-
- def testSetFetchedDirectory(self):
- """Tests setting all files in a directory."""
- self._CreateVtsPackage()
- self._CreateProdConfig()
- img_zip = self._CreateDeviceImageZip()
- img_file = self._CreateFile("userdata.img")
- txt_file = self._CreateFile("additional.txt")
- self._build_provider.SetFetchedDirectory(self._temp_dir)
-
- self.assertDictContainsSubset(
- {common.FULL_ZIPFILE: img_zip, "userdata.img": img_file},
- self._build_provider.GetDeviceImage())
- self.assertTrue(
- os.path.exists(self._build_provider.GetTestSuitePackage("vts")))
- self.assertTrue(
- os.path.exists(self._build_provider.GetConfigPackage("prod")))
- self.assertDictContainsSubset(
- {"additional.txt": txt_file},
- self._build_provider.GetAdditionalFile())
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/campaigns/__init__.py b/harnesses/host_controller/campaigns/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/campaigns/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/campaigns/campaign_common.py b/harnesses/host_controller/campaigns/campaign_common.py
deleted file mode 100644
index 82bb726..0000000
--- a/harnesses/host_controller/campaigns/campaign_common.py
+++ /dev/null
@@ -1,755 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller import common
-from vti.test_serving.proto import TestScheduleConfigMessage_pb2 as pb
-
-# The list of the kwargs key. can retrieve informations on the leased job.
-_JOB_ATTR_LIST = [
- "build_id",
- "test_name",
- "shards",
- "serial",
- "build_target",
- "manifest_branch",
- "gsi_branch",
- "gsi_build_target",
- "test_branch",
- "test_build_target",
-]
-
-
-def HasAttr(attr, **kwargs):
- """Returns True if 'attr' is in 'kwargs' as an arg."""
- return True if attr in kwargs and kwargs[attr] else False
-
-
-def GetVersion(branch):
- """Returns the API level (integer) for the given branch."""
- branch = str(branch.lower())
- if branch.startswith("git_"):
- branch = branch[4:]
- if branch.startswith("aosp-"):
- branch = branch[5:]
-
- if "-treble-" in branch:
- branch = branch.replace("-treble-", "-")
-
- if branch.endswith("-dev"):
- branch = branch[:-4]
- elif branch.endswith("-release"):
- branch = branch[:-8]
-
- if (branch.startswith("o") and branch.endswith(
- ("mr1", "m2", "m3", "m4", "m5", "m6"))):
- return 8.1
- elif branch.startswith("o"):
- return 8.0
- elif branch.startswith("p"):
- return 9.0
- elif branch.startswith("gs://"):
- if "v8.0" in branch:
- return 8.0
- elif "v8.1" in branch:
- return 8.1
- elif "v9.0" in branch:
- return 9.0
- return 9.0
-
-
-def EmitFetchCommands(**kwargs):
- """Returns a list of common fetch commands.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
-
- Args:
- kwargs: keyword argument, contains data about the leased job.
- Returns:
- list of command string.
- bool, True if GSI image is fetched. False otherwise
- """
- result = []
- if isinstance(kwargs["build_target"], list):
- build_target = kwargs["build_target"][0]
- else:
- build_target = kwargs["build_target"]
- shards = int(kwargs["shards"])
- suite_name, _ = kwargs["test_name"].split("/")
- serials = kwargs["serial"]
-
- if HasAttr("pab_account_id", **kwargs):
- pab_account_id = kwargs["pab_account_id"]
- else:
- pab_account_id = common._DEFAULT_ACCOUNT_ID_INTERNAL
-
- manifest_branch = kwargs["manifest_branch"]
- build_id = kwargs["build_id"]
- build_storage_type = pb.BUILD_STORAGE_TYPE_PAB
- if HasAttr("build_storage_type", **kwargs):
- build_storage_type = int(kwargs["build_storage_type"])
-
- if build_storage_type == pb.BUILD_STORAGE_TYPE_PAB:
- result.append(
- "fetch --type=pab --branch=%s --target=%s --artifact_name=%s-img-%s.zip "
- "--build_id=%s --account_id=%s" %
- (manifest_branch, build_target, build_target.split("-")[0],
- build_id if build_id != "latest" else "{build_id}", build_id,
- pab_account_id))
- if HasAttr("require_signed_device_build", **kwargs):
- result[-1] += " --fetch_signed_build=True"
- if common.UNIVERSAL9810 in build_target:
- result[-1] += " --full_device_images=True"
-
- if HasAttr("has_bootloader_img", **kwargs):
- result.append("fetch --type=pab --branch=%s --target=%s "
- "--artifact_name=bootloader.img --build_id=%s "
- "--account_id=%s" % (manifest_branch, build_target,
- build_id, pab_account_id))
-
- if HasAttr("has_radio_img", **kwargs):
- result.append("fetch --type=pab --branch=%s --target=%s "
- "--artifact_name=radio.img --build_id=%s "
- "--account_id=%s" % (manifest_branch, build_target,
- build_id, pab_account_id))
-
- elif build_storage_type == pb.BUILD_STORAGE_TYPE_GCS:
- result.append("fetch --type=gcs --path=%s" % (manifest_branch))
- if common.UNIVERSAL9810 in build_target:
- result[-1] += " --full_device_images=True"
- else:
- logging.error("unknown build storage type is given: %d",
- build_storage_type)
- return None
-
- if HasAttr("gsi_branch", **kwargs):
- gsi = True
- else:
- gsi = False
-
- if HasAttr("gsi_vendor_version", **kwargs):
- gsi_vendor_version = kwargs["gsi_vendor_version"]
- else:
- gsi_vendor_version = None
-
- if gsi:
- if common.SDM845 in build_target:
- if shards > 1:
- sub_commands = []
- if shards <= len(serials):
- for shard_index in range(shards):
- sub_commands.append(
- GenerateSdm845SetupCommands(serials[shard_index]))
- result.append(sub_commands)
- else:
- result.extend(GenerateSdm845SetupCommands(serials[0]))
-
- if HasAttr("gsi_build_id", **kwargs):
- gsi_build_id = kwargs["gsi_build_id"]
- else:
- gsi_build_id = "latest"
- gsi_storage_type = pb.BUILD_STORAGE_TYPE_PAB
- if HasAttr("gsi_storage_type", **kwargs):
- gsi_storage_type = int(kwargs["gsi_storage_type"])
-
- if gsi_storage_type == pb.BUILD_STORAGE_TYPE_PAB:
- result.append(
- "fetch --type=pab --branch=%s --target=%s --gsi=True "
- "--artifact_name=%s-img-{build_id}.zip --build_id=%s" %
- (kwargs["gsi_branch"], kwargs["gsi_build_target"],
- kwargs["gsi_build_target"].split("-")[0], gsi_build_id))
- elif gsi_storage_type == pb.BUILD_STORAGE_TYPE_GCS:
- result.append("fetch --type=gcs --path=%s/%s-img-%s.zip "
- "--gsi=True" %
- (kwargs["gsi_branch"],
- kwargs["gsi_build_target"].split("-")[0],
- gsi_build_id))
- else:
- logging.error("unknown gsi storage type is given: %d",
- gsi_storage_type)
- return None
-
- if HasAttr("gsi_pab_account_id", **kwargs):
- result[-1] += " --account_id=%s" % kwargs["gsi_pab_account_id"]
-
- if HasAttr("test_build_id", **kwargs):
- test_build_id = kwargs["test_build_id"]
- else:
- test_build_id = "latest"
- test_storage_type = pb.BUILD_STORAGE_TYPE_PAB
- if HasAttr("test_storage_type", **kwargs):
- test_storage_type = int(kwargs["test_storage_type"])
-
- if test_storage_type == pb.BUILD_STORAGE_TYPE_PAB:
- result.append("fetch --type=pab --branch=%s --target=%s "
- "--artifact_name=android-%s.zip --build_id=%s" %
- (kwargs["test_branch"], kwargs["test_build_target"],
- suite_name, test_build_id))
- elif test_storage_type == pb.BUILD_STORAGE_TYPE_GCS:
- result.append("fetch --type=gcs --path=%s/%s.zip --set_suite_as=%s" %
- (kwargs["test_branch"], kwargs["test_build_target"],
- suite_name))
- else:
- logging.error("unknown test storage type is given: %d",
- test_storage_type)
- return None
-
- if HasAttr("test_pab_account_id", **kwargs):
- result[-1] += " --account_id=%s" % kwargs["test_pab_account_id"]
-
- result.append("info")
- if gsi:
- gsispl_command = "gsispl --version_from_path=boot.img"
- if gsi_vendor_version:
- gsispl_command += " --vendor_version=%s" % gsi_vendor_version
- result.append(gsispl_command)
- result.append("info")
-
- return result, gsi
-
-
-def EmitFlashCommands(gsi, **kwargs):
- """Returns a list of common flash commands.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
-
- Args:
- gsi: bool, whether to flash GSI over vendor images or not.
- kwargs: keyword argument, contains data about the leased job.
- Returns:
- list of command string.
- """
- result = []
-
- if isinstance(kwargs["build_target"], list):
- build_target = kwargs["build_target"][0]
- else:
- build_target = kwargs["build_target"]
- shards = int(kwargs["shards"])
- serials = kwargs["serial"]
- if gsi:
- system_version = GetVersion(kwargs["gsi_branch"])
- else:
- system_version = GetVersion(kwargs["manifest_branch"])
-
- repack_command = "repack"
- if HasAttr("image_package_repo_base", **kwargs):
- repack_command += " --dest=%s" % kwargs["image_package_repo_base"]
- if common.SDM845 in build_target and gsi:
- repack_command += (" --additional_files")
- for lib_file in common.SDM845_LIB_LIST:
- repack_command += (" {tmp_dir}/%s/%s" % (serials[0], lib_file))
- # TODO: verify this before re-enabling.
- # result.append(repack_command)
-
- if shards > 1:
- sub_commands = []
-
- if shards <= len(serials):
- for shard_index in range(shards):
- new_cmd_list = []
- if (common.K39TV1_BSP in build_target
- or common.K39TV1_BSP_1G in build_target):
- new_cmd_list.extend(
- GenerateMt6739GsiFlashingCommands(
- serials[shard_index], gsi))
- elif common.SDM845 in build_target and gsi:
- new_cmd_list.extend(
- GenerateSdm845GsiFlashingCommands(
- serials[shard_index]))
- elif common.UNIVERSAL9810 in build_target:
- new_cmd_list.extend(
- GenerateUniversal9810GsiFlashingCommands(
- serials[shard_index], gsi))
- else:
- new_cmd_list.append(
- "flash --current --serial %s --skip-vbmeta=True " %
- serials[shard_index])
- new_cmd_list.append("adb -s %s root" % serials[shard_index])
- if common.SDM845 not in build_target: # b/78487061
- new_cmd_list.append(
- "dut --operation=wifi_on --serial=%s --ap=%s" %
- (serials[shard_index], common._DEFAULT_WIFI_AP))
- new_cmd_list.append(
- "dut --operation=volume_mute --serial=%s --version=%s"
- % (serials[shard_index], system_version))
- sub_commands.append(new_cmd_list)
- result.append(sub_commands)
- else:
- if (common.K39TV1_BSP in build_target
- or common.K39TV1_BSP_1G in build_target):
- result.extend(GenerateMt6739GsiFlashingCommands(serials[0], gsi))
- elif common.SDM845 in build_target and gsi:
- result.extend(GenerateSdm845GsiFlashingCommands(serials[0]))
- elif common.UNIVERSAL9810 in build_target:
- result.extend(
- GenerateUniversal9810GsiFlashingCommands(serials[0], gsi))
- else:
- result.append(
- "flash --current --serial %s --skip-vbmeta=True" % serials[0])
- if common.SDM845 not in build_target: # b/78487061
- result.append("dut --operation=wifi_on --serial=%s --ap=%s" %
- (serials[0], common._DEFAULT_WIFI_AP))
- result.append(
- "dut --operation=volume_mute --serial=%s --version=%s" %
- (serials[0], system_version))
- if serials:
- serial_arg_list = []
- for serial in serials:
- result.append("adb -s %s root" % serial)
- serial_arg_list.append("--serial %s" % serial)
-
- return result
-
-
-def EmitCommonConsoleCommands(**kwargs):
- """Runs a common VTS-on-GSI or CTS-on-GSI test.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
- """
- result = []
-
- if not set(_JOB_ATTR_LIST).issubset(kwargs):
- missing_keys = [key for key in _JOB_ATTR_LIST if key not in kwargs]
- logging.error("Leased job missing attribute(s): {}".format(
- ", ".join(missing_keys)))
- return None
-
- if isinstance(kwargs["build_target"], list):
- build_target = kwargs["build_target"][0]
- else:
- build_target = kwargs["build_target"]
- shards = int(kwargs["shards"])
- suite_name, plan_name = kwargs["test_name"].split("/")
- serials = kwargs["serial"]
-
- result.append("device --set_serial=%s --from_job_pool --interval=%s" %
- (",".join(serials), common.DEFAULT_DEVICE_TIMEOUT_SECS))
- fetch_commands_result, gsi = EmitFetchCommands(**kwargs)
- result.extend(fetch_commands_result)
- flash_commands_result = EmitFlashCommands(gsi, **kwargs)
- result.extend(flash_commands_result)
-
- param = ""
- if HasAttr("param", **kwargs):
- param = " ".join(kwargs["param"])
-
- test_branch = kwargs["test_branch"]
- if (GetVersion(test_branch) >= 9.0 and
- (suite_name in ["cts", "gts", "sts"] or plan_name.startswith("cts"))):
- shard_option = "--shard-count"
- else:
- shard_option = "--shards"
-
- if shards > 1:
- test_command = "test --suite %s --keep-result -- %s %s %d %s" % (
- suite_name, plan_name, shard_option, shards, param)
- if shards <= len(serials):
- for shard_index in range(shards):
- test_command += " --serial %s" % serials[shard_index]
- result.append(test_command)
- else:
- if serials:
- serial_arg_list = []
- for serial in serials:
- serial_arg_list.append("--serial %s" % serial)
- result.append("test --suite %s --keep-result -- %s %s %s" %
- (suite_name, plan_name, " ".join(serial_arg_list),
- param))
- else:
- result.append("test --suite %s --keep-result -- %s %s" %
- (suite_name, plan_name, param))
-
- if "retry_count" in kwargs:
- retry_count = int(kwargs["retry_count"])
- result.append(
- GenerateRetryCommand(build_target, test_branch, suite_name,
- plan_name, serials, retry_count))
-
- if HasAttr("test_build_id", **kwargs):
- test_build_id = kwargs["test_build_id"]
- else:
- test_build_id = "latest"
- test_storage_type = pb.BUILD_STORAGE_TYPE_PAB
- if HasAttr("test_storage_type", **kwargs):
- test_storage_type = int(kwargs["test_storage_type"])
-
- if HasAttr("report_bucket", **kwargs):
- report_buckets = kwargs["report_bucket"]
- else:
- report_buckets = ["gs://vts-report"]
-
- upload_dests = []
- upload_commands = []
- for report_bucket in report_buckets:
- if test_storage_type == pb.BUILD_STORAGE_TYPE_PAB:
- upload_dest = ("%s/{suite_plan}/%s/{branch}/{target}/"
- "%s_{build_id}_{timestamp}/" %
- (report_bucket, plan_name, build_target))
- elif test_storage_type == pb.BUILD_STORAGE_TYPE_GCS:
- upload_dest = ("%s/{suite_plan}/%s/%s/%s/%s_%s_{timestamp}/" %
- (report_bucket, plan_name,
- kwargs["test_branch"].replace("gs://", "gs_")
- if kwargs["test_branch"].startswith("gs://") else
- kwargs["test_branch"], kwargs["test_build_target"],
- build_target, test_build_id))
- upload_dests.append(upload_dest)
- upload_commands.append(
- "upload --src={result_full} --dest=%s "
- "--report_path=%s/suite_result/{timestamp_year}/{timestamp_month}/"
- "{timestamp_day}" % (upload_dest, report_bucket))
-
- if HasAttr("report_persistent_url", **kwargs):
- for upload_dest in kwargs["report_persistent_url"]:
- upload_dests.append(upload_dest)
- upload_commands.append("upload --src={result_full} --dest=%s "
- "--clear_dest" % upload_dest)
-
- result.extend(upload_commands)
-
- if HasAttr("report_reference_url", **kwargs):
- ref_urls = kwargs["report_reference_url"]
- else:
- ref_urls = []
-
- extra_rows = " ".join("logs," + x for x in upload_dests)
- if HasAttr("report_spreadsheet_id", **kwargs):
- for index, sheet_id in enumerate(kwargs["report_spreadsheet_id"]):
- sheet_command = ("sheet --src {result_zip} --dest %s "
- "--extra_rows %s" % (sheet_id, extra_rows))
- if plan_name == "cts-on-gsi":
- sheet_command += " --primary_abi_only"
- if index < len(ref_urls):
- sheet_command += " --ref " + ref_urls[index]
- sheet_command += " --client_secrets DATA/vtslab-gcs.json"
- result.append(sheet_command)
-
- result.append("device --update=stop")
-
- return result
-
-
-def GenerateRetryCommand(build_target,
- test_branch,
- suite_name,
- plan_name,
- serials,
- retry_count=None):
- """Returns a retry command.
-
- Args:
- build_target: string, build target of the device images
- test_branch: string, branch name from which the test suite is fetched
- suite_name: string, the name of the test suite
- plan_name: string, the name of the test plan that needs to be run
- serials: list of strings, serial numbers of the DUTs
- retry_count: int,
-
- Returns:
- a string, retry command of the console.
- """
- if GetVersion(test_branch) >= 9.0:
- retry_option = ""
- shard_option = "--shard-count"
- if suite_name in ["cts", "gts", "sts"]:
- retry_option = "--retry_plan=retry"
- elif plan_name.startswith("cts"):
- retry_option = "--retry_plan=%s-retry" % plan_name
- else:
- shard_option = "--shards"
- else:
- shard_option = "--shards"
- retry_option = ""
-
- if retry_count is None:
- retry_count = common.DEFAULT_RETRY_COUNT
- retry_command = ("retry --suite %s --count %d %s" %
- (suite_name, retry_count, retry_option))
- if serials:
- if len(serials) > 1:
- retry_command += " %s %d" % (shard_option, len(serials))
- for shard_index in range(len(serials)):
- retry_command += " --serial %s" % serials[shard_index]
- else:
- retry_command += " --serial %s" % serials[0]
- if suite_name in ["cts", "gts", "sts"] or plan_name.startswith("cts"):
- if common.SDM845 in build_target:
- # TODO(vtslab-dev): remove after b/77664643 is resolved
- pass
- else:
- retry_command += " --cleanup_devices=True"
-
- return retry_command
-
-
-def GenerateSdm845SetupCommands(serial):
- """Returns a sequence of console commands to setup a device.
-
- Args:
- serial: string, the target device serial number.
-
- Returns:
- a list of strings, each string is a console command.
- """
- result = []
-
- result.append(
- "fastboot -s %s flash bootloader {device-image[bootloader.img]}" %
- serial)
- result.append("fastboot -s %s -- reboot bootloader" % serial)
- result.append(
- "fastboot -s %s flash radio {device-image[radio.img]}" % serial)
- result.append("fastboot -s %s -- reboot bootloader" % serial)
- result.append(
- "fastboot -s %s flash boot {device-image[full-zipfile-dir]}/boot.img" %
- serial)
- result.append(
- "fastboot -s %s flash dtbo {device-image[full-zipfile-dir]}/dtbo.img" %
- serial)
- result.append(
- "fastboot --timeout=900 -s %s flash system {device-image[full-zipfile-dir]}/system.img"
- % serial)
- result.append(
- "fastboot -s %s flash userdata {device-image[full-zipfile-dir]}/userdata.img"
- % serial)
- result.append(
- "fastboot -s %s flash vbmeta {device-image[full-zipfile-dir]}/vbmeta.img"
- " -- --disable-verity" % serial)
- result.append(
- "fastboot -s %s flash vendor {device-image[full-zipfile-dir]}/vendor.img"
- % serial)
- result.append("fastboot -s %s reboot" % serial)
- result.append("sleep 90") # wait for boot_complete (success)
- result.append("adb -s %s root" % serial)
- # TODO: to make sure {tmp_dir} is unique per session and
- # is cleaned up at exit.
- result.append("shell -- mkdir -p {tmp_dir}/%s" % serial)
- result.extend([
- "adb -s %s pull /system/lib64/%s {tmp_dir}/%s" % (serial, lib_file,
- serial)
- for lib_file in common.SDM845_LIB_LIST
- ])
-
- # TODO: remove this paragraph after b/74552817 is fixed.
- result.append(
- "fetch --type=gcs --path=gs://vts-release/v9.0/sdm845/vbmeta.img "
- "--artifact_name=vbmeta.img")
- result.append("adb -s %s reboot bootloader" % serial)
- result.append("fastboot -s %s flash vbmeta {device-image[vbmeta.img]}"
- " -- --disable-verity" % serial)
-
- return result
-
-
-def GenerateSdm845GsiFlashingCommands(serial, repacked_imageset=False):
- """Returns a sequence of console commands to flash GSI to a device.
-
- Args:
- serial: string, the target device serial number.
- repacked_imageset: bool, True if this function is called directly from
- the console, adjusts the resulting commands for
- atomic flashing process.
-
- Returns:
- a list of strings, each string is a console command.
- """
- result = []
-
- if repacked_imageset:
- result.append(
- "fastboot -s %s flash bootloader {device-image[bootloader.img]}" %
- serial)
- result.append("fastboot -s %s -- reboot bootloader" % serial)
- result.append(
- "fastboot -s %s flash radio {device-image[radio.img]}" % serial)
- result.append("fastboot -s %s -- reboot bootloader" % serial)
- result.append(
- "fastboot -s %s flash boot {device-image[boot.img]}" % serial)
- result.append(
- "fastboot -s %s flash dtbo {device-image[dtbo.img]}" % serial)
- result.append(
- "fastboot -s %s flash userdata {device-image[userdata.img]}" %
- serial)
- result.append(
- "fastboot -s %s flash vbmeta {device-image[vbmeta.img]} -- --disable-verity"
- % serial)
- result.append(
- "fastboot -s %s flash vendor {device-image[vendor.img]}" % serial)
-
- result.append(
- "fastboot --timeout=900 -s %s flash system {device-image[system.img]}"
- % serial)
- # removed -w from below command
- result.append("fastboot -s %s -- reboot" % serial)
- result.append("sleep 90") # wait until adb shell (not boot complete)
- result.append("adb -s %s root" % serial)
- result.append("adb -s %s remount" % serial)
- result.append("adb -s %s shell setenforce 0" % serial)
- result.append("adb -s %s shell mkdir /bt_firmware" % serial)
- result.append("adb -s %s shell chown system:system /bt_firmware" % serial)
- result.append("adb -s %s shell chmod 650 /bt_firmware" % serial)
- result.append("adb -s %s shell setenforce 1" % serial)
- if repacked_imageset:
- result.extend([
- "adb -s %s push {tools[%s/%s]} /system/lib64" %
- (serial, common._ADDITIONAL_FILES_DIR, lib_file)
- for lib_file in common.SDM845_LIB_LIST
- ])
- else:
- result.extend([
- "adb -s %s push {tmp_dir}/%s/%s /system/lib64" % (serial, serial,
- lib_file)
- for lib_file in common.SDM845_LIB_LIST
- ])
- result.extend(
- [("adb -s %s push ../testcases/DATA/xml/media_profiles_vendor.xml "
- "/vendor/etc/media_profiles_vendor.xml") % serial])
-
- result.append("shell -- rm {tmp_dir}/%s -rf" % serial)
- result.append("adb -s %s reboot bootloader" % serial)
- result.append("sleep 5")
- # TODO(vtslab-dev): remove after b/112171990 is resolved
- result.append("fastboot -s %s erase userdata" % serial)
- # removed -w from below command
- result.append("fastboot -s %s -- reboot" % serial)
- if not repacked_imageset:
- result.append("sleep 300") # wait for boot_complete (success)
-
- return result
-
-
-def GenerateMt6739GsiFlashingCommands(serial,
- gsi=False,
- repacked_imageset=False):
- """Returns a sequence of console commands to flash device imgs and GSI.
-
- Args:
- serial: string, the target device serial number.
- gsi: bool, whether to flash GSI over vendor images or not.
- repacked_imageset: bool, True if this func is called directly from
- the console, adjusts the resulting commands for
- atomic flashing process.
-
- Returns:
- a list of strings, each string is a console command.
- """
- flash_img_cmd = ("fastboot -s %s flash %s "
- "{device-image[full-zipfile-dir]}/%s")
- flash_gsi_cmd = ("fastboot --timeout=900 -s %s flash system "
- "{device-image[system.img]}")
- result = [
- flash_img_cmd % (serial, partition, image)
- for partition, image in (
- ("preloader", "preloader_SBOOT_DIS.img"),
- ("loader_ext1", "loader_ext.img"),
- ("loader_ext2", "loader_ext.img"),
- ("tee1", "tee.img"),
- ("tee2", "tee.img"),
- ("lk", "lk.img"),
- ("lk2", "lk.img"),
- )
- ]
- result.append("fastboot -s %s -- reboot bootloader" % serial)
- # gpt is the partition table and must be flashed first.
- # The bootloader reloads partition table automatically after flashing gpt.
- result += [
- flash_img_cmd % (serial, partition, image)
- for partition, image in (
- ("gpt", "PGPT"),
- ("md1img", "md1img.img"),
- ("md1dsp", "md1dsp.img"),
- ("recovery", "recovery.img"),
- ("spmfw", "spmfw.img"),
- ("mcupmfw", "mcupmfw.img"),
- ("boot", "boot.img"),
- ("dtbo", "dtbo.img"),
- ("vendor", "vendor.img"),
- ("cache", "cache.img"),
- ("userdata", "userdata.img"),
- )
- ]
-
- if gsi:
- result.append(flash_gsi_cmd % serial)
- result.append("fastboot -s %s -- -w" % serial)
- else:
- flash_img_cmd += " --timeout=900"
- result.append(flash_img_cmd % (serial, "system", "system.img"))
-
- result.append("fastboot -s %s reboot" % serial)
- if not repacked_imageset:
- result.append("sleep 300") # wait for boot_complete (success)
-
- return result
-
-
-def GenerateUniversal9810GsiFlashingCommands(serial,
- gsi=False,
- repacked_imageset=False):
- """Returns a sequence of console commands to flash device imgs and GSI.
-
- Args:
- serial: string, the target device serial number.
- gsi: bool, whether to flash GSI over vendor images or not.
- repacked_imageset: bool, True if this func is called directly from
- the console, adjusts the resulting commands for
- atomic flashing process.
-
- Returns:
- a list of strings, each string is a console command.
- """
- result = [
- ("fastboot -s %s flash el3_mon "
- "{device-image[full-zipfile-dir]}/el3_mon.img" % serial),
- ("fastboot -s %s flash epbl "
- "{device-image[full-zipfile-dir]}/epbl.img" % serial),
- ("fastboot -s %s flash bootloader "
- "{device-image[full-zipfile-dir]}/u-boot.img" % serial),
- ("fastboot -s %s flash dtb "
- "{device-image[full-zipfile-dir]}/dtb.img" % serial),
- ("fastboot -s %s flash dtbo "
- "{device-image[full-zipfile-dir]}/dtbo.img" % serial),
- ("fastboot -s %s flash kernel "
- "{device-image[full-zipfile-dir]}/kernel.img" % serial),
- ("fastboot -s %s flash ramdisk "
- "{device-image[full-zipfile-dir]}/ramdisk.img" % serial),
- ("fastboot -s %s flash vendor "
- "{device-image[full-zipfile-dir]}/vendor.img -- -S 300M" % serial),
- ]
- if gsi:
- result.append(("fastboot --timeout=900 -s %s flash system "
- "{device-image[system.img]} -- -S 512M" % serial))
- else:
- result.append((
- "fastboot --timeout=900 -s %s flash system "
- "{device-image[full-zipfile-dir]}/system.img -- -S 512M" % serial))
- result.append("fastboot -s %s reboot -- -w" % serial)
- if not repacked_imageset:
- result.append("sleep 300") # wait for boot_complete (success)
-
- return result
-
-
-FLASH_COMMAND_EMITTER = {
- common.K39TV1_BSP: GenerateMt6739GsiFlashingCommands,
- common.K39TV1_BSP_1G: GenerateMt6739GsiFlashingCommands,
- common.SDM845: GenerateSdm845GsiFlashingCommands,
- common.UNIVERSAL9810: GenerateUniversal9810GsiFlashingCommands,
-}
diff --git a/harnesses/host_controller/campaigns/campaign_test.py b/harnesses/host_controller/campaigns/campaign_test.py
deleted file mode 100644
index ddb0c54..0000000
--- a/harnesses/host_controller/campaigns/campaign_test.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import unittest
-
-from host_controller.campaigns import cts
-from host_controller.campaigns import gts
-from host_controller.campaigns import sts
-from host_controller.campaigns import vts
-
-from host_controller.campaigns.testdata import default_testcase
-
-
-class CampaignTest(unittest.TestCase):
- """Unit tests for the campaign generators."""
-
- def setUp(self):
- self.maxDiff = None
-
- def testVtsBaseline(self):
- """Tests the default device's vts scenario."""
- test_name = "vts/vts"
- results = vts.EmitConsoleCommands(
- **default_testcase.GenerateInputData(test_name))
- self.assertEqual(
- default_testcase.GenerateOutputData(test_name), results)
-
- def testCtsOnGsiBaseline(self):
- """Tests the default device's vts scenario."""
- test_name = "vts/cts-on-gsi"
- results = vts.EmitConsoleCommands(
- **default_testcase.GenerateInputData(test_name))
- self.assertEqual(
- default_testcase.GenerateOutputData(test_name), results)
-
- def testCtsBaseline(self):
- """Tests the default device's vts scenario."""
- test_name = "cts/cts"
- results = cts.EmitConsoleCommands(
- **default_testcase.GenerateInputData(test_name))
- self.assertEqual(
- default_testcase.GenerateOutputData(test_name), results)
-
- def testGtsBaseline(self):
- """Tests the default device's vts scenario."""
- test_name = "gts/gts"
- results = gts.EmitConsoleCommands(
- **default_testcase.GenerateInputData(test_name))
- self.assertEqual(
- default_testcase.GenerateOutputData(test_name), results)
-
- def testStsBaseline(self):
- """Tests the default device's vts scenario."""
- test_name = "sts/sts"
- results = sts.EmitConsoleCommands(
- **default_testcase.GenerateInputData(test_name))
- self.assertEqual(
- default_testcase.GenerateOutputData(test_name), results)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/harnesses/host_controller/campaigns/cts.py b/harnesses/host_controller/campaigns/cts.py
deleted file mode 100644
index 28aa22d..0000000
--- a/harnesses/host_controller/campaigns/cts.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.campaigns import campaign_common
-
-
-def EmitConsoleCommands(**kwargs):
- """Runs a common CTS test.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
- """
- return campaign_common.EmitCommonConsoleCommands(**kwargs)
diff --git a/harnesses/host_controller/campaigns/gts.py b/harnesses/host_controller/campaigns/gts.py
deleted file mode 100644
index fec0eba..0000000
--- a/harnesses/host_controller/campaigns/gts.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.campaigns import campaign_common
-
-
-def EmitConsoleCommands(**kwargs):
- """Runs a common GTS test.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
- """
- return campaign_common.EmitCommonConsoleCommands(**kwargs)
diff --git a/harnesses/host_controller/campaigns/sts.py b/harnesses/host_controller/campaigns/sts.py
deleted file mode 100644
index 4850368..0000000
--- a/harnesses/host_controller/campaigns/sts.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.campaigns import campaign_common
-
-
-def EmitConsoleCommands(**kwargs):
- """Runs a common STS test.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
- """
- return campaign_common.EmitCommonConsoleCommands(**kwargs)
diff --git a/harnesses/host_controller/campaigns/testdata/__init__.py b/harnesses/host_controller/campaigns/testdata/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/campaigns/testdata/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/campaigns/testdata/default_testcase.py b/harnesses/host_controller/campaigns/testdata/default_testcase.py
deleted file mode 100644
index 9aeb91e..0000000
--- a/harnesses/host_controller/campaigns/testdata/default_testcase.py
+++ /dev/null
@@ -1,141 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import copy
-
-# Based on JobModel defined in
-# test/vti/test_serving/gae/webapp/src/proto/model.py
-input_data = {
- "test_type": 1,
- "hostname": "my_hostname",
- "priority": "low",
- "test_name": "vts/vts",
- "require_signed_device_build": True,
- "has_bootloader_img": True,
- "has_radio_img": True,
- "device": "my_device",
- "serial": ["my_serial1", "my_serial2", "my_serial3"],
-
- # device image information
- "build_storage_type": 1,
- "manifest_branch": "my_branch",
- "build_target": "my_build_target",
- "build_id": "my_build_id",
- "pab_account_id": "my_pab_account_id",
- "shards": 3,
- "param": "",
- "status": 1,
- "period": 24 * 60, # 1 day
-
- # GSI information
- "gsi_storage_type": 1,
- "gsi_branch": "my_gsi_branch",
- "gsi_build_target": "my_gsi_build_target",
- "gsi_build_id": "my_gsi_build_id",
- "gsi_pab_account_id": "my_gsi_pab_account_id",
- # gsi_vendor_version: "8.1.0"
-
- # test suite information
- "test_storage_type": 1,
- "test_branch": "my_test_branch",
- "test_build_target": "my_test_build_target",
- "test_build_id": "my_test_build_id",
- "test_pab_account_id": "my_test_pab_account_id",
-
- #timestamp = ndb.DateTimeProperty(auto_now=False)
- #heartbeat_stamp = ndb.DateTimeProperty(auto_now=False)
- "retry_count": 3,
- "infra_log_url": "infra_log_url",
-
- #parent_schedule = ndb.KeyProperty(kind="ScheduleModel")
- "image_package_repo_base": "image_package_repo_base",
- "report_bucket": ["report_bucket"],
- "report_spreadsheet_id": ["report_spreadsheet_id"],
-}
-
-expected_output = [
- 'device --set_serial=my_serial1,my_serial2,my_serial3 --from_job_pool --interval=300',
- 'fetch --type=pab --branch=my_branch --target=my_build_target --artifact_name=my_build_target-img-my_build_id.zip --build_id=my_build_id --account_id=my_pab_account_id --fetch_signed_build=True',
- 'fetch --type=pab --branch=my_branch --target=my_build_target --artifact_name=bootloader.img --build_id=my_build_id --account_id=my_pab_account_id',
- 'fetch --type=pab --branch=my_branch --target=my_build_target --artifact_name=radio.img --build_id=my_build_id --account_id=my_pab_account_id',
- 'fetch --type=pab --branch=my_gsi_branch --target=my_gsi_build_target --gsi=True --artifact_name=my_gsi_build_target-img-{build_id}.zip --build_id=my_gsi_build_id --account_id=my_gsi_pab_account_id',
- 'fetch --type=pab --branch=my_test_branch --target=my_test_build_target --artifact_name=android-{{test_suite}}.zip --build_id=my_test_build_id --account_id=my_test_pab_account_id',
- 'info', 'gsispl --version_from_path=boot.img', 'info',
- [[
- 'flash --current --serial my_serial1 --skip-vbmeta=True ',
- 'adb -s my_serial1 root',
- 'dut --operation=wifi_on --serial=my_serial1 --ap=GoogleGuest',
- 'dut --operation=volume_mute --serial=my_serial1 --version=9.0'
- ], [
- 'flash --current --serial my_serial2 --skip-vbmeta=True ',
- 'adb -s my_serial2 root',
- 'dut --operation=wifi_on --serial=my_serial2 --ap=GoogleGuest',
- 'dut --operation=volume_mute --serial=my_serial2 --version=9.0'
- ], [
- 'flash --current --serial my_serial3 --skip-vbmeta=True ',
- 'adb -s my_serial3 root',
- 'dut --operation=wifi_on --serial=my_serial3 --ap=GoogleGuest',
- 'dut --operation=volume_mute --serial=my_serial3 --version=9.0'
- ]],
- 'test --suite {{test_suite}} --keep-result -- {{test_plan}} --shards 3 --serial my_serial1 --serial my_serial2 --serial my_serial3',
- 'retry --suite {{test_suite}} --count 3 {{retry_plan}} --shards 3 --serial my_serial1 --serial my_serial2 --serial my_serial3{{cleanup_device}}',
- 'upload --src={result_full} --dest=report_bucket/{suite_plan}/{{test_plan}}/{branch}/{target}/my_build_target_{build_id}_{timestamp}/ --report_path=report_bucket/suite_result/{timestamp_year}/{timestamp_month}/{timestamp_day}',
- 'sheet --src {result_zip} --dest report_spreadsheet_id --extra_rows logs,report_bucket/{suite_plan}/{{test_plan}}/{branch}/{target}/my_build_target_{build_id}_{timestamp}/ --primary_abi_only --client_secrets DATA/vtslab-gcs.json',
- 'device --update=stop',
-]
-
-
-def GenerateInputData(test_name):
- """Returns an input data dict for a given `test_name`."""
- new_data = copy.copy(input_data)
- new_data["test_name"] = test_name
- return new_data
-
-
-def GenerateOutputData(test_name):
- """Returns an output data list for a given `test_name`."""
- test_suite, test_plan = test_name.split("/")
-
- def ReplaceChars(line):
- line = line.replace('{{test_suite}}', test_suite)
- line = line.replace('{{test_plan}}', test_plan)
- if test_plan != "cts-on-gsi":
- line = line.replace(' --primary_abi_only', '')
- if (test_suite == "cts" or test_suite == "gts" or test_suite == "sts"
- or test_plan.startswith("cts-")):
- line = line.replace('--shards', "--shard-count")
- if test_suite == "vts":
- line = line.replace('{{retry_plan}}',
- '--retry_plan=%s-retry' % test_plan)
- else:
- line = line.replace('{{retry_plan}}', '--retry_plan=retry')
- line = line.replace('{{cleanup_device}}',
- ' --cleanup_devices=True')
- else:
- line = line.replace('{{retry_plan}}', '')
- line = line.replace('{{cleanup_device}}', '')
- return line
-
- def RecursivelyApply(input_list, func):
- for number, item in enumerate(input_list):
- if type(item) is list:
- input_list[number] = RecursivelyApply(input_list[number], func)
- elif type(item) is str:
- input_list[number] = func(item)
- else:
- return None
- return input_list
-
- return RecursivelyApply(copy.copy(expected_output), ReplaceChars)
diff --git a/harnesses/host_controller/campaigns/vts.py b/harnesses/host_controller/campaigns/vts.py
deleted file mode 100644
index 0287455..0000000
--- a/harnesses/host_controller/campaigns/vts.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.campaigns import campaign_common
-
-
-def EmitConsoleCommands(**kwargs):
- """Runs a common VTS-on-GSI or CTS-on-GSI test.
-
- This uses a given device branch information and automatically
- selects a GSI branch and a test branch.
- """
- return campaign_common.EmitCommonConsoleCommands(**kwargs)
diff --git a/harnesses/host_controller/command_processor/__init__.py b/harnesses/host_controller/command_processor/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/command_processor/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/command_processor/base_command_processor.py b/harnesses/host_controller/command_processor/base_command_processor.py
deleted file mode 100644
index fa53f74..0000000
--- a/harnesses/host_controller/command_processor/base_command_processor.py
+++ /dev/null
@@ -1,135 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import argparse
-import logging
-import re
-
-from host_controller import console_argument_parser
-
-# tmp_dir variable name.
-TMP_DIR_VAR ="{tmp_dir}"
-
-
-class BaseCommandProcessor(object):
- '''Base class for command processors.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- arg_buffer: dict, stores last parsed argument, value pairs.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- '''
-
- command = 'base'
- command_detail = 'Command processor template'
-
- def _SetUp(self, console):
- '''Internal SetUp function that will call subclass' Setup function.
-
- Args:
- console: Console object.
- '''
- self.console = console
- self.arg_buffer = {}
- self.arg_parser = console_argument_parser.ConsoleArgumentParser(
- self.command, self.command_detail)
- self.SetUp()
-
- def SetUp(self):
- '''SetUp method for subclass to override.'''
- pass
-
- def _Run(self, arg_line):
- '''Internal function that will call subclass' Run function.
-
- Args:
- arg_line: string, line of command arguments
- '''
- ret = self.Run(arg_line)
- args = self.arg_parser.ParseLine(arg_line)
- for arg_tuple in args._get_kwargs():
- key = arg_tuple[0]
- value = arg_tuple[1]
- self.arg_buffer[key] = value
-
- if ret is not None:
- if ret == True: # exit command executed.
- return True
- elif ret == False:
- return False
- else:
- logging.warning("{} coommand returned {}".format(
- self.command, ret))
-
- def Run(self, arg_line):
- '''Run method to perform action when invoked from console.
-
- Args:
- arg_line: string, line of command
- '''
- pass
-
- def _Help(self):
- '''Internal function that will call subclass' Help function.'''
- self.Help()
-
- def Help(self):
- '''Help method to print help informations.'''
- if hasattr(self, 'arg_parser') and hasattr(self.console, '_out_file'):
- self.arg_parser.print_help(self.console._out_file)
-
- def _TearDown(self):
- '''Internal function that will call subclass' TearDown function.'''
- self.TearDown()
-
- def TearDown(self):
- '''TearDown tasks to be called when console is shutting down.'''
- pass
-
- def ReplaceVars(self, message_list):
- """Replaces vars in a 'messsag_list' to their values."""
- new_message_list = []
- for message in message_list:
- new_message = message
-
- vars = re.findall(r"{device-image\[[^]]+\]}", message)
- if vars:
- for var in vars:
- var_name = var[len("{device-image")+1:-2]
- if var_name and var_name in self.console.device_image_info:
- new_message = new_message.replace(
- var, self.console.device_image_info[var_name])
- else:
- new_message = new_message.replace(var, "{undefined}")
-
- vars = re.findall(r"{tools\[[^]]+\]}", message)
- if vars:
- for var in vars:
- var_name = var[len("{tools")+1:-2]
- if var_name and var_name in self.console.tools_info:
- new_message = new_message.replace(
- var, self.console.tools_info[var_name])
- else:
- new_message = new_message.replace(var, "{undefined}")
-
- if TMP_DIR_VAR in new_message:
- new_message = new_message.replace(
- TMP_DIR_VAR, self.console.tmpdir_default)
-
- new_message_list.append(new_message)
- return new_message_list
diff --git a/harnesses/host_controller/command_processor/command_acloud.py b/harnesses/host_controller/command_processor/command_acloud.py
deleted file mode 100644
index 42b377f..0000000
--- a/harnesses/host_controller/command_processor/command_acloud.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller.acloud import acloud_client
-from host_controller.command_processor import base_command_processor
-
-
-class CommandAcloud(base_command_processor.BaseCommandProcessor):
- '''Command processor for acloud command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- '''
-
- command = 'acloud'
- command_detail = 'Create acloud instances.'
-
- def Run(self, arg_line):
- '''Creates an acloud instance and connects to it via adb.
-
- Args:
- arg_line: string, line of command arguments
- '''
- args = self.arg_parser.ParseLine(arg_line)
-
- if args.provider == "ab":
- if args.build_id.lower() == "latest":
- build_id = self.console._build_provider["ab"].GetLatestBuildId(
- args.branch,
- args.target)
- else:
- # TODO(yuexima): support more provider types.
- logging.error("Provider %s not supported yet." % args.provider)
- return
-
- ac = acloud_client.ACloudClient()
- ac.PrepareConfig(args.config_path)
- ac.CreateInstance(args.build_id)
- ac.ConnectInstanceToAdb(ah.GetInstanceIP())
-
- def SetUp(self):
- """Initializes the parser for acloud command."""
- self.arg_parser.add_argument(
- "--build_id",
- help="Build ID to use.")
- self.arg_parser.add_argument(
- "--provider",
- default="ab",
- choices=("local_fs", "gcs", "pab", "ab"),
- help="Build provider type")
- self.arg_parser.add_argument(
- "--branch", # not required for local_fs
- help="Branch to grab the artifact from.")
- self.arg_parser.add_argument(
- "--target", # not required for local_fs
- help="Target product to grab the artifact from.")
- self.arg_parser.add_argument(
- "--config_path",
- required=True,
- help="Acloud config path.")
diff --git a/harnesses/host_controller/command_processor/command_adb.py b/harnesses/host_controller/command_processor/command_adb.py
deleted file mode 100644
index 1738820..0000000
--- a/harnesses/host_controller/command_processor/command_adb.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.usb import usb_utils
-
-from vts.utils.python.common import cmd_utils
-
-
-class CommandAdb(base_command_processor.BaseCommandProcessor):
- """Command processor for adb command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "adb"
- command_detail = "Runs an ADB command."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.arg_parser.add_argument(
- "--serial",
- "-s",
- default=None,
- help="The target device serial to run the command.")
- self.arg_parser.add_argument(
- "--timeout",
- type=float,
- default=common.DEFAULT_DEVICE_TIMEOUT_SECS,
- help="The maximum timeout value of this command in seconds. "
- "Set to 0 to disable the timeout functionality.")
- self.arg_parser.add_argument(
- "command",
- metavar="COMMAND",
- nargs="+",
- help="The command to be executed. If the command contains "
- "arguments starting with \"-\", place the command at end of line "
- "after \"--\".")
-
- # @Override
- def Run(self, arg_line):
- """Runs an adb command."""
- args = self.arg_parser.ParseLine(arg_line)
- cmd_list = ["adb"]
- if args.serial:
- if "," in args.serial:
- logging.error("Only one serial can be specified")
- return False
- cmd_list.append("-s %s" % args.serial)
- cmd_list.extend(self.ReplaceVars(args.command))
- if args.timeout == 0:
- stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- " ".join(cmd_list))
- else:
- stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- " ".join(cmd_list), args.timeout,
- usb_utils.ResetUsbDeviceOfSerial_Callback, args.serial)
- if stdout:
- logging.info(stdout)
- if stderr:
- logging.error(stderr)
- if self.console.job_pool and args.serial:
- self.console.device_status[
- args.serial] = common._DEVICE_STATUS_DICT["error"]
- if retcode != 0:
- return False
diff --git a/harnesses/host_controller/command_processor/command_build.py b/harnesses/host_controller/command_processor/command_build.py
deleted file mode 100644
index b1479d8..0000000
--- a/harnesses/host_controller/command_processor/command_build.py
+++ /dev/null
@@ -1,249 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import httplib2
-import logging
-import socket
-import threading
-import time
-
-from googleapiclient import errors
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.console_argument_parser import ConsoleArgumentError
-from host_controller.tradefed import remote_operation
-
-
-class CommandBuild(base_command_processor.BaseCommandProcessor):
- """Command processor for build command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- build_thread: dict containing threading.Thread instances(s) that
- update build info regularly.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "build"
- command_detail = "Specifies branches and targets to monitor."
-
- def UpdateBuild(self, account_id, branch, targets, artifact_type, method,
- userinfo_file, noauth_local_webserver, verify_signed):
- """Updates the build state.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- artifact_type: string, artifact type (`device`, 'gsi' or `test').
- method: string, method for getting build information.
- userinfo_file: string, the path of a file containing email and
- password (if method == POST).
- noauth_local_webserver: boolean, True to not use a local websever.
- verify_signed: A Boolean indicating whether to verify signed build.
- """
- builds = []
-
- self.console._build_provider["pab"].Authenticate(
- userinfo_file=userinfo_file,
- noauth_local_webserver=noauth_local_webserver)
- for target in targets.split(","):
- try:
- listed_builds = self.console._build_provider["pab"].GetBuildList(
- account_id=account_id,
- branch=branch,
- target=target,
- page_token="",
- max_results=100,
- method=method,
- verify_signed=verify_signed)
- except ValueError as e:
- logging.exception(e)
- continue
-
- for listed_build in listed_builds:
- if method == "GET":
- if "successful" in listed_build:
- if listed_build["successful"]:
- build = {}
- build["manifest_branch"] = branch
- build["build_id"] = listed_build["build_id"]
- if "-" in target:
- build["build_target"], build[
- "build_type"] = target.split("-")
- else:
- build["build_target"] = target
- build["build_type"] = ""
- build["artifact_type"] = artifact_type
- build["artifacts"] = []
- if "signed" in listed_build:
- build["signed"] = listed_build["signed"]
- else:
- build["signed"] = False
- builds.append(build)
- else:
- logging.error("Error: listed_build %s", listed_build)
- else: # POST
- build = {}
- build["manifest_branch"] = branch
- build["build_id"] = listed_build[u"1"]
- if "-" in target:
- (build["build_target"],
- build["build_type"]) = target.split("-")
- else:
- build["build_target"] = target
- build["build_type"] = ""
- build["artifact_type"] = artifact_type
- build["artifacts"] = []
- build["signed"] = False
- builds.append(build)
- self.console._vti_endpoint_client.UploadBuildInfo(builds)
-
- def UpdateBuildLoop(self, account_id, branch, target, artifact_type,
- method, userinfo_file, noauth_local_webserver,
- update_interval, verify_signed):
- """Regularly updates the build information.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- artifact_type: string, artifcat type (`device`, 'gsi' or `test).
- method: string, method for getting build information.
- userinfo_file: string, the path of a file containing email and
- password (if method == POST).
- noauth_local_webserver: boolean, True to not use a local websever.
- update_interval: int, number of seconds before repeating
- """
- thread = threading.currentThread()
- while getattr(thread, 'keep_running', True):
- try:
- self.UpdateBuild(account_id, branch, target, artifact_type,
- method, userinfo_file, noauth_local_webserver,
- verify_signed)
- except (socket.error, remote_operation.RemoteOperationException,
- httplib2.HttpLib2Error, errors.HttpError) as e:
- logging.exception(e)
- time.sleep(update_interval)
-
- # @Override
- def SetUp(self):
- """Initializes the parser for build command."""
- self.build_thread = {}
- self.arg_parser.add_argument(
- "--update",
- choices=("single", "start", "stop", "list"),
- default="start",
- help="Update build info")
- self.arg_parser.add_argument(
- "--id",
- default=None,
- help="session ID only required for 'stop' update command")
- self.arg_parser.add_argument(
- "--interval",
- type=int,
- default=30,
- help="Interval (seconds) to repeat build update.")
- self.arg_parser.add_argument(
- "--artifact-type",
- choices=("device", "gsi", "test"),
- default="device",
- help="The type of an artifact to update")
- self.arg_parser.add_argument(
- "--branch",
- required=True,
- help="Branch to grab the artifact from.")
- self.arg_parser.add_argument(
- "--target",
- required=True,
- help="a comma-separate list of build target product(s).")
- self.arg_parser.add_argument(
- "--account_id",
- default=common._DEFAULT_ACCOUNT_ID,
- help="Partner Android Build account_id to use.")
- self.arg_parser.add_argument(
- "--method",
- default="GET",
- choices=("GET", "POST"),
- help="Method for getting build information")
- self.arg_parser.add_argument(
- "--userinfo-file",
- help=
- "Location of file containing email and password, if using POST.")
- self.arg_parser.add_argument(
- "--noauth_local_webserver",
- default=False,
- type=bool,
- help="True to not use a local webserver for authentication.")
- self.arg_parser.add_argument(
- "--verify-signed-build",
- default=False,
- type=bool,
- help="True to verify whether the build is signed.")
- # @Override
- def Run(self, arg_line):
- """Updates build info."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.update == "single":
- self.UpdateBuild(args.account_id, args.branch, args.target,
- args.artifact_type, args.method,
- args.userinfo_file, args.noauth_local_webserver,
- args.verify_signed_build)
- elif args.update == "list":
- logging.info("Running build update sessions:")
- for id in self.build_thread:
- logging.info(" ID %d", id)
- elif args.update == "start":
- if args.interval <= 0:
- raise ConsoleArgumentError("update interval must be positive")
- # do not allow user to create new
- # thread if one is currently running
- if args.id is None:
- if not self.build_thread:
- args.id = 1
- else:
- args.id = max(self.build_thread) + 1
- else:
- args.id = int(args.id)
- if args.id in self.build_thread and not hasattr(
- self.build_thread[args.id], 'keep_running'):
- logging.warning(
- 'build update (session ID: %s) already running. '
- 'run build --update stop first.', args.id)
- return
- self.build_thread[args.id] = threading.Thread(
- target=self.UpdateBuildLoop,
- args=(
- args.account_id,
- args.branch,
- args.target,
- args.artifact_type,
- args.method,
- args.userinfo_file,
- args.noauth_local_webserver,
- args.interval,
- args.verify_signed_build,
- ))
- self.build_thread[args.id].daemon = True
- self.build_thread[args.id].start()
- elif args.update == "stop":
- if args.id is None:
- logging.error("--id must be set for stop")
- else:
- self.build_thread[int(args.id)].keep_running = False
diff --git a/harnesses/host_controller/command_processor/command_config.py b/harnesses/host_controller/command_processor/command_config.py
deleted file mode 100644
index a8b1a24..0000000
--- a/harnesses/host_controller/command_processor/command_config.py
+++ /dev/null
@@ -1,440 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import httplib2
-import itertools
-import logging
-import os
-import socket
-import threading
-import time
-
-from googleapiclient import errors
-from google.protobuf import text_format
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.console_argument_parser import ConsoleArgumentError
-from host_controller.tradefed import remote_operation
-
-from vti.test_serving.proto import TestLabConfigMessage_pb2 as LabCfgMsg
-from vti.test_serving.proto import TestScheduleConfigMessage_pb2 as SchedCfgMsg
-
-
-class CommandConfig(base_command_processor.BaseCommandProcessor):
- """Command processor for config command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- schedule_thread: dict containing threading.Thread instances(s) that
- update schedule info regularly.
- """
-
- command = "config"
- command_detail = "Specifies a global config type to monitor."
-
- def UpdateConfig(self, account_id, branch, targets, config_type, method,
- update_build, clear_schedule, clear_labinfo):
- """Updates the global configuration data.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- config_type: string, config type (`prod` or `test').
- method: string, HTTP method for fetching.
- update_build: boolean, indicating whether to upload build info.
- clear_schedule: bool, True to clear all schedule data exist on the
- scheduler
- clear_labinfo: bool, True to clear all lab data exist on the
- scheduler
- """
- for target in targets.split(","):
- fetch_path = self.FetchConfig(
- account_id=account_id,
- branch=branch,
- target=target,
- config_type=config_type,
- method=method)
- if fetch_path:
- self.UploadConfig(
- path=fetch_path,
- update_build=update_build,
- clear_schedule=clear_schedule,
- clear_labinfo=clear_labinfo)
-
- def FetchConfig(self, account_id, branch, target, config_type, method):
- """Fetches config files from the PAB build provider.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- target: string, build target.
- config_type: string, config type (`prod` or `test').
- method: string, HTTP method for fetching.
-
- Returns:
- string, a path to the temp directory where config files are stored.
- """
- path = ""
- self.console._build_provider["pab"].Authenticate()
- try:
- listed_builds = self.console._build_provider["pab"].GetBuildList(
- account_id=account_id,
- branch=branch,
- target=target,
- page_token="",
- max_results=1,
- method="GET")
- except ValueError as e:
- logging.exception(e)
- return path
-
- if listed_builds and len(listed_builds) > 0:
- listed_build = listed_builds[0]
- if listed_build["successful"]:
- device_images, test_suites, artifacts, configs = (
- self.console._build_provider["pab"].GetArtifact(
- account_id=account_id,
- branch=branch,
- target=target,
- artifact_name=(
- "vti-global-config-%s.zip" % config_type),
- build_id=listed_build["build_id"],
- method=method))
- path = os.path.dirname(configs[config_type])
-
- return path
-
- def UploadConfig(self, path, update_build, clear_schedule, clear_labinfo):
- """Uploads configs to VTI server.
-
- Args:
- path: string, a path where config files are stored.
- update_build: boolean, indicating whether to upload build info.
- clear_schedule: bool, True to clear all schedule data exist on the
- scheduler
- clear_labinfo: bool, True to clear all lab data exist on the
- scheduler
- """
- schedules_pbs = []
- lab_pbs = []
- for root, dirs, files in os.walk(path):
- for config_file in files:
- full_path = os.path.join(root, config_file)
- try:
- if config_file.endswith(".schedule_config"):
- with open(full_path, "r") as fd:
- context = fd.read()
- sched_cfg_msg = SchedCfgMsg.ScheduleConfigMessage()
- text_format.Merge(context, sched_cfg_msg)
- schedules_pbs.append(sched_cfg_msg)
- logging.info(sched_cfg_msg.manifest_branch)
- elif config_file.endswith(".lab_config"):
- with open(full_path, "r") as fd:
- context = fd.read()
- lab_cfg_msg = LabCfgMsg.LabConfigMessage()
- text_format.Merge(context, lab_cfg_msg)
- lab_pbs.append(lab_cfg_msg)
- except text_format.ParseError as e:
- logging.error("ERROR: Config parsing error %s", e)
- if update_build:
- commands = self.GetBuildCommands(schedules_pbs)
- if commands:
- for command in commands:
- ret = self.console.onecmd(command)
- if ret == False:
- break
- self.console._vti_endpoint_client.UploadScheduleInfo(
- schedules_pbs, clear_schedule)
- self.console._vti_endpoint_client.UploadLabInfo(lab_pbs, clear_labinfo)
-
- def UpdateConfigLoop(self, account_id, branch, target, config_type, method,
- update_build, update_interval, clear_schedule,
- clear_labinfo):
- """Regularly updates the global configuration.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- config_type: string, config type (`prod` or `test').
- method: string, HTTP method for fetching.
- update_build: boolean, indicating whether to upload build info.
- update_interval: int, number of seconds before repeating
- clear_schedule: bool, True to clear all schedule data exist on the
- scheduler
- clear_labinfo: bool, True to clear all lab data exist on the
- scheduler
- """
- thread = threading.currentThread()
- while getattr(thread, 'keep_running', True):
- try:
- self.UpdateConfig(account_id, branch, target, config_type,
- method, update_build, clear_schedule,
- clear_labinfo)
- except (socket.error, remote_operation.RemoteOperationException,
- httplib2.HttpLib2Error, errors.HttpError) as e:
- logging.exception(e)
- time.sleep(update_interval)
-
- def GetBuildCommands(self, schedule_pbs):
- """Generates a list of build commands with given schedules.
-
- Args:
- schedule_pbs: a list of TestScheduleConfig protobuf messages.
-
- Returns:
- a list of build command strings
- """
- attrs = {}
- attrs["device"] = [
- "build_storage_type", "manifest_branch", "pab_account_id",
- "require_signed_device_build", "name"
- ]
- attrs["gsi"] = [
- "gsi_storage_type", "gsi_branch", "gsi_pab_account_id",
- "gsi_build_target"
- ]
- attrs["test"] = [
- "test_storage_type", "test_branch", "test_pab_account_id",
- "test_build_target"
- ]
-
- class BuildInfo(object):
- """A build information class."""
-
- def __init__(self, _build_type):
- if _build_type in attrs:
- for attribute in attrs[_build_type]:
- setattr(self, attribute, "")
-
- def __eq__(self, compare):
- return self.__dict__ == compare.__dict__
-
- build_commands = []
- if not schedule_pbs:
- return build_commands
-
- # parses the given protobuf and stores as BuildInfo object.
- builds = {"device": [], "gsi": [], "test": []}
- for pb in schedule_pbs:
- for build_target in pb.build_target:
- build_type = "device"
- device = BuildInfo(build_type)
- for attr in attrs[build_type]:
- if hasattr(pb, attr):
- setattr(device, attr, getattr(pb, attr, None))
- elif hasattr(build_target, attr):
- setattr(device, attr, getattr(build_target, attr,
- None))
- if not [x for x in builds[build_type] if x == device]:
- builds[build_type].append(device)
- for test_schedule in build_target.test_schedule:
- build_type = "gsi"
- gsi = BuildInfo(build_type)
- for attr in attrs[build_type]:
- if hasattr(test_schedule, attr):
- setattr(gsi, attr,
- getattr(test_schedule, attr, None))
- if not [x for x in builds[build_type] if x == gsi]:
- builds[build_type].append(gsi)
-
- build_type = "test"
- test = BuildInfo(build_type)
- for attr in attrs[build_type]:
- if hasattr(test_schedule, attr):
- setattr(test, attr,
- getattr(test_schedule, attr, None))
- if not [x for x in builds[build_type] if x == test]:
- builds[build_type].append(test)
-
- # groups by artifact, branch, and account id, and builds a command.
- for artifact in attrs:
- load_attrs = attrs[artifact]
- if artifact == "device":
- storage_type_text = "build_storage_type"
- else:
- storage_type_text = "" + artifact + "_storage_type"
- pab_builds = [
- x for x in builds[artifact]
- if getattr(x, storage_type_text) ==
- SchedCfgMsg.BUILD_STORAGE_TYPE_PAB
- ]
- pab_builds.sort(key=lambda x: tuple([getattr(x, attribute)
- for attribute in load_attrs]))
- groups = [list(g) for k, g in itertools.groupby(
- pab_builds, lambda x: tuple([getattr(x, attribute)
- for attribute
- in load_attrs[1:-1]]))]
- for group in groups:
- command = ("build --artifact-type={} --method=GET "
- "--noauth_local_webserver=True --update=single".
- format(artifact))
- if artifact == "device":
- if group[0].manifest_branch:
- command += " --branch={}".format(
- group[0].manifest_branch)
- else:
- logging.debug(
- "Device manifest branch is a mandatory field.")
- continue
- if group[0].pab_account_id:
- command += " --account_id={}".format(
- group[0].pab_account_id)
- if group[0].require_signed_device_build:
- command += " --verify-signed-build=True"
- targets = ",".join([x.name for x in group if x.name])
- if targets:
- command += " --target={}".format(targets)
- build_commands.append(command)
- else:
- if getattr(group[0], "" + artifact + "_branch"):
- command += " --branch={}".format(
- getattr(group[0], "" + artifact + "_branch"))
- else:
- logging.debug(
- "{} branch is a mandatory field.".format(artifact))
- continue
- if getattr(group[0], "" + artifact + "_pab_account_id"):
- command += " --account_id={}".format(
- getattr(group[0],
- "" + artifact + "_pab_account_id"))
- targets = ",".join([
- getattr(x, "" + artifact + "_build_target")
- for x in group
- if getattr(x, "" + artifact + "_build_target")
- ])
- if targets:
- command += " --target={}".format(targets)
- build_commands.append(command)
-
- return build_commands
-
- # @Override
- def SetUp(self):
- """Initializes the parser for config command."""
- self.schedule_thread = {}
- self.arg_parser.add_argument(
- "--update",
- choices=("single", "start", "stop", "list"),
- default="start",
- help="Update build info")
- self.arg_parser.add_argument(
- "--id",
- default=None,
- help="session ID only required for 'stop' update command")
- self.arg_parser.add_argument(
- "--interval",
- type=int,
- default=60,
- help="Interval (seconds) to repeat build update.")
- self.arg_parser.add_argument(
- "--config-type",
- choices=("prod", "test"),
- default="prod",
- help="Whether it's for prod")
- self.arg_parser.add_argument(
- "--branch",
- required=True,
- help="Branch to grab the artifact from.")
- self.arg_parser.add_argument(
- "--target",
- required=True,
- help="a comma-separate list of build target product(s).")
- self.arg_parser.add_argument(
- "--account_id",
- default=common._DEFAULT_ACCOUNT_ID,
- help="Partner Android Build account_id to use.")
- self.arg_parser.add_argument(
- '--method',
- default='GET',
- choices=('GET', 'POST'),
- help='Method for fetching')
- self.arg_parser.add_argument(
- '--update_build',
- dest='update_build',
- action='store_true',
- help='A boolean value indicating whether to upload build info.')
- self.arg_parser.add_argument(
- "--clear_schedule",
- default=False,
- help="True to clear all schedule data on the scheduler cloud")
- self.arg_parser.add_argument(
- "--clear_labinfo",
- default=False,
- help="True to clear all lab info data on the scheduler cloud")
-
- # @Override
- def Run(self, arg_line):
- """Updates global config."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.update == "single":
- self.UpdateConfig(args.account_id, args.branch, args.target,
- args.config_type, args.method, args.update_build,
- args.clear_schedule, args.clear_labinfo)
- elif args.update == "list":
- logging.info("Running config update sessions:")
- for id in self.schedule_thread:
- logging.info(" ID %d", id)
- elif args.update == "start":
- if args.interval <= 0:
- raise ConsoleArgumentError("update interval must be positive")
- # do not allow user to create new
- # thread if one is currently running
- if args.id is None:
- if not self.schedule_thread:
- args.id = 1
- else:
- args.id = max(self.schedule_thread) + 1
- else:
- args.id = int(args.id)
- if args.id in self.schedule_thread and not hasattr(
- self.schedule_thread[args.id], 'keep_running'):
- logging.warning('config update already running. '
- 'run config --update=stop --id=%s first.',
- args.id)
- return
- self.schedule_thread[args.id] = threading.Thread(
- target=self.UpdateConfigLoop,
- args=(
- args.account_id,
- args.branch,
- args.target,
- args.config_type,
- args.method,
- args.update_build,
- args.interval,
- args.clear_schedule,
- args.clear_labinfo,
- ))
- self.schedule_thread[args.id].daemon = True
- self.schedule_thread[args.id].start()
- elif args.update == "stop":
- if args.id is None:
- logging.error("--id must be set for stop")
- else:
- self.schedule_thread[int(args.id)].keep_running = False
-
- def Help(self):
- base_command_processor.BaseCommandProcessor.Help(self)
- logging.info("Sample: config --branch=<branch name> "
- "--target=<build target> "
- "--account_id=<account id> --config-type=[prod|test] "
- "--update=single")
diff --git a/harnesses/host_controller/command_processor/command_config_local.py b/harnesses/host_controller/command_processor/command_config_local.py
deleted file mode 100644
index 6c6e5e0..0000000
--- a/harnesses/host_controller/command_processor/command_config_local.py
+++ /dev/null
@@ -1,166 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import httplib2
-import logging
-import socket
-import threading
-import time
-
-from googleapiclient import errors
-
-from host_controller.command_processor import base_command_processor
-from host_controller.command_processor import command_config
-from host_controller.console_argument_parser import ConsoleArgumentError
-from host_controller.tradefed import remote_operation
-
-
-class CommandConfigLocal(command_config.CommandConfig):
- """Command processor for config-local command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- schedule_thread: dict containing threading.Thread instances(s) that
- update schedule info regularly.
- """
-
- command = "config_local"
- command_detail = "Uploads configs from local path."
-
- # @Override
- def UpdateConfig(self, path, update_build, clear_schedule, clear_labinfo):
- """Updates the global configuration data.
-
- Args:
- path: string, a path where config files are stored.
- update_build: boolean, indicating whether to upload build info.
- """
- if path:
- self.UploadConfig(
- path=path,
- update_build=update_build,
- clear_schedule=clear_schedule,
- clear_labinfo=clear_labinfo)
-
- # @Override
- def UpdateConfigLoop(self, path, update_build, update_interval,
- clear_schedule, clear_labinfo):
- """Regularly updates the global configuration.
-
- Args:
- path: string, a path where config files are stored.
- update_build: boolean, indicating whether to upload build info.
- update_interval: int, number of seconds before repeating
- """
- thread = threading.currentThread()
- while getattr(thread, 'keep_running', True):
- try:
- self.UpdateConfig(path, update_build, clear_schedule,
- clear_labinfo)
- except (socket.error, remote_operation.RemoteOperationException,
- httplib2.HttpLib2Error, errors.HttpError) as e:
- logging.exception(e)
- time.sleep(update_interval)
-
- # @Override
- def SetUp(self):
- """Initializes the parser for config command."""
- self.schedule_thread = {}
- self.arg_parser.add_argument(
- "--update",
- choices=("single", "start", "stop", "list"),
- default="single",
- help="Update build info")
- self.arg_parser.add_argument(
- "--id",
- default=None,
- help="session ID only required for 'stop' update command")
- self.arg_parser.add_argument(
- "--interval",
- type=int,
- default=60,
- help="Interval (seconds) to repeat build update.")
- self.arg_parser.add_argument(
- "--path",
- required=True,
- help="A path where config files are stored.")
- self.arg_parser.add_argument(
- '--update_build',
- dest='update_build',
- action='store_true',
- help='A boolean value indicating whether to upload build info.')
- self.arg_parser.add_argument(
- "--clear_schedule",
- default=False,
- help="True to clear all schedule data on the scheduler cloud")
- self.arg_parser.add_argument(
- "--clear_labinfo",
- default=False,
- help="True to clear all lab info data on the scheduler cloud")
-
- # @Override
- def Run(self, arg_line):
- """Updates global config."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.update == "single":
- self.UpdateConfig(args.path, args.update_build,
- args.clear_schedule, args.clear_labinfo)
- elif args.update == "list":
- logging.info("Running config update sessions:")
- for id in self.schedule_thread:
- logging.info(" ID %d", id)
- elif args.update == "start":
- if args.interval <= 0:
- raise ConsoleArgumentError("update interval must be positive")
- # do not allow user to create new
- # thread if one is currently running
- if args.id is None:
- if not self.schedule_thread:
- args.id = 1
- else:
- args.id = max(self.schedule_thread) + 1
- else:
- args.id = int(args.id)
- if args.id in self.schedule_thread and not hasattr(
- self.schedule_thread[args.id], 'keep_running'):
- logging.warning(
- 'config update already running. '
- 'run config-local --update=stop --id=%s first.', args.id)
- return
- self.schedule_thread[args.id] = threading.Thread(
- target=self.UpdateConfigLoop,
- args=(
- args.path,
- args.update_build,
- args.interval,
- args.clear_schedule,
- args.clear_labinfo,
- ))
- self.schedule_thread[args.id].daemon = True
- self.schedule_thread[args.id].start()
- elif args.update == "stop":
- if args.id is None:
- logging.error("--id must be set for stop")
- else:
- self.schedule_thread[int(args.id)].keep_running = False
-
- def Help(self):
- base_command_processor.BaseCommandProcessor.Help(self)
- logging.info("Sample: config-local --path=/usr/local/home/config/prod "
- "--update=single --update_build")
diff --git a/harnesses/host_controller/command_processor/command_copy.py b/harnesses/host_controller/command_processor/command_copy.py
deleted file mode 100644
index 880e55e..0000000
--- a/harnesses/host_controller/command_processor/command_copy.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import shutil
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandCopy(base_command_processor.BaseCommandProcessor):
- """Command processor for copy command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "copy"
- command_detail = "Copy a file."
-
- # @Override
- def Run(self, arg_line):
- """Copy a file from source to destination path."""
- src, dst = arg_line.split()
- if dst == "{vts_tf_home}":
- dst = os.path.dirname(self.console.test_suite_info["vts"])
- elif "{" in dst:
- logging.error("unknown dst %s", dst)
- return
- shutil.copy(src, dst) \ No newline at end of file
diff --git a/harnesses/host_controller/command_processor/command_device.py b/harnesses/host_controller/command_processor/command_device.py
deleted file mode 100644
index 32d51dd..0000000
--- a/harnesses/host_controller/command_processor/command_device.py
+++ /dev/null
@@ -1,354 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import httplib2
-import logging
-import socket
-import threading
-import time
-
-from googleapiclient import errors
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.console_argument_parser import ConsoleArgumentError
-from host_controller.tradefed import remote_operation
-from host_controller.utils.usb import usb_utils
-
-from vts.utils.python.common import cmd_utils
-
-
-class CommandDevice(base_command_processor.BaseCommandProcessor):
- """Command processor for Device command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- update_thread: threading.Thread that updates device state regularly.
- """
-
- command = "device"
- command_detail = "Selects device(s) under test."
-
- def UpdateDevice(self,
- server_type,
- host,
- lease,
- suppress_lock_warning=True,
- from_job_pool=False):
- """Updates the device state of all devices on a given host.
-
- Args:
- server_type: string, the type of a test secheduling server.
- host: HostController object
- lease: boolean, True to lease and execute jobs.
- suppress_lock_warning: bool, True to suppress the warning msg from
- file_lock.
- from_job_pool: bool, True if the 'device' command is executed from
- one of the job pool processes. Checks only
- the availability of the devices when set.
- """
- if server_type == "vti":
- devices = []
-
- if from_job_pool:
- devices_dict = {}
- for serial in self.console.GetSerials():
- device = {}
- device["serial"] = serial
- device["status"] = common._DEVICE_STATUS_DICT[
- "no-response"]
- device["product"] = "error"
- devices_dict[serial] = device
-
- stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand(
- "adb devices")
- lines_adb = stdout.split("\n")
- stdout, stderr, returncode = cmd_utils.ExecuteOneShellCommand(
- "fastboot devices")
- lines_fastboot = stdout.split("\n")
-
- for line in lines_adb:
- if (len(line.strip()) and not (line.startswith("* ")
- or line.startswith("List "))):
- device = {}
- device["serial"] = line.split()[0]
- serial = device["serial"]
-
- if from_job_pool:
- if (serial in devices_dict
- and line.split()[1] == "device"):
- devices_dict[serial][
- "status"] = common._DEVICE_STATUS_DICT[
- "online"]
- product = (self.console._vti_endpoint_client.
- GetJobDeviceProductName())
- if product:
- devices_dict[serial]["product"] = product
- continue
-
- if self.console.file_lock.LockDevice(
- serial, suppress_lock_warning) == False:
- self.console.device_status[
- serial] = common._DEVICE_STATUS_DICT["use"]
- if not suppress_lock_warning:
- logging.info("Device %s already locked." % serial)
- continue
-
- stdout, _, retcode = cmd_utils.ExecuteOneShellCommand(
- "adb -s %s reboot bootloader" % device["serial"],
- common.DEFAULT_DEVICE_TIMEOUT_SECS,
- usb_utils.ResetUsbDeviceOfSerial_Callback,
- device["serial"])
- if retcode == 0:
- lines_fastboot.append(line)
-
- self.console.file_lock.UnlockDevice(serial)
-
- for line in lines_fastboot:
- if len(line.strip()):
- device = {}
- device["serial"] = line.split()[0]
- serial = device["serial"]
-
- if from_job_pool:
- if serial in devices_dict:
- devices_dict[serial][
- "status"] = common._DEVICE_STATUS_DICT[
- "fastboot"]
- product = (self.console._vti_endpoint_client.
- GetJobDeviceProductName())
- if product:
- devices_dict[serial]["product"] = product
- continue
-
- if self.console.file_lock.LockDevice(
- serial, suppress_lock_warning) == False:
- self.console.device_status[
- serial] = common._DEVICE_STATUS_DICT["use"]
- if not suppress_lock_warning:
- logging.info("Device %s already locked." % serial)
- continue
-
- _, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- "fastboot -s %s getvar product" % device["serial"],
- common.DEFAULT_DEVICE_TIMEOUT_SECS,
- usb_utils.ResetUsbDeviceOfSerial_Callback,
- device["serial"])
- if retcode == 0:
- res = stderr.splitlines()[0].rstrip()
- if ":" in res:
- device["product"] = res.split(":")[1].strip()
- elif "waiting for %s" % serial in res:
- res = stderr.splitlines()[1].rstrip()
- device["product"] = res.split(":")[1].strip()
- else:
- device["product"] = "error"
- self.console.device_status[
- serial] = common._DEVICE_STATUS_DICT["fastboot"]
- else:
- device["product"] = "error"
- self.console.device_status[
- serial] = common._DEVICE_STATUS_DICT["no-response"]
-
- device["status"] = self.console.device_status[serial]
- devices.append(device)
-
- self.console.file_lock.UnlockDevice(serial)
-
- if from_job_pool:
- devices = devices_dict.values()
- if devices:
- self.console._vti_endpoint_client.UploadDeviceInfo(
- host.hostname, devices)
- return
-
- self.console._vti_endpoint_client.UploadDeviceInfo(
- host.hostname, devices)
-
- if lease:
- self.console._job_in_queue.put("lease")
-
- if self.console.vtslab_version:
- self.console._vti_endpoint_client.UploadHostVersion(
- host.hostname, self.console.vtslab_version)
- elif server_type == "tfc":
- devices = host.ListDevices()
- for device in devices:
- device.Extend(['sim_state', 'sim_operator', 'mac_address'])
- snapshots = self.console._tfc_client.CreateDeviceSnapshot(
- host._cluster_ids[0], host.hostname, devices)
- self.console._tfc_client.SubmitHostEvents([snapshots])
- else:
- logging.error("Error: unknown server_type %s for UpdateDevice",
- server_type)
-
- def UpdateDeviceRepeat(self,
- server_type,
- host,
- lease,
- update_interval,
- suppress_lock_warning=True,
- from_job_pool=False):
- """Regularly updates the device state of devices on a given host.
-
- Args:
- server_type: string, the type of a test secheduling server.
- host: HostController object
- lease: boolean, True to lease and execute jobs.
- update_interval: int, number of seconds before repeating
- suppress_lock_warning: bool, True to suppress the warning msg from
- file_lock.
- from_job_pool: bool, True if the 'device' command is executed form
- one of the job pool processes.
- """
- thread = threading.currentThread()
- while getattr(thread, 'keep_running', True):
- try:
- self.UpdateDevice(server_type, host, lease,
- suppress_lock_warning, from_job_pool)
- except (socket.error, remote_operation.RemoteOperationException,
- httplib2.HttpLib2Error, errors.HttpError) as e:
- logging.exception(e)
- time.sleep(update_interval)
-
- def RunUSBResetTimer(self, serial, interval):
- """Sets up a timer to run the target function after 'interval' secs.
-
- Args:
- serial: string, serial number of the device whose USB device file
- will reset when the timeout happens.
- interval: int, sets up the timer for the target function to be
- executed after 'interval' seconds, if not canceled.
-
- Returns:
- threading.Timer, set to reset USB port corresponding the device
- with the given serial number.
- """
- usb_reset_timer = threading.Timer(interval, self.USBResetCallback,
- (serial, ))
- usb_reset_timer.daemon = True
- usb_reset_timer.start()
-
- return usb_reset_timer
-
- def USBResetCallback(self, serial):
- """Resets USB device file corresponding to the given device serial.
-
- Args:
- serial: string, serial number of the device whose USB device file
- will reset.
- """
- device_file_path = usb_utils.GetDevicesUSBFilePath()
- if serial in device_file_path:
- logging.error(
- "Device %s not responding. Resetting device file %s.", serial,
- device_file_path[serial])
- usb_utils.ResetDeviceUsb(device_file_path[serial])
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.update_thread = None
- self.arg_parser.add_argument(
- "--set_serial",
- default="",
- help="Serial number for device. Can be a comma-separated list.")
- self.arg_parser.add_argument(
- "--update",
- choices=("single", "start", "stop"),
- default="start",
- help="Update device info on cloud scheduler")
- self.arg_parser.add_argument(
- "--interval",
- type=int,
- default=30,
- help="Interval (seconds) to repeat device update.")
- self.arg_parser.add_argument(
- "--host", type=int, help="The index of the host.")
- self.arg_parser.add_argument(
- "--server_type",
- choices=("vti", "tfc"),
- default="vti",
- help="The type of a cloud-based test scheduler server.")
- self.arg_parser.add_argument(
- "--lease",
- default=False,
- type=bool,
- help="Whether to lease jobs and execute them.")
- self.arg_parser.add_argument(
- "--suppress_lock_warning",
- default=True,
- help="Whether to suppress device lock warning messages.")
- self.arg_parser.add_argument(
- "--from_job_pool",
- action="store_true",
- help="Whether the command is executed from the job pool. "
- "Check only the availability of the devices when set.")
-
- # @Override
- def Run(self, arg_line):
- """Sets device info such as serial number."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.set_serial:
- self.console.SetSerials(args.set_serial.split(","))
- logging.info("serials: %s", self.console._serials)
- if args.update:
- if args.host is None:
- if len(self.console._hosts) > 1:
- raise ConsoleArgumentError("More than one host.")
- args.host = 0
- host = self.console._hosts[args.host]
-
- if args.suppress_lock_warning:
- if (type(args.suppress_lock_warning) != str
- or args.suppress_lock_warning.lower() == "true"):
- suppress_lock_warning = True
- else:
- suppress_lock_warning = False
-
- if args.update == "single":
- self.UpdateDevice(args.server_type, host, args.lease,
- suppress_lock_warning, args.from_job_pool)
- elif args.update == "start":
- if args.interval <= 0:
- raise ConsoleArgumentError(
- "update interval must be positive")
- # do not allow user to create new
- # thread if one is currently running
- if self.update_thread is not None and not hasattr(
- self.update_thread, 'keep_running'):
- logging.warning('device update already running. '
- 'run device --update stop first.')
- return
- self.update_thread = threading.Thread(
- target=self.UpdateDeviceRepeat,
- args=(
- args.server_type,
- host,
- args.lease,
- args.interval,
- suppress_lock_warning,
- args.from_job_pool,
- ))
- self.update_thread.daemon = True
- self.update_thread.start()
- elif args.update == "stop":
- self.update_thread.keep_running = False
- if self.console.GetSerials():
- self.console.ResetSerials()
diff --git a/harnesses/host_controller/command_processor/command_device_test.py b/harnesses/host_controller/command_processor/command_device_test.py
deleted file mode 100644
index f951c56..0000000
--- a/harnesses/host_controller/command_processor/command_device_test.py
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import re
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_device
-
-
-def cmd_util_side_effect(value, timeout=0, callback_on_timeout=None, *args):
- ret = ("", "", 0)
- if value == "adb devices":
- ret = ("List of devices attached\ndevice1\tdevice\ndevice2\tdevice",
- "", 0)
- elif value == "fastboot devices":
- ret = ("device3\tfastboot\n", "", 0)
- elif re.match("fastboot -s .* getvar product", value):
- ret = ("", "product: somefish", 0)
- return ret
-
-
-class CommandDeviceTest(unittest.TestCase):
- """Tests for device command processor"""
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_device.cmd_utils")
- def testUpdateDevice(self, mock_cmd_utils, mock_console):
- command = command_device.CommandDevice()
- command._SetUp(mock_console)
- mock_host = mock.Mock()
- mock_host.hostname = "vtslab-001"
- mock_cmd_utils.ExecuteOneShellCommand.side_effect = cmd_util_side_effect
- command.UpdateDevice("vti", mock_host, False)
- mock_console._vti_endpoint_client.UploadDeviceInfo.assert_called_with(
- "vtslab-001", [{
- "status": mock.ANY,
- "serial": "device3",
- "product": "somefish"
- }, {
- "status": mock.ANY,
- "serial": "device1",
- "product": "somefish"
- }, {
- "status": mock.ANY,
- "serial": "device2",
- "product": "somefish"
- }])
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_device.cmd_utils")
- def testUpdateDeviceLeaseJob(self, mock_cmd_utils, mock_console):
- command = command_device.CommandDevice()
- command._SetUp(mock_console)
- mock_host = mock.Mock()
- mock_host.hostname = "vtslab-001"
- mock_cmd_utils.ExecuteOneShellCommand.side_effect = cmd_util_side_effect
- command.UpdateDevice("vti", mock_host, True)
- mock_console._job_in_queue.put.assert_called_with("lease")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_device.cmd_utils")
- def testUpdateDeviceFromJobPool(self, mock_cmd_utils, mock_console):
- mock_console.GetSerials.return_value = ["device1", "device4"]
- command = command_device.CommandDevice()
- command._SetUp(mock_console)
- mock_host = mock.Mock()
- mock_host.hostname = "vtslab-001"
- mock_cmd_utils.ExecuteOneShellCommand.side_effect = cmd_util_side_effect
- command.UpdateDevice("vti", mock_host, False, from_job_pool=True)
- mock_console._vti_endpoint_client.UploadDeviceInfo.assert_called_with(
- "vtslab-001",
- [{
- 'status': common._DEVICE_STATUS_DICT["no-response"],
- 'serial': 'device4',
- 'product': 'error'
- }, {
- 'status': common._DEVICE_STATUS_DICT["online"],
- 'serial': 'device1',
- 'product': mock.ANY
- }])
-
- @mock.patch("host_controller.console.Console")
- def testCommandDeviceUpdateSingle(self, mock_console):
- command = command_device.CommandDevice()
- command.UpdateDevice = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("--update=single")
- self.assertIsNone(ret)
- command.UpdateDevice.assert_called_with("vti", mock.ANY, False, True,
- False)
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_device.threading")
- def testCommandDeviceUpdateSetSerial(self, mock_threading, mock_console):
- mock_thread = mock.Mock()
- mock_threading.Thread.return_value = mock_thread
- command = command_device.CommandDevice()
- command.UpdateDevice = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("--set_serial=device1,device2,device3")
- self.assertIsNone(ret)
- mock_console.SetSerials.assert_called_with(
- ["device1", "device2", "device3"])
- mock_threading.Thread.assert_called_with(
- args=("vti", mock.ANY, False, 30, True, False),
- target=command.UpdateDeviceRepeat)
- mock_thread.start.assert_called_with()
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_device.threading")
- def testCommandDeviceUpdateStop(self, mock_threading, mock_console):
- mock_thread = mock.Mock()
- mock_threading.Thread.return_value = mock_thread
- command = command_device.CommandDevice()
- command.UpdateDevice = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("")
- self.assertIsNone(ret)
- mock_threading.Thread.assert_called_with(
- args=("vti", mock.ANY, False, 30, True, False),
- target=command.UpdateDeviceRepeat)
- mock_thread.start.assert_called_with()
- ret = command._Run("--update=stop")
- self.assertIsNone(ret)
- self.assertFalse(mock_thread.keep_running)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/command_processor/command_dut.py b/harnesses/host_controller/command_processor/command_dut.py
deleted file mode 100644
index a722b41..0000000
--- a/harnesses/host_controller/command_processor/command_dut.py
+++ /dev/null
@@ -1,141 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-
-from vts.utils.python.common import cmd_utils
-from vts.utils.python.controllers import adb
-from vts.utils.python.controllers import android_device
-
-# Default index of setStreamVolume() from IAudioService.aidl (1 based)
-SETSTREAMVOLUME_INDEX_DEFAULT = 3
-
-# Indices of each stream type (can be checked with "adb shell dumpsys audio" on the command line)
-STREAM_TYPE_CALL = 0
-STREAM_TYPE_RINGTONE = 2
-STREAM_TYPE_MEDIA = 3
-STREAM_TYPE_ALARM = 4
-
-STREAM_TYPE_LIST = [
- STREAM_TYPE_CALL,
- STREAM_TYPE_RINGTONE,
- STREAM_TYPE_MEDIA,
- STREAM_TYPE_ALARM,
-]
-
-
-class CommandDUT(base_command_processor.BaseCommandProcessor):
- """Command processor for DUT command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "dut"
- command_detail = "Performs certain operations on DUT (Device Under Test)."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for dut command."""
- self.arg_parser.add_argument(
- "--operation",
- choices=("wifi_on", "wifi_off", 'volume_mute', 'volume_max'),
- default="",
- required=True,
- help="Operation to perform.")
- self.arg_parser.add_argument(
- "--serial", default="", required=True, help="The device serial.")
- self.arg_parser.add_argument(
- "--ap",
- default="", # Required only for wifi_on
- help="Access point (AP) name for 'wifi_on' operation.")
- self.arg_parser.add_argument(
- "--volume_levels",
- type=int,
- default=30, # Required only for volume_mute and volume_max
- help="The number of volume control levels.")
- self.arg_parser.add_argument(
- "--version",
- type=float,
- default=8.0,
- help="System version information of the device on which "
- "the test will run.")
-
- # @Override
- def Run(self, arg_line):
- """Performs the requested operation on the selected DUT."""
- args = self.arg_parser.ParseLine(arg_line)
- device = android_device.AndroidDevice(
- args.serial, device_callback_port=-1)
- boot_complete = device.waitForBootCompletion()
- if not boot_complete:
- logging.error("Device %s failed to bootup.", args.serial)
- self.console.device_status[
- args.serial] = common._DEVICE_STATUS_DICT["error"]
- self.console.vti_endpoint_client.SetJobStatusFromLeasedTo(
- "bootup-err")
- return False
-
- adb_proxy = adb.AdbProxy(serial=args.serial)
- adb_proxy.root()
- try:
- if args.operation == "wifi_on":
- adb_proxy.shell("svc wifi enable")
- if args.ap:
- adb_proxy.install(
- "../testcases/DATA/app/WifiUtil/WifiUtil.apk")
- adb_proxy.shell(
- "am instrument -e method \"connectToNetwork\" "
- "-e ssid %s "
- "-w com.android.tradefed.utils.wifi/.WifiUtil" %
- args.ap)
- elif args.operation == "wifi_off":
- adb_proxy.shell("svc wifi disable")
- elif args.operation == "volume_mute":
- for _ in range(args.volume_levels):
- adb_proxy.shell("input keyevent 25")
- self.SetOtherVolumes(adb_proxy, 0, args.version)
- elif args.operation == "volume_max":
- for _ in range(args.volume_levels):
- adb_proxy.shell("input keyevent 24")
- self.SetOtherVolumes(adb_proxy, args.volume_levels,
- args.version)
- except adb.AdbError as e:
- logging.exception(e)
- return False
-
- def SetOtherVolumes(self, adb_proxy, volume_level, version=None):
- """Sets device's call/media/alarm volumes a certain level.
-
- Args:
- adb_proxy: AdbProxy, used for interacting with the device via adb.
- volume_level: int, volume level value.
- version: float, Android system version value. The index of
- setStreamVolume() depends on the Android version.
- """
- setStreamVolume_index = SETSTREAMVOLUME_INDEX_DEFAULT
- if version and version >= 9.0:
- setStreamVolume_index = 7
- for stream_type in STREAM_TYPE_LIST:
- adb_volume_command = "service call audio %s i32 %s i32 %s i32 1" % (
- setStreamVolume_index, stream_type, volume_level)
- adb_proxy.shell(adb_volume_command)
diff --git a/harnesses/host_controller/command_processor/command_dut_test.py b/harnesses/host_controller/command_processor/command_dut_test.py
deleted file mode 100644
index fff5c4d..0000000
--- a/harnesses/host_controller/command_processor/command_dut_test.py
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_dut
-
-
-class CommandDUTTest(unittest.TestCase):
- """Tests for DUT command processor"""
-
- def setUp(self):
- """Creates CommandSheet."""
- self._command = command_dut.CommandDUT()
- mock_console = mock.Mock()
- mock_console.device_status = {}
- self._command._SetUp(mock_console)
-
- def testSetOtherVolumesWithoutVersionInfo(self):
- mock_adb_proxy = mock.Mock()
- self._command.SetOtherVolumes(mock_adb_proxy, 5)
- self.assertEqual(mock_adb_proxy.shell.mock_calls, [
- mock.call("service call audio 3 i32 0 i32 5 i32 1"),
- mock.call("service call audio 3 i32 2 i32 5 i32 1"),
- mock.call("service call audio 3 i32 3 i32 5 i32 1"),
- mock.call("service call audio 3 i32 4 i32 5 i32 1"),
- ])
-
- def testSetOtherVolumesWithVersionInfo(self):
- mock_adb_proxy = mock.Mock()
- self._command.SetOtherVolumes(mock_adb_proxy, 0, 8.1)
- self.assertEqual(mock_adb_proxy.shell.mock_calls, [
- mock.call("service call audio 3 i32 0 i32 0 i32 1"),
- mock.call("service call audio 3 i32 2 i32 0 i32 1"),
- mock.call("service call audio 3 i32 3 i32 0 i32 1"),
- mock.call("service call audio 3 i32 4 i32 0 i32 1"),
- ])
- mock_adb_proxy.shell.mock_calls = []
- self._command.SetOtherVolumes(mock_adb_proxy, 10, 9.0)
- self.assertEqual(mock_adb_proxy.shell.mock_calls, [
- mock.call("service call audio 7 i32 0 i32 10 i32 1"),
- mock.call("service call audio 7 i32 2 i32 10 i32 1"),
- mock.call("service call audio 7 i32 3 i32 10 i32 1"),
- mock.call("service call audio 7 i32 4 i32 10 i32 1"),
- ])
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.logging")
- def testCommandDUTBootupFail(self, mock_logging, mock_android_device):
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = False
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run("--serial device1 --operation wifi_on")
- self.assertFalse(ret)
- mock_logging.error.assert_called_with("Device %s failed to bootup.",
- "device1")
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.adb")
- def testCommandDUTWifiOnWithoutAP(self, mock_adb, mock_android_device):
- mock_adb_proxy = mock.Mock()
- mock_adb.AdbProxy.return_value = mock_adb_proxy
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = True
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run("--serial device1 --operation wifi_on")
- self.assertIsNone(ret)
- mock_adb_proxy.root.assert_called_once()
- mock_adb_proxy.shell.assert_called_with("svc wifi enable")
- mock_adb_proxy.install.assert_not_called()
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.adb")
- def testCommandDUTWifiOn(self, mock_adb, mock_android_device):
- mock_adb_proxy = mock.Mock()
- mock_adb.AdbProxy.return_value = mock_adb_proxy
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = True
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run("--serial device1 --operation wifi_on --ap %s"
- % common._DEFAULT_WIFI_AP)
- self.assertIsNone(ret)
- mock_adb_proxy.root.assert_called_once()
- mock_adb_proxy.shell.assert_any_call("svc wifi enable")
- mock_adb_proxy.install.assert_called_with(
- "../testcases/DATA/app/WifiUtil/WifiUtil.apk")
- mock_adb_proxy.shell.assert_called_with(
- "am instrument -e method \"connectToNetwork\" -e ssid GoogleGuest "
- "-w com.android.tradefed.utils.wifi/.WifiUtil")
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.adb")
- def testCommandDUTWifiOff(self, mock_adb, mock_android_device):
- mock_adb_proxy = mock.Mock()
- mock_adb.AdbProxy.return_value = mock_adb_proxy
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = True
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run("--serial device1 --operation wifi_off")
- self.assertIsNone(ret)
- mock_adb_proxy.root.assert_called_once()
- mock_adb_proxy.shell.assert_any_call("svc wifi disable")
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.adb")
- def testCommandDUTVolumeMute(self, mock_adb, mock_android_device):
- self._command.SetOtherVolumes = mock.Mock()
- mock_adb_proxy = mock.Mock()
- mock_adb.AdbProxy.return_value = mock_adb_proxy
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = True
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run("--serial device1 --operation volume_mute")
- self.assertIsNone(ret)
- mock_adb_proxy.root.assert_called_once()
- self.assertEqual(mock_adb_proxy.shell.mock_calls,
- [mock.call("input keyevent 25")] * 30)
- self._command.SetOtherVolumes.assert_called_with(mock.ANY, 0, 8.0)
-
- @mock.patch("host_controller.command_processor.command_dut.android_device")
- @mock.patch("host_controller.command_processor.command_dut.adb")
- def testCommandDUTVolumeMax(self, mock_adb, mock_android_device):
- self._command.SetOtherVolumes = mock.Mock()
- mock_adb_proxy = mock.Mock()
- mock_adb.AdbProxy.return_value = mock_adb_proxy
- mock_device = mock.Mock()
- mock_device.waitForBootCompletion.return_value = True
- mock_android_device.AndroidDevice.return_value = mock_device
- ret = self._command._Run(
- "--serial device1 --operation volume_max --version 9.0")
- self.assertIsNone(ret)
- mock_adb_proxy.root.assert_called_once()
- self.assertEqual(mock_adb_proxy.shell.mock_calls,
- [mock.call("input keyevent 24")] * 30)
- self._command.SetOtherVolumes.assert_called_with(mock.ANY, 30, 9.0)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/command_processor/command_exit.py b/harnesses/host_controller/command_processor/command_exit.py
deleted file mode 100644
index 638e6ad..0000000
--- a/harnesses/host_controller/command_processor/command_exit.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandExit(base_command_processor.BaseCommandProcessor):
- """Command processor for exit command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "exit"
- command_detail = ""
-
- # @Override
- def SetUp(self):
- """Initializes the parser for request command."""
- self.arg_parser.add_argument(
- "--wait_for_jobs",
- default=True,
- help="True to wait for the running jobs to complete before exiting."
- )
-
- # @Override
- def Run(self, arg_line):
- """Terminates the console.
-
- Returns:
- True, which stops the cmdloop.
- """
- args = self.arg_parser.ParseLine(arg_line)
-
- self.console.onecmd("device --update stop")
-
- if args.wait_for_jobs:
- if (type(args.wait_for_jobs) != str or
- args.wait_for_jobs.lower() == "true"):
- logging.info("waiting for running jobs to complete...")
- self.console.WaitForJobsToExit()
-
- self.console.StopJobThreadAndProcessPool()
- self.console.__exit__()
- return True
diff --git a/harnesses/host_controller/command_processor/command_fastboot.py b/harnesses/host_controller/command_processor/command_fastboot.py
deleted file mode 100644
index 459a079..0000000
--- a/harnesses/host_controller/command_processor/command_fastboot.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.ipc import file_lock_semaphore
-from host_controller.utils.usb import usb_utils
-
-from vts.utils.python.common import cmd_utils
-
-
-class CommandFastboot(base_command_processor.BaseCommandProcessor):
- """Command processor for fastboot command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "fastboot"
- command_detail = "Runs a fastboot command."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.sem_fastboot = file_lock_semaphore.FileLockSemaphore("fastboot")
- self.arg_parser.add_argument(
- "--serial",
- "-s",
- required=True,
- default=None,
- help="The target device serial to run the command.")
- self.arg_parser.add_argument(
- "--retry",
- type=int,
- default=2,
- help="The number of times to retry if a command fails.")
- self.arg_parser.add_argument(
- "--timeout",
- type=float,
- default=common.DEFAULT_DEVICE_TIMEOUT_SECS,
- help="The maximum timeout value of this command in seconds. "
- "Set to 0 to disable the timeout functionality.")
- self.arg_parser.add_argument(
- "command",
- metavar="COMMAND",
- nargs="+",
- help="The command to be executed. If the command contains "
- "arguments starting with \"-\", place the command at end of line "
- "after \"--\".")
-
- # @Override
- def Run(self, arg_line):
- """Runs a fastboot command."""
- args = self.arg_parser.ParseLine(arg_line)
- cmd_list = ["fastboot"]
- if args.serial:
- if "," in args.serial:
- logging.error("Only one serial can be specified")
- return False
- cmd_list.append("-s %s" % args.serial)
- cmd_list.extend(self.ReplaceVars(args.command))
- cmd = " ".join(cmd_list)
- for _ in range(args.retry + 1):
- self.sem_fastboot.Acquire()
- if args.timeout == 0:
- stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(cmd)
- else:
- stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- cmd, args.timeout,
- usb_utils.ResetUsbDeviceOfSerial_Callback, args.serial)
- self.sem_fastboot.Release()
- if stdout:
- logging.info(stdout)
- if stderr:
- logging.error(stderr)
- if retcode == 0:
- return
- logging.warn("Retrying... (%s)", cmd)
-
- if self.console.job_pool and args.serial:
- self.console.device_status[
- args.serial] = common._DEVICE_STATUS_DICT["error"]
- return False
diff --git a/harnesses/host_controller/command_processor/command_fetch.py b/harnesses/host_controller/command_processor/command_fetch.py
deleted file mode 100644
index f43731d..0000000
--- a/harnesses/host_controller/command_processor/command_fetch.py
+++ /dev/null
@@ -1,204 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-
-
-class CommandFetch(base_command_processor.BaseCommandProcessor):
- """Command processor for fetch command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "fetch"
- command_detail = "Fetch a build artifact."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for fetch command."""
- self.arg_parser.add_argument(
- '--type',
- default='pab',
- choices=('local_fs', 'gcs', 'pab', 'ab'),
- help='Build provider type')
- self.arg_parser.add_argument(
- '--method',
- default='GET',
- choices=('GET', 'POST'),
- help='Method for fetching')
- self.arg_parser.add_argument(
- "--path", # required for local_fs
- help="The path of a local directory which keeps the artifacts.")
- self.arg_parser.add_argument(
- "--branch", # required for pab
- help="Branch to grab the artifact from.")
- self.arg_parser.add_argument(
- "--target", # required for pab
- help="Target product to grab the artifact from.")
- # TODO(lejonathan): find a way to not specify this?
- self.arg_parser.add_argument(
- "--account_id",
- default=common._DEFAULT_ACCOUNT_ID,
- help="Partner Android Build account_id to use.")
- self.arg_parser.add_argument(
- '--build_id',
- default='latest',
- help='Build ID to use default latest.')
- self.arg_parser.add_argument(
- "--artifact_name", # required for pab
- help=
- "Name of the artifact to be fetched. {id} replaced with build id.")
- self.arg_parser.add_argument(
- "--userinfo-file",
- help=
- "Location of file containing email and password, if using POST.")
- self.arg_parser.add_argument(
- "--noauth_local_webserver",
- default=False,
- type=bool,
- help="True to not use a local webserver for authentication.")
- self.arg_parser.add_argument(
- "--fetch_signed_build",
- default=False,
- type=bool,
- help="True to fetch only signed build images.")
- self.arg_parser.add_argument(
- "--full_device_images",
- default=False,
- type=bool,
- help="True to skip checking whether the fetched artifacts are "
- "fully packaged device images.")
- self.arg_parser.add_argument(
- "--gsi",
- default=False,
- type=bool,
- help="True if a target is GSI. Only system.img and "
- "vbmeta.img are taken.")
- self.arg_parser.add_argument(
- "--set_suite_as",
- default="",
- choices=("", "vts", "cts", "gts", "sts"),
- help="To specify the type of a test suite that is being fetched."
- "Used when the artifact's file name does not follow the "
- "standard naming convention.")
-
- # @Override
- def Run(self, arg_line):
- """Makes the host download a build artifact from PAB."""
- args = self.arg_parser.ParseLine(arg_line)
-
- if args.type not in self.console._build_provider:
- logging.error("ERROR: uninitialized fetch type %s", args.type)
- return False
-
- provider = self.console._build_provider[args.type]
- if args.type == "pab":
- # do we want this somewhere else? No harm in doing multiple times
- provider.Authenticate(args.userinfo_file,
- args.noauth_local_webserver)
- if not args.fetch_signed_build:
- (device_images, test_suites, fetch_environment,
- _) = provider.GetArtifact(
- account_id=args.account_id,
- branch=args.branch,
- target=args.target,
- artifact_name=args.artifact_name,
- build_id=args.build_id,
- method=args.method,
- full_device_images=args.full_device_images)
- self.console.fetch_info["fetch_signed_build"] = False
- else:
- (device_images, test_suites, fetch_environment,
- _) = provider.GetSignedBuildArtifact(
- account_id=args.account_id,
- branch=args.branch,
- target=args.target,
- artifact_name=args.artifact_name,
- build_id=args.build_id,
- method=args.method,
- full_device_images=args.full_device_images)
- self.console.fetch_info["fetch_signed_build"] = True
-
- self.console.fetch_info["build_id"] = fetch_environment["build_id"]
- elif args.type == "local_fs":
- device_images, test_suites = provider.Fetch(
- args.path, args.full_device_images)
- self.console.fetch_info["build_id"] = None
- elif args.type == "gcs":
- device_images, test_suites, tools = provider.Fetch(
- args.path, args.full_device_images, args.set_suite_as)
- self.console.fetch_info["build_id"] = None
- elif args.type == "ab":
- device_images, test_suites, fetch_environment = provider.Fetch(
- branch=args.branch,
- target=args.target,
- artifact_name=args.artifact_name,
- build_id=args.build_id,
- full_device_images=args.full_device_images)
- self.console.fetch_info["build_id"] = fetch_environment["build_id"]
- else:
- logging.error("ERROR: unknown fetch type %s", args.type)
- return False
-
- if args.gsi:
- filtered_images = {}
- image_names = device_images.keys()
- for image_name in image_names:
- if image_name.endswith(".img") and image_name not in [
- "system.img", "vbmeta.img"
- ]:
- provider.RemoveDeviceImage(image_name)
- continue
- filtered_images[image_name] = device_images[image_name]
- device_images = filtered_images
-
- if args.type == "gcs":
- gcs_path, filename = os.path.split(args.path)
- self.console.fetch_info["branch"] = gcs_path
- self.console.fetch_info["target"] = filename
- self.console.fetch_info["build_id"] = "latest"
- self.console.fetch_info["account_id"] = ""
- else:
- self.console.fetch_info["branch"] = args.branch
- self.console.fetch_info["target"] = args.target
- self.console.fetch_info["account_id"] = args.account_id
-
- self.console.UpdateFetchInfo(provider.GetFetchedArtifactType())
-
- self.console.device_image_info.update(device_images)
- self.console.test_suite_info.update(test_suites)
- self.console.tools_info.update(provider.GetAdditionalFile())
-
- if self.console.device_image_info:
- logging.info("device images:\n%s", "\n".join(
- image + ": " + path
- for image, path in self.console.device_image_info.iteritems()))
- if self.console.test_suite_info:
- logging.info("test suites:\n%s", "\n".join(
- suite + ": " + path
- for suite, path in self.console.test_suite_info.iteritems()))
- if self.console.tools_info:
- logging.info("additional files:\n%s", "\n".join(
- rel_path + ": " + full_path for rel_path, full_path in
- self.console.tools_info.iteritems()))
diff --git a/harnesses/host_controller/command_processor/command_flash.py b/harnesses/host_controller/command_processor/command_flash.py
deleted file mode 100644
index b8a2d7e..0000000
--- a/harnesses/host_controller/command_processor/command_flash.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import importlib
-import os
-import stat
-
-from host_controller import common
-from host_controller.build import build_flasher
-from host_controller.command_processor import base_command_processor
-
-
-class CommandFlash(base_command_processor.BaseCommandProcessor):
- """Command processor for flash command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "flash"
- command_detail = "Flash images to a device."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for flash command."""
- self.arg_parser.add_argument(
- "--image",
- help=("The file name of an image to flash."
- " Used to flash a single image."))
- self.arg_parser.add_argument(
- "--current",
- metavar="PARTITION_IMAGE",
- nargs="*",
- type=lambda x: x.split("="),
- help="The partitions and images to be flashed. The format is "
- "<partition>=<image>. If PARTITION_IMAGE list is empty, "
- "currently fetched " + ", ".join(common._DEFAULT_FLASH_IMAGES) +
- " will be flashed.")
- self.arg_parser.add_argument(
- "--serial", default="", help="Serial number for device.")
- self.arg_parser.add_argument(
- "--build_dir",
- help="Directory containing build images to be flashed.")
- self.arg_parser.add_argument(
- "--gsi", help="Path to generic system image")
- self.arg_parser.add_argument("--vbmeta", help="Path to vbmeta image")
- self.arg_parser.add_argument(
- "--flasher_type",
- default="fastboot",
- help="Flasher type. Valid arguments are \"fastboot\", \"custom\", "
- "and full module name followed by class name. The class must "
- "inherit build_flasher.BuildFlasher, and implement "
- "__init__(serial, flasher_path) and "
- "Flash(device_images, additional_files, *flasher_args).")
- self.arg_parser.add_argument(
- "--flasher_path", default=None, help="Path to a flasher binary")
- self.arg_parser.add_argument(
- "flasher_args",
- metavar="ARGUMENTS",
- nargs="*",
- help="The arguments passed to the flasher binary. If any argument "
- "starts with \"-\", place all of them after \"--\" at end of "
- "line.")
- self.arg_parser.add_argument(
- "--reboot_mode",
- default="bootloader",
- choices=("bootloader", "download"),
- help="Reboot device to bootloader/download mode")
- self.arg_parser.add_argument(
- "--repackage",
- default="tar.md5",
- choices=("tar.md5"),
- help="Repackage artifacts into given format before flashing.")
- self.arg_parser.add_argument(
- "--wait-for-boot",
- default="true",
- help="false to not wait for device booting.")
- self.arg_parser.add_argument(
- "--reboot", default="false", help="true to reboot the device(s).")
- self.arg_parser.add_argument(
- "--skip-vbmeta",
- default=False,
- type=bool,
- help="true to skip flashing vbmeta.img if the device does not have "
- "the vbmeta slot .")
-
- # @Override
- def Run(self, arg_line):
- """Flash GSI or build images to a device connected with ADB."""
- args = self.arg_parser.ParseLine(arg_line)
-
- # path
- if (self.console.tools_info is not None
- and args.flasher_path in self.console.tools_info):
- flasher_path = self.console.tools_info[args.flasher_path]
- elif args.flasher_path:
- flasher_path = args.flasher_path
- else:
- flasher_path = ""
- if os.path.exists(flasher_path):
- flasher_mode = os.stat(flasher_path).st_mode
- os.chmod(flasher_path,
- flasher_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
-
- # serial numbers
- if args.serial:
- flasher_serials = [args.serial]
- elif self.console._serials:
- flasher_serials = self.console._serials
- else:
- flasher_serials = [""]
-
- # images
- if args.image:
- partition_image = {}
- partition_image[args.image] = self.console.device_image_info[
- args.image]
- else:
- if args.current:
- partition_image = dict((partition,
- self.console.device_image_info[image])
- for partition, image in args.current)
- else:
- partition_image = dict(
- (image.rsplit(".img", 1)[0],
- self.console.device_image_info[image])
- for image in common._DEFAULT_FLASH_IMAGES
- if image in self.console.device_image_info)
-
- # type
- if args.flasher_type in ("fastboot", "custom"):
- flasher_class = build_flasher.BuildFlasher
- else:
- class_path = args.flasher_type.rsplit(".", 1)
- flasher_module = importlib.import_module(class_path[0])
- flasher_class = getattr(flasher_module, class_path[1])
- if not issubclass(flasher_class, build_flasher.BuildFlasher):
- raise TypeError(
- "%s is not a subclass of BuildFlasher." % class_path[1])
-
- flashers = [flasher_class(s, flasher_path) for s in flasher_serials]
-
- # Can be parallelized as long as that's proven reliable.
- for flasher in flashers:
- ret_flash = True
- if args.flasher_type == "fastboot":
- if args.image is not None:
- ret_flash = flasher.FlashImage(partition_image, True
- if args.reboot == "true"
- else False)
- elif args.current is not None:
- ret_flash = flasher.Flash(partition_image,
- args.skip_vbmeta)
- else:
- if args.gsi is None and args.build_dir is None:
- self.arg_parser.error("Nothing requested: "
- "specify --gsi or --build_dir")
- return False
- if args.build_dir is not None:
- ret_flash = flasher.Flashall(args.build_dir)
- if args.gsi is not None:
- ret_flash = flasher.FlashGSI(
- args.gsi,
- args.vbmeta,
- skip_vbmeta=args.skip_vbmeta)
- elif args.flasher_type == "custom":
- if flasher_path is not None:
- if args.repackage is not None:
- flasher.RepackageArtifacts(
- self.console.device_image_info, args.repackage)
- ret_flash = flasher.FlashUsingCustomBinary(
- self.console.device_image_info, args.reboot_mode,
- args.flasher_args, 300)
- else:
- self.arg_parser.error(
- "Please specify the path to custom flash tool.")
- return False
- else:
- ret_flash = flasher.Flash(partition_image,
- self.console.tools_info,
- *args.flasher_args)
- if ret_flash == False:
- return False
-
- if args.wait_for_boot == "true":
- for flasher in flashers:
- ret_wait = flasher.WaitForDevice()
- if ret_wait == False:
- self.console.device_status[
- flasher.device.serial] = common._DEVICE_STATUS_DICT[
- "error"]
- self.console.vti_endpoint_client.SetJobStatusFromLeasedTo(
- "bootup-err")
- return False
diff --git a/harnesses/host_controller/command_processor/command_gsispl.py b/harnesses/host_controller/command_processor/command_gsispl.py
deleted file mode 100644
index 9ebcd68..0000000
--- a/harnesses/host_controller/command_processor/command_gsispl.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import datetime
-import logging
-import os
-import shutil
-import tempfile
-import zipfile
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.gsi import img_utils
-
-from vts.utils.python.common import cmd_utils
-
-
-class CommandGsispl(base_command_processor.BaseCommandProcessor):
- """Command processor for gsispl command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "gsispl"
- command_detail = "Changes security patch level on a selected GSI file."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.arg_parser.add_argument(
- "--gsi",
- help="Path to GSI image to change security patch level. "
- "If path is not given, the most recently fetched system.img "
- "kept in device_image_info dictionary is used and then "
- "device_image_info will be updated with the new GSI file.")
- self.arg_parser.add_argument(
- "--version", help="New version ID. It should be YYYY-mm-dd format")
- self.arg_parser.add_argument(
- "--version_from_path",
- help="Path to vendor provided image file to retrieve SPL version. "
- "If just a file name is given, the most recently fetched .img "
- "file will be used.")
- self.arg_parser.add_argument(
- "--vendor_version",
- help="The version of vendor.img that will be used (e.g., 8.1.0).")
-
- # @Override
- def Run(self, arg_line):
- """Changes security patch level on a selected GSI file."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.gsi:
- if os.path.isfile(args.gsi):
- gsi_path = args.gsi
- else:
- logging.error("Cannot find system image in given path")
- return
- elif "system.img" in self.console.device_image_info:
- gsi_path = self.console.device_image_info["system.img"]
- else:
- logging.error("Cannot find system image.")
- return False
-
- if args.version:
- try:
- version_date = datetime.datetime.strptime(
- args.version, "%Y-%m-%d")
- version = "{:04d}-{:02d}-{:02d}".format(
- version_date.year, version_date.month, version_date.day)
- except ValueError as e:
- logging.error("version ID should be YYYY-mm-dd format.")
- return
- elif args.version_from_path:
- dest_path = None
- if os.path.isabs(args.version_from_path) and os.path.exists(
- args.version_from_path):
- img_path = args.version_from_path
- elif args.version_from_path in self.console.device_image_info:
- img_path = self.console.device_image_info[
- args.version_from_path]
- elif (args.version_from_path == "boot.img"
- and "full-zipfile" in self.console.device_image_info):
- tempdir_base = os.path.join(os.getcwd(), "tmp")
- if not os.path.exists(tempdir_base):
- os.mkdir(tempdir_base)
- dest_path = tempfile.mkdtemp(dir=tempdir_base)
-
- with zipfile.ZipFile(
- self.console.device_image_info["full-zipfile"],
- 'r') as zip_ref:
- zip_ref.extractall(dest_path)
- img_path = os.path.join(dest_path, "boot.img")
- if not os.path.exists(img_path):
- logging.error("No %s file in device img .zip.",
- args.version_from_path)
- shutil.rmtree(dest_path)
- return
- else:
- logging.error("Cannot find %s file.", args.version_from_path)
- return False
-
- version_dict = img_utils.GetSPLVersionFromBootImg(img_path)
- if dest_path:
- shutil.rmtree(dest_path)
- if "year" in version_dict and "month" in version_dict:
- version = "{:04d}-{:02d}-{:02d}".format(
- version_dict["year"], version_dict["month"],
- common._SPL_DEFAULT_DAY)
- else:
- logging.error("Failed to fetch SPL version from %s file.",
- img_path)
- return False
- else:
- logging.error("version ID or path of .img file must be given.")
- return False
-
- output_path = os.path.join(
- os.path.dirname(os.path.abspath(gsi_path)),
- "system-{}.img".format(version))
- command = "{} {} {} {}".format(
- os.path.join(os.getcwd(), "..", "bin",
- "change_security_patch_ver.sh"), gsi_path,
- output_path, version)
- if args.vendor_version:
- command = command + " -v " + args.vendor_version
- if self.console.password.value:
- command = "echo {} | sudo -S {}".format(
- self.console.password.value, command)
- stdout, stderr, err_code = cmd_utils.ExecuteOneShellCommand(command)
- if err_code is 0:
- if not args.gsi:
- logging.info(
- "system.img path is updated to : {}".format(output_path))
- self.console.device_image_info["system.img"] = output_path
- else:
- logging.error("gsispl error: {} {}".format(stdout, stderr))
- return False
diff --git a/harnesses/host_controller/command_processor/command_info.py b/harnesses/host_controller/command_processor/command_info.py
deleted file mode 100644
index 715f913..0000000
--- a/harnesses/host_controller/command_processor/command_info.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandInfo(base_command_processor.BaseCommandProcessor):
- '''Command processor for info command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- '''
-
- command = 'info'
- command_detail = 'Show status.'
-
- def Run(self, arg_line):
- '''Shows the console's session status information.
-
- Args:
- arg_line: string, line of command arguments
- '''
- logging.info('device image: %s', self.console.device_image_info)
- logging.info('test suite: %s', self.console.test_suite_info)
- logging.info('test result: %s', self.console.test_results)
- logging.info('fetch info: %s', self.console.fetch_info)
- logging.info('detailed fetch info: %s', self.console.detailed_fetch_info)
diff --git a/harnesses/host_controller/command_processor/command_lease.py b/harnesses/host_controller/command_processor/command_lease.py
deleted file mode 100644
index cc21306..0000000
--- a/harnesses/host_controller/command_processor/command_lease.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.command_processor import base_command_processor
-from host_controller.console_argument_parser import ConsoleArgumentError
-
-
-class CommandLease(base_command_processor.BaseCommandProcessor):
- """Command processor for lease command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "lease"
- command_detail = "Make a host lease command tasks from TFC."
-
- def _PrintTasks(self, tasks):
- """Shows a list of command tasks.
-
- Args:
- devices: A list of DeviceInfo objects.
- """
- attr_names = ("request_id", "command_id", "task_id", "device_serials",
- "command_line")
- self.console._PrintObjects(tasks, attr_names)
-
- # @Override
- def SetUp(self):
- """Initializes the parser for lease command."""
- self.arg_parser.add_argument(
- "--host", type=int, help="The index of the host.")
-
- # @Override
- def Run(self, arg_line):
- """Makes a host lease command tasks from TFC."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.host is None:
- if len(self.console._hosts) > 1:
- raise ConsoleArgumentError("More than one hosts.")
- args.host = 0
- tasks = self.console._hosts[args.host].LeaseCommandTasks()
- self._PrintTasks(tasks) \ No newline at end of file
diff --git a/harnesses/host_controller/command_processor/command_list.py b/harnesses/host_controller/command_processor/command_list.py
deleted file mode 100644
index e196a1a..0000000
--- a/harnesses/host_controller/command_processor/command_list.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandList(base_command_processor.BaseCommandProcessor):
- """Command processor for list command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "list"
- command_detail = "Show information about the hosts."
-
- def _PrintHosts(self, hosts):
- """Shows a list of host controllers.
-
- Args:
- hosts: A list of HostController objects.
- """
- self.console._Print("index name")
- for ind, host in enumerate(hosts):
- self.console._Print("[%3d] %s" % (ind, host.hostname))
-
- def _PrintDevices(self, devices):
- """Shows a list of devices.
-
- Args:
- devices: A list of DeviceInfo objects.
- """
- attr_names = ("device_serial", "state", "run_target", "build_id",
- "sdk_version", "stub")
- self.console._PrintObjects(devices, attr_names)
-
- # @Override
- def SetUp(self):
- """Initializes the parser for list command."""
- self.arg_parser.add_argument(
- "--host", type=int, help="The index of the host.")
- self.arg_parser.add_argument(
- "type",
- choices=("hosts", "devices"),
- help="The type of the shown objects.")
-
- # @Override
- def Run(self, arg_line):
- """Shows information about the hosts."""
- args = self.arg_parser.ParseLine(arg_line)
- if args.host is None:
- hosts = enumerate(self.console._hosts)
- else:
- hosts = [(args.host, self.console._hosts[args.host])]
- if args.type == "hosts":
- self._PrintHosts(self.console._hosts)
- elif args.type == "devices":
- for ind, host in hosts:
- devices = host.ListDevices()
- self.console._Print("[%3d] %s" % (ind, host.hostname))
- self._PrintDevices(devices)
-
- def Help(self):
- base_command_processor.BaseCommandProcessor.Help(self)
- logging.info("Sample: build --target=aosp_sailfish-userdebug "
- "--branch=<branch name> --artifact-type=device")
diff --git a/harnesses/host_controller/command_processor/command_password.py b/harnesses/host_controller/command_processor/command_password.py
deleted file mode 100644
index c767f22..0000000
--- a/harnesses/host_controller/command_processor/command_password.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import getpass
-import logging
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandPassword(base_command_processor.BaseCommandProcessor):
- """Command processor for password command.
-
- Attributes:
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "password"
- command_detail = "Sets password."
-
- # @Override
- def Run(self, arg_line):
- """Gets a new password."""
- new_password = getpass.getpass()
- if new_password:
- self.console.password.value = new_password
- logging.info("new password set.")
- else:
- logging.warn("password is not updated.")
-
- def Help(self):
- base_command_processor.BaseCommandProcessor.Help(self)
diff --git a/harnesses/host_controller/command_processor/command_release.py b/harnesses/host_controller/command_processor/command_release.py
deleted file mode 100644
index 71eccbf..0000000
--- a/harnesses/host_controller/command_processor/command_release.py
+++ /dev/null
@@ -1,267 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import datetime
-import logging
-import stat
-import threading
-import zipfile
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-
-_REPACKAGE_ADDITIONAL_FILE_LIST = [
- "android-vtslab/testcases/DATA/app/WifiUtil/WifiUtil.apk",
- "android-vtslab/testcases/DATA/vtslab-gcs.json",
- "android-vtslab/testcases/DATA/xml/media_profiles_vendor.xml",
- "android-vtslab/testcases/host_controller/build/client_secrets.json",
- "android-vtslab/testcases/host_controller/build/credentials",
-]
-
-_REPACKAGE_ADDITIONAL_BIN_LIST = [
- "android-vtslab/bin/adb",
-]
-
-# Path to the version.txt file in the fetched vtslab package zip.
-_VERSION_INFO_FILE_PATH = "android-vtslab/testcases/version.txt"
-
-# List of strings for supported ak versions.
-AK_VERSIONS = ["8.0.0", "8.0.1", "8.1.0", "9", "O", "OMR1", "P", "Q"]
-
-for version in AK_VERSIONS:
- file_path = "android-vtslab/testcases/DATA/ak/.%s.ak" % version
- _REPACKAGE_ADDITIONAL_FILE_LIST.append(file_path)
- file_path += ".pub"
- _REPACKAGE_ADDITIONAL_FILE_LIST.append(file_path)
-
-
-class CommandRelease(base_command_processor.BaseCommandProcessor):
- """Command processor for update command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- _timers: dict, instances of scheduled threading.Timer.
- Uses timestamp("%H:%M") string as a key.
- _vtslab_package_version: string, version information of the fetched
- vtslab package.
- (<git commit timestamp>:<git commit hash value>)
- """
-
- command = "release"
- command_detail = "Release HC. Used for fetching HC package from PAB and uploading to GCS."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for update command."""
- self._timers = {}
- self._vtslab_package_version = ""
- self.arg_parser.add_argument(
- "--schedule-for",
- default="17:00",
- help="Schedule to update HC package at the given time every day. "
- "Example: --schedule-for=%%H:%%M")
- self.arg_parser.add_argument(
- "--account_id",
- default=common._DEFAULT_ACCOUNT_ID,
- help="Partner Android Build account_id to use.")
- self.arg_parser.add_argument(
- "--branch", help="Branch to grab the artifact from.")
- self.arg_parser.add_argument(
- "--target",
- help="a comma-separate list of build target product(s).")
- self.arg_parser.add_argument(
- "--dest",
- help="Google Cloud Storage URL to which the file is uploaded.")
- self.arg_parser.add_argument(
- "--cancel", help="Cancel all scheduled release if given.")
- self.arg_parser.add_argument(
- "--print-all", help="Print all scheduled timers.")
- self.arg_parser.add_argument(
- "--additional_files_bucket",
- default="gs://vtslab-release",
- help="GCS bucket URL from where to fetch the additional files "
- "required for HC to run properly.")
-
- # @Override
- def Run(self, arg_line):
- """Schedule a host_constroller package release at a certain time."""
- args = self.arg_parser.ParseLine(arg_line)
-
- if args.print_all:
- logging.info(self._timers)
- return
-
- if not args.cancel:
- if args.schedule_for == "now":
- self.ReleaseCallback(args.schedule_for, args.account_id,
- args.branch, args.target, args.dest,
- args.additional_files_bucket)
- return
-
- elif len(args.schedule_for.split(":")) != 2:
- logging.error("The format of --schedule-for flag is %H:%M")
- return False
-
- if (int(args.schedule_for.split(":")[0]) not in range(24)
- or int(args.schedule_for.split(":")[-1]) not in range(60)):
- logging.error("The value of --schedule-for flag must be in "
- "\"00:00\"..\"23:59\" inclusive")
- return False
-
- if not args.schedule_for in self._timers:
- delta_time = datetime.datetime.now().replace(
- hour=int(args.schedule_for.split(":")[0]),
- minute=int(args.schedule_for.split(":")[-1]),
- second=0,
- microsecond=0) - datetime.datetime.now()
-
- if delta_time <= datetime.timedelta(0):
- delta_time += datetime.timedelta(days=1)
-
- self._timers[args.schedule_for] = threading.Timer(
- delta_time.total_seconds(), self.ReleaseCallback,
- (args.schedule_for, args.account_id, args.branch,
- args.target, args.dest, args.additional_files_bucket))
- self._timers[args.schedule_for].daemon = True
- self._timers[args.schedule_for].start()
- logging.info("Release job scheduled for {}".format(
- datetime.datetime.now() + delta_time))
- else:
- self.CancelAllEvents()
-
- def FetchVtslab(self, account_id, branch, target, bucket):
- """Fetchs android-vtslab.zip and return the fetched file path.
-
- Args:
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- bucket: string, GCS bucket URL from where to fetch the additional
- files.
-
- Returns:
- path to the fetched android-vtslab.zip file. None if the fetching
- has failed.
- """
- self.console.build_provider["pab"].Authenticate()
- fetched_path = self.console.build_provider[
- "pab"].FetchLatestBuiltHCPackage(account_id, branch, target)
-
- with zipfile.ZipFile(fetched_path, mode="a") as vtslab_package:
- if _VERSION_INFO_FILE_PATH in vtslab_package.namelist():
- self._vtslab_package_version = vtslab_package.open(
- _VERSION_INFO_FILE_PATH).readline().strip()
- else:
- self._vtslab_package_version = ""
-
- for path in _REPACKAGE_ADDITIONAL_FILE_LIST:
- additional_file = os.path.join(bucket, path)
- self.console.build_provider["gcs"].Fetch(additional_file)
- try:
- logging.info("Adding file %s into %s" %
- (os.path.basename(path),
- os.path.basename(fetched_path)))
- additional_file_path = self.console.build_provider[
- "gcs"].GetAdditionalFile(os.path.basename(path))
- vtslab_package.write(additional_file_path, path)
- except KeyError as e:
- logging.exception(e)
-
- for bin in _REPACKAGE_ADDITIONAL_BIN_LIST:
- additional_bin = os.path.join(bucket, bin)
- self.console.build_provider["gcs"].Fetch(additional_bin)
- try:
- logging.info("Adding executable %s into %s" %
- (os.path.basename(bin),
- os.path.basename(fetched_path)))
- additional_bin_path = self.console.build_provider[
- "gcs"].GetAdditionalFile(os.path.basename(bin))
- bin_mode = os.stat(additional_bin_path).st_mode
- os.chmod(
- additional_bin_path,
- bin_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
- vtslab_package.write(additional_bin_path, bin)
- except KeyError as e:
- logging.exception(e)
-
- return fetched_path
-
- def UploadVtslab(self, package_file_path, dest_path):
- """upload repackaged vtslab package to GCS.
-
- Args:
- package_file_path: string, path to the vtslab package file.
- dest_path: string, URL to GCS.
- """
- if dest_path and dest_path.endswith("/"):
- split_list = os.path.basename(package_file_path).split(".")
- if self._vtslab_package_version:
- try:
- timestamp, hash = self._vtslab_package_version.split(":")
- split_list[0] += "-%s-%s" % (timestamp, hash)
- except ValueError as e:
- logging.exception(e)
- split_list[0] += "-{timestamp_date}"
- else:
- split_list[0] += "-{timestamp_date}"
- dest_path += ".".join(split_list)
-
- upload_command = "upload --src %s --dest %s" % (package_file_path,
- dest_path)
- self.console.onecmd(upload_command)
-
- def ReleaseCallback(self, schedule_for, account_id, branch, target, dest,
- bucket):
- """Target function for the scheduled Timer.
-
- Args:
- schedule_for: string, scheduled time for this Timer.
- Format: "%H:%M" (from "00:00" to "23:59" inclusive)
- account_id: string, Partner Android Build account_id to use.
- branch: string, branch to grab the artifact from.
- targets: string, a comma-separate list of build target product(s).
- dest: string, URL to GCS.
- bucket: string, GCS bucket URL from where to fetch the additional
- files.
- """
- fetched_path = self.FetchVtslab(account_id, branch, target, bucket)
- if fetched_path:
- self.UploadVtslab(fetched_path, dest)
-
- if schedule_for != "now":
- delta_time = datetime.datetime.now().replace(
- hour=int(schedule_for.split(":")[0]),
- minute=int(schedule_for.split(":")[-1]),
- second=0,
- microsecond=0) - datetime.datetime.now() + datetime.timedelta(
- days=1)
- self._timers[schedule_for] = threading.Timer(
- delta_time.total_seconds(), self.ReleaseCallback,
- (schedule_for, account_id, branch, target, dest, bucket))
- self._timers[schedule_for].daemon = True
- self._timers[schedule_for].start()
- logging.info("Release job scheduled for {}".format(
- datetime.datetime.now() + delta_time))
-
- def CancelAllEvents(self):
- """Cancel all scheduled Timer."""
- for scheduled_time in self._timers:
- self._timers[scheduled_time].cancel()
- self._timers = {}
diff --git a/harnesses/host_controller/command_processor/command_repack.py b/harnesses/host_controller/command_processor/command_repack.py
deleted file mode 100644
index 98a9168..0000000
--- a/harnesses/host_controller/command_processor/command_repack.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import re
-import shutil
-import tempfile
-import zipfile
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-
-# Name of android-info.txt file which contains prerequisite data for the img.zip
-_ANDROID_INFO_TXT_FILENAME = "android-info.txt"
-
-
-class CommandRepack(base_command_processor.BaseCommandProcessor):
- """Command processor for repack command."""
-
- command = "repack"
- command_detail = ("Repackage the whole device image files, including GSI "
- "images if exist.")
-
- # @Override
- def SetUp(self):
- """Initializes the parser for repack command."""
- self.arg_parser.add_argument(
- "--dest",
- default="gs://vts-release/img_package",
- help="Google Cloud Storage base URL to which the file is uploaded."
- )
- self.arg_parser.add_argument(
- "--additional_files",
- nargs="*",
- default=[],
- help="Additional files that need to be added to the zip file.")
-
- # @Override
- def Run(self, arg_line):
- """Runs an repack command."""
- args = self.arg_parser.ParseLine(arg_line)
- try:
- device_zipfile_path = self.console.device_image_info[
- common.FULL_ZIPFILE]
- except KeyError as e:
- logging.exception(e)
- logging.error(
- "please execute this command after fetching at least one "
- "device img set.")
- return False
-
- with zipfile.ZipFile(device_zipfile_path, 'r') as zip_ref:
- zip_ref.extract(_ANDROID_INFO_TXT_FILENAME,
- common.FULL_ZIPFILE_DIR)
- self.console.tools_info[_ANDROID_INFO_TXT_FILENAME] = os.path.join(
- common.FULL_ZIPFILE_DIR, _ANDROID_INFO_TXT_FILENAME)
-
- tempdir_base = os.path.join(os.getcwd(), "tmp")
- tmpdir_rezip = tempfile.mkdtemp(dir=tempdir_base)
-
- dest_url_base, new_zipfile_name = os.path.split(
- self.GetDestURL(args.dest))
-
- new_zipfile_path = os.path.join(tmpdir_rezip, new_zipfile_name)
- with zipfile.ZipFile(
- new_zipfile_path, "w", allowZip64=True) as zip_ref:
- for img_path in self.console.device_image_info:
- if img_path not in (common.FULL_ZIPFILE,
- common.FULL_ZIPFILE_DIR,
- common.GSI_ZIPFILE,
- common.GSI_ZIPFILE_DIR):
- logging.info("Adding %s into the zip archive.", img_path)
- zip_ref.write(
- self.console.device_image_info[img_path],
- img_path,
- compress_type=zipfile.ZIP_DEFLATED)
- if args.additional_files:
- additional_file_list = self.ReplaceVars(args.additional_files)
- for file_path in additional_file_list:
- file_name = os.path.basename(file_path)
- logging.info(
- "Adding additional file %s into the zip archive.",
- file_name)
- zip_ref.write(
- file_path,
- os.path.join(common._ADDITIONAL_FILES_DIR, file_name),
- compress_type=zipfile.ZIP_DEFLATED)
- zip_ref.write(
- self.console.tools_info[_ANDROID_INFO_TXT_FILENAME],
- _ANDROID_INFO_TXT_FILENAME,
- compress_type=zipfile.ZIP_DEFLATED)
-
- logging.info("Repackaged image set: %s", new_zipfile_path)
- logging.info("Uploading %s to %s.", new_zipfile_name, dest_url_base)
-
- self.console.onecmd("upload --src=%s --dest=%s/%s" %
- (new_zipfile_path, dest_url_base,
- new_zipfile_name))
-
- shutil.rmtree(tmpdir_rezip, ignore_errors=True)
-
- def GetDestURL(self, dest_base_url):
- """Generates the destination URL to GCS bucket based on dest_base_url.
-
- Args:
- dest_base_url: string, URL to a GCS bucket.
-
- Returns:
- A string, device/gsi img sets branch/target info and the final
- .zip file name appended to the dest_base_url.
- """
- device_branch = re.sub(
- "git_", "", self.console.detailed_fetch_info["device"]["branch"])
- device_target = self.console.detailed_fetch_info["device"]["target"]
- device_build_id = self.console.detailed_fetch_info["device"][
- "build_id"]
- new_zipfile_name = ("%s_%s_%s.zip" % (device_branch, device_target,
- device_build_id))
- dest_url_base = os.path.join(dest_base_url, device_branch,
- device_target)
-
- if common.GSI_ZIPFILE in self.console.device_image_info:
- gsi_branch = re.sub(
- "git_", "", self.console.detailed_fetch_info["gsi"]["branch"])
- gsi_target = self.console.detailed_fetch_info["gsi"]["target"]
- gsi_build_id = self.console.detailed_fetch_info["gsi"]["build_id"]
- new_zipfile_name = new_zipfile_name[:-4] + ("_%s_%s_%s.zip" % (
- gsi_branch, gsi_target, gsi_build_id))
- dest_url_base = os.path.join(dest_url_base, gsi_branch, gsi_target)
-
- ret = os.path.join(dest_url_base, new_zipfile_name)
- self.console.repack_dest_path = ret
- return ret
diff --git a/harnesses/host_controller/command_processor/command_repack_test.py b/harnesses/host_controller/command_processor/command_repack_test.py
deleted file mode 100644
index 6eca960..0000000
--- a/harnesses/host_controller/command_processor/command_repack_test.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_repack
-
-_SAMPLE_COMMAND_REPACK = "repack --dest"
-
-
-class CommandRepackTest(unittest.TestCase):
- """Tests for repack command processor"""
-
- @mock.patch("host_controller.console.Console")
- def testGetDestURLWithoutGSI(self, mock_console):
- mock_console.detailed_fetch_info = {
- "device": {
- "branch": "git_device_branch",
- "target": "device_userdebug",
- "build_id": "1234567"
- }
- }
- command = command_repack.CommandRepack()
- command._SetUp(mock_console)
- ret = command.GetDestURL("gs://bucket/base/url")
-
- self.assertEqual(ret,
- "gs://bucket/base/url/device_branch/device_userdebug"
- "/device_branch_device_userdebug_1234567.zip")
-
- @mock.patch("host_controller.console.Console")
- def testGetDestURLWithGSI(self, mock_console):
- mock_console.device_image_info = {common.GSI_ZIPFILE: ""}
- mock_console.detailed_fetch_info = {
- "device": {
- "branch": "git_device_branch",
- "target": "device-userdebug",
- "build_id": "1234567"
- },
- "gsi": {
- "branch": "git_aosp_gsi_branch",
- "target": "gsi-userdebug",
- "build_id": "2345678"
- }
- }
-
- command = command_repack.CommandRepack()
- command._SetUp(mock_console)
- ret = command.GetDestURL("gs://bucket/base/url")
-
- self.assertEqual(
- ret, "gs://bucket/base/url/device_branch/device-userdebug/"
- "aosp_gsi_branch/gsi-userdebug/device_branch_device-userdebug"
- "_1234567_aosp_gsi_branch_gsi-userdebug_2345678.zip")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_repack.logging")
- def testRepackFullDeviceImageAbsent(self, mock_logger, mock_console):
- mock_console.device_image_info = {}
- command = command_repack.CommandRepack()
- command._SetUp(mock_console)
- ret = command._Run("--dest=gs://bucket/path/")
-
- self.assertFalse(ret)
- mock_logger.error.assert_called_with(
- "please execute this command after fetching at least"
- " one device img set.")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_repack.zipfile")
- @mock.patch("host_controller.command_processor.command_repack.os")
- @mock.patch("host_controller.command_processor.command_repack.tempfile")
- @mock.patch("host_controller.command_processor.command_repack.shutil")
- def testRepackWithFullDeviceImage(self, mock_shutil, mock_tempfile,
- mock_os, mock_zipfile, mock_console):
- mock_zip_ref = mock.Mock()
- mock_zip_ref.__enter__ = mock.Mock(return_value=mock_zip_ref)
- mock_zip_ref.__exit__ = mock.Mock(return_value=None)
- mock_zipfile.ZipFile.return_value = mock_zip_ref
- mock_console.device_image_info = {
- common.FULL_ZIPFILE: "/path/to/full-zipfile.zip",
- "system.img": "/path/to/full-zipfile.zip.dir/system.img",
- "odm.img": "/path/to/full-zipfile.zip.dir/odm.img"
- }
- mock_os.getcwd.return_value = "/abs/path/to/current/dir"
- mock_os.path.split = os.path.split
- command = command_repack.CommandRepack()
- command.GetDestURL = mock.Mock(
- return_value="gs://dest/gs/url/file.zip")
- command._SetUp(mock_console)
- ret = command._Run("--dest=gs://bucket/path/")
-
- self.assertIsNone(ret)
- mock_zip_ref.extract.assert_called_with("android-info.txt",
- common.FULL_ZIPFILE_DIR)
- mock_zip_ref.write.assert_any_call(
- "/path/to/full-zipfile.zip.dir/system.img",
- "system.img",
- compress_type=mock.ANY)
- mock_zip_ref.write.assert_any_call(
- "/path/to/full-zipfile.zip.dir/odm.img",
- "odm.img",
- compress_type=mock.ANY)
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_repack.zipfile")
- @mock.patch("host_controller.command_processor.command_repack.os")
- @mock.patch("host_controller.command_processor.command_repack.tempfile")
- @mock.patch("host_controller.command_processor.command_repack.shutil")
- def testRepackWithAdditionalFiles(self, mock_shutil, mock_tempfile,
- mock_os, mock_zipfile, mock_console):
- mock_zip_ref = mock.Mock()
- mock_zip_ref.__enter__ = mock.Mock(return_value=mock_zip_ref)
- mock_zip_ref.__exit__ = mock.Mock(return_value=None)
- mock_zipfile.ZipFile.return_value = mock_zip_ref
- mock_console.device_image_info = {
- common.FULL_ZIPFILE: "/path/to/full-zipfile.zip",
- "system.img": "/path/to/full-zipfile.zip.dir/system.img",
- "odm.img": "/path/to/full-zipfile.zip.dir/odm.img"
- }
- mock_os.getcwd.return_value = "/abs/path/to/current/dir"
- mock_os.path.split = os.path.split
- mock_os.path.join = os.path.join
- mock_os.path.basename = os.path.basename
- command = command_repack.CommandRepack()
- command.GetDestURL = mock.Mock(
- return_value="gs://dest/gs/url/file.zip")
- command._SetUp(mock_console)
- ret = command._Run(
- "--dest=gs://bucket/path/ --additional_files /path/to/tmp/af1"
- " /path/to/tmp/af2")
-
- self.assertIsNone(ret)
- mock_zip_ref.extract.assert_called_with("android-info.txt",
- common.FULL_ZIPFILE_DIR)
- mock_zip_ref.write.assert_any_call(
- "/path/to/full-zipfile.zip.dir/system.img",
- "system.img",
- compress_type=mock.ANY)
- mock_zip_ref.write.assert_any_call(
- "/path/to/full-zipfile.zip.dir/odm.img",
- "odm.img",
- compress_type=mock.ANY)
- mock_zip_ref.write.assert_any_call(
- "/path/to/tmp/af1", "additional_file/af1", compress_type=mock.ANY)
- mock_zip_ref.write.assert_any_call(
- "/path/to/tmp/af2", "additional_file/af2", compress_type=mock.ANY)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/command_processor/command_reproduce.py b/harnesses/host_controller/command_processor/command_reproduce.py
deleted file mode 100644
index 7d4f68f..0000000
--- a/harnesses/host_controller/command_processor/command_reproduce.py
+++ /dev/null
@@ -1,301 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import imp # Python v2 compatibility
-import logging
-import os
-import re
-import subprocess
-import zipfile
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.gcp import gcs_utils
-
-from vti.dashboard.proto import TestSuiteResultMessage_pb2 as SuiteResMsg
-from vti.test_serving.proto import TestScheduleConfigMessage_pb2 as SchedCfgMsg
-
-
-class CommandReproduce(base_command_processor.BaseCommandProcessor):
- """Command processor for reproduce command.
-
- Attributes:
- campaign_common: campaign module. Dynamically imported since
- the campaign might need to be separated from the
- host controller itself.
- """
-
- command = "reproduce"
- command_detail = ("Reproduce the test environment for a pre-run test and "
- "execute the tradefed command prompt of the fetched "
- "test suite. Setup the test env "
- "(fetching, flashing devices, etc) and retrieve "
- "formerly run test result to retry on, if the path "
- "to the report protobuf file is given.")
-
- # @Override
- def SetUp(self):
- """Initializes the parser for reproduce command."""
- self.campaign_common = None
- self.arg_parser.add_argument(
- "--suite",
- default="vts",
- choices=("vts", "cts", "gts", "sts"),
- help="To specify the type of a test suite to be run.")
- self.arg_parser.add_argument(
- "--report_path",
- required=True,
- help="Google Cloud Storage URL, the path of a report protobuf file."
- )
- self.arg_parser.add_argument(
- "--serial",
- default=None,
- help="The serial numbers for flashing and testing. "
- "Multiple serial numbers are separated by commas.")
- self.arg_parser.add_argument(
- "--automated_retry",
- action="store_true",
- help="Retries automatically until all test cases are passed "
- "or the number or the failed test cases is the same as "
- "the previous one.")
-
- # @Override
- def Run(self, arg_line):
- """Reproduces the test env of the pre-run test."""
- args = self.arg_parser.ParseLine(arg_line)
-
- if args.report_path:
- gsutil_path = gcs_utils.GetGsutilPath()
- if not gsutil_path:
- logging.error(
- "Please check whether gsutil is installed and on your PATH"
- )
- return False
-
- if (not args.report_path.startswith("gs://")
- or not gcs_utils.IsGcsFile(gsutil_path, args.report_path)):
- logging.error("%s is not a valid GCS path.", args.report_path)
- return False
-
- dest_path = os.path.join("".join(self.ReplaceVars(["{tmp_dir}"])),
- os.path.basename(args.report_path))
- gcs_utils.Copy(gsutil_path, args.report_path, dest_path)
- report_msg = SuiteResMsg.TestSuiteResultMessage()
- try:
- with open(dest_path, "r") as report_fd:
- report_msg.ParseFromString(report_fd.read())
- except IOError as e:
- logging.exception(e)
- return False
- serial = []
- if args.serial:
- serial = args.serial.split(",")
- setup_command_list = self.GenerateSetupCommands(report_msg, serial)
- if not setup_command_list:
- suite_fetch_command = self.GenerateTestSuiteFetchCommand(
- report_msg)
- if suite_fetch_command:
- setup_command_list.append(suite_fetch_command)
- for command in setup_command_list:
- self.console.onecmd(command)
-
- if not self.GetResultFromGCS(gsutil_path, report_msg, args.suite):
- return False
- else:
- logging.error("Path to a report protobuf file is required.")
- return False
-
- if args.suite not in self.console.test_suite_info:
- logging.error("test_suite_info doesn't have '%s': %s", args.suite,
- self.console.test_suite_info)
- return False
-
- if args.automated_retry:
- if self.campaign_common is None:
- self.campaign_common = imp.load_source(
- 'campaign_common',
- os.path.join(os.getcwd(), "host_controller", "campaigns",
- "campaign_common.py"))
- retry_command = self.campaign_common.GenerateRetryCommand(
- report_msg.schedule_config.build_target[0].name,
- report_msg.branch, report_msg.suite_name.lower(),
- report_msg.suite_plan, serial)
- self.console.onecmd(retry_command)
- else:
- subprocess.call(self.console.test_suite_info[args.suite])
-
- def GenerateSetupCommands(self, report_msg, serial):
- """Generates fetch, flash commands using fetch info from report_msg.
-
- Args:
- report_msg: pb2, contains fetch info of the test suite.
- serial: list of string, serial number(s) of the device(s)
- to be flashed.
-
- Returns:
- list of string, console commands to fetch device/gsi images
- and flash the device(s).
- """
- ret = []
- schedule_config = report_msg.schedule_config
- if not schedule_config.manifest_branch:
- logging.error("Report contains no fetch information. "
- "Aborting pre-test setups on the device(s).")
- elif not serial:
- logging.error("Device serial number(s) not given. "
- "Aborting pre-test setups on the device(s).")
- else:
- try:
- build_target_msg = schedule_config.build_target[0]
- test_schedule_msg = build_target_msg.test_schedule[0]
- except IndexError as e:
- logging.exception(e)
- return ret
- kwargs = {}
- # common fetch info
- kwargs["shards"] = str(len(serial))
- kwargs["test_name"] = "%s/%s" % (report_msg.suite_name.lower(),
- report_msg.suite_plan)
- kwargs["serial"] = serial
-
- # fetch info for device images
- kwargs["manifest_branch"] = schedule_config.manifest_branch
- kwargs["build_target"] = build_target_msg.name
- kwargs["build_id"] = report_msg.vendor_build_id
- kwargs["pab_account_id"] = schedule_config.pab_account_id
- if kwargs["manifest_branch"].startswith("gs://"):
- kwargs[
- "build_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_GCS
- else:
- kwargs[
- "build_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_PAB
- kwargs["require_signed_device_build"] = (
- build_target_msg.require_signed_device_build)
- kwargs["has_bootloader_img"] = build_target_msg.has_bootloader_img
- kwargs["has_radio_img"] = build_target_msg.has_radio_img
-
- # fetch info for gsi images and gsispl command
- kwargs["gsi_branch"] = test_schedule_msg.gsi_branch
- kwargs["gsi_build_target"] = test_schedule_msg.gsi_build_target
- kwargs["gsi_build_id"] = report_msg.gsi_build_id
- kwargs["gsi_pab_account_id"] = test_schedule_msg.gsi_pab_account_id
- if kwargs["gsi_branch"].startswith("gs://"):
- kwargs["gsi_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_GCS
- else:
- kwargs["gsi_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_PAB
- kwargs["gsi_vendor_version"] = test_schedule_msg.gsi_vendor_version
-
- # fetch info for test suite
- kwargs["test_build_id"] = report_msg.build_id
- kwargs["test_branch"] = report_msg.branch
- kwargs["test_build_target"] = report_msg.target
- if kwargs["test_build_target"].endswith(".zip"):
- kwargs["test_build_target"] = kwargs["test_build_target"][:-4]
- kwargs[
- "test_pab_account_id"] = test_schedule_msg.test_pab_account_id
- if kwargs["test_branch"].startswith("gs://"):
- kwargs[
- "test_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_GCS
- else:
- kwargs["gsi_storage_type"] = SchedCfgMsg.BUILD_STORAGE_TYPE_PAB
-
- self.campaign_common = imp.load_source(
- "campaign_common",
- os.path.join(os.getcwd(), "host_controller", "campaigns",
- "campaign_common.py"))
- fetch_commands_result, gsi = self.campaign_common.EmitFetchCommands(
- **kwargs)
- ret.extend(fetch_commands_result)
- flash_commands_result = self.campaign_common.EmitFlashCommands(
- gsi, **kwargs)
- ret.extend(flash_commands_result)
-
- return ret
-
- def GenerateTestSuiteFetchCommand(self, report_msg):
- """Generates a fetch command line using fetch info from report_msg.
-
- Args:
- report_msg: pb2, contains fetch info of the test suite.
-
- Returns:
- string, console command to fetch a test suite artifact.
- """
- ret = "fetch"
-
- if report_msg.branch.startswith("gs://"):
- ret += " --type=gcs --path=%s/%s --set_suite_as=%s" % (
- report_msg.branch, report_msg.target,
- report_msg.suite_name.lower())
- else:
- ret += (" --type=pab --branch=%s --target=%s --build_id=%s"
- " --artifact_name=android-%s.zip") % (
- report_msg.branch, report_msg.target,
- report_msg.build_id, report_msg.suite_name.lower())
- try:
- build_target_msg = report_msg.schedule_config.build_target[0]
- test_schedule_msg = build_target_msg.test_schedule[0]
- except IndexError as e:
- logging.exception(e)
- test_schedule_msg = SchedCfgMsg.TestScheduleConfigMessage()
- if test_schedule_msg.test_pab_account_id:
- ret += " --account_id=%s" % test_schedule_msg.test_pab_account_id
- else:
- ret += " --account_id=%s" % common._DEFAULT_ACCOUNT_ID_INTERNAL
-
- return ret
-
- def GetResultFromGCS(self, gsutil_path, report_msg, suite):
- """Downloads results.zip from GCS and unzip it to the results directory.
-
- Args:
- gsutil_path: string, path to the gsutil binary.
- report_msg: pb2, contains fetch info of the test suite.
- suite: string, specifies the type of test suite fetched.
-
- Returns:
- True if successful. False otherwise
- """
- result_base_path = report_msg.result_path
- try:
- tools_path = os.path.dirname(self.console.test_suite_info[suite])
- except KeyError as e:
- logging.exception(e)
- return False
- local_results_path = os.path.join(tools_path,
- common._RESULTS_BASE_PATH)
-
- if not os.path.exists(local_results_path):
- os.mkdir(local_results_path)
-
- result_path_list = gcs_utils.List(gsutil_path, result_base_path)
- result_zip_url = ""
- for result_path in result_path_list:
- if re.match(".*results_.*\.zip", result_path):
- result_zip_url = result_path
- break
-
- if (not result_zip_url or not gcs_utils.Copy(
- gsutil_path, result_zip_url, local_results_path)):
- logging.error("Fail to copy from %s.", result_base_path)
- return False
-
- result_zip_local_path = os.path.join(local_results_path,
- os.path.basename(result_zip_url))
- with zipfile.ZipFile(result_zip_local_path, mode="r") as zip_ref:
- zip_ref.extractall(local_results_path)
-
- return True
diff --git a/harnesses/host_controller/command_processor/command_reproduce_test.py b/harnesses/host_controller/command_processor/command_reproduce_test.py
deleted file mode 100644
index a5ab4f6..0000000
--- a/harnesses/host_controller/command_processor/command_reproduce_test.py
+++ /dev/null
@@ -1,353 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_reproduce
-
-
-def emit_fetch_commands(**kwargs):
- gsi = "gsi_branch" in kwargs
- return [], gsi
-
-
-def emit_flash_commands(gsi, **kwargs):
- return []
-
-
-class CommandReproduceTest(unittest.TestCase):
- """Tests for reproduce command processor"""
-
- def setUp(self):
- mock_console = mock.Mock()
- self._command = command_reproduce.CommandReproduce()
- self._command._SetUp(mock_console)
-
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testGenerateSetupCommandsNoFetchInfo(self, mock_logging):
- mock_msg = mock.Mock()
- mock_msg.schedule_config.manifest_branch = ""
- ret = self._command.GenerateSetupCommands(mock_msg,
- ["serial1", "serial2"])
- self.assertEqual(ret, [])
- mock_logging.error.assert_called_with(
- "Report contains no fetch information. "
- "Aborting pre-test setups on the device(s).")
-
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testGenerateSetupCommandsNoSerial(self, mock_logging):
- mock_msg = mock.Mock()
- mock_msg.schedule_config.manifest_branch = "some_branch"
- ret = self._command.GenerateSetupCommands(mock_msg, [])
- self.assertEqual(ret, [])
- mock_logging.error.assert_called_with(
- "Device serial number(s) not given. "
- "Aborting pre-test setups on the device(s).")
-
- @mock.patch("host_controller.command_processor.command_reproduce.imp")
- def testGenerateSetupCommands(self, mock_imp):
- mock_campiagn = mock.Mock()
- mock_campiagn.EmitFetchCommands.side_effect = emit_fetch_commands
- mock_campiagn.EmitFlashCommands.side_effect = emit_flash_commands
- mock_imp.load_source.return_value = mock_campiagn
- mock_msg = mock.Mock()
- mock_msg.suite_name = "vts"
- mock_msg.suite_plan = "vts"
- mock_msg.vendor_build_id = "1234567"
- mock_msg.gsi_build_id = "2345678"
- mock_msg.build_id = "3456789"
- mock_msg.branch = "git_whatever-release"
- mock_msg.target = "test_suites_bitness"
- mock_msg.schedule_config.manifest_branch = "git_whatever-release"
- mock_msg.schedule_config.pab_account_id = common._DEFAULT_ACCOUNT_ID_INTERNAL
- mock_msg.schedule_config.build_target = []
- mock_build_target_msg = mock.Mock()
- mock_build_target_msg.name = "somefish-userdebug"
- mock_build_target_msg.test_schedule = []
- mock_build_target_msg.require_signed_device_build = False
- mock_build_target_msg.has_bootloader_img = True
- mock_build_target_msg.has_radio_img = True
- mock_test_schedule_msg = mock.Mock()
- mock_test_schedule_msg.gsi_branch = "git_whatever-gsi"
- mock_test_schedule_msg.gsi_build_target = "aosp_bitness-userdebug"
- mock_test_schedule_msg.gsi_pab_account_id = common._DEFAULT_ACCOUNT_ID
- mock_test_schedule_msg.test_pab_account_id = common._DEFAULT_ACCOUNT_ID
- mock_build_target_msg.test_schedule.append(mock_test_schedule_msg)
- mock_msg.schedule_config.build_target.append(mock_build_target_msg)
- self._command.GenerateSetupCommands(mock_msg, ["serial1", "serial2"])
- mock_campiagn.EmitFetchCommands.assert_called_with(
- build_id="1234567",
- build_storage_type=1,
- build_target="somefish-userdebug",
- gsi_branch="git_whatever-gsi",
- gsi_build_id="2345678",
- gsi_build_target="aosp_bitness-userdebug",
- gsi_pab_account_id="543365459",
- gsi_storage_type=1,
- gsi_vendor_version=mock.ANY,
- has_bootloader_img=True,
- has_radio_img=True,
- manifest_branch="git_whatever-release",
- pab_account_id="541462473",
- require_signed_device_build=False,
- serial=["serial1", "serial2"],
- shards="2",
- test_branch="git_whatever-release",
- test_build_id="3456789",
- test_build_target="test_suites_bitness",
- test_name="vts/vts",
- test_pab_account_id="543365459")
- mock_campiagn.EmitFlashCommands.assert_called_with(
- True,
- build_id="1234567",
- build_storage_type=1,
- build_target="somefish-userdebug",
- gsi_branch="git_whatever-gsi",
- gsi_build_id="2345678",
- gsi_build_target="aosp_bitness-userdebug",
- gsi_pab_account_id="543365459",
- gsi_storage_type=1,
- gsi_vendor_version=mock.ANY,
- has_bootloader_img=True,
- has_radio_img=True,
- manifest_branch="git_whatever-release",
- pab_account_id="541462473",
- require_signed_device_build=False,
- serial=["serial1", "serial2"],
- shards="2",
- test_branch="git_whatever-release",
- test_build_id="3456789",
- test_build_target="test_suites_bitness",
- test_name="vts/vts",
- test_pab_account_id="543365459")
-
- def testGenerateTestSuiteFetchCommandGCS(self):
- report_msg = mock.Mock()
- report_msg.branch = "gs://bucket/path/to/vts/release"
- report_msg.target = "android-vts.zip"
- report_msg.suite_name = "VTS"
- ret = self._command.GenerateTestSuiteFetchCommand(report_msg)
- self.assertEqual(
- ret, "fetch --type=gcs "
- "--path=gs://bucket/path/to/vts/release/android-vts.zip "
- "--set_suite_as=vts")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.SchedCfgMsg")
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testGenerateTestSuiteFetchCommandIndexError(self, mock_logging,
- mock_sched_cfg_msg):
- report_msg = mock.Mock()
- report_msg.branch = "git_whatever-release"
- report_msg.target = "test_suites_bitness"
- report_msg.build_id = "1234567"
- report_msg.suite_name = "VTS"
- report_msg.schedule_config.build_target = []
- mock_test_schedule_msg = mock.Mock()
- mock_test_schedule_msg.test_pab_account_id = "1234567898765"
- mock_sched_cfg_msg.TestScheduleConfigMessage.return_value = (
- mock_test_schedule_msg)
- ret = self._command.GenerateTestSuiteFetchCommand(report_msg)
- mock_logging.exception.assert_called()
- self.assertEqual(ret, "fetch --type=pab --branch=git_whatever-release "
- "--target=test_suites_bitness --build_id=1234567 "
- "--artifact_name=android-vts.zip "
- "--account_id=1234567898765")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.SchedCfgMsg")
- def testGenerateTestSuiteFetchCommandPAB(self, mock_sched_cfg_msg):
- report_msg = mock.Mock()
- report_msg.branch = "git_whatever-release"
- report_msg.target = "test_suites_bitness"
- report_msg.build_id = "1234567"
- report_msg.suite_name = "VTS"
- mock_build_target_msg = mock.Mock()
- mock_test_schedule_msg = mock.Mock()
- mock_test_schedule_msg.test_pab_account_id = "987654321"
- mock_build_target_msg.test_schedule = []
- mock_build_target_msg.test_schedule.append(mock_test_schedule_msg)
- report_msg.schedule_config.build_target = []
- report_msg.schedule_config.build_target.append(mock_build_target_msg)
- mock_sched_cfg_msg.TestScheduleConfigMessage.return_value = (
- mock_test_schedule_msg)
-
- ret = self._command.GenerateTestSuiteFetchCommand(report_msg)
- self.assertEqual(ret, "fetch --type=pab --branch=git_whatever-release "
- "--target=test_suites_bitness --build_id=1234567 "
- "--artifact_name=android-vts.zip "
- "--account_id=987654321")
-
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testGetResultFromGCSNoTestSuite(self, mock_logging):
- report_msg = mock.Mock()
- report_msg.result_path = "gs://bucket/path/to/log/files"
- self._command.console.test_suite_info = {}
- ret = self._command.GetResultFromGCS("/mock_bin/gsutil", report_msg,
- "vts")
- self.assertFalse(ret)
- mock_logging.exception.assert_called()
-
- @mock.patch("host_controller.command_processor.command_reproduce.os")
- def testGetResultFromGCSMkdirResults(self, mock_os):
- report_msg = mock.Mock()
- report_msg.result_path = "gs://bucket/path/to/log/files"
- self._command.console.test_suite_info = {
- "vts": "tmp/android-vts/tools/vts-tradefed"
- }
- mock_os.path.exists.return_value = False
- mock_os.path.join = os.path.join
- mock_os.path.dirname = os.path.dirname
- self._command.GetResultFromGCS("/mock_bin/gsutil", report_msg, "vts")
- mock_os.mkdir.assert_called_with("tmp/android-vts/tools/../results")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch("host_controller.command_processor.command_reproduce.os")
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testGetResultFromGCSNoResult(self, mock_logging, mock_os,
- mock_gcs_util):
- report_msg = mock.Mock()
- report_msg.result_path = "gs://bucket/path/to/log/files"
- self._command.console.test_suite_info = {
- "vts": "tmp/android-vts/tools/vts-tradefed"
- }
- mock_gcs_util.List.return_value = [
- "some_log1.zip", "some_log2.zip", "not_a_result.zip"
- ]
- ret = self._command.GetResultFromGCS("/mock_bin/gsutil", report_msg,
- "vts")
- self.assertFalse(ret)
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch("host_controller.command_processor.command_reproduce.os")
- @mock.patch("host_controller.command_processor.command_reproduce.zipfile")
- def testGetResultFromGCS(self, mock_zipfile, mock_os, mock_gcs_util):
- report_msg = mock.Mock()
- report_msg.result_path = "gs://bucket/path/to/log/files"
- self._command.console.test_suite_info = {
- "vts": "tmp/android-vts/tools/vts-tradefed"
- }
- mock_zip_ref = mock.Mock()
- mock_zip_ref.__enter__ = mock.Mock(return_value=mock_zip_ref)
- mock_zip_ref.__exit__ = mock.Mock(return_value=None)
- mock_zipfile.ZipFile.return_value = mock_zip_ref
- mock_gcs_util.List.return_value = [
- "some_log1.zip", "some_log2.zip", "results_some_hash.zip"
- ]
- mock_gcs_util.Copy.return_value = True
- mock_os.path.join = os.path.join
- mock_os.path.exists.return_value = False
- mock_os.path.dirname = os.path.dirname
- ret = self._command.GetResultFromGCS("/mock_bin/gsutil", report_msg,
- "vts")
- self.assertTrue(ret)
- mock_zip_ref.extractall.assert_called_with(
- "tmp/android-vts/tools/../results")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testCommandReproduceGsutilAbsent(self, mock_logging, mock_gcs_util):
- mock_gcs_util.GetGsutilPath.return_value = ""
- ret = self._command._Run(
- "--report_path=gs://bucket/path/to/report/file")
- self.assertFalse(ret)
- mock_logging.error.assert_called_with(
- "Please check whether gsutil is installed and on your PATH")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch("host_controller.command_processor.command_reproduce.logging")
- def testCommandReproduceInvalidURL(self, mock_logging, mock_gcs_util):
- mock_gcs_util.GetGsutilPath.return_value = "/mock_bin/gsutil"
- ret = self._command._Run("--report_path=/some/path/to/report/file")
- self.assertFalse(ret)
- mock_logging.error.assert_called_with("%s is not a valid GCS path.",
- "/some/path/to/report/file")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch(
- "host_controller.command_processor.command_reproduce.open",
- new_callable=mock.mock_open)
- @mock.patch("host_controller.command_processor.command_reproduce.imp")
- def testCommandReproduceAutomatedRetry(self, mock_imp, mock_open,
- mock_gcs_util):
- mock_gcs_util.GetGsutilPath.return_value = "/mock_bin/gsutil"
- mock_gcs_util.IsGcsFile.return_value = True
- mock_gcs_util.Copy = mock.Mock(return_value=True)
- command_reproduce.SuiteResMsg.ParseFromString = mock.Mock(
- return_value={})
- self._command.console.test_suite_info = {
- "vts": "tmp/android-vts/tools/vts-tradefed"
- }
- mock_campiagn = mock.Mock()
- mock_imp.load_source.return_value = mock_campiagn
- self._command.ReplaceVars = mock.Mock(return_value="tmp")
- self._command.GetResultFromGCS = mock.Mock(return_value=True)
- self._command.GenerateSetupCommands = mock.Mock(return_value=[])
- self._command.GenerateTestSuiteFetchCommand = mock.Mock(
- return_value=[])
- ret = self._command._Run("--report_path=gs://some/path/to/report/file"
- " --serial=device1 --automated_retry")
- self.assertIsNone(ret)
- self._command.console.onecmd.assert_called_with(
- "retry --suite vts --serial device1")
-
- @mock.patch(
- "host_controller.command_processor.command_reproduce.gcs_utils")
- @mock.patch(
- "host_controller.command_processor.command_reproduce.open",
- new_callable=mock.mock_open)
- @mock.patch("host_controller.command_processor.command_reproduce.imp")
- def testCommandReproduceAutomatedRetryShardOption(
- self, mock_imp, mock_open, mock_gcs_util):
- mock_gcs_util.GetGsutilPath.return_value = "/mock_bin/gsutil"
- mock_gcs_util.IsGcsFile.return_value = True
- mock_gcs_util.Copy = mock.Mock(return_value=True)
- command_reproduce.SuiteResMsg.ParseFromString = mock.Mock()
- self._command.console.test_suite_info = {
- "vts": "tmp/android-vts/tools/vts-tradefed"
- }
- mock_campiagn = mock.Mock()
- mock_campiagn.GetVersion.return_value = 8.0
- mock_imp.load_source.return_value = mock_campiagn
- self._command.ReplaceVars = mock.Mock(return_value="tmp")
- self._command.GetResultFromGCS = mock.Mock(return_value=True)
- self._command.GenerateSetupCommands = mock.Mock(return_value=[])
- self._command.GenerateTestSuiteFetchCommand = mock.Mock(
- return_value=[])
- ret = self._command._Run(
- "--suite=vts --report_path=gs://some/path/to/report/file"
- " --serial=device1,device2 --automated_retry")
- self.assertIsNone(ret)
- self._command.console.onecmd.assert_called_with(
- "retry --suite vts --shards 2 "
- "--serial device1 --serial device2")
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/command_processor/command_request.py b/harnesses/host_controller/command_processor/command_request.py
deleted file mode 100644
index 3851823..0000000
--- a/harnesses/host_controller/command_processor/command_request.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.command_processor import base_command_processor
-from host_controller.tfc import request
-
-
-class CommandRequest(base_command_processor.BaseCommandProcessor):
- """Command processor for request command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "request"
- command_detail = "Send TFC a request to execute a command."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for request command."""
- self.arg_parser.add_argument(
- "--cluster",
- required=True,
- help="The cluster to which the request is submitted.")
- self.arg_parser.add_argument(
- "--run-target",
- required=True,
- help="The target device to run the command.")
- self.arg_parser.add_argument(
- "--user",
- required=True,
- help="The name of the user submitting the request.")
- self.arg_parser.add_argument(
- "command",
- metavar="COMMAND",
- nargs="+",
- help='The command to be executed. If the command contains '
- 'arguments starting with "-", place the command after '
- '"--" at end of line.')
-
- # @Override
- def Run(self, arg_line):
- """Sends TFC a request to execute a command."""
- args = self.arg_parser.ParseLine(arg_line)
- req = request.Request(
- cluster=args.cluster,
- command_line=" ".join(args.command),
- run_target=args.run_target,
- user=args.user)
- self.console._tfc_client.NewRequest(req)
diff --git a/harnesses/host_controller/command_processor/command_retry.py b/harnesses/host_controller/command_processor/command_retry.py
deleted file mode 100644
index 3108b4b..0000000
--- a/harnesses/host_controller/command_processor/command_retry.py
+++ /dev/null
@@ -1,301 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import itertools
-import logging
-import os
-import zipfile
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.gcp import gcs_utils
-from host_controller.utils.parser import xml_utils
-
-from vts.utils.python.common import cmd_utils
-
-# The command list for cleaning up each devices listed for the retry command.
-_DEVICE_CLEANUP_COMMAND_LIST = [
- "adb -s {serial} reboot bootloader",
- "fastboot -s {serial} erase metadata -- -w",
- "fastboot -s {serial} reboot",
- "adb -s {serial} wait-for-device",
- "dut --operation=wifi_on --serial={serial} --ap=" +
- common._DEFAULT_WIFI_AP,
-]
-
-
-class CommandRetry(base_command_processor.BaseCommandProcessor):
- """Command processor for retry command."""
-
- command = "retry"
- command_detail = "Retry last run test plan for certain times."
-
- def IsResultZipFile(self, zip_ref):
- """Determines whether the given zip_ref is the right result archive.
-
- Need to check the number of contents of the zip file since
- the "log-result_<>.zip" file only contains "test_result.xml",
- but cannot parsed by vts-tf properly when trying to do the retry.
-
- Args:
- zip_ref: ZipFile, reference to the downloaded results_<>.zip file
-
- Returns:
- True if the downloaded zip file is usable from vts-fs,
- False otherwise.
- """
- if len(zip_ref.namelist()) > 1:
- for name in zip_ref.namelist():
- if common._TEST_RESULT_XML in name:
- return True
- return False
-
- def GetResultFromGCS(self, gcs_result_path, local_results_dir):
- """Downloads a vts-tf result zip archive from GCS.
-
- And unzip the file to "android-vts/results/" path so
- the vts-tf will parse the result correctly.
-
- Args:
- gcs_result_path: string, path to GCS file.
- local_results_dir: string, abs path to the result directory of
- currently running vts-tf.
- Returns:
- A string which is the name of unzipped result directory.
- None if the download has failed or the downloaded zip file
- is not a correct result archive.
- """
- gsutil_path = gcs_utils.GetGsutilPath()
- if not gsutil_path:
- logging.error("Please check gsutil is installed and on your PATH")
- return None
-
- if (not gcs_result_path.startswith("gs://")
- or not gcs_utils.IsGcsFile(gsutil_path, gcs_result_path)):
- logging.error("%s is not correct GCS url.", gcs_result_path)
- return None
- if not gcs_result_path.endswith(".zip"):
- logging.error("%s is not a correct result archive file.",
- gcs_result_path)
- return None
-
- if not os.path.exists(local_results_dir):
- os.mkdir(local_results_dir)
- if not gcs_utils.Copy(gsutil_path, gcs_result_path, local_results_dir):
- logging.error("Fail to copy from %s.", gcs_result_path)
- return None
- result_zip = os.path.join(local_results_dir,
- gcs_result_path.split("/")[-1])
- with zipfile.ZipFile(result_zip, mode="r") as zip_ref:
- if self.IsResultZipFile(zip_ref):
- unzipped_result_dir = zip_ref.namelist()[0].rstrip("/")
- zip_ref.extractall(local_results_dir)
- return unzipped_result_dir
- else:
- logging.error("Not a correct vts-tf result archive file.")
- return None
-
- # @Override
- def SetUp(self):
- """Initializes the parser for retry command."""
- self.arg_parser.add_argument(
- "--suite",
- default="vts",
- choices=("vts", "cts", "gts", "sts"),
- help="To specify the type of a test suite to be run.")
- self.arg_parser.add_argument(
- "--count",
- type=int,
- default=common.DEFAULT_RETRY_COUNT,
- help="Retry count. Default retry count is %s." %
- common.DEFAULT_RETRY_COUNT)
- self.arg_parser.add_argument(
- "--force-count",
- type=int,
- default=3,
- help="Forced retry count. Retry certain test plan for the given "
- "times whether all testcases has passed or not.")
- self.arg_parser.add_argument(
- "--result-from-gcs",
- help="Google Cloud Storage URL from which the result is downloaded. "
- "Will retry based on the fetched result data")
- self.arg_parser.add_argument(
- "--serial",
- action="append",
- default=[],
- help="Serial number for device. Can pass this flag multiple times."
- )
- self.arg_parser.add_argument(
- "--shards", type=int, help="Test plan's shard count.")
- self.arg_parser.add_argument(
- "--shard-count",
- type=int,
- help=
- "Test plan's shard count. Same as the \"--shards\" flag but the "
- "value will be passed to the tradefed with \"--shard-count\" flag."
- )
- self.arg_parser.add_argument(
- "--cleanup_devices",
- default=False,
- type=bool,
- help="True to erase metadata and userdata (equivalent to "
- "factory reset) between retries.")
- self.arg_parser.add_argument(
- "--retry_plan", help="The name of a retry plan to use.")
-
- # @Override
- def Run(self, arg_line):
- """Retry last run plan for certain times."""
- args = self.arg_parser.ParseLine(arg_line)
- retry_count = args.count
- force_retry_count = args.force_count
-
- if args.suite not in self.console.test_suite_info:
- logging.error("test_suite_info doesn't have '%s': %s", args.suite,
- self.console.test_suite_info)
- return False
-
- tools_path = os.path.dirname(self.console.test_suite_info[args.suite])
- results_path = os.path.join(tools_path, common._RESULTS_BASE_PATH)
-
- unzipped_result_dir = ""
- unzipped_result_session_id = -1
- if args.result_from_gcs:
- unzipped_result_dir = self.GetResultFromGCS(
- args.result_from_gcs, results_path)
- if not unzipped_result_dir:
- return False
-
- former_results = [
- result for result in os.listdir(results_path)
- if os.path.isdir(os.path.join(results_path, result))
- and not os.path.islink(os.path.join(results_path, result))
- ]
- former_result_count = len(former_results)
- if former_result_count < 1:
- logging.error(
- "No test plan has been run yet, former results count is %d",
- former_result_count)
- return False
-
- if unzipped_result_dir:
- former_results.sort()
- unzipped_result_session_id = former_results.index(
- unzipped_result_dir)
-
- for result_index in range(retry_count):
- if unzipped_result_session_id >= 0:
- session_id = unzipped_result_session_id
- unzipped_result_session_id = -1
- latest_result_xml_path = os.path.join(
- results_path, unzipped_result_dir, common._TEST_RESULT_XML)
- else:
- session_id = former_result_count - 1 + result_index
- latest_result_xml_path = os.path.join(results_path, "latest",
- common._TEST_RESULT_XML)
- if not os.path.exists(latest_result_xml_path):
- latest_result_xml_path = os.path.join(
- results_path, former_results[-1],
- common._TEST_RESULT_XML)
-
- result_attrs = xml_utils.GetAttributes(
- latest_result_xml_path, common._RESULT_TAG,
- [common._SUITE_PLAN_ATTR_KEY])
-
- summary_attrs = xml_utils.GetAttributes(
- latest_result_xml_path, common._SUMMARY_TAG, [
- common._FAILED_ATTR_KEY, common._MODULES_TOTAL_ATTR_KEY,
- common._MODULES_DONE_ATTR_KEY
- ])
-
- result_fail_count = int(summary_attrs[common._FAILED_ATTR_KEY])
- result_skip_count = int(
- summary_attrs[common._MODULES_TOTAL_ATTR_KEY]) - int(
- summary_attrs[common._MODULES_DONE_ATTR_KEY])
-
- if (result_index >= force_retry_count and result_skip_count == 0
- and result_fail_count == 0):
- logging.info("All modules have run and passed. "
- "Skipping remaining %d retry runs.",
- (retry_count - result_index))
- break
-
- shard_flag_literal = ""
- if args.shards:
- shard_flag_literal = "--shards"
- shard_num = args.shards
- if args.shard_count:
- shard_flag_literal = "--shard-count"
- shard_num = args.shard_count
-
- if args.retry_plan:
- retry_plan = args.retry_plan
- else:
- retry_plan = result_attrs[common._SUITE_PLAN_ATTR_KEY]
- if shard_flag_literal:
- retry_test_command = (
- "test --suite=%s --keep-result -- %s --retry %d %s %d" %
- (args.suite, retry_plan, session_id, shard_flag_literal,
- shard_num))
- else:
- retry_test_command = (
- "test --suite=%s --keep-result -- %s --retry %d" %
- (args.suite, retry_plan, session_id))
- if args.serial:
- for serial in args.serial:
- retry_test_command += " --serial %s" % serial
-
- if args.cleanup_devices:
- for (command, serial) in itertools.product(
- _DEVICE_CLEANUP_COMMAND_LIST, args.serial):
- if not self.console.onecmd(command.format(serial=serial)):
- logging.error(
- "Factory reset failed on the devices %s. "
- "Skipping retry run(s)", serial)
- self.console.device_status[
- serial] = common._DEVICE_STATUS_DICT["use"]
- return
-
- self.console.onecmd(retry_test_command)
-
- for result in os.listdir(results_path):
- new_result = os.path.join(results_path, result)
- if (os.path.isdir(new_result)
- and not os.path.islink(new_result)
- and result not in former_results):
- former_results.append(result)
- break
-
- summary_after_retry = xml_utils.GetAttributes(
- os.path.join(results_path, former_results[-1],
- common._TEST_RESULT_XML), common._SUMMARY_TAG,
- [
- common._FAILED_ATTR_KEY, common._MODULES_TOTAL_ATTR_KEY,
- common._MODULES_DONE_ATTR_KEY
- ])
- fail_count_after_retry = int(
- summary_after_retry[common._FAILED_ATTR_KEY])
- skip_count_after_retry = int(
- summary_after_retry[common._MODULES_TOTAL_ATTR_KEY]) - int(
- summary_after_retry[common._MODULES_DONE_ATTR_KEY])
- if (result_index >= force_retry_count
- and fail_count_after_retry == result_fail_count
- and skip_count_after_retry == result_skip_count):
- logging.warning(
- "Same result as the former test run from the retry run. "
- "Skipping remaining %d retry runs.",
- (retry_count - result_index))
- break
diff --git a/harnesses/host_controller/command_processor/command_sheet.py b/harnesses/host_controller/command_processor/command_sheet.py
deleted file mode 100644
index 090f707..0000000
--- a/harnesses/host_controller/command_processor/command_sheet.py
+++ /dev/null
@@ -1,421 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import csv
-import os
-import logging
-import re
-import shutil
-import tempfile
-import zipfile
-
-try:
- # TODO: Remove when we stop supporting Python 2
- import StringIO as string_io_module
-except ImportError:
- import io as string_io_module
-
-import gspread
-
-from oauth2client.service_account import ServiceAccountCredentials
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.gcp import gcs_utils
-from host_controller.utils.parser import result_utils
-from host_controller.utils.parser import xml_utils
-
-# Attributes shown on spreadsheet
-_RESULT_ATTR_KEYS = [
- common._SUITE_NAME_ATTR_KEY, common._SUITE_PLAN_ATTR_KEY,
- common._SUITE_VERSION_ATTR_KEY, common._SUITE_BUILD_NUM_ATTR_KEY,
- common._START_DISPLAY_TIME_ATTR_KEY,
- common._END_DISPLAY_TIME_ATTR_KEY
-]
-
-_BUILD_ATTR_KEYS = [
- common._FINGERPRINT_ATTR_KEY,
- common._SYSTEM_FINGERPRINT_ATTR_KEY,
- common._VENDOR_FINGERPRINT_ATTR_KEY
-]
-
-_SUMMARY_ATTR_KEYS = [
- common._PASSED_ATTR_KEY, common._FAILED_ATTR_KEY,
- common._MODULES_TOTAL_ATTR_KEY, common._MODULES_DONE_ATTR_KEY
-]
-
-# Texts on spreadsheet
-_TABLE_HEADER = ("BITNESS", "TEST_MODULE", "TEST_CLASS", "TEST_CASE", "RESULT")
-
-_CMP_TABLE_HEADER = _TABLE_HEADER + ("REFERENCE_RESULT",)
-
-_TOO_MANY_DATA = "too many to be displayed"
-
-
-class CommandSheet(base_command_processor.BaseCommandProcessor):
- """Command processor for sheet command.
-
- Attributes:
- _SCOPE: The scope needed to access Google Sheets.
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
- _SCOPE = "https://www.googleapis.com/auth/drive"
- command = "sheet"
- command_detail = "Convert and upload a file to Google Sheets."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for sheet command."""
- self.arg_parser.add_argument(
- "--src",
- required=True,
- help="The local file or GCS URL to be uploaded to Google Sheets. "
- "This command supports the results produced by TradeFed in XML "
- "and ZIP formats. Variables enclosed in {} are replaced with the "
- "the values stored in the console.")
- self.arg_parser.add_argument(
- "--dest",
- required=True,
- help="The ID of the spreadsheet to which the file is uploaded.")
- self.arg_parser.add_argument(
- "--ref",
- default=None,
- help="The reference file to be compared with src. If a test in "
- "src fails, its result in ref is also written to the spreadsheet.")
- self.arg_parser.add_argument(
- "--extra_rows",
- nargs="*",
- default=[],
- help="The extra rows written to the spreadsheet. Each argument "
- "is a row. Cells in a row are separated by commas. Each cell can "
- "contain variables enclosed in {}.")
- self.arg_parser.add_argument(
- "--max",
- default=30000,
- type=int,
- help="Maximum number of results written to the spreadsheet. "
- "If there are too many results, only failing ones are written.")
- self.arg_parser.add_argument(
- "--primary_abi_only",
- action="store_true",
- help="Whether to upload only the test results for primary ABI. If "
- "ref is also specified, this command loads the primary ABI "
- "results from ref and compares regardless of bitness.")
- self.arg_parser.add_argument(
- "--client_secrets",
- default=None,
- help="The path to the client secrets file in JSON format for "
- "authentication. If this argument is not specified, this command "
- "uses PAB client secrets.")
-
- # @Override
- def Run(self, arg_line):
- """Uploads args.src file to args.dest on Google Sheets."""
- args = self.arg_parser.ParseLine(arg_line)
-
- try:
- src_path = self.console.FormatString(args.src)
- ref_path = (None if args.ref is None else
- self.console.FormatString(args.ref))
- extra_rows = []
- for row in args.extra_rows:
- extra_rows.append([self.console.FormatString(cell)
- for cell in row.split(",")])
- except KeyError as e:
- logging.error(
- "Unknown or uninitialized variable in arguments: %s", e)
- return False
-
- if args.client_secrets is not None:
- credentials = ServiceAccountCredentials.from_json_keyfile_name(
- args.client_secrets, scopes=self._SCOPE)
- else:
- credentials = self.console.build_provider["pab"].Authenticate(
- scopes=self._SCOPE)
- client = gspread.authorize(credentials)
-
- # Load result_attrs, build_attrs, summary_attrs,
- # src_dict, ref_dict, and exceed_max
- temp_dir = tempfile.mkdtemp()
- try:
- src_path = _GetResultAsXml(src_path, os.path.join(temp_dir, "src"))
- if not src_path:
- return False
-
- with open(src_path, "r") as src_file:
- (result_attrs,
- build_attrs,
- summary_attrs) = result_utils.LoadTestSummary(src_file)
- src_file.seek(0)
- if args.primary_abi_only:
- abis = build_attrs.get(
- common._ABIS_ATTR_KEY, "").split(",")
- src_bitness = str(result_utils.GetAbiBitness(abis[0]))
- src_dict, exceed_max = _LoadSrcResults(src_file, args.max,
- src_bitness)
- else:
- src_dict, exceed_max = _LoadSrcResults(src_file, args.max)
-
- if ref_path:
- ref_path = _GetResultAsXml(
- ref_path, os.path.join(temp_dir, "ref"))
- if not ref_path:
- return False
- with open(ref_path, "r") as ref_file:
- if args.primary_abi_only:
- ref_build_attrs = xml_utils.GetAttributes(
- ref_file, common._BUILD_TAG,
- (common._ABIS_ATTR_KEY, ))
- ref_file.seek(0)
- abis = ref_build_attrs[
- common._ABIS_ATTR_KEY].split(",")
- ref_bitness = str(result_utils.GetAbiBitness(abis[0]))
- ref_dict = _LoadRefResults(ref_file, src_dict,
- ref_bitness, src_bitness)
- else:
- ref_dict = _LoadRefResults(ref_file, src_dict)
- finally:
- shutil.rmtree(temp_dir)
-
- # Output
- csv_file = string_io_module.StringIO()
- try:
- writer = csv.writer(csv_file, lineterminator="\n")
-
- writer.writerows(extra_rows)
-
- for keys, attrs in (
- (_RESULT_ATTR_KEYS, result_attrs),
- (_BUILD_ATTR_KEYS, build_attrs),
- (_SUMMARY_ATTR_KEYS, summary_attrs)):
- writer.writerows((k, attrs.get(k, "")) for k in keys)
-
- src_list = sorted(src_dict.items())
- if ref_path:
- _WriteComparisonToCsv(src_list, ref_dict, writer)
- else:
- _WriteResultsToCsv(src_list, writer)
-
- if exceed_max:
- writer.writerow((_TOO_MANY_DATA,))
-
- client.import_csv(args.dest, csv_file.getvalue())
- finally:
- csv_file.close()
-
-
-def _DownloadResultZipFromGcs(gcs_url, local_dir):
- """Downloads a result ZIP from GCS.
-
- If the GCS URL is a directory, this function searches the directory for the
- file whose name matches the pattern of result ZIP.
-
- Args:
- gcs_url: The URL of the ZIP file or the directory.
- local_dir: The local directory where the ZIP is downloaded.
-
- Returns:
- The path to the downloaded ZIP file.
- None if fail to download.
- """
- gsutil_path = gcs_utils.GetGsutilPath()
- if not gsutil_path:
- return False
-
- if gcs_utils.IsGcsFile(gsutil_path, gcs_url):
- gcs_urls = [gcs_url]
- else:
- ls_urls = gcs_utils.List(gsutil_path, gcs_url)
- gcs_urls = [x for x in ls_urls if
- re.match(".+/results_\\d*\\.zip$", x)]
- if not gcs_urls:
- gcs_urls = [x for x in ls_urls if
- re.match(".+/log-result_\\d*\\.zip$", x)]
-
- if not gcs_urls:
- logging.error("No results on %s", gcs_url)
- return None
- if len(gcs_urls) > 1:
- logging.warning("More than one result. Select %s", gcs_urls[0])
-
- if not os.path.exists(local_dir):
- os.makedirs(local_dir)
- if not gcs_utils.Copy(gsutil_path, gcs_urls[0], local_dir):
- logging.error("Fail to copy from %s", gcs_urls[0])
- return None
-
- return os.path.join(local_dir, gcs_urls[0].rpartition("/")[2])
-
-
-def _GetResultAsXml(src, temp_dir):
- """Downloads and extracts an XML result.
-
- If src is a GCS URL, it is downloaded to temp_dir.
- If the file is a ZIP, it is extracted to temp_dir.
-
- Args:
- src: The location of the file, can be a directory on GCS,
- a ZIP file on GCS, a local ZIP file, or a local XML file.
- temp_dir: The directory where the ZIP is downloaded and extracted.
-
- Returns:
- The path to the XML file.
- None if fails to the find the XML.
- """
- original_src = src
- if src.startswith("gs://"):
- src = _DownloadResultZipFromGcs(src, os.path.join(temp_dir, "zipped"))
- if not src:
- return None
-
- if zipfile.is_zipfile(src):
- with zipfile.ZipFile(src, mode="r") as zip_file:
- src = result_utils.ExtractResultZip(
- zip_file, os.path.join(temp_dir, "unzipped"))
- if not src:
- logging.error("Cannot find XML result in %s", original_src)
- return None
-
- return src
-
-
-def _FilterTestResults(xml_file, max_return, filter_func):
- """Loads test results from XML to dictionary with a filter.
-
- Args:
- xml_file: The input file object in XML format.
- max_return: Maximum number of output results.
- filter_func: A function taking the test name and result as parameters,
- and returning whether it should be included.
-
- Returns:
- A dict of {name: result} where name is a tuple of strings and result
- is a string.
- """
- result_dict = dict()
- for module, testcase, test in result_utils.IterateTestResults(xml_file):
- if len(result_dict) >= max_return:
- break
- test_name = result_utils.GetTestName(module, testcase, test)
- result = test.attrib.get(common._RESULT_ATTR_KEY, "")
- if filter_func(test_name, result):
- result_dict[test_name] = result
-
- return result_dict
-
-
-def _LoadSrcResults(src_xml, max_return, bitness=""):
- """Loads test results from XML to dictionary.
-
- If number of results exceeds max_return, only failures are returned.
- If number of failures exceeds max_return, the results are truncated.
-
- Args
- src_xml: The file object in XML format.
- max_return: Maximum number of returned results.
- bitness: A string, the bitness of the returned results.
-
- Returns:
- A dict of {name: result} and a boolean which represents whether the
- results are truncated.
- """
- def FilterBitness(name):
- return not bitness or bitness == name[0]
-
- results = _FilterTestResults(
- src_xml, max_return + 1, lambda name, result: FilterBitness(name))
-
- if len(results) > max_return:
- src_xml.seek(0)
- results = _FilterTestResults(
- src_xml, max_return + 1,
- lambda name, result: result == "fail" and FilterBitness(name))
-
- exceed_max = len(results) > max_return
- if results and exceed_max:
- del results[max(results)]
-
- return results, exceed_max
-
-
-def _LoadRefResults(ref_xml, base_results, ref_bitness="", base_bitness=""):
- """Loads reference results from XML to dictionary.
-
- A test result in ref_xml is returned if the test fails in base_results.
-
- Args:
- ref_xml: The file object in XML format.
- base_results: A dict of {name: result} containing the test names to be
- loaded from ref_xml.
- ref_bitness: A string, the bitness of the results to be loaded from
- ref_xml.
- base_bitness: A string, the bitness of the returned results. If this
- argument is specified, the function ignores bitness when
- comparing test names.
-
- Returns:
- A dict of {name: result}, the test name in base_results and the result
- in ref_xml.
- """
- ref_results = dict()
- for module, testcase, test in result_utils.IterateTestResults(ref_xml):
- if len(ref_results) >= len(base_results):
- break
- result = test.attrib.get(common._RESULT_ATTR_KEY, "")
- name = result_utils.GetTestName(module, testcase, test)
-
- if ref_bitness and name[0] != ref_bitness:
- continue
- if base_bitness:
- name_in_base = (base_bitness, ) + name[1:]
- else:
- name_in_base = name
-
- if base_results.get(name_in_base, "") == "fail":
- ref_results[name_in_base] = result
-
- return ref_results
-
-
-def _WriteResultsToCsv(result_list, writer):
- """Writes a list of test names and results to a CSV file.
-
- Args:
- result_list: The list of (name, result).
- writer: The object of CSV writer.
- """
- writer.writerow(_TABLE_HEADER)
- writer.writerows(name + (result,) for name, result in result_list)
-
-
-def _WriteComparisonToCsv(result_list, reference_dict, writer):
- """Writes test names, results, and reference results to a CSV file.
-
- Args:
- result_list: The list of (name, result).
- reference_dict: The dict of {name: reference_result}.
- writer: The object of CSV writer.
- """
- writer.writerow(_CMP_TABLE_HEADER)
- for name, result in result_list:
- if result == "fail":
- reference = reference_dict.get(name, "no_data")
- else:
- reference = ""
- writer.writerow(name + (result, reference))
diff --git a/harnesses/host_controller/command_processor/command_sheet_test.py b/harnesses/host_controller/command_processor/command_sheet_test.py
deleted file mode 100644
index 1c61349..0000000
--- a/harnesses/host_controller/command_processor/command_sheet_test.py
+++ /dev/null
@@ -1,328 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import tempfile
-import unittest
-import zipfile
-
-try:
- from unittest import mock
-except ImportError:
- # TODO: Remove when we stop supporting Python 2
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_sheet
-
-# Input
-_EXTRA_ROWS = ("logs,gs://a/b", "logs,gs://c/{suite_plan}")
-
-_XML_HEAD = """\
-<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
-<?xml-stylesheet type="text/xsl" href="compatibility_result.xsl"?>
-<Result start="1528285394762" end="1528286467560" start_display="Wed Jun 06 04:43:14 PDT 2018" end_display="Wed Jun 06 05:01:07 PDT 2018" suite_name="VTS" suite_version="9.0_R1" suite_plan="vts" suite_build_number="4000000" report_version="5.0" command_line_args="vts" devices="TEST123456" host_name="unit.test" os_name="Linux" os_version="4.13.0-41-generic" os_arch="amd64" java_vendor="Oracle Corporation" java_version="1.8.0_171">
- <Build build_abis_64="arm64-v8a" build_manufacturer="Google" build_reference_fingerprint2="" build_board="unit_test" build_serial="TEST123456" build_system_fingerprint="system_fp" build_reference_fingerprint="null" build_fingerprint="vendor_fp" build_abis="arm64-v8a,armeabi-v7a,armeabi" build_device="unit_test" build_abi="arm64-v8a" build_abi2="null" build_version_release="9" build_version_base_os="" build_type="userdebug" build_vendor_model="Unit Test" build_abis_32="armeabi-v7a,armeabi" build_product="unit_test" build_brand="google" build_abi22="" build_version_security_patch="2018-06-05" build_vendor_manufacturer="Google" build_version_sdk="28" build_id="test_build" build_model="Unit Test" build_vendor_fingerprint="vendor_fp" build_version_incremental="4000000" build_fingerprint2="system_fp" build_tags="test-keys" />
-"""
-
-_XML_1 = _XML_HEAD + """\
- <Summary pass="1" failed="3" modules_done="2" modules_total="2" />
- <Module name="module1" abi="armeabi-v7a" runtime="1" done="true" pass="0">
- <TestCase name="testcase1">
- <Test result="fail" name="test1" />
- </TestCase>
- </Module>
- <Module name="module2" abi="arm64-v8a" runtime="1" done="true" pass="1">
- <TestCase name="testcase2">
- <Test result="pass" name="test1" />
- <Test result="fail" name="test2" />
- <Test result="fail" name="test3" />
- </TestCase>
- </Module>
-</Result>
-"""
-
-_XML_2 = _XML_HEAD + """\
- <Summary pass="3" failed="1" modules_done="2" modules_total="2" />
- <Module name="module1" abi="armeabi-v7a" runtime="1" done="true" pass="1">
- <TestCase name="testcase1">
- <Test result="pass" name="test1,test2" />
- </TestCase>
- </Module>
- <Module name="module2" abi="arm64-v8a" runtime="1" done="true" pass="2">
- <TestCase name="testcase2">
- <Test result="pass" name="test1" />
- <Test result="pass" name="test2" />
- <Test result="fail" name="test3" />
- </TestCase>
- </Module>
-</Result>
-"""
-
-# Expected output
-_CSV_HEAD = """\
-logs,gs://a/b
-logs,gs://c/vts
-suite_name,VTS
-suite_plan,vts
-suite_version,9.0_R1
-suite_build_number,4000000
-start_display,Wed Jun 06 04:43:14 PDT 2018
-end_display,Wed Jun 06 05:01:07 PDT 2018
-build_fingerprint,vendor_fp
-build_system_fingerprint,system_fp
-build_vendor_fingerprint,vendor_fp
-"""
-
-_ALL_RESULTS_1 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT
-32,module1,testcase1,test1,fail
-64,module2,testcase2,test1,pass
-64,module2,testcase2,test2,fail
-64,module2,testcase2,test3,fail
-"""
-
-_FAILING_RESULTS_1 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT
-32,module1,testcase1,test1,fail
-64,module2,testcase2,test2,fail
-64,module2,testcase2,test3,fail
-"""
-
-_TRUNCATED_RESULTS_1 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT
-32,module1,testcase1,test1,fail
-too many to be displayed
-"""
-
-_PRIMARY_ABI_RESULTS_1 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT
-64,module2,testcase2,test1,pass
-64,module2,testcase2,test2,fail
-64,module2,testcase2,test3,fail
-"""
-
-_COMPARISON_1_2 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT,REFERENCE_RESULT
-32,module1,testcase1,test1,fail,no_data
-64,module2,testcase2,test2,fail,pass
-64,module2,testcase2,test3,fail,fail
-"""
-
-_PRIMARY_ABI_COMPARISON_1_2 = _CSV_HEAD + """\
-pass,1
-failed,3
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT,REFERENCE_RESULT
-64,module2,testcase2,test1,pass,
-64,module2,testcase2,test2,fail,pass
-64,module2,testcase2,test3,fail,fail
-"""
-
-_COMPARISON_2_1 = _CSV_HEAD + """\
-pass,3
-failed,1
-modules_total,2
-modules_done,2
-BITNESS,TEST_MODULE,TEST_CLASS,TEST_CASE,RESULT,REFERENCE_RESULT
-32,module1,testcase1,"test1,test2",pass,
-64,module2,testcase2,test1,pass,
-64,module2,testcase2,test2,pass,
-64,module2,testcase2,test3,fail,fail
-"""
-
-
-@mock.patch("host_controller.command_processor.command_sheet.gspread")
-@mock.patch("host_controller.command_processor.command_sheet."
- "ServiceAccountCredentials")
-class CommandSheetTest(unittest.TestCase):
- """Unit test for CommandSheet.
-
- Attributes:
- _cmd: The CommandSheet being tested.
- _temp_files: The paths to the temporary files.
- """
-
- def setUp(self):
- """Creates CommandSheet."""
- self._cmd = command_sheet.CommandSheet()
- mock_console = mock.Mock()
- mock_console.FormatString = lambda x: x.replace("{suite_plan}", "vts")
- self._cmd._SetUp(mock_console)
- self._temp_files = []
-
- def tearDown(self):
- """Deletes CommandSheet and temoprary files."""
- self._cmd._TearDown()
- for temp_file in self._temp_files:
- os.remove(temp_file)
-
- def _CreateXml(self, xml_string):
- """Creates a test result in XML format.
-
- Args:
- xml_string: The file contents.
-
- Returns:
- The path to the XML file.
- """
- with tempfile.NamedTemporaryFile(
- suffix=".xml", delete=False) as temp_file:
- self._temp_files.append(temp_file.name)
- temp_file.write(xml_string)
- return temp_file.name
-
- def _CreateZip(self, xml_string):
- """Creates a zipped test result.
-
- Args:
- xml_string: The file contents.
-
- Returns:
- The path to the ZIP file.
- """
- with tempfile.NamedTemporaryFile(
- suffix=".zip", delete=False) as temp_file:
- self._temp_files.append(temp_file.name)
- with zipfile.ZipFile(temp_file, "w") as zip_file:
- zip_file.writestr(common._TEST_RESULT_XML, xml_string)
- return temp_file.name
-
- def testUploadZip(self, mock_credentials, mock_gspread):
- """Tests uploading zipped result."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --dest 123 --client_secret /abc "
- "--extra_rows %s" %
- (self._CreateZip(_XML_1), " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _ALL_RESULTS_1)
-
- def testShowFailureOnly(self, mock_credentials, mock_gspread):
- """Tests showing only failing tests."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --dest 123 --client_secret /abc "
- "--extra_rows %s --max 3" %
- (self._CreateXml(_XML_1), " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _FAILING_RESULTS_1)
-
- def testTruncate(self, mock_credentials, mock_gspread):
- """Tests truncating output."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --dest 123 --client_secret /abc "
- "--extra_rows %s --max 1" %
- (self._CreateXml(_XML_1), " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _TRUNCATED_RESULTS_1)
-
- def testPrimaryAbiOnly(self, mock_credentials, mock_gspread):
- """Tests showing only results for primary ABI."""
- """Tests showing only failing tests."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --dest 123 --client_secret /abc "
- "--extra_rows %s --primary_abi_only" %
- (self._CreateXml(_XML_1), " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _PRIMARY_ABI_RESULTS_1)
-
- def testCompareLocal(self, mock_credentials, mock_gspread):
- """Tests comparing two local XML files."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --ref %s --dest 123 --client_secret /abc "
- "--extra_rows %s --max 3" %
- (self._CreateXml(_XML_1), self._CreateZip(_XML_2),
- " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _COMPARISON_1_2)
-
- def testComparePrimaryAbi(self, mock_credentials, mock_gspread):
- """Tests comparing primary ABI only."""
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --ref %s --dest 123 --client_secret /abc "
- "--extra_rows %s --primary_abi_only" %
- (self._CreateXml(_XML_1), self._CreateZip(_XML_2),
- " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123",
- _PRIMARY_ABI_COMPARISON_1_2)
-
- @mock.patch("host_controller.command_processor.command_sheet.gcs_utils")
- def testCompareGcs(self, mock_gcs_utils, mock_credentials, mock_gspread):
- """Tests comparing a local XML with a ZIP on GCS."""
-
- zip_name = "results_123.zip"
- gcs_dir = "gs://unit/test"
- zip_url = gcs_dir + "/" + zip_name
-
- def MockCopy(gsutil_path, gcs_url, local_dir):
- self.assertEqual(zip_url, gcs_url)
- with zipfile.ZipFile(os.path.join(local_dir, zip_name),
- "w") as zip_file:
- zip_file.writestr(common._TEST_RESULT_XML, _XML_1)
- return True
-
- mock_gcs_utils.GetGsutilPath.return_value = "mock_gsutil"
- mock_gcs_utils.IsGcsFile.return_value = False
- mock_gcs_utils.List.return_value = [zip_url]
- mock_gcs_utils.Copy = MockCopy
-
- mock_client = mock.Mock()
- mock_gspread.authorize.return_value = mock_client
-
- self._cmd.Run("--src %s --ref %s --dest 123 --client_secret /abc "
- "--extra_rows %s" %
- (self._CreateXml(_XML_2), gcs_dir,
- " ".join(_EXTRA_ROWS)))
-
- mock_client.import_csv.assert_called_with("123", _COMPARISON_2_1)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/command_processor/command_shell.py b/harnesses/host_controller/command_processor/command_shell.py
deleted file mode 100644
index abe3274..0000000
--- a/harnesses/host_controller/command_processor/command_shell.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from host_controller.command_processor import base_command_processor
-
-from vts.utils.python.common import cmd_utils
-
-
-class CommandShell(base_command_processor.BaseCommandProcessor):
- """Command processor for shell command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "shell"
- command_detail = "Runs a shell command on the host OS."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.arg_parser.add_argument(
- "command",
- metavar="COMMAND",
- nargs="+",
- help="The command to be executed. If the command contains "
- "arguments starting with \"-\", place the command at end of line "
- "after \"--\".")
-
- # @Override
- def Run(self, arg_line):
- """Runs a shell command."""
- args = self.arg_parser.ParseLine(arg_line)
- cmd_list = self.ReplaceVars(args.command)
- stdout, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- " ".join(cmd_list))
- if stdout:
- logging.info(stdout)
- if stderr:
- logging.error(stderr)
- if retcode != 0:
- return False
diff --git a/harnesses/host_controller/command_processor/command_sleep.py b/harnesses/host_controller/command_processor/command_sleep.py
deleted file mode 100644
index 9517496..0000000
--- a/harnesses/host_controller/command_processor/command_sleep.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import time
-
-from host_controller.command_processor import base_command_processor
-
-
-class CommandSleep(base_command_processor.BaseCommandProcessor):
- """Command processor for sleep command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "sleep"
- command_detail = "Sleeps for N seconds."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for device command."""
- self.arg_parser.add_argument(
- "seconds",
- metavar="COMMAND",
- nargs=1,
- help=("an integer indicating the amount of time to sleep "
- "in seconds"))
-
- # @Override
- def Run(self, arg_line):
- """Blocks for a specified time interval."""
- args = self.arg_parser.ParseLine(arg_line)
- try:
- time.sleep(int(args.seconds[0]))
- except ValueError as e:
- logging.exception(e)
- return False
diff --git a/harnesses/host_controller/command_processor/command_test.py b/harnesses/host_controller/command_processor/command_test.py
deleted file mode 100644
index 8e5adb7..0000000
--- a/harnesses/host_controller/command_processor/command_test.py
+++ /dev/null
@@ -1,226 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import shutil
-import subprocess
-import tempfile
-import threading
-import zipfile
-
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.parser import xml_utils
-from vts.runners.host import utils
-
-
-class CommandTest(base_command_processor.BaseCommandProcessor):
- """Command processor for test command.
-
- Attributes:
- _RESULT_ATTRIBUTES: The attributes of <Result> in the XML report.
- After test execution, the attributes are loaded
- from report to console's dictionary.
- _result_dir: the path to the temporary result directory.
- """
-
- command = "test"
- command_detail = "Executes a command on TF."
- _RESULT_TAG = "Result"
- _RESULT_ATTRIBUTES = ["suite_plan"]
-
- # @Override
- def SetUp(self):
- """Initializes the parser for test command."""
- self._result_dir = None
- self.arg_parser.add_argument(
- "--suite",
- default="vts",
- choices=("vts", "cts", "gts", "sts"),
- help="To specify the type of a test suite to be run.")
- self.arg_parser.add_argument(
- "--serial",
- "-s",
- default=None,
- help="The target device serial to run the command. "
- "A comma-separate list.")
- self.arg_parser.add_argument(
- "--test-exec-mode",
- default="subprocess",
- help="The target exec model.")
- self.arg_parser.add_argument(
- "--keep-result",
- action="store_true",
- help="Keep the path to the result in the console instance.")
- self.arg_parser.add_argument(
- "command",
- metavar="COMMAND",
- nargs="+",
- help="The command to be executed. If the command contains "
- "arguments starting with \"-\", place the command after "
- "\"--\" at end of line. format: plan -m module -t testcase")
-
- def _ClearResultDir(self):
- """Deletes all files in the result directory."""
- if self._result_dir is None:
- self._result_dir = tempfile.mkdtemp()
- return
-
- for file_name in os.listdir(self._result_dir):
- shutil.rmtree(os.path.join(self._result_dir, file_name))
-
- @staticmethod
- def _GenerateTestSuiteCommand(bin_path, command, serials, result_dir=None):
- """Generates a *ts-tradefed command.
-
- Args:
- bin_path: the path to *ts-tradefed.
- command: a list of strings, the command arguments.
- serials: a list of strings, the serial numbers of the devices.
- result_dir: the path to the temporary directory where the result is
- saved.
-
- Returns:
- a list of strings, the *ts-tradefed command.
- """
- cmd = [bin_path, "run", "commandAndExit"]
- cmd.extend(str(c) for c in command)
-
- for serial in serials:
- cmd.extend(["-s", str(serial)])
-
- if result_dir:
- cmd.extend(["--log-file-path", result_dir, "--use-log-saver"])
-
- return cmd
-
- @staticmethod
- def _ExecuteCommand(cmd):
- """Executes a command and logs output in real time.
-
- Args:
- cmd: a list of strings, the command to execute.
- """
-
- def LogOutputStream(log_level, stream):
- try:
- while True:
- line = stream.readline()
- if not line:
- break
- logging.log(log_level, line.rstrip())
- finally:
- stream.close()
-
- proc = subprocess.Popen(
- cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-
- out_thread = threading.Thread(
- target=LogOutputStream, args=(logging.INFO, proc.stdout))
- err_thread = threading.Thread(
- target=LogOutputStream, args=(logging.ERROR, proc.stderr))
- out_thread.daemon = True
- err_thread.daemon = True
- out_thread.start()
- err_thread.start()
- proc.wait()
- logging.info("Return code: %d", proc.returncode)
- proc.stdin.close()
- out_thread.join()
- err_thread.join()
-
- # @Override
- def Run(self, arg_line):
- """Executes a command using a *TS-TF instance.
-
- Args:
- arg_line: string, line of command arguments.
- """
- args = self.arg_parser.ParseLine(arg_line)
- if args.serial:
- serials = args.serial.split(",")
- elif self.console.GetSerials():
- serials = self.console.GetSerials()
- else:
- serials = []
-
- if args.test_exec_mode == "subprocess":
- if args.suite not in self.console.test_suite_info:
- logging.error("test_suite_info doesn't have '%s': %s",
- args.suite, self.console.test_suite_info)
- return
-
- if args.keep_result:
- self._ClearResultDir()
- result_dir = self._result_dir
- else:
- result_dir = None
-
- cmd = self._GenerateTestSuiteCommand(
- self.console.test_suite_info[args.suite], args.command,
- serials, result_dir)
-
- logging.info("Command: %s", cmd)
- self._ExecuteCommand(cmd)
-
- if result_dir:
- result_paths = [
- os.path.join(dir_name, file_name)
- for dir_name, file_name in utils.iterate_files(result_dir)
- if file_name.startswith("log-result")
- and file_name.endswith(".zip")
- ]
-
- if len(result_paths) != 1:
- logging.warning("Unexpected number of results: %s",
- result_paths)
-
- self.console.test_result.clear()
- result = {}
- if len(result_paths) > 0:
- with zipfile.ZipFile(
- result_paths[0], mode="r") as result_zip:
- with result_zip.open(
- "log-result.xml", mode="rU") as result_xml:
- result = xml_utils.GetAttributes(
- result_xml, self._RESULT_TAG,
- self._RESULT_ATTRIBUTES)
- if not result:
- logging.warning("Nothing loaded from report.")
- result["result_zip"] = result_paths[0]
-
- result_paths_full = [
- os.path.join(dir_name, file_name)
- for dir_name, file_name in utils.iterate_files(result_dir)
- if file_name.endswith(".zip")
- ]
- result["result_full"] = " ".join(result_paths_full)
- result["suite_name"] = args.suite
-
- logging.debug(result)
- self.console.test_result.update(result)
- else:
- logging.error("unsupported exec mode: %s", args.test_exec_mode)
- return False
-
- # @Override
- def TearDown(self):
- """Deletes the result directory."""
- if self._result_dir:
- shutil.rmtree(self._result_dir, ignore_errors=True)
diff --git a/harnesses/host_controller/command_processor/command_upload.py b/harnesses/host_controller/command_processor/command_upload.py
deleted file mode 100644
index db57089..0000000
--- a/harnesses/host_controller/command_processor/command_upload.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-import shutil
-import socket
-import time
-
-from host_controller import common
-from host_controller.command_processor import base_command_processor
-from host_controller.utils.gcp import gcs_utils
-from host_controller.utils.parser import xml_utils
-
-from vts.utils.python.common import cmd_utils
-
-from vti.dashboard.proto import TestSuiteResultMessage_pb2 as SuiteResMsg
-from vti.test_serving.proto import TestScheduleConfigMessage_pb2 as SchedCfgMsg
-
-
-class CommandUpload(base_command_processor.BaseCommandProcessor):
- """Command processor for upload command.
-
- Attributes:
- arg_parser: ConsoleArgumentParser object, argument parser.
- console: cmd.Cmd console object.
- command: string, command name which this processor will handle.
- command_detail: string, detailed explanation for the command.
- """
-
- command = "upload"
- command_detail = "Upload <src> file to <dest> Google Cloud Storage. In <src> and <dest>, variables enclosed in {} are replaced with the values stored in the console."
-
- # @Override
- def SetUp(self):
- """Initializes the parser for upload command."""
- self.arg_parser.add_argument(
- "--src",
- required=True,
- default="latest-system.img",
- help="Path to a source file to upload. Only single file can be "
- "uploaded per once. Use 'latest- prefix to upload the latest "
- "fetch images. e.g. --src=latest-system.img If argument "
- "value is not given, the recently fetched system.img will be "
- "uploaded.")
- self.arg_parser.add_argument(
- "--dest",
- required=True,
- help="Google Cloud Storage URL to which the file is uploaded.")
- self.arg_parser.add_argument(
- "--report_path",
- help="Google Cloud Storage URL, the dest path of a report file")
- self.arg_parser.add_argument(
- "--clear_dest",
- action="store_true",
- help="Delete dest recursively before the upload.")
- self.arg_parser.add_argument(
- "--clear_results",
- default=False,
- help="True to clear all the results after the upload.")
- self.arg_parser.add_argument(
- "--result_from_suite",
- default="",
- choices=("", "vts", "cts", "gts", "sts"),
- help="To specify the type of a test suite report, since there can "
- "be multiple numbers of result sets from different test "
- "suites. If not specified, the HC will upload the report "
- "from last run suite and plan.")
- self.arg_parser.add_argument(
- "--result_from_plan",
- default="",
- help="To specify the type of the plan name from which "
- "the report is generated.")
-
- # @Override
- def Run(self, arg_line):
- """Upload args.src file to args.dest Google Cloud Storage."""
- args = self.arg_parser.ParseLine(arg_line)
-
- gsutil_path = gcs_utils.GetGsutilPath()
- if not gsutil_path:
- logging.error("Please check gsutil is installed and on your PATH")
- return False
-
- if args.src.startswith("latest-"):
- src_name = args.src[7:]
- if src_name in self.console.device_image_info:
- src_paths = self.console.device_image_info[src_name]
- else:
- logging.error(
- "Unable to find {} in device_image_info".format(src_name))
- return False
- else:
- try:
- src_paths = self.console.FormatString(args.src)
- except KeyError as e:
- logging.error("Unknown or uninitialized variable in src: %s",
- e)
- return False
-
- src_path_list_tmp = src_paths.split(" ")
- src_path_list = []
- if src_path_list_tmp:
- for src_path in src_path_list_tmp:
- file_path = src_path.strip()
- if os.path.isfile(file_path):
- src_path_list.append(file_path)
- else:
- logging.error("Cannot find a file: {}".format(file_path))
- src_paths = " ".join(src_path_list)
-
- try:
- dest_path = self.console.FormatString(args.dest)
- except KeyError as e:
- logging.error("Unknown or uninitialized variable in dest: %s", e)
- return False
-
- if not dest_path.startswith("gs://"):
- logging.error("{} is not correct GCS url.".format(dest_path))
- return False
- """ TODO(jongmok) : Before upload, login status, authorization,
- and dest check are required. """
- if args.clear_dest:
- if not gcs_utils.Remove(gsutil_path, dest_path, recursive=True):
- logging.error("Fail to remove %s", dest_path)
-
- if not gcs_utils.Copy(gsutil_path, src_paths, dest_path):
- logging.error("Fail to copy %s to %s", src_paths, dest_path)
-
- if args.report_path or args.clear_results:
- tools_path = ""
- if args.result_from_suite:
- tools_path = os.path.dirname(
- self.console.test_suite_info[args.result_from_suite])
- else:
- try:
- tools_path = os.path.dirname(self.console.test_suite_info[
- self.console.FormatString("{suite_name}")])
- except KeyError:
- if self.console.vti_endpoint_client.CheckBootUpStatus():
- logging.error(
- "No test results found from any fetched test suite."
- " Please fetch a test suite and run 'test' command,"
- " then try running 'upload' command again.")
- return False
- results_base_path = os.path.join(tools_path,
- common._RESULTS_BASE_PATH)
-
- if args.report_path:
- report_path = self.console.FormatString(args.report_path)
- if not report_path.startswith("gs://"):
- logging.error(
- "{} is not correct GCS url.".format(report_path))
- else:
- self.UploadReport(
- gsutil_path, report_path, dest_path, results_base_path,
- args.result_from_suite, args.result_from_plan)
-
- if args.clear_results:
- shutil.rmtree(results_base_path, ignore_errors=True)
-
- def UploadReport(self, gsutil_path, report_path, log_path, results_path,
- suite_name, plan_name):
- """Uploads report summary file to the given path.
-
- Args:
- gsutil_path: string, the path of a gsutil binary.
- report_path: string, the dest GCS URL to which the summarized report
- file will be uploaded.
- log_path: string, GCS URL where the log files from the test run
- have been uploaded.
- results_path: string, the base path for the results.
- """
- suite_res_msg = SuiteResMsg.TestSuiteResultMessage()
- suite_res_msg.result_path = log_path
- suite_res_msg.branch = self.console.FormatString("{branch}")
- suite_res_msg.target = self.console.FormatString("{target}")
- vti = self.console.vti_endpoint_client
- suite_res_msg.boot_success = vti.CheckBootUpStatus()
- suite_res_msg.test_type = vti.GetJobTestType()
-
- device_fetch_info = self.console.detailed_fetch_info[
- common._ARTIFACT_TYPE_DEVICE]
- gsi_fetch_info = None
- if common._ARTIFACT_TYPE_GSI in self.console.detailed_fetch_info:
- gsi_fetch_info = self.console.detailed_fetch_info[
- common._ARTIFACT_TYPE_GSI]
-
- if vti.CheckBootUpStatus():
- former_results = [
- result for result in os.listdir(results_path)
- if os.path.isdir(os.path.join(results_path, result))
- and not os.path.islink(os.path.join(results_path, result))
- ]
-
- if not former_results:
- logging.error("No test result found.")
- return False
-
- former_results.sort()
- latest_result = former_results[-1]
- latest_result_xml_path = os.path.join(results_path, latest_result,
- common._TEST_RESULT_XML)
-
- result_attrs = xml_utils.GetAttributes(
- latest_result_xml_path, common._RESULT_TAG, [
- common._SUITE_NAME_ATTR_KEY, common._SUITE_PLAN_ATTR_KEY,
- common._SUITE_VERSION_ATTR_KEY,
- common._SUITE_BUILD_NUM_ATTR_KEY,
- common._START_TIME_ATTR_KEY, common._END_TIME_ATTR_KEY,
- common._HOST_NAME_ATTR_KEY
- ])
- build_attrs = xml_utils.GetAttributes(
- latest_result_xml_path, common._BUILD_TAG, [
- common._FINGERPRINT_ATTR_KEY,
- common._SYSTEM_FINGERPRINT_ATTR_KEY,
- common._VENDOR_FINGERPRINT_ATTR_KEY
- ])
- summary_attrs = xml_utils.GetAttributes(
- latest_result_xml_path, common._SUMMARY_TAG, [
- common._PASSED_ATTR_KEY, common._FAILED_ATTR_KEY,
- common._MODULES_TOTAL_ATTR_KEY,
- common._MODULES_DONE_ATTR_KEY
- ])
-
- suite_res_msg.build_id = result_attrs[
- common._SUITE_BUILD_NUM_ATTR_KEY]
- suite_res_msg.suite_name = result_attrs[
- common._SUITE_NAME_ATTR_KEY]
- suite_res_msg.suite_plan = result_attrs[
- common._SUITE_PLAN_ATTR_KEY]
- suite_res_msg.suite_version = result_attrs[
- common._SUITE_VERSION_ATTR_KEY]
- suite_res_msg.suite_build_number = result_attrs[
- common._SUITE_BUILD_NUM_ATTR_KEY]
- suite_res_msg.start_time = long(
- result_attrs[common._START_TIME_ATTR_KEY])
- suite_res_msg.end_time = long(
- result_attrs[common._END_TIME_ATTR_KEY])
- suite_res_msg.host_name = result_attrs[common._HOST_NAME_ATTR_KEY]
- if common._SYSTEM_FINGERPRINT_ATTR_KEY in build_attrs:
- suite_res_msg.build_system_fingerprint = build_attrs[
- common._SYSTEM_FINGERPRINT_ATTR_KEY]
- else:
- suite_res_msg.build_system_fingerprint = build_attrs[
- common._FINGERPRINT_ATTR_KEY]
- if common._VENDOR_FINGERPRINT_ATTR_KEY in build_attrs:
- suite_res_msg.build_vendor_fingerprint = build_attrs[
- common._VENDOR_FINGERPRINT_ATTR_KEY]
- else:
- suite_res_msg.build_vendor_fingerprint = build_attrs[
- common._FINGERPRINT_ATTR_KEY]
- suite_res_msg.passed_test_case_count = int(
- summary_attrs[common._PASSED_ATTR_KEY])
- suite_res_msg.failed_test_case_count = int(
- summary_attrs[common._FAILED_ATTR_KEY])
- suite_res_msg.modules_done = int(
- summary_attrs[common._MODULES_DONE_ATTR_KEY])
- suite_res_msg.modules_total = int(
- summary_attrs[common._MODULES_TOTAL_ATTR_KEY])
- else:
- suite_res_msg.build_id = self.console.fetch_info["build_id"]
- suite_res_msg.suite_name = suite_name
- suite_res_msg.suite_plan = plan_name
- suite_res_msg.suite_version = ""
- suite_res_msg.suite_build_number = suite_res_msg.build_id
- suite_res_msg.start_time = long(time.time() * 1000)
- suite_res_msg.end_time = suite_res_msg.start_time
- suite_res_msg.host_name = socket.gethostname()
- suite_res_msg.build_vendor_fingerprint = "%s/%s/%s" % (
- device_fetch_info["branch"], device_fetch_info["target"],
- device_fetch_info["build_id"])
- if gsi_fetch_info:
- suite_res_msg.build_system_fingerprint = "%s/%s/%s" % (
- gsi_fetch_info["branch"], gsi_fetch_info["target"],
- gsi_fetch_info["build_id"])
- else:
- suite_res_msg.build_system_fingerprint = suite_res_msg.build_vendor_fingerprint
- suite_res_msg.passed_test_case_count = 0
- suite_res_msg.failed_test_case_count = 0
- suite_res_msg.modules_done = 0
- suite_res_msg.modules_total = 0
-
- suite_res_msg.infra_log_path = self.console.FormatString(
- "{hc_log_upload_path}")
- repack_path_list = []
- repack_path_list.append(self.console.FormatString("{repack_path}"))
- suite_res_msg.repacked_image_path.extend(repack_path_list)
-
- suite_res_msg.schedule_config.build_target.extend(
- [SchedCfgMsg.BuildScheduleConfigMessage()])
- build_target_msg = suite_res_msg.schedule_config.build_target[0]
- build_target_msg.test_schedule.extend(
- [SchedCfgMsg.TestScheduleConfigMessage()])
- test_schedule_msg = build_target_msg.test_schedule[0]
-
- suite_res_msg.vendor_build_id = device_fetch_info["build_id"]
- suite_res_msg.schedule_config.manifest_branch = str(
- device_fetch_info["branch"])
- build_target_msg.name = str(device_fetch_info["target"])
- if device_fetch_info["account_id"]:
- suite_res_msg.schedule_config.pab_account_id = str(
- device_fetch_info["account_id"])
- if device_fetch_info["fetch_signed_build"]:
- build_target_msg.require_signed_device_build = device_fetch_info[
- "fetch_signed_build"]
- if gsi_fetch_info:
- test_schedule_msg.gsi_branch = str(gsi_fetch_info["branch"])
- test_schedule_msg.gsi_build_target = str(gsi_fetch_info["target"])
- suite_res_msg.gsi_build_id = str(gsi_fetch_info["build_id"])
- if gsi_fetch_info["account_id"]:
- test_schedule_msg.gsi_pab_account_id = str(
- gsi_fetch_info["account_id"])
- test_schedule_msg.gsi_vendor_version = str(
- self.console.FormatString("{gsispl.vendor_version}"))
- test_schedule_msg.test_pab_account_id = str(
- self.console.FormatString("{account_id}"))
- build_target_msg.has_bootloader_img = "bootloader.img" in self.console.device_image_info
- build_target_msg.has_radio_img = "radio.img" in self.console.device_image_info
-
- report_file_path = os.path.join(
- self.console.tmp_logdir,
- self.console.FormatString("{timestamp_time}.bin"))
- with open(report_file_path, "w") as fd:
- fd.write(suite_res_msg.SerializeToString())
- fd.close()
-
- copy_command = "{} cp {} {}".format(
- gsutil_path, report_file_path,
- os.path.join(report_path, os.path.basename(report_file_path)))
- _, stderr, err_code = cmd_utils.ExecuteOneShellCommand(copy_command)
- if err_code:
- logging.error(stderr)
diff --git a/harnesses/host_controller/command_processor/command_upload_test.py b/harnesses/host_controller/command_processor/command_upload_test.py
deleted file mode 100644
index 67ccb69..0000000
--- a/harnesses/host_controller/command_processor/command_upload_test.py
+++ /dev/null
@@ -1,312 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.command_processor import command_upload
-
-
-def side_effect(value):
- return value
-
-
-class CommandUploadTest(unittest.TestCase):
- """Tests for upload command processor"""
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.open")
- @mock.patch("host_controller.command_processor.command_upload.cmd_utils")
- @mock.patch("host_controller.command_processor.command_upload.SuiteResMsg")
- @mock.patch("host_controller.command_processor.command_upload.SchedCfgMsg")
- def testUploadReportBootupErr(self, mock_sched_config_msg,
- mock_suite_res_msg, mock_cmd_util, mock_open,
- mock_console):
- mock_open.__enter__ = mock.Mock(return_value=mock_open)
- mock_open.__exit__ = mock.Mock(return_value=None)
- mock_cmd_util.ExecuteOneShellCommand = mock.Mock(
- return_value=("", "", 0))
- mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = False
- mock_console.FormatString.side_effect = side_effect
- mock_console.fetch_info = {
- "branch": "git_device_branch",
- "target": "device-userdebug",
- "build_id": "1234567",
- "account_id": common._DEFAULT_ACCOUNT_ID,
- }
- mock_console.detailed_fetch_info = {
- "device": {
- "branch": "git_device_branch",
- "target": "device-userdebug",
- "build_id": "1234567",
- "account_id": common._DEFAULT_ACCOUNT_ID,
- "fetch_signed_build": False,
- },
- "gsi": {
- "branch": "git_aosp_gsi_branch",
- "target": "gsi-userdebug",
- "build_id": "2345678",
- "account_id": common._DEFAULT_ACCOUNT_ID_INTERNAL,
- }
- }
- mock_console.tmp_logdir = "tmp/log"
- mock_console.device_image_info = {
- "bootloader.img": "path/to/bootloader.img"
- }
- mock_pb2 = mock.Mock()
- mock_pb2.repacked_image_path = []
- mock_pb2.schedule_config.build_target = []
- mock_build_sched_config_pb2 = mock.Mock()
- mock_build_sched_config_pb2.test_schedule = []
- mock_test_sched_config_pb2 = mock.Mock()
- mock_suite_res_msg.TestSuiteResultMessage.return_value = mock_pb2
- mock_sched_config_msg.BuildScheduleConfigMessage.return_value = (
- mock_build_sched_config_pb2)
- mock_sched_config_msg.TestScheduleConfigMessage.return_value = (
- mock_test_sched_config_pb2)
- command = command_upload.CommandUpload()
- command._SetUp(mock_console)
- command.UploadReport("/path/to/bin/gsutil", "gs://report-bucket/",
- "tmp/console.log", "tmp/result.log", "vts",
- "some_plan")
- self.assertEqual(mock_pb2.build_id, "1234567")
- self.assertEqual(mock_pb2.suite_name, "vts")
- self.assertEqual(mock_pb2.suite_plan, "some_plan")
- self.assertEqual(mock_pb2.suite_build_number, mock_pb2.build_id)
- self.assertEqual(mock_pb2.build_system_fingerprint,
- "git_aosp_gsi_branch/gsi-userdebug/2345678")
- self.assertEqual(mock_pb2.build_vendor_fingerprint,
- "git_device_branch/device-userdebug/1234567")
- self.assertEqual(mock_pb2.repacked_image_path, ["{repack_path}"])
- self.assertEqual(mock_pb2.schedule_config.manifest_branch,
- "git_device_branch")
- self.assertEqual(mock_pb2.schedule_config.pab_account_id,
- common._DEFAULT_ACCOUNT_ID)
-
- self.assertTrue(mock_build_sched_config_pb2.has_bootloader_img)
- self.assertFalse(mock_build_sched_config_pb2.has_radio_img)
-
- self.assertEqual(mock_test_sched_config_pb2.gsi_branch,
- "git_aosp_gsi_branch")
- self.assertEqual(mock_test_sched_config_pb2.gsi_build_target,
- "gsi-userdebug")
- self.assertEqual(mock_test_sched_config_pb2.gsi_pab_account_id,
- common._DEFAULT_ACCOUNT_ID_INTERNAL)
- mock_cmd_util.ExecuteOneShellCommand.assert_called_with(
- "/path/to/bin/gsutil cp tmp/log/{timestamp_time}.bin "
- "gs://report-bucket/{timestamp_time}.bin")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.open")
- @mock.patch("host_controller.command_processor.command_upload.cmd_utils")
- @mock.patch("host_controller.command_processor.command_upload.SuiteResMsg")
- @mock.patch("host_controller.command_processor.command_upload.os")
- @mock.patch("host_controller.command_processor.command_upload.xml_utils")
- @mock.patch("host_controller.command_processor.command_upload.SchedCfgMsg")
- def testUploadReportBootupOk(self, mock_sched_config_msg, mock_xml_util,
- mock_os, mock_suite_res_msg, mock_cmd_util,
- mock_open, mock_console):
- mock_open.__enter__ = mock.Mock(return_value=mock_open)
- mock_open.__exit__ = mock.Mock(return_value=None)
- mock_cmd_util.ExecuteOneShellCommand = mock.Mock(
- return_value=("", "", 0))
- mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = True
- mock_console.FormatString.side_effect = side_effect
- mock_console.tmp_logdir = "tmp/log"
- mock_pb2 = mock.Mock()
- mock_pb2.repacked_image_path = []
- mock_pb2.schedule_config.build_target = []
- mock_build_sched_config_pb2 = mock.Mock()
- mock_build_sched_config_pb2.test_schedule = []
- mock_test_sched_config_pb2 = mock.Mock()
- mock_suite_res_msg.TestSuiteResultMessage.return_value = mock_pb2
- mock_sched_config_msg.BuildScheduleConfigMessage.return_value = (
- mock_build_sched_config_pb2)
- mock_sched_config_msg.TestScheduleConfigMessage.return_value = (
- mock_test_sched_config_pb2)
- mock_os.listdir.return_value = ["1", "2", "3"]
- mock_os.path.isdir.return_value = True
- mock_os.path.islink.return_value = False
- mock_os.path.join = os.path.join
- mock_os.path.basename = os.path.basename
- mock_xml_util.GetAttributes.return_value = {
- common._SUITE_NAME_ATTR_KEY:
- "vts",
- common._SUITE_PLAN_ATTR_KEY:
- "some_plan",
- common._SUITE_VERSION_ATTR_KEY:
- "8.0_R1",
- common._SUITE_BUILD_NUM_ATTR_KEY:
- "1234567",
- common._START_TIME_ATTR_KEY:
- "0",
- common._END_TIME_ATTR_KEY:
- "1",
- common._HOST_NAME_ATTR_KEY:
- "this-host",
- common._FINGERPRINT_ATTR_KEY:
- "git_device_branch/device-userdebug/1234567",
- common._SYSTEM_FINGERPRINT_ATTR_KEY:
- "git_aosp_gsi_branch/gsi-userdebug/2345678",
- common._VENDOR_FINGERPRINT_ATTR_KEY:
- "git_device_branch/device-userdebug/1234567",
- common._PASSED_ATTR_KEY:
- "1265",
- common._FAILED_ATTR_KEY:
- "43",
- common._MODULES_TOTAL_ATTR_KEY:
- "100",
- common._MODULES_DONE_ATTR_KEY:
- "98",
- }
- command = command_upload.CommandUpload()
- command._SetUp(mock_console)
- command.UploadReport("/path/to/bin/gsutil", "gs://report-bucket/",
- "tmp/console.log", "tmp/vts/results", "vts",
- "some_plan")
- self.assertEqual(mock_pb2.build_id, "1234567")
- self.assertEqual(mock_pb2.suite_name, "vts")
- self.assertEqual(mock_pb2.suite_plan, "some_plan")
- self.assertEqual(mock_pb2.suite_version, "8.0_R1")
- self.assertEqual(mock_pb2.suite_build_number, mock_pb2.build_id)
- self.assertEqual(mock_pb2.start_time, 0)
- self.assertEqual(mock_pb2.end_time, 1)
- self.assertEqual(mock_pb2.host_name, "this-host")
- self.assertEqual(mock_pb2.build_system_fingerprint,
- "git_aosp_gsi_branch/gsi-userdebug/2345678")
- self.assertEqual(mock_pb2.build_vendor_fingerprint,
- "git_device_branch/device-userdebug/1234567")
- self.assertEqual(mock_pb2.passed_test_case_count, 1265)
- self.assertEqual(mock_pb2.failed_test_case_count, 43)
- self.assertEqual(mock_pb2.modules_total, 100)
- self.assertEqual(mock_pb2.modules_done, 98)
- self.assertEqual(mock_pb2.repacked_image_path, ["{repack_path}"])
- mock_cmd_util.ExecuteOneShellCommand.assert_called_with(
- "/path/to/bin/gsutil cp tmp/log/{timestamp_time}.bin "
- "gs://report-bucket/{timestamp_time}.bin")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.os")
- @mock.patch("host_controller.command_processor.command_upload.logging")
- def testUploadReportResultDirAbsent(self, mock_logger, mock_os,
- mock_console):
- mock_console.vti_endpoint_client.CheckBootUpStatus.return_value = True
- mock_console.FormatString.side_effect = side_effect
- mock_console.tmp_logdir = "tmp/log"
- mock_os.listdir.return_value = []
- command = command_upload.CommandUpload()
- command._SetUp(mock_console)
- ret = command.UploadReport("/path/to/bin/gsutil",
- "gs://report-bucket/", "tmp/console.log",
- "tmp/result.log", "vts", "some_plan")
- self.assertFalse(ret)
- mock_logger.error.assert_called_with("No test result found.")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
- @mock.patch("host_controller.command_processor.command_upload.logging")
- def testCommandUploadGsutilAbsent(self, mock_logger, mock_gcs_util,
- mock_console):
- mock_gcs_util.GetGsutilPath.return_value = ""
- command = command_upload.CommandUpload()
- command.UploadReport = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("--src=tmp/result.log --dest=gs://report-bucket/")
- self.assertFalse(ret)
- mock_logger.error.assert_called_with(
- "Please check gsutil is installed and on your PATH")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
- @mock.patch("host_controller.command_processor.command_upload.logging")
- def testCommandUploadLatestSrc(self, mock_logger, mock_gcs_util,
- mock_console):
- mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
- command = command_upload.CommandUpload()
- command.UploadReport = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run(
- "--src=latest-something.img --dest=gs://report-bucket/")
- self.assertFalse(ret)
- mock_logger.error.assert_called_with(
- "Unable to find something.img in device_image_info")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
- @mock.patch("host_controller.command_processor.command_upload.os")
- def testCommandUploadLatestLegitSrc(self, mock_os, mock_gcs_util,
- mock_console):
- mock_os.path.isfile.return_value = True
- mock_console.device_image_info = {"system.img": "path/to/system.img"}
- mock_console.FormatString.side_effect = side_effect
- mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
- command = command_upload.CommandUpload()
- command.UploadReport = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run(
- "--src=latest-system.img --dest=gs://report-bucket/dir "
- "--clear_dest")
- self.assertIsNone(ret)
- mock_gcs_util.Remove.assert_called_with(
- "/path/to/bin/gsutil", "gs://report-bucket/dir", recursive=True)
- mock_gcs_util.Copy.assert_called_with('/path/to/bin/gsutil',
- 'path/to/system.img',
- 'gs://report-bucket/dir')
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
- @mock.patch("host_controller.command_processor.command_upload.os")
- @mock.patch("host_controller.command_processor.command_upload.logging")
- def testCommandUploadFalseDest(self, mock_logger, mock_os, mock_gcs_util,
- mock_console):
- mock_os.path.isfile.return_value = True
- mock_console.device_image_info = {"system.img": "path/to/system.img"}
- mock_console.FormatString.side_effect = side_effect
- mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
- command = command_upload.CommandUpload()
- command.UploadReport = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("--src=latest-system.img --dest=/report-bucket/")
- self.assertFalse(ret)
- mock_logger.error.assert_called_with(
- "/report-bucket/ is not correct GCS url.")
-
- @mock.patch("host_controller.console.Console")
- @mock.patch("host_controller.command_processor.command_upload.gcs_utils")
- @mock.patch("host_controller.command_processor.command_upload.os")
- def testCommandUploadMultipleFiles(self, mock_os, mock_gcs_util,
- mock_console):
- mock_os.path.isfile.return_value = True
- mock_console.FormatString.side_effect = side_effect
- mock_gcs_util.GetGsutilPath.return_value = "/path/to/bin/gsutil"
- command = command_upload.CommandUpload()
- command.UploadReport = mock.Mock()
- command._SetUp(mock_console)
- ret = command._Run("--src=result.zip --dest=gs://report-bucket/")
- self.assertIsNone(ret)
- mock_gcs_util.Copy.assert_called_with(
- '/path/to/bin/gsutil', 'result.zip', 'gs://report-bucket/')
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/common.py b/harnesses/host_controller/common.py
deleted file mode 100644
index 76bb697..0000000
--- a/harnesses/host_controller/common.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# The default Partner Android Build (PAB) public account.
-# To obtain access permission, please reach out to Android partner engineering
-# department of Google LLC.
-
-from vti.test_serving.proto import TestScheduleConfigMessage_pb2 as pb
-
-_DEFAULT_ACCOUNT_ID = '543365459'
-
-# The default Partner Android Build (PAB) internal account.
-_DEFAULT_ACCOUNT_ID_INTERNAL = '541462473'
-
-# The key value used for getting a fetched .zip android img file.
-FULL_ZIPFILE = "full-zipfile"
-# The key of an item that stores the unziped files of a full image zip file.
-FULL_ZIPFILE_DIR = "full-zipfile-dir"
-
-# The key of an item that stores the fetch GSI image (.zip) file.
-GSI_ZIPFILE = "gsi-zipfile"
-# The key of an item that stores the unziped files of a GSI image zip file.
-GSI_ZIPFILE_DIR = "gsi-zipfile-dir"
-
-# The default value for "flash --current".
-_DEFAULT_FLASH_IMAGES = [
- FULL_ZIPFILE,
- FULL_ZIPFILE_DIR,
- "bootloader.img",
- "boot.img",
- "cache.img",
- "radio.img",
- "system.img",
- "userdata.img",
- "vbmeta.img",
- "vendor.img",
-]
-
-# The environment variable for default serial numbers.
-_ANDROID_SERIAL = "ANDROID_SERIAL"
-
-_DEVICE_STATUS_DICT = {
- "unknown": 0,
- "fastboot": 1,
- "online": 2,
- "ready": 3,
- "use": 4,
- "error": 5,
- "no-response": 6
-}
-
-_STORAGE_TYPE_DICT = {
- pb.UNKNOWN_BUILD_STORAGE_TYPE: "unknown",
- pb.BUILD_STORAGE_TYPE_PAB: "pab",
- pb.BUILD_STORAGE_TYPE_GCS: "gcs",
-}
-
-_STORAGE_TYPE_DICT_REVERSE = {
- "unknown": pb.UNKNOWN_BUILD_STORAGE_TYPE,
- "pab": pb.BUILD_STORAGE_TYPE_PAB,
- "gcs": pb.BUILD_STORAGE_TYPE_GCS,
-}
-
-# Default SPL date, used for gsispl command
-_SPL_DEFAULT_DAY = 5
-
-# Maximum number of leased jobs per host.
-_MAX_LEASED_JOBS = 14
-
-# Defualt access point for dut wifi_on command.
-_DEFAULT_WIFI_AP = "GoogleGuest"
-
-# SoC name list.
-K39TV1_BSP = "k39tv1_bsp"
-K39TV1_BSP_1G = "k39tv1_bsp_1g"
-
-SDM845 = "sdm845"
-
-UNIVERSAL9810 = "universal9810"
-
-# Lib files from SDM845 vendor system image need to be re-pushed
-# into GSI system image to boot the devices up properly.
-SDM845_LIB_LIST = [
- "libdrm.so",
- "vendor.display.color@1.0.so",
- "vendor.display.config@1.0.so",
- "vendor.display.config@1.1.so",
- "vendor.display.postproc@1.0.so",
- "vendor.qti.hardware.perf@1.0.so",
-]
-
-# Dir name in which the addtional files need to be repacked.
-_ADDITIONAL_FILES_DIR = "additional_file"
-
-# Relative path to the "results" directory from the tools directory.
-_RESULTS_BASE_PATH = "../results"
-
-# Test result file contains invoked test plan results.
-_TEST_RESULT_XML = "test_result.xml"
-
-_LOG_RESULT_XML = "log-result.xml"
-
-# XML tag name whose attributes represent a module.
-_MODULE_TAG = "Module"
-
-# XML tag name whose attribute is test plan.
-_RESULT_TAG = "Result"
-
-# XML tag name whose attributes represent a test case in a module.
-_TESTCASE_TAG = "TestCase"
-
-# XML tag name whose attributes represent a test result in a test case.
-_TEST_TAG = "Test"
-
-# XML tag name whose attributes are about the build info of the device.
-_BUILD_TAG = "Build"
-
-# XML tag name whose attributes are pass/fail count, modules run/total count.
-_SUMMARY_TAG = "Summary"
-
-# The key value for retrieving test plan and etc. from the result xml
-_SUITE_PLAN_ATTR_KEY = "suite_plan"
-
-_SUITE_BUILD_NUM_ATTR_KEY = "suite_build_number"
-
-_SUITE_VERSION_ATTR_KEY = "suite_version"
-
-_HOST_NAME_ATTR_KEY = "host_name"
-
-_START_TIME_ATTR_KEY = "start"
-
-_START_DISPLAY_TIME_ATTR_KEY = "start_display"
-
-_END_TIME_ATTR_KEY = "end"
-
-_END_DISPLAY_TIME_ATTR_KEY = "end_display"
-
-_SUITE_NAME_ATTR_KEY = "suite_name"
-
-# The key value for retrieving build fingerprint values from the result xml.
-_ABIS_ATTR_KEY = "build_abis"
-
-_FINGERPRINT_ATTR_KEY = "build_fingerprint"
-
-_SYSTEM_FINGERPRINT_ATTR_KEY = "build_system_fingerprint"
-
-_VENDOR_FINGERPRINT_ATTR_KEY = "build_vendor_fingerprint"
-
-# The key value for retrieving passed testcase count
-_PASSED_ATTR_KEY = "pass"
-
-# The key value for retrieving failed testcase count
-_FAILED_ATTR_KEY = "failed"
-
-# The key value for retrieving total module count
-_MODULES_TOTAL_ATTR_KEY = "modules_total"
-
-# The key value for retrieving run module count
-_MODULES_DONE_ATTR_KEY = "modules_done"
-
-# The key value for retrieving name of a test, testcase, or module.
-_NAME_ATTR_KEY = "name"
-
-# The key value for retrieving ABI of a module.
-_ABI_ATTR_KEY = "abi"
-
-# The key value for retrieving result of a test.
-_RESULT_ATTR_KEY = "result"
-
-# VTSLAB package version file
-_VTSLAB_VERSION_TXT = "version.txt"
-
-_VTSLAB_VERSION_DEFAULT_VALUE = "000000_000000:00000000"
-
-# String representations of the artifact types can be fetched.
-_ARTIFACT_TYPE_DEVICE = "device"
-_ARTIFACT_TYPE_GSI = "gsi"
-_ARTIFACT_TYPE_TEST_SUITE = "test_suite"
-# Artifact type that are usually for custom tools fetched from GCS.
-_ARTIFACT_TYPE_INFRA = "infra"
-
-# List of artifact types.
-_ARTIFACT_TYPE_LIST = [
- _ARTIFACT_TYPE_DEVICE,
- _ARTIFACT_TYPE_GSI,
- _ARTIFACT_TYPE_TEST_SUITE,
- _ARTIFACT_TYPE_INFRA,
-]
-# Directory relative to the home directory, in which the devices' lock files will be.
-_DEVLOCK_DIR = ".devlock"
-
-# Default timeout for "adb reboot/fastboot getvar" command in secs.
-DEFAULT_DEVICE_TIMEOUT_SECS = 300
-
-# Maximum number of concurrent adb/fastboot processes.
-MAX_ADB_FASTBOOT_PROCESS = 2
-
-# Default number of the actual retry runs for the "retry" command.
-DEFAULT_RETRY_COUNT = 30 \ No newline at end of file
diff --git a/harnesses/host_controller/console.py b/harnesses/host_controller/console.py
deleted file mode 100644
index 6ab0722..0000000
--- a/harnesses/host_controller/console.py
+++ /dev/null
@@ -1,920 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import cmd
-import ctypes
-import datetime
-import imp # Python v2 compatibility
-import logging
-import multiprocessing
-import multiprocessing.pool
-import os
-import re
-import shutil
-import signal
-import socket
-import sys
-import tempfile
-import threading
-import time
-import urlparse
-
-from host_controller import common
-from host_controller.command_processor import command_adb
-from host_controller.command_processor import command_build
-from host_controller.command_processor import command_config
-from host_controller.command_processor import command_config_local
-from host_controller.command_processor import command_copy
-from host_controller.command_processor import command_device
-from host_controller.command_processor import command_dut
-from host_controller.command_processor import command_exit
-from host_controller.command_processor import command_fastboot
-from host_controller.command_processor import command_fetch
-from host_controller.command_processor import command_flash
-from host_controller.command_processor import command_gsispl
-from host_controller.command_processor import command_info
-from host_controller.command_processor import command_lease
-from host_controller.command_processor import command_list
-from host_controller.command_processor import command_password
-from host_controller.command_processor import command_release
-from host_controller.command_processor import command_retry
-from host_controller.command_processor import command_request
-from host_controller.command_processor import command_repack
-from host_controller.command_processor import command_sheet
-from host_controller.command_processor import command_shell
-from host_controller.command_processor import command_sleep
-from host_controller.command_processor import command_test
-from host_controller.command_processor import command_reproduce
-from host_controller.command_processor import command_upload
-from host_controller.build import build_info
-from host_controller.build import build_provider_ab
-from host_controller.build import build_provider_gcs
-from host_controller.build import build_provider_local_fs
-from host_controller.build import build_provider_pab
-from host_controller.utils.ipc import file_lock
-from host_controller.utils.ipc import shared_dict
-from host_controller.vti_interface import vti_endpoint_client
-from vts.runners.host import logger
-from vts.utils.python.common import cmd_utils
-
-COMMAND_PROCESSORS = [
- command_adb.CommandAdb,
- command_build.CommandBuild,
- command_config.CommandConfig,
- command_config_local.CommandConfigLocal,
- command_copy.CommandCopy,
- command_device.CommandDevice,
- command_dut.CommandDUT,
- command_exit.CommandExit,
- command_fastboot.CommandFastboot,
- command_fetch.CommandFetch,
- command_flash.CommandFlash,
- command_gsispl.CommandGsispl,
- command_info.CommandInfo,
- command_lease.CommandLease,
- command_list.CommandList,
- command_password.CommandPassword,
- command_release.CommandRelease,
- command_retry.CommandRetry,
- command_request.CommandRequest,
- command_repack.CommandRepack,
- command_sheet.CommandSheet,
- command_shell.CommandShell,
- command_sleep.CommandSleep,
- command_test.CommandTest,
- command_reproduce.CommandReproduce,
- command_upload.CommandUpload,
-]
-
-
-class NonDaemonizedProcess(multiprocessing.Process):
- """Process class which is not daemonized."""
-
- def _get_daemon(self):
- return False
-
- def _set_daemon(self, value):
- pass
-
- daemon = property(_get_daemon, _set_daemon)
-
-
-class NonDaemonizedPool(multiprocessing.pool.Pool):
- """Pool class which is not daemonized."""
-
- Process = NonDaemonizedProcess
-
-
-def JobMain(vti_address, in_queue, out_queue, device_status, password, hosts):
- """Main() for a child process that executes a leased job.
-
- Currently, lease jobs must use VTI (not TFC).
-
- Args:
- vti_client: VtiEndpointClient needed to create Console.
- in_queue: Queue to get new jobs.
- out_queue: Queue to put execution results.
- device_status: SharedDict, contains device status information.
- shared between processes.
- password: multiprocessing.managers.ValueProxy, a proxy instance of a
- string(ctypes.c_char_p) represents the password which is
- to be passed to the prompt when executing certain command
- as root user.
- hosts: A list of HostController objects. Needed for the device command.
- """
-
- def SigTermHandler(signum, frame):
- """Signal handler for exiting pool process explicitly.
-
- Added to resolve orphaned pool process issue.
- """
- sys.exit(0)
-
- signal.signal(signal.SIGTERM, SigTermHandler)
-
- vti_client = vti_endpoint_client.VtiEndpointClient(vti_address)
- console = Console(vti_client, None, None, hosts, job_pool=True)
- console.device_status = device_status
- console.password = password
- multiprocessing.util.Finalize(console, console.__exit__, exitpriority=0)
-
- while True:
- command = in_queue.get()
- if command == "exit":
- break
- elif command == "lease":
- filepath, kwargs = vti_client.LeaseJob(socket.gethostname(), True)
- logging.debug("Job %s -> %s" % (os.getpid(), kwargs))
- if filepath is not None:
- # TODO: redirect console output and add
- # console command to access them.
-
- console._build_provider[
- "pab"] = build_provider_pab.BuildProviderPAB()
- console._build_provider[
- "gcs"] = build_provider_gcs.BuildProviderGCS()
-
- for serial in kwargs["serial"]:
- console.ChangeDeviceState(
- serial, common._DEVICE_STATUS_DICT["use"])
- print_to_console = True
- if not print_to_console:
- sys.stdout = out
- sys.stderr = err
-
- ret, gcs_log_url = console.ProcessConfigurableScript(
- os.path.join(os.getcwd(), "host_controller", "campaigns",
- filepath), **kwargs)
- if ret:
- job_status = "complete"
- else:
- job_status = "infra-err"
-
- vti_client.StopHeartbeat(job_status, gcs_log_url)
- logging.info("Job execution complete. "
- "Setting job status to {}".format(job_status))
-
- if not print_to_console:
- sys.stdout = sys.__stdout__
- sys.stderr = sys.__stderr__
-
- for serial in kwargs["serial"]:
- console.ChangeDeviceState(
- serial, common._DEVICE_STATUS_DICT["ready"])
-
- del console._build_provider["pab"]
- del console._build_provider["gcs"]
- console.fetch_info = {}
- console._detailed_fetch_info = {}
- else:
- logging.error("Unknown job command %s", command)
-
- out_queue.put("exit")
-
-
-class Console(cmd.Cmd):
- """The console for host controllers.
-
- Attributes:
- command_processors: dict of string:BaseCommandProcessor,
- map between command string and command processors.
- device_image_info: dict containing info about device image files.
- prompt: The prompt string at the beginning of each command line.
- test_result: dict containing info about the last test result.
- test_suite_info: dict containing info about test suite package files.
- tools_info: dict containing info about custom tool files.
- scheduler_thread: dict containing threading.Thread instances(s) that
- update configs regularly.
- _build_provider_pab: The BuildProviderPAB used to download artifacts.
- _vti_address: string, VTI service URI.
- _vti_client: VtiEndpoewrClient, used to upload data to a test
- scheduling infrastructure.
- _tfc_client: The TfcClient that the host controllers connect to.
- _hosts: A list of HostController objects.
- _in_file: The input file object.
- _out_file: The output file object.
- _serials: A list of string where each string is a device serial.
- _device_status: SharedDict, shared with process pool.
- contains status data on each devices.
- _job_pool: bool, True if Console is created from job pool process
- context.
- _password: multiprocessing.managers.ValueProxy, a proxy instance of a
- string(ctypes.c_char_p) represents the password which is
- to be passed to the prompt when executing certain command
- as root user.
- _manager: SyncManager. an instance of a manager for shared objects and
- values between processes.
- _vtslab_version: string, contains version information of vtslab package.
- (<git commit timestamp>:<git commit hash value>)
- _detailed_fetch_info: A nested dict, holds the branch and target value
- of the device, gsi, or test suite artifact.
- _file_lock: FileLock, an instance used for synchronizing the devices'
- use when the automated self-update happens.
- """
-
- def __init__(self,
- vti_endpoint_client,
- tfc,
- pab,
- host_controllers,
- vti_address=None,
- in_file=sys.stdin,
- out_file=sys.stdout,
- job_pool=False,
- password=None):
- """Initializes the attributes and the parsers."""
- # cmd.Cmd is old-style class.
- cmd.Cmd.__init__(self, stdin=in_file, stdout=out_file)
- self._build_provider = {}
- self._job_pool = job_pool
- if not self._job_pool:
- self._build_provider["pab"] = pab
- self._build_provider["gcs"] = build_provider_gcs.BuildProviderGCS()
- self._build_provider[
- "local_fs"] = build_provider_local_fs.BuildProviderLocalFS()
- self._build_provider["ab"] = build_provider_ab.BuildProviderAB()
- self._manager = multiprocessing.Manager()
- self._device_status = shared_dict.SharedDict(self._manager)
- self._password = self._manager.Value(ctypes.c_char_p, password)
- try:
- with open(common._VTSLAB_VERSION_TXT, "r") as file:
- self._vtslab_version = file.readline().strip()
- file.close()
- logging.info("VTSLAB version: %s" % self._vtslab_version)
- except IOError as e:
- logging.exception(e)
- logging.error("Version info missing in vtslab package. "
- "Setting version as %s",
- common._VTSLAB_VERSION_DEFAULT_VALUE)
- self._vtslab_version = common._VTSLAB_VERSION_DEFAULT_VALUE
- self._logfile_upload_path = ""
-
- self._vti_endpoint_client = vti_endpoint_client
- self._vti_address = vti_address
- self._tfc_client = tfc
- self._hosts = host_controllers
- self._in_file = in_file
- self._out_file = out_file
- self.prompt = "> "
- self.command_processors = {}
- self.device_image_info = build_info.BuildInfo()
- self.test_result = {}
- self.test_suite_info = build_info.BuildInfo()
- self.tools_info = build_info.BuildInfo()
- self.fetch_info = {}
- self._detailed_fetch_info = {}
- self.test_results = {}
- self._file_lock = file_lock.FileLock()
- self.repack_dest_path = ""
-
- if common._ANDROID_SERIAL in os.environ:
- self._serials = [os.environ[common._ANDROID_SERIAL]]
- else:
- self._serials = []
-
- self.InitCommandModuleParsers()
- self.SetUpCommandProcessors()
-
- tempdir_base = os.path.join(os.getcwd(), "tmp")
- if not os.path.exists(tempdir_base):
- os.mkdir(tempdir_base)
- self._tmpdir_default = tempfile.mkdtemp(dir=tempdir_base)
- self._tmp_logdir = tempfile.mkdtemp(dir=tempdir_base)
- if not self._job_pool:
- self._logfile_path = logger.setupTestLogger(
- self._tmp_logdir, create_symlink=False)
-
- def __exit__(self):
- """Finalizes the build provider attributes explicitly when exited."""
- for bp in self._build_provider:
- self._build_provider[bp].__del__()
- if os.path.exists(self._tmp_logdir):
- shutil.rmtree(self._tmp_logdir)
-
- @property
- def job_pool(self):
- """getter for self._job_pool"""
- return self._job_pool
-
- @property
- def device_status(self):
- """getter for self._device_status"""
- return self._device_status
-
- @device_status.setter
- def device_status(self, device_status):
- """setter for self._device_status"""
- self._device_status = device_status
-
- @property
- def build_provider(self):
- """getter for self._build_provider"""
- return self._build_provider
-
- @property
- def tmpdir_default(self):
- """getter for self._password"""
- return self._tmpdir_default
-
- @tmpdir_default.setter
- def tmpdir_default(self, tmpdir):
- """getter for self._password"""
- self._tmpdir_default = tmpdir
-
- @property
- def password(self):
- """getter for self._password"""
- return self._password
-
- @password.setter
- def password(self, password):
- """getter for self._password"""
- self._password = password
-
- @property
- def logfile_path(self):
- """getter for self._logfile_path"""
- return self._logfile_path
-
- @property
- def tmp_logdir(self):
- """getter for self._tmp_logdir"""
- return self._tmp_logdir
-
- @property
- def vti_endpoint_client(self):
- """getter for self._vti_endpoint_client"""
- return self._vti_endpoint_client
-
- @property
- def vtslab_version(self):
- """getter for self._vtslab_version"""
- return self._vtslab_version
-
- @property
- def detailed_fetch_info(self):
- return self._detailed_fetch_info
-
- def UpdateFetchInfo(self, artifact_type):
- if artifact_type in common._ARTIFACT_TYPE_LIST:
- self._detailed_fetch_info[artifact_type] = {}
- self._detailed_fetch_info[artifact_type].update(self.fetch_info)
- else:
- logging.error("Unrecognized artifact type: %s", artifact_type)
-
- @property
- def file_lock(self):
- """getter for self._file_lock"""
- return self._file_lock
-
- def ChangeDeviceState(self, serial, state):
- """Changes a device's state and (un)locks the file lock if necessary.
-
- Args:
- serial: string, serial number of a device.
- state: int, devices' status value pre-defined in
- common._DEVICE_STATUS_DICT.
- Returns:
- True if the state change and locking/unlocking are successful.
- False otherwise.
- """
- if state == common._DEVICE_STATUS_DICT["use"]:
- ret = self._file_lock.LockDevice(serial)
- if ret == False:
- return False
-
- current_status = self.device_status[serial]
- self.device_status[serial] = state
-
- if (current_status in (common._DEVICE_STATUS_DICT["use"],
- common._DEVICE_STATUS_DICT["error"])
- and current_status != state):
- self._file_lock.UnlockDevice(serial)
-
- def InitCommandModuleParsers(self):
- """Init all console command modules"""
- for name in dir(self):
- if name.startswith('_Init') and name.endswith('Parser'):
- attr_func = getattr(self, name)
- if hasattr(attr_func, '__call__'):
- attr_func()
-
- def SetUpCommandProcessors(self):
- """Sets up all command processors."""
- for command_processor in COMMAND_PROCESSORS:
- cp = command_processor()
- cp._SetUp(self)
- do_text = "do_%s" % cp.command
- help_text = "help_%s" % cp.command
- setattr(self, do_text, cp._Run)
- setattr(self, help_text, cp._Help)
- self.command_processors[cp.command] = cp
-
- def TearDown(self):
- """Removes all command processors."""
- for command_processor in self.command_processors.itervalues():
- command_processor._TearDown()
- self.command_processors.clear()
- self.__exit__()
-
- def FormatString(self, format_string):
- """Replaces variables with the values in the console's dictionaries.
-
- Args:
- format_string: The string containing variables enclosed in {}.
-
- Returns:
- The formatted string.
-
- Raises:
- KeyError if a variable is not found in the dictionaries or the
- value is empty.
- """
-
- def ReplaceVariable(match):
- """Replacement functioon for re.sub().
-
- replaces string encased in braces with values in the console's dict.
-
- Args:
- match: regex, used for extracting the variable name.
-
- Returns:
- string value corresponding to the input variable name.
- """
- name = match.group(1)
- if name in ("build_id", "branch", "target", "account_id"):
- value = self.fetch_info[name]
- elif name in ("result_full", "result_zip", "suite_plan",
- "suite_name"):
- value = self.test_result[name]
- elif "timestamp" in name:
- current_datetime = datetime.datetime.now()
- value_date = current_datetime.strftime("%Y%m%d")
- value_time = current_datetime.strftime("%H%M%S")
- if "_date" in name:
- value = value_date
- elif "_time" in name:
- value = value_time
- elif "_year" in name:
- value = value_date[0:4]
- elif "_month" in name:
- value = value_date[4:6]
- elif "_day" in name:
- value = value_date[6:8]
- else:
- value = "%s-%s" % (value_date, value_time)
- elif name in ("hc_log", "hc_log_file", "hc_log_upload_path"):
- # hc_log: full abs path to the current process's infra log.
- # hc_log_file: infra log file name, with no path information.
- # hc_log_upload_path: path of the infra log file in GCS.
- value = self._logfile_path
- if name == "hc_log_file":
- value = os.path.basename(value)
- elif name == "hc_log_upload_path":
- value = self._logfile_upload_path
- elif name in ("repack_path"):
- value = self.repack_dest_path
- self.repack_dest_path = ""
- elif name in ("hostname"):
- value = socket.gethostname()
- elif "." in name and name.split(".")[0] in self.command_processors:
- command, arg = name.split(".")
- try:
- value = self.command_processors[command].arg_buffer[arg]
- except KeyError as e:
- logging.exception(e)
- value = ""
- if value is None:
- value = ""
- else:
- value = None
-
- if value is None:
- raise KeyError(name)
-
- return value
-
- return re.sub("{([^}]+)}", ReplaceVariable, format_string)
-
- def ProcessScript(self, script_file_path):
- """Processes a .py script file.
-
- A script file implements a function which emits a list of console
- commands to execute. That function emits an empty list or None if
- no more command needs to be processed.
-
- Args:
- script_file_path: string, the path of a script file (.py file).
-
- Returns:
- True if successful; False otherwise
- """
- if not script_file_path.endswith(".py"):
- logging.error("Script file is not .py file: %s" % script_file_path)
- return False
-
- script_module = imp.load_source('script_module', script_file_path)
-
- commands = script_module.EmitConsoleCommands()
- if commands:
- for command in commands:
- ret = self.onecmd(command)
- if ret == False:
- return False
- return True
-
- def ProcessConfigurableScript(self, script_file_path, **kwargs):
- """Processes a .py script file.
-
- A script file implements a function which emits a list of console
- commands to execute. That function emits an empty list or None if
- no more command needs to be processed.
-
- Args:
- script_file_path: string, the path of a script file (.py file).
- kwargs: extra args for the interface function defined in
- the script file.
-
- Returns:
- True if successful; False otherwise
- String which represents URL to the upload infra log file.
- """
- if script_file_path and not script_file_path.endswith(".py"):
- script_file_path += ".py"
-
- if not script_file_path.endswith(".py"):
- logging.error("Script file is not .py file: %s", script_file_path)
- return False
-
- ret = True
-
- self._logfile_path, file_handler = logger.addLogFile(self._tmp_logdir)
- src = self.FormatString("{hc_log}")
- dest = self.FormatString(
- "gs://vts-report/infra_log/{hostname}/%s_{timestamp}/{hc_log_file}"
- % kwargs["build_target"])
- self._logfile_upload_path = dest
-
- script_module = imp.load_source('script_module', script_file_path)
-
- commands = script_module.EmitConsoleCommands(**kwargs)
- logging.info("Command list: %s", commands)
- if commands:
- logging.info("Console commands: %s", commands)
- for command in commands:
- ret = self.onecmd(command)
- if ret == False:
- break
- else:
- ret = False
-
- file_handler.flush()
- infra_log_upload_command = "upload"
- infra_log_upload_command += " --src=%s" % src
- infra_log_upload_command += " --dest=%s" % dest
- for serial in kwargs["serial"]:
- if self.device_status[serial] == common._DEVICE_STATUS_DICT[
- "error"]:
- self.vti_endpoint_client.SetJobStatusFromLeasedTo("bootup-err")
- break
- if not self.vti_endpoint_client.CheckBootUpStatus():
- infra_log_upload_command += (" --report_path=gs://vts-report/"
- "suite_result/{timestamp_year}/"
- "{timestamp_month}/{timestamp_day}")
- suite_name, plan_name = kwargs["test_name"].split("/")
- infra_log_upload_command += (
- " --result_from_suite=%s" % suite_name)
- infra_log_upload_command += (" --result_from_plan=%s" % plan_name)
- self.onecmd(infra_log_upload_command)
- if self.GetSerials():
- self.onecmd("device --update=stop")
- logging.getLogger().removeHandler(file_handler)
- os.remove(self._logfile_path)
- return (ret != False), dest
-
- def _Print(self, string):
- """Prints a string and a new line character.
-
- Args:
- string: The string to be printed.
- """
- self._out_file.write(string + "\n")
-
- def _PrintObjects(self, objects, attr_names):
- """Shows objects as a table.
-
- Args:
- object: The objects to be shown, one object in a row.
- attr_names: The attributes to be shown, one attribute in a column.
- """
- width = [len(name) for name in attr_names]
- rows = [attr_names]
- for dev_info in objects:
- attrs = [
- _ToPrintString(getattr(dev_info, name, ""))
- for name in attr_names
- ]
- rows.append(attrs)
- for index, attr in enumerate(attrs):
- width[index] = max(width[index], len(attr))
-
- for row in rows:
- self._Print(" ".join(
- attr.ljust(width[index]) for index, attr in enumerate(row)))
-
- def DownloadTestResources(self, request_id):
- """Download all of the test resources for a TFC request id.
-
- Args:
- request_id: int, TFC request id
- """
- resources = self._tfc_client.TestResourceList(request_id)
- for resource in resources:
- self.DownloadTestResource(resource['url'])
-
- def DownloadTestResource(self, url):
- """Download a test resource with build provider, given a url.
-
- Args:
- url: a resource locator (not necessarily HTTP[s])
- with the scheme specifying the build provider.
- """
- parsed = urlparse.urlparse(url)
- path = (parsed.netloc + parsed.path).split('/')
- if parsed.scheme == "pab":
- if len(path) != 5:
- logging.error("Invalid pab resource locator: %s", url)
- return
- account_id, branch, target, build_id, artifact_name = path
- cmd = ("fetch"
- " --type=pab"
- " --account_id=%s"
- " --branch=%s"
- " --target=%s"
- " --build_id=%s"
- " --artifact_name=%s") % (account_id, branch, target,
- build_id, artifact_name)
- self.onecmd(cmd)
- elif parsed.scheme == "ab":
- if len(path) != 4:
- logging.error("Invalid ab resource locator: %s", url)
- return
- branch, target, build_id, artifact_name = path
- cmd = ("fetch"
- "--type=ab"
- " --branch=%s"
- " --target=%s"
- " --build_id=%s"
- " --artifact_name=%s") % (branch, target, build_id,
- artifact_name)
- self.onecmd(cmd)
- elif parsed.scheme == gcs:
- cmd = "fetch --type=gcs --path=%s" % url
- self.onecmd(cmd)
- else:
- logging.error("Invalid URL: %s", url)
-
- def SetSerials(self, serials):
- """Sets the default serial numbers for flashing and testing.
-
- Args:
- serials: A list of strings, the serial numbers.
- """
- self._serials = serials
-
- def FlashImgPackage(self, package_path_gcs):
- """Fetches a repackaged image set from GCS and flashes to the device(s).
-
- Args:
- package_path_gcs: GCS URL to the packaged img zip file. May contain
- the GSI imgs.
- """
- self.onecmd("fetch --type=gcs --path=%s --full_device_images=True" %
- package_path_gcs)
- if common.FULL_ZIPFILE not in self.device_image_info:
- logging.error("Failed to fetch the given file: %s",
- package_path_gcs)
- return False
-
- if not self._serials:
- logging.error("Please specify the serial number(s) of target "
- "device(s) for flashing.")
- return False
-
- campaign_common = imp.load_source(
- 'campaign_common',
- os.path.join(os.getcwd(), "host_controller", "campaigns",
- "campaign_common.py"))
- flash_command_list = []
-
- for serial in self._serials:
- flash_commands = []
- cmd_utils.ExecuteOneShellCommand(
- "adb -s %s reboot bootloader" % serial)
- _, stderr, retcode = cmd_utils.ExecuteOneShellCommand(
- "fastboot -s %s getvar product" % serial)
- if retcode == 0:
- res = stderr.splitlines()[0].rstrip()
- if ":" in res:
- product = res.split(":")[1].strip()
- elif "waiting for %s" % serial in res:
- res = stderr.splitlines()[1].rstrip()
- product = res.split(":")[1].strip()
- else:
- product = "error"
- else:
- product = "error"
- logging.info("Device %s product type: %s", serial, product)
- if product in campaign_common.FLASH_COMMAND_EMITTER:
- flash_commands.append(
- campaign_common.FLASH_COMMAND_EMITTER[product](
- serial, repacked_imageset=True))
- elif product != "error":
- flash_commands.append(
- "flash --current --serial %s --skip-vbmeta=True" % serial)
- else:
- logging.error(
- "Device %s does not exist. Omitting the flashing "
- "to the device.", serial)
- continue
- flash_command_list.append(flash_commands)
-
- ret = self.onecmd(flash_command_list)
- if ret == False:
- logging.error("Flash failed on device %s.", self._serials)
- else:
- logging.info("Flash succeeded on device %s.", self._serials)
-
- return ret
-
- def GetSerials(self):
- """Returns the serial numbers saved in the console.
-
- Returns:
- A list of strings, the serial numbers.
- """
- return self._serials
-
- def ResetSerials(self):
- """Clears all the serial numbers set to this console obj."""
- self._serials = []
-
- def JobThread(self):
- """Job thread which monitors and uploads results."""
- thread = threading.currentThread()
- while getattr(thread, "keep_running", True):
- time.sleep(1)
-
- if self._job_pool:
- self._job_pool.close()
- self._job_pool.terminate()
- self._job_pool.join()
-
- def StartJobThreadAndProcessPool(self):
- """Starts a background thread to control leased jobs."""
- self._job_in_queue = multiprocessing.Queue()
- self._job_out_queue = multiprocessing.Queue()
- self._job_pool = NonDaemonizedPool(
- common._MAX_LEASED_JOBS, JobMain,
- (self._vti_address, self._job_in_queue, self._job_out_queue,
- self._device_status, self._password, self._hosts))
-
- self._job_thread = threading.Thread(target=self.JobThread)
- self._job_thread.daemon = True
- self._job_thread.start()
-
- def StopJobThreadAndProcessPool(self):
- """Terminates the thread and processes that runs the leased job."""
- if hasattr(self, "_job_thread"):
- self._job_thread.keep_running = False
- self._job_thread.join()
-
- def WaitForJobsToExit(self):
- """Wait for the running jobs to complete before exiting HC."""
- if self._job_pool:
- pool_process_count = common._MAX_LEASED_JOBS
- for _ in range(common._MAX_LEASED_JOBS):
- self._job_in_queue.put("exit")
-
- while True:
- response = self._job_out_queue.get()
- if response == "exit":
- pool_process_count -= 1
- if pool_process_count <= 0:
- break
-
- # @Override
- def onecmd(self, line, depth=1, ret_out_queue=None):
- """Executes command(s) and prints any exception.
-
- Parallel execution only for 2nd-level list element.
-
- Args:
- line: a list of string or string which keeps the command to run.
- """
- if not line:
- return
-
- if type(line) == list:
- if depth == 1: # 1 to use multi-threading
- jobs = []
- ret_queue = multiprocessing.Queue()
- for sub_command in line:
- p = multiprocessing.Process(
- target=self.onecmd,
- args=(
- sub_command,
- depth + 1,
- ret_queue,
- ))
- jobs.append(p)
- p.start()
- for job in jobs:
- job.join()
-
- ret_cmd_list = True
- while not ret_queue.empty():
- ret_from_subprocess = ret_queue.get()
- ret_cmd_list = ret_cmd_list and ret_from_subprocess
- if ret_cmd_list == False:
- return False
- else:
- for sub_command in line:
- ret_cmd_list = self.onecmd(sub_command, depth + 1)
- if ret_cmd_list == False and ret_out_queue:
- ret_out_queue.put(False)
- return False
- return
-
- logging.info("Command: %s", line)
- try:
- ret_cmd = cmd.Cmd.onecmd(self, line)
- if ret_cmd == False and ret_out_queue:
- ret_out_queue.put(ret_cmd)
- return ret_cmd
- except Exception as e:
- self._Print("%s: %s" % (type(e).__name__, e))
- if ret_out_queue:
- ret_out_queue.put(False)
- return False
-
- # @Override
- def emptyline(self):
- """Ignores empty lines."""
- pass
-
- # @Override
- def default(self, line):
- """Handles unrecognized commands.
-
- Returns:
- True if receives EOF; otherwise delegates to default handler.
- """
- if line == "EOF":
- return self.do_exit(line)
- return cmd.Cmd.default(self, line)
-
-
-def _ToPrintString(obj):
- """Converts an object to printable string on console.
-
- Args:
- obj: The object to be printed.
- """
- if isinstance(obj, (list, tuple, set)):
- return ",".join(str(x) for x in obj)
- return str(obj)
diff --git a/harnesses/host_controller/console_argument_parser.py b/harnesses/host_controller/console_argument_parser.py
deleted file mode 100644
index 71a756f..0000000
--- a/harnesses/host_controller/console_argument_parser.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import argparse
-import logging
-
-
-class ConsoleArgumentError(Exception):
- """Raised when the console fails to parse commands."""
- pass
-
-
-class ConsoleArgumentParser(argparse.ArgumentParser):
- """The argument parser for a console command."""
-
- def __init__(self, command_name, description):
- """Initializes the ArgumentParser without --help option.
-
- Args:
- command_name: A string, the first argument of the command.
- description: The help message about the command.
- """
- super(ConsoleArgumentParser, self).__init__(
- prog=command_name, description=description, add_help=False)
-
- def ParseLine(self, line):
- """Parses a command line.
-
- Args:
- line: A string, the command line.
-
- Returns:
- An argparse.Namespace object.
- """
- return self.parse_args(line.split())
-
- # @Override
- def error(self, message):
- """Raises an exception when failing to parse the command.
-
- Args:
- message: The error message.
-
- Raises:
- ConsoleArgumentError.
- """
- raise ConsoleArgumentError(message) \ No newline at end of file
diff --git a/harnesses/host_controller/console_test.py b/harnesses/host_controller/console_test.py
deleted file mode 100644
index bd4906a..0000000
--- a/harnesses/host_controller/console_test.py
+++ /dev/null
@@ -1,263 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-try:
- import StringIO as string_io_module
-except ImportError:
- import io as string_io_module
-
-from host_controller.build import build_flasher
-from host_controller.tfc import command_task
-from host_controller.tfc import device_info
-from host_controller import common
-from host_controller import console
-
-
-class ConsoleTest(unittest.TestCase):
- """A test for console.Console.
-
- Attribute:
- _out_file: The console output buffer.
- _host_controller: A mock tfc_host_controller.HostController.
- _build_provider_pab: A mock build_provider_pab.BuildProviderPAB.
- _tfc_client: A mock tfc_client.TfcClient.
- _vti_client A mock vti_endpoint_client.VtiEndpointClient.
- _console: The console being tested.
- """
- _DEVICES = [
- device_info.DeviceInfo(
- device_serial="ABC001",
- run_target="sailfish",
- state="Available",
- build_id="111111",
- sdk_version="27")
- ]
- _TASKS = [
- command_task.CommandTask(
- request_id="1",
- task_id="1-0",
- command_id="2",
- command_line="vts -m SampleShellTest",
- device_serials=["ABC001"])
- ]
-
- def setUp(self):
- """Creates the console."""
- self._out_file = string_io_module.StringIO()
- self._host_controller = mock.Mock()
- self._build_provider_pab = mock.Mock()
- self._tfc_client = mock.Mock()
- self._vti_client = mock.Mock()
- self._console = console.Console(
- self._vti_client,
- self._tfc_client,
- self._build_provider_pab, [self._host_controller],
- None,
- out_file=self._out_file)
- self._console.device_image_info = {}
-
- def tearDown(self):
- """Closes the output file."""
- self._out_file.close()
-
- def _IssueCommand(self, command_line):
- """Issues a command in the console.
-
- Args:
- command_line: A string, the input to the console.
-
- Returns:
- A string, the output of the console.
- """
- out_position = self._out_file.tell()
- self._console.onecmd(command_line)
- self._out_file.seek(out_position)
- return self._out_file.read()
-
- def testLease(self):
- """Tests the lease command."""
- self._host_controller.LeaseCommandTasks.return_value = self._TASKS
- output = self._IssueCommand("lease")
- expected = (
- "request_id command_id task_id device_serials command_line \n"
- "1 2 1-0 ABC001 vts -m SampleShellTest\n"
- )
- self.assertEqual(expected, output)
- output = self._IssueCommand("lease --host 0")
- self.assertEqual(expected, output)
-
- def testRequest(self):
- """Tests the request command."""
- user = "user0"
- cluster = "cluster0"
- run_target = "sailfish"
- command_line = "vts -m SampleShellTest"
- self._IssueCommand("request --user %s --cluster %s --run-target %s "
- "-- %s" % (user, cluster, run_target, command_line))
- req = self._tfc_client.NewRequest.call_args[0][0]
- self.assertEqual(user, req.user)
- self.assertEqual(cluster, req.cluster)
- self.assertEqual(run_target, req.run_target)
- self.assertEqual(command_line, req.command_line)
-
- def testListHosts(self):
- """Tests the list command."""
- self._host_controller.hostname = "host0"
- output = self._IssueCommand("list hosts")
- self.assertEqual("index name\n" "[ 0] host0\n", output)
-
- def testListDevices(self):
- """Tests the list command."""
- self._host_controller.ListDevices.return_value = self._DEVICES
- self._host_controller.hostname = "host0"
- output = self._IssueCommand("list devices")
- expected = (
- "[ 0] host0\n"
- "device_serial state run_target build_id sdk_version stub\n"
- "ABC001 Available sailfish 111111 27 \n"
- )
- self.assertEqual(expected, output)
- output = self._IssueCommand("list devices --host 0")
- self.assertEqual(expected, output)
-
- def testWrongHostIndex(self):
- """Tests host index out of range."""
- output = self._IssueCommand("list devices --host 1")
- expected = "IndexError: "
- self.assertTrue(output.startswith(expected))
- output = self._IssueCommand("lease --host 1")
- self.assertTrue(output.startswith(expected))
-
- @mock.patch('host_controller.build.build_flasher.BuildFlasher')
- def testFetchPOSTAndFlash(self, mock_class):
- """Tests fetching from pab and flashing."""
- self._build_provider_pab.GetArtifact.return_value = ({
- "system.img":
- "/mock/system.img",
- "odm.img":
- "/mock/odm.img"
- }, {}, {
- "build_id":
- "build_id"
- }, {})
- self._build_provider_pab.GetFetchedArtifactType.return_value = common._ARTIFACT_TYPE_DEVICE
- self._IssueCommand(
- "fetch --branch=aosp-master-ndk --target=darwin_mac "
- "--account_id=100621237 "
- "--artifact_name=foo-{build_id}.tar.bz2 --method=POST")
- self._build_provider_pab.GetArtifact.assert_called_with(
- account_id='100621237',
- branch='aosp-master-ndk',
- target='darwin_mac',
- artifact_name='foo-{build_id}.tar.bz2',
- build_id='latest',
- method='POST',
- full_device_images=False)
- self.assertEqual(self._console.device_image_info, {
- "system.img": "/mock/system.img",
- "odm.img": "/mock/odm.img"
- })
-
- flasher = mock.Mock()
- mock_class.return_value = flasher
- self._IssueCommand("flash --current system=system.img odm=odm.img")
- flasher.Flash.assert_called_with({
- "system": "/mock/system.img",
- "odm": "/mock/odm.img"
- }, False)
-
- def testFetchAndEnvironment(self):
- """Tests fetching from pab and check stored os environment"""
- build_id_return = "4328532"
- target_return = "darwin_mac"
- expected_fetch_info = {"build_id": build_id_return}
-
- self._build_provider_pab.GetArtifact.return_value = ({
- "system.img":
- "/mock/system.img",
- "odm.img":
- "/mock/odm.img"
- }, {}, expected_fetch_info, {})
- self._IssueCommand(
- "fetch --branch=aosp-master-ndk --target=%s "
- "--account_id=100621237 "
- "--artifact_name=foo-{id}.tar.bz2 --method=POST" % target_return)
- self._build_provider_pab.GetArtifact.assert_called_with(
- account_id='100621237',
- branch='aosp-master-ndk',
- target='darwin_mac',
- artifact_name='foo-{id}.tar.bz2',
- build_id='latest',
- full_device_images=False,
- method='POST')
-
- expected = expected_fetch_info["build_id"]
- self.assertEqual(build_id_return, expected)
-
- @mock.patch('host_controller.build.build_flasher.BuildFlasher')
- def testFlashGSI(self, mock_class):
- flasher = mock.Mock()
- mock_class.return_value = flasher
- self._IssueCommand("flash --gsi=system.img")
- flasher.FlashGSI.assert_called_with(
- 'system.img', None, skip_vbmeta=False)
-
- @mock.patch('host_controller.build.build_flasher.BuildFlasher')
- def testFlashGSIWithVbmeta(self, mock_class):
- flasher = mock.Mock()
- mock_class.return_value = flasher
- self._IssueCommand("flash --gsi=system.img --vbmeta=vbmeta.img")
- flasher.FlashGSI.assert_called_with(
- 'system.img', 'vbmeta.img', skip_vbmeta=False)
-
- @mock.patch('host_controller.build.build_flasher.BuildFlasher')
- def testFlashall(self, mock_class):
- flasher = mock.Mock()
- mock_class.return_value = flasher
- self._IssueCommand("flash --build_dir=path/to/dir/")
- flasher.Flashall.assert_called_with('path/to/dir/')
-
- @mock.patch('host_controller.command_processor.command_flash.importlib')
- @mock.patch('host_controller.command_processor.command_flash.issubclass')
- def testImportFlasher(self, mock_issubclass, mock_importlib):
- mock_issubclass.return_value = True
- flasher_module = mock.Mock()
- flasher = mock.Mock()
- mock_importlib.import_module.return_value = flasher_module
- flasher_module.Flasher.return_value = flasher
- self._IssueCommand("flash --serial ABC001 "
- "--flasher_type test.flasher.Flasher "
- "--flasher_path /test/flasher "
- "-- --unit test")
- mock_issubclass.assert_called_once_with(flasher_module.Flasher,
- build_flasher.BuildFlasher)
- mock_importlib.import_module.assert_called_with("test.flasher")
- flasher_module.Flasher.assert_called_with("ABC001", "/test/flasher")
- flasher.Flash.assert_called_with({}, {}, "--unit", "test")
- flasher.WaitForDevice.assert_called_with()
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/gsi/change_security_patch_ver.sh b/harnesses/host_controller/gsi/change_security_patch_ver.sh
deleted file mode 100755
index 1a09178..0000000
--- a/harnesses/host_controller/gsi/change_security_patch_ver.sh
+++ /dev/null
@@ -1,288 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script modifies the original GSI to match the vendor version and
-# the security patch level.
-#
-# Usage: change_security_patch_ver.sh <system.img> [<output_system.img> \
-# [<new_security_patch_level> [<input_file_contexts.bin>]]] \
-# [-v <vendor_version>]
-#
-# Examples:
-# change_security_patch_ver.sh system.img
-# - Shows current version information.
-# change_security_patch_ver.sh system.img new_system.img 2018-04-05
-# - Make new_system.img that has replaced SPL with 2018-04-05.
-# change_security_patch_ver.sh system.img new_system.img -v 8.1.0
-# - Make new_system.img that includes the patches for vendor version 8.1.0.
-# change_security_patch_ver.sh system.img new_system.img 2018-04-05 -v 8.1.0
-# - Make new_system.img that has both new SPL and vendor version.
-
-function unmount() {
- echo "Unmounting..."
- sudo umount "${MOUNT_POINT}/"
-}
-
-SCRIPT_NAME=$(basename $0)
-
-declare -a SUPPORTED_VENDOR_VERSIONS=(
- 8.1.0
- 9
-)
-SUPPORTED_VENDOR_VERSIONS="${SUPPORTED_VENDOR_VERSIONS[@]}"
-
-param_count=0
-while [[ $# -gt 0 ]]
-do
-case $1 in
--v|--vendor) # set vendor version
- VENDOR_VERSION=$2
- shift
- shift
- ;;
-*) # set the ordered parameters
- ((param_count++))
- case $param_count in
- 1) # The input file name for original GSI
- SYSTEM_IMG=$1
- shift
- ;;
- 2) # The output file name for modified GSI
- OUTPUT_SYSTEM_IMG=$1
- shift
- ;;
- 3) # New Security Patch Level to be written. It must be YYYY-MM-DD format.
- NEW_SPL=$1
- shift
- ;;
- 4) # Selinux file context
- FILE_CONTEXTS_BIN=$1
- shift
- ;;
- *)
- ERROR=true
- break
- ;;
- esac
- ;;
-esac
-done
-
-if ((param_count == 0)) || [ "$ERROR" == "true" ]; then
- echo "Usage: $SCRIPT_NAME <system.img> [<output_system.img> [<new_security_patch_level> [<input_file_contexts.bin>]]] [-v <vendor_version>]"
- exit 1
-fi
-
-# SPL must have YYYY-MM-DD format
-if ((param_count >= 3)) && [[ ! ${NEW_SPL} =~ ^[0-9]{4}-(0[0-9]|1[012])-([012][0-9]|3[01])$ ]]; then
- echo "<new_security_patch_level> must have YYYY-MM-DD format"
- exit 1
-fi
-
-if [ "$VENDOR_VERSION" != "" ] && [[ ! ${VENDOR_VERSION} =~ ^(${SUPPORTED_VENDOR_VERSIONS// /\|})$ ]]; then
- echo "Available vendor_version: $SUPPORTED_VENDOR_VERSIONS"
- exit 1
-fi
-
-if [ "$VENDOR_VERSION" != "" ] && [ "$OUTPUT_SYSTEM_IMG" == "" ]; then
- echo "<output_system.img> must be provided to set vendor version"
- exit 1
-fi
-
-REQUIRED_BINARIES_LIST=(
- "img2simg"
- "simg2img"
-)
-if [ ! -z "${FILE_CONTEXTS_BIN}" ]; then
- REQUIRED_BINARIES_LIST+=("mkuserimg_mke2fs")
-fi
-
-# number of binaries to find.
-BIN_COUNT=${#REQUIRED_BINARIES_LIST[@]}
-
-# use an associative array to store binary path
-declare -A BIN_PATH
-for bin in ${REQUIRED_BINARIES_LIST[@]}; do
- BIN_PATH[${bin}]=""
-done
-
-# check current PATH environment first
-for bin in ${REQUIRED_BINARIES_LIST[@]}; do
- if command -v ${bin} >/dev/null 2>&1; then
- echo "found ${bin} in PATH."
- BIN_PATH[${bin}]=${bin}
- ((BIN_COUNT--))
- fi
-done
-
-if [ ${BIN_COUNT} -gt 0 ]; then
- # listed in the recommended order.
- PATH_LIST=("${PWD}")
- if [ "${PWD##*/}" == "testcases" ] && [ -d "${PWD}/../bin" ]; then
- PATH_LIST+=("${PWD}/../bin")
- fi
- if [ -d "${ANDROID_HOST_OUT}" ]; then
- PATH_LIST+=("${ANDROID_HOST_OUT}/bin")
- fi
-
- for dir in ${PATH_LIST[@]}; do
- for bin in ${REQUIRED_BINARIES_LIST[@]}; do
- if [ -z "${BIN_PATH[${bin}]}" ] && [ -f "${dir}/${bin}" ]; then
- echo "found ${bin} in ${dir}."
- BIN_PATH[${bin}]=${dir}/${bin}
- ((BIN_COUNT--))
- if [ ${BIN_COUNT} -eq 0 ]; then break; fi
- fi
- done
- done
-fi
-
-if [ ${BIN_COUNT} -gt 0 ]; then
- echo "Cannot find the required binaries. Need lunch; or run in a correct path."
- exit 1
-fi
-echo "Found all binaries."
-
-MOUNT_POINT="${PWD}/temp_mnt"
-SPL_PROPERTY_NAME="ro.build.version.security_patch"
-RELEASE_VERSION_PROPERTY_NAME="ro.build.version.release"
-VNDK_VERSION_PROPERTY="ro.vndk.version"
-VNDK_VERSION_PROPERTY_OMR1="${VNDK_VERSION_PROPERTY}=27"
-
-UNSPARSED_SYSTEM_IMG="${SYSTEM_IMG}.raw"
-SYSTEM_IMG_MAGIC="$(xxd -g 4 -l 4 "$SYSTEM_IMG" | head -n1 | awk '{print $2}')"
-if [ "$SYSTEM_IMG_MAGIC" = "3aff26ed" ]; then
- echo "Unsparsing ${SYSTEM_IMG}..."
- ${BIN_PATH["simg2img"]} "$SYSTEM_IMG" "$UNSPARSED_SYSTEM_IMG"
-else
- echo "Copying unsparse input system image ${SYSTEM_IMG}..."
- cp "$SYSTEM_IMG" "$UNSPARSED_SYSTEM_IMG"
-fi
-
-IMG_SIZE=$(stat -c%s "$UNSPARSED_SYSTEM_IMG")
-
-echo "Mounting..."
-mkdir -p "$MOUNT_POINT"
-sudo mount -t ext4 -o loop "$UNSPARSED_SYSTEM_IMG" "${MOUNT_POINT}/"
-
-# check the property file path
-BUILD_PROP_PATH_LIST=(
- "/system/build.prop" # layout of A/B support
- "/build.prop" # layout of non-A/B support
-)
-BUILD_PROP_MOUNT_PATH=""
-BUILD_PROP_PATH=""
-
-echo "Finding build.prop..."
-for path in ${BUILD_PROP_PATH_LIST[@]}; do
- if [ -f "${MOUNT_POINT}${path}" ]; then
- BUILD_PROP_MOUNT_PATH="${MOUNT_POINT}${path}"
- BUILD_PROP_PATH=${path}
- echo " ${path}"
- break
- fi
-done
-
-PROP_DEFAULT_PATH_LIST=(
- "/system/etc/prop.default" # layout of A/B support
- "/etc/prop.default" # layout of non-A/B support
-)
-
-if [ "$BUILD_PROP_MOUNT_PATH" != "" ]; then
- if [ "$OUTPUT_SYSTEM_IMG" != "" ]; then
- echo "Replacing..."
- fi
- CURRENT_SPL=`sudo sed -n -r "s/^${SPL_PROPERTY_NAME}=(.*)$/\1/p" ${BUILD_PROP_MOUNT_PATH}`
- CURRENT_VERSION=`sudo sed -n -r "s/^${RELEASE_VERSION_PROPERTY_NAME}=(.*)$/\1/p" ${BUILD_PROP_MOUNT_PATH}`
- echo " Current security patch level: ${CURRENT_SPL}"
- echo " Current release version: ${CURRENT_VERSION}"
-
- # Update SPL to <new_security_patch_level>
- if [[ "$OUTPUT_SYSTEM_IMG" != "" && "$NEW_SPL" != "" ]]; then
- if [[ "$CURRENT_SPL" == "" ]]; then
- echo "ERROR: Cannot find ${SPL_PROPERTY_NAME} in ${BUILD_PROP_PATH}"
- else
- echo " New security patch level: ${NEW_SPL}"
- seek=$(sudo grep --byte-offset "${SPL_PROPERTY_NAME}=" "${BUILD_PROP_MOUNT_PATH}" | cut -d':' -f 1)
- seek=$(($seek + ${#SPL_PROPERTY_NAME} + 1)) # 1 is for '='
- echo "${NEW_SPL}" | sudo dd of="${BUILD_PROP_MOUNT_PATH}" seek="$seek" bs=1 count=10 conv=notrunc
- fi
- fi
-
- # Update release version to <vendor_version>
- if [[ "$OUTPUT_SYSTEM_IMG" != "" && "$VENDOR_VERSION" != "" ]]; then
- if [[ "$CURRENT_VERSION" == "" ]]; then
- echo "ERROR: Cannot find ${RELEASE_VERSION_PROPERTY_NAME} in ${BUILD_PROP_PATH}"
- else
- echo " New release version for vendor.img: ${VENDOR_VERSION}"
- sudo sed -i -e "s/^${RELEASE_VERSION_PROPERTY_NAME}=.*$/${RELEASE_VERSION_PROPERTY_NAME}=${VENDOR_VERSION}/" ${BUILD_PROP_MOUNT_PATH}
- fi
-
- if [[ "$VENDOR_VERSION" == "8.1.0" ]]; then
- # add ro.vndk.version for O-MR1
- echo "Finding prop.default..."
- for path in ${PROP_DEFAULT_PATH_LIST[@]}; do
- if [ -f "${MOUNT_POINT}${path}" ]; then
- PROP_DEFAULT_PATH=${path}
- echo " ${path}"
- break
- fi
- done
-
- if [[ "$PROP_DEFAULT_PATH" != "" ]]; then
- CURRENT_VNDK_VERSION=`sudo sed -n -r "s/^${VNDK_VERSION_PROPERTY}=(.*)$/\1/p" ${MOUNT_POINT}${PROP_DEFAULT_PATH}`
- if [[ "$CURRENT_VNDK_VERSION" != "" ]]; then
- echo "WARNING: ${VNDK_VERSION_PROPERTY} is already set to ${CURRENT_VNDK_VERSION} in ${PROP_DEFAULT_PATH}"
- else
- echo " Add \"${VNDK_VERSION_PROPERTY_OMR1}\" to ${PROP_DEFAULT_PATH} for O-MR1 vendor image."
- sudo sed -i -e "\$a\#\n\# FOR O-MR1 DEVICES\n\#\n${VNDK_VERSION_PROPERTY_OMR1}" ${MOUNT_POINT}${PROP_DEFAULT_PATH}
- fi
- else
- echo "ERROR: Cannot find prop.default."
- fi
- fi
- fi
-else
- echo "ERROR: Cannot find build.prop."
-fi
-
-if [ "$OUTPUT_SYSTEM_IMG" != "" ]; then
- if [ "$FILE_CONTEXTS_BIN" != "" ]; then
- echo "Writing ${OUTPUT_SYSTEM_IMG}..."
-
- (cd $ANDROID_BUILD_TOP
- if [[ "$(whereis mkuserimg_mke2fs | wc -w)" < 2 ]]; then
- make mkuserimg_mke2fs -j
- fi
- NON_AB=$(expr "$BUILD_PROP_PATH" == "/build.prop")
- if [ $NON_AB -eq 1 ]; then
- sudo /bin/bash -c "PATH=out/host/linux-x86/bin/:\$PATH mkuserimg_mke2fs -s ${MOUNT_POINT} $OUTPUT_SYSTEM_IMG ext4 system $IMG_SIZE -D ${MOUNT_POINT} -L system $FILE_CONTEXTS_BIN"
- else
- sudo /bin/bash -c "PATH=out/host/linux-x86/bin/:\$PATH mkuserimg_mke2fs -s ${MOUNT_POINT} $OUTPUT_SYSTEM_IMG ext4 / $IMG_SIZE -D ${MOUNT_POINT}/system -L / $FILE_CONTEXTS_BIN"
- fi)
-
- unmount
- else
- unmount
-
- echo "Writing ${OUTPUT_SYSTEM_IMG}..."
- ${BIN_PATH["img2simg"]} "$UNSPARSED_SYSTEM_IMG" "$OUTPUT_SYSTEM_IMG"
- fi
-else
- unmount
-fi
-
-echo "Done."
diff --git a/harnesses/host_controller/invocation_thread.py b/harnesses/host_controller/invocation_thread.py
deleted file mode 100644
index b8760f3..0000000
--- a/harnesses/host_controller/invocation_thread.py
+++ /dev/null
@@ -1,169 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import socket
-import threading
-
-import httplib2
-from googleapiclient import errors
-
-from host_controller.tfc import command_attempt
-from host_controller.tradefed import remote_operation
-
-
-class InvocationThread(threading.Thread):
- """The thread that remotely executes a command task.
-
- Attributes:
- _remote_client: The RemoteClient which executes the command.
- _tfc_client: The TfcClient to which the command events are sent.
- _attempt: The CommandAttempt whose events are sent to TFC.
- _command: A list of strings, the command and arguments.
- device_serials: A list of strings, the serial numbers of the devices
- which need to be allocated to the task.
- _allocated_serials: A list of strings, the serial numbers of the devices
- which are successfully allocated.
- _tfc_heartbeat_interval: The interval of TestRunInProgress events in
- seconds.
- """
-
- def __init__(self,
- remote_client,
- tfc_client,
- attempt,
- command,
- device_serials,
- tfc_heartbeat_interval=5 * 60):
- """Initializes the attributes."""
- super(InvocationThread, self).__init__()
- self._remote_client = remote_client
- self._tfc_client = tfc_client
- self._attempt = attempt
- self._command = command
- self.device_serials = device_serials
- self._allocated_serials = None
- # The value in Java implementation is 5 minutes.
- self._tfc_heartbeat_interval = tfc_heartbeat_interval
-
- def _AllocateDevices(self):
- """Allocates all of device_serial."""
- for serial in self.device_serials:
- self._remote_client.SendOperation(
- remote_operation.AllocateDevice(serial))
- self._allocated_serials.append(serial)
-
- def _StartInvocation(self):
- """Starts executing command and sends the event to TFC."""
- self._remote_client.SendOperation(
- remote_operation.ExecuteCommand(self.device_serials[0],
- *self._command))
- event = self._attempt.CreateCommandEvent(
- command_attempt.EventType.INVOCATION_STARTED)
- self._tfc_client.SubmitCommandEvents([event])
-
- def _WaitForCommandResult(self):
- """Waits for command result and keeps sending heartbeat to TFC
-
- Returns:
- A JSON object returned from TradeFed remote manager.
- """
- while True:
- result = self._remote_client.WaitForCommandResult(
- self.device_serials[0], self._tfc_heartbeat_interval)
- if result:
- return result
- event = self._attempt.CreateCommandEvent(
- command_attempt.EventType.TEST_RUN_IN_PROGRESS)
- self._tfc_client.SubmitCommandEvents([event])
-
- def _CompleteInvocation(self, result):
- """Sends InvocationCompleted event according to the result.
-
- Args:
- result: A JSON object returned from TradeFed remote manager.
- """
- if result["status"] == "INVOCATION_SUCCESS":
- event = self._attempt.CreateInvocationCompletedEvent(
- str(result), 1, 0)
- else:
- event = self._attempt.CreateInvocationCompletedEvent(
- str(result), 1, 1, error=str(result))
- self._tfc_client.SubmitCommandEvents([event])
-
- def _FreeAllocatedDevices(self):
- """Frees allocated devices and tolerates RemoteOperationException."""
- for serial in self._allocated_serials:
- try:
- self._remote_client.SendOperation(
- remote_operation.FreeDevice(serial))
- except remote_operation.RemoteOperationException as e:
- logging.exception(e)
- except socket.error as e:
- logging.exception(e)
- break
- self._allocated_serials = []
-
- def _SubmitErrorEvent(self, event_type, error_msg):
- """Submits an error event and tolerates http exceptions.
-
- Args:
- event_type: A string, the type of the command event.
- error_msg: A string, the error message.
- """
- try:
- self._tfc_client.SubmitCommandEvents(
- [self._attempt.CreateCommandEvent(event_type, error_msg)])
- except (httplib2.HttpLib2Error, errors.HttpError) as e:
- logging.exception(e)
-
- # @Override
- def run(self):
- """Executes a command task with exception handling."""
- self._allocated_serials = []
- last_error = None
- error_event = command_attempt.EventType.ALLOCATION_FAILED
- try:
- self._AllocateDevices()
- error_event = command_attempt.EventType.EXECUTE_FAILED
- self._StartInvocation()
- result = self._WaitForCommandResult()
- self._CompleteInvocation(result)
- error_event = None
- except errors.HttpError as e:
- logging.exception(e)
- last_error = e
- except remote_operation.RemoteOperationException as e:
- logging.exception(e)
- last_error = e
- # ConfigurationException on TradeFed remote manager.
- if str(e).startswith("Config error: "):
- error_event = command_attempt.EventType.CONFIGURATION_ERROR
- except httplib2.HttpLib2Error as e:
- logging.exception("Cannot communicate with TradeFed cluster: %s\n"
- "Skip submitting event %s.", e, error_event)
- last_error = e
- error_event = None
- except socket.error as e:
- logging.exception("Cannot communicate with TradeFed remote "
- "manager: %s\nSkip freeing devices %s.",
- e, self._allocated_serials)
- last_error = e
- self._allocated_serials = []
- finally:
- if error_event:
- self._SubmitErrorEvent(error_event, str(last_error))
- self._FreeAllocatedDevices()
diff --git a/harnesses/host_controller/invocation_thread_test.py b/harnesses/host_controller/invocation_thread_test.py
deleted file mode 100644
index 07847b9..0000000
--- a/harnesses/host_controller/invocation_thread_test.py
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import invocation_thread
-from host_controller.tfc import command_attempt
-from host_controller.tradefed import remote_operation
-
-
-class InvocationThreadTest(unittest.TestCase):
- """A test for invocation_thread.InvocationThread.
-
- Attributes:
- _remote_client: A mock remote_client.RemoteClient.
- _tfc_client: A mock tfc_client.TfcClient.
- _inv_thread: The InvocationThread being tested.
- """
-
- def setUp(self):
- """Creates the InvocationThread."""
- self._remote_client = mock.Mock()
- self._tfc_client = mock.Mock()
- attempt = command_attempt.CommandAttempt(
- task_id="321-0",
- attempt_id="abcd-1234",
- hostname="host0",
- device_serial="ABCDEF")
- command = ["vts", "-m", "SampleShellTest"]
- serials = ["serial123", "serial456"]
- self._inv_thread = invocation_thread.InvocationThread(
- self._remote_client, self._tfc_client,
- attempt, command, serials)
-
- def _GetSubmittedEventTypes(self):
- """Gets the types of the events submitted by the mock TfcClient.
-
- Returns:
- A list of strings, the event types.
- """
- event_types = []
- for args, kwargs in self._tfc_client.SubmitCommandEvents.call_args_list:
- event_types.extend(event["type"] for event in args[0])
- return event_types
-
- def _GetSentOperationTypes(self):
- """Gets the types of the operations sent by the mock RemoteClient.
-
- Returns:
- A list of strings, the operation types.
- """
- operation_types = [args[0].type for args, kwargs in
- self._remote_client.SendOperation.call_args_list]
- return operation_types
-
- def testAllocationFailed(self):
- """Tests AllocationFailed event."""
- self._remote_client.SendOperation.side_effect = (
- lambda op: _RaiseExceptionForOperation(op, "ALLOCATE_DEVICE"))
- self._inv_thread.run()
- self.assertEqual([command_attempt.EventType.ALLOCATION_FAILED],
- self._GetSubmittedEventTypes())
- self.assertEqual(["ALLOCATE_DEVICE"],
- self._GetSentOperationTypes())
-
- def testExecuteFailed(self):
- """Tests ExecuteFailed event."""
- self._remote_client.SendOperation.side_effect = (
- lambda op: _RaiseExceptionForOperation(op, "EXEC_COMMAND"))
- self._inv_thread.run()
- self.assertEqual([command_attempt.EventType.EXECUTE_FAILED],
- self._GetSubmittedEventTypes())
- self.assertEqual(["ALLOCATE_DEVICE",
- "ALLOCATE_DEVICE",
- "EXEC_COMMAND",
- "FREE_DEVICE",
- "FREE_DEVICE"],
- self._GetSentOperationTypes())
-
- def testConfigurationError(self):
- """Tests ConfigurationError event."""
- self._remote_client.SendOperation.side_effect = (
- lambda op: _RaiseExceptionForOperation(op, "EXEC_COMMAND",
- "Config error: test"))
- self._inv_thread.run()
- self.assertEqual([command_attempt.EventType.CONFIGURATION_ERROR],
- self._GetSubmittedEventTypes())
- self.assertEqual(["ALLOCATE_DEVICE",
- "ALLOCATE_DEVICE",
- "EXEC_COMMAND",
- "FREE_DEVICE",
- "FREE_DEVICE"],
- self._GetSentOperationTypes())
-
- def testInvocationCompleted(self):
- """Tests InvocationCompleted event."""
- self._remote_client.WaitForCommandResult.side_effect = (
- None, {"status": "INVOCATION_SUCCESS"})
- self._inv_thread.run()
- self.assertEqual([command_attempt.EventType.INVOCATION_STARTED,
- command_attempt.EventType.TEST_RUN_IN_PROGRESS,
- command_attempt.EventType.INVOCATION_COMPLETED],
- self._GetSubmittedEventTypes())
- # GET_LAST_COMMAND_RESULT isn't called in mock WaitForCommandResult.
- self.assertEqual(["ALLOCATE_DEVICE",
- "ALLOCATE_DEVICE",
- "EXEC_COMMAND",
- "FREE_DEVICE",
- "FREE_DEVICE"],
- self._GetSentOperationTypes())
-
-
-def _RaiseExceptionForOperation(operation, op_type, error_msg="unit test"):
- """Raises exception for specific operation type.
-
- Args:
- operation: A remote_operation.RemoteOperation object.
- op_type: A string, the expected type.
- error_msg: The message in the exception.
-
- Raises:
- RemoteOperationException if the operation's type matches op_type.
- """
- if operation.type == op_type:
- raise remote_operation.RemoteOperationException(error_msg)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/main.py b/harnesses/host_controller/main.py
deleted file mode 100644
index 348134e..0000000
--- a/harnesses/host_controller/main.py
+++ /dev/null
@@ -1,227 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import argparse
-import json
-import logging
-import socket
-import time
-import threading
-import sys
-
-from host_controller import console
-from host_controller import tfc_host_controller
-from host_controller.build import build_provider_pab
-from host_controller.tfc import tfc_client
-from host_controller.vti_interface import vti_endpoint_client
-from host_controller.tradefed import remote_client
-from vts.utils.python.os import env_utils
-
-_ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP"
-_SECONDS_PER_UNIT = {
- "m": 60,
- "h": 60 * 60,
- "d": 60 * 60 * 24
-}
-
-
-def _ParseInterval(interval_str):
- """Parses string to time interval.
-
- Args:
- interval_str: string, a floating-point number followed by time unit.
-
- Returns:
- float, the interval in seconds.
-
- Raises:
- ValueError if the argument format is wrong.
- """
- if not interval_str:
- raise ValueError("Argument is empty.")
-
- unit = interval_str[-1]
- if unit not in _SECONDS_PER_UNIT:
- raise ValueError("Unknown unit: %s" % unit)
-
- interval = float(interval_str[:-1])
- if interval < 0:
- raise ValueError("Invalid time interval: %s" % interval)
-
- return interval * _SECONDS_PER_UNIT[unit]
-
-
-def _ScriptLoop(hc_console, script_path, loop_interval):
- """Runs a console script repeatedly.
-
- Args:
- hc_console: the host controller console.
- script_path: string, the path to the script.
- loop_interval: float or integer, the interval in seconds.
- """
- next_start_time = time.time()
- while hc_console.ProcessScript(script_path):
- if loop_interval == 0:
- continue
- current_time = time.time()
- skip_cnt = (current_time - next_start_time) // loop_interval
- if skip_cnt >= 1:
- logging.warning("Script execution time is longer than loop "
- "interval. Skip %d iteration(s).", skip_cnt)
- next_start_time += (skip_cnt + 1) * loop_interval
- if next_start_time - current_time >= 0:
- time.sleep(next_start_time - current_time)
- else:
- logging.error("Unexpected timestamps: current=%f, next=%f",
- current_time, next_start_time)
-
-
-def main():
- """Parses arguments and starts console."""
- parser = argparse.ArgumentParser()
- parser.add_argument("--config-file",
- default=None,
- type=argparse.FileType('r'),
- help="The configuration file in JSON format")
- parser.add_argument("--poll", action="store_true",
- help="Disable console and start host controller "
- "threads polling TFC.")
- parser.add_argument("--use-tfc", action="store_true",
- help="Enable TFC (TradeFed Cluster).")
- parser.add_argument("--vti",
- default=None,
- help="The base address of VTI endpoint APIs")
- parser.add_argument("--script",
- default=None,
- help="The path to a script file in .py format")
- parser.add_argument("--serial",
- default=None,
- help="The default serial numbers for flashing and "
- "testing in the console. Multiple serial numbers "
- "are separated by comma.")
- parser.add_argument("--loop",
- default=None,
- metavar="INTERVAL",
- type=_ParseInterval,
- help="The interval of repeating the script. "
- "The format is a float followed by unit which is "
- "one of 'm' (minute), 'h' (hour), and 'd' (day). "
- "If this option is unspecified, the script will "
- "be processed once.")
- parser.add_argument("--console", action="store_true",
- help="Whether to start a console after processing "
- "a script.")
- parser.add_argument("--password",
- default=None,
- help="Password string to pass to the prompt "
- "when running certain command as root previlege.")
- parser.add_argument("--flash",
- default=None,
- help="GCS URL to an img package. Fetches and flashes "
- "the device(s) given as the '--serial' flag.")
- args = parser.parse_args()
- if args.config_file:
- config_json = json.load(args.config_file)
- else:
- config_json = {}
- config_json["log_level"] = "DEBUG"
- config_json["hosts"] = []
- host_config = {}
- host_config["cluster_ids"] = ["local-cluster-1",
- "local-cluster-2"]
- host_config["lease_interval_sec"] = 30
- config_json["hosts"].append(host_config)
-
- env_vars = env_utils.SaveAndClearEnvVars([_ANDROID_BUILD_TOP])
-
- root_logger = logging.getLogger()
- root_logger.setLevel(getattr(logging, config_json["log_level"]))
-
- if args.vti:
- vti_endpoint = vti_endpoint_client.VtiEndpointClient(args.vti)
- else:
- vti_endpoint = None
-
- tfc = None
- if args.use_tfc:
- if args.config_file:
- tfc = tfc_client.CreateTfcClient(
- config_json["tfc_api_root"],
- config_json["service_key_json_path"],
- api_name=config_json["tfc_api_name"],
- api_version=config_json["tfc_api_version"],
- scopes=config_json["tfc_scopes"])
- else:
- logging.warning("WARN: If --use_tfc is set, --config_file argument "
- "value must be provided. Starting without TFC.")
-
- pab = build_provider_pab.BuildProviderPAB()
-
- hosts = []
- for host_config in config_json["hosts"]:
- cluster_ids = host_config["cluster_ids"]
- # If host name is not specified, use local host.
- hostname = host_config.get("hostname", socket.gethostname())
- port = host_config.get("port", remote_client.DEFAULT_PORT)
- cluster_ids = host_config["cluster_ids"]
- remote = remote_client.RemoteClient(hostname, port)
- host = tfc_host_controller.HostController(remote, tfc, hostname,
- cluster_ids)
- hosts.append(host)
- if args.poll:
- lease_interval_sec = host_config["lease_interval_sec"]
- host_thread = threading.Thread(target=host.Run,
- args=(lease_interval_sec,))
- host_thread.daemon = True
- host_thread.start()
-
- if args.poll:
- while True:
- sys.stdin.readline()
- else:
- main_console = console.Console(vti_endpoint, tfc, pab, hosts,
- vti_address=args.vti,
- password=args.password)
- if args.vti:
- main_console.StartJobThreadAndProcessPool()
- else:
- logging.warning("vti address is not set. example : "
- "$ run --vti=<url>")
-
- try:
- if args.serial:
- main_console.SetSerials(args.serial.split(","))
- if args.script:
- if args.loop is None:
- main_console.ProcessScript(args.script)
- else:
- _ScriptLoop(main_console, args.script, args.loop)
-
- if args.console:
- main_console.cmdloop()
- elif args.flash:
- main_console.FlashImgPackage(args.flash)
- else: # if not script, the default is console mode.
- main_console.cmdloop()
- finally:
- main_console.TearDown()
-
- env_utils.RestoreEnvVars(env_vars)
-
-
-if __name__ == "__main__":
- main()
diff --git a/harnesses/host_controller/script/pip_requirements.txt b/harnesses/host_controller/script/pip_requirements.txt
deleted file mode 100644
index 92a83cb..0000000
--- a/harnesses/host_controller/script/pip_requirements.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# Required pip packages for host controller.
-enum
-future
-futures
-google-api-python-client
-google-cloud-pubsub
-gspread
-httplib2
-multiprocessing
-protobuf
-requests
-selenium
-setuptools
diff --git a/harnesses/host_controller/script/release.sh b/harnesses/host_controller/script/release.sh
deleted file mode 100755
index 969cb6f..0000000
--- a/harnesses/host_controller/script/release.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-# VTS lab release script
-# Usage: <this script> prod|test
-# Caution: only used by a VTS lab release engineer
-rm out/host/linux-x86/vtslab/android-vtslab/testcases/tmp/ -rf
-. build/envsetup.sh
-lunch aosp_arm64
-make WifiUtil -j
-mkdir -p out/host/linux-x86/vtslab/android-vtslab/testcases/DATA/app
-cp out/target/product/generic_arm64/testcases/WifiUtil/ out/host/linux-x86/vtslab/android-vtslab/testcases/DATA/app/ -rf
-make vtslab -j
-
-if [ $# -eq 1 ]; then
- ls -al out/host/linux-x86/vtslab/android-vtslab.zip
- echo "To upload, please run:"
- echo gsutil cp out/host/linux-x86/vtslab/android-vtslab.zip gs://vtslab-release/$1/android-vtslab-$(date +%F).zip
-fi
diff --git a/harnesses/host_controller/tfc/__init__.py b/harnesses/host_controller/tfc/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/tfc/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/tfc/api_message.py b/harnesses/host_controller/tfc/api_message.py
deleted file mode 100644
index d1b618e..0000000
--- a/harnesses/host_controller/tfc/api_message.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-
-class ApiMessage(object):
- """The class for the messages defined by TFC API."""
-
- def __init__(self, all_keys, **kwargs):
- """Initializes the attributes.
-
- Args:
- all_keys: A collection of attribute names.
- **kwargs: The names and values of the attributes. The names must be
- in all_keys.
-
- Raises:
- KeyError if any key in kwargs is not in all_keys.
- """
- for key, value in kwargs.iteritems():
- if key not in all_keys:
- raise KeyError(key)
- setattr(self, key, value)
-
- def ToJson(self, keys):
- """Creates a JSON object containing the specified keys.
-
- Args:
- keys: The attributes to be included in the object.
-
- Returns:
- A JSON object.
- """
- return dict((x, getattr(self, x)) for x in keys if hasattr(self, x))
diff --git a/harnesses/host_controller/tfc/command_attempt.py b/harnesses/host_controller/tfc/command_attempt.py
deleted file mode 100644
index 930ec71..0000000
--- a/harnesses/host_controller/tfc/command_attempt.py
+++ /dev/null
@@ -1,137 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import time
-
-from host_controller.tfc import api_message
-
-
-class EventType(object):
- """The types of command events."""
- ALLOCATION_FAILED = "AllocationFailed"
- CONFIGURATION_ERROR = "ConfigurationError"
- EXECUTE_FAILED = "ExecuteFailed"
- FETCH_FAILED = "FetchFailed"
- INVOCATION_COMPLETED = "InvocationCompleted"
- INVOCATION_STARTED = "InvocationStarted"
- TEST_RUN_IN_PROGRESS = "TestRunInProgress"
-
-
-class CommandAttempt(api_message.ApiMessage):
- """The command attempt defined by TFC API.
-
- Attributes:
- _COMMAND_EVENT: The parameters of command_events.submit.
- _COMMAND_EVENT_DATA: The fields in "data" parameter of command_events.
- _LIST_ATTEMPT: The fields returned by commandAttempts.list.
- """
- _COMMAND_EVENT = {
- "attempt_id",
- "data",
- "device_serial",
- "hostname",
- "task_id",
- "time",
- "type"}
- _COMMAND_EVENT_DATA = {
- "error",
- "failed_test_count",
- "summary",
- "test_run_name",
- "total_test_count"}
- _LIST_ATTEMPT = {
- "attempt_id",
- "command_id",
- "create_time",
- "end_time",
- "error",
- "device_serial",
- "failed_test_count",
- "hostname",
- "request_id",
- "start_time",
- "state",
- "status",
- "summary",
- "task_id",
- "total_test_count",
- "update_time"}
-
- def __init__(self, task_id, attempt_id, hostname, device_serial, **kwargs):
- """Initializes the attributes.
-
- Args:
- task_id: A string, the task id assigned by the server.
- attempt_id: A string or UUID, the attempt id generated by the host.
- hostname: The name of the TradeFed host.
- device_serial: The serial number of the device.
- **kwargs: The optional attributes.
- """
- super(CommandAttempt, self).__init__(self._LIST_ATTEMPT,
- task_id=task_id,
- attempt_id=str(attempt_id),
- hostname=hostname,
- device_serial=device_serial,
- **kwargs)
-
- def CreateCommandEvent(self, event_type, error=None, event_time=None):
- """Creates an event defined by command_events.submit.
-
- Args:
- event_type: A string in EventType.
- error: A string, the error message for *Failed, *Error, and
- *Completed events.
- event_time: A float, Unix timestamp of the event in seconds.
-
- Returns:
- A JSON object.
- """
- obj = self.ToJson(self._COMMAND_EVENT)
- obj["type"] = event_type
- obj["time"] = int(event_time if event_time is not None else time.time())
- data_obj = self.ToJson(self._COMMAND_EVENT_DATA)
- if error is not None:
- data_obj["error"] = error
- if data_obj:
- obj["data"] = data_obj
- return obj
-
- def CreateInvocationCompletedEvent(self,
- summary,
- total_test_count,
- failed_test_count,
- error=None,
- event_time=None):
- """Creates an InvocationCompleted event.
-
- Args:
- summary: A string, the result of the command.
- total_test_count: Number of test cases.
- failed_test_count: Number of failed test cases.
- error: A string, the error message.
- event_time: A float, Unix timestamp of the event in seconds.
-
- Returns:
- A JSON object.
- """
- obj = self.CreateCommandEvent(EventType.INVOCATION_COMPLETED,
- error, event_time)
- if "data" not in obj:
- obj["data"] = dict()
- obj["data"].update({"summary": summary,
- "total_test_count": total_test_count,
- "failed_test_count": failed_test_count})
- return obj
diff --git a/harnesses/host_controller/tfc/command_task.py b/harnesses/host_controller/tfc/command_task.py
deleted file mode 100644
index d4aa53e..0000000
--- a/harnesses/host_controller/tfc/command_task.py
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.tfc import api_message
-
-
-class CommandTask(api_message.ApiMessage):
- """The task of executing a command defined by TFC API.
-
- Attributes:
- _LEASE_HOST_TASK: The fields returned by commandAttempts.list.
- """
- _LEASE_HOST_TASK = {
- "request_id",
- "command_id",
- "task_id",
- "command_line",
- "request_type",
- "device_serials"}
-
- def __init__(self, task_id, command_line, device_serials, **kwargs):
- super(CommandTask, self).__init__(self._LEASE_HOST_TASK,
- task_id=task_id,
- command_line=command_line,
- device_serials=device_serials,
- **kwargs)
diff --git a/harnesses/host_controller/tfc/device_info.py b/harnesses/host_controller/tfc/device_info.py
deleted file mode 100644
index 6a1b35d..0000000
--- a/harnesses/host_controller/tfc/device_info.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.tfc import api_message
-from vts.utils.python.controllers import android_device
-
-
-class DeviceInfo(api_message.ApiMessage):
- """The device information defined by TFC API.
-
- Attributes:
- _DEVICE_SNAPSHOT: The parameters of host_events.
- _LEASE_HOST_TASKS: The parameters of leasehosttasks.
- _OTHER: The data retrieved from TradeFed host but not used by the API.
- _ALL_KEYS: Union of above.
- """
- _DEVICE_SNAPSHOT = {
- "battery_level",
- "build_id",
- "device_serial",
- "group_name",
- "mac_address",
- "product",
- "product_variant",
- "run_target",
- "sim_operator",
- "sim_state",
- "sdk_version",
- "state"}
- _LEASE_HOST_TASKS = {
- "device_serial",
- "group_name",
- "run_target",
- "state"}
- _OTHER = {"stub"}
- _ALL_KEYS = (_DEVICE_SNAPSHOT | _LEASE_HOST_TASKS | _OTHER)
-
- def __init__(self, device_serial, **kwargs):
- """Initializes the attributes.
-
- Args:
- device_serial: The serial number of the device.
- **kwargs: The optional attributes.
- """
- super(DeviceInfo, self).__init__(self._ALL_KEYS,
- device_serial=device_serial, **kwargs)
-
- def IsAvailable(self):
- """Returns whether the device is available for running commands.
-
- Returns:
- A boolean.
- """
- return getattr(self, "state", None) == "Available"
-
- def IsStub(self):
- """Returns whether the device is a stub.
-
- Returns:
- A boolean.
- """
- return getattr(self, "stub", False)
-
- def ToDeviceSnapshotJson(self):
- """Converts to the parameter of host_events.
-
- Returns:
- A JSON object.
- """
- return self.ToJson(self._DEVICE_SNAPSHOT)
-
- def ToLeaseHostTasksJson(self):
- """Converts to the parameter of leasehosttasks.
-
- Returns:
- A JSON object.
- """
- return self.ToJson(self._LEASE_HOST_TASKS)
-
- def Extend(self, properties):
- """Adds properties to a DeviceInfo object, using AndroidDevice
-
- Args:
- properties: list of strings, list of properties to update
- """
- serial = getattr(self, "device_serial", None)
- ad = android_device.AndroidDevice(serial)
- for prop in properties:
- val = getattr(ad, prop, None)
- if val is None:
- continue
- setattr(self, prop, val)
diff --git a/harnesses/host_controller/tfc/request.py b/harnesses/host_controller/tfc/request.py
deleted file mode 100644
index a086bf2..0000000
--- a/harnesses/host_controller/tfc/request.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from host_controller.tfc import api_message
-
-
-class Request(api_message.ApiMessage):
- """The requests defined by TFC API.
-
- Attributes:
- _BODY: The requests.new parameters that are put into http message body.
- _PARAMETERS: The requests.new parameters put into http parameters.
- _ALL_KEYS: Union of above.
- """
- _BODY = {
- "command_line",
- "user"}
- _PARAMETERS = {
- "branch",
- "build_flavor",
- "build_id",
- "build_os",
- "cluster",
- "no_build_args",
- "run_target",
- "shard_count"
- "run_count"}
- _ALL_KEYS = (_BODY | _PARAMETERS)
-
- def __init__(self, cluster, command_line, run_target, user, **kwargs):
- """Initializes the attributes.
-
- Args:
- cluster: The ID of the cluster to send this request to.
- command_line: The command to execute on a host.
- run_target: The target device to run the command.
- user: The name of the user sending this request.
- **kwargs: The optional attributes.
- """
- super(Request, self).__init__(self._ALL_KEYS,
- cluster=cluster,
- command_line=command_line,
- run_target=run_target,
- user=user,
- **kwargs)
-
- def GetBody(self):
- """Returns the http message body.
-
- Returns:
- A JSON object.
- """
- return self.ToJson(self._BODY)
-
- def GetParameters(self):
- """Returns the http parameters.
-
- Returns:
- A dict of strings.
- """
- return self.ToJson(self._PARAMETERS)
diff --git a/harnesses/host_controller/tfc/tfc_client.py b/harnesses/host_controller/tfc/tfc_client.py
deleted file mode 100644
index 8380573..0000000
--- a/harnesses/host_controller/tfc/tfc_client.py
+++ /dev/null
@@ -1,176 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import httplib2
-import logging
-import threading
-import time
-
-from googleapiclient import discovery
-from googleapiclient import http
-from oauth2client.service_account import ServiceAccountCredentials
-
-from host_controller.tfc import command_task
-
-API_NAME = "tradefed_cluster"
-API_VERSION = "v1"
-SCOPES = ['https://www.googleapis.com/auth/userinfo.email']
-
-
-class TfcClient(object):
- """The class for accessing TFC API.
-
- Attributes:
- _service: The TFC service.
- """
-
- def __init__(self, service):
- self._service = service
-
- def LeaseHostTasks(self, cluster_id, next_cluster_ids, hostname, device_infos):
- """Calls leasehosttasks.
-
- Args:
- cluster_id: A string, the primary cluster to lease tasks from.
- next_cluster_ids: A list of Strings, the secondary clusters to lease
- tasks from.
- hostname: A string, the name of the TradeFed host.
- device_infos: A list of DeviceInfo, the information about the
- devices connected to the host.
-
- Returns:
- A list of command_task.CommandTask, the leased tasks.
- """
- lease = {"hostname": hostname,
- "cluster": cluster_id,
- "next_cluster_ids": next_cluster_ids,
- "device_infos": [x.ToLeaseHostTasksJson()
- for x in device_infos]}
- logging.info("tasks.leasehosttasks body=%s", lease)
- tasks = self._service.tasks().leasehosttasks(body=lease).execute()
- logging.info("tasks.leasehosttasks response=%s", tasks)
- if "tasks" not in tasks:
- return []
- return [command_task.CommandTask(**task) for task in tasks["tasks"]]
-
- def TestResourceList(self, request_id):
- """Calls testResource.list.
-
- Args:
- request_id: int, id of request to grab resources for
-
- Returns:
- A list of TestResources
- """
- logging.info("request.testResource.list request_id=%s", request_id)
- test_resources = self._service.requests().testResource().list(request_id=request_id).execute()
- logging.info("request.testResource.list response=%s", test_resources)
- if 'test_resources' not in test_resources:
- return {}
- return test_resources['test_resources']
-
- @staticmethod
- def CreateDeviceSnapshot(cluster_id, hostname, dev_infos):
- """Creates a DeviceSnapshot which can be uploaded as host event.
-
- Args:
- cluster_id: A string, the cluster to upload snapshot to.
- hostname: A string, the name of the TradeFed host.
- dev_infos: A list of DeviceInfo.
-
- Returns:
- A JSON object.
- """
- obj = {"time": int(time.time()),
- "data": {},
- "cluster": cluster_id,
- "hostname": hostname,
- "tf_version": "(unknown)",
- "type": "DeviceSnapshot",
- "device_infos": [x.ToDeviceSnapshotJson() for x in dev_infos]}
- return obj
-
- def SubmitHostEvents(self, host_events):
- """Calls host_events.submit.
-
- Args:
- host_events: A list of JSON objects. Currently DeviceSnapshot is
- the only type of host events.
- """
- json_obj = {"host_events": host_events}
- logging.info("host_events.submit body=%s", json_obj)
- self._service.host_events().submit(body=json_obj).execute()
-
- def SubmitCommandEvents(self, command_events):
- """Calls command_events.submit.
-
- Args:
- command_events: A list of JSON objects converted from CommandAttempt.
- """
- json_obj = {"command_events": command_events}
- logging.info("command_events.submit body=%s", json_obj)
- self._service.command_events().submit(body=json_obj).execute()
-
- def NewRequest(self, request):
- """Calls requests.new.
-
- Args:
- request: An instance of Request.
-
- Returns:
- A JSON object, the new request queued in the cluster.
-
- Sample
- {'state': 'UNKNOWN',
- 'command_line': 'vts-codelab --run-target sailfish',
- 'id': '2',
- 'user': 'testuser'}
- """
- body = request.GetBody()
- params = request.GetParameters()
- logging.info("requests.new parameters=%s body=%s", params, body)
- return self._service.requests().new(body=body, **params).execute()
-
-
-def CreateTfcClient(api_root, oauth2_service_json,
- api_name=API_NAME, api_version=API_VERSION, scopes=SCOPES):
- """Builds an object of TFC service from a given URL.
-
- Args:
- api_root: The URL to the service.
- oauth2_service_json: The path to service account key file.
-
- Returns:
- A TfcClient object.
- """
- discovery_url = "%s/discovery/v1/apis/%s/%s/rest" % (
- api_root, api_name, api_version)
- logging.info("Build service from: %s", discovery_url)
- credentials = ServiceAccountCredentials.from_json_keyfile_name(
- oauth2_service_json, scopes=scopes)
- # httplib2.Http is not thread-safe. Use thread local object.
- thread_local = threading.local()
- thread_local.http = credentials.authorize(httplib2.Http())
- def BuildHttpRequest(unused_http, *args, **kwargs):
- if not hasattr(thread_local, "http"):
- thread_local.http = credentials.authorize(httplib2.Http())
- return http.HttpRequest(thread_local.http, *args, **kwargs)
-
- service = discovery.build(
- api_name, api_version, http=thread_local.http,
- discoveryServiceUrl=discovery_url,
- requestBuilder=BuildHttpRequest)
- return TfcClient(service)
diff --git a/harnesses/host_controller/tfc/tfc_client_test.py b/harnesses/host_controller/tfc/tfc_client_test.py
deleted file mode 100644
index 10db850..0000000
--- a/harnesses/host_controller/tfc/tfc_client_test.py
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller.tfc import tfc_client
-from host_controller.tfc import command_attempt
-from host_controller.tfc import device_info
-from host_controller.tfc import request
-
-
-class TfcClientTest(unittest.TestCase):
- """A test for tfc_client.TfcClient.
-
- Attributes:
- _client: The tfc_client.TfcClient being tested.
- _service: The mock service that _client connects to.
- """
- _DEVICE_INFOS = [device_info.DeviceInfo(
- device_serial="ABCDEF", group_name="group0",
- run_target="sailfish", state="Available")]
-
- def setUp(self):
- """Creates a TFC client connecting to a mock service."""
- self._service = mock.Mock()
- self._client = tfc_client.TfcClient(self._service)
-
- def testNewRequest(self):
- """Tests requests.new."""
- req = request.Request(cluster="cluster0",
- command_line="vts-codelab",
- run_target="sailfish",
- user="user0")
- self._client.NewRequest(req)
- self._service.assert_has_calls([
- mock.call.requests().new().execute()])
-
- def testLeaseHostTasks(self):
- """Tests tasks.leasehosttasks."""
- tasks = {"tasks": [{"request_id": "2",
- "command_line": "vts-codelab --serial ABCDEF",
- "task_id": "1-0",
- "device_serials": ["ABCDEF"],
- "command_id": "1"}]}
- self._service.tasks().leasehosttasks().execute.return_value = tasks
- self._client.LeaseHostTasks("cluster0", ["cluster1", "cluster2"],
- "host0", self._DEVICE_INFOS)
- self._service.assert_has_calls([
- mock.call.tasks().leasehosttasks().execute()])
-
- def testHostEvents(self):
- """Tests host_events.submit."""
- device_snapshots = [
- self._client.CreateDeviceSnapshot("vts-staging", "host0",
- self._DEVICE_INFOS),
- self._client.CreateDeviceSnapshot("vts-presubmit", "host0",
- self._DEVICE_INFOS)]
- self._client.SubmitHostEvents(device_snapshots)
- self._service.assert_has_calls([
- mock.call.host_events().submit().execute()])
-
- def testCommandEvents(self):
- """Tests command_events.submit."""
- cmd = command_attempt.CommandAttempt(
- task_id="321-0",
- attempt_id="abcd-1234",
- hostname="host0",
- device_serial="ABCDEF")
- expected_event = {
- "task_id": "321-0",
- "attempt_id": "abcd-1234",
- "hostname": "host0",
- "device_serial": "ABCDEF",
- "time": 1}
-
- normal_event = cmd.CreateCommandEvent(
- command_attempt.EventType.INVOCATION_STARTED,
- event_time=1)
- expected_event["type"] = command_attempt.EventType.INVOCATION_STARTED
- self.assertDictEqual(expected_event, normal_event)
-
- error_event = cmd.CreateCommandEvent(
- command_attempt.EventType.EXECUTE_FAILED,
- error="unit test", event_time=1.1)
- expected_event["type"] = command_attempt.EventType.EXECUTE_FAILED
- expected_event["data"] = {"error":"unit test"}
- self.assertDictEqual(expected_event, error_event)
-
- complete_event = cmd.CreateInvocationCompletedEvent(
- summary="complete", total_test_count=2, failed_test_count=1,
- error="unit test")
- expected_event["type"] = command_attempt.EventType.INVOCATION_COMPLETED
- expected_event["data"] = {"summary": "complete", "error": "unit test",
- "total_test_count": 2, "failed_test_count": 1}
- del expected_event["time"]
- self.assertDictContainsSubset(expected_event, complete_event)
- self.assertIn("time", complete_event)
-
- self._client.SubmitCommandEvents([
- normal_event, error_event, complete_event])
- self._service.assert_has_calls([
- mock.call.command_events().submit().execute()])
-
- def testWrongParameter(self):
- """Tests raising exception for wrong parameter name."""
- self.assertRaisesRegexp(KeyError, "sdk", device_info.DeviceInfo,
- device_serial="123", sdk="25")
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/tfc_host_controller.py b/harnesses/host_controller/tfc_host_controller.py
deleted file mode 100644
index 6e8fb23..0000000
--- a/harnesses/host_controller/tfc_host_controller.py
+++ /dev/null
@@ -1,140 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import socket
-import time
-import uuid
-
-import httplib2
-from googleapiclient import errors
-
-from host_controller import invocation_thread
-from host_controller.tradefed import remote_operation
-from host_controller.tfc import command_attempt
-
-
-class HostController(object):
- """The class that relays commands between a TradeFed host and clusters.
-
- Attributes:
- _remote_client: The RemoteClient which runs commands.
- _tfc_client: The TfcClient from which the command tasks are leased.
- _hostname: A string, the name of the TradeFed host.
- _cluster_ids: A list of strings, the cluster IDs for leasing tasks.
- _invocation_threads: The list of running InvocationThread.
- """
-
- def __init__(self, remote_client, tfc_client, hostname, cluster_ids):
- """Initializes the attributes."""
- self._remote_client = remote_client
- self._tfc_client = tfc_client
- self._hostname = hostname
- self._cluster_ids = cluster_ids
- self._invocation_threads = []
-
- @property
- def hostname(self):
- """Returns the name of the host."""
- return self._hostname
-
- def _JoinInvocationThreads(self):
- """Removes terminated threads from _invocation_threads."""
- alive_threads = []
- for inv_thread in self._invocation_threads:
- inv_thread.join(0)
- if inv_thread.is_alive():
- alive_threads.append(inv_thread)
- self._invocation_threads = alive_threads
-
- def _CreateInvocationThread(self, task):
- """Creates an invocation thread from a command task.
-
- Args:
- task: The CommandTask object.
-
- Returns:
- An InvocationThread.
- """
- attempt_id = uuid.uuid4()
- attempt = command_attempt.CommandAttempt(
- task.task_id, attempt_id,
- self._hostname, task.device_serials[0])
- inv_thread = invocation_thread.InvocationThread(
- self._remote_client, self._tfc_client, attempt,
- task.command_line.split(), task.device_serials)
- return inv_thread
-
- def ListDevices(self):
- """Lists present devices on the host.
-
- Returns:
- A list of DeviceInfo.
- """
- devices = self._remote_client.ListDevices()
- return [dev for dev in devices if not dev.IsStub()]
-
- def ListAvailableDevices(self):
- """Lists available devices for command tasks.
-
- Returns:
- A list of DeviceInfo.
- """
- self._JoinInvocationThreads()
- allocated_serials = set()
- for inv_thread in self._invocation_threads:
- allocated_serials.update(inv_thread.device_serials)
-
- present_devices = self.ListDevices()
- return [dev for dev in present_devices if
- dev.IsAvailable() and
- dev.device_serial not in allocated_serials]
-
- def LeaseCommandTasks(self):
- """Leases command tasks and creates threads to execute them.
-
- Returns:
- A list of CommandTask. The leased command tasks.
- """
- available_devices = self.ListAvailableDevices()
- if not available_devices:
- return []
-
- tasks = self._tfc_client.LeaseHostTasks(
- self._cluster_ids[0], self._cluster_ids[1:],
- self._hostname, available_devices)
- for task in tasks:
- inv_thread = self._CreateInvocationThread(task)
- inv_thread.daemon = True
- inv_thread.start()
- self._invocation_threads.append(inv_thread)
- return tasks
-
- def Run(self, poll_interval):
- """Starts polling TFC for tasks.
-
- Args:
- poll_interval: The poll interval in seconds.
- """
- while True:
- try:
- self.LeaseCommandTasks()
- except (socket.error,
- remote_operation.RemoteOperationException,
- httplib2.HttpLib2Error,
- errors.HttpError) as e:
- logging.exception(e)
- time.sleep(poll_interval)
diff --git a/harnesses/host_controller/tfc_host_controller_test.py b/harnesses/host_controller/tfc_host_controller_test.py
deleted file mode 100644
index 0d4b8b5..0000000
--- a/harnesses/host_controller/tfc_host_controller_test.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import threading
-import unittest
-import time
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import tfc_host_controller
-from host_controller.tfc import command_task
-from host_controller.tfc import device_info
-
-
-class HostControllerTest(unittest.TestCase):
- """A test for tfc_host_controller.HostController.
-
- Args:
- _remote_client: A mock remote_client.RemoteClient.
- _tfc_client: A mock tfc_client.TfcClient.
- _host_controller: The HostController being tested.
- """
- _AVAILABLE_DEVICE = device_info.DeviceInfo(
- device_serial="ABC001",
- run_target="sailfish",
- state="Available")
- _ALLOCATED_DEVICE = device_info.DeviceInfo(
- device_serial="ABC002",
- run_target="sailfish",
- state="Allocated")
- _STUB_DEVICE = device_info.DeviceInfo(
- device_serial="emulator-5554",
- run_target="unknown",
- state="Available",
- stub=True)
- _DEVICES = [_AVAILABLE_DEVICE, _ALLOCATED_DEVICE, _STUB_DEVICE]
- _TASKS = [command_task.CommandTask(task_id="1-0",
- command_line="vts -m SampleShellTest",
- device_serials=["ABC001"])]
-
- def setUp(self):
- """Creates the HostController."""
- self._remote_client = mock.Mock()
- self._tfc_client = mock.Mock()
- self._host_controller = tfc_host_controller.HostController(
- self._remote_client, self._tfc_client, "host1", ["cluster1"])
-
- @mock.patch("host_controller.invocation_thread."
- "InvocationThread.run")
- def testDeviceStateDuringInvocation(self, mock_run):
- """Tests LeaseHostTasks and ListAvailableDevices."""
- self._remote_client.ListDevices.return_value = self._DEVICES
- self._tfc_client.LeaseHostTasks.return_value = self._TASKS
- run_event = threading.Event()
- mock_run.side_effect = lambda: run_event.wait()
-
- self._host_controller.LeaseCommandTasks()
- devices = self._host_controller.ListAvailableDevices()
- self.assertEqual([], devices)
- run_event.set()
- # Wait for thread termination
- time.sleep(0.2)
- devices = self._host_controller.ListAvailableDevices()
- self.assertEqual([self._AVAILABLE_DEVICE], devices)
-
- def testListDevices(self):
- """Tests ListDevices."""
- self._remote_client.ListDevices.return_value = self._DEVICES
- devices = self._host_controller.ListDevices()
- self.assertEqual([self._AVAILABLE_DEVICE, self._ALLOCATED_DEVICE],
- devices)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/tradefed/__init__.py b/harnesses/host_controller/tradefed/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/tradefed/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/tradefed/remote_client.py b/harnesses/host_controller/tradefed/remote_client.py
deleted file mode 100644
index e369ba3..0000000
--- a/harnesses/host_controller/tradefed/remote_client.py
+++ /dev/null
@@ -1,135 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import socket
-import time
-
-from host_controller.tradefed import remote_operation
-
-LOCALHOST = "localhost"
-DEFAULT_PORT = 30103
-
-
-class RemoteClient(object):
- """The class for sending remote operations to TradeFed."""
-
- def __init__(self, host=LOCALHOST, port=DEFAULT_PORT, timeout=None):
- """Initializes the client of TradeFed remote manager.
-
- Args:
- _host: The host name of the remote manager.
- _port: The port of the remote manager.
- _timeout: The connect and receive timeout in seconds
- """
- self._host = host
- self._port = port
- self._timeout = timeout if timeout else socket.getdefaulttimeout()
-
- def SendOperations(self, *operations):
- """Sends a list of operations and waits for each response.
-
- Args:
- *operations: A list of remote_operation.RemoteOperation objects.
-
- Returns:
- A list of JSON objects.
-
- Raises:
- socket.error if fails to communicate with remote manager.
- remote_operation.RemoteOperationException if any operation fails or
- has no response.
- """
- op_socket = socket.create_connection((self._host, self._port),
- self._timeout)
- logging.info("Connect to %s:%d", self._host, self._port)
- try:
- if self._timeout is not None:
- op_socket.settimeout(self._timeout)
- ops_str = "\n".join(str(op) for op in operations)
- logging.info("Send: %s", ops_str)
- op_socket.send(ops_str)
- op_socket.shutdown(socket.SHUT_WR)
- resp_str = ""
- while True:
- buf = op_socket.recv(4096)
- if not buf:
- break
- resp_str += buf
- finally:
- op_socket.close()
- logging.info("Receive: %s", resp_str)
- resp_lines = [x for x in resp_str.split("\n") if x]
- if len(resp_lines) != len(operations):
- raise remote_operation.RemoteOperationException(
- "Unexpected number of responses: %d" % len(resp_lines))
- return [operations[i].ParseResponse(resp_lines[i])
- for i in range(len(resp_lines))]
-
- def SendOperation(self, operation):
- """Sends one operation and waits for its response.
-
- Args:
- operation: A remote_operation.RemoteOperation object.
-
- Returns:
- A JSON object.
-
- Raises:
- socket.error if fails to communicate with remote manager.
- remote_operation.RemoteOperationException if the operation fails or
- has no response.
- """
- return self.SendOperations(operation)[0]
-
- def ListDevices(self):
- """Sends ListDevices operation.
-
- Returns:
- A list of device_info.DeviceInfo which are the devices connected to
- the host.
- """
- json_obj = self.SendOperation(remote_operation.ListDevices())
- return remote_operation.ParseListDevicesResponse(json_obj)
-
- def WaitForCommandResult(self, serial, timeout, poll_interval=5):
- """Sends a series of operations to wait until a command finishes.
-
- Args:
- serial: The serial number of the device.
- timeout: A float, the timeout in seconds.
- poll_interval: A float, the interval of each GetLastCommandResult
- operation in seconds.
-
- Returns:
- A JSON object which is the result of the command.
- None if timeout.
-
- Sample
- {'status': 'INVOCATION_SUCCESS',
- 'free_device_state': 'AVAILABLE'}
- """
- deadline = time.time() + timeout
- get_result_op = remote_operation.GetLastCommandResult(serial)
- while True:
- result = self.SendOperation(get_result_op)
- if result["status"] != "EXECUTING":
- return result
- if time.time() > deadline:
- return None
- time.sleep(poll_interval)
- if time.time() > deadline:
- return None
diff --git a/harnesses/host_controller/tradefed/remote_client_test.py b/harnesses/host_controller/tradefed/remote_client_test.py
deleted file mode 100644
index bc9cbb8..0000000
--- a/harnesses/host_controller/tradefed/remote_client_test.py
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import queue
-import socket
-import threading
-import unittest
-
-from host_controller.tradefed import remote_client
-from host_controller.tradefed import remote_operation
-
-
-class MockRemoteManagerThread(threading.Thread):
- """A thread which mocks remote manager.
-
- Attributes:
- HOST: Local host name.
- PORT: The port that the remote manager listens to.
- _remote_mgr_socket: The remote manager socket.
- _response_queue: A queue.Queue object containing the response strings.
- _timeout: Socket timeout in seconds.
- last_error: The exception which caused this thread to terminate.
- """
- HOST = remote_client.LOCALHOST
- PORT = 32123
-
- def __init__(self, timeout):
- """Creates and listens to remote manager socket."""
- super(MockRemoteManagerThread, self).__init__()
- self._response_queue = queue.Queue()
- self._timeout = timeout
- self.last_error = None
- self._remote_mgr_socket = socket.socket()
- try:
- self._remote_mgr_socket.settimeout(self._timeout)
- self._remote_mgr_socket.bind((self.HOST, self.PORT))
- self._remote_mgr_socket.listen(1)
- except socket.error:
- self._remote_mgr_socket.close()
-
- def _Respond(self, response_str):
- """Accepts a client connection and responds.
-
- Args:
- response_str: The response string.
- """
- (server_socket, client_address) = self._remote_mgr_socket.accept()
- try:
- server_socket.settimeout(self._timeout)
- # Receive until connection is closed
- while not server_socket.recv(4096):
- pass
- server_socket.send(response_str)
- finally:
- server_socket.close()
-
- def AddResponse(self, response_str):
- """Add a response string to the queue.
-
- Args:
- response_str: The response string.
- """
- self._response_queue.put_nowait(response_str)
-
- def CloseSocket(self):
- """Closes the remote manager socket."""
- if self._remote_mgr_socket:
- self._remote_mgr_socket.close()
- self._remote_mgr_socket = None
-
- # @Override
- def run(self):
- """Sends the queued responses to the clients."""
- try:
- while True:
- response_str = self._response_queue.get()
- self._response_queue.task_done()
- if response_str is None:
- break
- self._Respond(response_str)
- except socket.error as e:
- self.last_error = e
- finally:
- self.CloseSocket()
-
-
-class RemoteClientTest(unittest.TestCase):
- """A test for remote_client.RemoteClient.
-
- Attributes:
- _remote_mgr_thread: An instance of MockRemoteManagerThread.
- _client: The remote_client.RemoteClient being tested.
- """
-
- def setUp(self):
- """Creates remote manager thread."""
- self._remote_mgr_thread = MockRemoteManagerThread(5)
- self._remote_mgr_thread.daemon = True
- self._remote_mgr_thread.start()
- self._client = remote_client.RemoteClient(self._remote_mgr_thread.HOST,
- self._remote_mgr_thread.PORT,
- 5)
-
- def tearDown(self):
- """Terminates remote manager thread."""
- self._remote_mgr_thread.AddResponse(None)
- self._remote_mgr_thread.join(15)
- self._remote_mgr_thread.CloseSocket()
- self.assertFalse(self._remote_mgr_thread.is_alive(),
- "Cannot stop remote manager thread.")
- if self._remote_mgr_thread.last_error:
- raise self._remote_mgr_thread.last_error
-
- def testListDevice(self):
- """Tests ListDevices operation."""
- self._remote_mgr_thread.AddResponse('{"serials": []}')
- self._client.ListDevices()
-
- def testAddCommand(self):
- """Tests AddCommand operation."""
- self._remote_mgr_thread.AddResponse('{}')
- self._client.SendOperation(remote_operation.AddCommand(0, "COMMAND"))
-
- def testMultipleOperations(self):
- """Tests sending multiple operations via one connection."""
- self._remote_mgr_thread.AddResponse('{}\n{}')
- self._client.SendOperations(remote_operation.ListDevices(),
- remote_operation.ListDevices())
-
- def testExecuteCommand(self):
- """Tests executing a command and waiting for result."""
- self._remote_mgr_thread.AddResponse('{}')
- self._client.SendOperation(remote_operation.AllocateDevice("serial123"))
- self._remote_mgr_thread.AddResponse('{}')
- self._client.SendOperation(remote_operation.ExecuteCommand(
- "serial123", "vts", "-m", "SampleShellTest"))
-
- self._remote_mgr_thread.AddResponse('{"status": "EXECUTING"}')
- result = self._client.WaitForCommandResult("serial123",
- timeout=0.5, poll_interval=1)
- self.assertIsNone(result, "Client returns result before command finishes.")
-
- self._remote_mgr_thread.AddResponse('{"status": "EXECUTING"}')
- self._remote_mgr_thread.AddResponse('{"status": "INVOCATION_SUCCESS"}')
- result = self._client.WaitForCommandResult("serial123",
- timeout=5, poll_interval=1)
- self._remote_mgr_thread.AddResponse('{}')
- self._client.SendOperation(remote_operation.FreeDevice("serial123"))
- self.assertIsNotNone(result, "Client doesn't return command result.")
-
- def testSocketError(self):
- """Tests raising exception when socket error occurs."""
- self.assertRaises(socket.timeout, self._client.ListDevices)
- self._remote_mgr_thread.AddResponse(None)
- self.assertRaises(socket.error, self._client.ListDevices)
-
- def testRemoteOperationException(self):
- """Tests raising exception when response is an error."""
- self._remote_mgr_thread.AddResponse('{"error": "unit test"}')
- self.assertRaises(remote_operation.RemoteOperationException,
- self._client.ListDevices)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/tradefed/remote_operation.py b/harnesses/host_controller/tradefed/remote_operation.py
deleted file mode 100644
index 00839d4..0000000
--- a/harnesses/host_controller/tradefed/remote_operation.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import json
-
-from host_controller.tfc import device_info
-
-
-class RemoteOperationException(Exception):
- """Raised when remote operation fails."""
- pass
-
-
-class RemoteOperation(object):
- """The operation sent to TradeFed remote manager.
-
- Args:
- _obj: A JSON object with at least 2 entries, "type" and "version".
- """
- CURRENT_PROTOCOL_VERSION = 8
-
- def __init__(self, type, **kwargs):
- """Initializes a remote operation.
-
- Args:
- type: A string, the type of the operation.
- **kwargs: The arguments which are specific to the operation type.
- """
- self._obj = kwargs
- self._obj["type"] = type
- if "version" not in self._obj:
- self._obj["version"] = self.CURRENT_PROTOCOL_VERSION
-
- def ParseResponse(self, response_str):
- """Parses the response to the operation.
-
- Args:
- response_str: A JSON string.
-
- Returns:
- A JSON object.
-
- Raises:
- RemoteOperationException if the response is an error.
- """
- response = json.loads(response_str)
- if "error" in response:
- raise RemoteOperationException(response["error"])
- return response
-
- @property
- def type(self):
- """Returns the type of this operation."""
- return self._obj["type"]
-
- def __str__(self):
- """Converts the JSON object to string."""
- return json.dumps(self._obj)
-
-
-def ListDevices():
- """Creates an operation of listing devices."""
- return RemoteOperation("LIST_DEVICES")
-
-
-def ParseListDevicesResponse(json_obj):
- """Parses ListDevices response to a list of DeviceInfo.
-
- Sample response:
- {"serials": [
- {"product": "unknown", "battery": "0", "variant": "unknown",
- "stub": True, "state": "Available", "build": "unknown",
- "serial": "emulator-5554", "sdk": "unknown"},
- ]}
-
- Args:
- json_obj: A JSON object, the response to ListDevices.
-
- Returns:
- A list of DeviceInfo object.
- """
- dev_infos = []
- for dev_obj in json_obj["serials"]:
- if dev_obj["product"] == dev_obj["variant"]:
- run_target = dev_obj["product"]
- else:
- run_target = dev_obj["product"] + ":" + dev_obj["variant"]
- dev_info = device_info.DeviceInfo(
- battery_level=dev_obj["battery"],
- build_id=dev_obj["build"],
- device_serial=dev_obj["serial"],
- product=dev_obj["product"],
- product_variant=dev_obj["variant"],
- run_target=run_target,
- sdk_version=dev_obj["sdk"],
- state=dev_obj["state"],
- stub=dev_obj["stub"])
- dev_infos.append(dev_info)
- return dev_infos
-
-
-def AllocateDevice(serial):
- """Creates an operation of allocating a device.
-
- Args:
- serial: The serial number of the device.
- """
- return RemoteOperation("ALLOCATE_DEVICE", serial=serial)
-
-
-def FreeDevice(serial):
- """Creates an operation of freeing a device.
-
- Args:
- serial: The serial number of the device.
- """
- return RemoteOperation("FREE_DEVICE", serial=serial)
-
-
-def Close():
- """Creates an operation of stopping the remote manager."""
- return RemoteOperation("CLOSE")
-
-
-def AddCommand(time, *command_args):
- """Creates an operation of adding a command to the queue.
-
- Args:
- time: The time in ms that the command has been executing for. The value
- is non-zero in handover situation.
- command_args: The command to execute.
- """
- return RemoteOperation("ADD_COMMAND", time=time, commandArgs=command_args)
-
-
-def ExecuteCommand(serial, *command_args):
- """Creates an operation of executing a command on a device.
-
- Args:
- serial: The serial number of the device.
- command_args: The command to execute.
- """
- return RemoteOperation(
- "EXEC_COMMAND", serial=serial, commandArgs=command_args)
-
-
-def GetLastCommandResult(serial):
- """Creates an operation of getting last EXEC_COMMAND result on a device.
-
- Sample response:
- {"status": "INVOCATION_ERROR",
- "invocation_error": "java.lang.NullPointerException",
- "free_device_state": "AVAILABLE"
- }
-
- Args:
- serial: The serial number of the device.
- """
- return RemoteOperation("GET_LAST_COMMAND_RESULT", serial=serial)
diff --git a/harnesses/host_controller/utils/__init__.py b/harnesses/host_controller/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/gcp/__init__.py b/harnesses/host_controller/utils/gcp/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/gcp/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/gcp/gcs_utils.py b/harnesses/host_controller/utils/gcp/gcs_utils.py
deleted file mode 100644
index ffe0913..0000000
--- a/harnesses/host_controller/utils/gcp/gcs_utils.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from vts.utils.python.common import cmd_utils
-
-
-def GetGsutilPath():
- """Finds gsutil in PATH.
-
- Instead of a Python library, gsutil binary is used to avoid packaging GCS
- PIP package as part of VTS HC (Host Controller).
-
- Returns:
- The gsutil file path if found; None otherwise.
- """
- sh_stdout, sh_stderr, ret_code = cmd_utils.ExecuteOneShellCommand(
- "which gsutil")
- if ret_code == 0:
- return sh_stdout.strip()
- else:
- logging.fatal("`gsutil` doesn't exist on the host; "
- "please install Google Cloud SDK before retrying.")
- return None
-
-
-def IsGcsFile(gsutil_path, url):
- """Checks whether a given path is for a GCS file.
-
- Args:
- gsutil_path: string, the path of a gsutil binary.
- url: string, the GCS URL. e.g., gs://<bucket>/<file>.
-
- Returns:
- True if url is a file, False otherwise.
- """
- check_command = "%s stat %s" % (gsutil_path, url)
- _, _, ret_code = cmd_utils.ExecuteOneShellCommand(check_command)
- return ret_code == 0
-
-
-def Copy(gsutil_path, src_urls, dst_url):
- """Copies files between local file system and GCS.
-
- Args:
- gsutil_path: string, the path of a gsutil binary.
- src_urls: string, the source paths or GCS URLs separated by spaces.
- dst_url: string, the destination path or GCS URL.
-
- Returns:
- True if the command succeeded, False otherwise.
- """
- copy_command = "%s cp %s %s" % (gsutil_path, src_urls, dst_url)
- _, _, ret_code = cmd_utils.ExecuteOneShellCommand(copy_command)
- return ret_code == 0
-
-
-def List(gsutil_path, url):
- """Lists a directory or file on GCS.
-
- Args:
- gsutil_path: string, the path of a gsutil binary.
- url: string, the GCS URL of the directory or file.
-
- Returns:
- list of strings, the GCS URLs of the listed files.
- """
- ls_command = "%s ls %s" % (gsutil_path, url)
- stdout, _, ret_code = cmd_utils.ExecuteOneShellCommand(ls_command)
- return stdout.strip("\n").split("\n") if ret_code == 0 else []
-
-
-def Remove(gsutil_path, url, recursive=False):
- """Removes a directory or file on GCS.
-
- Args:
- gsutil_path: string, the path of a gsutil binary.
- url: string, the GCS URL of the directory or file.
- recursive: boolean, whether to remove the directory recursively.
-
- Returns:
- True if the command succeeded, False otherwise.
- """
- if "/" not in url.lstrip("gs://").rstrip("/"):
- logging.error("Cannot remove bucket %s", url)
- return False
- rm_command = "%s rm -f%s %s" % (
- gsutil_path, ("r" if recursive else ""), url)
- ret_code = cmd_utils.ExecuteOneShellCommand(rm_command)
- return ret_code == 0
diff --git a/harnesses/host_controller/utils/gsi/__init__.py b/harnesses/host_controller/utils/gsi/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/gsi/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/gsi/img_utils.py b/harnesses/host_controller/utils/gsi/img_utils.py
deleted file mode 100644
index ae197ab..0000000
--- a/harnesses/host_controller/utils/gsi/img_utils.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Copyright 2018 - The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import logging
-import struct
-
-OS_VERSOIN_OFFSET_BOOTIMG = 44
-SPL_YEAR_BASE_BOOTIMG = 2000
-SPL_YEAR_MASK_BOOTIMG = 127 << 4
-SPL_MONTH_MASK_BOOTIMG = 15
-
-
-def GetSPLVersionFromBootImg(img_file_path):
- """Gets SPL version from given boot.img file path.
-
- Args:
- img_file_path : string, path to the boot.img file.
- Returns:
- A dict contains SPL version's year and date value.
- """
- try:
- fo = open(img_file_path, "rb")
- fo.seek(OS_VERSOIN_OFFSET_BOOTIMG, 0)
-
- os_version = fo.read(4)
- except IOError as e:
- logging.error(e.strerror)
- return {}
-
- os_version_int = struct.unpack("i", os_version)[0]
-
- year = (os_version_int & SPL_YEAR_MASK_BOOTIMG) >> 4
- month = os_version_int & SPL_MONTH_MASK_BOOTIMG
-
- version_date = {}
- version_date["year"] = SPL_YEAR_BASE_BOOTIMG + year
- version_date["month"] = month
-
- return version_date \ No newline at end of file
diff --git a/harnesses/host_controller/utils/ipc/__init__.py b/harnesses/host_controller/utils/ipc/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/ipc/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/ipc/file_lock.py b/harnesses/host_controller/utils/ipc/file_lock.py
deleted file mode 100644
index 67b6bf7..0000000
--- a/harnesses/host_controller/utils/ipc/file_lock.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import fcntl
-import logging
-import os
-
-from host_controller import common
-
-
-class FileLock(object):
- """Class for using files as a locking mechanism for devices.
-
- This is for checking whether a certain device is running a job or not when
- the automated self-update happens.
-
- Attributes:
- _devlock_dir: string, represent the home directory of the user.
- _lock_fd: dict, maps serial number of the devices and file descriptor.
- """
-
- def __init__(self, file_name=None, mode=None):
- """Initializes the file lock managing class instance.
-
- Args:
- file_name: string, name of the file to be opened. Existing lock
- files will be opened if given as None
- mode: string, the mode argument to open() function.
- """
- self._lock_fd = {}
- self._devlock_dir = os.path.join(
- os.path.expanduser("~"), common._DEVLOCK_DIR)
- if not os.path.exists(self._devlock_dir):
- os.mkdir(self._devlock_dir)
-
- if file_name:
- file_list = [file_name]
- else:
- file_list = [file_name for file_name in os.listdir(self._devlock_dir)]
- logging.debug("file_list for file lock: %s", file_list)
-
- for file_name in file_list:
- if os.path.isfile(os.path.join(self._devlock_dir, file_name)):
- self._OpenFile(file_name, mode)
-
- def _OpenFile(self, serial, mode=None):
- """Opens the given lock file and store the file descriptor to _lock_fd.
-
- Args:
- serial: string, serial number of a device.
- mode: string, the mode argument to open() function.
- Set to "w+" if given as None.
- """
- if serial in self._lock_fd and self._lock_fd[serial]:
- logging.info("Lock for the device %s already exists." % serial)
- return
-
- try:
- if not mode:
- mode = "w+"
- self._lock_fd[serial] = open(
- os.path.join(self._devlock_dir, serial), mode, 0)
- except IOError as e:
- logging.exception(e)
- return False
-
- def LockDevice(self, serial, suppress_lock_warning=False, block=False):
- """Tries to lock the file corresponding to "serial".
-
- Args:
- serial: string, serial number of a device.
- suppress_lock_warning: bool, True to suppress warning log output.
- block: bool, True to block at the fcntl.lockf(), waiting for it
- to be unlocked.
-
- Returns:
- True if successfully acquired the lock. False otherwise.
- """
- if serial not in self._lock_fd:
- ret = self._OpenFile(serial)
- if ret == False:
- return ret
-
- try:
- operation = fcntl.LOCK_EX
- if not block:
- operation |= fcntl.LOCK_NB
- fcntl.lockf(self._lock_fd[serial], operation)
- except IOError as e:
- if not suppress_lock_warning:
- logging.exception(e)
- return False
-
- return True
-
- def UnlockDevice(self, serial):
- """Releases the lock file corresponding to "serial".
-
- Args:
- serial: string, serial number of a device.
- """
- if serial not in self._lock_fd:
- logging.error("Lock for the device %s does not exist." % serial)
- return False
-
- fcntl.lockf(self._lock_fd[serial], fcntl.LOCK_UN)
diff --git a/harnesses/host_controller/utils/ipc/file_lock_semaphore.py b/harnesses/host_controller/utils/ipc/file_lock_semaphore.py
deleted file mode 100644
index 724c7b0..0000000
--- a/harnesses/host_controller/utils/ipc/file_lock_semaphore.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the 'License');
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an 'AS IS' BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import time
-
-from host_controller import common
-from host_controller.utils.ipc import file_lock
-
-MIN_SLEEP_TIME_IN_SECS = 1
-MAX_SLEEP_TIME_IN_SECS = 60
-
-
-class FileLockSemaphore(file_lock.FileLock):
- """Class for system-wide semaphores for inter process-group sync.
-
- Inherited FileLock to make the decrement and increment operation atomic,
- thus any read and write operations to the file must be leaded by Lock()
- and be followed by Unlock() operations, defined in FileLock
-
- Attributes:
- _name: string, name of the file used as semaphore.
- """
-
- def __init__(self, name):
- super(FileLockSemaphore, self).__init__(name, "r+")
- self._name = name
- self._InitSemaphore()
-
- def Acquire(self):
- """P() operation for the FileLockSemaphore.
-
- To minimize the starvation, the sleep time will decrease
- for the processes that have waited longer than the others.
- """
- sleep_time = MAX_SLEEP_TIME_IN_SECS
- while self.LockDevice(self._name, True, True):
- if self.sem_value > 0:
- break
- self.UnlockDevice(self._name)
- time.sleep(sleep_time)
- sleep_time = max(sleep_time / 2, MIN_SLEEP_TIME_IN_SECS)
-
- self._Decrement()
- self.UnlockDevice(self._name)
-
- def Release(self):
- """V() operation for the FileLockSemaphore."""
- self.LockDevice(self._name, True, True)
- self._Increment()
- self.UnlockDevice(self._name)
-
- def _InitSemaphore(self):
- """Initializes the file content for semaphore operations.
-
- The value of the counter must remain in the range
- [0, common.MAX_ADB_FASTBOOT_PROCESS] inclusive.
- """
- self.LockDevice(self._name, True, True)
-
- try:
- diff = common.MAX_ADB_FASTBOOT_PROCESS - self.sem_max_value
- value_to_init = min(
- max(0, self.sem_value + diff), common.MAX_ADB_FASTBOOT_PROCESS)
- except IndexError:
- value_to_init = common.MAX_ADB_FASTBOOT_PROCESS
-
- self._lock_fd[self._name].seek(0)
- self._lock_fd[self._name].truncate(0)
- self._lock_fd[self._name].write(
- "%s\n%s" % (common.MAX_ADB_FASTBOOT_PROCESS, value_to_init))
-
- self.UnlockDevice(self._name)
-
- @property
- def sem_value(self):
- """Reads the current counter value of the semaphore.
-
- Returns:
- int, the value of the current counter.
- """
- self._lock_fd[self._name].seek(0)
- return int(self._lock_fd[self._name].read().split()[1])
-
- @property
- def sem_max_value(self):
- """Reads the current maximum counter value of the semaphore.
-
- Since the maximum counter value may vary at the deployment time,
- the existing HC process group needs to look for the maximum value
- every time it tries to access the semaphore
-
- Returns:
- int, the value of the maximum counter.
- """
- self._lock_fd[self._name].seek(0)
- return int(self._lock_fd[self._name].read().split()[0])
-
- def _Decrement(self):
- """Decrements the internal counter of the semaphore."""
- current_value = self.sem_value
- current_max = self.sem_max_value
- self._lock_fd[self._name].seek(len(str(current_max)) + 1)
- self._lock_fd[self._name].write("%s" % max(0, (current_value - 1)))
-
- def _Increment(self):
- """Increments the internal counter of the semaphore."""
- current_value = self.sem_value
- current_max = self.sem_max_value
- self._lock_fd[self._name].seek(len(str(current_max)) + 1)
- self._lock_fd[self._name].write("%s" % min(current_max,
- (current_value + 1)))
diff --git a/harnesses/host_controller/utils/ipc/file_lock_semaphore_test.py b/harnesses/host_controller/utils/ipc/file_lock_semaphore_test.py
deleted file mode 100644
index e1bae8b..0000000
--- a/harnesses/host_controller/utils/ipc/file_lock_semaphore_test.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import os
-import threading
-import time
-import unittest
-
-try:
- from unittest import mock
-except ImportError:
- import mock
-
-from host_controller import common
-from host_controller.utils.ipc import file_lock_semaphore
-
-
-class FileLockSemaphoreTest(unittest.TestCase):
- """Tests for FileLockSemaphore"""
-
- def setUp(self):
- self.sem = file_lock_semaphore.FileLockSemaphore("fls_test")
-
- def tearDown(self):
- os.remove(os.path.join(os.path.expanduser("~"), common._DEVLOCK_DIR, "fls_test"))
-
- def testFileLockSemaphoreAcquire(self):
- current_sem_value = self.sem.sem_value
- self.sem.Acquire()
- self.assertEqual(current_sem_value - 1, self.sem.sem_value)
- self.sem.Release()
-
- def testFileLockSemaphoreRelease(self):
- self.sem.Acquire()
- current_sem_value = self.sem.sem_value
- self.sem.Release()
- self.assertEqual(current_sem_value + 1, self.sem.sem_value)
-
- def testFileLockSemaphoreOverflow(self):
- current_sem_value = self.sem.sem_value
- self.sem.Release()
- self.assertEqual(current_sem_value, self.sem.sem_value)
-
- def _AcquireRelease(self):
- self.sem.Acquire()
- self.assertEqual(self.sem.sem_value, 0)
- self.sem.Release()
-
- def testFileLockSemaphoreUnderflow(self):
- for _ in range(common.MAX_ADB_FASTBOOT_PROCESS):
- self.sem.Acquire()
-
- file_lock_semaphore.MAX_SLEEP_TIME_IN_SECS = 2
- self._thread = threading.Thread(target=self._AcquireRelease)
- self._thread.daemon = True
- self._thread.start()
-
- time.sleep(1)
- self.sem.Release()
- self._thread.join()
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/harnesses/host_controller/utils/ipc/shared_dict.py b/harnesses/host_controller/utils/ipc/shared_dict.py
deleted file mode 100644
index a0e707f..0000000
--- a/harnesses/host_controller/utils/ipc/shared_dict.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import multiprocessing
-
-from host_controller import common
-
-
-class SharedDict(object):
- """Class for the state of devices connected to the host.
-
- shared between the main process and the process pool.
-
- Attributes:
- _manager: SyncManager. an instance of a manager for shared objects and
- values between processes.
- _dict: multiprocessing.SyncManager.dict. A dictionary
- shared among processes.
- """
-
- def __init__(self, manager=None):
- if manager is not None:
- self._manager = manager
- else:
- self._manager = multiprocessing.Manager()
- self._dict = self._manager.dict()
-
- def __getitem__(self, key):
- """getitem for self._dict.
-
- Args:
- key: string, serial number of a device.
-
- Returns:
- integer value defined in _DEVICE_STATUS_DICT.
- """
- if key not in self._dict:
- self._dict[key] = common._DEVICE_STATUS_DICT["unknown"]
- return self._dict[key]
-
- def __setitem__(self, key, value):
- """setitem for self._dict.
-
- if the value is not a defined one, device status is set to "unknown".
-
- Args:
- key: string, serial number of a device.
- value: integer, status value defined in _DEVICE_STATUS_DICT.
- """
- if value not in range(len(common._DEVICE_STATUS_DICT)):
- self._dict[key] = common._DEVICE_STATUS_DICT["unknown"]
- else:
- self._dict[key] = value \ No newline at end of file
diff --git a/harnesses/host_controller/utils/parser/__init__.py b/harnesses/host_controller/utils/parser/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/parser/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/parser/pb2_utils.py b/harnesses/host_controller/utils/parser/pb2_utils.py
deleted file mode 100644
index af96588..0000000
--- a/harnesses/host_controller/utils/parser/pb2_utils.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import json
-import logging
-import requests
-
-# timeout seconds for requests
-REQUESTS_TIMEOUT_SECONDS = 60
-
-
-def FillDictAndPost(msg,
- dict_to_fill,
- url,
- headers,
- filters={},
- caller_name=""):
- """Fills up a dict using contents of the pb2 message and uploads it.
-
- Args:
- msg: pb2 msg, containing info about all task schedules.
- dict_to_fill: dict, to be converted into a json object before
- upload.
- url: string, URL for the endpoit API to be called when
- the dict_to_fill fills up.
-
- Returns:
- True if successful, False otherwise.
- """
- terminal = True
- ret = True
- sub_msg_list = []
- for field in msg.DESCRIPTOR.fields:
- if field.type == field.TYPE_MESSAGE:
- terminal = False
- for sub_msg in msg.__getattribute__(field.name):
- # make all the messages to be processed after other attrs,
- # otherwise dict_to_fill would be incomplete.
- sub_msg_list.append(sub_msg)
- else:
- if filters and field.name in filters:
- filtered_key = filters[field.name]
- else:
- filtered_key = field.name
-
- if field.label == field.LABEL_REPEATED:
- dict_to_fill[filtered_key] = list(
- msg.__getattribute__(field.name))
- else:
- dict_to_fill[filtered_key] = msg.__getattribute__(field.name)
-
- for sub_msg in sub_msg_list:
- ret = ret and FillDictAndPost(sub_msg, dict_to_fill, url, headers,
- filters, caller_name)
-
- if terminal:
- try:
- response = requests.post(url, data=json.dumps(dict_to_fill),
- headers=headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- return False
- if response.status_code != requests.codes.ok:
- logging.error("%s error: %s", caller_name, response)
- ret = ret and False
-
- return ret
diff --git a/harnesses/host_controller/utils/parser/result_utils.py b/harnesses/host_controller/utils/parser/result_utils.py
deleted file mode 100644
index 9b9b51e..0000000
--- a/harnesses/host_controller/utils/parser/result_utils.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-import os
-
-from xml.etree import ElementTree
-
-from host_controller import common
-
-
-def ExtractResultZip(zip_file, output_dir, xml_only=True):
- """Extracts XML result from a zip file.
-
- Args:
- zip_file: An object of zipfile.ZipFile.
- output_dir: The directory where the zip is extracted.
- xml_only: Whether to extract log-result.xml or test_result.xml only.
-
- Returns:
- The path to the XML in the output directory.
- None if the zip does not contain the XML result.
- """
- try:
- xml_name = next(x for x in zip_file.namelist() if
- x.endswith(common._TEST_RESULT_XML) or
- x.endswith(common._LOG_RESULT_XML))
- except StopIteration:
- return None
-
- if not os.path.exists(output_dir):
- os.makedirs(output_dir)
- if xml_only:
- return zip_file.extract(xml_name, path=output_dir)
- else:
- zip_file.extractall(path=output_dir)
- return os.path.join(output_dir, xml_name)
-
-
-def LoadTestSummary(result_xml):
- """Gets attributes of <Result>, <Build>, and <Summary>.
-
- Args:
- result_xml: A file object of the TradeFed report in XML format.
-
- Returns:
- 3 dictionaries, the attributes of <Result>, <Build>, and <Summary>.
- """
- result_attrib = {}
- build_attrib = {}
- summary_attrib = {}
- for event, elem in ElementTree.iterparse(result_xml, events=("start", )):
- if all((result_attrib, build_attrib, summary_attrib)):
- break
- if elem.tag == common._RESULT_TAG:
- result_attrib = dict(elem.attrib)
- elif elem.tag == common._BUILD_TAG:
- build_attrib = dict(elem.attrib)
- elif elem.tag == common._SUMMARY_TAG:
- summary_attrib = dict(elem.attrib)
- return result_attrib, build_attrib, summary_attrib
-
-
-def IterateTestResults(result_xml):
- """Yields test records in test_result.xml.
-
- Args:
- result_xml: A file object of the TradeFed report in XML format.
-
- Yields:
- A tuple of ElementTree.Element, (Module, TestCase, Test) for each
- </Test>.
- """
- module_elem = None
- testcase_elem = None
- for event, elem in ElementTree.iterparse(
- result_xml, events=("start", "end")):
- if event == "start":
- if elem.tag == common._MODULE_TAG:
- module_elem = elem
- elif elem.tag == common._TESTCASE_TAG:
- testcase_elem = elem
-
- if event == "end" and elem.tag == common._TEST_TAG:
- yield module_elem, testcase_elem, elem
-
-
-def GetAbiBitness(abi):
- """Gets bitness of an ABI.
-
- Args:
- abi: A string, the ABI name.
-
- Returns:
- 32 or 64, the ABI bitness.
- """
- return 64 if "arm64" in abi or "x86_64" in abi else 32
-
-
-def GetTestName(module, testcase, test):
- """Gets the bitness and the full test name.
-
- Args:
- module: The Element for <Module>.
- testcase: The Element for <TestCase>.
- test: The Element for <Test>.
-
- Returns:
- A tuple of (bitness, module_name, testcase_name, test_name).
- """
- abi = module.attrib.get(common._ABI_ATTR_KEY, "")
- bitness = str(GetAbiBitness(abi))
- module_name = module.attrib.get(common._NAME_ATTR_KEY, "")
- testcase_name = testcase.attrib.get(common._NAME_ATTR_KEY, "")
- test_name = test.attrib.get(common._NAME_ATTR_KEY, "")
- return bitness, module_name, testcase_name, test_name
diff --git a/harnesses/host_controller/utils/parser/xml_utils.py b/harnesses/host_controller/utils/parser/xml_utils.py
deleted file mode 100644
index 3717d5a..0000000
--- a/harnesses/host_controller/utils/parser/xml_utils.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import logging
-
-from xml.etree import ElementTree
-
-
-def GetAttributes(xml_file, tag, attrs):
- """Parses attributes in the given tag from xml file.
-
- Args:
- xml_file: The xml file object.
- tag: a string, tag to parse from tag xml_file.
- attrs: a list of attributes to look up inside the tag.
-
- Returns:
- A dict containing values of the attributes in tag from the xml file.
- """
- result = {}
- for _, elem in ElementTree.iterparse(xml_file, ("start", )):
- if elem.tag == tag:
- for attr in attrs:
- if attr in elem.attrib:
- result[attr] = elem.attrib[attr]
- else:
- logging.warning(
- "Cannot find an attribute {} in <{}>".format(
- attr, tag))
- return result
diff --git a/harnesses/host_controller/utils/usb/__init__.py b/harnesses/host_controller/utils/usb/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/utils/usb/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/utils/usb/usb_utils.py b/harnesses/host_controller/utils/usb/usb_utils.py
deleted file mode 100644
index 5c42c7b..0000000
--- a/harnesses/host_controller/utils/usb/usb_utils.py
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import fcntl
-import logging
-import os
-
-from vts.utils.python.common import cmd_utils
-
-# _IO('U', 20)
-USBDEVFS_RESET = ord("U") << 8 | 20
-
-
-def GetDevicesUSBFilePath():
- """Sweeps through connected USB device info and maps serial number to them.
-
- Returns:
- A dict, serial numbers of the devices as the key, device file path
- corresponding to the serial number as the value.
- """
- ret = {}
-
- sh_stdout, sh_stderr, ret_code = cmd_utils.ExecuteOneShellCommand("which udevadm")
- if ret_code != 0:
- logging.error("`udevadm` doesn't exist on the host; "
- "please install 'udev' pkg before retrying.")
- return ret
-
- sh_stdout, _, _ = cmd_utils.ExecuteOneShellCommand(
- "find /sys/bus/usb/devices/usb*/ -name dev")
- lines = sh_stdout.split()
- for line in lines:
- syspath = os.path.dirname(line)
- devname, _, _ = cmd_utils.ExecuteOneShellCommand(
- "udevadm info -q name -p %s" % syspath)
- if devname.startswith("bus/"):
- serial, _, _ = cmd_utils.ExecuteOneShellCommand(
- "udevadm info -q property --export -p %s | grep ID_SERIAL_SHORT"
- % syspath)
- if serial:
- device_serial = serial.split("=")[1].strip().strip("'")
- ret[device_serial] = os.path.join("/dev", devname.strip())
-
- return ret
-
-
-def ResetDeviceUsb(dev_file_path):
- """Invokes ioctl that resets the USB device on the given file path.
-
- Args:
- dev_file_path: string, abs path to the device file
-
- Returns:
- True if successful, False otherwise.
- """
- try:
- with open(dev_file_path, "wb") as usb_fd:
- fcntl.ioctl(usb_fd, USBDEVFS_RESET, 0)
- except IOError as e:
- logging.exception(e)
- return False
-
- return True
-
-
-def ResetUsbDeviceOfSerial(serial):
- """Resets a USB device file corresponding to the given serial.
-
- Args:
- serial: string, serial number of the device whose USB device file
- will reset.
-
- Returns:
- True if successful, False otherwise.
- """
- device_file_path = GetDevicesUSBFilePath()
- if serial in device_file_path:
- logging.error(
- "Device %s not responding. Resetting device file %s.", serial,
- device_file_path[serial])
- return ResetDeviceUsb(device_file_path[serial])
- return False
-
-
-def ResetUsbDeviceOfSerial_Callback(*args):
- """Wrapper of the ResetUsbDeviceOfSerial(), for handling the *args.
-
- Args:
- args: tuple of arguments, expected to have the serial number of
- the devices as the first element.
-
- Returns:
- True if successful, False otherwise.
- """
- try:
- return ResetUsbDeviceOfSerial(args[0])
- except IndexError as e:
- logging.exception(e)
- return False \ No newline at end of file
diff --git a/harnesses/host_controller/vti_interface/__init__.py b/harnesses/host_controller/vti_interface/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/harnesses/host_controller/vti_interface/__init__.py
+++ /dev/null
diff --git a/harnesses/host_controller/vti_interface/vti_endpoint_client.py b/harnesses/host_controller/vti_interface/vti_endpoint_client.py
deleted file mode 100644
index 17fdc9d..0000000
--- a/harnesses/host_controller/vti_interface/vti_endpoint_client.py
+++ /dev/null
@@ -1,452 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import json
-import logging
-import requests
-import threading
-import time
-
-from host_controller.utils.parser import pb2_utils
-
-# Job status dict
-JOB_STATUS_DICT = {
- # scheduled but not leased yet
- "ready": 0,
- # scheduled and in running
- "leased": 1,
- # completed job
- "complete": 2,
- # unexpected error during running
- "infra-err": 3,
- # never leased within schedule period
- "expired": 4,
- # device boot error after flashing the given img sets
- "bootup-err": 5
-}
-
-SCHEDULE_INFO_PB2_ATTR_FILTERS = {
- "pab_account_id": "device_pab_account_id",
- "name": "build_target",
-}
-
-# timeout seconds for requests
-REQUESTS_TIMEOUT_SECONDS = 60
-
-
-class VtiEndpointClient(object):
- """VTI (Vendor Test Infrastructure) endpoint client.
-
- Attributes:
- _headers: A dictionary, containing HTTP request header information.
- _url: string, the base URL of an endpoint API.
- _job: dict, currently leased job info.
- """
-
- def __init__(self, url):
- if url == "localhost":
- url = "http://localhost:8080/_ah/api/"
- else:
- if not url.startswith(("https://")) and not url.startswith("http://"):
- url = "https://" + url
- if url.endswith("appspot.com"):
- url += "/_ah/api/"
- self._headers = {"content-type": "application/json",
- "Accept-Charset": "UTF-8"}
- self._url = url
- self._job = {}
- self._heartbeat_thread = None
-
- def UploadBuildInfo(self, builds):
- """Uploads the given build information to VTI.
-
- Args:
- builds: a list of dictionaries, containing info about all new
- builds found.
-
- Returns:
- True if successful, False otherwise.
- """
- url = self._url + "build/v1/set"
- fail = False
- for build in builds:
- try:
- response = requests.post(url, data=json.dumps(build),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- if response.status_code != requests.codes.ok:
- logging.error("UploadBuildInfo error: %s", response)
- fail = True
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- fail = True
- if fail:
- return False
- return True
-
- def UploadDeviceInfo(self, hostname, devices):
- """Uploads the given device information to VTI.
-
- Args:
- hostname: string, the hostname of a target host.
- devices: a list of dicts, containing info about all detected
- devices that are attached to the host.
-
- Returns:
- True if successful, False otherwise.
- """
- url = self._url + "host/v1/set"
- payload = {}
- payload["hostname"] = hostname
- payload["devices"] = []
- for device in devices:
- new_device = {
- "serial": device["serial"],
- "product": device["product"],
- "status": device["status"]}
- payload["devices"].append(new_device)
-
- try:
- response = requests.post(url, data=json.dumps(payload),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except (requests.exceptions.ConnectionError,
- requests.exceptions.Timeout) as e:
- logging.exception(e)
- return False
- if response.status_code != requests.codes.ok:
- logging.error("UploadDeviceInfo error: %s", response)
- return False
- return True
-
- def UploadScheduleInfo(self, pbs, clear_schedule):
- """Uploads the given schedule information to VTI.
-
- Args:
- pbs: a list of dicts, containing info about all task schedules.
- clear_schedule: bool, True to clear all schedule data exist on the
- scheduler
-
- Returns:
- True if successful, False otherwise.
- """
- if pbs is None or len(pbs) == 0:
- return False
-
- url = self._url + "schedule/v1/clear"
- succ = True
- if clear_schedule:
- try:
- response = requests.post(
- url, data=json.dumps({"manifest_branch": "na"}),
- headers=self._headers, timeout=REQUESTS_TIMEOUT_SECONDS)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- return False
- if response.status_code != requests.codes.ok:
- logging.error("UploadScheduleInfo error: %s", response)
- succ = False
-
- if not succ:
- return False
-
- url = self._url + "schedule/v1/set"
- for pb in pbs:
- schedule = {}
- succ = succ and pb2_utils.FillDictAndPost(
- pb, schedule, url, self._headers,
- SCHEDULE_INFO_PB2_ATTR_FILTERS, "UploadScheduleInfo")
-
- return succ
-
- def UploadLabInfo(self, pbs, clear_labinfo):
- """Uploads the given lab information to VTI.
-
- Args:
- pbs: a list of dicts, containing info about all known labs.
- clear_labinfo: bool, True to clear all lab data exist on the
- scheduler
-
- Returns:
- True if successful, False otherwise.
- """
- if pbs is None or len(pbs) == 0:
- return
-
- url = self._url + "lab/v1/clear"
- succ = True
- if clear_labinfo:
- try:
- response = requests.post(url, data=json.dumps({"name": "na"}),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- return False
- if response.status_code != requests.codes.ok:
- logging.error("UploadLabInfo error: %s", response)
- succ = False
-
- if not succ:
- return False
-
- url = self._url + "lab/v1/set"
- for pb in pbs:
- lab = {}
- lab["name"] = pb.name
- lab["owner"] = pb.owner
- lab["admin"] = []
- lab["admin"].extend(pb.admin)
- lab["host"] = []
- for host in pb.host:
- new_host = {}
- new_host["hostname"] = host.hostname
- new_host["ip"] = host.ip
- new_host["script"] = host.script
- if host.host_equipment:
- new_host["host_equipment"] = []
- new_host["host_equipment"].extend(host.host_equipment)
- new_host["device"] = []
- if host.device:
- for device in host.device:
- new_device = {}
- new_device["serial"] = device.serial
- new_device["product"] = device.product
- if device.device_equipment:
- new_device["device_equipment"] = []
- new_device["device_equipment"].extend(
- device.device_equipment)
- new_host["device"].append(new_device)
- lab["host"].append(new_host)
- try:
- response = requests.post(url, data=json.dumps(lab),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- if response.status_code != requests.codes.ok:
- logging.error("UploadLabInfo error: %s", response)
- succ = False
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- succ = False
- return succ
-
- def LeaseJob(self, hostname, execute=True):
- """Leases a job for the given host, 'hostname'.
-
- Args:
- hostname: string, the hostname of a target host.
- execute: boolean, True to lease and execute StartHeartbeat, which is
- the case that the leased job will be executed on this
- process's context.
-
- Returns:
- True if successful, False otherwise.
- """
- if not hostname:
- return None, {}
-
- url = self._url + "job/v1/lease"
- try:
- response = requests.post(url, data=json.dumps({"hostname": hostname}),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- return None, {}
-
- if response.status_code != requests.codes.ok:
- logging.error("LeaseJob error: %s", response.status_code)
- return None, {}
-
- response_json = json.loads(response.text)
- if ("return_code" in response_json
- and response_json["return_code"] != "SUCCESS"):
- logging.debug("LeaseJob error: %s", response_json)
- return None, {}
-
- if "jobs" not in response_json:
- logging.error(
- "LeaseJob jobs not found in response json %s", response.text)
- return None, {}
-
- jobs = response_json["jobs"]
- if jobs and len(jobs) > 0:
- for job in jobs:
- if execute == True:
- self._job = job
- self.StartHeartbeat("leased", 60)
- return job["test_name"].split("/")[0], job
- return None, {}
-
- def ExecuteJob(self, job):
- """Executes leased job passed from parent process.
-
- Args:
- job: dict, information the on leased job.
-
- Returns:
- a string which is path to a script file for onecmd().
- a dict contains info on the leased job, will be passed to onecmd().
- """
- logging.info("Job info : {}".format(json.dumps(job)))
- if job is not None:
- self._job = job
- self.StartHeartbeat("leased", 60)
- return job["test_name"].split("/")[0], job
-
- return None, {}
-
- def UpdateLeasedJobStatus(self, status, update_interval):
- """Updates the status of the leased job.
-
- Args:
- status: string, status value.
- update_interval: int, time between heartbeats in second.
- """
- if self._job is None:
- return
-
- url = self._url + "job/v1/heartbeat"
- self._job["status"] = JOB_STATUS_DICT[status]
-
- thread = threading.currentThread()
- while getattr(thread, 'keep_running', True):
- try:
- response = requests.post(url, data=json.dumps(self._job),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- if response.status_code != requests.codes.ok:
- logging.error("UpdateLeasedJobStatus error: %s", response)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
- time.sleep(update_interval)
-
- def StartHeartbeat(self, status="leased", update_interval=60):
- """Starts the hearbeat_thread.
-
- Args:
- status: string, status value.
- update_interval: int, time between heartbeats in second.
- """
- if (self._heartbeat_thread is None
- or hasattr(self._heartbeat_thread, 'keep_running')):
- self._heartbeat_thread = threading.Thread(
- target=self.UpdateLeasedJobStatus,
- args=(
- status,
- update_interval,
- ))
- self._heartbeat_thread.daemon = True
- self._heartbeat_thread.start()
-
- def StopHeartbeat(self, status="complete", infra_log_url=""):
- """Stops the hearbeat_thread and sets current job's status.
-
- Args:
- status: string, status value.
- infra_log_url: string, URL to the uploaded infra log.
- """
- self._heartbeat_thread.keep_running = False
-
- if self._job is None:
- return
-
- url = self._url + "job/v1/heartbeat"
- self.SetJobStatusFromLeasedTo(status)
- self._job["infra_log_url"] = infra_log_url
-
- try:
- response = requests.post(url, data=json.dumps(self._job),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- if response.status_code != requests.codes.ok:
- logging.error("StopHeartbeat error: %s", response)
- except requests.exceptions.Timeout as e:
- logging.exception(e)
-
- self._job = None
-
- def SetJobStatusFromLeasedTo(self, status):
- """Sets current job's status only when the job's status is 'leased'.
-
- Args:
- status: string, status value.
- """
- if (self._job is not None and
- self._job["status"] == JOB_STATUS_DICT["leased"]):
- self._job["status"] = JOB_STATUS_DICT[status]
-
- def UploadHostVersion(self, hostname, vtslab_version):
- """Uploads vtslab version.
-
- Args:
- hostname: string, the name of the host.
- vtslab_version: string, current version of vtslab package.
- """
- url = self._url + "lab/v1/set_version"
- host = {}
- host["hostname"] = hostname
- host["vtslab_version"] = vtslab_version
-
- try:
- response = requests.post(url, data=json.dumps(host),
- headers=self._headers,
- timeout=REQUESTS_TIMEOUT_SECONDS)
- except (requests.exceptions.ConnectionError,
- requests.exceptions.Timeout) as e:
- logging.exception(e)
- return
- if response.status_code != requests.codes.ok:
- logging.error("UploadHostVersion error: %s", response)
-
- def CheckBootUpStatus(self):
- """Checks whether the device_img + gsi from the job fails to boot up.
-
- Returns:
- True if the devices flashed with the given imgs from the leased job
- succeed to boot up. False otherwise.
- """
- if self._job:
- return (self._job["status"] != JOB_STATUS_DICT["bootup-err"])
- return False
-
- def GetJobTestType(self):
- """Returns the test type of the leased job.
-
- Returns:
- int, test_type attr in the job message. 0 when there is no job
- leased to this vti_endpoint_client.
- """
- if self._job and "test_type" in self._job:
- try:
- return int(self._job["test_type"])
- except ValueError as e:
- logging.exception(e)
- return 0
-
- def GetJobDeviceProductName(self):
- """Returns the product name of the DUTs of the leased job.
-
- Returns:
- string, product name. An empty string if there is no job leased or
- "device" attr of the job obj is not well formatted.
- """
- if self._job and "device" in self._job:
- try:
- return self._job["device"].split("/")[1]
- except IndexError as e:
- logging.exception(e)
- return ""
diff --git a/host_setup/fabfile.py b/host_setup/fabfile.py
deleted file mode 100644
index d37bb58..0000000
--- a/host_setup/fabfile.py
+++ /dev/null
@@ -1,386 +0,0 @@
-#
-# Copyright 2018 - The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-import imp
-import os
-import sys
-import time
-
-from fabric.api import env
-from fabric.api import local
-from fabric.api import run
-from fabric.api import settings
-from fabric.api import sudo
-from fabric.contrib.files import contains
-from fabric.contrib.files import exists
-from fabric.context_managers import cd
-
-_PIP_REQUIREMENTS_PATHS = [
- "test/vts/script/pip_requirements.txt",
- "test/framework/harnesses/host_controller/script/pip_requirements.txt"
-]
-
-# Path to the file that contains the abs path to the deployed vtslab pakcage.
-_VTSLAB_PACKAGE_PATH_FILENAME = ".vtslab_package_path"
-
-# Zone filter list for GCE instances.
-_DEFAULT_GCE_ZONE_LIST = [
- "us-east1-b",
- "asia-northeast1-a",
-]
-
-
-def SetPassword(password):
- """Sets password for hosts to access through ssh and to run sudo commands
-
- usage: $ fab SetPassword:<password for hosts>
-
- Args:
- password: string, password for hosts.
- """
- env.password = password
-
-
-def GetHosts(hosts_file_path, gce_instance_name=None, account=None):
- """Configures env.hosts to a given list of hosts.
-
- usage: $ fab GetHosts:<path to a source file contains hosts info>
-
- Args:
- hosts_file_path: string, path to a python file passed from command file
- input.
- gce_instance_name: string, GCE instance name.
- account: string, account name used for the ssh connection.
- """
- if hosts_file_path.endswith(".py"):
- hosts_module = imp.load_source('hosts_module', hosts_file_path)
- env.hosts = hosts_module.EmitHostList()
- else:
- if not gce_instance_name or not account:
- print(
- "Please specify gce_instance_name and account using -H option. "
- "Example: -H <Google_Cloud_project>,<GCE_instance>,<account>")
- sys.exit(0)
- env.key_filename = "~/.ssh/google_compute_engine"
- gce_list_out = local(
- "gcloud compute instances list --project=%s --filter=\"zone:(%s)\""
- % (hosts_file_path, " ".join(_DEFAULT_GCE_ZONE_LIST)),
- capture=True)
- for line in gce_list_out.split("\n")[1:]:
- if line.startswith(gce_instance_name):
- env.hosts.append("%s@%s" % (account, line.strip().split()[-2]))
-
-
-def SetupIptables(ip_address_file_path):
- """Configures iptables setting for all hosts listed.
-
- usage: $ fab SetupIptables:<path to a source file contains ip addresses of
- certified machines>
-
- Args:
- ip_address_file_path: string, path to a python file passed from command
- file input.
- """
- ip_addresses_module = imp.load_source('ip_addresses_module',
- ip_address_file_path)
- ip_address_list = ip_addresses_module.EmitIPAddressList()
-
- sudo("apt-get install -y iptables-persistent")
- sudo("iptables -P INPUT ACCEPT")
- sudo("iptables -P FORWARD ACCEPT")
- sudo("iptables -F")
-
- for ip_address in ip_address_list:
- sudo(
- "iptables -A INPUT -p tcp -s %s --dport 22 -j ACCEPT" % ip_address)
-
- sudo("iptables -P INPUT DROP")
- sudo("iptables -P FORWARD DROP")
- sudo("iptables -A INPUT -p icmp -j ACCEPT")
- sudo("netfilter-persistent save")
- sudo("netfilter-persistent reload")
-
-
-def SetupSudoers():
- """Append sudo rules for vtslab user.
-
- usage: $ fab SetupSudoers
- """
- if not contains("/etc/sudoers", "vtslab", use_sudo=True):
- sudo("echo '' | sudo tee -a /etc/sudoers")
- sudo("echo '# Let vtslab account have all authorization' | "
- "sudo tee -a /etc/sudoers")
- sudo("echo 'vtslab ALL=(ALL:ALL) ALL' | sudo tee -a /etc/sudoers")
-
-
-def SetupUSBPermission():
- """Sets up the USB permission for adb and fastboot.
-
- usage: $ fab SetupUSBPermission
- """
- sudo("curl --create-dirs -L -o /etc/udev/rules.d/51-android.rules -O -L "
- "https://raw.githubusercontent.com/snowdream/51-android/master/"
- "51-android.rules")
- sudo("chmod a+r /etc/udev/rules.d/51-android.rules")
- sudo("service udev restart")
-
-
-def SetupADBVendorKeysEnvVar():
- """Appends scripts for ADB_VENDOR_KEYS path setup.
-
- In setup step, this function looks into .bashrc file for this script, and
- if there is not then appends the below scripts to .bashrc.
- Later when shell env created (through ssh or screen instance creation time),
- .bashrc file will look for _VTSLAB_PACKAGE_PATH_FILENAME and use the
- contents of the file to set ADB_VENDOR_KEYS.
-
- usage: $ fab SetupADBVendorKeysEnvVar
- """
- if not contains("~/.bashrc", _VTSLAB_PACKAGE_PATH_FILENAME):
- run("echo '' >> ~/.bashrc", )
- run("echo '# Set $ADB_VENDOR_KEYS as paths to adb private key files "
- "within the vtslab-package' >> ~/.bashrc")
- run("echo 'if [ -f ~/%s ]; then' >> ~/.bashrc" %
- _VTSLAB_PACKAGE_PATH_FILENAME)
- run("echo ' export ADB_VENDOR_KEYS=$(find $(cat ~/%s)/android-vtslab/"
- "testcases/DATA/ak -name \".*.ak\" | tr \"\\n\" \":\")' "
- ">> ~/.bashrc" % _VTSLAB_PACKAGE_PATH_FILENAME)
- run("echo 'fi' >> ~/.bashrc")
-
-
-def _CheckADBVendorKeysEnvVar(vtslab_package_file_name=""):
- """Checks if there is a change in ADB_VENDOR_KEYS env variable.
-
- if there is, then the adbd needs to be restarted in the screen context
- before running the HC.
-
- Args:
- vtslab_package_file_name: string, the HC package file name that is about
- to be deployed.
-
- Returns:
- True if the list of the adb vendor key files have changed,
- False otherwise.
- """
- former_key_set = set()
- current_key_set = set()
- set_keyfile_set = lambda set, path_list: map(set.add, map(os.path.basename,
- path_list))
- vtslab_package_path_filepath = "~/%s" % _VTSLAB_PACKAGE_PATH_FILENAME
-
- if exists(vtslab_package_path_filepath):
- former_HC_package_path = run("cat %s" % vtslab_package_path_filepath)
- former_HC_package_adbkey_path = os.path.join(
- former_HC_package_path, "android-vtslab/testcases/DATA/ak")
- if exists(former_HC_package_adbkey_path):
- adb_vendor_keys_list = run("find %s -name \".*.ak\"" %
- former_HC_package_adbkey_path).split()
- set_keyfile_set(former_key_set, adb_vendor_keys_list)
-
- if exists("~/run/%s.dir/android-vtslab/testcases/DATA/ak" %
- vtslab_package_file_name):
- adb_vendor_keys_list = run(
- "find ~/run/%s.dir/android-vtslab/testcases/DATA/ak -name \".*.ak\""
- % vtslab_package_file_name).split()
- set_keyfile_set(current_key_set, adb_vendor_keys_list)
-
- return former_key_set != current_key_set
-
-
-def SetupPackages(ip_address_file_path=None):
- """Sets up the execution environment for vts `run` command.
-
- Need to temporarily open the ports for apt-get and pip commands.
-
- usage: $ fab SetupPackages
-
- Args:
- ip_address_file_path: string, path to a python file passed from command
- file input. Will be passed to SetupIptables().
- """
- sudo("iptables -P INPUT ACCEPT")
-
- # todo : replace "kr.ubuntu." to "ubuntu" in /etc/apt/sources.list
- sudo("apt-get upgrade -y")
- sudo("apt-get update -y")
- sudo("apt-get install -y git-core gnupg flex bison gperf build-essential "
- "zip curl zlib1g-dev gcc-multilib g++-multilib x11proto-core-dev "
- "libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc "
- "unzip liblz4-tool udev screen")
-
- sudo("apt-get install -y android-tools-adb")
- sudo("usermod -aG plugdev $LOGNAME")
-
- SetupUSBPermission()
-
- sudo("apt-get update")
- sudo("apt-get install -y python2.7")
- sudo("apt-get install -y python-pip")
- run("pip install --upgrade pip")
- sudo("apt-get install -y python-virtualenv")
-
- sudo("apt-get install -y python-dev python-protobuf protobuf-compiler "
- "python-setuptools")
-
- for req_path in _PIP_REQUIREMENTS_PATHS:
- full_path = os.path.join(os.environ["ANDROID_BUILD_TOP"], req_path)
- pip_requirement_list = []
- try:
- requirements_fd = open(full_path, "r")
- lines = requirements_fd.readlines()
- for line in lines:
- req = line.rstrip()
- if req != "" and not req.startswith("#"):
- pip_requirement_list.append(req)
- except IOError as e:
- print("%s: %s" % (e.strerror, full_path))
- return
- sudo("pip install %s" % " ".join(pip_requirement_list))
-
- sudo("pip install --upgrade protobuf")
-
- lsb_result = run("lsb_release -c -s")
- sudo("echo \"deb http://packages.cloud.google.com/apt cloud-sdk-%s "
- "main\" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list" %
- lsb_result)
- sudo("curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | "
- "sudo apt-key add -")
- sudo("apt-get update && sudo apt-get install -y google-cloud-sdk")
- sudo("apt-get install -y google-cloud-sdk-app-engine-java "
- "google-cloud-sdk-app-engine-python kubectl")
-
- sudo("apt-get install -y m4 bison")
-
- if ip_address_file_path is not None:
- SetupIptables(ip_address_file_path)
-
- SetupADBVendorKeysEnvVar()
-
-
-def DeployVtslab(vtslab_package_gcs_url=None):
- """Deploys vtslab package.
-
- Fetches and deploy vtslab by going through the processes described below
- 1. Send the "exit --wait_for_jobs=True" command to all detached screen.
- And let the screen to terminate itself.
- 2. Create a new screen instance that downloads and runs the new HC,
- give password and device command to the HC without actually attaching it
-
- usage: $ fab DeployVtslab -p <password> -H hosts.py -f <gs://vtslab-release/...>
-
- Args:
- vtslab_package_gcs_url: string, URL to a certain vtslab package file.
- """
- if not vtslab_package_gcs_url:
- print("Please specify vtslab package file URL using -f option.")
- return
- elif not vtslab_package_gcs_url.startswith("gs://vtslab-release/"):
- print("Please spcify a valid URL for the vtslab package.")
- return
- else:
- vti = "vtslab-schedule-" + vtslab_package_gcs_url[len(
- "gs://vtslab-release/"):].split("/")[0] + ".appspot.com"
- with settings(warn_only=True):
- screen_list_result = run("screen -list")
- lines = screen_list_result.split("\n")
- for line in lines:
- if "(Detached)" in line:
- screen_name = line.split("\t")[1]
- print(screen_name)
- with settings(warn_only=True):
- run("screen -S %s -X stuff \"exit --wait_for_jobs=True\"" %
- screen_name)
- run("screen -S %s -X stuff \"^M\"" % screen_name)
- run("screen -S %s -X stuff \"exit\"" % screen_name)
- run("screen -S %s -X stuff \"^M\"" % screen_name)
-
- vtslab_package_file_name = os.path.basename(vtslab_package_gcs_url)
- run("mkdir -p ~/run/%s.dir/" % vtslab_package_file_name)
- with cd("~/run/%s.dir" % vtslab_package_file_name):
- run("gsutil cp %s ./" % vtslab_package_gcs_url)
- run("unzip -o %s" % vtslab_package_file_name)
- adb_vendor_keys_changed = _CheckADBVendorKeysEnvVar(
- vtslab_package_file_name)
- run("pwd > ~/%s" % _VTSLAB_PACKAGE_PATH_FILENAME)
-
- with cd("~/run/%s.dir/android-vtslab/tools" % vtslab_package_file_name):
- new_screen_name = run("cat ../testcases/version.txt")
-
- with cd("~/run/%s.dir/android-vtslab/tools" % vtslab_package_file_name):
- run("./make_screen %s ; sleep 1" % new_screen_name)
-
- if adb_vendor_keys_changed:
- reset_adbd = ""
- while reset_adbd.lower() not in ["y", "n"]:
- if reset_adbd:
- print("Please type 'y' or 'n'")
- reset_adbd = raw_input(
- "Reset adb server daemon on host %s (y/n)? " % env.host)
- if reset_adbd.lower() == "y":
- run("screen -S %s -X stuff \"adb kill-server^M\"" %
- new_screen_name)
- run("screen -S %s -X stuff \"adb start-server^M\"" %
- new_screen_name)
- run("screen -S %s -X stuff \"./run --vti=%s\"" % (new_screen_name, vti))
- run("screen -S %s -X stuff \"^M\"" % new_screen_name)
- time.sleep(5)
- run("screen -S %s -X stuff \"password\"" % new_screen_name)
- run("screen -S %s -X stuff \"^M\"" % new_screen_name)
- run("screen -S %s -X stuff \"%s\"" % (new_screen_name, env.password))
- run("screen -S %s -X stuff \"^M\"" % new_screen_name)
- run("screen -S %s -X stuff \"device --lease=True\"" % new_screen_name)
- run("screen -S %s -X stuff \"^M\"" % new_screen_name)
-
- with cd("~/run/%s.dir" % vtslab_package_file_name):
- run("rm %s" % vtslab_package_file_name)
-
-
-def DeployGCE(vtslab_package_gcs_url=None):
- """Deploys a vtslab package to GCE nodes.
-
- Fetches and deploy vtslab on monitor-hc by doing;
- 1. Download android-vtslab-<>.zip from GCS using the given URL and upzip it.
- 2. Send the Ctrl-c key input to all detached screen, then cursor-up
- key input and enter key input, making the screen to execute
- the last run command.
-
- usage: $ fab DeployVtslab -p <password> -H <Google Cloud Platform project name> -f <gs://vtslab-release/...>
-
- Args:
- vtslab_package_gcs_url: string, URL to a certain vtslab package file.
- """
- if not vtslab_package_gcs_url:
- print("Please specify vtslab package file URL using -f option.")
- return
- elif not vtslab_package_gcs_url.startswith("gs://"):
- print("Please spcify a valid URL for the vtslab package.")
- return
-
- vtslab_package_file_name = os.path.basename(vtslab_package_gcs_url)
- with cd("~/run"):
- run("gsutil cp %s ./" % vtslab_package_gcs_url)
- run("unzip -o %s" % vtslab_package_file_name)
- run("rm %s" % vtslab_package_file_name)
-
- with settings(warn_only=True):
- screen_list_result = run("screen -list")
- lines = screen_list_result.split("\n")
- for line in lines:
- if "(Detached)" in line:
- screen_name = line.split("\t")[1]
- run("screen -S %s -X stuff \"^C\"" % screen_name)
- run("screen -S %s -X stuff \"\033[A\"" % screen_name)
- run("screen -S %s -X stuff \"^M\"" % screen_name)
diff --git a/host_setup/host_setup.sh b/host_setup/host_setup.sh
deleted file mode 100755
index fa52755..0000000
--- a/host_setup/host_setup.sh
+++ /dev/null
@@ -1,96 +0,0 @@
-#/bin/bash
-#
-# Copyright 2018 - The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-SCRIPT_NAME=$(basename $0)
-usage()
-{
- echo "Usage: $SCRIPT_NAME <task_name> [options]"
- echo ""
- echo "Options:"
- echo " -p PASSWORD password for hosts."
- echo " -H PATH path to a .py file contains hosts information"
- echo " -i PATH path to a .py file contains ip address information of certified machines"
- echo " -f GCS_URL URL to the vtslab package file to be deployed."
- exit 1
-}
-
-FABRIC_EXISTS=$(pip show fabric)
-if [ -z "$FABRIC_EXISTS" ]; then
- INSTALL_FABRIC=true
-else
- FABRIC_VERSION=$(fab -V | grep Fabric)
- if [ "${FABRIC_VERSION:7:1}" -ne 1 ]; then
- INSTALL_FABRIC=true
- fi
-fi
-if [ "$INSTALL_FABRIC" == true ]; then
- sudo pip install fabric==1.14.0 --force
-fi
-
-TASK=$1
-if [[ ${TASK:0:1} == "-" ]]; then
- usage
-fi
-
-shift
-PASSWORD=""
-HOSTS_PATH="hosts.py"
-IPADDRESSES_PATH=""
-VTSLAB_PACKAGE_GCS_URL=""
-
-while getopts ":p:H:i:f:" opt; do
- case $opt in
- p)
- PASSWORD=$OPTARG
- ;;
- H)
- HOSTS_PATH=$OPTARG
- ;;
- i)
- IPADDRESSES_PATH=$OPTARG
- ;;
- f)
- VTSLAB_PACKAGE_GCS_URL=$OPTARG
- ;;
- \?)
- echo "Invalid option: -$OPTARG"
- usage
- ;;
- :)
- echo "Option -$OPTARG requires an argument."
- usage
- ;;
- esac
-done
-
-if [ "$TASK" == "SetupIptables" ]; then
- fab SetPassword:$PASSWORD GetHosts:$HOSTS_PATH $TASK:$IPADDRESSES_PATH
-elif [ "$TASK" == "SetupPackages" ]; then
- if [ -z "$IPADDRESSES_PATH" ]; then
- fab SetPassword:$PASSWORD GetHosts:$HOSTS_PATH $TASK
- else
- fab SetPassword:$PASSWORD GetHosts:$HOSTS_PATH $TASK:$IPADDRESSES_PATH
- fi
-elif [ "$TASK" == "DeployVtslab" ] || [ "$TASK" == "DeployGCE" ]; then
- if [ -z "$VTSLAB_PACKAGE_GCS_URL" ]; then
- echo "Please specify vtslab package file URL using -f option."
- exit
- fi
- fab SetPassword:$PASSWORD GetHosts:$HOSTS_PATH $TASK:$VTSLAB_PACKAGE_GCS_URL
-else
- fab SetPassword:$PASSWORD GetHosts:$HOSTS_PATH $TASK
-fi
diff --git a/script/run-unittest.sh b/script/run-unittest.sh
deleted file mode 100755
index fb4fca0..0000000
--- a/script/run-unittest.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-TEST_FRAMEWORK_DIR=`dirname $0`
-TEST_FRAMEWORK_DIR=`dirname $TEST_FRAMEWORK_DIR`
-
-if [ -z "$ANDROID_BUILD_TOP" ]; then
- echo "Missing ANDROID_BUILD_TOP env variable. Run 'lunch' first."
- exit 1
-fi
-
-touch $ANDROID_BUILD_TOP/test/vti/__init__.py
-
-avoid_list=(
- # known failures
- "./harnesses/host_controller/console_test.py"
- "./harnesses/host_controller/invocation_thread_test.py"
- # not unit tests
- "./harnesses/host_controller/command_processor/command_test.py"
- )
-
-#######################################
-# Checks if a given file is included in the list of files to avoid
-# Globals:
-# Arguments:
-# $1: list of files to avoid
-# $2: the given file
-# Returns:
-# SUCCESS, if the given file exists in the list
-# FAILURE, otherwise
-#######################################
-function contains_file() {
- local -n arr=$1
- for avoid in "${arr[@]}"; do
- if [ "$2" = "$avoid" ]; then
- return # contains
- fi
- done
- false # not contains
-}
-
-# Runs all unit tests under test/framework.
-for t in $(find $TEST_FRAMEWORK_DIR -type f -name "*_test.py");
-do
- if contains_file avoid_list $t; then
- continue
- fi
- echo "UNIT TEST", $t
- echo PYTHONPATH=$ANDROID_BUILD_TOP/test/framework/harnesses:$ANDROID_BUILD_TOP/test python $t;
- PYTHONPATH=$ANDROID_BUILD_TOP/test/framework/harnesses:$ANDROID_BUILD_TOP/test python $t;
-done
diff --git a/tools/host_controller/Android.bp b/tools/host_controller/Android.bp
deleted file mode 100644
index 174ac4a..0000000
--- a/tools/host_controller/Android.bp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-sh_binary_host {
- name: "run",
- src: "run",
-}
diff --git a/tools/host_controller/make_screen b/tools/host_controller/make_screen
deleted file mode 100755
index 10aed5d..0000000
--- a/tools/host_controller/make_screen
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Sets env var PATH and makes a detached screen instance
-pushd ../bin
-export PATH=`pwd`:$PATH
-popd
-screen -mdS $1
diff --git a/tools/host_controller/run b/tools/host_controller/run
deleted file mode 100755
index 43b3707..0000000
--- a/tools/host_controller/run
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# launcher script for vts-hc (host controller)
-# can be used from an Android build environment, or a standalone vts zip
-
-# get OS
-HOST=`uname`
-if [ "$HOST" == "Linux" ]; then
- OS="linux-x86"
-elif [ "$HOST" == "Darwin" ]; then
- OS="darwin-x86"
-else
- echo "Unrecognized OS"
- exit
-fi
-
-# check if in Android build env
-if [ ! -z "${ANDROID_BUILD_TOP}" ]; then
- if [ ! -z "${ANDROID_HOST_OUT}" ]; then
- VTS_ROOT=${ANDROID_HOST_OUT}/vtslab
- else
- VTS_ROOT=${ANDROID_BUILD_TOP}/${OUT_DIR:-out}/host/${OS}/vtslab
- fi
- if [ ! -d ${VTS_ROOT} ]; then
- echo "Could not find $VTS_ROOT in Android build environment. Try 'make vts'"
- exit
- fi;
-fi;
-
-if [ -z ${VTS_ROOT} ]; then
- # assume we're in an extracted vts install
- VTS_ROOT="$(dirname $(readlink -e $0))/../.."
-fi;
-
-find ${VTS_ROOT} -name "*.pyc" -exec rm -f {} \;
-cd ${VTS_ROOT}/android-vtslab/testcases/; python -m host_controller.main "$@"