【Ansible常用模块】

安装

[root@docker01 ~]# yum -y install epel-release
[root@docker01 ~]# yum -y install ansible ##只在控制节点进行安装
[root@docker01 ~]# pip3 install ansible  或者通过pip
[root@docker01 ~]# rpm -qif `which mysql` #返回软件包的有关信息
[root@docker01 ~]# rpm -qf `which mysql`  #返回软件包的全名
[root@docker01 ~]# rpm -qlf `which mysql` #返回软件安装的文件列表;涉及哪些文件
# CentOS/RHEL
yum -y install python-argcomplete   ##ansible自动补全功能
# 任何系统都可以使用pip工具安装argcomplete
pip3 install argcomplete

一个控制节点A+2个执行节点B\C:

[root@computeA ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''  #生成密钥对
[root@computeA ~]# for host in 192.168.200.{1.2} node{1..2};do   ##将两台节点主机信息写入到控制节点A中know_hosts
  ssh-keyscan $host >>~/.ssh/known_hosts 2>/dev/null
done
# sshpass -p选项指定的是密码;将computeA上的ssh公钥分发给各节点
[root@computeA ~]# for host in 192.168.200.{1..2} node{1..2};do  
  sshpass -p'123456' ssh-copy-id root@$host &>/dev/null
done

ansible-doc -l | grep 'xxx' ###来筛选需要用的模块
$ ansible-doc copy # 详细的模块描述手册
$ ansible-doc -s copy # 只包含模块参数用法的模块描述手册

ansible localhost -m copy -a 'src=/etc/passwd dest=/tmp' ##在本地,将/etc/passwd 文件copy到/tmp下
ansible localhost -m file -a 'path=/tmp/passwd state=absent' ##通过Ansible删除本地文件/tmp/passwd
$ ansible localhost -m file -a 'path=/tmp/a.log state=touch' # 创建文件
$ ansible localhost -m file -a 'path=/tmp/dir1/dir2 state=directory' # 创建目录

var参数引用变量的时候,不能使用 {{}} 包围,因为var参数已经隐式地包围了一层 {{}}
debug: var=AAA ##AAA是个变量

{{inventory_hostname}} ###执行目标主机的主机名

hosts:

- name: first play
  hosts: nginx

hosts: nginx[1]:mysql[0] ##使用组名时,可以使用数值索引的方式表示组中的第几个主机
hosts: nginx:localhost  ##可使用冒号或逗号隔开多个pattern
hosts: 192.168.200.3[0:3]/web[A:D] ##可以使用范围表示法
hosts: *.example.com  ##可以使用通配符*
hosts: *,            ##这等价于hosts: all
hosts: ~(web|db)\.example\.com   ##可以使用正则表达式,需使用~开头
hosts: pattern1:&pattern2     ##要求同时存在于pattern1和pattern2中的主机
pattern1:!pattern2    ##要求出现在pattern1中但未出现在pattern2中 


Ansible 基础

官方文档模块
-C 只是测试一下会改变什么内容,不会真正去执行
-i 指定hosts文件路径,默认default=/etc/ansible/hosts
-vvv 显示详细日志
-l ##列出可用模块
-s ##显示指定模块

ansible-doc -l  ##列出所有可用模块
ansible-doc file -s #显示file模块的playbook参数
ansible-playbook   --version ##可以查看版本,以及加载的config file和module
ansible-playbook   --forks 10 ##更改并发数,默认是5台机器一起执行
ansible-playbook --syntax-check  ##检查语法
ansible-playbook nginx.yml --list-tasks   ##列出任务
ansible-playbook nginx.yml --list-hosts   ##列出主机
-- step  ##一步步执行
-- start-at-task=' ' ##指定从哪个task 开始执行
--timeout 180  ##指定超时时间
--list-host  ##显示会在哪些主机上执行
ansible-playbook -i host.ini --extra-vars "@parameters.yml"   --ssh-common-args '-F ssh_config' test.yml


##传递变量有两种方式:
ansible localhost -m shell -a "echo {{say_hi}}" -e 'say_hi="hello world"'
ansible localhost -m shell -a "echo {{say_hi}}" -e @/tmp/var_file1.yml

$ ansible localhost -e 'str=world' -m debug -a 'msg="hello {{str}}"'
localhost | SUCCESS => {
    "msg": "hello world"
}

$ ansible localhost -m debug -a 'msg="hello world"'
localhost | SUCCESS => {
    "msg": "hello world"
}


$ ansible localhost -e 'str="hello world"' -m debug -a 'var=str'
localhost | SUCCESS => {
    "str": "hello world"
}

ANSIBLE_DEBUG=1 ansible-playbook test.yml -vvvvv ##ANSIBLE_DEBUG=1查看详细信息
比较运算符:==、!=、>、>=、<、<=、
逻辑运算符:and、or、not

注意: 编写playbook缩进只能以空格,用 tab会报错
注意: 冒号结尾时不需要有空格 注释信息中冒号也不需要。

- name: test
  tags: test
  hosts: web1
  gather_facts: False
  vars:
    AAA: "English"
    BBB: "{{ Name }}"  ##这个来源于parameter.yaml
  tasks:

内置变量

groups

通过groups内置变量可以获取主机的分组

- hosts: db,myql         #选择hosts文件中的主机;可以选择多个主机,使用逗号分隔
  remote_user: root      #指定远程主机用什么用户执行
  gather_facts: no		 #收集远程主机信息,默认为yes

  tasks:                        #任务列表
    - name: create file         #任务说明
      file: path=/ansible/f2 state=touch  mode=0500      #使用file模块,创建文件;mode设置权限
    - name: test1
      shell: echo "{{groups.test|join('|')}}"		#将test组的机器用|分割组合成“A|B|C”
      
- hosts: redis:!redis[0]		#排除redis组的第一个成员

ignore_errors: True 忽略报错
become提权
become: yes
become_user: root		#提升为root权限,类似sudo

inventory_dir

ansible主机中清单文件的存放路径;也就是host.ini 的路径

playbook_dir

playbook的执行路径

- debug: var=playbook_dir 打印playbook在哪个路径执行,也就是playbook的路径
ok: [localhost] => {
    "playbook_dir": "/home/root/AAA"
}

ansible_version

通过内置变量ansible_version获取到ansible的版本号

    - debug: 
        msg: "{{ansible_version}}"
#############################
ok: [localhost] => {
    "msg": {
        "full": "2.7.9",
        "major": 2,
        "minor": 7,
        "revision": 9,
        "string": "2.7.9"
    }
}


hostvars

hostvars可以帮助我们在操作当前主机时获取到其他主机中的信息
如何在Ansible playbook中将变量从一个主机传递到另一个主机:
当一个playbook中有两个hosts执行主机时:

- hosts: testserver
  become: yes
  become_user: root

  tasks:
  - name: get disk size
   ansible.builtin.shell: /bin/df -l | /bin/grep "/mnt/data" | /usr/bin/awk '{print $3}'
   register: disk_space_reg

  - debug:
    var: disk_space_reg.stdout_lines[0]

- hosts: Server2
  tasks:
    - set_fact:
        MDM_Master_list: "{{hostvars['testserver']['disk_space_reg'].stdout}}"
    - debug: var=hostvars['testserver']['disk_space_reg.stdout_lines[0]']
      when: hostvars['testserver']['disk_space_reg.stdout_lines[0]'] is defined

inventory_hostname

通过inventory_hostname变量可以获取到被操作的当前主机的主机名称

---
    - hosts: serve1
      tasks:
        - shell: echo haha
          register: AAA
        - set_fact: var1="{{AAA.stdout}}"
        - set_fact: var2="your name is"
        - debug: msg="{{var2}} {{var1}}"

‘inventory_hostname == “server1”’ ##当主机组是server1\server2\server3;只有当运行主机等于server1时才运行

---

- hosts: server1;server2;server3
  tasks:
    - debug:
        msg: "{{ inventory_hostname }}"
      delegate_to: server2
#####这样仍会运行三次
TASK [debug] *****************************************************
ok: [server1-> server2] => {
    "msg": "server1"
}
ok: [server2-> server2] => {
    "msg": "server2"
}
ok: [server3-> server2] => {
    "msg": "server3"
}
如果加上run_once: true,只会运行一次,就不会运行三次了或者when: 'inventory_hostname == "server1"'

通过vars_files传递变量

在外部文件定义playbook变量(vars_files);使用vars_files关键字,被引入的文件需要以- 开头
cat test.yml

- name: test
  tags: test
  hosts: localhost
  gather_facts: False
  vars_files:
    - 'route_info.yml'
  tasks:
  #####打印所有的destination值
    - debug: msg='{{need_create_nfs_routings[item.0].destination}}'
      with_indexed_items:
        - '{{need_create_nfs_routings}}'

#########route_info.yml保存需要用的变量
need_create_nfs_routings:
- destination: 192.168.0.1/28
   gateway: 192.168.0.254
- destination: 192.168.1.1/28
   gateway: 192.168.1.254
- destination: 192.168.2.1/28
   gateway: 192.168.2.254
- destination: 192.168.3.1/27
   gateway: 192.168.3.254

或者,三种写法
  testvar1: testfile
  testvar2: testfile2
或者
  - testvar1: testfile
  - testvar2: testfile2
或者
httpd:
  conf80: /etc/httpd/conf.d/80.conf
  conf8080: /etc/httpd/conf.d/8080.conf

play_hosts

play_hosts可以获取到当前play所操作的所有主机的主机名列表

forks&&serial

forks: 每个task先按照forks约定的参数,在所有hosts定义的主机分批并行执行,等这个task执行完毕之后,继续按照相同的方式执行后续的task。决定一次在多少个服务器上并行执行相应的task。
serial:先按serial约定数目的主机上并行执行完所有的task之后,才会继续在下一组服务器上执行所有tasks

ansible-playbook -f 5 test_forks.yml  ##执行的时候指定forks

- name: test play
  hosts: tests
  serial: 2  ##playbook中执行serial的值
tests组中的2台机器跑完整个play后, 其他2台机器才会开始执行

- name: test play
  hosts: webservers
  serial: "30%"  ##还可以指定为百分数

- name: test play
  hosts: webservers
  serial:   ##还可以将serial的值设置为列表,每个列表项表示并行执行的服务器数量
    - 1
    - 5
    - 10
#####从指定的服务器中选择1台执行playbook,当执行完成之后,从剩余主机中选择全部主机总数的50%的主机执行playbook,此时如果还有未执行过的指定主机,则按照forks的指定参数,并行执行
- name: test play
  hosts: webservers
  serial:
    - 1
    - "50%"

更改返回码rc

tasks:
  - name: Run this command and ignore the result
    shell: ls 123.txx || /bin/true ##如果文件不存在,rc的返回值也是0

  - name: Run this command and ignore the result
    shell: ls 123.txx ;/bin/true ##如果文件不存在,rc的返回值也是0  

handlers

当task A 执行时,状态为 changed 时,(notify)触发执行的操作。

在需要执行触发器的位置设置
notify:标记
然后在定义触发器
handlers:

  • name:标记
    触发执行命令
  tasks:
    - name: task A
      shell: pwd
      notify: touch B file  
  handlers:
    - name: touch B file   ## 需要和notify的名字保持一致;当task A 执行成功时才会执行这
      shell: touch B

tag标签

通过设置tags,我们可以给playbook中的某一些任务打上“标签”,而在执行playbook的时候,我们可以通过选定标签的方式指定只执行哪一些任务或者不执行哪一些任务

调试代码比较好

  tasks:
    - name: task A
      shell: pwd
      tags: list_dir

ansible-playbook --tags="list_dir" test.yml  ##只跑tag为list_dir的task
ansible-playbook --tags="list_dir,aaaa" test.yml  ##只跑tag为list_dir和aaaa的task
ansible-playbook --skip-tags="install_httpd" test.yml ##除了指定tag的task不执行,其他task都执行
ansible-playbook --list-tags test.yml   ##列出playbook中所有的tag


一个task可以指定多个tag:
tags:                    ###方式一
  - install_httpd
  - install_web
  
tags: ['install_httpd','install_web'] ###方式二
tags: install_httpd,install_web  ###方式三

roles

mkdir roles/nginx/{files,handlers,tasks,templates,vars} -p
touch roles/site.yaml roles/nginx/{handlers,tasks,vars}/main.yaml
[root@localhost ~]# tree roles/
roles/
├── nginx
│   ├── files
│   ├── handlers
│   │   └── main.yaml
│   ├── tasks
│   │   └── main.yaml
│   ├── templates
│   └── vars
│       └── main.yaml
└── site.yaml
 #####注释说明
nginx 角色名
files  普通文件,用来存放由 copy 模块或 script 模块调用的文件。
handlers  触发器程序,此目录应当包含一个 main.yml 文件,用于定义此角色中触发条件时执行的动作。
tasks  主任务,此目录应当包含一个 main.yml 文件,用于定义此角色的任务列表,此文件可以使用 include 包含其它的位于此目录的 task 文件。
templates 金甲模板(有变量的文件),用来存放 j2 模板,template 模块会自动在此目录中寻找 j2 模板文件。
vars 自定义变量,此目录应当包含一个 main.yml 文件,用于定义此角色用到的变量。
除开files目录和templates目录不需要创建main.yml文件,其余都要各个目录下都要创建


在tasks目录下的main.yaml编写任务。
在vars目录下的main.yaml编写变量。
在handlers目录下的main.yaml编写handles。
在site.yaml编写剧本中的角色。

error --报错

  • 1 默认情况下,当主机上的任务失败时,Ansible会停止执行该主机上的任务。使用ignore_errors:true,即使碰到任务失败,仍会继续下面的任务。
  • 2 可以使用ignore_unreachable关键字忽略由于主机(hosts定义的主机)实例为UNREACHABLE 而导致的任务失败。Ansible忽略任务错误,但继续对不可达的主机执行后续的任务。
#task1中忽略不可达的主机继续执行;task2不忽略不可达主机,且该不可达主机终止执行
  hosts: all
  gather_facts: False
  tasks:
    - name: task1
      shell: hostname
      ignore_unreachable: yes  ##如果hosts定义的主机,如果有存在不能ssh进去的,正常会报错,就算加ignore_errors;加上这个就不会报错了
    - name: task2
      shell: pwd
 或者
  hosts: all
  gather_facts: False
  ignore_unreachable: yes   所有任务都会忽略不通的主机

lineinfile

查找某一行文本是否存在于指定的文件中,删除指定的文本行,还可以根据正则表达式替换某一行文本。

 - name: delete    ###会把行中包含23、234、12的行都删除
    lineinfile:
      path: 1.txt
      regexp: "{{ item }}"
      state: absent
    with_items:
       - 23
       - 234
       - 12

  - name: add before #####在某行上一行添加一行内容
    lineinfile:
      path: 1.txt
      insertbefore: '1'   #在1上一行添加add before 23
      line: add before 23

  - name: update test  #####在某行后一行添加多行内容
    lineinfile:
      path: /root/11.txt
      insertafter: '^targetHostsAddress:'  ##匹配到得行后添加内容
      line: "  AZ: 11111\n- hostname: 22222222"   插入多行


  - name: add after  #####在某行下一行添加一行内容
    lineinfile:
      path: 1.txt
      insertafter: '2'    #在2下一行添加add after 45
      line: add after 45

  - name: replace   ### #####替换某行内容
    lineinfile:
      path: 1.txt
      regexp: '3'    #  将3所在行替换为cheng
      line: cheng

文件存在则添加一行内容:
往/etc/hosts里添加一行192.168.0.131 test.breezey.top(多次执行,不会重复添加),示例如下
- name: add a line
  lineinfile:
    dest: /etc/hosts
    line: '192.168.0.131 test.breezey.top'

判断某一行文本是否存在于文件中,如果存在,则不做任何操作,如果不存在,默认在文件末尾加入:
  - name: add    ### #####判断cheng是否在文件中,如果不在就添加
    lineinfile:
      path: 1.txt
      line: cheng

参数backrefs,backup说明
backup: 是否备份原文件,默认为no
backrefs:

    1. 当backrefs为no时,如果regex没有匹配到行,则添加一行,如果Regx匹配到行,则修改该行
    1. 当backrefs为yes时,如果regex没有匹配到行,则保持原文件不变,如果regex匹配到行,则修改该行
使用valiate参数,在保存sudoers文件前,验证语法,如果有错,执行时,会报出来,重新编辑playbook
- name: test validate
      lineinfile:
          dest: /etc/sudoers
          state: present
          regexp: '^%ADMIN ALL='
          line: '%ADMIN ALL=(ALL)'
          validate: 'visudo -cf %s'

blockinfile

针对一段文本得操作;查找、删除、添加

block下的内容,如果原文有匹配的字符就会更新,没有就会追究,并用##BEGIN       ##END包括
  - name: insert/updat block in /root/ren.txt
    blockinfile: 
      path: /root/ren.txt
      block: |       ##会按照下面的内容打印进去,如果不加|会出现在一行
        Match User ansible-agent
        PasswordAuthentication no

path:必须参数,指定要操作的文件
block:用于指定一段文本,与content参数相同
marker:用于指定标记符,即一个开始标记一个结束标记;默认开始标记为# BEGIN ANSIBLE MANAGED BLOCK,结束标记为# END ANSIBLE MANAGED BLOCK,使用marker自定义标记。{mark}为内置的BEGIN和END
state:有两个可选值,present和absent,如果对应的文件中已经存在对应标记的文本,默认会更新对应段落,在执行插入操作或更新操作时,state的值为present,默认值即为present;如果对应的文件中已经存在对应标记的文本并且将state的值设置为absent,则表示从文件中删除对应标记的段落。
insertafter:默认值为EOF,即在文件的末尾插入文本,使用此参数可在指定的行后插入。如果多行文本都能够匹配对应的正则,则以最后一个满足条件的行为准
insertbefore:默认值为BOF,即在文件的开头插入文本。如果多行都能够匹配对应的正则,则以最后一个满足条件的行为准
backup:是否在修改文件之前对文件进行备份
create:当目标文件不存在时,是否创建

删除文件中的标记块:
PS:当state为absent时,参数block将失效,即使指定了,并且block中的内容在目标文件中匹配不到,也会删除匹配到的marker,所以,在匹配的时候,是以marker进行匹配的

在这里插入图片描述

Facts

  • 1 Ansible Facts 是 Ansible 在被托管主机上自动收集的变量。它是通过在执行 Ad-Hoc 以及 Playbook 时使用 setup 模块进行收集的,并且这个操作是默认的。
    因为这个收集托管主机上的 Facts 比较耗费时间,所以可以在不需要的时候关闭 setup 模块: gather_facts: False;这时就不能取到 ansible_hostname、ansible_eth0.ipv4.address、ansible_eth1 [‘ipv4’][‘address’] 变量信息
    收集的 Facts 中包含了以下常用的信息:
    主机名、内核版本、网卡接口、IP 地址、操作系统版本、环境变量、CPU 核数、可用内存、可用磁盘 等等……
  gather_facts: True
  tasks:
    - debug: 
        msg: "{{ansible_facts}}"
    - debug:
        msg: "This host name is {{ ansible_hostname }} ,eth0: {{ ansible_eth0.ipv4.address }}, eth1: {{ ansible_eth1['ipv4']['address'] }}"

block-resuce-always

在playbook执行的路径下,查找一个文件,如果不存在执行rescue,然后执行always
block:运行的主要任务块
rescue:在block中定义的任务失败时,需要运行的任务
always:始终都会运行的任务,无论block和rescue中的任务是成功还是失败

  hosts: localhost
  gather_facts: False
  tasks:
    - name:
      block:
        - name: list
          shell: ls {{ playbook_dir }}/test.yml
      rescue:
        - name: debug
          debug:
            msg: this is rescue
      always:
        - name: always
          debug:
            msg: this is always

TASK [list] ***********************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "ls ./test.yml", "delta": "0:00:00.003327", "end": "2023-08-16 15:52:55.018888", "msg": "non-zero return code", "rc": 2, "start": "2023-08-16 15:52:55.015561", "stderr": "ls: cannot access ./test.yml: No such file or directory", "stderr_lines": ["ls: cannot access ./test.yml: No such file or directory"], "stdout": "", "stdout_lines": []}

TASK [debug] **********************************************************************************************************************************
ok: [localhost] => {
    "msg": "this is rescue"
}

TASK [always] *********************************************************************************************************************************
ok: [localhost] => {
    "msg": "this is always"
}

script

在ansible主机中写好的脚本在受控主机中执行

copy

功能:从ansible主机复制文件到受控主机
从执行playbook的主机上copy

    - name: copy shell script
      copy: 
        src: ../Files/11111.sh
        dest: /tmp/
        mode: 0755

fetch

功能:从受控主机(目标主机)把文件复制到ansible主机(playbook运行主机),但不支持目录

    - name: copy file
      fetch:
        src: '/tmp/11111'
        dest: ./root/
        flat: yes

linefile模块

lineinfile:文件内容修改:在某行前面添加一行、后面添加一行、删除某一行、末尾加入一行、替换或添加某一行

Assert模块

assert模块是用来判断palybook的表达式,当成功或者失败给抛出一些信息
在这里插入图片描述
和fail debug模块比较像

  gather_facts: False
  tasks:
    - block:
        - name: ddsss
          shell: pwd
          register: AAA
        - name: dddd
          assert:
            that:
              - '"TTTTT" in AAA.stdout'
            fail_msg: 'not exist'  ##当上述条件不满足就打印not exist
            success_msg: 'exist'   ##当上述条件满足就打印exist
        - name: dddddddd
          assert:
            that:
              - '"replacement" in AAA.stdout'
            msg: '{{AAA.stdout}}'  ###只有条件不满足才会打印这个信息
      delegate_to: localhost

TASK [dddd] ***********************************************************************************************************************************
ok: [lcm_vip -> None] => {
    "changed": false,
    "msg": "exist"
}
Perform task: TASK: dddddddd (N)o/(y)es/(c)ontinue: y

Perform task: TASK: dddddddd (N)o/(y)es/(c)ontinue: *******************************************************************************************

TASK [dddddddd] *******************************************************************************************************************************
ok: [lcm_vip -> None] => {
    "changed": false,
    "msg": "All assertions passed"
}

File 模块

state: file

    - name: 判断文件是否存在
      file:
        path: "/root/screen111.sh"
        state: file
      register: File_Check

    - debug:
         var: File_Check
comments: 
1. 当文件不存在时报错,报错信息如下:
TASK [判断文件是否存在] ***************************************************************************************************************************
fatal: [localhost]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "file (/root/screen111.sh) is absent, cannot continue", "path": "/root/screen111.sh", "state": "absent"}

PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
2. 当文件存在时,信息如下:
TASK [判断文件是否存在] ***************************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "gid": 0, "group": "root", "mode": "0644", "owner": "root", "path": "/root/screen111.sh", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "state": "file", "uid": 0}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

Perform task: TASK: debug (N)o/(y)es/(c)ontinue: ******************************************************************************************

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "File_Check": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": false, 
        "diff": {
            "after": {
                "path": "/root/screen111.sh"
            }, 
            "before": {
                "path": "/root/screen111.sh"
            }
        }, 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "mode": "0644", 
        "owner": "root", 
        "path": "/root/screen111.sh", 
        "secontext": "unconfined_u:object_r:admin_home_t:s0", 
        "size": 0, 
        "state": "file", 
        "uid": 0
    }
}
##"changed": false说明没有修改

state: touch

    - name: 创建文件
      file:
        path: "/root/screen111.sh"
        state: touch
      register: File_Check

    - debug:
         var: File_Check
1. 当文件存在时,执行这个命令,只是改变文件的时间戳,原先文件的内容还是存在
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "dest": "/root/screen111.sh", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 4, "state": "file", "uid": 0}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

Perform task: TASK: debug (N)o/(y)es/(c)ontinue: ******************************************************************************************

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "File_Check": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": true, 
        "dest": "/root/screen111.sh", 
        "diff": {
            "after": {
                "atime": 1651246754.301492, #2022-04-29 23:39:14
                "mtime": 1651246754.301492, 
                "path": "/root/screen111.sh", 
                "state": "touch"
            }, 
            "before": {
                "atime": 1651246739.777903, #2022-04-29 23:38:59
                "mtime": 1651246737.761903, 
                "path": "/root/screen111.sh", 
                "state": "file"
            }
        }, 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "mode": "0644", 
        "owner": "root", 
        "secontext": "unconfined_u:object_r:admin_home_t:s0", 
        "size": 4, 
        "state": "file", 
        "uid": 0
    }
}
当文件存在时,再执行touch时,文件的before时间戳就是指原先文件创建时间,after时间戳就是执行touch时创建的;before与after时间戳不一样
2. 当初始文件不存在的时候,执行touch等于新建一个
TASK [创建文件] *******************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "dest": "/root/screen111.sh", "gid": 0, "group": "root", "mode": "0644", "owner": "root", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 0, "state": "file", "uid": 0}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

Perform task: TASK: debug (N)o/(y)es/(c)ontinue: ******************************************************************************************

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "File_Check": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": true, 
        "dest": "/root/screen111.sh", 
        "diff": {
            "after": {
                "atime": 1651247066.617086, ##2022-04-29 23:44:26
                "mtime": 1651247066.617086, 
                "path": "/root/screen111.sh", 
                "state": "touch"
            }, 
            "before": {
                "atime": 1651247066.5459297, ##2022-04-29 23:44:26
                "mtime": 1651247066.5459297, 
                "path": "/root/screen111.sh", 
                "state": "absent"
            }
        }, 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "mode": "0644", 
        "owner": "root", 
        "secontext": "unconfined_u:object_r:admin_home_t:s0", 
        "size": 0, 
        "state": "file", 
        "uid": 0
    }
}
当文件不存在时,再执行touch时,after时间戳就是执行touch时创建的;before与after时间戳一样,说明原先文件不存在

state: directory

    - name: 创建目录
      file:
        path: "/root/rencs11"
        state: directory
      register: File_Check

    - debug:
         var: File_Check
1. 当rencs11目录不存在的时候,会创建个目录:
TASK [创建目录] *******************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/root/rencs11", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 4096, "state": "directory", "uid": 0}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

Perform task: TASK: debug (N)o/(y)es/(c)ontinue: ******************************************************************************************

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "File_Check": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": true, 
        "diff": {
            "after": {
                "path": "/root/rencs11", 
                "state": "directory" ##新建个目录
            }, 
            "before": {
                "path": "/root/rencs11", 
                "state": "absent"  ##说明原先目录不存在
            }
        }, 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "mode": "0755", 
        "owner": "root", 
        "path": "/root/rencs11", 
        "secontext": "unconfined_u:object_r:admin_home_t:s0", 
        "size": 4096, 
        "state": "directory", 
        "uid": 0
    }
}
备注: before中的state": "absent代表原先目录不存在
2. 当目录存在时,执行这个命令,什么都不操作
TASK [创建目录] *******************************************************************************************************************************
ok: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/root/rencs11", "secontext": "unconfined_u:object_r:admin_home_t:s0", "size": 4096, "state": "directory", "uid": 0}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

Perform task: TASK: debug (N)o/(y)es/(c)ontinue: ******************************************************************************************

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "File_Check": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        }, 
        "changed": false, 
        "diff": {
            "after": {
                "path": "/root/rencs11" ##前后一样,说明文件存在
            }, 
            "before": {
                "path": "/root/rencs11"
            }
        }, 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "mode": "0755", 
        "owner": "root", 
        "path": "/root/rencs11", 
        "secontext": "unconfined_u:object_r:admin_home_t:s0", 
        "size": 4096, 
        "state": "directory", 
        "uid": 0
    }
}

state: absent

    - name: 删除文件
      file:
        path: "/root/{{item}}"
        state: absent
      register: File_Check
      with_items:
        - "1.sh"
        - "file.txt"
1. 当文件存在时:开头是changed
TASK [删除文件] *******************************************************************************************************************************
changed: [localhost] => (item=1.sh) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": true, "item": "1.sh", "path": "/root/1.sh", "state": "absent"}
changed: [localhost] => (item=file.txt) => {"ansible_loop_var": "item", "changed": true, "item": "file.txt", "path": "/root/file.txt", "state": "absent"}
Perform task: TASK: debug (N)o/(y)es/(c)ontinue: y

2. 当文件不存在时,也不报错:开头是ok
TASK [删除文件] *******************************************************************************************************************************
ok: [localhost] => (item=1.sh) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "item": "1.sh", "path": "/root/1.sh", "state": "absent"}
ok: [localhost] => (item=file.txt) => {"ansible_loop_var": "item", "changed": false, "item": "file.txt", "path": "/root/file.txt", "state": "absent"}

循环

with_dict

[root@k8s-master ansible]# cat test11.yml
- name: testing 
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
    test1:
      english: pass
      math: fail

  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA

    - debug:
        msg: "{{item}}"
      when: '"root" in AAA.stdout'
      with_dict: "{{test1}}"
##字典格式的test1变量经过"with_dict"处理后,字典中的每个键值对被放到了item变量中,而且,键值对中的"键"被放入了"key"关键字中,键值对中的"值"被放入了"value"关键字中
TASK [dsiefwaa] ***************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "pwd", "delta": "0:00:00.007101", "end": "2022-06-19 23:12:32.972053", "rc": 0, "start": "2022-06-19 23:12:32.964952", "stderr": "", "stderr_lines": [], "stdout": "/root", "stdout_lines": ["/root"]}

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => (item={u'key': u'math', u'value': u'fail'}) => {
    "msg": {
        "key": "math", 
        "value": "fail"
    }
}
ok: [localhost] => (item={u'key': u'english', u'value': u'pass'}) => {
    "msg": {
        "key": "english", 
        "value": "pass"
    }
}

PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
[root@k8s-master ansible]# cat test11.yml
- name: testing 
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
    test1:
      english: pass
      math: fail

  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA

    - debug:
        msg: "course name: {{item.key}},result: {{item.value}}"
      when: '"root" in AAA.stdout'
      with_dict: "{{test1}}"
##
TASK [dsiefwaa] ***************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "pwd", "delta": "0:00:00.011418", "end": "2022-06-19 23:17:09.514453", "rc": 0, "start": "2022-06-19 23:17:09.503035", "stderr": "", "stderr_lines": [], "stdout": "/root", "stdout_lines": ["/root"]}

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => (item={u'key': u'math', u'value': u'fail'}) => {
    "msg": "course name: math,result: fail"
}
ok: [localhost] => (item={u'key': u'english', u'value': u'pass'}) => {
    "msg": "course name: english,result: pass"
}

PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
[root@k8s-master ansible]# cat test11.yml
- name: testing 
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
    test1:
      AA:
        english: pass
        math: fail
      BB:
        english: 'not pass'
        math: pass

  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA

    - debug:
        msg: "course name: {{item.key}},result: {{item.value.math}}"
      when: '"root" in AAA.stdout'
      with_dict: "{{test1}}"
##从输出信息可以看出,english、math都被放入了value关键字中,如果想要获取到english、math的值,则可以使用如下playbook中的方法
TASK [dsiefwaa] ***************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "pwd", "delta": "0:00:00.009192", "end": "2022-06-19 23:21:28.653796", "rc": 0, "start": "2022-06-19 23:21:28.644604", "stderr": "", "stderr_lines": [], "stdout": "/root", "stdout_lines": ["/root"]}

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => (item={u'key': u'AA', u'value': {u'math': u'fail', u'english': u'pass'}}) => {
    "msg": "course name: AA,result: fail"
}
ok: [localhost] => (item={u'key': u'BB', u'value': {u'math': u'pass', u'english': u'not pass'}}) => {
    "msg": "course name: BB,result: pass"
}

PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


loop

可以使用item循环变量+loop模块组合使用,实现普通循环。

  tasks:
    - name:  停止服务
      service:
        name: "{{ item }}"     ##引用循环变量。
        state: stopped
      loop:              ##使用loop模块,要操作的对象列表。
        - ntp
        - network 
        - sshd

with_indexed_items

处理results结果

    - fail:
        msg: "server connection status is not ok"
      when: '"100%" in AAA.results[item.0].stdout'
      with_indexed_items: "{{ AAA.results }}"

with_subelements

- hosts: localhost
  gather_facts: no
  vars:
    users:
    - name: zhangsan
      gender: male
      hobby:
        - Skateboard
        - VideoGame
    - name: xiaofeng
      gender: female
      hobby:
        - Music
  tasks:
  - debug:
      msg: "{{ item.0.name }} 's hobby is {{item.1}}"
    with_subelements:
    - "{{users}}"
    - hobby

ok: [localhost] => (item=[{'name': 'zhangsan', 'gender': 'male'}, 'Skateboard']) => {
    "msg": "zhangsan 's hobby is Skateboard"
}
ok: [localhost] => (item=[{'name': 'zhangsan', 'gender': 'male'}, 'VideoGame']) => {
    "msg": "zhangsan 's hobby is VideoGame"
}
ok: [localhost] => (item=[{'name': 'xiaofeng', 'gender': 'female'}, 'Music']) => {
    "msg": "xiaofeng 's hobby is Music"
}

with_items

设置初始变量为0,循环,当满足条件,变量加1

    - set_fact:
        Num: 0
    - set_fact:
        Num: "{{ Num|int +1 }}"
      when: item in AAA.stdout
      with_items: "{{BBB.stdout}}"

- hosts: server1
  gather_facts: no 
  tasks:
    - debug:
        msg: "{{ item }}"
      with_items:
        - demo1.example.com
        - demo2.example.com
        - demo3.example.com

- hosts: server1
  gather_facts: no 
  tasks:
    - name: "create directory"
      file: 
        path: "/tmp/{{ item.path1 }}/{{ item.path2 }}"
        state: directory
      with_items:
        - {path1: a, path2: b} 
        - {path1: c, path2: d}

with_list与 with_items一样,也是用于循环列表。区别是,如果列表的值也是列表,with_iems会将第一层嵌套的列表拉平,而with_list会将值作为一个整体返回。with_flatten会将所有列表全部拉平
[[1,2,[3,4]],[5,6],7,8]

with_item------->[1,2,[3,4],5,6,7,8] 拉平第一层

with_list--------->[[1,2,[3,4]],[5,6],7,8] 整体返回

with_flatten----->[1,2,3,4,5,6,7,8] 全部拉平

with_together

将两个列表对齐合并;两个列表的同一列合并

- hosts: Server2
  gather_facts: no 
  vars:
    alpha: [ 'a','b','c','d']
    numbers: [ 1,2,3,4 ]
  tasks:
    - debug: msg="{{ item.0 }} and {{ item.1 }}"
      with_together:
         - "{{ alpha1 }}"
         - "{{ numbers1 }}"

#####
ok: [Server2] => (item=[u'a', 1]) => {
    "msg": "a and 1"
}
ok: [Server2] => (item=[u'b', 2]) => {
    "msg": "b and 2"
}

with_nested

嵌套循环,相当于像个for

- hosts: Server2
  gather_facts: no 
  tasks:
    - debug: msg="name is {{ item[0] }}  vaule is {{ item[1] }} num is {{ item[2] }}"
      with_nested:
        - ['alice','bob']
        - ['a','b','c']
        - ['1','2','3']
 alice 和a 1组合
 alice 和a 2组合
 alice 和a 3组合
 alice 和b 1组合
 alice 和b 2组合
 alice 和b 3组合
 alice 和c 1组合
 alice 和c 2组合
 alice 和c 3组合
 bob 和a 1组合
 ####结果
 ok: [Server2] => (item=[u'alice', u'a', u'1']) => {
    "msg": "name is alice  vaule is a num is 1"
}

变量列表追加

  tasks:
    - name: set fact 1
      set_fact: 
        foo: "[ 'zero' ]"

    - name: set fact 2
      set_fact: 
        foo: "{{ foo }} + [ 'one' ]"
    - debug: var=foo

    - name: set fact 3
      set_fact: 
        foo: "{{ foo }} + [ '{{ item }}' ]"
      with_items:
        - four
        - five
        - six

    - debug: var=foo
##结果
TASK [debug] **********************************************************************************************************************************
ok: [localhost] => {
    "foo": [
        "zero",
        "one"
    ]
}

TASK [set fact 3] *****************************************************************************************************************************
ok: [localhost] => (item=four)
ok: [localhost] => (item=five)
ok: [localhost] => (item=six)

TASK [debug] **********************************************************************************************************************************
ok: [localhost] => {
    "foo": [
        "zero",
        "one",
        "four",
        "five",
        "six"
    ]
}

或者
    - name: set fact 1
      set_fact: 
        foo: "[ 'host1' ]"

    - name: set fact 2
      set_fact: 
        foo: "{{ foo }} + [ '{{ item.host }}' ]"
      with_items: "{{主机列表}}"

    - debug: var=foo  ##会将新增的机器和host1放到一个列表

shell 模块
特殊字符处理:
下面加上/bin/bash -c 就ok

    - name: 5.dfgg
      shell: "/bin/bash -c 'echo >(cat /root/ansible/ping.sh)'"
      register: AAA

    - debug: var=AAA

在这里插入图片描述

when

使用and表示所有条件满足时,才执行任务。
or表示满足任意一个条件时,就可执行任务。
when: service_name is defined  ## 当变量存在时执行,##判断变量是否为空,不为空则执行任务
  1. when的语句中变量不使用{{}},否则会出现warning
    在这里插入图片描述
  2. 如果变量中要使用{{}},要加双引号,否则会报错
- name: testing
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA

    - debug:
        msg: "dshierfe"
      when: '"root" in "{{AAA.stdout}}"'
当写成'"root" in {{AAA.stdout}}' 会报
TASK [debug] ******************************************************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: "root" in {{AAA.stdout}}
fatal: [localhost]: FAILED! => {"msg": "The conditional check '\"root\" in {{AAA.stdout}}' failed. The error was: Invalid conditional detected: invalid syntax (<unknown>, line 1)\n\nThe error appears to be in '/root/ansible/test11.yml': line 11, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - debug:\n      ^ here\n"}

when: ‘all_test.stdout_lines|length|int != 1’ 判断列表变量长度

    - debug:
        var: "{{num.stdout_lines|length|int}}"
    - debug:
        var: "{{check_healthcheck.stdout_lines|length|int}}"
    - debug:
        msg: "dahfeiahfeifaw"
      when: "(num.stdout_lines|length|int)-43 == check_healthcheck.stdout_lines|length|int"
      when: "(num.stdout_lines|length|int)-(check_healthcheck.stdout_lines|length|int) == 43"

还可以组合多个失败条件。如果两个条件都为真,该任务将失败:
  failed_when:
   - result.rc == 0
   - '"No such" not in result.stdout'
如果有太多的条件不能整齐地放在一行中,可以使用“>”将其分割为多行yaml值:
  failed_when: >
    ("No such file or directory" in ret.stdout) or
    (ret.stderr != '') or
    (ret.rc == 10)

列表处理

判断一个对象是否是字符串 when: testvar2 is string
判断对象是否为一个数字 when: testvar2 is number

subset superset

子集和父集

- hosts: localhost
  gather_facts: no
  vars:
    a:
    - 2
    - 5
    b: [1,2,3,4,5]
  tasks:
  - debug:
      msg: "a subset of b"
    when: a is subset(b)
  - debug:
      msg: "b superset of a"
    when: b is superset(a)

过滤器

字符串过滤器

upper 小写字母变成大写字母 {{var1|upper}}
lower 大写字母变成小写字母 {{var1| lower}}
capitalize 首字母变成大写 {{var1| capitalize}}
first:返回字符串的第一个字符
last:返回字符串的最后一个字符。
trim:去除字符串开头和结尾的空格
count和length:返回字符串长度
list:将字符串转换成列表,每个字符作为一个元素

      #将字符串转换成纯大写
      msg: "{{ testvar | upper }}"
      #将字符串转换成纯小写
      msg: "{{ testvar | lower }}"
      #将字符串变成首字母大写,之后所有字母纯小写
      msg: "{{ testvar | capitalize }}"
      #将字符串反转
      msg: "{{ testvar | reverse }}"
      #返回字符串的第一个字符
      msg: "{{ testvar | first }}"
      #返回字符串的最后一个字符
      msg: "{{ testvar | last }}"
      #将字符串开头和结尾的空格去除
      msg: "{{ testvar1 | trim }}"
      #将字符串放在中间,并且设置字符串的长度为30,字符串两边用空格补齐30位长
      msg: "{{ testvar1 | center(width=30) }}"
      #返回字符串长度,length与count等效,可以写为count
      msg: "{{ testvar2 | length }}"
      #将字符串转换成列表,每个字符作为一个元素
      msg: "{{ testvar3 | list }}"
$ ansible servera -m debug -a 'msg={{ "abc" | lower}}'
servera | SUCCESS => {
    "msg": "ABC"
}
$ ansible servera -m debug -a 'msg={{ "abc" | capitalize }}'
servera | SUCCESS => {
    "msg": "Abc"
}

列表过滤器

sort:排序
将列表升序排序输出:{{ var | sort }}
将列表降序排序输出:{{ var | sort(reverse=true) }}
flatten:拉平列表
将嵌套列表展开平铺:{{ var | flatten }}
只展开第一层的嵌套列表:{{ var | flatten(levels=1) }}
取嵌套列表中的最大值:{{ var | flatten | max }}
join:
将列表中的元素合并成一个字符串:{{ var | join }}
将列表中的元素合并成一个字符串,每个元素之间用指定的字符隔开:{{ var | join(‘/’) }}
unique:去除重复的元素。{{var | unique}}
union:合并列表,也就是求列表的并集。{{ var1 | union(var2) }}
intersect:求列表的交集。{{ var1 | intersect(var2) }} ##在var1而没有在var2中
symmetric_difference:取出两个列表中各自独有的元素,重复的元素只留下一个。{{var1 | symmetric_difference(var2)}} ##没有顺序性

  - debug:
      #返回列表长度,length与count等效,可以写为count
      msg: "{{ testvar7 | length }}"
  - debug:
      #返回列表中的第一个值
      msg: "{{ testvar7 | first }}"
  - debug:
      #返回列表中的最后一个值
      msg: "{{ testvar7 | last }}"
  - debug:
      #返回列表中最小的值
      msg: "{{ testvar7 | min }}"
  - debug:
      #返回列表中最大的值
      msg: "{{ testvar7 | max }}"
  - debug:
      #将列表升序排序输出
      msg: "{{ testvar7 | sort }}"
  - debug:
      #将列表降序排序输出
      msg: "{{ testvar7 | sort(reverse=true) }}"
  - debug:
      #返回纯数字非嵌套列表中所有数字的和
      msg: "{{ testvar7 | sum }}"
  - debug:
      #如果列表中包含列表,那么使用flatten可以'拉平'嵌套的列表
      #2.5版本中可用,执行如下示例后查看效果
      msg: "{{ testvar8 | flatten }}"
  - debug:
      #过滤器都是可以自由结合使用的,就好像linux命令中的管道符一样
      #如下,取出嵌套列表中的最大值
      msg: "{{ testvar8 | flatten | max }}"
  - debug:
      #将列表中的元素合并成一个字符串
      msg: "{{ testvar9 | join }}"
  - debug:
      #将列表中的元素合并成一个字符串,每个元素之间用指定的字符隔开
      msg: "{{ testvar9 | join(' , ') }}"
  - debug:
      #从列表中随机返回一个元素
      #对列表使用random过滤器时,不能使用start和step参数
      msg: "{{ testvar9 | random }}"
  - debug:
      #从列表中随机返回一个元素,并将ansible_date_time.epoch的值设置为随机种子
      #seed参数从ansible2.3版本开始可用
      msg: "{{ testvar9 | random(seed=(ansible_date_time.epoch)) }}"
  - debug:
      #随机打乱顺序列表中元素的顺序
      #shuffle的字面意思为洗牌
      msg: "{{ testvar9 | shuffle }}"
  - debug:
      #将列表中的每个元素变成纯大写
      msg: "{{ testvar10 | upper }}"
  - debug:
      #将列表中的每个元素变成纯小写
      msg: "{{ testvar10 | lower }}"
  - debug:
      #去掉列表中重复的元素,重复的元素只留下一个
      msg: "{{ testvar11 | unique }}"
  - debug:
      #将两个列表合并,重复的元素只留下一个
      #也就是求两个列表的并集
      msg: "{{ testvar11 | union(testvar12) }}"
  - debug:
      #取出两个列表的交集,重复的元素只留下一个
      msg: "{{ testvar11 | intersect(testvar12) }}"
  - debug:
      #取出存在于testvar11列表中,但是不存在于testvar12列表中的元素
      #去重后重复的元素只留下一个
      #换句话说就是:两个列表的交集在列表1中的补集
      msg: "{{ testvar11 | difference(testvar12) }}"
  - debug:
      #取出两个列表中各自独有的元素,重复的元素只留下一个
      #即去除两个列表的交集,剩余的元素
      msg: "{{ testvar11 | symmetric_difference(testvar12) }}"

其他过滤器

  • 1 default:
    如果变量没有定义,临时返回一个指定好的默认值: msg: “{{var1|default(‘默认’)}}”
    如果变量的值是一个空字符串或者变量没有定义,临时返回一个指定的默认值。
    msg: “{{var1|default(‘moren’,boolean=true)}}”
  • 2 json_query:读取yaml文件
msg: "{{ var1 | json_query('users[?name==`bob`].hobby[*]') }}"	# 单双引号都存在了,就使用了反引号。
$ cat json.yaml
---
- name: json demo
  hosts: servera
  vars_files:
    - name_list.yaml
  tasks:
    - debug:
            msg: "{{ users  | json_query('[*].name') }}"
  • 3 ternary:
    实现三元运算的效果,类似if else的功能
# 如果变量var1等于Selina,则对应值是Ms,否则是Mr。
  tasks:
  - debug:
      msg: "{{ (var1=='selina') | ternary('Ms','Mr') }}"
    vars:
      var1: "selina"

var1: “/server/ops_ansible/variables.yaml”

  • 4 basename:获取路径字符串的文件名 “{{var1 | basename}}”
  • 5 dirname:获取路径字符串的目录路径 “{{var1 | dirname}}”
  • 6 splitext:将文件名与后缀名分开。
  • 7 map:获取数据中每个直接子元素所共有的属性(不能获取嵌套列表的属性),并将值组成一个列表呈现
msg: "{{users | map(attribute='name') | list}}"
# 也可以组成一个字符串,用指定的字符隔开,比如分号。
msg: "{{users | map(attribute='name') | join(';')}}"

regex_search过滤器,查找所有出现的子字符串,匹配行中第一个项目,并返回一个列表值。
regex_findall过滤器,查找所有出现的子字符串,匹配行中所有项目,并返回一个列表值。
replace过滤器,换输入字符串中所有出现的子字符串,不支持正则表达式。
regex_replace过滤器,换输入字符串中所有出现的子字符串。

comment 过滤器,可以实现注释字符串的功能,默认为 #注释。ansible servera -m debug -a “msg={{ ‘lieeeeeee’ | comment }}”

[root@k8s-master ansible]# cat test11.yml
- name: testing 
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
    test1:
      - ["first","english"]
      - ["second","math"]
  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA
    - debug:
        msg: "{{test1|map('join','=') | join('//')}}"
      when: '"root" in "{{AAA.stdout}}"'
##map里用的join,map表示在双层列表中,将第二层的列表中的元素使用”=“连接起来,join('//')在第一层将这些第二层层列表再次用”//“拼接起来。
[root@k8s-master ansible]# ansible-playbook -v test11.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [testing] ****************************************************************************************************************************

TASK [dsiefwaa] ***************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "pwd", "delta": "0:00:00.010752", "end": "2022-06-19 22:52:23.796943", "rc": 0, "start": "2022-06-19 22:52:23.786191", "stderr": "", "stderr_lines": [], "stdout": "/root", "stdout_lines": ["/root"]}

TASK [debug] ******************************************************************************************************************************
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: "root" in
"{{AAA.stdout}}"
ok: [localhost] => {
    "msg": "first=english//second=math"
}
PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

获取双层列表中第二层的指定位置元素?使用到map(‘extract’) 过滤器

[root@k8s-master ansible]# cat test11.yml
- name: testing 
  tags: testing
  hosts: localhost
  gather_facts: False
  vars:
    test1:
      - ["first","english"]
      - ["second","math"]
  tasks:
    - name: dsiefwaa
      shell: pwd
      register: AAA
    - debug:
        msg: "{{ [0,1] | map('extract', test1, [1]) | list |  join(',') }}"
      when: '"root" in AAA.stdout'
##[0,1], 就表示只取前两列,每个列表最大长度是2,所以就写0,1
map('extract',test1, [1])
	extract: 表示提取的意思
	test1: 表示需要提的的变量
	[1], 表示了第二层中的哪个元素, 这里为第二层的序号为1的元素
TASK [dsiefwaa] ***************************************************************************************************************************
changed: [localhost] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "pwd", "delta": "0:00:00.010004", "end": "2022-06-19 23:02:07.946654", "rc": 0, "start": "2022-06-19 23:02:07.936650", "stderr": "", "stderr_lines": [], "stdout": "/root", "stdout_lines": ["/root"]}

TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
    "msg": "english,math"
}

PLAY RECAP ********************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

当一个playbook有两个hosts组的时候,如何让变量共用

- name: test1
  tags: test1
  hosts: AAA  ##主机AAA
  gather_facts: False
  tasks:
    - name:  hostname
      shell: hostname
      register: HostName
    
- name: test2
  tags: test2
  hosts: BBB  ##主机BBB
  gather_facts: False
  tasks:
  ## 将AAA的变量在BBB主机中设置成一个变量
    - set_fact:
        MDM_Master_list: "{{hostvars['AAA']['HostName'].stdout}}"
   ##在BBB这个主机打印AAA的变量
    - debug: var=hostvars['AAA']['HostName'].stdout 

delegate_to:

- hosts: AAA:test_group 
  gather_facts: False
  tasks:
    - name: def
      shell: hostname
      register: Hostname
      delegate_to: AAA
#      delegate_to: "{{groups['test_group'][0]}}" 
#      run_once: true

当delegate_to 是AAA的时候,不加run_once ,默认hosts定义的所有主机都会委派到AAA执行一次,意思一共有5台主机,就会委派5次,hostname这个命令就会在AAA主机组跑5遍
当加了run_once,就只在AAA跑一次
当delegate_to 是groups['test_group'][0]的时候,不加run_once,一样有多少主机,就跑多少遍;加run_once默认只跑一遍,但是是AAA委派到groups['test_group'][0]去执行的

delegate_to: “{{ groups[‘test’][0] }}” ##当test主机组有多个机器,这是委派到第一个主机执行
run_once:布尔值,它将绕过主机循环,强制任务尝试在第一个可用主机上执行,然后将任何结果和事实应用于同一批次中的所有活动主机。

- hosts: tests
  tasks:
    - name: get hostname
      shell: hostname
      delegate_to: "{{item}}"
      with_items: "{{groups['tests'}}"

去除列表空元素

[root@dddd]$ cat test
1

3
4
5
[root@dddd]$ cat test.yml 
- name: Test
  tags: Test
  hosts: localhost
  gather_facts: False
  vars:
  tasks:
    - name: cat tes
      shell: cat /home/..../test
      register: AAA

    - debug: var=AAA.stdout_lines

    - set_fact:
        BBB: "{{ AAA.stdout_lines|select()|list }}"
  ##另一种在循环的时候先去除loop: "{{ list1|reject('要删除的内容')|list }}"
 ##    - set_fact:  ##或者使用下面这个也是一样
 ##       BBB: "{{ AAA.stdout_lines|reject('match', '^$')|list }}"
    - debug:
        var: BBB

在这里插入图片描述

日期转换

#下例中,我们使用to_datatime过滤器将字符串类型转换成了日期了类型,并且算出了时间差
  - debug:
      msg: '{{ ("2016-08-14 20:00:12"| to_datetime) - ("2012-12-25 19:00:00" | to_datetime) }}'
      ok: [localhost] => {"msg": "1328 days, 1:00:12"}
  #默认情况下,to_datatime转换的字符串的格式必须是“%Y-%m-%d %H:%M:%S”
  #如果对应的字符串不是这种格式,则需要在to_datetime中指定与字符串相同的时间格式,才能正确的转换为时间类型
  - debug:
      msg: '{{ ("20160814"| to_datetime("%Y%m%d")) - ("2012-12-25 19:00:00" | to_datetime) }}'
     ok: [localhost] => {"msg": "1327 days, 5:00:00"}

  #如下方法可以获取到两个日期之间一共相差多少秒
  - debug:
      msg: '{{ ( ("20160814"| to_datetime("%Y%m%d")) - ("20121225" | to_datetime("%Y%m%d")) ).total_seconds() }}'
    ok: [localhost] => {"msg": "114739200.0"}

  #如下方法可以获取到两个日期**“时间位”**相差多少秒,注意:日期位不会纳入对比计算范围
  #也就是说,下例中的2016-08-14和2012-12-25不会纳入计算范围只是计算20:00:12与08:30:00相差多少秒
  #如果想要算出连带日期的秒数差则使用total_seconds()
  - debug:
      msg: '{{ ( ("2016-08-14 20:00:12"| to_datetime) - ("2012-12-25 08:30:00" | to_datetime) ).seconds }}'

  #如下方法可以获取到两个日期“日期位”相差多少天,注意:时间位不会纳入对比计算范围
  - debug:
      msg: '{{ ( ("2016-08-14 20:00:12"| to_datetime) - ("2012-12-25 08:30:00" | to_datetime) ).days }}'
      ok: [localhost] => {"msg": "1328"}

result 结果格式处理

    - debug: var=AAA
 #####将result下的stdout都拼接出来,在一行,用空格隔开,字符串
    - set_fact: 
        AAA_stdout: "{{AAA.results|map(attribute='stdout')|list|join(' ')}}"
    - debug: var=AAA_stdout
  ####将result下的stdout都拼接出来,放在列表
    - debug:
        msg: "{{AAA| json_query('results[*].stdout')}}" 
    ####将result下的item都拼接出来,放在列表
    - debug:
        msg: "{{AAA| json_query('results[*].item')}}" 
  ####将result下的stdout和rc都拼接出来,放在列表
    - debug: 
        msg: "{{ AAA| json_query('results[*].{Stdout:stdout,RC:rc}')}}"
 ####将result下的stdout都拼接出来,放在列表
        set_fact:
          AAA_stdout: "{% for item in AAA.results %} {{ item.stdout }} {% endfor %}"
        set_fact: 
            ping_result_failed: "{% set ping_result_tmp = [] %}{% for item in ping_result.results %}{% if '100% packet loss' in item.stdout  -%}{{ping_result_tmp.append(item.stdout)}}{%- endif %}{%- endfor %}{{ping_result_tmp}}"

        - set_fact:
            backup_result_total: "{{check_backup_file.stdout | regex_findall('.*environment_bak' + current_timestamp.stdout )}}"
        - debug: var=backup_result_total

处理结果为results的形式

  - set_fact:
      AAA: "{{ BBB.results|map(attribute='rc')|list |join(' ')  }}"
 
 cat test.yml 
- name: test
  tags: test
  hosts: localhost
  gather_facts: false
  tasks:
    - name: ping other IP
#      shell: ping  -c 2 "{{item}}" | grep '0% packet loss';/bin/true
      shell: ping  -c 2 "{{item}}" | grep "64 bytes";/bin/true
      register: AAA
      with_items:
           - '127.0.0.2'
           - '127.0.0.5'
    - debug: var=AAA
   
    - set_fact:
        my_list: "{{ my_list|default([]) + [{item.0.item:item.1}] }}"
      with_subelements:
        - "{{ AAA.results}}"
        - stdout_lines

    - debug: var=my_list
 ####AAA的结果如下:结果就是results 下有两个列表
 ok: [localhost] => {
    "AAA": {
        "changed": true, 
        "msg": "All items completed", 
        "results": [
            {
                "_ansible_ignore_errors": null, 
                "_ansible_item_result": true, 
                "_ansible_no_log": false, 
                "_ansible_parsed": true, 
                "changed": true, 
                "cmd": "ping -c 2 \"127.0.0.2\" | grep \"64 bytes\";/bin/true", 
                "delta": "0:00:01.006785", 
                "end": "2022-08-21 17:38:08.486643", 
                "failed": false, 
                "invocation": {
                    "module_args": {
                        "_raw_params": "ping -c 2 \"127.0.0.2\" | grep \"64 bytes\";/bin/true", 
                        "_uses_shell": true, 
                        "chdir": null, 
                        "creates": null, 
                        "executable": null, 
                        "removes": null, 
                        "stdin": null, 
                        "warn": true
                    }
                }, 
                "item": "127.0.0.2", 
                "rc": 0, 
                "start": "2022-08-21 17:38:07.479858", 
                "stderr": "", 
                "stderr_lines": [], 
                "stdout": "64 bytes from 127.0.0.2: icmp_seq=1 ttl=64 time=0.016 ms\n64 bytes from 127.0.0.2: icmp_seq=2 ttl=64 time=0.025 ms", 
                "stdout_lines": [
                    "64 bytes from 127.0.0.2: icmp_seq=1 ttl=64 time=0.016 ms", 
                    "64 bytes from 127.0.0.2: icmp_seq=2 ttl=64 time=0.025 ms"
                ]
            }, 
            {
                "_ansible_ignore_errors": null, 
                "_ansible_item_result": true, 
                "_ansible_no_log": false, 
                "_ansible_parsed": true, 
                "changed": true, 
                "cmd": "ping -c 2 \"127.0.0.5\" | grep \"64 bytes\";/bin/true", 
                "delta": "0:00:01.004975", 
                "end": "2022-08-21 17:38:09.641506", 
                "failed": false, 
                "invocation": {
                    "module_args": {
                        "_raw_params": "ping -c 2 \"127.0.0.5\" | grep \"64 bytes\";/bin/true", 
                        "_uses_shell": true, 
                        "chdir": null, 
                        "creates": null, 
                        "executable": null, 
                        "removes": null, 
                        "stdin": null, 
                        "warn": true
                    }
                }, 
                "item": "127.0.0.5", 
                "rc": 0, 
                "start": "2022-08-21 17:38:08.636531", 
                "stderr": "", 
                "stderr_lines": [], 
                "stdout": "64 bytes from 127.0.0.5: icmp_seq=1 ttl=64 time=0.042 ms\n64 bytes from 127.0.0.5: icmp_seq=2 ttl=64 time=0.019 ms", 
                "stdout_lines": [
                    "64 bytes from 127.0.0.5: icmp_seq=1 ttl=64 time=0.042 ms", 
                    "64 bytes from 127.0.0.5: icmp_seq=2 ttl=64 time=0.019 ms"
                ]
            }
        ]
    }
}
    - set_fact:
    ###将结果中的item和stdout_line拼接起来
        my_list: "{{ my_list|default([]) + [{item.0.item:item.1}] }}"
      with_subelements:
        - "{{ AAA.results}}"  ##用的是AAA.results
        - stdout_lines        ##用的是AAA下面列表中的stdout_lines
ok: [localhost] => {
    "my_list": [
        {
            "127.0.0.2": "64 bytes from 127.0.0.2: icmp_seq=1 ttl=64 time=0.016 ms"
        }, 
        {
            "127.0.0.2": "64 bytes from 127.0.0.2: icmp_seq=2 ttl=64 time=0.025 ms"
        }, 
        {
            "127.0.0.5": "64 bytes from 127.0.0.5: icmp_seq=1 ttl=64 time=0.042 ms"
        }, 
        {
            "127.0.0.5": "64 bytes from 127.0.0.5: icmp_seq=2 ttl=64 time=0.019 ms"
        }
    ]
}

当shell是 shell: ping  -c 2 "{{item}}" | grep '0% packet loss';/bin/true
拼接的结果如下:输出结果这是一行
ok: [localhost] => {
    "my_list": [
        {
            "127.0.0.2": "2 packets transmitted, 2 received, 0% packet loss, time 1000ms"
        }, 
        {
            "127.0.0.5": "2 packets transmitted, 2 received, 0% packet loss, time 999ms"
        }
    ]
}

常见用法

  1. 把结果信息写到标准错误 stderr中:echo "errormsg" >&2
  2. 把结果信息写到标准输出 stdout中: echo "infomsg" >&1 ##默认也会输出到stdout中
  - name: AAA
    shell: echo "errormsg" >&2
    register: BBB

  - debug: var=BBB
###############################################

TASK [debug] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "BBB": {
        "changed": true,
        "cmd": "echo \"errormsg\" >&2",
        "delta": "0:00:00.002315",
        "end": "2024-04-26 16:24:56.463073",
        "failed": false,
        "rc": 0,
        "start": "2024-04-26 16:24:56.460758",
        "stderr": "errormsg",
        "stderr_lines": [
            "errormsg"
        ],
        "stdout": "",
        "stdout_lines": []
    }
}

常见问题

如果结果报错:
ansible.parsing.yaml.objects.AnsibleUnicode object' has no attribute 'stdout'

解决方案:
看该变量system_type在parameter中是不是也有定义,如果有定义,优先会去取parameter中system_type对应的值。外置变量不需要加{{}}也可以
- debug: var=system_type
ansible-playbook -i host.ini --extra-vars “@parameters.yml” --ssh-common-args ‘-F ssh_config’ test111.yml -vvv

when指令因为已经明确是做条件判断,所以它的值必定是一个表达式,它会自动隐式地包围一层 {{}} ,所以在写when指令的条件判断时,不要再手动加上 {{}};when: myname == “1111111”;加上有时候也不会报错,但在某些场景下是错误的,而且Ansible会给出警告。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值