[Ansible] Hướng dẫn viết Ansible Module bằng Bash Shell

Hướng dẫn viết Ansible Module bằng Bash ShellCuongquach.com | Chào các bạn, nếu bạn đọc bài viết này hẳn bạn đã biết sử dụng Ansible với các module Ansible để tự động hoá các tác vụ cần thiết trên các máy chủ remote như Linux, Windows, Cisco Router,… Hôm nay chúng ta sẽ cùng tìm hiểu cơ bản cách thức viết một Ansible Module bằng ngôn ngữ lập trình Bash Shell chạy trên các máy chủ remote Linux như thế nào nhé.

viết ansible module bằng bash shell

Có thể bạn quan tâm chủ đề khác
Sự khác biệt giữa Ansible Module Command và Module Shell
Tìm hiểu cấu hình Handler trong Ansible Playbook
Cấu hình Ansible Retry Task trong playbook
Các mẹo tăng tốc độ thực thi của Ansible

1. Ansible Module với Bash Shell

Một trong những điểm mạnh của Ansible đó là ngoài các module đang có, Ansible cho phép bạn lập trình viết thêm các Module Ansible tuỳ biến theo nhu cầu cá nhân với các ngôn ngữ phổ biến khác nhau như: python, go, bash shell, perl,… giúp bạn đạt được nội dung tự động hoá tác vụ riêng biệt của mình.

Vì vậy trong hôm nay chúng ta sẽ thử làm quen với việc lập trình code ansible module bằng Bash Shell thử xem nhé. Mình khuyến khích các bạn sử dụng Python để lập trình Ansible Module vì sự tích hợp tương thích với các Ansible Plugins. Nhưng khi làm quen hoặc thích đơn giản, không quan tâm nhiều xử lý output trả về,.. thì bạn có thể làm quen với Ansible Module Bash Shell.

(Chúng ta sẽ tìm hiểu cách lập trình code ansible module bằng Python ở bài viết khác)

Bash Shell ?
Bash Shell (shell script) là ngôn ngữ lập trình kịch bản phổ biến trên các hệ điều hành Unix/Linux. Một ngôn ngữ đơn giản để chạy các tác vụ Linux tự động mà bạn mong muốn. Mặc định khi đọc bài viết này là bạn đã biết lập trình Bash Shell rồi nhé.

Yêu cầu code ansible module bằng Bash Shell

  • Biết lập trình Bash Shell trên Linux.
  • Biết kiến thức về JSON.
  • Biết sử dụng Ansible.

2. Một Ansible Module Bash Shell tuỳ biến hoạt động sao ?

Ở phần này ta sẽ lưu ý cách mà Ansible hoạt động với các module (ví dụ với module bằng code bash shell chạy trên máy chủ remote Linux) như thế nào nhé :

  • Ansible khởi tạo file bash shell (python/go/perl/…) với module chỉ định và các tham số biến truyền vào. File bash shell này sẽ sử dụng cho tác vụ chạy trên máy chủ remote.
  • Kết nối SSH đến máy chủ remote khởi tạo thư mục tạm ở thư mục $HOME user kết nối.
  • Upload file code bash shell lên thư mục tạm.
  • Chạy file bash shell tác vụ và khi hoàn tất thì xoá thư mục tạm.
  • Lấy thông tin kết quả thực thi tác vụ với output định dạng json.

3. Chương trình chạy Ansible Module phía máy chủ remote

Ở phần trên như bạn đã biết thì Ansible sẽ copy file code module với các option truyền biến vào qua bên máy chủ remote để khởi chạy. Thì tất nhiên yêu cầu phía máy chủ phải có các chương trình cần thiết để chạy được code đó như ‘bash’, ‘python’, ‘go’,…

4. Thư mục chứa Ansible Module Bash Shell tuỳ biến

Chúng ta cần khai báo thư mục chứa các file code module đơn lẻ, sử dụng như một module Ansible tuỳ biến. Nếu bạn cài Ansible bằng package hoặc repository thì file cấu hình Ansible sẽ nằm ở đường dẫn như dưới :

/etc/ansible/ansible.cfg

Bạn cấu hình giá trị ‘library’ để chỉ định Ansible thư mục sẽ load các module tuỳ biến của bạn. Ví dụ mình dùng thư mục ‘/etc/ansible/custom_modules’ để chứa các file code Ansible Module tuỳ biến của mình.

# vi /etc/ansible/ansible.cfg
library = /etc/ansible/custom_modules/
# mkdir -p /etc/ansible/custom_modules/

5. Tên Ansible Module tuỳ biến

Một Ansible Module tuỳ biến nên có tên đơn giản, tránh trùng với các tên module hiện hành của Ansible, không nên để đuôi extension bất kì. Ví dụ:

cqinventory
cqbackup

6. Nhược điểm khi viết Anssible Module tuỳ biến bằng Bash Shell

  • Khá là khó khăn khi muốn tận dụng nguồn tài nguyên plugin của Ansible, vì Ansible core được viết chính bằng Python nên không thể tương thích hoặc tích hợp chung được.
  • Yêu cầu output trả về phải là định dạng JSON, bash shell xử lý định dạng JSON khá là phức tạp không linh động được.

7. Cấu trúc cần nắm khi code ansible modules với Bash Shell

Input Ansible Module

Thường khi cấu hình Ansible Playbook bạn sẽ thấy các biến như sau được truyền vào như dưới :

---
- hosts: collectors_init
  gather_facts: false
  become: yes
    tasks:
    - name: "Create User Apps - Cuongquach"
      user:
        name: "apps"
        state: present
        shell: "/sbin/nologin"
        comment: “Cuongquach-user"
        createhome: no

Các biến này có dạng ‘key=value’, trong code bash shell thì bạn sẽ dùng option dưới  trong file module code bash shell để load tất cả các biến truyền vào bash shell trong suốt quá trình chạy Ansible Module tuỳ biến bash shell của bạn.

source $1

Ví dụ, bạn có 2 option khi sử dụng module:

src=/home/cuongquach/ action=backup

thì khi sử dụng ‘source $1’, nó sẽ tạo ra 2 biến

src="/home/cuongquach/“
action=“backup"

Bạn sẽ gọi các biến này trong script như bình thường khi code bash shell :

echo $src
echo $action

Output Ansible Module

Output trả về cho Ansible từ module của bạn sau khi chạy xong ở máy chủ remote phải là định dạng JSON. Nếu bạn trả về bất kì thứ gì không phải dịnh dạng JSON hoặc Ansible không thể phân tích được output JSON của bạn thì Ansible sẽ coi Module đó đã chạy thất bại. Điều này có nghĩa trong code Bash Shell của bạn cần in ra nội dung json cho Ansible. (Bạn có thể tìm hiểu trước về JSON trên Google nếu chưa biết)

Trong nội dung output JSON thì bạn cần quan tâm tồn tại ít nhất hai key như sau:

  • changed: giá trị này thể hiện việc thực thi các tác vụ module của bạn đã thành công. Set giá trị ‘true‘ nếu các thay đổi thành công, set giá trị ‘false‘ nếu không có gì thay đổi.
  • msg: trả về bất kì chuỗi thông tin nào mà bạn muốn đính kèm.

Bên cạnh đó bạn có thể chèn thêm rất nhiều giá trị key=value mà bạn mong muốn khác vào output JSON cho Ansible từ Module tuỳ biến của bạn. Giúp bạn kiểm soát được các thông tin bạn muốn.

Tham khảo một số giá trị biến trả về phổ biến khác của Ansible: LINK

Ví dụ, code bạn sau cùng nên in ra JSON Output như sau. Các tiến trình trong khi chạy không nên để in ra màn hình làm hư định dạng output, có thể redirect stdout stderr vào /dev/null .

echo '{"changed": true, "msg": "Backup thanh cong"}'

hoặc

echo "{\"changed\": true, \"msg\": \"Backup thanh cong\"}"

hoặc

msg="Backup thanh cong"
printf '{"changed": true, "msg": "%s"}' "$msg"

Lưu ý:

  • Nếu bạn đã tìm hiểu về JSON rồi thì luôn luôn nhớ là giá trị key hay tên biến luôn phải nằm trong dấu ngoặc kép “..” .
  • Value của key nếu là chuỗi thì phải nằm trong dấu ngoặc kép luôn; còn nếu là số hoặc boolean (true hoặc false), lists, dictionaries thì không cần dấu ngoặc kép.

Thực hành 1 – Ansible Module Bash Shell Inventory

Với phần này chúng ta sẽ đến với việc code một file Ansible Module Bash Shell với nội dung đơn giản là đi thống kê thông tin của máy chủ remote Linux mà bạn đang chạy.

Chúng ta sẽ truyền một tham số theo yêu cầu ví dụ code thứ 1:

  • option: nếu bạn có xử lý nhiều function tác vụ khác nhau thì truyền vào đây.

File code mẫu Ansible Module Bash Shell Inventory sẽ nằm ở :

/etc/ansible/custom_modules/cqinventory

Code mẫu Ansible Module Bash Shell Inventory :

#!/bin/bash
#Author: CuongQuach
#Homepage: https://cuongquach.com/
#Module ansible bash shell - lab

function guess_distro_os(){
    [ -f /etc/redhat-release ] && awk '{print ($1,$3~/^[0-9]/?$3:$4)}' /etc/redhat-release && return
    [ -f /etc/os-release ] && awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release && return
    [ -f /etc/lsb-release ] && awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release && return
}

function get_info_os(){
    cpu_name="$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//' | awk '$1=$1')"
    cpu_cores="$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)"
    mem_total="$(free -m | awk '/Mem/ {print $2}')"
    swap_total=$( free -m | awk '/Swap/ {print $2}' )
    os_distro=$(guess_distro_os)
    os_arch=$(uname -m)
    os_lbit=$(getconf LONG_BIT )
    os_kern=$(uname -r)
    os_hostname="$(hostname)"
    date=$(date)

    #Change result variable
    changed_result="false"
    msg_result="Get information about this host $os_hostname successful."

    #Json result summary
    info_result="{
        \"cpu_name\": \"${cpu_name}\",
        \"cpu_cores\": \"${cpu_cores}\",
        \"mem_total\": \"${mem_total}\",
        \"swap_total\": \"${swap_total}\",
        \"os_distro\": \"${os_distro}\",
        \"os_arch\": \"${os_arch}\",
        \"os_lbit\": \"${os_lbit}\",
        \"os_kern\": \"${os_kern}\",
        \"os_hostname\": \"${os_hostname}\",
        \"date\": \"${date}\"
    }"
    info_result="$(echo $info_result | tr -d '\n')"
}

#################
# Main function #
#################

#Load agruments
source $1

if [ -z "${option}" ];then
    printf '{"failed": true, "msg": "missing required arguments: option"}'
    exit 1
fi

#Default variables
changed_result="false"
msg_result=""
info_result=""

case $option in
    os)
        get_info_os
        ;;
    *)
        printf '{"failed": true, "msg": "invalid option: %s"}' "$option"
        exit 1
        ;;
esac

#Return final result to ansible
printf '{"changed": %s, "msg": "%s", "info": %s}' "$changed_result" "$msg_result" "$info_result"

exit 0

Chạy thử Ansible Module Bash Shell Inventory

Khai báo host cho Ansible trước nhé (còn setup ssh key thì bạn tự làm nhé).

# vi /etc/ansible/hosts
[servers]
demo-host-cuongquach ansible_host=192.168.1.1

+ Ansible Ad-hoc Command

Giờ chạy thử Ansible Module mới ‘cqinventory’ bằng Ad-hoc lệnh ansible thử. Với một lần truyền sai tham số ‘option’ và một lần truyền đúng.

# ansible -i /etc/ansible/hosts servers -m cqinventory -a 'option=ip'
collector-cs | FAILED! => {
    "changed": false,
    "failed": true,
    "msg": "invalid option: ip"
}

# ansible -i /etc/ansible/hosts servers -m cqinventory -a 'option=os'
demo-host-cuongquach | SUCCESS => {
    "changed": false,
    "info": {
        "cpu_cores": "8",
        "cpu_name": "Intel Core Processor (Haswell, no TSX)",
        "date": "Fri Sep 21 16:21:05 ICT 2018",
        "mem_total": "7820",
        "os_arch": "x86_64",
        "os_distro": "CentOS 7.2.1511",
        "os_hostname": "DEMO-CUONGQUACH",
        "os_kern": "3.10.0-693.21.1.el7.x86_64",
        "os_lbit": "64",
        "swap_total": "2047"
    },
    "msg": "Get information about this host DEMO-CUONGQUACH successful."
}

+ Ansible Playbook

Khai báo một task playbook đơn giản sử dụng module ansible bash shell mới của chúng ta. Chúng ta sẽ thêm option debug để in ra màn hình thông tin key ‘info‘ của chúng ta.

# vi /etc/ansible/check-host.yml
---
- hosts: servers
  gather_facts: false
  become: yes
  tasks:
  - name: "Show information of hosts"
    cqinventory:
      option: os
    register: inventory_var

  - debug: msg="{{ inventory_var.info }}"

Giờ chạy playbook mới tạo nào.

# ansible-playbook -s /etc/ansible/check-host.yml -i /etc/ansible/hosts
PLAY [servers] **************************************************************************************************************************************************************

TASK [Show information of hosts] **************************************************************************************************************************************************
ok: [demo-host-cuongquach]

TASK [debug] **********************************************************************************************************************************************************************
ok: [demo-host-cuongquach] => {
    "msg": {
        "cpu_cores": "8",
        "cpu_name": "Intel Core Processor (Broadwell)",
        "date": "Fri Sep 21 23:03:18 +07 2018",
        "mem_total": "7821",
        "os_arch": "x86_64",
        "os_distro": "CentOS 7.4.1708",
        "os_hostname": "TOS-V2-Proxy-CS",
        "os_kern": "3.10.0-327.el7.x86_64",
        "os_lbit": "64",
        "swap_total": "2047"
    }
}

PLAY RECAP ************************************************************************************************************************************************************************
demo-host-cuongquach               : ok=2    changed=0    unreachable=0    failed=0

Lưu ý:
– Bạn sẽ thấy là in ra output kiểu JSON trong khá xấu. Mong muốn in ra output đẹp như module ‘command’ hoặc ‘shell’ của Ansible, thì bạn phải code module ansible tuỳ biến của bạn bằng Python. Lúc đó mới có thể sử dụng một số plugin ansible như callback hỗ trợ.

Thực hành 2 – Ansible Module Bash Shell Backup

Với phần thực hành số 2 này, chúng ta sẽ đến với việc code một file Ansible Module Bash Backup với tác vụ đơn giản là nén zip một thư mục hoặc file mong muốn rồi lưu ở file đích.

Chúng ta sẽ truyền ba tham số theo yêu cầu ví dụ code thứ 2:

  • source: source thư mục hoặc file mà bạn cần backup.
  • dest: file đích mà bạn muốn lưu trữ.
  • action: hành vi bạn muốn thực hiện, nếu bạn code bash shell xử lý nhiều tình huống.

File code mẫu Ansible Module Bash Shell Backup sẽ nằm ở :

/etc/ansible/custom_modules/cqbackup

Code mẫu Ansible Module Bash Shell Backup:

#!/bin/bash
#Author: CuongQuach
#Homepage: https://cuongquach.com/
#Module ansible bash shell - lab

function backup_process(){
    if [[ -d ${source} ]];then
        # Remove dest backup file
        if [[ -f ${dest} ]];then
            rm -f ${dest}
        fi

        # Backup zip source
        zip -r ${dest} ${source} &> /dev/null
        checksum=$(md5sum ${dest})
    fi

    changed_result="true"
    msg_result="Zip backup source $source successful."

    #Json result summary
    info_result="{
        \"source_backup\": \"${source}\",
        \"dest_backup\": \"${dest}\",
        \"checksum_backup\": \"${checksum}\"
    }"
    info_result="$(echo $info_result | tr -d '\n')"
}

#################
# Main function #
#################

#Load agruments
source $1

if [ -z "${source}" ];then
    printf '{"failed": true, "msg": "missing required arguments: source"}'
    exit 1
fi

if [ -z "${dest}" ];then
    printf '{"failed": true, "msg": "missing required arguments: dest"}'
    exit 1
fi

if [ -z "${action}" ];then
    printf '{"failed": true, "msg": "missing required arguments: action"}'
    exit 1
fi

#Default variables
changed_result="false"
msg_result=""
info_result=""

case $action in
    backup)
        backup_process
        ;;
    *)
        printf '{"failed": true, "msg": "invalid action: %s"}' "$action"
        exit 1
        ;;
esac

#Return final result to ansible
printf '{"changed": %s, "msg": "%s", "info": %s}' "$changed_result" "$msg_result" "$info_result"

exit 0

Chạy thử Ansible Module Bash Shell Backup

Khai báo host cho Ansible trước nhé.

# vi /etc/ansible/hosts
[servers]
demo-host-cuongquach ansible_host=192.168.1.1

+ Ansible Ad-hoc Command

Giờ chạy thử Ansible Module mới ‘cqbackup’ bằng Ad-hoc lệnh ansible thử.

# ansible -i /etc/ansible/hosts servers -m cqbackup -a 'source=/home/cuongqc/ dest=/tmp/cuongqc-home.zip action=backup'
demo-host-cuongquach | SUCCESS => {
    "changed": true,
    "info": {
        "checksum_backup": "c8d11bdbdb1b8d86923f2e9a32414528 /tmp/cuongqc-home.zip",
        "dest_backup": "/tmp/cuongqc-home.zip",
        "source_backup": "/home/cuongqc/"
    },
    "msg": "Zip backup source /home/cuongqc/ successful."
}

+ Ansible Playbook

Khai báo một task playbook đơn giản sử dụng module ansible bash shell mới của chúng ta. Chúng ta sẽ thêm option debug để in ra màn hình thông tin key ‘info‘ của chúng ta.

# vi /etc/ansible/backup-home.yml
---
- hosts: servers
  gather_facts: false
  become: yes
  tasks:
  - name: "Backup folder home"
    cqbackup:
      source: /home/cuongqc/
      dest: /tmp/cuongqc-home.zip
      action: backup
    register: backup_var

  - debug: msg="{{ backup_var.info }}"

Giờ chạy playbook ‘backup-home‘ mới tạo nào.

# ansible-playbook -s /etc/ansible/backup-home.yml -i /etc/ansible/hosts

PLAY [servers] **************************************************************************************************************************************************************

TASK [Backup folder home] *********************************************************************************************************************************************************
changed: [demo-host-cuongquach]

TASK [debug] **********************************************************************************************************************************************************************
ok: [demo-host-cuongquach] => {
    "msg": {
        "checksum_backup": "d675caeeb2a242bed80f3e14327e8163 /tmp/cuongqc-home.zip",
        "dest_backup": "/tmp/cuongqc-home.zip",
        "source_backup": "/home/cuongqc/"
    }
}

PLAY RECAP ************************************************************************************************************************************************************************
demo-host-cuongquach               : ok=2    changed=1    unreachable=0    failed=0

Bạn SSH vào máy chủ remote host vừa được chạy, kiểm tra xem có file backup zip và kiểm tra checksum file zip xem khớp với output JSON từ Module Ansible ‘cqbackup’ của chúng ta khớp chưa nhé.

Tổng kết

Vậy là bạn đã biết cách viết một Ansible Module đơn giản bằng Bash Shell rồi phải không nào. Chúng ta sẽ sớm đến với nội dung về cách code ansible module bằng Python ở bài khác nhé.

Nguồn: https://cuongquach.com/

Previous articleMức lương trung bình của kiến trúc sư bảo mật mạng năm 2018
Next articleMCSA 2012: Domain Network – Home Folder và User Profile
Bạn đang theo dõi website "https://cuongquach.com/" nơi lưu trữ những kiến thức tổng hợp và chia sẻ cá nhân về Quản Trị Hệ Thống Dịch Vụ & Mạng, được xây dựng lại dưới nền tảng kinh nghiệm của bản thân mình, Quách Chí Cường. Hy vọng bạn sẽ thích nơi này !