【问题标题】:How to use ansible-playbook --limit with an IP address, rather than a hostname?如何使用 ansible-playbook --limit 与 IP 地址,而不是主机名?
【发布时间】:2019-09-03 09:44:59
【问题描述】:

我们的 INI 样式库存如下所示:

foo-host ansible_host=1.2.3.4 some_var=bla
bar-host ansible_host=5.6.7.8 some_var=blup

我可以使用主机别名将 playbook 运行限制为单个主机:

$ ansible-playbook playbook.yml --limit foo-host

但我不能通过在ansible_host 变量中提及主机的 IP 地址来限制运行:

$ ansible-playbook playbook.yml --limit 1.2.3.4
ERROR! Specified hosts and/or --limit does not match any hosts

我想这样做的原因是因为 Ansible 是由一个只知道 IP 地址而不知道别名的外部系统触发的。

有没有办法让它工作?修改 IP 地址(例如ip_1_2_3_4)是可以接受的。

我考虑过的事情:

  • 打开它并通过IP地址识别所有主机:

    1.2.3.4 some_var=bla
    5.6.7.8 some_var=blup
    

    但现在我们不能再使用漂亮的主机别名了,库存文件的可读性也降低了。

  • 编写一个在常规清单之后运行的自定义清单脚本,并创建一个像 ip_1_2_3_4 这样只包含单个主机的组,因此我们可以使用 --limit ip_1_2_3_4。但是无法从库存脚本访问之前加载的库存,所以我不知道要创建哪些组。

  • 使用group_by module 动态创建新组。但是因为这是一个任务,它只有在 --limit 已经确定没有匹配该模式的主机之后才会运行,并且此时 Ansible 只是放弃并且不再运行 group_by 任务。

【问题讨论】:

    标签: ansible ansible-inventory


    【解决方案1】:

    仍然欢迎更好的解决方案,但目前我正在使用一个小型库存插件(与库存脚本相反)确实可以访问以前添加的库存:

    plugins/inventory/ip_based_groups.py

    import os.path
    import re
    
    from ansible.plugins.inventory import BaseInventoryPlugin
    from ansible.inventory.group import Group
    
    
    PATH_PLACEHOLDER = 'IP_BASED_GROUPS'
    IP_RE = re.compile('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
    
    
    class InventoryModule(BaseInventoryPlugin):
        '''
        This inventory plugin does not create any hosts, but just adds groups based
        on IP addresses. For each host whose ansible_host looks like an IPv4
        address (e.g. 1.2.3.4), a corresponding group is created by prefixing the
        IP address with 'ip_' and replacing dots by underscores (e.g. ip_1_2_3_4).
    
        Use it by putting the literal string IP_BASED_GROUPS at the end of the list
        of inventory sources.
        '''
    
        NAME = 'ip_based_groups'
    
        def verify_file(self, path):
            return self._is_path_placeholder(path)
    
        def parse(self, inventory, loader, path, cache=True):
            if not self._is_path_placeholder(path):
                return
            for host_name, host in inventory.hosts.items():
                ansible_host = host.vars.get('ansible_host', '')
                if self._is_ip_address(ansible_host):
                    group = 'ip_' + ansible_host.replace('.', '_')
                    inventory.add_group(group)
                    inventory.add_host(host_name, group)
    
        def _is_path_placeholder(self, path):
            return os.path.basename(path) == PATH_PLACEHOLDER
    
        def _is_ip_address(self, s):
            return bool(IP_RE.match(s))
    

    ansible.cfg

    [defaults]
    
    # Load plugins from these directories.
    inventory_plugins = plugins/inventory
    
    # Directory that contains all inventory files, and placeholder to create
    # IP-based groups.
    inventory = inventory/,IP_BASED_GROUPS
    
    [inventory]
    
    # Enable our custom inventory plugin.
    enable_plugins = ip_based_groups, host_list, script, auto, yaml, ini, toml
    

    【讨论】:

      【解决方案2】:

      问:Playbook 运行一个脚本运行一个 playbook...它可以工作,但它有点 hacky

      答:FWIW。可以使用 json_query 并避免脚本。例如

      - hosts: all
        gather_facts: false
        tasks:
          - set_fact:
              my_host: "{{ hostvars|dict2items|json_query(query)|first }}"
            vars:
              query: "[?value.ansible_host == '{{ my_host_ip }}' ].key"
            run_once: true
          - add_host:
              hostname: "{{ my_host }}"
              groups: my_group
            run_once: true
      
      - hosts: my_group
        gather_facts: false
        tasks:
          - debug:
              var: inventory_hostname
      

      问:“很遗憾,我正在从 AWX 运行 ansible-playbook,因此不允许使用包装脚本。”

      答:可以从剧本中运行脚本。例如下面的剧本

      - hosts: all
        gather_facts: false
        tasks:
          - set_fact:
              my_host: "{{ lookup('pipe', playbook_dir ~ '/script.sh ' ~ my_host_ip) }}"
            delegate_to: localhost
            run_once: true
          - add_host:
              hostname: "{{ my_host }}"
              groups: my_group
            run_once: true
      
      - hosts: my_group
        gather_facts: false
        tasks:
          - debug:
              var: inventory_hostname
      

      给予

      $ ansible-playbook -e 'my_host_ip=10.1.0.53' play.yml
      
      PLAY [all] ********************
      
      TASK [set_fact] ***************
      ok: [test_01 -> localhost]
      
      TASK [add_host] ***************
      changed: [test_01]
      
      PLAY [my_group] ***************
      
      TASK [debug] ************
      ok: [test_03] => {
          "inventory_hostname": "test_03"
      }
      

      (使脚本仅打印主机名。)

      问:我可以通过使用主机别名将 playbook 运行限制在单个主机上:

      $ ansible-playbook playbook.yml --limit foo-host
      

      但我不能通过从 ansible_host 变量中提及主机的 IP 地址来限制运行

      $ ansible-playbook playbook.yml --limit 1.2.3.4
      

      答:ansible-inventoryjq 能够解析主机。例如脚本

      #!/bin/bash
      my_host="$(ansible-inventory --list | jq '._meta.hostvars | to_entries[] | select (.value.ansible_host=="'"$1"'") | .key')"
      my_host="$(echo $my_host | sed -e 's/^"//' -e 's/"$//')"
      echo host: $my_host
      ansible-playbook -l $my_host play.yml
      

      有库存

      test_01 ansible_host=10.1.0.51
      test_02 ansible_host=10.1.0.52
      test_03 ansible_host=10.1.0.53
      

      和 playbook.yml

      - hosts: all
        gather_facts: false
        tasks:
          - debug:
              var: inventory_hostname
      

      给予

      $ ./script.bash 10.1.0.53
      host: test_03
      
      PLAY [all] **********
      
      TASK [debug] ************
      ok: [test_03] => {
          "inventory_hostname": "test_03"
      }
      

      【讨论】:

      • 谢谢!不幸的是,我正在从 AWX 运行 ansible-playbook,因此不允许使用包装脚本 :(
      • 没问题。可以从剧本运行脚本。
      • 哈哈,是的,这是真的 :D 运行脚本的剧本运行剧本......它会工作,但它有点 hacky,并且可能很难通过所有其他变量像 @ 987654333@、--vault-password-file--user,还有谁知道呢。
      • FWIW。没有脚本也可以运行它。
      猜你喜欢
      • 2022-06-28
      • 2021-01-18
      • 1970-01-01
      • 2016-09-27
      • 2014-12-19
      • 1970-01-01
      • 2016-05-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多