Ansible自动化运维工具之synchronize模块学习

synchronize is a wrapper around rsync to make common tasks in your playbooks quick and easy.

It is run and originates on the local host where Ansible is being run.

Of course, you could just use the command action to call rsync yourself, but you also have to add a fair number of boilerplate options and host facts.

This module is not intended to provide access to the full power of rsync, but does make the most common invocations easier to implement. You still may need to call rsync directly via command or shell depending on your use case.

该模块是 ansible.posix 集合(版本 1.4.0)的一部分。

如果您使用的是 ansible 软件包,您可能已经安装了这个集合。 它不包含在 ansible-core 中。 要检查它是否已安装,请运行 ansible-galaxy collection list。

要安装它,请使用:ansible-galaxy collection install ansible.posix。

要在 playbook 中使用它,请指定:ansible.posix.synchronize。

问题1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---                        #固定格式
- hosts: centos # 将要执行任务的主机,已经在hosts文件中定义好了,可是单个主机或主机组
become: False # 在目标主机上执行任务时的用户身份
gather_facts: False
vars:
- ansible_shell_executable: /bin/sh
- ansible_shell_type: sh
tasks:
- name: "start categraf agent"
shell: ls -alh
- name: "test ping"
ping:
- name: "Synchronize and delete files for categraf"
ansible.posix.synchronize:
src: /work/jenkins/workspace/categraf/
dest: categraf/
delete: yes
recursive: yes


在执行 这个模块的时候 总是报错:

1
"Could not find the shell plugin required (bash)."

经过源码分析,发现 是在 ansible/plugins/loader.py 文件中的 get_shell_plugin 抛出的异常
shell_type= sh executable /bin/bash # 这个不会报错
shell_type= bash executable /bin/bash # 这个会报错的,因为会根据这个 shell type 值去找 ansible/plugins/shell 下面的哪个 文件的。如果 这个目录下面 复制个 文件 叫 bash.py 的也就不会报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def get_shell_plugin(shell_type=None, executable=None):

if not shell_type:
# default to sh
shell_type = 'sh'

# mostly for backwards compat
if executable:
if isinstance(executable, string_types):
shell_filename = os.path.basename(executable)
try:
shell = shell_loader.get(shell_filename)
except Exception:
shell = None

if shell is None:
for shell in shell_loader.all():
if shell_filename in shell.COMPATIBLE_SHELLS:
shell_type = shell.SHELL_FAMILY
break
else:
raise AnsibleError("Either a shell type or a shell executable must be provided ")

shell = shell_loader.get(shell_type)
if not shell:
raise AnsibleError("Could not find the shell plugin required (%s)." % shell_type)

if executable:
setattr(shell, 'executable', executable)

return shell

问题分析 1

1
2
3
4
5
6
7
8
9
10
11
12
在执行 task的时候,3个 task 发现 fact和 ping task 都是 sh,但是 Synchronize 为什么执行了2 次,第一次是 sh,第二次 就是 bash拉????正式因为这第二次 导致报错
TASK [Gathering Facts] *********************************************************
sh /bin/sh
ok: [192.168.1.105]

TASK [Induce an exception to see what happens] *********************************
sh /bin/sh
ok: [192.168.1.105]

TASK [Synchronize and delete files for categraf] *******************************
sh /bin/sh
bash /bin/bash

问题分析 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
在  ansible/plugins/loader.py 方法的 846 行加上 打印 print(self.class_name, str(obj))
第一次是
class_name ansible.plugins.connectin.ssh.Connection
第二次是
class_name ansible.plugins.connectin.local.Connection

每个task都是先调用了 一个 Connection, 然后调用对应的 xxxModule

shell 模块的 调用
TASK [start categraf agent] ****************************************************
Connection <class 'ansible.plugins.connection.ssh.Connection'>
ShellModule <class 'ansible.plugins.shell.sh.ShellModule'>
shell_type= sh executable /bin/bash

ActionModule <class 'ansible.plugins.action.shell.ActionModule'>
ActionModule <class 'ansible.plugins.action.command.ActionModule'>
changed: [192.168.1.105]

ping模块的 调用
TASK [test ping] ***************************************************************
Connection <class 'ansible.plugins.connection.ssh.Connection'>
ShellModule <class 'ansible.plugins.shell.sh.ShellModule'>
shell_type= sh executable /bin/bash

ActionModule <class 'ansible.plugins.action.normal.ActionModule'>
ok: [192.168.1.105]

1 Connection <class 'ansible.plugins.connection.ssh.Connection'>
1 ShellModule <class 'ansible.plugins.shell.sh.ShellModule'>
2 ShellModule <ansible.plugins.shell.sh.ShellModule object at 0x7f39bdb239a0> 这里正好反过来了,类似 入栈,出栈的效果 Connection -> ShellModule -> ShellModule -> Connection
2 Connection <ansible.plugins.connection.ssh.Connection object at 0x7f39bdb23a30>

1 ActionModule <class 'ansible.plugins.action.normal.ActionModule'>
2 ActionModule <ansible.plugins.action.normal.ActionModule object at 0x7f39bdb233a0>

但是 synchronize 这个就比较特殊了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
这个会调用2次 Connection, 一次ssh的,另外一次 变成 local 了
TASK [Synchronize and delete files for categraf] *******************************
Connection <class 'ansible.plugins.connection.ssh.Connection'> # 最初这里 我们得到 self._play_context.shell 和 self._play_context.executable 分别是 None /bin/sh
ShellModule <class 'ansible.plugins.shell.sh.ShellModule'>
shell_type= sh executable /bin/bash


ActionModule <class 'ansible_collections.ansible.posix.plugins.action.synchronize.ActionModule'> # 经过这里的时候也是 self._play_context.shell 和 self._play_context.executable 分别是 None /bin/sh,但是接着往下的时候 在 synchronize.ActionModule 的run方法里面 把它改了!!!!!!!!!!1

Connection <class 'ansible.plugins.connection.local.Connection'> 这个调用是 因为在 synchronize.ActionModule 中的run() 方法里面 new_connection = connection_loader.get('local', self._play_context, new_stdin) 主动去调用了一个 获取了一个 新的 Connection
shell_type= bash executable /bin/bash

fatal: [192.168.1.105]: FAILED! => {"msg": "Could not find the shell plugin required (bash)."}

ShellModule <class 'ansible.plugins.shell.sh.ShellModule'> # 如果不失败,后续会执行这个


通过追踪 我们 发现 self._play_context 中有问题:

这个 self._play_context 是 从 ssh.Connection 传过来的 <ansible.playbook.play_context.PlayContext object at 0x7fdd6f7b40a0>,
然后传给到了 synchronize.ActionModule 的init方法里面。
在 .synchronize.ActionModule 的run方法里面 它自己有修改了 几个 self._play_context,中的变量的值 self._play_context.shell 和 self._play_context.executable
然后在 调用 connection_loader.get('local', self._play_context, new_stdin) 时候 就传入了 self._play_context

通过打印 __init__方法中的 self._play_context.shell 和 self._play_context.executable 值分别是: None 和 /bin/sh
通过打印 run 方法最开始的时候 self._play_context.shell 和 self._play_context.executable 值分别是: None /bin/sh
通过打印 run 方法调用 connection_loader.get('local', self._play_context, new_stdin) 之前的 时候 值分别是: bash /bin/bash

可以发现 这里确实是 在 synchronize.ActionModule 的 run() 方法中把这2个 self._play_context.shell 和 self._play_context.executable 值重新修改了

下面是默认的代码。可以发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if not use_delegate and remote_transport:
# Create a connection to localhost to run rsync on
new_stdin = self._connection._new_stdin

# Unlike port, there can be only one shell
localhost_shell = None
for host in C.LOCALHOST: # 这里是从 '127.0.0.1', 'localhost', '::1'这个3个主机配置参数中获取 C.MAGIC_VARIABLE_MAPPING['shell']=ansible_shell_type 的,这3个主机中肯定是没单独配置
localhost_vars = task_vars['hostvars'].get(host, {})
for shell_var in C.MAGIC_VARIABLE_MAPPING['shell']:
localhost_shell = localhost_vars.get(shell_var, None)
if localhost_shell:
break
if localhost_shell:
break
else:
localhost_shell = os.path.basename(C.DEFAULT_EXECUTABLE) # 上面的 没有 获取到 就从 ansible.cfg 文件中的这个executable=/bin/bash
self._play_context.shell = localhost_shell # 最终就是这里 没事多去 赋值了这个值导致了 报错

# Unlike port, there can be only one executable
localhost_executable = None
for host in C.LOCALHOST:
localhost_vars = task_vars['hostvars'].get(host, {})
for executable_var in C.MAGIC_VARIABLE_MAPPING['executable']:
localhost_executable = localhost_vars.get(executable_var, None)
if localhost_executable:
break
if localhost_executable:
break
else:
localhost_executable = C.DEFAULT_EXECUTABLE
self._play_context.executable = localhost_executable

经过我们的修复,我们不然它从 DEFAULT_EXECUTABLE 获取这个值,优先从 剧本的yaml文件中获取,如果没有再用默认的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

if not use_delegate and remote_transport:
# Create a connection to localhost to run rsync on
new_stdin = self._connection._new_stdin

# Unlike port, there can be only one shell
localhost_shell = None
for host in C.LOCALHOST:
localhost_vars = task_vars['hostvars'].get(host, {})
for shell_var in C.MAGIC_VARIABLE_MAPPING['shell']: # 这里是从 '127.0.0.1', 'localhost', '::1'这个3个主机配置参数中获取 C.MAGIC_VARIABLE_MAPPING['shell']=ansible_shell_type 的,这3个主机中肯定是没单独配置这个变量的
localhost_shell = localhost_vars.get(shell_var, None)
if localhost_shell:
break
if localhost_shell:
break
else:
for shell_var in C.MAGIC_VARIABLE_MAPPING['shell']: # ansible_shell_type
localhost_shell = task_vars.get(shell_var, None)
if localhost_shell:
break
if not localhost_shell: #从 task vars 里面获取,这里是 剧本中 特意指定了,如果没有再去使用 默认的 ansible.cfg 文件中的这个executable
localhost_shell = os.path.basename(C.DEFAULT_EXECUTABLE) # ansible.cfg 文件中的这个executable=/bin/bash
self._play_context.shell = localhost_shell

# Unlike port, there can be only one executable
localhost_executable = None
for host in C.LOCALHOST:
localhost_vars = task_vars['hostvars'].get(host, {})
for executable_var in C.MAGIC_VARIABLE_MAPPING['executable']: # # 这里是从 '127.0.0.1', 'localhost', '::1'这个3个主机配置参数中获取 C.MAGIC_VARIABLE_MAPPING['executable']=ansible_shell_executable 的,这3个主机中肯定是没单独配置这个变量的
localhost_executable = localhost_vars.get(executable_var, None)
if localhost_executable:
break
if localhost_executable:
break
else:
for executable_var in C.MAGIC_VARIABLE_MAPPING['executable']: # ansible_shell_executable
localhost_executable = task_vars.get(executable_var, None)
if localhost_executable:
break
if not localhost_executable:
localhost_executable = C.DEFAULT_EXECUTABLE # ansible.cfg 文件中的这个executable=/bin/bash
self._play_context.executable = localhost_executable
print("run2: ", self._play_context.shell , self._play_context.executable)


pull 的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# /usr/bin/rsync --delay-updates -F --compress --archive --rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --out-format='<<CHANGED>>%i %n%L' mage@192.168.1.105:/tmp/hsperfdata_mamh/ /tmp/hsperfdata/
- name: Synchronization of two paths both on the control machine
ansible.posix.synchronize:
src: /tmp/hsperfdata_mamh/
dest: /tmp/hsperfdata/
mode: pull

本地执行 playbook 命令的机器上会有如下进程
│ ├─python,31664 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml
│ │ ├─python,31671 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml
│ │ │ └─bash,31681 -c /home/conda/envs/python3.8/bin/python /tmp/mamh/ansibletmpl/ansible-local-3166456lt3s78/ansible-tmp-1665713880.6339345-31671-178074395576684/AnsiballZ_synchronize.py && sleep 0
│ │ │ └─python,31682 /tmp/mamh/ansibletmpl/ansible-local-3166456lt3s78/ansible-tmp-1665713880.6339345-31671-178074395576684/AnsiballZ_synchronize.py
│ │ │ └─rsync,31683 --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L mage@192.168.1.105:/tmp/hsperfdata_mamh/ /tmp/hsperfdata/
│ │ │ ├─rsync,31691 --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L mage@192.168.1.105:/tmp/hsperfdata_mamh/ /tmp/hsperfdata/
│ │ │ └─ssh,31684 -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -l mage 192.168.1.105 rsync --server --sender -logDtprze.LsfxC . /tmp/hsperfdata_mamh/


对应的远端服务器上有如下进程:
│ └─sshd,154588
│ └─sshd,154592
│ └─rsync,154593 --server --sender -logDtprze.LsfxC . /tmp/hsperfdata_mamh/


push 的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# /usr/bin/rsync --delay-updates -F --compress --archive --rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --out-format='<<CHANGED>>%i %n%L' /tmp/hsperfdata_mamh/ mage@192.168.1.105:/tmp/hsperfdata/
- name: Synchronization of two paths both on the control machine
ansible.posix.synchronize:
src: /tmp/hsperfdata_mamh/
dest: /tmp/hsperfdata/
mode: push

默认没有配置 delegate_to的 都是在 当前 执行 playbook 命令的机器上执行的。

过程大致如下:
执行到 lib/ansible_collections/ansible/posix/plugins/action/synchronize.py 文件中 run() 方法,
new_stdin = self._connection._new_stdin
new_connection = connection_loader.get('local', self._play_context, new_stdin)
self._connection = new_connection
通过上面的 代码 从新 弄个 local的 _connection, 这样就会在本机 执行了。
后面调用都这里
result.update(self._execute_module('ansible.posix.synchronize', module_args=_tmp_args, task_vars=task_vars))
进入到 _execute_module 方法中 lib/ansible/plugins/action/__init__.py 中的 _execute_module
改方法会拼接出来一个 cmd 执行的命令,如下:
'/home/conda/envs/python3.8/bin/python /tmp/mamh/ansibletmpl/ansible-local-3029657nhez0k/ansible-tmp-1665712649.202284-30307-156158933036685/AnsiballZ_synchronize.py'
后面会调用:
res = self._low_level_execute_command(cmd, sudoable=sudoable, in_data=in_data)
然后直接就 执行了 cmd 命令了。
本地机器上就会有个这样的 进程:
│ ├─python,31570 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml
│ │ ├─python,31577 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml
│ │ │ └─bash,31587 -c /home/conda/envs/python3.8/bin/python /tmp/mamh/ansibletmpl/ansible-local-31570uqrgmtd8/ansible-tmp-1665713765.2156665-31577-964977609392/AnsiballZ_synchronize.py && sleep 0
│ │ │ └─python,31588 /tmp/mamh/ansibletmpl/ansible-local-31570uqrgmtd8/ansible-tmp-1665713765.2156665-31577-964977609392/AnsiballZ_synchronize.py
│ │ │ └─rsync,31589 --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L /tmp/hsperfdata_mamh/ mage@192.168.1.105:/tmp/hsperfdata/
│ │ │ └─ssh,31590 -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -l mage 192.168.1.105 rsync --server -logDtprze.iLsfxC --log-format=%i --delay-updates . /tmp/hsperfdata/


delegate_to 例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# /usr/bin/rsync --delay-updates -F --compress --archive --rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --out-format='<<CHANGED>>%i %n%L' /tmp/hsperfdata_mamh/ mage@192.168.1.105:/tmp/hsperfdata/    
- name: Synchronization of two paths both on the control machine
ansible.posix.synchronize:
src: /tmp/hsperfdata_mamh/
dest: /tmp/hsperfdata/
delegate_to: localhost #


# /usr/bin/rsync --delay-updates -F --compress --archive --rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' --out-format='<<CHANGED>>%i %n%L' /tmp/hsperfdata_mamh/ mage@192.168.1.105:/tmp/hsperfdata/
- name: Synchronization of two paths both on the control machine
ansible.posix.synchronize:
src: /tmp/hsperfdata_mamh/
dest: /tmp/hsperfdata/
delegate_to: 192.168.1.106 # 这样执行的时候实际是在 12.106 这台服务器上执行的 rsync 命令, 这个task 执行 host 是 192.168.1.105,采用的push模式,也就是 这里把 12.106 上的文件 push到 12.105 上了。playbook执行的机器也不是这2台,是我本地的一台 61.75这个。


在12.106 服务器上如下进程
│ └─sshd,83521
│ └─sshd,83525
│ └─bash,83526 -c /usr/bin/python3 /tmp/mage/ansibletmpr/ansible-tmp-1665645007.8448188-6693-24565751160851/AnsiballZ_synchronize.py && sleep 0
│ └─python3,83527 /tmp/mage/ansibletmpr/ansible-tmp-1665645007.8448188-6693-24565751160851/AnsiballZ_synchronize.py
│ └─rsync,83529 --delay-updates -F --compress --archive --rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null --out-format=<<CHANGED>>%i %n%L /tmp/hsperfdata_mamh/ mage@192.168.1.105:/tmp/hsperfdata/
│ └─ssh,83530 -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -l mage 192.168.1.105 rsync --server -logDtprze.iLsfxCIvu --log-format=%i --delay-updates . /tmp/hsperfdata/

在 12.105 服务器上如下进程,这个是 rsync 同步的目标机器
│ └─sshd,147613
│ └─sshd,147617
│ └─rsync,147618 --server -logDtprze.iLsfxCIvu --log-format=%i --delay-updates . /tmp/hsperfdata/
│ └─rsync,147619 --server -logDtprze.iLsfxCIvu --log-format=%i --delay-updates . /tmp/hsperfdata/


执行 playbook 命令机器上 如下进程:
│ ├─python,6667 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml -v
│ │ ├─python,6693 -u /home/mamh/work/github/ansible-ansible/bin/ansible-playbook -i /home/mamh/work/aais/tools/ansible/inventory.conf /home/mamh/work/aais/tools/ansible/categraf.yaml -v
│ │ │ └─ssh,6700 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User="mage" -o ConnectTimeout=10 -tt 192.168.1.106 /bin/bash -c '/usr/bin/python3 /tmp/mage/ansibletmpr/ansible-tmp-1665645007.8448188-6693-24565751160851/AnsiballZ_synchronize.py && sleep 0'
│ │ └─{python},6673

playbook 命令机器上 执行,然后会在 12.106 上复制一个 AnsiballZ_synchronize.py 文件
通过这个文件 调用了 rsync 命令。
在 ansible/plugins/action/__init__.py 中的 _execute_module 方法中有个 复制 这个 AnsiballZ_synchronize.py 文件 文件的方法调用,这个取自ping模块调试时候看到的,估计其他模块也是类似的。
if module_style == 'binary':
self._transfer_file(module_path, remote_module_path)
else:
self._transfer_data(remote_module_path, module_data)



# /usr/bin/rsync --delay-updates -F --compress --archive --out-format='<<CHANGED>>%i %n%L' /tmp/105_src/ /tmp/105_dst/
- name: Synchronization of two paths both on the control machine
ansible.posix.synchronize:
src: /tmp/105_src/
dest: /tmp/105_dst/
delegate_to: 192.168.1.105 这里把 delegate_to的ip改成192.168.1.15, 这样执行 rsync 命令的服务器是 12.105, 需要 push到的目标 服务器也是 12.105. 这就相当于,在12.105 服务器 本地 自己 执行 rsync,也就是从本地一个目录复制到本地另外一个目录。
在 12.105 上 会有如下进程:
│ └─sshd,148680
│ └─sshd,148684
│ └─bash,148685 -c /usr/bin/python3 /tmp/mage/ansibletmpr/ansible-tmp-1665645894.8538651-7370-177548815339364/AnsiballZ_synchronize.py && sleep 0
│ └─python3,148686 /tmp/mage/ansibletmpr/ansible-tmp-1665645894.8538651-7370-177548815339364/AnsiballZ_synchronize.py
│ └─rsync,148688 --delay-updates -F --compress --archive --out-format=<<CHANGED>>%i %n%L /tmp/105_src/ /tmp/105_dst/
│ └─rsync,148689 --delay-updates -F --compress --archive --out-format=<<CHANGED>>%i %n%L /tmp/105_src/ /tmp/105_dst/
│ └─rsync,148690 --delay-updates -F --compress --archive --out-format=<<CHANGED>>%i %n%L /tmp/105_src/ /tmp/105_dst/

```


# 其他

/home/mamh/work/github/ansible-ansible/lib/ansible/executor/task_queue_manager.py
321行
play_return = strategy.run(iterator, play_context) # 入口执行 task的地方

/home/mamh/work/github/ansible-ansible/lib/ansible/plugins/strategy/linear.py

315行
self._queue_task(host, task, task_vars, play_context)

1
2
3
4
5





├─ansible-playboo,257412 /home/conda/envs/ansible/bin/ansible-playbook -i /work/jenkins/workspace/mage-auto-devops/aais/tools/ansible/inventory.conf -vvv /work/jenkins/workspace/mage-auto-devops/aais/tools/ansible/categraf.yaml
│ └─sh,257426 -c /bin/sh -c ‘/home/conda/envs/ansible/bin/python /tmp/jenkins/ansibletmpl/ansible-local-257296rnz0q177/ansible-tmp-1665562937.9518697-257412-178133226040161/AnsiballZ_synchronize.py && sleep 0’
│ └─sh,257427 -c /home/conda/envs/ansible/bin/python /tmp/jenkins/ansibletmpl/ansible-local-257296rnz0q177/ansible-tmp-1665562937.9518697-257412-178133226040161/AnsiballZ_synchronize.py && sleep 0
│ └─python,257428 /tmp/jenkins/ansibletmpl/ansible-local-257296rnz0q177/ansible-tmp-1665562937.9518697-257412-178133226040161/AnsiballZ_synchronize.py
│ └─rsync,257429 –delay-updates -F –compress –delete-after –archive –rsh=/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null –out-format=<>%i %n%L /work/jenkins/workspace/categraf/ mage@192.168.1.160:categraf/
│ └─ssh,257430 -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -l mage 192.168.1.160 rsync –server -logDtprze.iLsfxC –log-format=%i –delete-after –delay-updates . categraf/

1

task_vars[‘ansible_shell_executable’]
task_vars[‘ansible_shell_type’]
task_vars[‘inventory_hostname’] ‘192.168.1.105’

会循环这3个,然后获取对应的 hostvars 值
LOCALHOST = (‘127.0.0.1’, ‘localhost’, ‘::1’)

task_vars[‘hostvars’]

loader.py
817 行
plugin_load_context = self.find_plugin_with_context(name, collection_list=collection_list) # collection_list=None, name=bash
if not plugin_load_context.resolved or not plugin_load_context.plugin_resolved_path:

当是 bash的时候 plugin_load_context.plugin_resolved_path 这个是 None,导致这个if 进去了。
如果是 sh 的时候这个就是 ‘/home/mamh/work/github/ansible-ansible/lib/ansible/plugins/shell/sh.py’
如果是 cmd 的时候这个就是 ‘/home/mamh/work/github/ansible-ansible/lib/ansible/plugins/shell/cmd.py’ 可以发现 这个变量 决定 用 ansible/plugins/shell 下面的哪个 文件了。

后面就看这个里面 怎么查找这个 插件的???
plugin_load_context = self.find_plugin_with_context(name, collection_list=collection_list) # collection_list=None, name=bash