diff --git a/doc/cli_wrapper/images/architecture.jpg b/doc/cli_wrapper/images/architecture.jpg new file mode 100755 index 00000000000..bd4c11effb6 Binary files /dev/null and b/doc/cli_wrapper/images/architecture.jpg differ diff --git a/doc/cli_wrapper/images/architecture_1.jpg b/doc/cli_wrapper/images/architecture_1.jpg new file mode 100755 index 00000000000..3d0fe755687 Binary files /dev/null and b/doc/cli_wrapper/images/architecture_1.jpg differ diff --git a/doc/cli_wrapper/images/architecture_2.jpg b/doc/cli_wrapper/images/architecture_2.jpg new file mode 100755 index 00000000000..206248a29e1 Binary files /dev/null and b/doc/cli_wrapper/images/architecture_2.jpg differ diff --git a/doc/cli_wrapper/images/command_execution_flow.jpg b/doc/cli_wrapper/images/command_execution_flow.jpg new file mode 100755 index 00000000000..b4be115c468 Binary files /dev/null and b/doc/cli_wrapper/images/command_execution_flow.jpg differ diff --git a/doc/cli_wrapper/images/compiler_stage.jpg b/doc/cli_wrapper/images/compiler_stage.jpg new file mode 100755 index 00000000000..59aa4ba9c79 Binary files /dev/null and b/doc/cli_wrapper/images/compiler_stage.jpg differ diff --git a/doc/cli_wrapper/images/encode_format.jpg b/doc/cli_wrapper/images/encode_format.jpg new file mode 100755 index 00000000000..a0ba3223e81 Binary files /dev/null and b/doc/cli_wrapper/images/encode_format.jpg differ diff --git a/doc/cli_wrapper/images/generate_wrapper_file.jpg b/doc/cli_wrapper/images/generate_wrapper_file.jpg new file mode 100755 index 00000000000..d0a94d6e650 Binary files /dev/null and b/doc/cli_wrapper/images/generate_wrapper_file.jpg differ diff --git a/doc/cli_wrapper/images/source_tree.jpg b/doc/cli_wrapper/images/source_tree.jpg new file mode 100755 index 00000000000..9cce307415e Binary files /dev/null and b/doc/cli_wrapper/images/source_tree.jpg differ diff --git a/doc/cli_wrapper/images/test_result_arm_1.jpg b/doc/cli_wrapper/images/test_result_arm_1.jpg new file mode 100755 index 00000000000..837fa834996 Binary files /dev/null and b/doc/cli_wrapper/images/test_result_arm_1.jpg differ diff --git a/doc/cli_wrapper/images/test_result_arm_2.jpg b/doc/cli_wrapper/images/test_result_arm_2.jpg new file mode 100755 index 00000000000..a71f7b8e107 Binary files /dev/null and b/doc/cli_wrapper/images/test_result_arm_2.jpg differ diff --git a/doc/cli_wrapper/images/watchdog_flow.jpg b/doc/cli_wrapper/images/watchdog_flow.jpg new file mode 100755 index 00000000000..2205a2fefb8 Binary files /dev/null and b/doc/cli_wrapper/images/watchdog_flow.jpg differ diff --git a/doc/cli_wrapper/images/wrapper_disable.jpg b/doc/cli_wrapper/images/wrapper_disable.jpg new file mode 100755 index 00000000000..5cfefb7d25e Binary files /dev/null and b/doc/cli_wrapper/images/wrapper_disable.jpg differ diff --git a/doc/cli_wrapper/images/wrapper_enable.jpg b/doc/cli_wrapper/images/wrapper_enable.jpg new file mode 100755 index 00000000000..dd27444afd6 Binary files /dev/null and b/doc/cli_wrapper/images/wrapper_enable.jpg differ diff --git a/doc/cli_wrapper/shd-cli_wrapper_hld.md b/doc/cli_wrapper/shd-cli_wrapper_hld.md new file mode 100755 index 00000000000..b4eebb104fa --- /dev/null +++ b/doc/cli_wrapper/shd-cli_wrapper_hld.md @@ -0,0 +1,213 @@ +# CLI wrapper + + +**Table of Contents** + +- [Revision](#revision) + +- [1. High Level Design Document](#1-high-level-design-document) + + - [1.1. About this Manual](#11-about-this-manual) + + - [1.2. Architecture](#12-architecture) + + - [1.3. Design](#13-design) + + - [1.3.1. Generate wrapped files](#131-generate-wrapped-files) + + - [1.3.2. Command execution flow](#132-command-execution-flow) + + - [1.3.3. Watchdog execution flow](#133-watchdog-execution-flow) + + - [1.3.4. Encoded command format](#134-encoded-command-format) + + - [1.3.5. CLI wrapper service](#135-cli-wrapper-service) + + - [1.3.6. Source tree](#136-source-tree) + +- [2. Performance improvements](#2-performance-improvements) + +- [3. Open questions](#3-open-questions) + +- [4. Reference](#4-reference) + + + +## Revision + +| Rev | Date | Author | Change Description | +| :--: | :--------: | :-----------------------------------: | ------------------ | +| 0.1 | 08/12/2022 | Justin Lu | 1. Initial version | +| 0.2 | 09/03/2022 | Justin Lu | 1. Add watchdog design
2. Modify architecture picture | +| 0.3 | 09/22/2022 | Antonio Ho | 1. Add enable/disable design | +| 0.4 | 01/16/2023 | Antonio Ho | 1. Add performance improvements | +| 0.5 | 05/03/2023 | Antonio Ho | 1. Modify performance improvements
2. Add TODO list | + +# 1. High Level Design Document + +## 1.1 About this Manual + +This document provides the high-level design for CLI_wrapper in SONiC + +## 1.2 Architecture + +SONiC uses click package as its CLI engine, although click package provides developers a convenient way to establish whole CLI architecture, it also has some disadvantage. The problem we want to address here is about the bad performance of CLI execution time. + +Everytime user executes a command, CLI engine will load main source file (ex. config/main.py), then starts to execute the command. In this process, all libraries always have to be imported again. It costs about 80% execution time on this process, so CLI_wrapper is designed to reduce the time. + +The figure below depicts the architecture of CLI_wrapper. + +![Architecture](images/architecture_1.jpg) + +* CLI_wrapper builder + + In order to integrate CLI_wrapper with current CLI architecture, we develop CLI_wrapper builder to generate CLI_wrapper for command source file (ex. main.py). When sonic-utilities debian is packaged, it calls CLI_wrapper builder to generate wrapped files which contains only function interface and minimal libraries. It replaces function body with the calling of CLI_wrapper client to send a encoded command to CLI_wrapper server and acquires the result. Since the CLI_wrapper only contains minimal libraries, CLI engine doesn't need a lot of time to execute. + +![Architecture](images/architecture_2.jpg) + +* CLI_wrapper server + + CLI_wrapper server is responsible for the execution of click command. It starts when system boots up, and loads all needed libraries to server. Then setup a socket to wait client's request. Once it accepts a request, it spawns a CLI_worker process to deal with this request. + + In order to avoid that some command may stuck in CLI worker and cause resource leakage, a watchdog thread is designed to watch the execution time of CLI worker, once the execution time exceeds maximum execution time, the CLI worker will be gracefully terminated. But if the CLI worker is still alive after one minute, a kill signal will be sent to CLI worker to tear down it. + + The local DB is used to store the record of spawned CLI worker, it records the steady timestamp and pid of the CLI worker. + +* CLI_worker + + Decode the request and execute this command. Return the execution result to client and finish this process. + + Everytime cli_wapper server receives a request, it spawns a process to handle this request, so no single block point may happen in this design. + +* CLI_wrapper client + + It is a library that can be used to interact with CLI wrapper server. + +--- + +## 1.3 Design + +This section provides design details for CLI_wrapper. + +### 1.3.1 Generate wrapped files + +![Generate wrapper file](images/generate_wrapper_file.jpg) + +* In the step 1 and step 2, builder copies the necessary libraries to wrapped file and discard other libraries to reduce loading time + +* In the step 3, builder removes the content of command function body and replaces it with CLI_wrapper client + +### 1.3.2 Command execution flow + +![Command execution flow](images/command_execution_flow.jpg) + +* System ctrl bring up CLI_wrapper server when system starts + +* Click engine imports wrapped main.py and call CLI_wrapper client to send request + +* CLI_wrapper client gets command parameter and environment setting from CLI context, then encodes those information to a command string and send to CLI_wrapper server. + +* CLI_wrapper server launches a CLI worker to deal with this request and write timestamp and pid to local DB + +* CLI worker decodes the command to get parameters and environment setting, then execute this command based on those settings. In the end, it sends return code and data back to CLI wrapper client + +### 1.3.3 Watchdog execution flow + +![Command execution flow](images/watchdog_flow.jpg) +* Default polling time is 60 seconds, watchdog check if the pid in local DB is still running, if not, update local DB. + +* Default expired time is 600 seconds (To send terminate signal) and 660 seconds (To send kill signal), watchdog check if CLI worker execution time exceeds expired time, send terminate or kill signal to recycle it. + +### 1.3.4 Encoded command format + +In order to send all needed information in a request, some encode processes must be applied before sending it to server. +The following picture shows two examples for encoded requests. + +![Encode format](images/encode_format.jpg) + +* \__wrapper_calling_cmd__: used to indicate the start of a command + +* \__wrapper_env__ : used to indicate the environment setting when executing this command, separate the value by ";", this is a optional field. + +### 1.3.5 CLI wrapper service + +In compile stage, CLI_wrapper builder put generated wrapped file and original main.py to a backup folder. + +CLI wrapper service use these backup files to enable/disable CLI_wrapper feature. + +![Encode format](images/compiler_stage.jpg) + +* Enable CLI wrapper service + +![Encode format](images/wrapper_enable.jpg) + + 1. Replace main.py with wrapped main.py + + 2. Start CLI wrapper daemon + +* Disable CLI wrapper service + +![Encode format](images/wrapper_disable.jpg) + + 1. Replace main.py with main_impl.py + + 2. Stop CLI wrapper daemon + +### 1.3.6 Source tree + +Here lists the related source tree of CLI_wrapper + +![Encode format](images/source_tree.jpg) + +* CLI_wrapper builder : files/build_scripts/cli_wrapper +* CLI_wrapper client : src/sonic-utilities/cli_wrapper +* CLI_wrapper server : src/sonic-utilities/cli_wrapper +* Integrate CLI wrapper builder to sonic-utilities debian: rules/sonic-utilities.mk + +# 2. Performance improvements + +* The following test results are made in arm platform(dual-core A53 processor): + + * Executing "show vlan brief" command 20 times + + - The following results are compared with and without cli_wrapper. + + ![Encode format](images/test_result_arm_1.jpg) + + * The following is an analysis of the execution time of the "show vlan brief" command + + - The following results are compared with and without cli_wrapper. + + ![Encode format](images/test_result_arm_2.jpg) + +* Conclusion + + * Cli_wrapper can improve command execution time when Python code imports more global packages. + + * For less powerful CPUs, cli_wrapper can significantly reduce execution time. + +# 3. Open questions + + * TODO: + + * Dynamically generating wrapped files + + - Why I did + + - Section 1.3.5 mentions that during the compilation stage, cli_wrapper builder generates wrapped files, backs up the original files, and then packages all files into Debian. + + - However, this static method of generating wrapped files has the following drawbacks: + + * It is inconvenient for developers as they need to consider whether the cli_wrapper function is enabled and which files have been modified. + + * Moreover, while the cli_wrapper service is starting or stopping, there is a possibility of overwriting the contents that developers have modified. + + - How I did + + - When the cli_wrapper service is starting, the cli_wrapper builder will generate wrapped files. + + - Conversely, when the cli_wrapper service is stopping, all wrapped files will be deleted and then the main.py file will be restored. + +# 4. Reference + +N/A