oeDeploy Plugin Development ​

1. Introduction to oeDeploy Plugins ​

oeDeploy plugins are components that provide automated deployment capabilities within the oeDeploy tool. They achieve automation by converting complex deployment processes into Ansible playbooks. A plugin may integrate multiple deployment actions (such as installation, uninstallation, and environment cleanup), each corresponding to one or more Ansible playbooks. All configurable parameters should be centralized to reduce both user learning costs and developer maintenance efforts.

2. Plugin Directory Structure ​

The plugin directory name, i.e., the plugin name, may include a version number (indicating the software version, not the plugin version).

The plugin directory contains the following:

File/DirectoryTypeDescription
config.yamlYAMLThe only configuration file exposed to users. Contains host-related configurations (IP, password, key, port, etc.) and software deployment settings.
main.yamlYAMLContains plugin information such as name, version, description, and the specific steps for each deployment action (action). Each step corresponds to a script or playbook (relative to the workspace directory).
docDirectoryCarries user documentation and other materials for the plugin. This directory is optional.
doc/readme.mdMarkdownUser documentation for the plugin. The plugin store reads this file and displays it on the interface, making it the most direct entry for users to obtain plugin information. It is strongly recommended to write this document.
workspaceDirectoryCarries all files, source code, binaries, scripts, playbooks, etc., used for software installation and deployment. The internal structure is not restricted.

Naming constraints for plugin_name:

  • The deployed software name is name, and its version is version.
  • plugin_name must follow one of these formats:
    • {name}-{version}
    • {name}-{version}-xxx
  • Example: For a Kubernetes deployment plugin (version 1.31.1), valid names include kubernetes-1.31.1, kubernetes-1.31.1-20240101, and kubernetes-1.31.1-offline-20240101.

3. main.yaml ​

The main.yaml file records key plugin information including: name, version, description, and action.

  • When users run the oedp info command, oedp reads and parses this file to display relevant information.
  • When users run the oedp run xxxx command, oedp executes the corresponding automation scripts based on the content in the action field.

3.1 Format Description ​

  • type indicates the plugin type, default value is app. This is a reserved field and currently not effective.
  • localhost_available indicates whether local deployment is supported. It can be true as long as it is not a "multi-node group deployment" scenario.
  • The action field contains one or more operations.
  • Each operation's key is the operation name, the description item is the introduction of the operation, and tasks carries the specific steps (a list) required to execute the operation. When the operation is triggered by the user, each step will be executed in order.
  • Each step contains the following fields:
    • name: Step name, required field.
    • playbook: Indicates using ansible-playbook to execute the corresponding script, the content is the path of the playbook (relative path based on the workspace directory). Required field. Currently only the playbook mode is supported.
    • vars: The path of the configuration file read when the script is executed (relative path based on the workspace directory). Optional field.
    • scope: Limits the nodes that need to execute this step, i.e., the host group (defined in config.yaml). Required field, usually defaults to all.
  • When the user executes the command line oedp run <action>, the oedp tool will execute all steps (tasks) under this operation one by one in order.
  • In playbook mode, each step is equivalent to the following command line:
bash
ansible-playbook <playbook>.yaml -i config.yaml -e @<vars>.yaml --limit <scope>

3.2 Example ​

yaml
name: kubernetes-1.31.1  # Plugin name
version: 1.0.0           # Plugin version
description: install kubernetes 1.31.1  # Plugin description
description_zh: 帮助开发者在本地快速部署kubernetes-1.31.1的oeDeploy插件                             # Chinese introduction
description_en: The oeDeploy plugin that helps developers quickly deploy kubernetes-1.31.1 locally. # English introduction
type: app                   # Reserved field, currently not effective
localhost_available: true   # Whether local deployment is supported, can be true as long as it is not "multi-node group deployment"
action:
  install:                              # Operation install
    description: install kubernetes     # Description of operation install
    tasks:
      - name: prepare for install       # Step 1 of operation install
        playbook: set-env.yaml           # Playbook file executed in the current step (relative path based on workspace directory)
        scope: all                      # Host group name executing the current step (defined in config.yaml)
      - name: install kubernetes        # Step 2 of operation install
        playbook: init-k8s.yaml          # Playbook file executed in the current step (relative path based on workspace directory)
        vars: variables.yaml             # Configuration file read in the current step (relative path based on workspace directory)
        scope: all                      # Host group name executing the current step (defined in config.yaml)
  delete:
    description: delete kubernetes
    tasks:
      - name: delete kubernetes
        playbook: delete-k8s.yaml
        scope: all
  clean:
    description: clean cache files
    tasks:
      - name: clean cache files
        playbook: clean-k8s.yaml
        scope: all
  • In this example, when the user executes the oedp run install command, the tool will execute the following commands in order: ansible-playbook set-env.yaml -i config.yaml --limit allansible-playbook init-k8s.yaml -i config.yaml -e @variables.yaml --limit all
  • Users can trigger corresponding operations via oedp run install, oedp run delete, oedp run clean command lines.

4. config.yaml ​

The configurable parameters during the deployment script execution are all carried in the same configuration file config.yaml, reducing the learning cost for users and maintenance cost for developers.

  • config.yaml contains node information (host groups) of the deployment environment, as well as parameter configurations related to software deployment.
  • config.yaml follows the configuration rules of Ansible inventory files, and the tool directly passes it as inventory when executing Ansible playbooks.
  • Note that config.yaml is the only configuration file exposed to ordinary users. It should be aesthetically pleasing, avoid ambiguity, and retain as detailed comments as possible. Ansible-related parameters should be uniformly placed at the front or back of the vars field.
  • In playbook mode, when the user triggers a deployment operation, the configuration in config.yaml is imported via the following command line:
bash
ansible-playbook <playbook>.yaml -i config.yaml -e @<vars>.yaml --limit <scope>

For relatively fixed configuration items that do not require user modification, they can be presented in <vars>.yaml instead of config.yaml.

4.1 Example 1: Local Deployment ​

Except for a few cases that can only be deployed locally, ansible_connection: local is generally not used.

yaml
all:
  hosts:
    localhost:
      ansible_connection: local
  vars:
    deepseek_version: 8b
    ollama_download_url:
      amd64: https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/files/ollama-linux-amd64.tgz
      arm64: https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/files/ollama-linux-arm64.tgz
    ollama_download_path: /root/tmp     # Target download path
    modelfile_download: https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/contrib/oedp/files/DeepSeek-R1-Distill-Llama-8B-Q4_K_M.gguf
    modelfile_download_path: /root/tmp  # Target download path
    # Model parameters
    parameter:
      temperature: 0.7
      top_p: 0.7
      top_k: 30
      num_ctx: 4096
      num_thread: 4    # Number of threads, recommended not to exceed CPU cores
      num_gpu: 0       # Number of GPUs, 0 for none, -1 for all.
    download_checksum: true        # Whether to enable sha256sum verification
    download_timeout: 600          # Timeout for downloading large files (seconds)
    download_retry: 6              # Retry count for downloading large files
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

4.2 Example 2: Single‑Node Deployment ​

yaml
all:
  hosts:
    host1:
      ansible_host: 127.0.0.1      # Remote IP
      ansible_port: 22             # Port
      ansible_user: root           # Username
      ansible_password: ""   # Password
  vars:
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

    kubectl_apply: pytorch-deployment.yaml
    namespace: pytorch-namespace
    replicas: 1
    containers:
      http:
        name: http-container
        image: hub.oepkgs.net/oedeploy/pytorch/pytorch:latest  # amd64
      workspace_mount: /tmp
    service:
      port: 8080
      target_port: 8080
      node_port: 30699
    training:
      epoch: 2   # Number of training epochs

4.3 Example 3: Multi‑Node Deployment ​

yaml
all:
  hosts:
    host1:
      ansible_host: 192.168.1.101
      ansible_port: 22
      ansible_user: root
      ansible_password: ""
      architecture: amd64          # e.g. [ amd64, arm64 ]
    host2:
      ansible_host: 192.168.1.102
      ansible_port: 22
      ansible_user: root
      ansible_password: ""
      architecture: amd64          # e.g. [ amd64, arm64 ]
  vars:
    version: 3.9.0
    download_url: https://get.helm.sh/
    temp_path: /tmp
    install_path: /usr/local/bin
    force_install: yes
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

4.4 Example 4: Multi‑Node Group Deployment ​

yaml
all:
  children:
    masters:
      hosts:
        HOST1:                             # e.g. 192.168.10.1
          ansible_host: 192.168.10.1       # e.g. 192.168.10.1
          ansible_port: 22
          ansible_user: root
          ansible_password: ""
          architecture: amd64              # e.g. [ amd64, arm64 ]
          oeversion: 24.03-LTS             # e.g. [ 22.03-LTS, 24.03-LTS ]
          runtime: docker                  # e.g. [ docker, containerd ]
    workers:
      hosts:
        HOST2:
          ansible_host: 192.168.10.2
          ansible_port: 22
          ansible_user: root
          ansible_password: ""
          architecture: amd64
          oeversion: 24.03-LTS
          runtime: docker
        HOST3:
          ansible_host: 192.168.10.3
          ansible_port: 22
          ansible_user: root
          ansible_password: ""
          architecture: amd64
          oeversion: 24.03-LTS
          runtime: docker
    new-workers:  # The new-workers field cannot be deleted
      hosts:
  vars:
    ansible_ssh_common_args: '-o StrictHostKeyChecking=no'

    init_cluster_force: "true"             # e.g. [ "true", "false" ]  Force cluster initialization
    remove_master_no_schedule_taints: "true"
    service_cidr: 10.96.0.0/16             # Service network segment
    pod_cidr: 10.244.0.0/16                # Pod IP network segment
    certs_expired: 3650                    # Certificate expiration time
    # lb_kube_apiserver_ip:
    lb_kube_apiserver_port: 8443
    has_deployed_containerd: "false"       # e.g. [ "true", "false" ]  Whether containerd is already deployed

    # The following parameters need to be consistent with host.ini during the build process, ignore if not involved
    kubernetes_version: 1.31.1
    calico_version: 3.28.2
    pause_image: "registry.k8s.io/pause:3.10"

5. workspace Directory ​

The workspace directory carries the main content of the deployment scripts, and its structure is not restricted.

  • Various files, source code, binaries, scripts, playbooks involved in main.yaml and config.yaml are all carried in the workspace directory, and developers can customize subdirectories within it.
  • The workspace directory is considered the root directory of the entire plugin execution process.

5.1 Example ​

Here is an oeDeploy plugin for deploying an nginx service.

main.yaml content:

yaml
name: nginx
version: 1.25.3
description: Install and configure nginx web server
action:
  install:
    description: Install nginx and start service
    tasks:
      - name: Install nginx via yum
        playbook: install.yaml
        scope: all
      - name: Configure and start nginx
        playbook: configure.yaml
        scope: all

config.yaml content:

yaml
all:
  hosts:
    target-host:
      ansible_host: 127.0.0.1
      ansible_port: 22
      ansible_user: root # It is recommended to use the root user or a user with sudo privileges
      ansible_password: ""
  vars:
    nginx_port: 80
    ansible_ssh_common_args: "-o StrictHostKeyChecking=no"

The workspace directory contains the following files:

  1. install.yaml: Used to install nginx and other dependent software
  2. configure.yaml: Used to configure nginx parameters and start the nginx service
  3. nginx.conf.j2: Serves as a template for configuring nginx parameters. Although not directly reflected in main.yaml, it will be used in configure.yaml.

install.yaml content:

yaml
---
- hosts: all
  become: yes
  tasks:
    - name: Install nginx
      yum:
        name: nginx
        state: present
    - name: Ensure nginx is running and enabled
      service:
        name: nginx
        state: started
        enabled: yes

configure.yaml content:

yaml
---
- hosts: all
  become: yes
  tasks:
    - name: Generate nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
    - name: Validate nginx configuration
      command: nginx -t
      register: nginx_test
      changed_when: false
      notify: restart nginx
    - name: Restart nginx
      service:
        name: nginx
        state: restarted
      when: nginx_test.rc == 0

nginx.conf.j2 content:

jinja2
worker_processes auto;
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       {{ nginx_port }};
        server_name  localhost;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}

6. Plugin Packaging ​

To publish a plugin for other users, you need to package the plugin into {plugin_name}.tar.gz format. The compressed file name must match the plugin name, and extraction should produce only one directory. For example:

text
kubernetes-1.31.1.tar.gz
`-- kubernetes-1.31.1
    |-- main.yaml
    |-- config.yaml
    `-- workspace/

Usually, you can use the following command line to complete the packaging:

bash
tar zcvf kubernetes-1.31.1.tar.gz kubernetes-1.31.1/

7. Plugin Development Tips ​

  • If the Ansible deployment operation requires sudo privileges (e.g., installing software packages), you need to add become: yes at an appropriate position in the playbook script. If necessary, you can require only the root user password in config.yaml.
  • If the Ansible script involves file copying, modification, deletion, creation, etc., you must install the package python3-libselinux on the target node beforehand. Based on experience, this package is almost always necessary.
  • To avoid deployment interruption due to strict HostKey verification, it is recommended to add the configuration item ansible_ssh_common_args: '-o StrictHostKeyChecking=no' in the vars field of config.yaml.
  • When printing prompt messages with the msg module, traditional line breaks will output \n, affecting readability. Therefore, you should use a string list form (as shown below).
yaml
- name: Print already installed message
  debug:
    msg:
      - "========================================"
      - "CANN Toolkit 开发套件包已经安装"
      - "======================================="
  • If the value of a playbook configuration item is a string, be sure to enclose it in quotes. For example, the meanings of these two cases are different:
yaml
# config.yaml
vars:
  sendfile: on   # This may be interpreted as a boolean True
  sendfile: "on" # This is the string "on"

8. Performance Tuning Tips ​

  • Enable SSH connection reuse to reduce the time delay of each operation, which significantly optimizes performance in environments with high SSH connection latency (generally not required, only used in performance‑sensitive scenarios). Add configuration items similar to the following in the vars field of config.yaml:
yaml
# SSH connection reuse parameters: ControlMaster=auto enables connection reuse, ControlPersist=60s keeps the connection for 60 seconds, ConnectTimeout=30 sets connection timeout to 30 seconds
ansible_ssh_args: '-o ControlMaster=auto -o ControlPersist=60s -o ConnectTimeout=30'
# Enable Ansible pipelining acceleration to reduce SSH connection count
ansible_pipelining: True
# Enable SSH pipelining acceleration to improve file transfer efficiency
ansible_ssh_pipelining: True
# Set the number of parallel tasks
ansible_forks: 10
  • Add gather_facts: false at an appropriate position in the playbook script to turn off information collection before command execution (generally not recommended to modify, only used in performance‑sensitive scenarios).
  • Use the synchronize module instead of copy module, which requires pre‑installation of the rsync package (can avoid repeated copying, use as needed):
yaml
- name: install python3-libselinux and rsync
  yum:
    name:
      - python3-libselinux
      - rsync
    state: present

- name: Sync prepare.sh to hosts using rsync
  synchronize:
    src: prepare.sh
    dest: "{{ dir_path }}"
    rsync_opts: "--chmod=755"