Azure磁盘挂载自动化中Ansible include_tasks循环'item'未定义问题解决
问题描述
通过Terraform创建Azure虚拟机时,需自动挂载基于Jenkins用户输入创建的磁盘——每个磁盘对应传入的LUN编号,通过grep命令获取设备名(如sdc、sdd)。编写了my_tasks.yml实现磁盘分区、格式化、挂载及写入/etc/fstab的操作,计划在playbook.yml中循环调用该任务,但执行时报错。
相关代码
my_tasks.yml
--- - parted: device: "{{ volumename.stdout }}" number: 1 state: present - filesystem: fstype: xfs dev: "{{ volumename.stdout }}" - mount: fstype: xfs opts: noatime src: "{{ volumename.stdout }}" path: "{{ item.mountpoint }}" state: mounted - command: blkid -s UUID -o value {{ volumename.stdout }} register: volumename_disk - blockinfile: path: /etc/fstab state: present block: | UUID={{ volumename_disk.stdout }} {{ volumename.stdout }} xfs defaults,noatime,nofail 0 0
playbook.yml
--- - hosts: "{{ host }}" become: true become_method: sudo become_user: root vars: mount: "{{ lookup('file', '/home/xyz/vars.txt') }}" tasks: - name: Generate the Lun_Name shell: "tree /dev/disk/azure/scsi1 | grep -i lun | awk '{print $2}'" register: lun - set_fact: lun_name: "{{ lun_name|default([]) + [ { 'name': lun.stdout } ] }}" - debug: msg: "LUN is: {{ lun_name }}" - name: Generate the Volume_Name shell: echo "$(ls -l /dev/disk/azure/scsi1 |grep lun |egrep -o "([^\/]+$)")" register: volumename - set_fact: volumenames: "{{ volumenames|default([]) + [ { 'name': volumename.stdout } ] }}" - debug: msg: "VOLUMENAME is: {{ volumenames }}" # - debug: # msg: "the mountpoints are {{ mount }}" - set_fact: mountpoint: "{{ lookup('file', '/home/xyz/vars.txt').split(',') }}" - debug: msg: "the mountpoints are {{ mountpoint }}" # loop: "{{ mountpoint }}" - include_tasks: my_tasks.yml loop: "{{ item.volumenames | list }} {{ item.mountpoint | list }}" loop_control: loop_var: "{{ item }}"
执行错误
fatal: [10.102.26.74]: FAILED! => {"msg": "'item' is undefined"}
单独循环mountpoint可正常运行,确认问题出在include_tasks的循环逻辑部分,需解决该问题并优化代码。
解决方法与优化建议
1. 修复循环逻辑错误
报错核心是item未定义:你在loop中错误引用了item.volumenames和item.mountpoint,但此时item尚未生成。正确做法是先将设备名与挂载点配对成统一的字典列表,再循环这个列表。
2. 修正playbook.yml关键部分
--- - hosts: "{{ host }}" become: true become_method: sudo become_user: root vars: mount_file_path: "/home/xyz/vars.txt" tasks: # 修正:直接获取所有LUN对应的设备名字符串列表 - name: 获取磁盘设备名列表 shell: "ls -l /dev/disk/azure/scsi1 | grep lun | egrep -o '[^/]+$'" register: volumename_result - set_fact: volumenames: "{{ volumename_result.stdout_lines }}" - debug: msg: "磁盘设备名: {{ volumenames }}" # 获取挂载点列表(去除空格) - set_fact: mountpoints: "{{ lookup('file', mount_file_path).split(',') | map('trim') | list }}" - debug: msg: "挂载点列表: {{ mountpoints }}" # 配对设备名与挂载点为字典列表(需确保两者长度一致) - set_fact: disk_mount_pairs: "{{ disk_mount_pairs|default([]) + [ {'device': volumenames[index], 'mountpoint': mountpoints[index]} ] }}" loop: "{{ range(0, volumenames|length) | list }}" loop_control: index_var: index - debug: msg: "磁盘-挂载点配对: {{ disk_mount_pairs }}" # 循环调用任务,传递配对后的变量 - include_tasks: my_tasks.yml loop: "{{ disk_mount_pairs }}" loop_control: loop_var: disk_pair
3. 调整my_tasks.yml接收变量
原任务中的volumename.stdout和item.mountpoint需替换为循环传递的disk_pair变量,同时注意分区后设备名会追加1(如sdc变为sdc1):
--- - parted: device: "/dev/{{ disk_pair.device }}" number: 1 state: present - filesystem: fstype: xfs dev: "/dev/{{ disk_pair.device }}1" - mount: fstype: xfs opts: noatime src: "/dev/{{ disk_pair.device }}1" path: "{{ disk_pair.mountpoint }}" state: mounted - command: blkid -s UUID -o value "/dev/{{ disk_pair.device }}1" register: disk_uuid - blockinfile: path: /etc/fstab state: present block: | UUID={{ disk_uuid.stdout }} {{ disk_pair.mountpoint }} xfs defaults,noatime,nofail 0 0
4. 额外优化点
- 替换shell命令获取设备:改用
azure_rm_disk_info模块从Azure API直接获取磁盘信息,比解析/dev目录更可靠 - 增加长度校验:在配对前检查
volumenames与mountpoints长度是否一致,避免不匹配 - 幂等性优化:给
parted和filesystem模块添加条件判断,如检查分区是否已存在、文件系统是否已格式化,避免重复执行 - 错误处理:添加
failed_when或rescue块,处理磁盘操作失败的场景
内容的提问来源于stack exchange,提问作者Sagar
相关产品推荐
相关产品推荐

