ansible-是否从目录中删除非托管文件?

ansible-是否从目录中删除非托管文件?,ansible,ansible-template,Ansible,Ansible Template,我想递归地复制目录并将其中的所有.j2文件作为模板呈现。为此,我目前使用以下行: - template: > src=/src/conf.d/{{ item }} dest=/dest/conf.d/{{ item|replace('.j2','') }} with_lines: find /src/conf.d/ -type f -printf "%P\n" 现在我正在寻找一种从这个目录中删除非托管文件的方法。例如,如果我从/src/

我想递归地复制目录并将其中的所有.j2文件作为模板呈现。为此,我目前使用以下行:

- template: >
            src=/src/conf.d/{{ item }}
            dest=/dest/conf.d/{{ item|replace('.j2','') }}
  with_lines: find /src/conf.d/ -type f -printf "%P\n"
现在我正在寻找一种从这个目录中删除非托管文件的方法。例如,如果我从
/src/conf.d/
中删除一个文件/模板,我希望Ansible也从
/dest/conf.d/
中删除它


有没有办法做到这一点?我试着摆弄
rsync--delete
,但是我发现模板有一个问题,它们的后缀
.j2
被删除了。

可能有两种方法来处理这个问题,但是在模板步骤之前是否可以在任务中完全清空目标目录?或者将模板文件放到临时目录中,然后在后续步骤中删除并重命名?

我会这样做,假设顶部有一个定义为“managed_files”的变量,即列表

-shell:ls-1/some/dir
注册:目录
-文件:path=/some/dir/{{item}}state=不存在
带\u项:contents.stdout\u行
何时:项目不在托管_文件中

显然,目前ansible不可能做到这一点。我和mdehaan在IRC上进行了一次对话,归结起来是因为没有资源,这让事情变得非常困难

向mdehaan询问一个示例,例如,授权管理sudoers.d目录,他想到了以下几点:

14:17 < mdehaan> Robe: http://pastebin.com/yrdCZB0y
14:19 < Robe> mdehaan: HM
14:19 < Robe> mdehaan: that actually looks relatively sane
14:19 < mdehaan> thanks :)
14:19 < Robe> the problem I'm seeing is that I'd have to gather the managed files myself
14:19 < mdehaan> you would yes
14:19 < mdehaan> ALMOST
14:20 < mdehaan> you could do a fileglob and ... well, it would be a little gross
[..]
14:32 < mdehaan> eh, theoretical syntax, nm
14:33 < mdehaan> I could do it by writing a lookup plugin that filtered a list
14:34 < mdehaan> http://pastebin.com/rjF7QR24
14:34 < mdehaan> if that plugin existed, for instance, and iterated across lists in A that were also in B
14:17长袍:http://pastebin.com/yrdCZB0y
14:19mdehaan:HM
14:19mdehaan:这看起来相对正常
14:19谢谢:)
14:19我看到的问题是,我必须自己收集托管文件
14:19你会同意的
14:19几乎
14:20你可以做一个fileglob然后。。。那会有点恶心
[..]
14:32eh,理论语法,nm
14:33我可以通过编写一个过滤列表的查找插件来实现
14:34http://pastebin.com/rjF7QR24
14:34例如,如果该插件存在,并且在A中的列表中迭代,也在B中迭代

通常我不会删除文件,但会在其名称中添加
-unmanaged
后缀。 任务示例:

- name: Get sources.list.d files
  shell: grep -r --include=\*.list -L '^# Ansible' /etc/apt/sources.list.d || true
  register: grep_unmanaged
  changed_when: grep_unmanaged.stdout_lines

- name: Add '-unmanaged' suffix
  shell: rename 's/$/-unmanaged/' {{ item }}
  with_items: grep_unmanaged.stdout_lines
解释

Grep命令使用:

  • -r
    执行递归搜索
  • --include=\*.list
    -仅获取文件 在递归搜索期间使用.list扩展名
  • -L'^#Ansible'
    -显示没有以'#Ansible'开头的行的文件名
  • | | true
    -用于忽略错误。Ansible的
    ignore_errors
    也可以工作,但在忽略错误之前,Ansible将在Ansible playbook运行期间以红色显示它 这是不想要的(至少对我来说)
然后我将grep命令的输出注册为一个变量。当grep显示任何输出时,我将此任务设置为已更改(当对此负责时,
changed\u行)

在下一个任务中,我迭代grep输出(即grep返回的文件名),并运行rename命令为每个文件添加后缀


就这些。下次运行命令时,第一个任务应为绿色,第二个任务将被跳过。

我们使用nginx文件执行此操作,因为我们希望它们以特殊顺序来自模板,但删除非托管文件这一操作有效:

  # loop through the nginx sites array and create a conf for each file in order
  # file will be name 01_file.conf, 02_file.conf etc
  - name: nginx_sites conf
    template: >
      src=templates/nginx/{{ item.1.template }}
      dest={{ nginx_conf_dir }}/{{ '%02d' % item.0 }}_{{ item.1.conf_name|default(item.1.template) }}
      owner={{ user }}
      group={{ group }}
      mode=0660
    with_indexed_items: nginx_sites
    notify:
      - restart nginx
    register: nginx_sites_confs

  # flatten and map the results into simple list
  # unchanged files have attribute dest, changed have attribute path
  - set_fact:
      nginx_confs: "{{ nginx_sites_confs.results|selectattr('dest', 'string')|map(attribute='dest')|list + nginx_sites_confs.results|selectattr('path', 'string')|map(attribute='path')|select|list }}"
    when: nginx_sites

  # get contents of conf dir
  - shell: ls -1 {{ nginx_conf_dir }}/*.conf
    register: contents
    when: nginx_sites

  # so we can delete the ones we don't manage
  - name: empty old confs
    file: path="{{ item }}" state=absent
    with_items: contents.stdout_lines
    when: nginx_sites and item not in nginx_confs
技巧(如您所见)是模板和with_项在寄存器结果中具有不同的属性。然后将它们转换为您管理的文件列表,然后获得目录列表,并删除不在该列表中的文件


如果您已经有一个文件列表,那么可以用更少的代码来完成。但在本例中,我正在创建一个索引列表,因此需要使用map创建该列表。

我想与大家分享我在本例中的经验

Ansible from 2.2 is had with_filetree loop提供了上传目录、链接、静态文件甚至(!)模板的简单方法。这是保持配置目录同步的最佳方法

- name: etc config - Create directories
  file:
    path: "{{ nginx_conf_dir }}/{{ item.path }}"
    state: directory
    mode: 0755
  with_filetree: etc/nginx
  when: item.state == 'directory'

- name: etc config - Creating configuration files from templates
  template:
    src: "{{ item.src }}"
    dest: "{{ nginx_conf_dir }}/{{ item.path | regex_replace('\\.j2$', '') }}"
    mode: 0644
  with_filetree: etc/nginx
  when:
    - item.state == "file"
    - item.path | match('.+\.j2$') | bool

- name: etc config - Creating staic configuration files
  copy:
    src: "{{ item.src }}"
    dest: "{{ nginx_conf_dir }}/{{ item.path }}"
    mode: 0644
  with_filetree: etc/nginx
  when:
    - item.state == "file"
    - not (item.path | match('.+\.j2$') | bool)

- name: etc config - Recreate symlinks
  file:
    src: "{{ item.src }}"
    dest: "{{ nginx_conf_dir }}/{{ item.path }}"
    state: link
    force: yes
    mode: "{{ item.mode }}"
  with_filetree: etc/nginx
  when: item.state == "link"
接下来,我们可能需要从config dir中删除未使用的文件。很简单。 我们收集上传文件和远程服务器上存在的文件的列表,然后删除差异

但我们可能希望在config dir中有非托管文件。 我使用了
find
-prune
功能来避免清除包含非托管文件的文件夹

PS(Y)\在我删除了一些非托管文件后,请确定

- name: etc config - Gathering managed files
  set_fact:
    __managed_file_path: "{{ nginx_conf_dir }}/{{ item.path | regex_replace('\\.j2$', '') }}"
  with_filetree: etc/nginx
  register: __managed_files

- name: etc config - Convert managed files to list
  set_fact: managed_files="{{ __managed_files.results | map(attribute='ansible_facts.__managed_file_path') | list }}"

- name: etc config - Gathering exist files (excluding .ansible_keep-content dirs)
  shell: find /etc/nginx -mindepth 1 -type d -exec test -e '{}/.ansible_keep-content' \; -prune -o -print
  register: exist_files
  changed_when: False

- name: etc config - Delete unmanaged files
  file: path="{{ item }}" state=absent
  with_items: "{{ exist_files.stdout_lines }}"
  when:
    - item not in managed_files

以下是我想到的:

- template: src=/source/directory{{ item }}.j2 dest=/target/directory/{{ item }} register: template_results with_items: - a_list.txt - of_all.txt - templates.txt - set_fact: managed_files: "{{ template_results.results|selectattr('invocation', 'defined')|map(attribute='invocation.module_args.dest')|list }}" - debug: var: managed_files verbosity: 0 - find: paths: "/target/directory/" patterns: "*.txt" register: all_files - set_fact: files_to_delete: "{{ all_files.files|map(attribute='path')|difference(managed_files) }}" - debug: var: all_files verbosity: 0 - debug: var: files_to_delete verbosity: 0 - file: path={{ item }} state=absent with_items: "{{ files_to_delete }}" -模板:src=/source/directory{{item}}.j2 dest=/target/directory/{{item} 注册:模板\u结果 有以下项目: -a_list.txt -of_all.txt -templates.txt -设定事实: 托管_文件:“{template_results.results}selectattr('invocation','defined')| map(attribute='invocation.module_args.dest')| list}” -调试: var:managed_文件 详细程度:0 -查找: 路径:“/target/directory/” 模式:“*.txt” 注册:所有文件 -设定事实: files_to_delete:“{{all_files.files | map(attribute='path')| difference(managed_files)}” -调试: var:所有文件 详细程度:0 -调试: 变量:要删除的文件 详细程度:0 -文件:路径={item}}}状态=不存在 与_项一起:“{{files_to_delete}”
  • 这将生成模板(无论您希望以何种方式),并将结果记录在“模板结果”中
  • 结果被破坏,得到每个模板的“dest”的简单列表。跳过的模板(由于when条件,未显示)没有“invocation”属性,因此会被过滤掉
  • 然后使用“find”来获取除非特别编写,否则应该不存在的所有文件的列表
  • 然后,这将被破坏,以获得当前文件的原始列表,然后删除“应该在那里”的文件
  • 然后删除剩余的“要删除的文件”
优点:可以避免在删除过程中出现多个“跳过”条目

缺点:如果要执行mult,则需要将每个模板连接到results.results