【问题标题】:Validate line with multiple config variables使用多个配置变量验证行
【发布时间】:2021-02-07 04:28:50
【问题描述】:

我正在学习 ansible,但无法用简单的 asnible 来解决这个问题。

我有一个文件包含:

net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory

这是一行,例如,我想检查 cgroup_memory 是否设置为 1,如果不设置为 1,以及是否缺少整个设置,请将其附加到行尾。

我知道如何用 bash 和 sed 来做这件事的非常简单的方法,但是我不能用 ansible “正确”的方式来解决问题,而不使用 lineinfile 并替换整行...如前所述 lineinfile 正在使用整个行,所以我认为这不是理想的..我研究的下一个模块是替换它可能会起作用,但我不能只寻找“cgroup_memory = 1”,因为它会查看“cgroup_memory = 0”并评估为不是“cgroup_memory=1”,我最终在一个文件中得到 cgroup_memory=1 cgroup_memory=0 ......不知何故,我需要提取值,比较和替换值(保持整行不变),如果它不存在附加到行尾……

这是我想要的 bash 版本: https://pastebin.com/1TCz30Nq

这是我最接近我需要的地方:

- name: "Check for cgroup_memory"
  shell: grep -v '^[[:space:]]*#' /tmp/cmdline.txt_old | grep -icP "cgroup_memory=-?\d+[[:space:]]" || true
  register: grep_value
  changed_when: False

- debug:
    var: grep_value

- name: "Variable Missing: Add cgroup_memory=1 to end of line"
  become: true
  replace: dest=/tmp/cmdline.txt_old regexp='(\s+)$' replace=' cgroup_memory=1'
  when: grep_value.stdout == "0"

- name: "Variable Exist: Set cgroup_memory=1"
  become: true
  replace: dest=/tmp/cmdline.txt_old regexp='cgroup_memory=\d+' replace='cgroup_memory=1'
  when: grep_value.stdout == "1"

这不会忽略 # 行,因此替换将愉快地更改 cmets 中的值,这是不好的。这也没有评估价值,但我想我可以在确定它在那里之后进行 grep 和拆分。我不想抱怨,但我认为 ansible 是配置管理的完美选择,也是我做的第一件简单的事情很多通过 shell 脚本并不是那么简单......(如果我不想使用 shell 模块)

【问题讨论】:

    标签: ansible


    【解决方案1】:

    确保cgroup_memory 设置为1 的一种方法是使用Ansible replace module。 replace 模块可以匹配一个regexp 并用我们想要的替换它。

    例子:

      tasks:
      - name: set cgroup_memory to 1
        replace:
          path: /path/to/file
          regexp: 'cgroup_memory=\d+'
          replace: 'cgroup_memory=1'
    

    如果有不止一行有这个表达式,这将替换所有出现的地方。我们可以在上述任务中使用beforeafter参数来定位特定的。

    或者我们可以使用lineinfile module 确保存在整行。

      tasks:
      - name: ensure line with cgroup_memory is present in file
        lineinfile:
          path: /path/to/file
          regexp: 'cgroup_memory=\d+'
          line: "net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory"
    

    【讨论】:

    • 这实际上非常好,但是如果它不存在,它可以将它附加到行尾......还是必须在单独的任务中构建?之前和之后只在“line”之后的“line”之前工作吧?
    • 为此有一个 Ansible lineinfile 模块。让我更新我的答案。
    • 是的,replace 模块可以引用行,而不是 BOF 或 EOF。
    【解决方案2】:

    恕我直言,最好的选择是blockinfile。给定下面的配置文件

    shell> cat test.cfg
    line1
    line2
    net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_memory=1 cgroup_enable=memory
    line4
    line5
    

    首先标记该行。 blockinfile 需要标记来找到块(在这种情况下是一行)。例如

        - lineinfile:
            path: test.cfg
            insertbefore: "^net.ifnames(.*)$"
            line: "# BEGIN ANSIBLE MANAGED BLOCK my_block_01"
        - lineinfile:
            path: test.cfg
            insertafter: "^net.ifnames(.*)$"
            line: "# END ANSIBLE MANAGED BLOCK my_block_01"
    

    给予

    shell> cat test.cfg
    line1
    line2
    # BEGIN ANSIBLE MANAGED BLOCK my_block_01
    net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait=fixrtc cgroup_memory=1 cgroup_enable=memory 
    # END ANSIBLE MANAGED BLOCK my_block_01
    line4
    line5
    

    上面的 lineinfile 任务是幂等的。然后使用配置创建一个变量。例如

    shell> cat net_cfg.yml 
    net_cfg_01:
      - {key: 'net.ifnames', val: 0}
      - {key: 'dwc_otg.lpm_enable', val: 0}
      - {key: 'console', val: 'serial0,115200'}
      - {key: 'console', val: 'tty1'}
      - {key: 'root', val: 'LABEL=writable'}
      - {key: 'rootfstype', val: 'ext4'}
      - {key: 'elevator', val: 'deadline'}
      - {key: 'rootwait', val: 'fixrtc'}
      - {key: 'cgroup_memory', val: 1}
      - {key: 'cgroup_enable', val: 'memory'}
    

    必须使用列表而不是更简单的字典,因为键不是唯一的。创建一个模板。例如

    shell> cat templates/net_cfg_01.j2
    {% for i in net_cfg_01 %}{{i.key}}={{i.val}} {% endfor %}
    

    现在您可以在文件中包含变量并配置该行

        - include_vars: net_cfg.yml
        - blockinfile:
            path: test.cfg
            block: "{{ lookup('template', 'net_cfg_01.j2') }}"
            marker: "# {mark} ANSIBLE MANAGED BLOCK my_block_01"
    

    注意事项

    • 根据您的需要和文件的注释格式调整 标记
    • 模板在行尾之前留有空白。必要时使用loop.last 修复它
    • 所有任务都是幂等的

    如何使用

    关于如何使用上述框架有很多选择。例如,将配置变量放入列表中

    shell> cat net_cfg.yml
    net_cfg_01:
      ... 
      - {key: 'cgroup_memory', val: {{ my_cgroup_memory }}}
    

    那么下面的命令将确保文件配置正确

    shell> ansible-playbook playbook.yml -e 'my_cgroup_memory=1'
    

    【讨论】:

    • 谢谢,这实际上对我需要的另一件事非常有用 :) 是的,它可以解决这个问题,但这意味着该行在所有服务器上的所有其他参数都相同,这可能不是例如,在少数服务的情况下,console=tty2 将是有效的,我最终会替换它...看起来对我来说最好的解决方案是使用 shell 模块并 grep/split 文件中的值,注册它而不是在另一个任务中用 "when:" 评估它,我有点希望在 ansible 中有一些更简单的方法,因为您需要多次评估配置中的变量。
    • 1) 如果没有必要,不要使用 shell/grep/split。而在你的情况下,它不是。 2) 使 net_cfg_01 中的所有值都可配置,并将配置放在您想要和需要的任何位置。见Variable Precedence: Where Should I Put A Variable?。事实上,这是一个简单的解决方案。
    • 我认为我们彼此不了解...假设您有 50 台具有此配置的服务器,您唯一的兴趣是强制执行 my_cgroup_memory=1 行中的其他值不是您关心的问题,它们每台服务器上的值可能不同,我无法保留每台服务器的值,因为客户/管理员可以随时更改它们...这是我想在 ansible 中实现的简化 shell 代码:pastebin.com/1TCz30Nq 它就像 10 行实际代码,不会更改任何其他值并在其丢失时附加到行尾......在ansible中必须有一个更简单的解决方案
    • 我明白了。在 Ansible 中,恐怕不是一个更简单的解决方案。 lineinfile 识别行有问题,replace 无法添加缺失的属性。 blockinfiletemplate 保留,但不能使用,因为并非所有属性都在您的控制之下。在这种情况下,您真的只能靠自己使用 shell
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 2021-07-11
    • 2011-10-20
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多