Ansible playbook 用于验证目录并在非空时检索服务器主机名或IP。

huangapple go评论76阅读模式
英文:

Ansible playbook to verify directory and retrieve server hostname or IP if not empty

问题

我想使用Ansible检查一个目录是否为空。
如果目录不为空,我想要获取服务器的主机名或IP地址。
有人可以帮助我吗?
谢谢!

我尝试进行了一个测试。

- hosts: all
  tasks:
  - find :
      paths: /tmp/foo
      file_type: all
      hidden: true
    register: out

  - fail :
      msg: /tmp/foo目录不为空
    when: out.matched != 0
英文:

I want to check a directory with ansible if it's not empty.
I want to have the server hostname or IP address if the directory is not empty.
Can anyone help me?
Thanks!

I tried to make a test.

- hosts: all
  tasks:
  - find :
      paths: /tmp/foo
      file_type: all
      hidden: true
    register: out

  - fail :
      msg: the /tmp/foo directory is not empty
    when: out.matched != 0

答案1

得分: 1

"Check a directory and list servers if empty."

答: 有两种非常有用的模式,你应该逐步了解并理解细节。

例如,给定两个远程主机,第二个主机上的目录是空的

shell> ssh admin@test_11 find /tmp/test_dir
/tmp/test_dir
/tmp/test_dir/bar
/tmp/test_dir/foo
shell> ssh admin@test_13 find /tmp/test_dir
/tmp/test_dir

查找文件

    - find:
        paths: /tmp/test_dir
        file_type: any
        hidden: true
      register: out
  1. 创建一个事实的字典
  report: "{{ dict(ansible_play_hosts|
                   zip(ansible_play_hosts|
                       map('extract', hostvars, ['out', 'matched']))) }}"

得到

  report:
    test_11: 2
    test_13: 0
  1. 从字典中选择/拒绝属性,并获取满足条件的属性的名称
  empty: "{{ report|dict2items|
             rejectattr('value')|
             map(attribute='key') }}"

得到你想要的结果

  empty:
    - test_13

完整的用于测试的示例playbook

- hosts: all

  vars:

    report: "{{ dict(ansible_play_hosts|
                     zip(ansible_play_hosts|
                         map('extract', hostvars, ['out', 'matched']))) }}"
    empty: "{{ report|dict2items|
               rejectattr('value')|
               map(attribute='key') }}"

  tasks:

    - find:
        paths: /tmp/test_dir
        file_type: any
        hidden: true
      register: out

    - debug:
        var: report
      run_once: true

    - debug:
        var: empty
      run_once: true

英文:

Q: "Check a directory and list servers if empty."

A: There are two very useful patterns you should go step by step through and understand the details.

For example, given two remote hosts, the directory is empty on the second one

shell> ssh admin@test_11 find /tmp/test_dir
/tmp/test_dir
/tmp/test_dir/bar
/tmp/test_dir/foo
shell> ssh admin@test_13 find /tmp/test_dir
/tmp/test_dir

Find the files

    - find:
        paths: /tmp/test_dir
        file_type: any
        hidden: true
      register: out
  1. Crete a dictionary of the facts
  report: "{{ dict(ansible_play_hosts|
                   zip(ansible_play_hosts|
                       map('extract', hostvars, ['out', 'matched']))) }}"

gives

  report:
    test_11: 2
    test_13: 0
  1. Select/reject attributes from a dictionary and get the names of the attributes that match the condition
  empty: "{{ report|dict2items|
             rejectattr('value')|
             map(attribute='key') }}"

gives what you want

  empty:
    - test_13

<hr>

<sup>

Example of a complete playbook for testing

- hosts: all

  vars:

    report: &quot;{{ dict(ansible_play_hosts|
                     zip(ansible_play_hosts|
                         map(&#39;extract&#39;, hostvars, [&#39;out&#39;, &#39;matched&#39;]))) }}&quot;
    empty: &quot;{{ report|dict2items|
               rejectattr(&#39;value&#39;)|
               map(attribute=&#39;key&#39;) }}&quot;

  tasks:

    - find:
        paths: /tmp/test_dir
        file_type: any
        hidden: true
      register: out

    - debug:
        var: report
      run_once: true

    - debug:
        var: empty
      run_once: true

</sup>

答案2

得分: 0

> 我想用Ansible检查一个目录是否为空。

既然未定义“空”是什么意思,以及是否可能存在零字节文件,我理解你的问题是你对文件夹大小感兴趣。

根据在条件中使用文件夹大小的内容,stat 模块返回的 size 属性 不会 告诉你文件夹内容的大小!它只报告目录条目的大小,这可能取决于诸如目录中包含的文件数量等多种因素。

如果你想计算目录中包含的数据量,你可以进一步参考这里的回答Ansible:需要查找文件夹/目录或文件的通用解决方案

由于这需要使用 commandshell,这在一般情况下不被推荐,可能可以将任务转换为自定义模块。为此,创建一个用 Bash 或 Shell 编写的自定义模块似乎是最好的方法。

以下是一个概念/示例模块,它接受一个字符串参数(path),并测试它是目录还是文件。然后提供目录及其所有文件(包括子目录)的大小,或仅文件大小作为结果集。

概念/示例模块 size.sh

#!/bin/sh

exec 3&gt;&amp;1 4&gt;&amp;2 &gt; /dev/null 2&gt;&amp;1

# 创建函数

function return_json() {

    exec 1&gt;&amp;3 2&gt;&amp;4

    jq --null-input --monochrome-output \
        --arg changed &quot;$changed&quot; \
        --arg rc &quot;$rc&quot; \
        --arg stdout &quot;$stdout&quot; \
        --arg stderr &quot;$stderr&quot; \
        --arg directory &quot;$directory&quot; \
        --arg file &quot;$file&quot; \
        --arg msg &quot;$msg&quot; \
       &#39;$ARGS.named&#39;

    exec 3&gt;&amp;1 4&gt;&amp;2 &gt; /dev/null 2&gt;&amp;1

}

# 模块辅助函数

function dirsize() {

    du -sh &quot;$path&quot; | sort -h

}

function filesize() {

    du -sh &quot;$path&quot;

}

# 模块主要逻辑

function module_logic() {

    if [ -f &quot;$path&quot; ]; then
        filesize $path
        file=&quot;true&quot;
    elif [ -d &quot;$path&quot; ]; then
        dirsize $path
        directory=&quot;true&quot;
    else
        stderr=&quot;无法访问:文件或目录不存在或权限被拒绝。&quot;
        rc=1
    fi

}

# 设置默认值

changed=&quot;false&quot;
rc=0
stdout=&quot;&quot;
stderr=&quot;&quot;
directory=&quot;false&quot;
file=&quot;false&quot;
msg=&quot;&quot;

source &quot;$1&quot;

# 检查先决条件

if ! which jq &amp;&gt;/dev/null; then
    exec 1&gt;&amp;3 2&gt;&amp;4
    printf &quot;{ \&quot;changed\&quot;: \&quot;$changed\&quot;,
           \&quot;rc\&quot;: \&quot;1\&quot; ,
           \&quot;stdout\&quot;: \&quot;\&quot;,
           \&quot;stderr\&quot;: \&quot;找不到命令:此模块需要在目标主机上安装 'jq'。正在退出... \&quot;,
           \&quot;directory\&quot;: \&quot;false\&quot; ,
           \&quot;file\&quot;: \&quot;false\&quot;,
           \&quot;msg\&quot;: \&quot;\&quot;}&quot;
    exit 127
fi

if ! grep -q &quot;Red Hat Enterprise Linux&quot; /etc/redhat-release; then
    stderr=&quot;不支持此操作:未找到 Red Hat Enterprise Linux。正在退出...&quot;
    rc=95

    return_json

    exit $rc

fi

# 验证参数键和值

if [ -z &quot;$path&quot; ]; then
    stderr=&quot;无效参数:未提供参数路径。正在退出...&quot;
    rc=22

    return_json

    exit $rc
fi

if [[ $path == *[&#39;!:*&#39;]* ]]; then
    stderr=&quot;无效参数:值路径包含无效字符。正在退出...&quot;
    rc=22

    return_json

    exit $rc
fi

if [ ${#path} -ge 127 ]; then
    stderr=&quot;参数太长:值路径字符太多。正在退出...&quot;
    rc=7

    return_json

    exit $rc
fi

# 主要

module_logic $path

changed=&quot;false&quot;
msg=$(module_logic $path | sed &#39;s/\t/ /g&#39;)

return_json

exit $rc

测试Playbook size.yml

---
- hosts: test
  become: false
  gather_facts: false

  tasks:

  - name: 获取路径后面的大小
    size:
      path: /root/anaconda-ks.cfg
    register: result
    ignore_errors: true

  - name: 获取路径后面的大小
    size:
      path: &#39;&#39;
    register: result
    ignore_errors: true

  - name: 获取路径后面的大小
    size:
    register: result
    ignore_errors: true

  - name: 获取路径后面的大小
    size:
      path: &quot;/home/{{ ansible_user }}/*&quot;
    register: result
    ignore_errors: true

  - name: 获取路径后面的大小
    size:
      path: &quot;/home/{{ ansible_user }}/1111111111/2222222222/3333333333/4444444444/5555555555/6666666666/

<details>
<summary>英文:</summary>

&gt; _I want to check a directory with Ansible if it&#39;s not empty._

Since it is not defined what empty means and if there could be [zero byte files](https://stackoverflow.com/a/4955006/6771046), I understand your question that you are interested in the folder size.

According [Use folder size in Conditional](https://stackoverflow.com/a/59910613/6771046)

&gt; _The size attribute returned by the stat module for a folder does **not** tell you the size of the folder&#39;s contents! It only reports the size of the directory entry, which may depend on a number of factors such as the number of files contained in the directory._

If you&#39;re looking to calculate the amount of data contained in a folder, you may [proceed further with the answer there](https://stackoverflow.com/a/59910613/6771046) or [Ansible: Need generic solution to find size of folder/directory or file](https://stackoverflow.com/a/73687567/6771046).

Since that require to use `command` or `shell` which is not generally recommended for all cases, it might be possible to transfer the task into a Custom Module. To do so, creating a Custom Module written in Bash or Shell seems to be the best approach.

The following concept / example module takes a string parameter (`path`) and test if it is a directory or file. After it is providing the size of the directory with all files and including sub directories or the file size only as results set.

**Concept / Example Module** `size.sh`

```sh
#!/bin/sh

exec 3&gt;&amp;1 4&gt;&amp;2 &gt; /dev/null 2&gt;&amp;1

# Create functions

function return_json() {

    exec 1&gt;&amp;3 2&gt;&amp;4

    jq --null-input --monochrome-output \
        --arg changed &quot;$changed&quot; \
        --arg rc &quot;$rc&quot; \
        --arg stdout &quot;$stdout&quot; \
        --arg stderr &quot;$stderr&quot; \
        --arg directory &quot;$directory&quot; \
        --arg file &quot;$file&quot; \
        --arg msg &quot;$msg&quot; \
       &#39;$ARGS.named&#39;

    exec 3&gt;&amp;1 4&gt;&amp;2 &gt; /dev/null 2&gt;&amp;1

}

# Module helper functions

function dirsize() {

    du -sh &quot;$path&quot; | sort -h

}

function filesize() {

    du -sh &quot;$path&quot;

}

# Module main logic

function module_logic() {

    if [ -f &quot;$path&quot; ]; then
        filesize $path
        file=&quot;true&quot;
    elif [ -d &quot;$path&quot; ]; then
        dirsize $path
        directory=&quot;true&quot;
    else
        stderr=&quot;Cannot access: No such file or directory or Permission denied.&quot;
        rc=1
    fi

}

# Set default values

changed=&quot;false&quot;
rc=0
stdout=&quot;&quot;
stderr=&quot;&quot;
directory=&quot;false&quot;
file=&quot;false&quot;
msg=&quot;&quot;

source &quot;$1&quot;

# Check prerequisites

if ! which jq &amp;&gt;/dev/null; then
    exec 1&gt;&amp;3 2&gt;&amp;4
    printf &quot;{ \&quot;changed\&quot;: \&quot;$changed\&quot;,
           \&quot;rc\&quot;: \&quot;1\&quot; ,
           \&quot;stdout\&quot;: \&quot;\&quot;,
           \&quot;stderr\&quot;: \&quot;Command not found: This module require &#39;jq&#39; installed on the target host. Exiting ...\&quot;,
           \&quot;directory\&quot;: \&quot;false\&quot; ,
           \&quot;file\&quot;: \&quot;false\&quot;,
           \&quot;msg\&quot;: \&quot;\&quot;}&quot;
    exit 127
fi

if ! grep -q &quot;Red Hat Enterprise Linux&quot; /etc/redhat-release; then
    stderr=&quot;Operation not supported: Red Hat Enterprise Linux not found. Exiting ...&quot;
    rc=95

    return_json

    exit $rc

fi

# Validate parameter key and value

if [ -z &quot;$path&quot; ]; then
    stderr=&quot;Invalid argument: Parameter path is not provided. Exiting ...&quot;
    rc=22

    return_json

    exit $rc
fi

if [[ $path == *[&#39;!:*&#39;]* ]]; then
    stderr=&quot;Invalid argument: Value path contains invalid characters. Exiting ...&quot;
    rc=22

    return_json

    exit $rc
fi

if [ ${#path} -ge 127 ]; then
    stderr=&quot;Argument too long: Value path has too many characters. Exiting ...&quot;
    rc=7

    return_json

    exit $rc
fi

# Main

module_logic $path

changed=&quot;false&quot;
msg=$(module_logic $path | sed &#39;s/\t/ /g&#39;)

return_json

exit $rc

Test Playbook size.yml

---
- hosts: test
  become: false
  gather_facts: false

  tasks:

  - name: Get size behind path
    size:
      path: /root/anaconda-ks.cfg
    register: result
    ignore_errors: true

  - name: Get size behind path
    size:
      path: &#39;&#39;
    register: result
    ignore_errors: true

  - name: Get size behind path
    size:
    register: result
    ignore_errors: true

  - name: Get size behind path
    size:
      path: &quot;/home/{{ ansible_user }}/*&quot;
    register: result
    ignore_errors: true

  - name: Get size behind path
    size:
      path: &quot;/home/{{ ansible_user }}/1111111111/2222222222/3333333333/4444444444/5555555555/6666666666/7777777777/8888888888/9999999999/0000000000/aaaaaaaaaa/bbbbbbbbbb/cccccccccc/&quot;
    register: result
    ignore_errors: true

  - name: Get size behind path
    size:
      path: /home/{{ ansible_user }}
    register: result

  - name: Show result
    debug:
      var: result

Result ansible-playbook --user ${ANSIBLE_USER} size.yml

TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! =&gt; changed=false
  msg: &#39;&#39;
  rc: &#39;1&#39;
  stderr: &#39;Cannot access: No such file or directory or Permission denied.&#39;
  stderr_lines: &lt;omitted&gt;
  stdout: &#39;&#39;
  stdout_lines: &lt;omitted&gt;
...ignoring

TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! =&gt; changed=false
  msg: &#39;&#39;
  rc: &#39;1&#39;
  stderr: Parameter path is not provided. Exiting ...
  stderr_lines: &lt;omitted&gt;
  stdout: &#39;&#39;
  stdout_lines: &lt;omitted&gt;
...ignoring

TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! =&gt; changed=false
  directory: &#39;false&#39;
  file: &#39;false&#39;
  msg: &#39;&#39;
  rc: &#39;22&#39;
  stderr: &#39;Invalid argument: Value path contains invalid characters. Exiting ...&#39;
  stderr_lines: &lt;omitted&gt;
  stdout: &#39;&#39;
  stdout_lines: &lt;omitted&gt;
...ignoring

TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! =&gt; changed=false
  msg: &#39;&#39;
  rc: &#39;7&#39;
  stderr: Argument &#39;path&#39; too long. Exiting ...
  stderr_lines: &lt;omitted&gt;
  stdout: &#39;&#39;
  stdout_lines: &lt;omitted&gt;
...ignoring

TASK [Get size behind path] **********************************************
changed: [test.example.com]

TASK [Show result] *******************************************************
ok: [test.example.com] =&gt;
  result:
    changed: &#39;false&#39;
    failed: false
    msg: 722M /home/user
    rc: &#39;0&#39;
    stderr: &#39;&#39;
    stderr_lines: []
    stdout: &#39;&#39;
    stdout_lines: []

Thanks to

For script and module

For general Linux and Shell

huangapple
  • 本文由 发表于 2023年6月1日 16:05:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76379857.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定