首页 > 解决方案 > Ansible:唯一的最短路径

问题描述

语境

我有一个要删除的文件和目录的列表。
这是从 rsync 标准输出中以单词“deleting”开头的行获得的。

rsync stdout_lines: [
    "building file list ... done",
    "*deleting   Lab02/Ex2/Doc1.txt",
    "*deleting   Lab02/Ex2/",
    "*deleting   Lab02/Ex1/Doc1.txt",
    "*deleting   Lab02/Ex1/",
    "*deleting   Lab02/",
    ".d..t...... ./",
    "*deleting   Lab01/Ex2/Doc1.txt",
    "*deleting   Lab01/Ex2/",
    "*deleting   Lab01/Ex1/Doc2.txt",
    "*deleting   Lab01/Ex1/Doc1.txt",
    ".d..t...... Lab01/",
    ".d..t...... Lab01/Ex1/",
    "sent 350 bytes  received 191 bytes  360.67 bytes/sec",
    "total size is 614  speedup is 1.13 (DRY RUN)"
]  

格式化使用:

'{{ sync_return.stdout_lines | select("regex", "^[*]deleting") | map("regex_replace", "^[*]deleting", "") | map("regex_replace", " ", "") | list }}'

此列表格式的原始示例如下:

formatted list: [
    "Lab02/Ex2/Doc1.txt",
    "Lab02/Ex2/",
    "Lab02/Ex1/Doc1.txt",
    "Lab02/Ex1/",
    "Lab02/",
    "Lab01/Ex2/Doc1.txt",
    "Lab01/Ex2/",
    "Lab01/Ex1/Doc2.txt",
    "Lab01/Ex1/Doc1.txt"
]

为了加快删除过程(通过减少要迭代的元素数量) - 我将列表分为 2 个子列表:

上面示例的子列表将是...

directories to delete: [
    "Lab02/Ex2/",
    "Lab02/Ex1/",
    "Lab02/",
    "Lab01/Ex2/"
]

files to delete: [
    "Lab01/Ex1/Doc2.txt",
    "Lab01/Ex1/Doc1.txt"
]

问题

虽然当前的解决方案有效,但它并不是最好的。梦想是有一个解决方案,其中“要删除的目录”列表仅包含可能的最高级别目录。即由于我们知道目录“Lab02/”正在被删除,“要删除的目录”将不包含“Lab02/Ex2/”或“Lab02/Ex1/”。
我相信我的目标有点类似于 os.path.commonprefix python 函数,但是必须为列表中的各种文件路径完成。

我对 Ansible 比较陌生,因此我们将不胜感激任何有关此问题的指导/帮助。

标签: listfilteransiblejinja2

解决方案


我不会问你为什么要实现它,我会把它当作一个练习。

想法是,您可以按字母顺序对目录进行排序,然后在循环路径时,删除以上一行开头的任何目录。

您可以像这样编写过滤器(放在filter_plugins目录中):

def common_paths(paths=[]):
    sorted_paths = sorted(paths)
    pfx = sorted_paths[0]
    for path in sorted_paths[1:]:
      if re.compile("^%s.*" % pfx).match(path):
         sorted_paths.remove(path)
      else:
         pfx = path
    return sorted_paths

class FilterModule(object):
    def filters(self):
        return { 'common_paths': common_paths }

然后:

 - name: Filter
   set_fact:
       bar: "{{ foo | common_paths }}"

本地测试:

---
 
- hosts: localhost
  tasks:
    - name: Test data
      set_fact: 
        foo:
          - 'Lab01/'
          - 'Lab01/Ex5/'
          - 'Ex2/foo3/'
          - 'Ex2/foo2/'
          - 'Ex2/'
          - 'Lab03/Ex5/e/'
          - 'Lab02/y/z/Lab01/1/'
          - 'Lab02/y/z/Lab01/3/'
          - 'Lab01/Ex5/Lab02/'
          - 'Lab03/Ex5/d/1'

    - name: Filter
      set_fact: 
        bar: "{{ foo | common_paths }}"

输出:

$ ansible-playbook common_paths.yml  -vvv
ansible-playbook 2.10.4

PLAYBOOK: common_paths.yml *********************************************************************************
1 plays in common_paths.yml

PLAY [localhost] *********************************************************************************

TASK [Gathering Facts] *********************************************************************************
ok: [localhost]

TASK [Test data] *********************************************************************************    task path: /home/guido/Development/git/ansible-local/common_paths.yml:5
ok: [localhost] => {
    "ansible_facts": {
        "foo": [
            "Lab01/",
            "Lab01/Ex5/",
            "Ex2/foo3/",
            "Ex2/foo2/",
            "Ex2/",
            "Lab03/Ex5/e/",
            "Lab02/y/z/Lab01/1/",
            "Lab02/y/z/Lab01/3/",
            "Lab01/Ex5/Lab02/",
            "Lab03/Ex5/d/1/"
        ]
    },
    "changed": false
}

TASK [Filter] *********************************************************************************
task path: /home/guido/Development/git/ansible-local/common_paths.yml:19
ok: [localhost] => {
    "ansible_facts": {
        "bar": [
            "Ex2/",
            "Lab01/",
            "Lab02/y/z/Lab01/1/",
            "Lab02/y/z/Lab01/3/",
            "Lab03/Ex5/d/1/",
            "Lab03/Ex5/e/"
        ]
    },
    "changed": false
}

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

推荐阅读