多主机场景下Ansible自定义Filter Plugin写入JSON报错求助
问题描述
我是Python新手,正在开发一款Ansible自定义过滤器插件。单主机执行相关Playbook时插件运行正常,但多主机(host1和host2)执行时出现报错,错误提示为“Extra data: line 21 column 2 (char 273)”。查看生成的JSON文件,发现存在}{格式问题,导致JSON解析失败。
示例Playbook
hosts: host1:host2 tasks: - name: 生成0-10的随机数 command: shuf -i 0-10 -n 1 register: out until: out | customfilter1(file, ansible_hostname) or out.stdout == "2" retries: 50 delay: 0 vars: file: ./file.json
自定义过滤器customfilter1代码
#!/usr/bin/python import os import json class FilterModule(object): def filters(self): return { 'customfilter1': self.customfilter1 } def customfilter1(self, obj, file, ansible_hostname): if obj["attempts"] == 1: print(obj) newdata = {ansible_hostname: {"Attempts": {obj["attempts"]: {"Result": obj["stdout"]}}}} json_file = json.dumps(newdata, sort_keys=False, indent=2) with open(file, 'a') as f: f.write(json_file) else: with open(file) as f: data = json.load(f) data[ansible_hostname]["Attempts"][obj["attempts"]] = {"Result": obj["stdout"]} json_file = json.dumps(data, sort_keys=False, indent=2) with open(file, 'w') as f: f.write(json_file) return False
报错信息
fatal: [ampere]: FAILED! => {"msg": "The conditional check 'out | customfilter1(file, ansible_hostname) or out.stdout == \"2\"' failed. The error was: Extra data: line 21 column 2 (char 273)"}
生成的异常JSON内容
{ "host1": { "Attempts": { "1": { "Result": "4" }, "2": { "Result": "5" }, "3": { "Result": "7" }, "4": { "Result": "0" }, "5": { "Result": "2" } } } }{ "host2": { "Attempts": { "1": { "Result": "5" } } } }
解决方案
问题根源是多主机并行执行时,第一个主机写入完整JSON对象后,第二个主机首次执行(attempts=1)会用追加模式直接写入另一个JSON对象,导致文件变成两个独立JSON对象的拼接(}{),不符合JSON规范,后续解析失败。同时多进程并发读写文件也会加剧数据损坏的概率。
修复后的过滤器代码
#!/usr/bin/python import os import json from filelock import FileLock class FilterModule(object): def filters(self): return { 'customfilter1': self.customfilter1 } def customfilter1(self, obj, file, ansible_hostname): # 用文件锁保证同一时间只有一个进程操作JSON文件 lock_file = f"{file}.lock" with FileLock(lock_file): # 处理文件不存在或为空的情况 if not os.path.exists(file) or os.path.getsize(file) == 0: data = {} else: with open(file, 'r') as f: try: data = json.load(f) except json.JSONDecodeError: # 若文件已损坏,重置为空对象 data = {} # 初始化当前主机的Attempts结构 if ansible_hostname not in data: data[ansible_hostname] = {"Attempts": {}} # 添加当前尝试的结果,注意将attempts转为字符串避免JSON键类型问题 data[ansible_hostname]["Attempts"][str(obj["attempts"])] = {"Result": obj["stdout"]} # 写入文件 with open(file, 'w') as f: json.dump(data, f, sort_keys=False, indent=2) return False
注意事项
- 需先安装
filelock库:执行pip install filelock,确保Ansible运行环境能访问该库。 - 原异常JSON中
"host1:的引号缺失是笔误,修复后的代码会自动生成格式合法的JSON。 - 文件锁机制避免了多主机并行读写时的冲突,保证JSON结构始终符合规范。
内容的提问来源于stack exchange,提问作者EdFred
相关产品推荐
相关产品推荐

