From 9f975adcc7eeec9faef33f4467e3028a7724c185 Mon Sep 17 00:00:00 2001 From: Bodong Yang <86948717+Bodong-Yang@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:12:05 +0900 Subject: [PATCH] chore(tools): remove out-of-date unmaintained tools (#313) This PR removes the unmaintained old tools, including tools.emulator, tools.test_utils and tools/build_image.sh. --- .dockerignore | 1 - tools/build_image.sh | 23 ---- tools/emulator/README.md | 10 -- tools/emulator/config.yml | 31 ----- tools/emulator/ecu.py | 180 ------------------------ tools/emulator/main.py | 102 -------------- tools/emulator/ota_client_stub.py | 105 -------------- tools/emulator/path_loader.py | 29 ---- tools/emulator/requirements.txt | 3 - tools/test_utils/README.md | 43 ------ tools/test_utils/__init__.py | 13 -- tools/test_utils/_logutil.py | 27 ---- tools/test_utils/_update_call.py | 58 -------- tools/test_utils/api_caller.py | 109 --------------- tools/test_utils/ecu_info.yaml | 9 -- tools/test_utils/setup_ecu.py | 197 --------------------------- tools/test_utils/update_request.yaml | 13 -- 17 files changed, 953 deletions(-) delete mode 100644 .dockerignore delete mode 100644 tools/build_image.sh delete mode 100644 tools/emulator/README.md delete mode 100644 tools/emulator/config.yml delete mode 100644 tools/emulator/ecu.py delete mode 100644 tools/emulator/main.py delete mode 100644 tools/emulator/ota_client_stub.py delete mode 100644 tools/emulator/path_loader.py delete mode 100644 tools/emulator/requirements.txt delete mode 100644 tools/test_utils/README.md delete mode 100644 tools/test_utils/__init__.py delete mode 100644 tools/test_utils/_logutil.py delete mode 100644 tools/test_utils/_update_call.py delete mode 100644 tools/test_utils/api_caller.py delete mode 100644 tools/test_utils/ecu_info.yaml delete mode 100644 tools/test_utils/setup_ecu.py delete mode 100644 tools/test_utils/update_request.yaml diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 85196eea0..000000000 --- a/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -ota-image.* diff --git a/tools/build_image.sh b/tools/build_image.sh deleted file mode 100644 index 9cad89ec4..000000000 --- a/tools/build_image.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -eux - -docker build -t ota-image -f ./docker/build_image/Dockerfile . -id=$(docker create -it ota-image) -ota_image_dir="ota-image.$(date +%Y%m%d%H%M%S)" -mkdir ${ota_image_dir} - -cd ${ota_image_dir} -docker export ${id} > ota-image.tar -docker rm ${id} -mkdir data -sudo tar xf ota-image.tar -C data -git clone https://github.com/tier4/ota-metadata - -cp ../tests/keys/sign.pem . -cp ota-metadata/metadata/persistents.txt . - -sudo python3 ota-metadata/metadata/ota_metadata/metadata_gen.py --target-dir data --ignore-file ota-metadata/metadata/ignore.txt -sudo python3 ota-metadata/metadata/ota_metadata/metadata_sign.py --sign-key ../tests/keys/sign.key --cert-file sign.pem --persistent-file persistents.txt --rootfs-directory data - -sudo chown -R $(whoami) data diff --git a/tools/emulator/README.md b/tools/emulator/README.md deleted file mode 100644 index a536010de..000000000 --- a/tools/emulator/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# ota-client emulator - -This tool mimics ota-client behavior. -This tool can be used to develop software which requests ota-client. - -## Usage - -```bash -python main.py --config config.yml -``` diff --git a/tools/emulator/config.yml b/tools/emulator/config.yml deleted file mode 100644 index 8532f3185..000000000 --- a/tools/emulator/config.yml +++ /dev/null @@ -1,31 +0,0 @@ -ecus: -- main: true # main ecu or not. only one ecu should be main. - name: autoware # should be unique - status: INITIALIZED # INITIALIZED | SUCCESS | FAILURE | UPDATING | NO_CONNECTION - version: 123.456 # current version - time_to_update: 40 # in second - time_to_restart: 10 # in second - -- name: perception1 # should be unique - status: INITIALIZED # INITIALIZED | SUCCESS | FAILURE | UPDATING | NO_CONNECTION - version: abc.def # current version - time_to_update: 60 # in second - time_to_restart: 10 # in second - -- name: perception2 - status: INITIALIZED # INITIALIZED | SUCCESS | FAILURE | UPDATING | NO_CONNECTION - version: abc.def - time_to_update: 60 # in second - time_to_restart: 10 # in second - -- name: perception3 - status: INITIALIZED # INITIALIZED | SUCCESS | FAILURE | UPDATING | NO_CONNECTION - version: abc.def - time_to_update: 60 # in second - time_to_restart: 10 # in second - -#- name: perception4 -# status: INITIALIZED # INITIALIZED | SUCCESS | FAILURE | UPDATING | NO_CONNECTION -# version: abc.def -# time_to_update: 60 # in second -# time_to_restart: 10 # in second diff --git a/tools/emulator/ecu.py b/tools/emulator/ecu.py deleted file mode 100644 index afd8e2795..000000000 --- a/tools/emulator/ecu.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 - -import log_setting -import otaclient_v2_pb2 as v2 -from configs import config as cfg - -logger = log_setting.get_logger( - __name__, cfg.LOG_LEVEL_TABLE.get(__name__, cfg.DEFAULT_LOG_LEVEL) -) - - -class Ecu: - TOTAL_REGULAR_FILES = 123456789 - TOTAL_REGULAR_FILE_SIZE = 987654321 - TIME_TO_UPDATE = 60 * 5 - TIME_TO_RESTART = 10 - - def __init__( - self, - is_main, - name, - status, - version, - time_to_update=TIME_TO_UPDATE, - time_to_restart=TIME_TO_RESTART, - ): - self._is_main = is_main - self._name = name - self._version = version - self._version_to_update = None - self._status = v2.Status() - self._status.status = v2.StatusOta.Value(status) - self._update_time = None - self._time_to_update = time_to_update - self._time_to_restart = time_to_restart - - def create(self): - return Ecu( - is_main=self._is_main, - name=self._name, - status=v2.StatusOta.Name(self._status.status), - version=self._version, - time_to_update=self._time_to_update, - time_to_restart=self._time_to_restart, - ) - - def change_to_success(self): - if self._status.status != v2.StatusOta.UPDATING: - logger.warning(f"current status: {v2.StatusOta.Name(self._status.status)}") - return - logger.info(f"change_to_success: {self._name=}") - self._status.status = v2.StatusOta.SUCCESS - self._version = self._version_to_update - - def update(self, response_ecu, version): - ecu = response_ecu - ecu.ecu_id = self._name - ecu.result = v2.FailureType.NO_FAILURE - - # update status - self._status = v2.Status() # reset - self._status.status = v2.StatusOta.UPDATING - self._version_to_update = version - self._update_time = time.time() - - def status(self, response_ecu): - ecu = response_ecu - ecu.ecu_id = self._name - ecu.result = v2.FailureType.NO_FAILURE - - try: - elapsed = time.time() - self._update_time - progress_rate = elapsed / self._time_to_update - should_restart = elapsed > (self._time_to_update + self._time_to_restart) - except TypeError: # when self._update_time is None - elapsed = 0 - progress_rate = 0 - should_restart = False - - # Main ecu waits for all sub ecu sucesss, while sub ecu transitions to - # success by itself. This code is intended to mimic that. - # The actual ecu updates, restarts and then transitions to success. - # In this code, after starting update and after time_to_update + - # time_to_restart elapsed, it transitions to success. - if not self._is_main and should_restart: - self.change_to_success() - else: - ecu.status.progress.CopyFrom(self._progress_rate_to_progress(progress_rate)) - - ecu.status.status = self._status.status - ecu.status.failure = v2.FailureType.NO_FAILURE - ecu.status.failure_reason = "" - ecu.status.version = self._version - - def _progress_rate_to_progress(self, rate): - progress = v2.StatusProgress() - if rate == 0: - progress.phase = v2.StatusProgressPhase.INITIAL - elif rate <= 0.01: - progress.phase = v2.StatusProgressPhase.METADATA - elif rate <= 0.02: - progress.phase = v2.StatusProgressPhase.DIRECTORY - elif rate <= 0.03: - progress.phase = v2.StatusProgressPhase.SYMLINK - elif rate <= 0.95: - progress.phase = v2.StatusProgressPhase.REGULAR - progress.total_regular_files = self.TOTAL_REGULAR_FILES - progress.regular_files_processed = int(self.TOTAL_REGULAR_FILES * rate) - - progress.files_processed_copy = int(progress.regular_files_processed * 0.4) - progress.files_processed_link = int(progress.regular_files_processed * 0.01) - progress.files_processed_download = ( - progress.regular_files_processed - - progress.files_processed_copy - - progress.files_processed_link - ) - size_processed = int(self.TOTAL_REGULAR_FILE_SIZE * rate) - progress.file_size_processed_copy = int(size_processed * 0.4) - progress.file_size_processed_link = int(size_processed * 0.01) - progress.file_size_processed_download = ( - size_processed - - progress.file_size_processed_copy - - progress.file_size_processed_link - ) - - progress.elapsed_time_copy.FromSeconds( - int(self._time_to_update * rate * 0.4) - ) - progress.elapsed_time_link.FromSeconds( - int(self._time_to_update * rate * 0.01) - ) - progress.elapsed_time_download.FromSeconds( - int(self._time_to_update * rate * 0.6) - ) - progress.errors_download = int(rate * 0.1) - progress.total_regular_file_size = self.TOTAL_REGULAR_FILE_SIZE - progress.total_elapsed_time.FromSeconds(int(self._time_to_update * rate)) - else: - progress.phase = v2.StatusProgressPhase.PERSISTENT - progress.total_regular_files = self.TOTAL_REGULAR_FILES - progress.regular_files_processed = self.TOTAL_REGULAR_FILES - - progress.files_processed_copy = int(progress.regular_files_processed * 0.4) - progress.files_processed_link = int(progress.regular_files_processed * 0.01) - progress.files_processed_download = ( - progress.regular_files_processed - - progress.files_processed_copy - - progress.files_processed_link - ) - size_processed = self.TOTAL_REGULAR_FILE_SIZE - progress.file_size_processed_copy = int(size_processed * 0.4) - progress.file_size_processed_link = int(size_processed * 0.01) - progress.file_size_processed_download = ( - size_processed - - progress.file_size_processed_copy - - progress.file_size_processed_link - ) - - progress.elapsed_time_copy.FromSeconds(int(self._time_to_update * 0.4)) - progress.elapsed_time_link.FromSeconds(int(self._time_to_update * 0.01)) - progress.elapsed_time_download.FromSeconds(int(self._time_to_update * 0.6)) - progress.errors_download = int(rate * 0.1) - progress.total_regular_file_size = self.TOTAL_REGULAR_FILE_SIZE - progress.total_elapsed_time.FromSeconds(self._time_to_update) - return progress diff --git a/tools/emulator/main.py b/tools/emulator/main.py deleted file mode 100644 index 8c859d54e..000000000 --- a/tools/emulator/main.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 pathlib import Path - -import log_setting -import otaclient_v2_pb2 as v2 -import otaclient_v2_pb2_grpc as v2_grpc -import path_loader # noqa -import yaml -from configs import config as cfg -from configs import server_cfg -from ecu import Ecu -from ota_client_service import ( - OtaClientServiceV2, - service_start, - service_stop, - service_wait_for_termination, -) -from ota_client_stub import OtaClientStub - -logger = log_setting.get_logger( - __name__, cfg.LOG_LEVEL_TABLE.get(__name__, cfg.DEFAULT_LOG_LEVEL) -) - -DEFAULT_ECUS = [ - {"main": True, "id": "autoware", "status": "INITIALIZED", "version": "123.456"} -] - - -def main(config_file): - logger.info("started") - - server = None - - try: - config = yaml.safe_load(config_file.read_text()) - ecu_config = config["ecus"] - except Exception as e: - logger.warning(e) - logger.warning( - f"{config_file} couldn't be parsed. Default config is used instead." - ) - ecu_config = DEFAULT_ECUS - ecus = [] - logger.info(ecu_config) - for ecu in ecu_config: - e = Ecu( - is_main=ecu.get("main", False), - name=ecu.get("name", "autoware"), - status=ecu.get("status", "INITIALIZED"), - version=str(ecu.get("version", "")), - time_to_update=ecu.get("time_to_update"), - time_to_restart=ecu.get("time_to_restart"), - ) - ecus.append(e) - logger.info(ecus) - - def terminate(restart_time): - logger.info(f"{server=}") - service_stop(server) - logger.info(f"restarting. wait {restart_time}s.") - time.sleep(restart_time) - - while True: - ota_client_stub = OtaClientStub(ecus, terminate) - ota_client_service_v2 = OtaClientServiceV2(ota_client_stub) - - logger.info("starting grpc server.") - server = service_start( - f"localhost:{server_cfg.SERVER_PORT}", - [ - {"grpc": v2_grpc, "instance": ota_client_service_v2}, - ], - ) - - service_wait_for_termination(server) - - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--config", help="config.yml", default="config.yml") - args = parser.parse_args() - - logger.info(args) - - main(Path(args.config)) diff --git a/tools/emulator/ota_client_stub.py b/tools/emulator/ota_client_stub.py deleted file mode 100644 index c20d7c2ac..000000000 --- a/tools/emulator/ota_client_stub.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 pathlib import Path -from threading import Thread, Timer - -import log_setting -import otaclient_v2_pb2 as v2 -from configs import config as cfg -from ecu import Ecu - -logger = log_setting.get_logger( - __name__, cfg.LOG_LEVEL_TABLE.get(__name__, cfg.DEFAULT_LOG_LEVEL) -) - - -class OtaClientStub: - def __init__(self, ecus: list, terminate=None): - # check if all the names are unique - names = [ecu._name for ecu in ecus] - assert len(names) == len(set(names)) - # check if only one ecu is main - mains = [ecu for ecu in ecus if ecu._is_main] - assert len(mains) == 1 - - self._ecus = ecus - self._main_ecu = mains[0] - self._terminate = terminate - - async def update(self, request: v2.UpdateRequest) -> v2.UpdateResponse: - logger.info(f"{request=}") - response = v2.UpdateResponse() - - for ecu in self._ecus: - entry = OtaClientStub._find_request(request.ecu, ecu._name) - if entry: - logger.info(f"{ecu=}, {entry.version=}") - response_ecu = response.ecu.add() - ecu.update(response_ecu, entry.version) - - logger.info(f"{response=}") - return response - - def rollback(self, request): - logger.info(f"{request=}") - response = v2.RollbackResponse() - - return response - - async def status(self, request: v2.StatusRequest) -> v2.StatusResponse: - logger.info(f"{request=}") - response = v2.StatusResponse() - - for ecu in self._ecus: - response_ecu = response.ecu.add() - ecu.status(response_ecu) - response.available_ecu_ids.extend([ecu._name]) - - logger.debug(f"{response=}") - - if self._sub_ecus_success_and_main_ecu_phase_persistent(response.ecu): - self._main_ecu.change_to_success() - for index, ecu in enumerate(self._ecus): - self._ecus[index] = ecu.create() # create new ecu instances - self._terminate(self._main_ecu._time_to_restart) - - return response - - @staticmethod - def _find_request(update_request, ecu_id): - for request in update_request: - if request.ecu_id == ecu_id: - return request - return None - - def _update(self, ecu, response): - ecu.update(response) - - def _status(self, ecu, response): - ecu.status(response) - - def _sub_ecus_success_and_main_ecu_phase_persistent(self, response_ecu): - for ecu in response_ecu: - if ecu.ecu_id == self._main_ecu._name: - if ( - ecu.status.status != v2.StatusOta.UPDATING - or ecu.status.progress.phase != v2.StatusProgressPhase.PERSISTENT - ): - return False - else: - if ecu.status.status != v2.StatusOta.SUCCESS: - return False - return True diff --git a/tools/emulator/path_loader.py b/tools/emulator/path_loader.py deleted file mode 100644 index dd93760fb..000000000 --- a/tools/emulator/path_loader.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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. - - -# NOTE: this file should only be loaded once by the program entry! - - -###### load path ###### -def _path_load(): - import sys - from pathlib import Path - - project_base = Path(__file__).absolute().parent.parent - sys.path.extend([str(project_base), str(project_base / "app")]) - - -_path_load() -###### diff --git a/tools/emulator/requirements.txt b/tools/emulator/requirements.txt deleted file mode 100644 index 3eadadb31..000000000 --- a/tools/emulator/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -grpcio==1.53.0 -protobuf==3.18.3 -PyYAML>=3.12 diff --git a/tools/test_utils/README.md b/tools/test_utils/README.md deleted file mode 100644 index 8384ef764..000000000 --- a/tools/test_utils/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Test utils for debugging otaclient - -This test utils set provides lib to directly query otaclient `update/status/rollback` API, and a tool to simulate dummy multi-ecu setups. - -## Usage guide for setting up test environemnt - -This test_utils can be used to setup a test environment consists of a real otaclient(either on VM or on actually ECU) as main ECU, -and setup many dummy subECUs that can receive update request and return the expected status report. - -### 1. Install the otaclient's dependencies - -`test_utils` depends on otaclient, so you need to install at least the dependencies of otaclient. -Please refer to [docs/INSTALLATION.md](docs/INSTALLATION.md). - -### 2. Update the `ecu_info.yaml` and `update_request.yaml` accordingly - -Update the `ecu_info.yaml` under the `test_utils` folder as your test environment design, -the example `ecu_info.yaml` consists of one mainECU `autoware`(expecting to be a real otaclient), -and 2 dummy subECUs which will be prepared at step 2. - -Update the `update_request.yaml` under the `test_utils` folder as your test environment setup. -This file contains the update request to be sent. - -### 3. Launch `setup_ecu.py` to setup dummy subECUs, and launch the real otaclient - -Setup subECUs: - -```python -# with venv, under the tools/ folder -python3 -m test_utils.setup_ecu subecus -``` - -And then launch the real otaclient, be sure that the otaclient is reachable to the machine -that running the test_utils. - -### 4. Send an update request to main ECU - -For example, we have `autoware` ECU as main ECU, then - -```python -# with venv, under the tools/ folder -python3 -m test_utils.api_caller update -t autoware -``` diff --git a/tools/test_utils/__init__.py b/tools/test_utils/__init__.py deleted file mode 100644 index bcfd866ad..000000000 --- a/tools/test_utils/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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. diff --git a/tools/test_utils/_logutil.py b/tools/test_utils/_logutil.py deleted file mode 100644 index a26aabd64..000000000 --- a/tools/test_utils/_logutil.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 - -_log_format = ( - "[%(asctime)s][%(levelname)s]-%(filename)s:%(funcName)s:%(lineno)d,%(message)s" -) -logging.basicConfig(format=_log_format) - - -def get_logger(name: str, level: int = logging.DEBUG) -> logging.Logger: - logger = logging.getLogger(name) - logger.setLevel(level) - return logger diff --git a/tools/test_utils/_update_call.py b/tools/test_utils/_update_call.py deleted file mode 100644 index b3c45fcbb..000000000 --- a/tools/test_utils/_update_call.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 yaml - -from otaclient.app.ota_client_call import ECUNoResponse, OtaClientCall -from otaclient.app.proto import wrapper - -from . import _logutil - -logger = _logutil.get_logger(__name__) - - -def load_external_update_request(request_yaml_file: str) -> wrapper.UpdateRequest: - with open(request_yaml_file, "r") as f: - try: - request_yaml = yaml.safe_load(f) - assert isinstance(request_yaml, list), "expect update request to be a list" - except Exception as e: - logger.exception(f"invalid update request yaml: {e!r}") - raise - - logger.info(f"load external request: {request_yaml!r}") - request = wrapper.UpdateRequest() - for request_ecu in request_yaml: - request.ecu.append(wrapper.UpdateRequestEcu(**request_ecu)) - return request - - -async def call_update( - ecu_id: str, - ecu_ip: str, - ecu_port: int, - *, - request_file: str, -): - logger.debug(f"request update on ecu(@{ecu_id}) at {ecu_ip}:{ecu_port}") - update_request = load_external_update_request(request_file) - - try: - update_response = await OtaClientCall.update_call( - ecu_id, ecu_ip, ecu_port, request=update_request - ) - logger.info(f"{update_response.export_pb()=}") - except ECUNoResponse as e: - logger.exception(f"update request failed: {e!r}") diff --git a/tools/test_utils/api_caller.py b/tools/test_utils/api_caller.py deleted file mode 100644 index 529e9cd54..000000000 --- a/tools/test_utils/api_caller.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 asyncio -import sys -from pathlib import Path - -import yaml - -try: - import otaclient # noqa: F401 -except ImportError: - sys.path.insert(0, str(Path(__file__).parent.parent.parent)) -from . import _logutil, _update_call - -logger = _logutil.get_logger(__name__) - - -async def main(args: argparse.Namespace): - with open(args.ecu_info, "r") as f: - ecu_info = yaml.safe_load(f) - assert isinstance(ecu_info, dict) - - target_ecu_id = args.target - # by default, send request to main ECU - try: - ecu_id = ecu_info["ecu_id"] - ecu_ip = ecu_info["ip_addr"] - ecu_port = 50051 - except KeyError: - raise ValueError(f"invalid ecu_info: {ecu_info=}") - - if target_ecu_id != ecu_info.get("ecu_id"): - found = False - # search for target by ecu_id - for subecu in ecu_info.get("secondaries", []): - try: - if subecu["ecu_id"] == target_ecu_id: - ecu_id = subecu["ecu_id"] - ecu_ip = subecu["ip_addr"] - ecu_port = int(subecu.get("port", 50051)) - found = True - break - except KeyError: - continue - - if not found: - logger.critical(f"target ecu {target_ecu_id} is not found") - sys.exit(-1) - - logger.info(f"send request to target ecu: {ecu_id=}, {ecu_ip=}") - cmd = args.command - if cmd == "update": - await _update_call.call_update( - ecu_id, - ecu_ip, - ecu_port, - request_file=args.request, - ) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="calling ECU's API", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - parser.add_argument( - "-c", - "--ecu_info", - type=str, - default="test_utils/ecu_info.yaml", - help="ecu_info file to configure the caller", - ) - parser.add_argument("command", help="API to call, available API: update") - parser.add_argument( - "-t", - "--target", - default="autoware", - help="indicate the target for the API request", - ) - parser.add_argument( - "-r", - "--request", - default="test_utils/update_request.yaml", - help="(update) yaml file that contains the request to send", - ) - - args = parser.parse_args() - if args.command != "update": - parser.error(f"unknown API: {args.command} (available: update)") - if not Path(args.ecu_info).is_file(): - parser.error(f"ecu_info file {args.ecu_info} not found!") - if args.command == "update" and not Path(args.request).is_file(): - parser.error(f"update request file {args.request} not found!") - - asyncio.run(main(args)) diff --git a/tools/test_utils/ecu_info.yaml b/tools/test_utils/ecu_info.yaml deleted file mode 100644 index 2c8c7c8e4..000000000 --- a/tools/test_utils/ecu_info.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# sample ecu_info.yaml, with 2 perception ECUs -format_version: 1 -ecu_id: "autoware" -ip_addr: "10.0.0.2" -secondaries: - - ecu_id: "p1" - ip_addr: "10.0.0.11" - - ecu_id: "p2" - ip_addr: "10.0.0.12" diff --git a/tools/test_utils/setup_ecu.py b/tools/test_utils/setup_ecu.py deleted file mode 100644 index 4190262a6..000000000 --- a/tools/test_utils/setup_ecu.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright 2022 TIER IV, INC. All rights reserved. -# -# 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 asyncio -import sys -from pathlib import Path -from typing import List - -import grpc -import yaml - -try: - import otaclient # noqa: F401 -except ImportError: - sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - -from otaclient.app.ota_client_service import service_wait_for_termination -from otaclient.app.proto import v2, v2_grpc - -from . import _logutil - -logger = _logutil.get_logger(__name__) - -_DEFAULT_PORT = 50051 -_MODE = {"standalone", "mainecu", "subecus"} - - -class MiniOtaClientServiceV2(v2_grpc.OtaClientServiceServicer): - UPDATE_TIME_COST = 10 - REBOOT_INTERVAL = 5 - - def __init__(self, ecu_id: str): - self.ecu_id = ecu_id - self._lock = asyncio.Lock() - self._in_update = asyncio.Event() - self._rebooting = asyncio.Event() - - async def _on_update(self): - await asyncio.sleep(self.UPDATE_TIME_COST) - logger.debug(f"{self.ecu_id=} finished update, rebooting...") - self._rebooting.set() - await asyncio.sleep(self.REBOOT_INTERVAL) - self._rebooting.clear() - self._in_update.clear() - - async def Update(self, request: v2.UpdateRequest, context: grpc.ServicerContext): - peer = context.peer() - logger.debug(f"{self.ecu_id}: update request from {peer=}") - logger.debug(f"{request=}") - - # return if not listed as target - found = False - for ecu in request.ecu: - if ecu.ecu_id == self.ecu_id: - found = True - break - if not found: - logger.debug(f"{self.ecu_id}, Update: not listed as update target, abort") - return v2.UpdateResponse() - - results = v2.UpdateResponse() - if self._in_update.is_set(): - resp_ecu = v2.UpdateResponseEcu( - ecu_id=self.ecu_id, - result=v2.RECOVERABLE, - ) - results.ecu.append(resp_ecu) - else: - logger.debug("start update") - self._in_update.set() - asyncio.create_task(self._on_update()) - - return results - - async def Status(self, _, context: grpc.ServicerContext): - peer = context.peer() - logger.debug(f"{self.ecu_id}: status request from {peer=}") - if self._rebooting.is_set(): - return v2.StatusResponse() - - result_ecu = v2.StatusResponseEcu( - ecu_id=self.ecu_id, - result=v2.NO_FAILURE, - ) - - ecu_status = result_ecu.status - if self._in_update.is_set(): - ecu_status.status = v2.UPDATING - else: - ecu_status.status = v2.SUCCESS - - result = v2.StatusResponse() - result.ecu.append(result_ecu) - return result - - -async def launch_otaclient(ecu_id, ecu_ip, ecu_port): - server = grpc.aio.server() - service = MiniOtaClientServiceV2(ecu_id) - v2_grpc.add_OtaClientServiceServicer_to_server(service, server) - - server.add_insecure_port(f"{ecu_ip}:{ecu_port}") - await server.start() - await service_wait_for_termination(server) - - -async def mainecu_mode(ecu_info_file: str): - ecu_info = yaml.safe_load(Path(ecu_info_file).read_text()) - ecu_id = ecu_info["ecu_id"] - ecu_ip = ecu_info["ip_addr"] - ecu_port = int(ecu_info.get("port", _DEFAULT_PORT)) - - logger.info(f"start {ecu_id=} at {ecu_ip}:{ecu_port}") - await launch_otaclient(ecu_id, ecu_ip, ecu_port) - - -async def subecu_mode(ecu_info_file: str): - ecu_info = yaml.safe_load(Path(ecu_info_file).read_text()) - - # schedule the servers to the thread pool - tasks: List[asyncio.Task] = [] - for subecu in ecu_info["secondaries"]: - ecu_id = subecu["ecu_id"] - ecu_ip = subecu["ip_addr"] - ecu_port = int(subecu.get("port", _DEFAULT_PORT)) - logger.info(f"start {ecu_id=} at {ecu_ip}:{ecu_port}") - tasks.append(asyncio.create_task(launch_otaclient(ecu_id, ecu_ip, ecu_port))) - - await asyncio.gather(*tasks) - - -async def standalone_mode(args: argparse.Namespace): - await launch_otaclient("standalone", args.ip, args.port) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="calling main ECU's API", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - parser.add_argument( - "-c", "--ecu_info", default="test_utils/ecu_info.yaml", help="ecu_info" - ) - parser.add_argument( - "mode", - default="standalone", - help=( - "running mode for mini_ota_client(standalone, subecus, mainecu)\n" - "\tstandalone: run a single mini_ota_client\n" - "\tmainecu: run a single mini_ota_client as mainecu according to ecu_info.yaml\n" - "\tsubecus: run subecu(s) according to ecu_info.yaml" - ), - ) - parser.add_argument( - "--ip", - default="127.0.0.1", - help="(standalone) listen at IP", - ) - parser.add_argument( - "--port", - default=_DEFAULT_PORT, - help="(standalone) use port PORT", - ) - - args = parser.parse_args() - - if args.mode not in _MODE: - parser.error(f"invalid mode {args.mode}, should be one of {_MODE}") - if args.mode != "standalone" and not Path(args.ecu_info).is_file(): - parser.error( - f"invalid ecu_info_file {args.ecu_info!r}. ecu_info.yaml is required for non-standalone mode" - ) - - if args.mode == "subecus": - logger.info("subecus mode") - coro = subecu_mode(args.ecu_info) - elif args.mode == "mainecu": - logger.info("mainecu mode") - coro = mainecu_mode(args.ecu_info) - else: - logger.info("standalone mode") - coro = standalone_mode(args) - - asyncio.run(coro) diff --git a/tools/test_utils/update_request.yaml b/tools/test_utils/update_request.yaml deleted file mode 100644 index 02e612a9e..000000000 --- a/tools/test_utils/update_request.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# sample update request -- ecu_id: "autoware" - version: "789.x" - url: "http://10.0.0.1:8080" - cookies: '{"test": "my-cookie"}' -- ecu_id: "p1" - version: "789.x" - url: "http://10.0.0.1:8080" - cookies: '{"test": "my-cookie"}' -- ecu_id: "p2" - version: "789.x" - url: "http://10.0.0.1:8080" - cookies: '{"test": "my-cookie"}'