英文:
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
- 创建一个事实的字典
report: "{{ dict(ansible_play_hosts|
zip(ansible_play_hosts|
map('extract', hostvars, ['out', 'matched']))) }}"
得到
report:
test_11: 2
test_13: 0
- 从字典中选择/拒绝属性,并获取满足条件的属性的名称
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
- 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
- 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: "{{ 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
</sup>
答案2
得分: 0
> 我想用Ansible检查一个目录是否为空。
既然未定义“空”是什么意思,以及是否可能存在零字节文件,我理解你的问题是你对文件夹大小感兴趣。
根据在条件中使用文件夹大小的内容,stat
模块返回的 size 属性 不会 告诉你文件夹内容的大小!它只报告目录条目的大小,这可能取决于诸如目录中包含的文件数量等多种因素。
如果你想计算目录中包含的数据量,你可以进一步参考这里的回答或Ansible:需要查找文件夹/目录或文件的通用解决方案。
由于这需要使用 command
或 shell
,这在一般情况下不被推荐,可能可以将任务转换为自定义模块。为此,创建一个用 Bash 或 Shell 编写的自定义模块似乎是最好的方法。
以下是一个概念/示例模块,它接受一个字符串参数(path
),并测试它是目录还是文件。然后提供目录及其所有文件(包括子目录)的大小,或仅文件大小作为结果集。
概念/示例模块 size.sh
#!/bin/sh
exec 3>&1 4>&2 > /dev/null 2>&1
# 创建函数
function return_json() {
exec 1>&3 2>&4
jq --null-input --monochrome-output \
--arg changed "$changed" \
--arg rc "$rc" \
--arg stdout "$stdout" \
--arg stderr "$stderr" \
--arg directory "$directory" \
--arg file "$file" \
--arg msg "$msg" \
'$ARGS.named'
exec 3>&1 4>&2 > /dev/null 2>&1
}
# 模块辅助函数
function dirsize() {
du -sh "$path" | sort -h
}
function filesize() {
du -sh "$path"
}
# 模块主要逻辑
function module_logic() {
if [ -f "$path" ]; then
filesize $path
file="true"
elif [ -d "$path" ]; then
dirsize $path
directory="true"
else
stderr="无法访问:文件或目录不存在或权限被拒绝。"
rc=1
fi
}
# 设置默认值
changed="false"
rc=0
stdout=""
stderr=""
directory="false"
file="false"
msg=""
source "$1"
# 检查先决条件
if ! which jq &>/dev/null; then
exec 1>&3 2>&4
printf "{ \"changed\": \"$changed\",
\"rc\": \"1\" ,
\"stdout\": \"\",
\"stderr\": \"找不到命令:此模块需要在目标主机上安装 'jq'。正在退出... \",
\"directory\": \"false\" ,
\"file\": \"false\",
\"msg\": \"\"}"
exit 127
fi
if ! grep -q "Red Hat Enterprise Linux" /etc/redhat-release; then
stderr="不支持此操作:未找到 Red Hat Enterprise Linux。正在退出..."
rc=95
return_json
exit $rc
fi
# 验证参数键和值
if [ -z "$path" ]; then
stderr="无效参数:未提供参数路径。正在退出..."
rc=22
return_json
exit $rc
fi
if [[ $path == *['!:*']* ]]; then
stderr="无效参数:值路径包含无效字符。正在退出..."
rc=22
return_json
exit $rc
fi
if [ ${#path} -ge 127 ]; then
stderr="参数太长:值路径字符太多。正在退出..."
rc=7
return_json
exit $rc
fi
# 主要
module_logic $path
changed="false"
msg=$(module_logic $path | sed 's/\t/ /g')
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: ''
register: result
ignore_errors: true
- name: 获取路径后面的大小
size:
register: result
ignore_errors: true
- name: 获取路径后面的大小
size:
path: "/home/{{ ansible_user }}/*"
register: result
ignore_errors: true
- name: 获取路径后面的大小
size:
path: "/home/{{ ansible_user }}/1111111111/2222222222/3333333333/4444444444/5555555555/6666666666/
<details>
<summary>英文:</summary>
> _I want to check a directory with Ansible if it'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)
> _The size attribute returned by the stat module for a folder does **not** tell you the size of the folder'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'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>&1 4>&2 > /dev/null 2>&1
# Create functions
function return_json() {
exec 1>&3 2>&4
jq --null-input --monochrome-output \
--arg changed "$changed" \
--arg rc "$rc" \
--arg stdout "$stdout" \
--arg stderr "$stderr" \
--arg directory "$directory" \
--arg file "$file" \
--arg msg "$msg" \
'$ARGS.named'
exec 3>&1 4>&2 > /dev/null 2>&1
}
# Module helper functions
function dirsize() {
du -sh "$path" | sort -h
}
function filesize() {
du -sh "$path"
}
# Module main logic
function module_logic() {
if [ -f "$path" ]; then
filesize $path
file="true"
elif [ -d "$path" ]; then
dirsize $path
directory="true"
else
stderr="Cannot access: No such file or directory or Permission denied."
rc=1
fi
}
# Set default values
changed="false"
rc=0
stdout=""
stderr=""
directory="false"
file="false"
msg=""
source "$1"
# Check prerequisites
if ! which jq &>/dev/null; then
exec 1>&3 2>&4
printf "{ \"changed\": \"$changed\",
\"rc\": \"1\" ,
\"stdout\": \"\",
\"stderr\": \"Command not found: This module require 'jq' installed on the target host. Exiting ...\",
\"directory\": \"false\" ,
\"file\": \"false\",
\"msg\": \"\"}"
exit 127
fi
if ! grep -q "Red Hat Enterprise Linux" /etc/redhat-release; then
stderr="Operation not supported: Red Hat Enterprise Linux not found. Exiting ..."
rc=95
return_json
exit $rc
fi
# Validate parameter key and value
if [ -z "$path" ]; then
stderr="Invalid argument: Parameter path is not provided. Exiting ..."
rc=22
return_json
exit $rc
fi
if [[ $path == *['!:*']* ]]; then
stderr="Invalid argument: Value path contains invalid characters. Exiting ..."
rc=22
return_json
exit $rc
fi
if [ ${#path} -ge 127 ]; then
stderr="Argument too long: Value path has too many characters. Exiting ..."
rc=7
return_json
exit $rc
fi
# Main
module_logic $path
changed="false"
msg=$(module_logic $path | sed 's/\t/ /g')
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: ''
register: result
ignore_errors: true
- name: Get size behind path
size:
register: result
ignore_errors: true
- name: Get size behind path
size:
path: "/home/{{ ansible_user }}/*"
register: result
ignore_errors: true
- name: Get size behind path
size:
path: "/home/{{ ansible_user }}/1111111111/2222222222/3333333333/4444444444/5555555555/6666666666/7777777777/8888888888/9999999999/0000000000/aaaaaaaaaa/bbbbbbbbbb/cccccccccc/"
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! => changed=false
msg: ''
rc: '1'
stderr: 'Cannot access: No such file or directory or Permission denied.'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! => changed=false
msg: ''
rc: '1'
stderr: Parameter path is not provided. Exiting ...
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! => changed=false
directory: 'false'
file: 'false'
msg: ''
rc: '22'
stderr: 'Invalid argument: Value path contains invalid characters. Exiting ...'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
TASK [Get size behind path] **********************************************
fatal: [test.example.com]: FAILED! => changed=false
msg: ''
rc: '7'
stderr: Argument 'path' too long. Exiting ...
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
TASK [Get size behind path] **********************************************
changed: [test.example.com]
TASK [Show result] *******************************************************
ok: [test.example.com] =>
result:
changed: 'false'
failed: false
msg: 722M /home/user
rc: '0'
stderr: ''
stderr_lines: []
stdout: ''
stdout_lines: []
Thanks to
For script and module
- Ansible: Need generic solution to find size of folder/directory or file
- How to execute a shell script on a Remote Server using Ansible?
- Create A Simple Custom Module Using Bash Script In Ansible
For general Linux and Shell
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论