Repository: tier4/ota-client
Pull request #496: refactor: modularize ota core
Author: login: airkei
Labels: name: refactor
description: Rewrite/remove related code instead of patching them
Created: 2025-03-05T00:17:25Z | Merged: 2025-03-07T00:27:35Z

## Description

<!-- Summarize the change this PR wants to introduce.

For better understanding, adding reason/motivation of this PR are also recommended.
-->
### Why
This is refactoring PR and the initial PR to support otaclient update.
This PR doesn't change the logic/behavior at all.

## Check list

<!-- A list of things needed to be done before set the PR as ready-for-review. -->

- [x] test file(s) that covers the change(s) is implemented.
- [x] local test is passed.

## Changes
- Create `_OTAUpdateOperator` class, which will provide common features for both new client and the existing data updates. client update feature will be implemented in next PR. The existing `_OTAUpdater` class inherits this common `_OTAUpdateOperator` class.
This PR doesn't include `_OTAClientUpdater` part at all.
![image](https://github.com/user-attachments/assets/0d45a7ee-f2ee-455c-a652-3468dda7f5b2)


- modularize `_execute_update` functions into multiple functions for single responsibility and testability.
- `DownloadInfo` is generic format and reusable for client update, thus move it from `ota_metadata` to `otaclient_common/downloader`


<!-- A list of code change(s) that introduced by this PR. -->

## Behavior changes

Does this PR introduce behavior change(s)?

- [ ] Yes, internal behaivor (will not impact user experience).
- [ ] Yes, external behaivor (will impact user experience).
- [x] No.

### Previous behavior

<!-- Behaivor before the PR is introduced -->

### Behavior with this PR

<!-- Behavior after the PR is introduced -->

## Breaking change

Does this PR introduce breaking change?

- [ ] Yes.
- [x] No.

<!-- List the breaking change(s) -->

## Related links & tickets

<!-- List of tickets or links related to this PR -->
https://tier4.atlassian.net/browse/RT4-13645


Diff:
diff --git a/src/ota_metadata/legacy2/metadata.py b/src/ota_metadata/legacy2/metadata.py
index d62750b73..b5e455967 100644
--- a/src/ota_metadata/legacy2/metadata.py
+++ b/src/ota_metadata/legacy2/metadata.py
@@ -54,10 +54,10 @@
     FileTableNonRegularORM,
     FileTableRegularORM,
 )
-from ota_metadata.utils import DownloadInfo
 from ota_metadata.utils.cert_store import CAChainStore
 from otaclient_common._typing import StrOrPath
 from otaclient_common.common import urljoin_ensure_base
+from otaclient_common.download_info import DownloadInfo
 
 from . import DIGEST_ALG, SUPORTED_COMPRESSION_TYPES
 from .csv_parser import (
diff --git a/src/ota_metadata/utils/__init__.py b/src/ota_metadata/utils/__init__.py
index 5fd55ae45..bcfd866ad 100644
--- a/src/ota_metadata/utils/__init__.py
+++ b/src/ota_metadata/utils/__init__.py
@@ -11,19 +11,3 @@
 # 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 dataclasses import dataclass
-from pathlib import Path
-from typing import Optional
-
-
-@dataclass
-class DownloadInfo:
-    url: str
-    dst: Path
-    original_size: int = 0
-    """NOTE: we are using transparent decompression, so we always use the original_size."""
-    digest_alg: Optional[str] = None
-    digest: Optional[str] = None
-    compression_alg: Optional[str] = None
diff --git a/src/otaclient/ota_core.py b/src/otaclient/ota_core.py
index cc6eb500a..be5abe5db 100644
--- a/src/otaclient/ota_core.py
+++ b/src/otaclient/ota_core.py
@@ -43,7 +43,6 @@
     ResourceMeta,
     check_base_filetable,
 )
-from ota_metadata.utils import DownloadInfo
 from ota_metadata.utils.cert_store import (
     CACertStoreInvalid,
     CAChainStore,
@@ -81,6 +80,7 @@
 from otaclient.create_standby.rebuild_mode import RebuildMode
 from otaclient_common import EMPTY_FILE_SHA256, human_readable_size, replace_root
 from otaclient_common.common import ensure_otaproxy_start
+from otaclient_common.download_info import DownloadInfo
 from otaclient_common.downloader import (
     Downloader,
     DownloaderPool,
@@ -160,8 +160,8 @@ def _download_exception_handler(_fut: Future[Any]) -> bool:
         del exc, _fut  # drop ref to exc instance
 
 
-class _OTAUpdater:
-    """The implementation of OTA update logic."""
+class _OTAUpdateOperator:
+    """The base common class of OTA update logic."""
 
     def __init__(
         self,
@@ -171,8 +171,6 @@ def __init__(
         cookies_json: str,
         ca_chains_store: CAChainStore,
         upper_otaproxy: str | None = None,
-        boot_controller: BootControllerProtocol,
-        create_standby_cls: type[RebuildMode],
         ecu_status_flags: MultipleECUStatusFlags,
         status_report_queue: Queue[StatusReport],
         session_id: str,
@@ -202,9 +200,6 @@ def __init__(
         )
 
         # ------ prepare runtime dirs ------ #
-        self._resource_dir_on_standby = Path(cfg.STANDBY_SLOT_MNT) / Path(
-            cfg.OTA_TMP_STORE
-        ).relative_to("/")
         # TODO: use a tmpfs mount with 320MB in size for session workdir
         self._session_workdir = session_wd = (
             Path(cfg.RUN_DIR) / f"update_session-{session_id}"
@@ -229,8 +224,6 @@ def __init__(
 
         # ------ init updater implementation ------ #
         self.ecu_status_flags = ecu_status_flags
-        self._boot_controller = boot_controller
-        self._create_standby_cls = create_standby_cls
 
         # ------ init variables needed for update ------ #
         _url_base = urlparse(raw_url_base)
@@ -260,33 +253,144 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
 
-        Returns:
-            Retry counts, downloaded files size and traffic on wire.
+    def _process_metadata(self) -> None:
+        """Process the metadata.jwt file and report."""
+        self._status_report_queue.put_nowait(
+            StatusReport(
+                payload=OTAUpdatePhaseChangeReport(
+                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
+                    trigger_timestamp=int(time.time()),
+                ),
+                session_id=self.session_id,
+            )
+        )
+
+        try:
+            logger.info("verify and download OTA image metadata ...")
+            self._download_and_parse_metadata()
+            _metadata_jwt = self._ota_metadata.metadata_jwt
+            assert _metadata_jwt, "invalid metadata jwt"
+
+            logger.info(
+                "ota_metadata parsed finished: \n"
+                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
+                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
+            )
+
+            self._status_report_queue.put_nowait(
+                StatusReport(
+                    payload=SetUpdateMetaReport(
+                        image_file_entries=self._ota_metadata.total_regulars_num,
+                        image_size_uncompressed=_metadata_jwt.total_regular_size,
+                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
+                    ),
+                    session_id=self.session_id,
+                )
+            )
+        except ota_errors.OTAError:
+            raise  # raise top-level OTAError as it
+        except ota_metadata_error.MetadataJWTVerificationFailed as e:
+            _err_msg = f"failed to verify metadata.jwt: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTVerficationFailed(
+                _err_msg, module=__name__
+            ) from e
+        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
+            _err_msg = f"metadata.jwt is invalid: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
+        except Exception as e:
+            _err_msg = f"failed to prepare ota metafiles: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
+        finally:
+            self._downloader_pool.release_instance()
+
+    def _download_and_parse_metadata(self) -> None:
+        self._download_and_process_file_with_condition(
+            thread_name_prefix="download_metadata_files",
+            get_downloads_generator=self._ota_metadata.download_metafiles,
+        )
+
+    def _download_and_process_file_with_condition(
+        self,
+        thread_name_prefix: str,
+        get_downloads_generator: Callable,
+    ) -> None:
+        """
+        Download and process a list of files with a condition.
+        Each file downloading and processing are done in parallel by multiple threads.
+        This method is supposed to be used for downloading metadata and client files.
         """
-        if (_digest := entry.digest) == EMPTY_FILE_SHA256:
-            return DownloadResult(0, 0, 0)
 
-        downloader = self._downloader_mapper[threading.get_native_id()]
-        # NOTE: currently download only use sha256
-        return downloader.download(
-            url=entry.url,
-            dst=entry.dst,
-            digest=_digest,
-            size=entry.original_size,
-            compression_alg=entry.compression_alg,
+        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
+        _mapper = ThreadPoolExecutorWithRetry(
+            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
+            max_workers=cfg.DOWNLOAD_THREADS,
+            thread_name_prefix=thread_name_prefix,
+            initializer=self._downloader_workder_initializer,
+            watchdog_func=partial(
+                self._downloader_pool.downloading_watchdog,
+                ctx=self._download_watchdog_ctx,
+                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
+            ),
         )
 
-    def _download_metadata_file(
+        _condition = threading.Condition()
+        _generator = get_downloads_generator(_condition)
+
+        try:
+            for _fut in _mapper.ensure_tasks(
+                partial(self._download_file_with_condition, condition=_condition),
+                _generator,
+            ):
+                if not (_exc := _fut.exception()):
+                    continue
+
+                logger.warning(f"failed to download one file, keep retrying: {_exc!r}")
+                if isinstance(_exc, requests_exc.HTTPError) and isinstance(
+                    (_response := _exc.response), Response
+                ):
+                    if _response.status_code == HTTPStatus.NOT_FOUND:
+                        raise ota_errors.OTAImageInvalid(
+                            "failed to download", module=__name__
+                        ) from _exc
+
+                    if _response.status_code in [
+                        HTTPStatus.FORBIDDEN,
+                        HTTPStatus.UNAUTHORIZED,
+                    ]:
+                        raise ota_errors.UpdateRequestCookieInvalid(
+                            module=__name__
+                        ) from _exc
+        except Exception as e:
+            _generator.throw(e)
+            raise
+        finally:
+            _exc = None  # resolve cycle ref
+            _mapper.shutdown(wait=True)
+            self._downloader_pool.release_all_instances()
+
+    def _download_file_with_condition(
         self, entries: list[DownloadInfo], *, condition: threading.Condition
     ) -> DownloadResult:
-        """Download a single OTA image metadata file.
-
-        Just a wrapper around _download_file method.
+        """Download a single OTA image metadata and client file with a condition.
+        This method is supposed to be used for downloading metadata and client files.
+        Just a wrapper around _download_single_file method.
 
         Returns:
             Retry counts, downloaded files size and traffic on wire.
@@ -294,7 +398,7 @@ def _download_metadata_file(
         _retry_count, _download_size, _traffic_on_wire = 0, 0, 0
         with condition:
             for entry in entries:
-                _res = self._download_file(entry)
+                _res = self._download_single_file(entry)
                 _retry_count += _res.retry_count
                 _download_size += _res.download_size
                 _traffic_on_wire += _res.traffic_on_wire
@@ -302,9 +406,25 @@ def _download_metadata_file(
             condition.notify()  # notify the metadata generator that this batch of download is finished
         return DownloadResult(_retry_count, _download_size, _traffic_on_wire)
 
-    def _downloader_workder_initializer(self) -> None:
-        self._downloader_mapper[threading.get_native_id()] = (
-            self._downloader_pool.get_instance()
+    def _download_single_file(self, entry: DownloadInfo) -> DownloadResult:
+        """Download a single file.
+        This method is supposed to be used for any file download.
+        This is the single task being executed in the downloader pool.
+
+        Returns:
+            Retry counts, downloaded files size and traffic on wire.
+        """
+        if (_digest := entry.digest) == EMPTY_FILE_SHA256:
+            return DownloadResult(0, 0, 0)
+
+        downloader = self._downloader_mapper[threading.get_native_id()]
+        # NOTE: currently download only use sha256
+        return downloader.download(
+            url=entry.url,
+            dst=entry.dst,
+            digest=_digest,
+            size=entry.original_size,
+            compression_alg=entry.compression_alg,
         )
 
     def _download_resources(self, resource_meta: ResourceMeta) -> None:
@@ -330,7 +450,7 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             )
             for _done_count, _fut in enumerate(
                 _mapper.ensure_tasks(
-                    self._download_file,
+                    self._download_single_file,
                     resource_meta.iter_resources(
                         batch_size=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS
                     ),
@@ -379,161 +499,48 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             self._downloader_pool.release_all_instances()
             self._downloader_pool.shutdown()
 
-    def _process_persistents(self, ota_metadata: OTAMetadata):
-        logger.info("start persist files handling...")
-        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
-
-        _handler = PersistFilesHandler(
-            src_passwd_file=Path(cfg.PASSWD_FPATH),
-            src_group_file=Path(cfg.GROUP_FPATH),
-            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
-            dst_group_file=Path(standby_slot_mp / "etc/group"),
-            src_root=cfg.ACTIVE_SLOT_MNT,
-            dst_root=cfg.STANDBY_SLOT_MNT,
+    def _downloader_workder_initializer(self) -> None:
+        self._downloader_mapper[threading.get_native_id()] = (
+            self._downloader_pool.get_instance()
         )
 
-        for persiste_entry in ota_metadata.iter_persist_entries():
-            # NOTE(20240520): with update_swapfile ansible role being used wildly,
-            #   now we just ignore the swapfile entries in the persistents.txt if any,
-            #   and issue a warning about it.
-            if persiste_entry in ["/swapfile", "/swap.img"]:
-                logger.warning(
-                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
-                )
-                logger.warning(
-                    (
-                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
-                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
-                    )
-                )
-                continue
-
-            try:
-                _handler.preserve_persist_entry(persiste_entry)
-            except Exception as e:
-                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
-                logger.warning(_err_msg)
-
-    def _download_and_parse_metadata(self) -> None:
-        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
-        _mapper = ThreadPoolExecutorWithRetry(
-            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
-            max_workers=cfg.DOWNLOAD_THREADS,
-            thread_name_prefix="download_metadata_files",
-            initializer=self._downloader_workder_initializer,
-            watchdog_func=partial(
-                self._downloader_pool.downloading_watchdog,
-                ctx=self._download_watchdog_ctx,
-                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
-            ),
-        )
 
-        _condition = threading.Condition()
-        _metadata_processor = self._ota_metadata.download_metafiles(_condition)
+class _OTAUpdater(_OTAUpdateOperator):
+    """The implementation of OTA update logic."""
 
-        try:
-            for _fut in _mapper.ensure_tasks(
-                partial(self._download_metadata_file, condition=_condition),
-                _metadata_processor,
-            ):
-                if not (_exc := _fut.exception()):
-                    continue
+    def __init__(
+        self,
+        boot_controller: BootControllerProtocol,
+        create_standby_cls: type[RebuildMode],
+        **kwargs,
+    ) -> None:
+        # ------ init base class ------ #
+        super().__init__(**kwargs)
 
-                logger.warning(
-                    f"failed to download one metafile, keep retrying: {_exc!r}"
-                )
-                if isinstance(_exc, requests_exc.HTTPError) and isinstance(
-                    (_response := _exc.response), Response
-                ):
-                    if _response.status_code == HTTPStatus.NOT_FOUND:
-                        raise ota_errors.OTAImageInvalid(
-                            "failed to download metadata", module=__name__
-                        ) from _exc
+        # ------ prepare runtime dirs ------ #
+        self._resource_dir_on_standby = Path(cfg.STANDBY_SLOT_MNT) / Path(
+            cfg.OTA_TMP_STORE
+        ).relative_to("/")
 
-                    if _response.status_code in [
-                        HTTPStatus.FORBIDDEN,
-                        HTTPStatus.UNAUTHORIZED,
-                    ]:
-                        raise ota_errors.UpdateRequestCookieInvalid(
-                            module=__name__
-                        ) from _exc
-        except Exception as e:
-            _metadata_processor.throw(e)
-            raise
-        finally:
-            _exc = None  # resolve cycle ref
-            _mapper.shutdown(wait=True)
-            self._downloader_pool.release_all_instances()
+        # ------ init updater implementation ------ #
+        self._boot_controller = boot_controller
+        self._create_standby_cls = create_standby_cls
 
     def _execute_update(self):
         """Implementation of OTA updating."""
         logger.info(f"execute local update({ecu_info.ecu_id=}): {self.update_version=}")
 
-        if _upper_proxy := self._upper_proxy:
-            logger.info(
-                f"use {_upper_proxy} for local OTA update, "
-                f"wait for otaproxy@{_upper_proxy} online..."
-            )
-
-            # NOTE: will raise a built-in ConnnectionError at timeout
-            ensure_otaproxy_start(
-                _upper_proxy,
-                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
-            )
-
-        # ------ init, processing metadata ------ #
-        self._status_report_queue.put_nowait(
-            StatusReport(
-                payload=OTAUpdatePhaseChangeReport(
-                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
-                    trigger_timestamp=int(time.time()),
-                ),
-                session_id=self.session_id,
-            )
-        )
-
-        try:
-            logger.info("verify and download OTA image metadata ...")
-            self._download_and_parse_metadata()
-            _metadata_jwt = self._ota_metadata.metadata_jwt
-            assert _metadata_jwt, "invalid metadata jwt"
-
-            logger.info(
-                "ota_metadata parsed finished: \n"
-                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
-                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
-            )
-
-            self._status_report_queue.put_nowait(
-                StatusReport(
-                    payload=SetUpdateMetaReport(
-                        image_file_entries=self._ota_metadata.total_regulars_num,
-                        image_size_uncompressed=_metadata_jwt.total_regular_size,
-                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
-                    ),
-                    session_id=self.session_id,
-                )
-            )
-        except ota_errors.OTAError:
-            raise  # raise top-level OTAError as it
-        except ota_metadata_error.MetadataJWTVerificationFailed as e:
-            _err_msg = f"failed to verify metadata.jwt: {e!r}"
-            logger.error(_err_msg)
-            raise ota_errors.MetadataJWTVerficationFailed(
-                _err_msg, module=__name__
-            ) from e
-        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
-            _err_msg = f"metadata.jwt is invalid: {e!r}"
-            logger.error(_err_msg)
-            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
-        except Exception as e:
-            _err_msg = f"failed to prepare ota metafiles: {e!r}"
-            logger.error(_err_msg)
-            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
-        finally:
-            self._downloader_pool.release_instance()
-
-        # ------ pre-update ------ #
+        self._handle_upper_proxy()
+        self._process_metadata()
+        self._prepare_standby_slot()
+        self._calculate_delta()
+        self._download_delta_resources()
+        self._apply_update()
+        self._post_update()
+        self._finalize_update()
+
+    def _prepare_standby_slot(self):
+        """Prepare the standby slot and optimize the file_table."""
         logger.info("enter local OTA update...")
         self._boot_controller.pre_update(
             self.update_version,
@@ -559,16 +566,9 @@ def _execute_update(self):
         logger.info("prepare and optimize file_table ...")
         self._ota_metadata.prepare_fstable()
 
-        # ------ in-update: calculate delta ------ #
+    def _calculate_delta(self):
+        """Calculate the delta bundle."""
         logger.info("start to calculate delta ...")
-        # NOTE(20250205): for current rebuild-mode, we look at active slot's file_table.
-        # NOTE: get the db via active_slot mount_point
-        _base_ft_db = replace_root(
-            Path(cfg.IMAGE_META_DPATH) / OTAMetadata.FSTABLE_DB,
-            old_root="/",
-            new_root=Path(cfg.ACTIVE_SLOT_MNT),
-        )
-
         self._status_report_queue.put_nowait(
             StatusReport(
                 payload=OTAUpdatePhaseChangeReport(
@@ -579,6 +579,14 @@ def _execute_update(self):
             )
         )
 
+        # NOTE(20250205): for current rebuild-mode, we look at active slot's file_table.
+        # NOTE: get the db via active_slot mount_point
+        _base_ft_db = replace_root(
+            Path(cfg.IMAGE_META_DPATH) / OTAMetadata.FSTABLE_DB,
+            old_root="/",
+            new_root=Path(cfg.ACTIVE_SLOT_MNT),
+        )
+
         try:
             if _verified_db := check_base_filetable(_base_ft_db):
                 logger.info(
@@ -611,6 +619,8 @@ def _execute_update(self):
                 _err_msg, module=__name__
             ) from e
 
+    def _download_delta_resources(self) -> None:
+        """Download all the resources needed for the OTA update."""
         # ------ in-update: download resources ------ #
         self._status_report_queue.put_nowait(
             StatusReport(
@@ -656,7 +666,8 @@ def _execute_update(self):
             # NOTE: after this point, we don't need downloader anymore
             self._downloader_pool.shutdown()
 
-        # ------ apply update ------ #
+    def _apply_update(self) -> None:
+        """Apply the OTA update to the standby slot."""
         logger.info("start to apply changes to standby slot...")
         self._status_report_queue.put_nowait(
             StatusReport(
@@ -676,7 +687,8 @@ def _execute_update(self):
         )
         standby_slot_creator.rebuild_standby()
 
-        # ------ post-update ------ #
+    def _post_update(self) -> None:
+        """Post-update phase."""
         logger.info("enter post update phase...")
         self._status_report_queue.put_nowait(
             StatusReport(
@@ -691,7 +703,43 @@ def _execute_update(self):
         self._process_persistents(self._ota_metadata)
         self._boot_controller.post_update()
 
-        # ------ finalizing update ------ #
+    def _process_persistents(self, ota_metadata: OTAMetadata):
+        logger.info("start persist files handling...")
+        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
+
+        _handler = PersistFilesHandler(
+            src_passwd_file=Path(cfg.PASSWD_FPATH),
+            src_group_file=Path(cfg.GROUP_FPATH),
+            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
+            dst_group_file=Path(standby_slot_mp / "etc/group"),
+            src_root=cfg.ACTIVE_SLOT_MNT,
+            dst_root=cfg.STANDBY_SLOT_MNT,
+        )
+
+        for persiste_entry in ota_metadata.iter_persist_entries():
+            # NOTE(20240520): with update_swapfile ansible role being used wildly,
+            #   now we just ignore the swapfile entries in the persistents.txt if any,
+            #   and issue a warning about it.
+            if persiste_entry in ["/swapfile", "/swap.img"]:
+                logger.warning(
+                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
+                )
+                logger.warning(
+                    (
+                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
+                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
+                    )
+                )
+                continue
+
+            try:
+                _handler.preserve_persist_entry(persiste_entry)
+            except Exception as e:
+                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
+                logger.warning(_err_msg)
+
+    def _finalize_update(self) -> None:
+        """Finalize the OTA update."""
         logger.info("local update finished, wait on all subecs...")
         self._status_report_queue.put_nowait(
             StatusReport(
diff --git a/src/otaclient_common/download_info.py b/src/otaclient_common/download_info.py
new file mode 100644
index 000000000..5fd55ae45
--- /dev/null
+++ b/src/otaclient_common/download_info.py
@@ -0,0 +1,29 @@
+# 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 dataclasses import dataclass
+from pathlib import Path
+from typing import Optional
+
+
+@dataclass
+class DownloadInfo:
+    url: str
+    dst: Path
+    original_size: int = 0
+    """NOTE: we are using transparent decompression, so we always use the original_size."""
+    digest_alg: Optional[str] = None
+    digest: Optional[str] = None
+    compression_alg: Optional[str] = None
diff --git a/tests/test_ota_metadata/test_legacy2/test_e2e.py b/tests/test_ota_metadata/test_legacy2/test_e2e.py
index e7e65146a..d287a8ddf 100644
--- a/tests/test_ota_metadata/test_legacy2/test_e2e.py
+++ b/tests/test_ota_metadata/test_legacy2/test_e2e.py
@@ -25,8 +25,8 @@
 import pytest
 
 from ota_metadata.legacy2.metadata import MetadataJWTParser, OTAMetadata
-from ota_metadata.utils import DownloadInfo
 from ota_metadata.utils.cert_store import load_ca_cert_chains
+from otaclient_common.download_info import DownloadInfo
 from otaclient_common.downloader import DownloaderPool
 from otaclient_common.retry_task_map import ThreadPoolExecutorWithRetry
 from tests.conftest import CERTS_DIR, OTA_IMAGE_DIR, OTA_IMAGE_SIGN_CERT, OTA_IMAGE_URL


Reviews:
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T00:29:24Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T00:31:01Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T00:32:47Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:04:51Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:05:59Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:07:00Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:08:06Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:08:52Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:09:21Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:39:13Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:40:03Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T01:42:17Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-05T07:57:13Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T02:35:47Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T02:36:58Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T02:37:15Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T02:37:33Z
user: login: airkei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-06T02:38:09Z
user: login: Bodong-Yang
body: LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T00:16:31Z

Review comments:
diff_hunk: @@ -160,8 +160,8 @@ def _download_exception_handler(_fut: Future[Any]) -> bool:
         del exc, _fut  # drop ref to exc instance
 
 
-class _OTAUpdater:
-    """The implementation of OTA update logic."""
+class _OTAUpdateOperator:
+    """The base common class of OTA update logic."""
path: src/otaclient/ota_core.py
user: login: airkei
body: This is common(base) class for both client and data updates.
Currently this class only includes proxy, metadata downloading features.
created_at: 2025-03-05T00:29:24Z
author_association: COLLABORATOR
diff_hunk: @@ -260,28 +253,134 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
path: src/otaclient/ota_core.py
user: login: airkei
body: these modularized functions doesn't change its behavior at all(= just copied code).
created_at: 2025-03-05T00:31:01Z
author_association: COLLABORATOR
diff_hunk: @@ -260,28 +253,134 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
 
-        Returns:
-            Retry counts, downloaded files size and traffic on wire.
-        """
-        if (_digest := entry.digest) == EMPTY_FILE_SHA256:
-            return DownloadResult(0, 0, 0)
+    def _process_metadata(self) -> None:
+        """Process the metadata.jwt file and report."""
+        self._status_report_queue.put_nowait(
+            StatusReport(
+                payload=OTAUpdatePhaseChangeReport(
+                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
+                    trigger_timestamp=int(time.time()),
+                ),
+                session_id=self.session_id,
+            )
+        )
 
-        downloader = self._downloader_mapper[threading.get_native_id()]
-        # NOTE: currently download only use sha256
-        return downloader.download(
-            url=entry.url,
-            dst=entry.dst,
-            digest=_digest,
-            size=entry.original_size,
-            compression_alg=entry.compression_alg,
+        try:
+            logger.info("verify and download OTA image metadata ...")
+            self._download_and_parse_metadata()
+            _metadata_jwt = self._ota_metadata.metadata_jwt
+            assert _metadata_jwt, "invalid metadata jwt"
+
+            logger.info(
+                "ota_metadata parsed finished: \n"
+                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
+                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
+            )
+
+            self._status_report_queue.put_nowait(
+                StatusReport(
+                    payload=SetUpdateMetaReport(
+                        image_file_entries=self._ota_metadata.total_regulars_num,
+                        image_size_uncompressed=_metadata_jwt.total_regular_size,
+                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
+                    ),
+                    session_id=self.session_id,
+                )
+            )
+        except ota_errors.OTAError:
+            raise  # raise top-level OTAError as it
+        except ota_metadata_error.MetadataJWTVerificationFailed as e:
+            _err_msg = f"failed to verify metadata.jwt: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTVerficationFailed(
+                _err_msg, module=__name__
+            ) from e
+        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
+            _err_msg = f"metadata.jwt is invalid: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
+        except Exception as e:
+            _err_msg = f"failed to prepare ota metafiles: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
+        finally:
+            self._downloader_pool.release_instance()
+
+    def _download_and_parse_metadata(self) -> None:
+        self._download_and_process_file(
+            thread_name_prefix="download_metadata_files",
+            get_downloads_generator=self._ota_metadata.download_metafiles,
+        )
+
+    def _download_and_process_file(
+        self,
+        thread_name_prefix: str,
+        get_downloads_generator: Callable,
+    ) -> None:
path: src/otaclient/ota_core.py
user: login: airkei
body: To reuse this download function in client updates, generalized this function(add `get_downloads_generator` parameter). This doesn't change the existing behavior, just code organization.
created_at: 2025-03-05T00:32:47Z
author_association: COLLABORATOR
diff_hunk: @@ -202,9 +202,6 @@ def __init__(
         )
 
         # ------ prepare runtime dirs ------ #
-        self._resource_dir_on_standby = Path(cfg.STANDBY_SLOT_MNT) / Path(
-            cfg.OTA_TMP_STORE
-        ).relative_to("/")
path: src/otaclient/ota_core.py
user: login: airkei
body: will be initialized in the inherited class(_OTAUpdater).
created_at: 2025-03-05T01:04:51Z
author_association: COLLABORATOR
diff_hunk: @@ -379,161 +496,52 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             self._downloader_pool.release_all_instances()
             self._downloader_pool.shutdown()
 
-    def _process_persistents(self, ota_metadata: OTAMetadata):
-        logger.info("start persist files handling...")
-        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
-
-        _handler = PersistFilesHandler(
-            src_passwd_file=Path(cfg.PASSWD_FPATH),
-            src_group_file=Path(cfg.GROUP_FPATH),
-            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
-            dst_group_file=Path(standby_slot_mp / "etc/group"),
-            src_root=cfg.ACTIVE_SLOT_MNT,
-            dst_root=cfg.STANDBY_SLOT_MNT,
+    def _downloader_workder_initializer(self) -> None:
+        self._downloader_mapper[threading.get_native_id()] = (
+            self._downloader_pool.get_instance()
         )
 
-        for persiste_entry in ota_metadata.iter_persist_entries():
-            # NOTE(20240520): with update_swapfile ansible role being used wildly,
-            #   now we just ignore the swapfile entries in the persistents.txt if any,
-            #   and issue a warning about it.
-            if persiste_entry in ["/swapfile", "/swap.img"]:
-                logger.warning(
-                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
-                )
-                logger.warning(
-                    (
-                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
-                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
-                    )
-                )
-                continue
-
-            try:
-                _handler.preserve_persist_entry(persiste_entry)
-            except Exception as e:
-                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
-                logger.warning(_err_msg)
 
-    def _download_and_parse_metadata(self) -> None:
-        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
-        _mapper = ThreadPoolExecutorWithRetry(
-            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
-            max_workers=cfg.DOWNLOAD_THREADS,
-            thread_name_prefix="download_metadata_files",
-            initializer=self._downloader_workder_initializer,
-            watchdog_func=partial(
-                self._downloader_pool.downloading_watchdog,
-                ctx=self._download_watchdog_ctx,
-                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
-            ),
-        )
+class _OTAUpdater(_OTAUpdateOperator):
path: src/otaclient/ota_core.py
user: login: airkei
body: The inherited class of the existing data update.
created_at: 2025-03-05T01:05:59Z
author_association: COLLABORATOR
diff_hunk: @@ -379,161 +496,52 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             self._downloader_pool.release_all_instances()
             self._downloader_pool.shutdown()
 
-    def _process_persistents(self, ota_metadata: OTAMetadata):
-        logger.info("start persist files handling...")
-        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
-
-        _handler = PersistFilesHandler(
-            src_passwd_file=Path(cfg.PASSWD_FPATH),
-            src_group_file=Path(cfg.GROUP_FPATH),
-            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
-            dst_group_file=Path(standby_slot_mp / "etc/group"),
-            src_root=cfg.ACTIVE_SLOT_MNT,
-            dst_root=cfg.STANDBY_SLOT_MNT,
+    def _downloader_workder_initializer(self) -> None:
+        self._downloader_mapper[threading.get_native_id()] = (
+            self._downloader_pool.get_instance()
         )
 
-        for persiste_entry in ota_metadata.iter_persist_entries():
-            # NOTE(20240520): with update_swapfile ansible role being used wildly,
-            #   now we just ignore the swapfile entries in the persistents.txt if any,
-            #   and issue a warning about it.
-            if persiste_entry in ["/swapfile", "/swap.img"]:
-                logger.warning(
-                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
-                )
-                logger.warning(
-                    (
-                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
-                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
-                    )
-                )
-                continue
-
-            try:
-                _handler.preserve_persist_entry(persiste_entry)
-            except Exception as e:
-                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
-                logger.warning(_err_msg)
 
-    def _download_and_parse_metadata(self) -> None:
-        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
-        _mapper = ThreadPoolExecutorWithRetry(
-            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
-            max_workers=cfg.DOWNLOAD_THREADS,
-            thread_name_prefix="download_metadata_files",
-            initializer=self._downloader_workder_initializer,
-            watchdog_func=partial(
-                self._downloader_pool.downloading_watchdog,
-                ctx=self._download_watchdog_ctx,
-                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
-            ),
-        )
+class _OTAUpdater(_OTAUpdateOperator):
+    """The implementation of OTA update logic."""
 
-        _condition = threading.Condition()
-        _metadata_processor = self._ota_metadata.download_metafiles(_condition)
+    def __init__(self, **kwargs) -> None:
+        # ------ init base class ------ #
+        super().__init__(**kwargs)
 
-        try:
-            for _fut in _mapper.ensure_tasks(
-                partial(self._download_metadata_file, condition=_condition),
-                _metadata_processor,
-            ):
-                if not (_exc := _fut.exception()):
-                    continue
+        # ------ prepare runtime dirs ------ #
+        self._resource_dir_on_standby = Path(cfg.STANDBY_SLOT_MNT) / Path(
+            cfg.OTA_TMP_STORE
+        ).relative_to("/")
 
-                logger.warning(
-                    f"failed to download one metafile, keep retrying: {_exc!r}"
-                )
-                if isinstance(_exc, requests_exc.HTTPError) and isinstance(
-                    (_response := _exc.response), Response
-                ):
-                    if _response.status_code == HTTPStatus.NOT_FOUND:
-                        raise ota_errors.OTAImageInvalid(
-                            "failed to download metadata", module=__name__
-                        ) from _exc
+        # ------ init updater implementation ------ #
+        self._boot_controller = kwargs.get("boot_controller")
+        if self._boot_controller is None:
+            raise ValueError("boot_controller is required")
 
-                    if _response.status_code in [
-                        HTTPStatus.FORBIDDEN,
-                        HTTPStatus.UNAUTHORIZED,
-                    ]:
-                        raise ota_errors.UpdateRequestCookieInvalid(
-                            module=__name__
-                        ) from _exc
-        except Exception as e:
-            _metadata_processor.throw(e)
-            raise
-        finally:
-            _exc = None  # resolve cycle ref
-            _mapper.shutdown(wait=True)
-            self._downloader_pool.release_all_instances()
+        self._create_standby_cls = kwargs.get("create_standby_cls")
+        if self._create_standby_cls is None:
+            raise ValueError("create_standby_cls is required")
 
     def _execute_update(self):
         """Implementation of OTA updating."""
         logger.info(f"execute local update({ecu_info.ecu_id=}): {self.update_version=}")
 
-        if _upper_proxy := self._upper_proxy:
-            logger.info(
-                f"use {_upper_proxy} for local OTA update, "
-                f"wait for otaproxy@{_upper_proxy} online..."
-            )
-
-            # NOTE: will raise a built-in ConnnectionError at timeout
-            ensure_otaproxy_start(
-                _upper_proxy,
-                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
-            )
-
-        # ------ init, processing metadata ------ #
-        self._status_report_queue.put_nowait(
-            StatusReport(
-                payload=OTAUpdatePhaseChangeReport(
-                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
-                    trigger_timestamp=int(time.time()),
-                ),
-                session_id=self.session_id,
-            )
-        )
-
-        try:
-            logger.info("verify and download OTA image metadata ...")
-            self._download_and_parse_metadata()
-            _metadata_jwt = self._ota_metadata.metadata_jwt
-            assert _metadata_jwt, "invalid metadata jwt"
-
-            logger.info(
-                "ota_metadata parsed finished: \n"
-                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
-                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
-            )
+        self._handle_upper_proxy()
+        self._process_metadata()
+        self._prepare_standby_slot()
+        self._calculate_delta()
+        self._download_delta_resources()
+        self._apply_update()
+        self._post_update()
+        self._finalize_update()
path: src/otaclient/ota_core.py
user: login: airkei
body: clear and modularized sequence :)
Hopefully, better to have clear input/output of each function. This will be next refactoring step.
created_at: 2025-03-05T01:07:00Z
author_association: COLLABORATOR
diff_hunk: @@ -579,6 +580,14 @@ def _execute_update(self):
             )
         )
 
+        # NOTE(20250205): for current rebuild-mode, we look at active slot's file_table.
+        # NOTE: get the db via active_slot mount_point
+        _base_ft_db = replace_root(
+            Path(cfg.IMAGE_META_DPATH) / OTAMetadata.FSTABLE_DB,
+            old_root="/",
+            new_root=Path(cfg.ACTIVE_SLOT_MNT),
+        )
+
path: src/otaclient/ota_core.py
user: login: airkei
body: change the order in this function(report the status first, then execute the logic).
created_at: 2025-03-05T01:08:06Z
author_association: COLLABORATOR
diff_hunk: @@ -691,7 +704,43 @@ def _execute_update(self):
         self._process_persistents(self._ota_metadata)
         self._boot_controller.post_update()
 
-        # ------ finalizing update ------ #
+    def _process_persistents(self, ota_metadata: OTAMetadata):
+        logger.info("start persist files handling...")
+        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
+
+        _handler = PersistFilesHandler(
+            src_passwd_file=Path(cfg.PASSWD_FPATH),
+            src_group_file=Path(cfg.GROUP_FPATH),
+            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
+            dst_group_file=Path(standby_slot_mp / "etc/group"),
+            src_root=cfg.ACTIVE_SLOT_MNT,
+            dst_root=cfg.STANDBY_SLOT_MNT,
+        )
+
+        for persiste_entry in ota_metadata.iter_persist_entries():
+            # NOTE(20240520): with update_swapfile ansible role being used wildly,
+            #   now we just ignore the swapfile entries in the persistents.txt if any,
+            #   and issue a warning about it.
+            if persiste_entry in ["/swapfile", "/swap.img"]:
+                logger.warning(
+                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
+                )
+                logger.warning(
+                    (
+                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
+                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
+                    )
+                )
+                continue
+
+            try:
+                _handler.preserve_persist_entry(persiste_entry)
+            except Exception as e:
+                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
+                logger.warning(_err_msg)
path: src/otaclient/ota_core.py
user: login: airkei
body: just moved the function in this file.
created_at: 2025-03-05T01:08:52Z
author_association: COLLABORATOR
diff_hunk: @@ -57,6 +60,19 @@
 DEFAULT_READ_TIMEOUT = 32  # seconds
 
 
+@dataclass
+class DownloadInfo:
+    """Download information for Downloader."""
+
+    url: str
+    dst: Path
+    original_size: int = 0
+    """NOTE: we are using transparent decompression, so we always use the original_size."""
+    digest_alg: Optional[str] = None
+    digest: Optional[str] = None
+    compression_alg: Optional[str] = None
path: src/otaclient_common/downloader.py
user: login: airkei
body: moved from `ota_metadata/utils/__init__.py`.
created_at: 2025-03-05T01:09:21Z
author_association: COLLABORATOR
diff_hunk: @@ -379,161 +494,52 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             self._downloader_pool.release_all_instances()
             self._downloader_pool.shutdown()
 
-    def _process_persistents(self, ota_metadata: OTAMetadata):
-        logger.info("start persist files handling...")
-        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
-
-        _handler = PersistFilesHandler(
-            src_passwd_file=Path(cfg.PASSWD_FPATH),
-            src_group_file=Path(cfg.GROUP_FPATH),
-            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
-            dst_group_file=Path(standby_slot_mp / "etc/group"),
-            src_root=cfg.ACTIVE_SLOT_MNT,
-            dst_root=cfg.STANDBY_SLOT_MNT,
+    def _downloader_workder_initializer(self) -> None:
+        self._downloader_mapper[threading.get_native_id()] = (
+            self._downloader_pool.get_instance()
         )
 
-        for persiste_entry in ota_metadata.iter_persist_entries():
-            # NOTE(20240520): with update_swapfile ansible role being used wildly,
-            #   now we just ignore the swapfile entries in the persistents.txt if any,
-            #   and issue a warning about it.
-            if persiste_entry in ["/swapfile", "/swap.img"]:
-                logger.warning(
-                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
-                )
-                logger.warning(
-                    (
-                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
-                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
-                    )
-                )
-                continue
 
-            try:
-                _handler.preserve_persist_entry(persiste_entry)
-            except Exception as e:
-                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
-                logger.warning(_err_msg)
-
-    def _download_and_parse_metadata(self) -> None:
-        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
-        _mapper = ThreadPoolExecutorWithRetry(
-            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
-            max_workers=cfg.DOWNLOAD_THREADS,
-            thread_name_prefix="download_metadata_files",
-            initializer=self._downloader_workder_initializer,
-            watchdog_func=partial(
-                self._downloader_pool.downloading_watchdog,
-                ctx=self._download_watchdog_ctx,
-                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
-            ),
-        )
-
-        _condition = threading.Condition()
-        _metadata_processor = self._ota_metadata.download_metafiles(_condition)
+class _OTAUpdater(_OTAUpdateOperator):
+    """The implementation of OTA update logic."""
 
-        try:
-            for _fut in _mapper.ensure_tasks(
-                partial(self._download_metadata_file, condition=_condition),
-                _metadata_processor,
-            ):
-                if not (_exc := _fut.exception()):
-                    continue
+    def __init__(
+        self,
+        boot_controller: BootControllerProtocol,
+        create_standby_cls: type[RebuildMode],
path: src/otaclient/ota_core.py
user: login: airkei
body: These variables are necessary only for data updating, and essential for data updating.
created_at: 2025-03-05T01:39:13Z
author_association: COLLABORATOR
diff_hunk: @@ -171,8 +171,6 @@ def __init__(
         cookies_json: str,
         ca_chains_store: CAChainStore,
         upper_otaproxy: str | None = None,
-        boot_controller: BootControllerProtocol,
-        create_standby_cls: type[RebuildMode],
path: src/otaclient/ota_core.py
user: login: airkei
body: These variables are not necessary in base class.
created_at: 2025-03-05T01:40:02Z
author_association: COLLABORATOR
diff_hunk: @@ -379,161 +494,52 @@ def _download_resources(self, resource_meta: ResourceMeta) -> None:
             self._downloader_pool.release_all_instances()
             self._downloader_pool.shutdown()
 
-    def _process_persistents(self, ota_metadata: OTAMetadata):
-        logger.info("start persist files handling...")
-        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
-
-        _handler = PersistFilesHandler(
-            src_passwd_file=Path(cfg.PASSWD_FPATH),
-            src_group_file=Path(cfg.GROUP_FPATH),
-            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
-            dst_group_file=Path(standby_slot_mp / "etc/group"),
-            src_root=cfg.ACTIVE_SLOT_MNT,
-            dst_root=cfg.STANDBY_SLOT_MNT,
+    def _downloader_workder_initializer(self) -> None:
+        self._downloader_mapper[threading.get_native_id()] = (
+            self._downloader_pool.get_instance()
         )
 
-        for persiste_entry in ota_metadata.iter_persist_entries():
-            # NOTE(20240520): with update_swapfile ansible role being used wildly,
-            #   now we just ignore the swapfile entries in the persistents.txt if any,
-            #   and issue a warning about it.
-            if persiste_entry in ["/swapfile", "/swap.img"]:
-                logger.warning(
-                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
-                )
-                logger.warning(
-                    (
-                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
-                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
-                    )
-                )
-                continue
 
-            try:
-                _handler.preserve_persist_entry(persiste_entry)
-            except Exception as e:
-                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
-                logger.warning(_err_msg)
-
-    def _download_and_parse_metadata(self) -> None:
-        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
-        _mapper = ThreadPoolExecutorWithRetry(
-            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
-            max_workers=cfg.DOWNLOAD_THREADS,
-            thread_name_prefix="download_metadata_files",
-            initializer=self._downloader_workder_initializer,
-            watchdog_func=partial(
-                self._downloader_pool.downloading_watchdog,
-                ctx=self._download_watchdog_ctx,
-                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
-            ),
-        )
-
-        _condition = threading.Condition()
-        _metadata_processor = self._ota_metadata.download_metafiles(_condition)
+class _OTAUpdater(_OTAUpdateOperator):
+    """The implementation of OTA update logic."""
 
-        try:
-            for _fut in _mapper.ensure_tasks(
-                partial(self._download_metadata_file, condition=_condition),
-                _metadata_processor,
-            ):
-                if not (_exc := _fut.exception()):
-                    continue
+    def __init__(
+        self,
+        boot_controller: BootControllerProtocol,
+        create_standby_cls: type[RebuildMode],
+        **kwargs,
+    ) -> None:
+        # ------ init base class ------ #
+        super().__init__(**kwargs)
path: src/otaclient/ota_core.py
user: login: airkei
body: call the init of base class.
created_at: 2025-03-05T01:42:17Z
author_association: COLLABORATOR
diff_hunk: @@ -11,19 +11,3 @@
 # 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 dataclasses import dataclass
-from pathlib import Path
-from typing import Optional
-
-
-@dataclass
-class DownloadInfo:
-    url: str
-    dst: Path
-    original_size: int = 0
-    """NOTE: we are using transparent decompression, so we always use the original_size."""
-    digest_alg: Optional[str] = None
-    digest: Optional[str] = None
-    compression_alg: Optional[str] = None
path: src/ota_metadata/utils/__init__.py
user: login: airkei
body: This format is reusable not only for metadata but also client download. Moved to `otaclient_common/downloader.py`.
created_at: 2025-03-05T07:57:13Z
author_association: COLLABORATOR
diff_hunk: @@ -11,19 +11,3 @@
 # 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 dataclasses import dataclass
-from pathlib import Path
-from typing import Optional
-
-
-@dataclass
-class DownloadInfo:
-    url: str
-    dst: Path
-    original_size: int = 0
-    """NOTE: we are using transparent decompression, so we always use the original_size."""
-    digest_alg: Optional[str] = None
-    digest: Optional[str] = None
-    compression_alg: Optional[str] = None
path: src/ota_metadata/utils/__init__.py
user: login: airkei
body: Moved to `otaclient_common/download_info.py`.
created_at: 2025-03-06T02:35:46Z
author_association: COLLABORATOR
diff_hunk: @@ -260,51 +253,178 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
 
-        Returns:
-            Retry counts, downloaded files size and traffic on wire.
+    def _process_metadata(self) -> None:
+        """Process the metadata.jwt file and report."""
+        self._status_report_queue.put_nowait(
+            StatusReport(
+                payload=OTAUpdatePhaseChangeReport(
+                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
+                    trigger_timestamp=int(time.time()),
+                ),
+                session_id=self.session_id,
+            )
+        )
+
+        try:
+            logger.info("verify and download OTA image metadata ...")
+            self._download_and_parse_metadata()
+            _metadata_jwt = self._ota_metadata.metadata_jwt
+            assert _metadata_jwt, "invalid metadata jwt"
+
+            logger.info(
+                "ota_metadata parsed finished: \n"
+                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
+                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
+            )
+
+            self._status_report_queue.put_nowait(
+                StatusReport(
+                    payload=SetUpdateMetaReport(
+                        image_file_entries=self._ota_metadata.total_regulars_num,
+                        image_size_uncompressed=_metadata_jwt.total_regular_size,
+                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
+                    ),
+                    session_id=self.session_id,
+                )
+            )
+        except ota_errors.OTAError:
+            raise  # raise top-level OTAError as it
+        except ota_metadata_error.MetadataJWTVerificationFailed as e:
+            _err_msg = f"failed to verify metadata.jwt: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTVerficationFailed(
+                _err_msg, module=__name__
+            ) from e
+        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
+            _err_msg = f"metadata.jwt is invalid: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
+        except Exception as e:
+            _err_msg = f"failed to prepare ota metafiles: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
+        finally:
+            self._downloader_pool.release_instance()
+
+    def _download_and_parse_metadata(self) -> None:
+        self._download_and_process_file_with_condition(
+            thread_name_prefix="download_metadata_files",
+            get_downloads_generator=self._ota_metadata.download_metafiles,
+        )
+
+    def _download_and_process_file_with_condition(
+        self,
+        thread_name_prefix: str,
+        get_downloads_generator: Callable,
+    ) -> None:
+        """
+        Download and process a list of files with a condition.
+        Each file downloading and processing are done in parallel by multiple threads.
+        This method is supposed to be used for downloading metadata and client files.
path: src/otaclient/ota_core.py
user: login: airkei
body: renamed method name and added doc string.
created_at: 2025-03-06T02:36:58Z
author_association: COLLABORATOR
diff_hunk: @@ -260,51 +253,178 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
 
-        Returns:
-            Retry counts, downloaded files size and traffic on wire.
+    def _process_metadata(self) -> None:
+        """Process the metadata.jwt file and report."""
+        self._status_report_queue.put_nowait(
+            StatusReport(
+                payload=OTAUpdatePhaseChangeReport(
+                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
+                    trigger_timestamp=int(time.time()),
+                ),
+                session_id=self.session_id,
+            )
+        )
+
+        try:
+            logger.info("verify and download OTA image metadata ...")
+            self._download_and_parse_metadata()
+            _metadata_jwt = self._ota_metadata.metadata_jwt
+            assert _metadata_jwt, "invalid metadata jwt"
+
+            logger.info(
+                "ota_metadata parsed finished: \n"
+                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
+                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
+            )
+
+            self._status_report_queue.put_nowait(
+                StatusReport(
+                    payload=SetUpdateMetaReport(
+                        image_file_entries=self._ota_metadata.total_regulars_num,
+                        image_size_uncompressed=_metadata_jwt.total_regular_size,
+                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
+                    ),
+                    session_id=self.session_id,
+                )
+            )
+        except ota_errors.OTAError:
+            raise  # raise top-level OTAError as it
+        except ota_metadata_error.MetadataJWTVerificationFailed as e:
+            _err_msg = f"failed to verify metadata.jwt: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTVerficationFailed(
+                _err_msg, module=__name__
+            ) from e
+        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
+            _err_msg = f"metadata.jwt is invalid: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
+        except Exception as e:
+            _err_msg = f"failed to prepare ota metafiles: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
+        finally:
+            self._downloader_pool.release_instance()
+
+    def _download_and_parse_metadata(self) -> None:
+        self._download_and_process_file_with_condition(
+            thread_name_prefix="download_metadata_files",
+            get_downloads_generator=self._ota_metadata.download_metafiles,
+        )
+
+    def _download_and_process_file_with_condition(
+        self,
+        thread_name_prefix: str,
+        get_downloads_generator: Callable,
+    ) -> None:
+        """
+        Download and process a list of files with a condition.
+        Each file downloading and processing are done in parallel by multiple threads.
+        This method is supposed to be used for downloading metadata and client files.
         """
-        if (_digest := entry.digest) == EMPTY_FILE_SHA256:
-            return DownloadResult(0, 0, 0)
 
-        downloader = self._downloader_mapper[threading.get_native_id()]
-        # NOTE: currently download only use sha256
-        return downloader.download(
-            url=entry.url,
-            dst=entry.dst,
-            digest=_digest,
-            size=entry.original_size,
-            compression_alg=entry.compression_alg,
+        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
+        _mapper = ThreadPoolExecutorWithRetry(
+            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
+            max_workers=cfg.DOWNLOAD_THREADS,
+            thread_name_prefix=thread_name_prefix,
+            initializer=self._downloader_workder_initializer,
+            watchdog_func=partial(
+                self._downloader_pool.downloading_watchdog,
+                ctx=self._download_watchdog_ctx,
+                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
+            ),
         )
 
-    def _download_metadata_file(
+        _condition = threading.Condition()
+        _generator = get_downloads_generator(_condition)
+
+        try:
+            for _fut in _mapper.ensure_tasks(
+                partial(self._download_file_with_condition, condition=_condition),
+                _generator,
+            ):
+                if not (_exc := _fut.exception()):
+                    continue
+
+                logger.warning(f"failed to download one file, keep retrying: {_exc!r}")
+                if isinstance(_exc, requests_exc.HTTPError) and isinstance(
+                    (_response := _exc.response), Response
+                ):
+                    if _response.status_code == HTTPStatus.NOT_FOUND:
+                        raise ota_errors.OTAImageInvalid(
+                            "failed to download", module=__name__
+                        ) from _exc
+
+                    if _response.status_code in [
+                        HTTPStatus.FORBIDDEN,
+                        HTTPStatus.UNAUTHORIZED,
+                    ]:
+                        raise ota_errors.UpdateRequestCookieInvalid(
+                            module=__name__
+                        ) from _exc
+        except Exception as e:
+            _generator.throw(e)
+            raise
+        finally:
+            _exc = None  # resolve cycle ref
+            _mapper.shutdown(wait=True)
+            self._downloader_pool.release_all_instances()
+
+    def _download_file_with_condition(
         self, entries: list[DownloadInfo], *, condition: threading.Condition
     ) -> DownloadResult:
-        """Download a single OTA image metadata file.
-
-        Just a wrapper around _download_file method.
+        """Download a single OTA image metadata and client file with a condition.
+        This method is supposed to be used for downloading metadata and client files.
+        Just a wrapper around _download_single_file method.
path: src/otaclient/ota_core.py
user: login: airkei
body: renamed method name and added doc string.
created_at: 2025-03-06T02:37:15Z
author_association: COLLABORATOR
diff_hunk: @@ -260,51 +253,178 @@ def __init__(
             ca_chains_store=ca_chains_store,
         )
 
-    def _download_file(self, entry: DownloadInfo) -> DownloadResult:
-        """Download a single file.
+    def _handle_upper_proxy(self) -> None:
+        """Ensure the upper proxy is online before starting the local OTA update."""
+        if _upper_proxy := self._upper_proxy:
+            logger.info(
+                f"use {_upper_proxy} for local OTA update, "
+                f"wait for otaproxy@{_upper_proxy} online..."
+            )
 
-        This is the single task being executed in the downloader pool.
+            # NOTE: will raise a built-in ConnnectionError at timeout
+            ensure_otaproxy_start(
+                _upper_proxy,
+                probing_timeout=WAIT_FOR_OTAPROXY_ONLINE,
+            )
 
-        Returns:
-            Retry counts, downloaded files size and traffic on wire.
+    def _process_metadata(self) -> None:
+        """Process the metadata.jwt file and report."""
+        self._status_report_queue.put_nowait(
+            StatusReport(
+                payload=OTAUpdatePhaseChangeReport(
+                    new_update_phase=UpdatePhase.PROCESSING_METADATA,
+                    trigger_timestamp=int(time.time()),
+                ),
+                session_id=self.session_id,
+            )
+        )
+
+        try:
+            logger.info("verify and download OTA image metadata ...")
+            self._download_and_parse_metadata()
+            _metadata_jwt = self._ota_metadata.metadata_jwt
+            assert _metadata_jwt, "invalid metadata jwt"
+
+            logger.info(
+                "ota_metadata parsed finished: \n"
+                f"total_regulars_num: {self._ota_metadata.total_regulars_num} \n"
+                f"total_regulars_size: {_metadata_jwt.total_regular_size}"
+            )
+
+            self._status_report_queue.put_nowait(
+                StatusReport(
+                    payload=SetUpdateMetaReport(
+                        image_file_entries=self._ota_metadata.total_regulars_num,
+                        image_size_uncompressed=_metadata_jwt.total_regular_size,
+                        metadata_downloaded_bytes=self._downloader_pool.total_downloaded_bytes,
+                    ),
+                    session_id=self.session_id,
+                )
+            )
+        except ota_errors.OTAError:
+            raise  # raise top-level OTAError as it
+        except ota_metadata_error.MetadataJWTVerificationFailed as e:
+            _err_msg = f"failed to verify metadata.jwt: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTVerficationFailed(
+                _err_msg, module=__name__
+            ) from e
+        except (ota_metadata_error.MetadataJWTPayloadInvalid, AssertionError) as e:
+            _err_msg = f"metadata.jwt is invalid: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.MetadataJWTInvalid(_err_msg, module=__name__) from e
+        except Exception as e:
+            _err_msg = f"failed to prepare ota metafiles: {e!r}"
+            logger.error(_err_msg)
+            raise ota_errors.OTAMetaDownloadFailed(_err_msg, module=__name__) from e
+        finally:
+            self._downloader_pool.release_instance()
+
+    def _download_and_parse_metadata(self) -> None:
+        self._download_and_process_file_with_condition(
+            thread_name_prefix="download_metadata_files",
+            get_downloads_generator=self._ota_metadata.download_metafiles,
+        )
+
+    def _download_and_process_file_with_condition(
+        self,
+        thread_name_prefix: str,
+        get_downloads_generator: Callable,
+    ) -> None:
+        """
+        Download and process a list of files with a condition.
+        Each file downloading and processing are done in parallel by multiple threads.
+        This method is supposed to be used for downloading metadata and client files.
         """
-        if (_digest := entry.digest) == EMPTY_FILE_SHA256:
-            return DownloadResult(0, 0, 0)
 
-        downloader = self._downloader_mapper[threading.get_native_id()]
-        # NOTE: currently download only use sha256
-        return downloader.download(
-            url=entry.url,
-            dst=entry.dst,
-            digest=_digest,
-            size=entry.original_size,
-            compression_alg=entry.compression_alg,
+        self._download_watchdog_ctx["previous_active_timestamp"] = int(time.time())
+        _mapper = ThreadPoolExecutorWithRetry(
+            max_concurrent=cfg.MAX_CONCURRENT_DOWNLOAD_TASKS,
+            max_workers=cfg.DOWNLOAD_THREADS,
+            thread_name_prefix=thread_name_prefix,
+            initializer=self._downloader_workder_initializer,
+            watchdog_func=partial(
+                self._downloader_pool.downloading_watchdog,
+                ctx=self._download_watchdog_ctx,
+                max_idle_timeout=cfg.DOWNLOAD_INACTIVE_TIMEOUT,
+            ),
         )
 
-    def _download_metadata_file(
+        _condition = threading.Condition()
+        _generator = get_downloads_generator(_condition)
+
+        try:
+            for _fut in _mapper.ensure_tasks(
+                partial(self._download_file_with_condition, condition=_condition),
+                _generator,
+            ):
+                if not (_exc := _fut.exception()):
+                    continue
+
+                logger.warning(f"failed to download one file, keep retrying: {_exc!r}")
+                if isinstance(_exc, requests_exc.HTTPError) and isinstance(
+                    (_response := _exc.response), Response
+                ):
+                    if _response.status_code == HTTPStatus.NOT_FOUND:
+                        raise ota_errors.OTAImageInvalid(
+                            "failed to download", module=__name__
+                        ) from _exc
+
+                    if _response.status_code in [
+                        HTTPStatus.FORBIDDEN,
+                        HTTPStatus.UNAUTHORIZED,
+                    ]:
+                        raise ota_errors.UpdateRequestCookieInvalid(
+                            module=__name__
+                        ) from _exc
+        except Exception as e:
+            _generator.throw(e)
+            raise
+        finally:
+            _exc = None  # resolve cycle ref
+            _mapper.shutdown(wait=True)
+            self._downloader_pool.release_all_instances()
+
+    def _download_file_with_condition(
         self, entries: list[DownloadInfo], *, condition: threading.Condition
     ) -> DownloadResult:
-        """Download a single OTA image metadata file.
-
-        Just a wrapper around _download_file method.
+        """Download a single OTA image metadata and client file with a condition.
+        This method is supposed to be used for downloading metadata and client files.
+        Just a wrapper around _download_single_file method.
 
         Returns:
             Retry counts, downloaded files size and traffic on wire.
         """
         _retry_count, _download_size, _traffic_on_wire = 0, 0, 0
         with condition:
             for entry in entries:
-                _res = self._download_file(entry)
+                _res = self._download_single_file(entry)
                 _retry_count += _res.retry_count
                 _download_size += _res.download_size
                 _traffic_on_wire += _res.traffic_on_wire
 
             condition.notify()  # notify the metadata generator that this batch of download is finished
         return DownloadResult(_retry_count, _download_size, _traffic_on_wire)
 
-    def _downloader_workder_initializer(self) -> None:
-        self._downloader_mapper[threading.get_native_id()] = (
-            self._downloader_pool.get_instance()
+    def _download_single_file(self, entry: DownloadInfo) -> DownloadResult:
path: src/otaclient/ota_core.py
user: login: airkei
body: renamed method name and added doc string.
created_at: 2025-03-06T02:37:33Z
author_association: COLLABORATOR
diff_hunk: @@ -691,7 +703,43 @@ def _execute_update(self):
         self._process_persistents(self._ota_metadata)
         self._boot_controller.post_update()
 
-        # ------ finalizing update ------ #
+    def _process_persistents(self, ota_metadata: OTAMetadata):
+        logger.info("start persist files handling...")
+        standby_slot_mp = Path(cfg.STANDBY_SLOT_MNT)
+
+        _handler = PersistFilesHandler(
+            src_passwd_file=Path(cfg.PASSWD_FPATH),
+            src_group_file=Path(cfg.GROUP_FPATH),
+            dst_passwd_file=Path(standby_slot_mp / "etc/passwd"),
+            dst_group_file=Path(standby_slot_mp / "etc/group"),
+            src_root=cfg.ACTIVE_SLOT_MNT,
+            dst_root=cfg.STANDBY_SLOT_MNT,
+        )
+
+        for persiste_entry in ota_metadata.iter_persist_entries():
+            # NOTE(20240520): with update_swapfile ansible role being used wildly,
+            #   now we just ignore the swapfile entries in the persistents.txt if any,
+            #   and issue a warning about it.
+            if persiste_entry in ["/swapfile", "/swap.img"]:
+                logger.warning(
+                    f"swapfile entry {persiste_entry} is listed in persistents.txt, ignored"
+                )
+                logger.warning(
+                    (
+                        "using persis file feature to preserve swapfile is MISUSE of persist file handling feature!"
+                        "please change your OTA image build setting and remove swapfile entries from persistents.txt!"
+                    )
+                )
+                continue
+
+            try:
+                _handler.preserve_persist_entry(persiste_entry)
+            except Exception as e:
+                _err_msg = f"failed to preserve {persiste_entry}: {e!r}, skip"
+                logger.warning(_err_msg)
+
+    def _finalize_update(self) -> None:
path: src/otaclient/ota_core.py
user: login: airkei
body: include all of the existing finalization instructions.
created_at: 2025-03-06T02:38:09Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-05T00:35:42Z
body: <!-- Pytest Coverage Comment: pytest_with_coverage -->
<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/README.md"><img alt="Coverage" src="https://img.shields.io/badge/Coverage-72%25-yellow.svg" /></a><details><summary>Coverage Report </summary><table><tr><th>File</th><th>Stmts</th><th>Miss</th><th>Cover</th><th>Missing</th></tr><tbody><tr><td colspan="5"><b>src/ota_metadata/file_table</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/__init__.py">\_\_init\_\_.py</a></td><td>4</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_orm.py">_orm.py</a></td><td>38</td><td>2</td><td>94%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_orm.py#L58">58</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_orm.py#L62">62</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py">_table.py</a></td><td>94</td><td>17</td><td>81%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L139-L140">139&ndash;140</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L143">143</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L179">179</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L198-L203">198&ndash;203</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L205-L208">205&ndash;208</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_table.py#L220-L222">220&ndash;222</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_types.py">_types.py</a></td><td>31</td><td>4</td><td>87%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_types.py#L47">47</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/file_table/_types.py#L54-L56">54&ndash;56</a></td></tr><tr><td colspan="5"><b>src/ota_metadata/legacy</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/__init__.py">\_\_init\_\_.py</a></td><td>2</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py">_parser.py</a></td><td>335</td><td>116</td><td>65%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L106">106</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L170">170</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L175">175</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L211-L212">211&ndash;212</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L222-L223">222&ndash;223</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L226">226</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L238">238</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L289-L291">289&ndash;291</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L295-L298">295&ndash;298</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L324-L327">324&ndash;327</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L396">396</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L399">399</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L407-L409">407&ndash;409</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L422">422</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L431-L432">431&ndash;432</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L435-L436">435&ndash;436</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L447-L448">447&ndash;448</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L454">454</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L459">459</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L600-L603">600&ndash;603</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L605-L611">605&ndash;611</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L614-L616">614&ndash;616</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L620">620</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L628-L629">628&ndash;629</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L631">631</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L635">635</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L637-L638">637&ndash;638</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L640-L642">640&ndash;642</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L652-L654">652&ndash;654</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L657">657</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L659">659</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L664">664</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L667-L670">667&ndash;670</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L672-L674">672&ndash;674</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L684-L686">684&ndash;686</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L689-L690">689&ndash;690</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L692">692</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L696">696</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L698-L699">698&ndash;699</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L701">701</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L703-L704">703&ndash;704</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L707">707</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L711">711</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L713-L717">713&ndash;717</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L728-L729">728&ndash;729</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L732">732</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L735-L736">735&ndash;736</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L739-L740">739&ndash;740</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L743">743</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L747-L748">747&ndash;748</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L752-L753">752&ndash;753</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L756-L758">756&ndash;758</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L763-L766">763&ndash;766</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L769-L772">769&ndash;772</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L783">783</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L788">788</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_parser.py#L800">800</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py">_types.py</a></td><td>84</td><td>36</td><td>57%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L33">33</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L36-L42">36&ndash;42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L56-L59">56&ndash;59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L78-L80">78&ndash;80</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L99">99</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L103-L104">103&ndash;104</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L106">106</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L112-L116">112&ndash;116</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L122-L125">122&ndash;125</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L131-L134">131&ndash;134</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy/_types.py#L140-L143">140&ndash;143</a></td></tr><tr><td colspan="5"><b>src/ota_metadata/legacy2</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/__init__.py">\_\_init\_\_.py</a></td><td>2</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/_errors.py">_errors.py</a></td><td>2</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/csv_parser.py">csv_parser.py</a></td><td>91</td><td>3</td><td>96%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/csv_parser.py#L148-L150">148&ndash;150</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py">metadata.py</a></td><td>221</td><td>18</td><td>91%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L88">88</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L91">91</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L95">95</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L124-L126">124&ndash;126</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L331-L332">331&ndash;332</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L335">335</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L365-L367">365&ndash;367</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L469-L471">469&ndash;471</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata.py#L493-L495">493&ndash;495</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py">metadata_jwt.py</a></td><td>95</td><td>18</td><td>81%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L129-L131">129&ndash;131</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L135-L138">135&ndash;138</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L144-L146">144&ndash;146</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L164-L167">164&ndash;167</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L199-L201">199&ndash;201</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/metadata_jwt.py#L215">215</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/rs_table.py">rs_table.py</a></td><td>35</td><td>1</td><td>97%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/legacy2/rs_table.py#L70">70</a></td></tr><tr><td colspan="5"><b>src/ota_metadata/utils</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py">cert_store.py</a></td><td>86</td><td>8</td><td>90%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L58-L59">58&ndash;59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L73">73</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L87">87</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L91">91</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L102">102</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L123">123</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_metadata/utils/cert_store.py#L127">127</a></td></tr><tr><td colspan="5"><b>src/ota_proxy</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py">\_\_init\_\_.py</a></td><td>16</td><td>7</td><td>56%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py#L48-L49">48&ndash;49</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py#L51">51</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py#L53">53</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py#L62">62</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__init__.py#L72-L73">72&ndash;73</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__main__.py">\_\_main\_\_.py</a></td><td>6</td><td>6</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__main__.py#L16">16</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__main__.py#L18-L19">18&ndash;19</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__main__.py#L21-L22">21&ndash;22</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/__main__.py#L24">24</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/_consts.py">_consts.py</a></td><td>17</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_control_header.py">cache_control_header.py</a></td><td>68</td><td>4</td><td>94%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_control_header.py#L71">71</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_control_header.py#L91">91</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_control_header.py#L113">113</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_control_header.py#L121">121</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py">cache_streaming.py</a></td><td>143</td><td>22</td><td>84%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L152-L154">152&ndash;154</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L182-L184">182&ndash;184</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L209">209</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L223">223</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L227-L228">227&ndash;228</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L263-L264">263&ndash;264</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L266">266</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L278">278</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L347">347</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L353-L354">353&ndash;354</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L357">357</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/cache_streaming.py#L365-L368">365&ndash;368</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/config.py">config.py</a></td><td>20</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py">db.py</a></td><td>80</td><td>18</td><td>77%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L103">103</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L109">109</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L167">167</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L173-L174">173&ndash;174</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L177">177</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L183">183</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L185">185</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L209-L216">209&ndash;216</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/db.py#L218-L219">218&ndash;219</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/errors.py">errors.py</a></td><td>5</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py">external_cache.py</a></td><td>28</td><td>20</td><td>28%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L31">31</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L35">35</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L40-L42">40&ndash;42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L44-L45">44&ndash;45</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L48-L49">48&ndash;49</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L51-L53">51&ndash;53</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L60">60</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L63-L65">63&ndash;65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/external_cache.py#L69-L72">69&ndash;72</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/lru_cache_helper.py">lru_cache_helper.py</a></td><td>46</td><td>2</td><td>95%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/lru_cache_helper.py#L85-L86">85&ndash;86</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py">ota_cache.py</a></td><td>234</td><td>64</td><td>72%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L72-L73">72&ndash;73</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L140">140</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L143-L144">143&ndash;144</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L156-L157">156&ndash;157</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L189">189</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L192">192</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L214">214</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L234">234</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L253-L257">253&ndash;257</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L261-L263">261&ndash;263</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L265">265</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L267-L274">267&ndash;274</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L276-L278">276&ndash;278</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L281-L282">281&ndash;282</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L286-L287">286&ndash;287</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L291">291</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L338">338</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L346-L348">346&ndash;348</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L421">421</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L448">448</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L451-L452">451&ndash;452</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L474-L476">474&ndash;476</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L480-L482">480&ndash;482</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L488">488</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L490-L492">490&ndash;492</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L497">497</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L523-L525">523&ndash;525</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L559-L561">559&ndash;561</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L588">588</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L594">594</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/ota_cache.py#L609">609</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py">server_app.py</a></td><td>140</td><td>39</td><td>72%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L71">71</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L74">74</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L80">80</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L99">99</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L103">103</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L162">162</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L171">171</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L213-L214">213&ndash;214</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L216-L218">216&ndash;218</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L221">221</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L226-L227">226&ndash;227</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L230">230</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L233-L234">233&ndash;234</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L237">237</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L240">240</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L243">243</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L246">246</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L259-L260">259&ndash;260</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L263-L264">263&ndash;264</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L266">266</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L269">269</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L295-L298">295&ndash;298</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L301">301</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L315-L317">315&ndash;317</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/server_app.py#L323-L325">323&ndash;325</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/ota_proxy/utils.py">utils.py</a></td><td>13</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td colspan="5"><b>src/otaclient</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/__init__.py">\_\_init\_\_.py</a></td><td>5</td><td>2</td><td>60%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/__init__.py#L17">17</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/__init__.py#L19">19</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/__main__.py">\_\_main\_\_.py</a></td><td>1</td><td>1</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/__main__.py#L16">16</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py">_logging.py</a></td><td>51</td><td>33</td><td>35%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L43-L44">43&ndash;44</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L46-L47">46&ndash;47</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L49-L54">49&ndash;54</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L56-L57">56&ndash;57</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L59-L60">59&ndash;60</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L62-L65">62&ndash;65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L67">67</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L77">77</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L80-L82">80&ndash;82</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L84-L86">84&ndash;86</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L89-L90">89&ndash;90</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_logging.py#L92-L96">92&ndash;96</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py">_otaproxy_ctx.py</a></td><td>42</td><td>42</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L20">20</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L22-L29">22&ndash;29</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L31-L36">31&ndash;36</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L38">38</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L40-L41">40&ndash;41</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L44">44</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L46-L50">46&ndash;50</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L53-L56">53&ndash;56</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L59-L60">59&ndash;60</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L62-L63">62&ndash;63</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L65-L67">65&ndash;67</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L69">69</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L74-L78">74&ndash;78</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_otaproxy_ctx.py#L80">80</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py">_status_monitor.py</a></td><td>194</td><td>21</td><td>89%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L50-L51">50&ndash;51</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L163">163</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L166">166</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L186">186</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L189">189</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L205-L206">205&ndash;206</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L214">214</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L217">217</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L280">280</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L302">302</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L327-L328">327&ndash;328</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L335">335</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L337">337</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L340-L341">340&ndash;341</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L356">356</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_status_monitor.py#L361-L362">361&ndash;362</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_types.py">_types.py</a></td><td>96</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_utils.py">_utils.py</a></td><td>30</td><td>2</td><td>93%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/_utils.py#L80-L81">80&ndash;81</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/errors.py">errors.py</a></td><td>120</td><td>1</td><td>99%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/errors.py#L97">97</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py">main.py</a></td><td>25</td><td>25</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L17">17</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L19-L29">19&ndash;29</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L31-L33">31&ndash;33</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L35">35</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L37">37</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L41-L42">41&ndash;42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L44-L46">44&ndash;46</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/main.py#L48-L50">48&ndash;50</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py">ota_core.py</a></td><td>411</td><td>144</td><td>64%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L129">129</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L131-L132">131&ndash;132</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L136-L137">136&ndash;137</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L139-L141">139&ndash;141</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L145-L146">145&ndash;146</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L151-L152">151&ndash;152</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L158">158</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L160">160</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L216-L219">216&ndash;219</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L259">259</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L265">265</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L304-L309">304&ndash;309</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L312-L319">312&ndash;319</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L364-L365">364&ndash;365</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L368-L369">368&ndash;369</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L373">373</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L377">377</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L380-L382">380&ndash;382</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L470">470</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L563-L564">563&ndash;564</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L615-L618">615&ndash;618</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L658-L659">658&ndash;659</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L663-L664">663&ndash;664</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L707-L708">707&ndash;708</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L710">710</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L719">719</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L723-L724">723&ndash;724</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L727">727</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L733">733</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L735-L739">735&ndash;739</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L776-L783">776&ndash;783</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L790">790</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L793-L800">793&ndash;800</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L825-L826">825&ndash;826</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L832">832</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L836-L837">836&ndash;837</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L843">843</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L868-L870">868&ndash;870</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L872">872</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L912">912</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L934">934</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L961-L963">961&ndash;963</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L972-L978">972&ndash;978</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L992-L998">992&ndash;998</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1000-L1001">1000&ndash;1001</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1006-L1007">1006&ndash;1007</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1015">1015</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1017">1017</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1023">1023</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1025">1025</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1031">1031</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1033">1033</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1037">1037</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1043">1043</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1045">1045</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1051">1051</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1054-L1056">1054&ndash;1056</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1066-L1067">1066&ndash;1067</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1078-L1080">1078&ndash;1080</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1082-L1083">1082&ndash;1083</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1085">1085</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1087-L1088">1087&ndash;1088</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1093-L1094">1093&ndash;1094</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1096">1096</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/ota_core.py#L1101">1101</a></td></tr><tr><td colspan="5"><b>src/otaclient/boot_control</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/__init__.py">\_\_init\_\_.py</a></td><td>4</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py">_firmware_package.py</a></td><td>93</td><td>22</td><td>76%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L82">82</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L86">86</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L136">136</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L180">180</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L186">186</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L209-L210">209&ndash;210</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L213-L218">213&ndash;218</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L220-L221">220&ndash;221</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L224-L229">224&ndash;229</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_firmware_package.py#L231">231</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py">_grub.py</a></td><td>418</td><td>127</td><td>69%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L214">214</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L262-L265">262&ndash;265</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L271-L275">271&ndash;275</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L312-L313">312&ndash;313</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L320-L325">320&ndash;325</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L328-L334">328&ndash;334</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L337">337</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L340-L341">340&ndash;341</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L346">346</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L348-L350">348&ndash;350</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L359-L365">359&ndash;365</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L367-L368">367&ndash;368</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L370-L372">370&ndash;372</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L381-L383">381&ndash;383</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L385-L387">385&ndash;387</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L466-L467">466&ndash;467</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L471-L472">471&ndash;472</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L524">524</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L530">530</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L556">556</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L578">578</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L582-L583">582&ndash;583</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L598-L600">598&ndash;600</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L624-L627">624&ndash;627</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L639">639</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L643-L645">643&ndash;645</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L647-L649">647&ndash;649</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L708-L711">708&ndash;711</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L736-L739">736&ndash;739</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L762-L765">762&ndash;765</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L777-L778">777&ndash;778</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L781-L782">781&ndash;782</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L817">817</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L823">823</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L843-L844">843&ndash;844</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L846">846</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L868-L870">868&ndash;870</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L888-L891">888&ndash;891</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L916-L919">916&ndash;919</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L926-L929">926&ndash;929</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L934-L942">934&ndash;942</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_grub.py#L947-L954">947&ndash;954</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py">_jetson_cboot.py</a></td><td>261</td><td>261</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L20">20</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L22-L25">22&ndash;25</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L27-L29">27&ndash;29</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L35-L40">35&ndash;40</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L42">42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L58-L60">58&ndash;60</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L62">62</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L64-L65">64&ndash;65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L71">71</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L75">75</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L134">134</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L137">137</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L139-L140">139&ndash;140</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L143">143</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L150-L151">150&ndash;151</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L159-L160">159&ndash;160</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L163">163</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L169-L170">169&ndash;170</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L178">178</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L187-L191">187&ndash;191</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L193">193</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L199">199</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L202-L203">202&ndash;203</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L209">209</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L212-L213">212&ndash;213</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L218-L219">218&ndash;219</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L221">221</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L227-L228">227&ndash;228</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L231-L232">231&ndash;232</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L235-L237">235&ndash;237</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L239">239</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L245">245</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L250-L252">250&ndash;252</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L254-L256">254&ndash;256</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L261">261</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L263-L266">263&ndash;266</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L268-L269">268&ndash;269</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L278-L279">278&ndash;279</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L282-L283">282&ndash;283</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L288-L289">288&ndash;289</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L292-L296">292&ndash;296</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L299-L300">299&ndash;300</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L305-L306">305&ndash;306</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L309">309</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L312-L316">312&ndash;316</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L321-L324">321&ndash;324</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L327">327</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L330-L331">330&ndash;331</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L334">334</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L337-L338">337&ndash;338</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L341">341</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L345-L350">345&ndash;350</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L354-L355">354&ndash;355</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L359">359</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L362-L363">362&ndash;363</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L366">366</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L369-L372">369&ndash;372</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L374">374</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L377-L378">377&ndash;378</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L382">382</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L385">385</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L388-L391">388&ndash;391</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L393">393</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L400">400</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L404-L405">404&ndash;405</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L408-L409">408&ndash;409</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L415-L416">415&ndash;416</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L422">422</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L424-L425">424&ndash;425</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L429">429</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L431">431</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L433-L435">433&ndash;435</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L438">438</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L442">442</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L445">445</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L448-L449">448&ndash;449</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L451">451</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L454">454</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L462-L463">462&ndash;463</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L470">470</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L480">480</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L483">483</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L491-L492">491&ndash;492</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L497-L500">497&ndash;500</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L502">502</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L509">509</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L511-L513">511&ndash;513</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L519-L520">519&ndash;520</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L524-L525">524&ndash;525</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L528">528</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L532">532</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L535">535</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L537">537</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L544-L548">544&ndash;548</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L550">550</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L562-L565">562&ndash;565</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L568">568</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L571">571</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L573">573</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L580">580</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L587-L589">587&ndash;589</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L591">591</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L593">593</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L596">596</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L599">599</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L602">602</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L604-L605">604&ndash;605</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L608-L612">608&ndash;612</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L616-L618">616&ndash;618</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L620">620</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L628-L632">628&ndash;632</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L634">634</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L637">637</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L641">641</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L644">644</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L655-L656">655&ndash;656</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L661">661</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L671">671</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L674-L680">674&ndash;680</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L684-L690">684&ndash;690</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L694-L703">694&ndash;703</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L707-L715">707&ndash;715</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L719">719</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L721">721</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_cboot.py#L723-L725">723&ndash;725</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py">_jetson_common.py</a></td><td>172</td><td>45</td><td>73%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L132">132</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L140">140</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L288-L291">288&ndash;291</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L294">294</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L311">311</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L319">319</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L354">354</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L359-L364">359&ndash;364</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L382">382</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L408-L409">408&ndash;409</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L411-L413">411&ndash;413</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L417-L420">417&ndash;420</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L422-L423">422&ndash;423</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L425-L429">425&ndash;429</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L431">431</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L438-L439">438&ndash;439</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L442-L443">442&ndash;443</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L453">453</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L456-L457">456&ndash;457</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L460">460</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L462">462</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_common.py#L506-L507">506&ndash;507</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py">_jetson_uefi.py</a></td><td>404</td><td>274</td><td>32%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L124-L126">124&ndash;126</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L131-L132">131&ndash;132</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L151-L153">151&ndash;153</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L158-L161">158&ndash;161</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L328">328</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L446">446</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L448-L451">448&ndash;451</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L455">455</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L459-L460">459&ndash;460</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L462-L470">462&ndash;470</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L472">472</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L484-L485">484&ndash;485</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L488-L489">488&ndash;489</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L492-L493">492&ndash;493</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L496-L498">496&ndash;498</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L502-L503">502&ndash;503</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L508-L510">508&ndash;510</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L514">514</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L518-L519">518&ndash;519</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L522-L523">522&ndash;523</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L526-L527">526&ndash;527</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L531">531</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L534-L535">534&ndash;535</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L537">537</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L542-L543">542&ndash;543</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L547">547</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L550-L551">550&ndash;551</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L556">556</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L560-L561">560&ndash;561</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L564">564</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L568-L570">568&ndash;570</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L572">572</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L576-L579">576&ndash;579</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L581-L582">581&ndash;582</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L604-L605">604&ndash;605</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L609-L610">609&ndash;610</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L612">612</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L616">616</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L620-L621">620&ndash;621</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L624-L625">624&ndash;625</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L632">632</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L635-L637">635&ndash;637</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L640">640</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L642-L643">642&ndash;643</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L648-L649">648&ndash;649</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L652-L655">652&ndash;655</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L657-L658">657&ndash;658</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L663">663</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L665-L666">665&ndash;666</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L674">674</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L677-L680">677&ndash;680</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L682-L683">682&ndash;683</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L685">685</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L689-L690">689&ndash;690</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L694">694</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L702-L706">702&ndash;706</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L709-L710">709&ndash;710</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L712">712</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L715-L719">715&ndash;719</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L722">722</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L725-L729">725&ndash;729</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L733-L734">733&ndash;734</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L737-L742">737&ndash;742</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L745-L746">745&ndash;746</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L749-L752">749&ndash;752</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L754-L755">754&ndash;755</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L762-L763">762&ndash;763</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L773-L776">773&ndash;776</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L779">779</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L782-L785">782&ndash;785</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L788-L792">788&ndash;792</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L795-L796">795&ndash;796</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L799">799</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L802-L805">802&ndash;805</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L808">808</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L810">810</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L815-L816">815&ndash;816</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L819">819</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L822-L825">822&ndash;825</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L827">827</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L833">833</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L838-L839">838&ndash;839</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L858-L859">858&ndash;859</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L862">862</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L870-L871">870&ndash;871</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L878">878</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L888">888</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L891">891</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L898-L899">898&ndash;899</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L904-L907">904&ndash;907</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L915-L918">915&ndash;918</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L926-L927">926&ndash;927</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L939-L942">939&ndash;942</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L944">944</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L947">947</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L950">950</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L958">958</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L966-L968">966&ndash;968</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L970-L972">970&ndash;972</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L974-L978">974&ndash;978</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L983-L984">983&ndash;984</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L986">986</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L999">999</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1003">1003</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1006">1006</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1016">1016</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1021">1021</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1029-L1030">1029&ndash;1030</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1033">1033</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1037">1037</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1039-L1041">1039&ndash;1041</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1047-L1048">1047&ndash;1048</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1053">1053</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1061-L1066">1061&ndash;1066</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1071-L1076">1071&ndash;1076</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1081-L1089">1081&ndash;1089</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1094-L1101">1094&ndash;1101</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_jetson_uefi.py#L1109-L1111">1109&ndash;1111</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py">_ota_status_control.py</a></td><td>102</td><td>11</td><td>89%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L117">117</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L122">122</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L127">127</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L240">240</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L244-L245">244&ndash;245</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L248">248</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L255">255</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L257-L258">257&ndash;258</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_ota_status_control.py#L273">273</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py">_rpi_boot.py</a></td><td>287</td><td>133</td><td>53%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L53">53</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L56">56</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L120-L121">120&ndash;121</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L125">125</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L133-L136">133&ndash;136</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L150-L153">150&ndash;153</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L158-L159">158&ndash;159</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L161-L162">161&ndash;162</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L167-L168">167&ndash;168</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L171-L172">171&ndash;172</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L181-L182">181&ndash;182</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L222">222</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L228-L232">228&ndash;232</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L235">235</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L253-L255">253&ndash;255</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L259-L261">259&ndash;261</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L266-L268">266&ndash;268</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L272-L274">272&ndash;274</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L284-L285">284&ndash;285</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L288">288</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L291">291</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L293-L294">293&ndash;294</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L296-L297">296&ndash;297</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L299-L301">299&ndash;301</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L307">307</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L310-L311">310&ndash;311</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L321-L324">321&ndash;324</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L332-L336">332&ndash;336</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L338">338</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L340-L341">340&ndash;341</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L346-L347">346&ndash;347</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L354">354</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L357-L362">357&ndash;362</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L393">393</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L395-L398">395&ndash;398</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L408-L411">408&ndash;411</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L415-L416">415&ndash;416</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L418-L422">418&ndash;422</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L450-L453">450&ndash;453</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L472-L475">472&ndash;475</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L498-L501">498&ndash;501</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L506-L514">506&ndash;514</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L519-L526">519&ndash;526</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L541-L544">541&ndash;544</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L551-L554">551&ndash;554</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_rpi_boot.py#L562-L564">562&ndash;564</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/_slot_mnt_helper.py">_slot_mnt_helper.py</a></td><td>10</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/configs.py">configs.py</a></td><td>51</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/protocol.py">protocol.py</a></td><td>5</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py">selecter.py</a></td><td>41</td><td>29</td><td>29%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L44-L46">44&ndash;46</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L49-L50">49&ndash;50</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L54-L55">54&ndash;55</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L58-L60">58&ndash;60</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L63">63</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L65">65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L69">69</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L77-L79">77&ndash;79</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L81-L82">81&ndash;82</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L84-L85">84&ndash;85</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L89">89</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L91">91</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L93-L94">93&ndash;94</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L96">96</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L98-L99">98&ndash;99</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L101">101</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/boot_control/selecter.py#L103">103</a></td></tr><tr><td colspan="5"><b>src/otaclient/configs</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/__init__.py">\_\_init\_\_.py</a></td><td>17</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_cfg_configurable.py">_cfg_configurable.py</a></td><td>47</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_cfg_consts.py">_cfg_consts.py</a></td><td>47</td><td>1</td><td>97%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_cfg_consts.py#L97">97</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_common.py">_common.py</a></td><td>8</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_ecu_info.py">_ecu_info.py</a></td><td>56</td><td>4</td><td>92%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_ecu_info.py#L59">59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_ecu_info.py#L64-L65">64&ndash;65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_ecu_info.py#L112">112</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py">_proxy_info.py</a></td><td>50</td><td>8</td><td>84%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py#L84">84</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py#L86-L87">86&ndash;87</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py#L89">89</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py#L100">100</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/_proxy_info.py#L113-L115">113&ndash;115</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/configs/cfg.py">cfg.py</a></td><td>23</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td colspan="5"><b>src/otaclient/create_standby</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/__init__.py">\_\_init\_\_.py</a></td><td>11</td><td>1</td><td>90%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/__init__.py#L31">31</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py">_delta_gen.py</a></td><td>154</td><td>9</td><td>94%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L115">115</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L132">132</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L229">229</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L259-L260">259&ndash;260</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L298">298</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L301-L302">301&ndash;302</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/_delta_gen.py#L336">336</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py">rebuild_mode.py</a></td><td>119</td><td>11</td><td>90%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py#L159">159</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py#L177-L179">177&ndash;179</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py#L212">212</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py#L255-L257">255&ndash;257</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/create_standby/rebuild_mode.py#L272-L274">272&ndash;274</a></td></tr><tr><td colspan="5"><b>src/otaclient/grpc/api_v2</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/_types.py">_types.py</a></td><td>44</td><td>2</td><td>95%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/_types.py#L78-L79">78&ndash;79</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py">ecu_status.py</a></td><td>145</td><td>7</td><td>95%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L117">117</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L142">142</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L144">144</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L275">275</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L347-L348">347&ndash;348</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_status.py#L384">384</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py">ecu_tracker.py</a></td><td>53</td><td>53</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L17">17</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L19-L22">19&ndash;22</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L24-L30">24&ndash;30</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L32">32</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L34">34</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L38-L39">38&ndash;39</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L42">42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L44">44</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L50-L53">50&ndash;53</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L55">55</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L57">57</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L59-L62">59&ndash;62</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L69">69</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L73-L76">73&ndash;76</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L80-L81">80&ndash;81</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L83">83</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L85">85</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L87-L95">87&ndash;95</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L99-L100">99&ndash;100</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L102">102</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/ecu_tracker.py#L104-L107">104&ndash;107</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py">main.py</a></td><td>41</td><td>41</td><td>0%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L17">17</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L19-L24">19&ndash;24</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L26-L27">26&ndash;27</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L29">29</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L32">32</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L39">39</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L41-L42">41&ndash;42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L44-L45">44&ndash;45</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L47-L48">47&ndash;48</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L50-L55">50&ndash;55</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L57-L59">57&ndash;59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L61">61</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L64">64</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L70">70</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L72-L73">72&ndash;73</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L76-L77">76&ndash;77</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L79-L82">79&ndash;82</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L84-L85">84&ndash;85</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/main.py#L87">87</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py">servicer.py</a></td><td>127</td><td>105</td><td>17%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L58-L62">58&ndash;62</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L64-L65">64&ndash;65</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L67-L68">67&ndash;68</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L74-L78">74&ndash;78</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L82-L83">82&ndash;83</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L88">88</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L91">91</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L95-L97">95&ndash;97</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L101-L103">101&ndash;103</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L111-L113">111&ndash;113</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L118-L119">118&ndash;119</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L122-L123">122&ndash;123</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L129">129</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L132-L136">132&ndash;136</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L145-L155">145&ndash;155</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L162">162</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L168">168</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L171-L173">171&ndash;173</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L184-L186">184&ndash;186</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L189-L191">189&ndash;191</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L196">196</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L203-L206">203&ndash;206</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L209">209</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L213-L214">213&ndash;214</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L219">219</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L222">222</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L226-L228">226&ndash;228</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L232-L234">232&ndash;234</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L242-L243">242&ndash;243</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L248-L249">248&ndash;249</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L252-L253">252&ndash;253</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L259">259</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L262-L266">262&ndash;266</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L275-L284">275&ndash;284</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L291">291</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L297">297</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L300-L302">300&ndash;302</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L307-L308">307&ndash;308</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient/grpc/api_v2/servicer.py#L311">311</a></td></tr><tr><td colspan="5"><b>src/otaclient_api/v2</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py">_types.py</a></td><td>256</td><td>32</td><td>87%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L61">61</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L64">64</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L67-L70">67&ndash;70</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L86">86</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L89-L92">89&ndash;92</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L131">131</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L209-L210">209&ndash;210</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L212">212</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L259">259</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L262-L263">262&ndash;263</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L506-L508">506&ndash;508</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L512-L513">512&ndash;513</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L515">515</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L518-L519">518&ndash;519</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L522-L523">522&ndash;523</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L578">578</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L585-L586">585&ndash;586</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/_types.py#L588">588</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/api_caller.py">api_caller.py</a></td><td>39</td><td>6</td><td>84%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/api_caller.py#L45-L47">45&ndash;47</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_api/v2/api_caller.py#L83-L85">83&ndash;85</a></td></tr><tr><td colspan="5"><b>src/otaclient_common</b></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py">\_\_init\_\_.py</a></td><td>30</td><td>7</td><td>76%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py#L40-L42">40&ndash;42</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py#L48">48</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py#L51">51</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py#L68">68</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/__init__.py#L70">70</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_io.py">_io.py</a></td><td>66</td><td>1</td><td>98%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_io.py#L42">42</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_logging.py">_logging.py</a></td><td>42</td><td>4</td><td>90%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_logging.py#L56">56</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_logging.py#L87-L88">87&ndash;88</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_logging.py#L95">95</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_typing.py">_typing.py</a></td><td>31</td><td>4</td><td>87%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_typing.py#L48">48</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_typing.py#L97-L98">97&ndash;98</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/_typing.py#L100">100</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/cmdhelper.py">cmdhelper.py</a></td><td>13</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py">common.py</a></td><td>106</td><td>10</td><td>90%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L148">148</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L151-L153">151&ndash;153</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L168">168</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L175-L177">175&ndash;177</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L271">271</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/common.py#L275">275</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/download_info.py">download_info.py</a></td><td>12</td><td>0</td><td>100%</td><td>&nbsp;</td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py">downloader.py</a></td><td>202</td><td>9</td><td>95%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L123-L124">123&ndash;124</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L142">142</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L169">169</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L385">385</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L440">440</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L444">444</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L542">542</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/downloader.py#L557">557</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py">linux.py</a></td><td>61</td><td>15</td><td>75%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L51-L53">51&ndash;53</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L59">59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L69">69</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L74">74</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L76">76</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L108-L109">108&ndash;109</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L133-L134">133&ndash;134</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L190">190</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L195-L196">195&ndash;196</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/linux.py#L198">198</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py">persist_file_handling.py</a></td><td>118</td><td>18</td><td>84%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L113">113</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L118">118</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L150-L152">150&ndash;152</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L163">163</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L192-L193">192&ndash;193</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L228-L232">228&ndash;232</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L242-L244">242&ndash;244</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/persist_file_handling.py#L246-L247">246&ndash;247</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py">proto_streamer.py</a></td><td>42</td><td>27</td><td>35%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L33">33</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L37">37</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L44">44</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L46-L49">46&ndash;49</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L58-L59">58&ndash;59</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L63-L67">63&ndash;67</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L72">72</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L75-L76">75&ndash;76</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L81-L82">81&ndash;82</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L84-L86">84&ndash;86</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L95-L96">95&ndash;96</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_streamer.py#L99-L101">99&ndash;101</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py">proto_wrapper.py</a></td><td>398</td><td>70</td><td>82%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L87">87</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L134-L141">134&ndash;141</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L165">165</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L172">172</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L184-L186">184&ndash;186</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L189-L190">189&ndash;190</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L205">205</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L210">210</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L221">221</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L257">257</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L263">263</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L268">268</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L299">299</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L303">303</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L307">307</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L402">402</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L462">462</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L469">469</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L472">472</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L492">492</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L499">499</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L501">501</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L526">526</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L532">532</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L535">535</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L537">537</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L562">562</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L568">568</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L571">571</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L573">573</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L605">605</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L609">609</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L611">611</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L625">625</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L642">642</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L669">669</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L672">672</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L676">676</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L683-L690">683&ndash;690</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L692-L695">692&ndash;695</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L707">707</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L713">713</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L730">730</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L734">734</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L762-L763">762&ndash;763</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L765">765</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/proto_wrapper.py#L803-L805">803&ndash;805</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py">retry_task_map.py</a></td><td>129</td><td>9</td><td>93%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py#L134-L135">134&ndash;135</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py#L153-L154">153&ndash;154</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py#L207-L208">207&ndash;208</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py#L210">210</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/retry_task_map.py#L230-L231">230&ndash;231</a></td></tr><tr><td>&nbsp; &nbsp;<a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py">shm_status.py</a></td><td>95</td><td>21</td><td>77%</td><td><a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L79-L80">79&ndash;80</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L83-L84">83&ndash;84</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L105">105</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L120-L122">120&ndash;122</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L134">134</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L139">139</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L156-L160">156&ndash;160</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L169-L170">169&ndash;170</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L172">172</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L179">179</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L192">192</a>, <a href="https://github.com/tier4/ota-client/blob/7a5f515c7bbe305dc0014320a8e527ef1427ce14/src/otaclient_common/shm_status.py#L204">204</a></td></tr><tr><td><b>TOTAL</b></td><td><b>7341</b></td><td><b>2053</b></td><td><b>72%</b></td><td>&nbsp;</td></tr></tbody></table></details>

| Tests | Skipped | Failures | Errors | Time |
| ----- | ------- | -------- | -------- | ------------------ |
| 259 | 0 :zzz: | 0 :x: | 0 :fire: | 13m 52s :stopwatch: |

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-06T02:52:03Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tier4_ota-client&pullRequest=496) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=tier4_ota-client&pullRequest=496&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=tier4_ota-client&pullRequest=496&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tier4_ota-client&pullRequest=496&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [80.5% Coverage on New Code](https://sonarcloud.io/component_measures?id=tier4_ota-client&pullRequest=496&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tier4_ota-client&pullRequest=496&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=tier4_ota-client&pullRequest=496)


author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-21T00:53:07Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tier4_ota-client&pullRequest=496) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=tier4_ota-client&pullRequest=496&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=tier4_ota-client&pullRequest=496&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tier4_ota-client&pullRequest=496&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=tier4_ota-client&pullRequest=496&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tier4_ota-client&pullRequest=496&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=tier4_ota-client&pullRequest=496)


author_association: NONE