command模块执行不返回变量
本文的撰写契机是因为我想修改kvm服务器上的libvirtd.conf
配置,在修改之前,想先备份该配置文件。简单的复制成 libvirtd.conf.bak
文件担心多次执行备份被覆盖,所以想使用常见的脚本 date +%Y-%m-%d_%H:%M:%S
作为文件名后缀,类似常用的脚本:
cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.bak`date +%Y-%m-%d_%H:%M:%S`
我以为ansible command
模块也是类似执行shell命令,但是结果却出乎意料
完整的 playbook fix_libvirtd.yaml
如下
---
- hosts: all
sudo: true
gather_facts: False
vars:
libvirtd_conf: libvirtd.conf
tasks:
- name: copy libvirtd_conf
copy:
src: "{{ libvirtd_conf }}"
dest: /tmp/{{ libvirtd_conf }}
- name: backup libvirtd_conf
command: cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.bak_`date +%Y-%m-%d_%H:%M:%S`
- name: replace libvirtd_conf
command: cp /tmp/{{ libvirtd_conf }} /etc/libvirt/libvirtd.conf
- service: name=libvirtd state=restarted
然而发现报错提示
stderr: cp: target ‘+%Y-%m-%d_%H:%M:%S`’ is not a directory
failed: [192.168.1.1] => {"changed": true, "cmd": ["cp", "/etc/libvirt/libvirtd.conf", "/etc/libvirt/libvirtd.conf.bak_`date", "+%Y-%m-%d_%H:%M:%S`"], "delta": "0:00:00.049106", "end": "2016-09-26 23:35:15.798034", "rc": 1, "start": "2016-09-26 23:35:15.748928", "warnings": []}
这说明Ansible的command
模块传递命令是以空格作为变量分隔,就尝试改成
- name: backup libvirtd_conf
command: cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.bak_`date\ +%Y-%m-%d_%H:%M:%S`
这次之行的结果是在目标主机上备份成了文件名
libvirtd.conf.bak_`date +%Y-%m-%d_%H:%M:%S`
并不是预想中的带时间戳的文件名。
shell命令
Ansible还有一个标准模块shell
,这个模块看上去和command
模块非常类似,也是远程在服务器上执行命令。但是有没有什么区别呢?
我们来尝试一下:
- name: backup libvirtd_conf
shell: cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.bak_`date +%Y-%m-%d_%H:%M:%S`
果然,仅仅修改了Ansible 模块的名字就在目标主机正确生成了文件 libvirtd.conf.bak_2016-09-26_23:41:05
command和shell区别
- command模块
command模块执行命令跟随一系列空格分隔的参数。注意command
不是通过shell执行的,所以不能使用环境变零 SHOME
以及操作符 <
,>
,|
和 &
。其他则和shell没有区别
- shell模块
shell模块是通过远程主机上的shell来执行的(/bin/sh
),所以可以实现较为复杂的命令。
- raw模块
还有一种直接通过SSH命令底层执行的方法,不通过模块子系统。这个模式适合2中情况:第一种情况是在旧系统(Python 2.4或更早)安装python-simplejson
,这样就需要作为依赖的模块。另一种情况是和一些没有安装Python的设备,如路由器交互。这里raw
模式执行的所有参数都直接在远程配置的shell中执行。标准输出,标准错误输出和返回码都在可用时返回。这个方法不需要模块支持,不需要远程主机安装Python。
大多数情况建议使用
shell
和command
模块,仅在老版本python或没有python模块的路由器上使用raw
方式。
修正后的 playbook
---
- hosts: all
sudo: true
gather_facts: False
vars:
libvirtd_conf: libvirtd.conf
tasks:
- name: copy libvirtd_conf
copy:
src: "{{ libvirtd_conf }}"
dest: /tmp/{{ libvirtd_conf }}
- name: backup libvirtd_conf
shell: cp /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.bak_`date +%Y-%m-%d_%H:%M:%S`
- name: replace libvirtd_conf
command: cp /tmp/{{ libvirtd_conf }} /etc/libvirt/libvirtd.conf
- service: name=libvirtd state=restarted