Tab подстановка хостов из Ansible inventory для ansible, ssh, scp, rsync
Приветствую!

Если много работаешь с хостами через Ansible, быстро надоедает печатать их имена целиком или открывать инвентарь, чтобы скопировать имя. Мне очень хотелось получать список доступных хостов через нажатие Tab.

Ниже инструкция как настроить автоподстановку хостов из Ansible inventory с помощью клавиши Tab при использовании команд ssh, scp, rsync, ansible и ansible-playbook.

Вводные данные

Условно есть ~/Ansible/inventory.yml:

YAML
all:
  children:
    test:
      children:
        test_hosts_1:
          hosts:
            test-1.r4ven.me:
              ansible_host: 192.168.122.140

            test-2.r4ven.me:
              ansible_host: 192.168.122.141
              ansible_port: 22

            test-3.r4ven.me:
              ansible_host: 192.168.122.142
              ansible_port: 22

        test_hosts_2:
          hosts:
            test-4.r4ven.me:
              ansible_host: 192.168.122.143

            test-5.r4ven.me:
              ansible_host: 192.168.122.144
              ansible_port: 22

            test-6.r4ven.me:
              ansible_host: 192.168.122.145
              ansible_port: 22
Нажмите, чтобы развернуть и увидеть больше

И ~/.ssh/config

BASH
Host orangepi
    HostName 10.10.10.12
    User orange
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    Compression yes

Host router
    HostName 10.10.10.13
    User root
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    Compression yes

Host proxmox
    HostName 10.10.10.15
    User root
    Port 22
    IdentityFile ~/.ssh/id_ed25519
Нажмите, чтобы развернуть и увидеть больше

Gif-демо

Посмотрите короткое gif-демо, чтобы наглядно понять о чем речь:

Сниппеты для Zsh и Bash

Если вы используете замечательный ZSH, то открываем .zshrc с помощью vim и добавляем в него следующий сниппет:

BASH
vim ~/.zshrc
Нажмите, чтобы развернуть и увидеть больше
BASH
_ansible_inventory_hosts() {
    local inv="${ANSIBLE_INVENTORY:-$HOME/Ansible/inventory.yml}"
    [[ -r "$inv" ]] || return 0
    grep -oP '^\s*\K[^:#\s]+(?=:\s*$)' "$inv" 2>/dev/null \
        | grep -vxE 'all|hosts|children|vars|ansible_.*' | sort -u
}

if [[ -r "${ANSIBLE_INVENTORY:-$HOME/Ansible/inventory.yml}" ]]; then
    zstyle -e ':completion:*:(ssh|scp|sftp|rsync):*' hosts 'reply=(
        ${(f)"$(_ansible_inventory_hosts)"}
        ${=${${${(M)${(f)"$(cat /dev/null ~/.ssh/config(N))"}:#(#i)Host *}#(#i)Host }:#*[*?]*}}
        ${(@)${=${${(M)${(f)"$(cat /dev/null {/etc/ssh/ssh_,$HOME/.ssh/}known_hosts(N))"}:#[^|#]*}%%[[:space:]]*}//,/ }}
    )'

    _ansible_hosts_completion() {
        local -a hosts
        if [[ $words[CURRENT-1] == (-l|--limit) ]] ||
           [[ $service == ansible && $CURRENT -eq 2 && $words[CURRENT] != -* ]]; then
            hosts=( ${(f)"$(_ansible_inventory_hosts)"} )
            if (( ${#hosts} )); then
                _describe 'ansible hosts' hosts
                return
            fi
        fi
        _default
    }
    (( $+functions[compdef] )) && compdef _ansible_hosts_completion ansible ansible-playbook
fi
Нажмите, чтобы развернуть и увидеть больше

Если ваша оболочка не менее прекрасный Bash, то редактируем .bashrc:

BASH
vim ~/.bashrc
Нажмите, чтобы развернуть и увидеть больше
BASH
_ansible_inventory_hosts() {
    local inv="${ANSIBLE_INVENTORY:-$HOME/Ansible/inventory.yml}"
    [[ -r "$inv" ]] || return 0

    grep -oP '^\s*\K[^:#\s]+(?=:\s*$)' "$inv" 2> /dev/null \
        | grep -vxE 'all|hosts|children|vars|ansible_.*' | sort -u
}

if [[ -r "${ANSIBLE_INVENTORY:-$HOME/Ansible/inventory.yml}" ]]; then
    declare -A _ansible_orig_comp

    _ansible_hosts_augmented() {
        local fn=${_ansible_orig_comp[$1]}
        if [[ -n $fn ]] && declare -F "$fn" &>/dev/null; then
            "$fn" "$@"
        fi

        local cur=${COMP_WORDS[COMP_CWORD]}
        if [[ $cur != -* ]]; then
            local hosts
            hosts=$(_ansible_inventory_hosts)
            [[ -n $hosts ]] && COMPREPLY+=( $(compgen -W "$hosts" -- "$cur") )
        fi
    }

    _ansible_augment_hosts_completion() {
        local cmd spec fn
        
        for cmd in "$@"; do
            if ! complete -p "$cmd" &>/dev/null; then
                if declare -F _comp_load &>/dev/null; then
                    _comp_load "$cmd" 2>/dev/null
                elif declare -F _completion_loader &>/dev/null; then
                    _completion_loader "$cmd" 2>/dev/null
                fi
            fi
            
            fn=""
            spec=$(complete -p "$cmd" 2>/dev/null) || spec=""
            
            if [[ $spec =~ \ -F\ ([^\ ]+) ]]; then
                fn=${BASH_REMATCH[1]}
            fi
            
            _ansible_orig_comp[$cmd]=$fn
            complete -o default -F _ansible_hosts_augmented "$cmd"
        done
    }

    _ansible_augment_hosts_completion ssh scp sftp rsync
    _ansible_cli_completion() {
        local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
        COMPREPLY=()
        
        if [[ $prev == -l || $prev == --limit ]] ||
           [[ $1 == ansible && $COMP_CWORD -eq 1 && $cur != -* ]]; then
            COMPREPLY=( $(compgen -W "$(_ansible_inventory_hosts)" -- "$cur") )
        fi
    }

    complete -o default -F _ansible_cli_completion ansible ansible-playbook
fi
Нажмите, чтобы развернуть и увидеть больше

Обратите внимание, что важно корректно указать путь до вашего файла инвентаря. По умолчанию сниппет ищет inventory по пути $HOME/Ansible/inventory.yml. Если путь другой, то можете переопределить его в самом сниппете, либо через переменную ANSIBLE_INVENTORY:

BASH
export ANSIBLE_INVENTORY=/path/to/your/inventory.yml
Нажмите, чтобы развернуть и увидеть больше

Применение изменений в текущей сессии

В случае Zsh:

BASH
exec zsh
Нажмите, чтобы развернуть и увидеть больше

В случае Bash:

BASH
exec bash
Нажмите, чтобы развернуть и увидеть больше

Completion загрузится автоматически при первом Tab.

Проверка работы

BASH
ssh <TAB><TAB>

scp <TAB><TAB>

rsync -e ssh ivan@<TAB><TAB>

ansible <TAB><TAB>

ansible-playbook -l <TAB><TAB>
Нажмите, чтобы развернуть и увидеть больше

Должно вывести список всех хостов из inventory плюс те, что в ~/.ssh/config и known_hosts. Если ничего не появляется - проверьте, что файл inventory читаемый и что там действительно есть хосты в формате YAML.

На практике использование данного сниппета сэкономит кучу времени, особенно если хостов много и названия длинные 🤯.

Почему код для bash раза в три длиннее zsh?

Авторские права

Автор: Иван Чёрный

Ссылка: https://r4ven.me/dots/tab-podstanovka-hostov-iz-ansible-inventory-dlya-ansible-ssh-scp-rsync/

Лицензия: CC BY-NC-SA 4.0

Использование материалов блога разрешается при условии: указания авторства/источника, некоммерческого использования и сохранения лицензии.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша