Ansible 文件行重新添加条目 [英] Ansible line-in-file re-adding entries

查看:26
本文介绍了Ansible 文件行重新添加条目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要解决的问题:我有几台服务器(在本例中为 3、1 个 nfs 服务器、2 个客户端 - 对于这个最小示例,它们都解析回本地主机),并且客户端需要访问服务器上的共享,这是使用剧本创建的.

What I'm trying to solve: I have several servers (in this example 3, 1 nfs server, 2 clients - they all resolve back to localhost for this minimal example), and the clients need to access shares on the server, which are created using the playbook.

客户端的 IP 地址需要添加到/etc/exports 中的相应条目中 - 该列表在任何给定时间都未预定义.(在我的实际剧本中,我使用了可靠的事实,对于这个例子,我将它们添加为变量)

The IP addresses of the clients need to be added to the respective entries in /etc/exports as they go - the list is not predefined at any given time. (In my actual playbook I use ansible facts, for this example I've added them as a variable)

Ansible lineinfile regexp 中管理/etc/exports Vladimir非常友善,可以帮助完成最初的事情,这可行,但似乎不是幂等的.第一次正确添加了条目/ip 地址,但是第二次运行时,IP 地址被重新添加到/etc/exports 中的条目中,导致当时 nfs 被炸毁(重复条目)

In Ansible lineinfile regexp to manage /etc/exports Vladimir was so kind as to help with an initial thing, which works, but doesn't seem to be idempotent. The entries / ip addresses are added correctly the first time round, but the second run the IP addresses get re-added to the entries in /etc/exports, causing nfs to bomb out at that time (double entries)

更正/etc/exports:

Correct /etc/exports:

bar 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check) 
foo 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)

我得到了什么:

bar 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check) 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)
foo 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check) 192.168.34.47(rw,sync,no_root_squash,no_subtree_check) 192.168.34.46(rw,sync,no_root_squash,no_subtree_check)

我一直在思考它,但我想不出任何有效的方法.我已将其提炼为以下剧本:

I've been butting my head around it but I can't come up with anything that works. I've distilled it down to the following playbook:

$ ansible-playbook main.yml

main.yml 的内容:

- hosts: localhost
  become: yes
  vars:
    client_ip: 192.168.34.46
    nfs_server: localhost
    shares:
      - bar
      - foo
  tasks:
  - name: Mountpoint management
    ansible.builtin.include_tasks: task.yml
    loop: "{{ shares | default([]) }}"
    loop_control:
      loop_var: volume
    args:
      apply:
        delegate_to: "{{ nfs_server }}"
        
- hosts: localhost
  become: yes
  vars:
    client_ip: 192.168.34.47
    nfs_server: localhost
    shares:
      - bar
      - foo

  tasks:
  - name: Mountpoint management
    ansible.builtin.include_tasks: task.yml
    loop: "{{ shares | default([]) }}"
    loop_control:
      loop_var: volume
    args:
      apply:
        delegate_to: "{{ nfs_server }}"

执行循环所需的第二个文件 task.yml:

Second file task.yml which is needed to do a loop:

---
---
- name: Ensure /etc/exports exists
  ansible.builtin.file:
    path: /etc/exports
    owner: root
    group: root
    mode: '0644'
    state: touch
  changed_when: False

- name: Add host {{ client_ip }} to {{ volume }}
  ansible.builtin.lineinfile:
    path: "/etc/exports"
    regex: '^{{ volume }}(\s+)({{ ip_regex }})*({{ mount_opts_regex }})*(\s*)(.*)$'
    line: '{{ volume }}\g<1>{{ ip }}{{ mount_opts }} \g<5>'
    backrefs: true
  vars:
    ip: "{{ client_ip }}"
    ip_regex: '{{ client_ip | regex_escape() }}'
    mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
    mount_opts_regex: '\(.*?\)'


- name: Read /etc/exports
  command: "cat {{ item }}"
  register: result
  check_mode: no
  loop:
    - /etc/exports
  changed_when: False

- ansible.builtin.set_fact:
    content: "{{ dict(_files|zip(_lines)) }}"
  vars:
    _lines: "{{ result.results|map(attribute='stdout_lines')|list }}"
    _files: "{{ result.results|map(attribute='item')|list }}"

- name: Add new line to /etc/exports
  ansible.builtin.lineinfile:
    path: "/etc/exports"
    line: '{{ volume }} {{ client_ip }}{{ mount_opts }}'
  vars:
    mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
  loop: "{{ content }}"
  when: content[item] | select('search', volume)|length == 0

推荐答案

好吧,经过深思熟虑,我终于开始构建两个列表,将它们映射到一个 dict 并检查这些值是否已经存在.如果是,不要做任何事情,如果不是,添加它.

Well, thinking it through I finally went with constructing two lists, mapping those into a dict and checking if the values are already present. If they are, don't do anything, if not, add it.

main.yaml:

- hosts: localhost
  become: yes
  vars:
    client_ip: 192.168.34.46
    nfs_server: localhost
    shares:
      - bar
      - foo
  tasks:
  - name: Mountpoint management
    ansible.builtin.include_tasks: task.yml
    loop: "{{ shares | default([]) }}"
    loop_control:
      loop_var: volume
    args:
      apply:
        delegate_to: "{{ nfs_server }}"
        
- hosts: localhost
  become: yes
  vars:
    client_ip: 192.168.34.47
    nfs_server: localhost
    shares:
      - bar
      - foo

  tasks:
  - name: Mountpoint management
    ansible.builtin.include_tasks: task.yml
    loop: "{{ shares | default([]) }}"
    loop_control:
      loop_var: volume
    args:
      apply:
        delegate_to: "{{ nfs_server }}"

task.yaml:

---
- name: Ensure /etc/exports exists
  ansible.builtin.file:
    path: /etc/exports
    owner: root
    group: root
    mode: '0644'
    state: touch
  changed_when: false

- name: Read /etc/exports
  command: "cat /etc/exports"
  register: result
  check_mode: no
  changed_when: false

- ansible.builtin.set_fact:
    share_names: "{{ share_names | default([]) + [item.split(' ')[0]] }}"
    share_values: "{{ share_values | default([]) + [item.split(' ')[1:]] }}"
  loop:
    "{{ result.stdout_lines }}"

- ansible.builtin.set_fact:
    share_content: "{{ dict(share_names | zip(share_values)) }}"

- name: Add host {{ client_ip }} to {{ volume }}
  ansible.builtin.lineinfile:
    path: "/etc/exports"
    regex: '^{{ volume }}(\s+)({{ ip_regex }})*({{ mount_opts_regex }})*(\s*)(.*)$'
    line: '{{ volume }}\g<1>{{ client_ip }}{{ mount_opts }} \g<5>'
    backrefs: true
  vars:
    ip_regex: '{{ client_ip | regex_escape() }}'
    mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
    mount_opts_regex: '\(.*?\)'
    str: '{{ client_ip }}{{ mount_opts }}'
  when: volume in share_content and str not in share_content[volume]

- name: Add new line to /etc/exports
  ansible.builtin.lineinfile:
    path: "/etc/exports"
    line: '{{ volume }} {{ client_ip }}{{ mount_opts }}'
  vars:
    mount_opts: '(rw,sync,no_root_squash,no_subtree_check)'
  loop: "{{ share_content }}"
  when: volume not in share_content

这篇关于Ansible 文件行重新添加条目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆