学习Ansible的python api调用出现了问题
网上看了很多文章,要不是英文的,中文的指导文档大部分都是讲ansible的命令的。
关于python api这篇文章讲的还是不错的,Python调用ansible API系列
结果运行第二段代码就出了问题,代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from collections import namedtuple
# 核心类
# 用于读取YAML和JSON格式的文件
from ansible.parsing.dataloader import DataLoader
# 用于存储各类变量信息
from ansible.vars.manager import VariableManager
# 用于导入资产文件
from ansible.inventory.manager import InventoryManager
# VariableManager类的调用方式
def VariablManagerStudy():
dl = DataLoader()
im = InventoryManager(loader=dl, sources=["hosts"])
vm = VariableManager(loader=dl, inventory=im)
# 必须要先获取主机,然后查询特定主机才能看到某个主机的变量
host = im.get_host("172.16.48.242")
# 动态添加变量
vm.set_host_variable(host=host, varname="AAA", value="aaa")
# 获取指定主机的变量
print(vm.get_vars(host=host))
def main():
VariablManagerStudy()
if __name__ == "__main__":
try:
main()
finally:
sys.exit()
添加主机变量这句代码,死活都不生效,搜了很多其他的帖子,调用方式也都没错,但是我就是不生效get_vars()的时候就是打印不出设置的变量
vm.set_host_variable(host=host, varname="AAA", value="aaa")
排查思路
没办法,只能硬着头皮,开始调试lib的代码了,看看问题出在哪里
ansible的lib中,VariableManager 有两个相关的函数
一个是设置变量的 set_host_variable()
另外一个就是读取 get_vars
相关的代码如下
def get_vars(self, play=None, host=None, task=None, include_hostvars=True, include_delegate_to=True, use_cache=True,
_hosts=None, _hosts_all=None, stage='task'):
…………
if host:
# include_vars non-persistent cache
all_vars = _combine_and_track(all_vars, self._vars_cache.get(host.get_name(), dict()), "include_vars")
# fact non-persistent cache
all_vars = _combine_and_track(all_vars, self._nonpersistent_fact_cache.get(host.name, dict()), "set_fact")
…………
def set_host_variable(self, host, varname, value):
'''
Sets a value in the vars_cache for a host.
'''
if host not in self._vars_cache:
self._vars_cache[host] = dict()
if varname in self._vars_cache[host] and isinstance(self._vars_cache[host][varname], MutableMapping) and isinstance(value, MutableMapping):
self._vars_cache[host] = combine_vars(self._vars_cache[host], {varname: value})
else:
self._vars_cache[host][varname] = value
get_vars()的代码很长就只看和这个问题相关的代码,就这么多了
看了一下 其他也不复杂,set_host_variable()把变量存在了_vars_cache里,然后get_vars()的时候不只读取host本身的变量,还要抓取_vars_cache的变量。
流程很清晰,但是调试的时候发现到到这一步的时候:
all_vars = _combine_and_track(all_vars, self._vars_cache.get(host.get_name(), dict()), "include_vars")
self._vars_cache.get(host.get_name(), dict())的值是空的,而_vars_cache调试过程中打印看到是有数据的
难道维护ansible开源项目的大神脑子抽了 这句代码写的有问题?
又继续排查发现获取变量的时候,键值是host.get_name() ,但是我存的时候用的是host,那难道是我存的时候有问题?
果不其然修改代码
vm.set_host_variable(host=host.get_name(), varname="AAA", value="aaa")
结果问题就解决了,那么难道是写帖子的老师粗心了吗?我想应该还是我的版本比较新的原因
github翻了一下历史的ansible的代码果然是这样
问题原因
我的ansible的版本是比较新的 pip安装的时候没有选版本直接上的是2.13.2的
ansible --version
ansible [core 2.13.2]
config file = None
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /venv/ansibledemo/lib/python3.9/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /venv/ansibledemo/bin/ansible
python version = 3.9.10 (main, Jul 29 2022, 17:41:38) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
jinja version = 3.1.2
libyaml = True
github翻了一下历史的代码 ,比较新的几个版本set_host_variable()都是这么写的
def set_host_variable(self, host, varname, value):
'''
Sets a value in the vars_cache for a host.
'''
if host not in self._vars_cache:
self._vars_cache[host] = dict()
if varname in self._vars_cache[host] and isinstance(self._vars_cache[host][varname], MutableMapping) and isinstance(value, MutableMapping):
self._vars_cache[host] = combine_vars(self._vars_cache[host], {varname: value})
else:
self._vars_cache[host][varname] = value
我不死心一个版本一个版本看过去,果然让我发现了问题,2.8及之前的版本的代码不是这样的多了一句
def set_host_variable(self, host, varname, value):
'''
Sets a value in the vars_cache for a host.
'''
host_name = host.get_name()
if host_name not in self._vars_cache:
self._vars_cache[host_name] = dict()
if varname in self._vars_cache[host_name] and isinstance(self._vars_cache[host_name][varname], MutableMapping) and isinstance(value, MutableMapping):
self._vars_cache[host_name] = combine_vars(self._vars_cache[host_name], {varname: value})
else:
self._vars_cache[host_name][varname] = value
到这里基本上就破案了,那么写博客教程的老师以及其他的帖子 应该使用的ansible的版本是2.8或者以下的版本。
存变量的时候lib里的函数里面会get_name
2.9版本之后,去掉了 host_name = host.get_name() 这个句代码,所以存变量的时候就要用host_name去存 。
具体这么改的原因,因为英文水平有限没看出github上修改的原因,英文好的大神可以去瞅瞅,搞明白了麻烦给我说一下。
解决方法
解决方法已经很清晰了其实,我把在2.9之后的版本能够正常跑的代码贴出来一下,作为总结吧。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from collections import namedtuple
# 核心类
# 用于读取YAML和JSON格式的文件
from ansible.parsing.dataloader import DataLoader
# 用于存储各类变量信息
from ansible.vars.manager import VariableManager
# 用于导入资产文件
from ansible.inventory.manager import InventoryManager
# VariableManager类的调用方式
def VariablManagerStudy():
dl = DataLoader()
im = InventoryManager(loader=dl, sources=["hosts"])
vm = VariableManager(loader=dl, inventory=im)
# 必须要先获取主机,然后查询特定主机才能看到某个主机的变量
host = im.get_host("172.16.48.242")
# 动态添加变量
vm.set_host_variable(host=host.get_name(), varname="AAA", value="aaa")
# 获取指定主机的变量
print(vm.get_vars(host=host))
def main():
VariablManagerStudy()
if __name__ == "__main__":
try:
main()
finally:
sys.exit()