Ansible2.9版本后VariableManager的set_host_variable方法不生效解决方法

学习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()
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Melody Chaser

所有打赏收入将全部捐赠给我娃

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值