将 Ansible 变量从一个角色(在一个主机上运行)传递给在同一剧本中的另一台主机上运行的另一个角色

Posted

技术标签:

【中文标题】将 Ansible 变量从一个角色(在一个主机上运行)传递给在同一剧本中的另一台主机上运行的另一个角色【英文标题】:Pass Ansible variables from one role (running on one host) to another role running on another host within the same playbook 【发布时间】:2015-06-04 19:05:06 【问题描述】:

我有一本在不同主机上运行不同角色的剧本。 是否可以将变量从在一个主机上运行的一个角色传递到在同一剧本运行中运行的另一台主机上的另一个角色?或者任何解决方法?

playbook
   host1
     role1
       here I get some variables: var1 var2 ...etc
   host2
     role2
       here I need to use var1 var2 ... etc from the above host/role

role1 中设置变量 db 的任务如下所示:

- shell: cd /ACE/conf && grep ^db.url local1.properties | awk -F/ 'print $4' | awk -F? 'print $1'
  register: db

更新:在第一台主机上,这些值是动态的,就像一个始终更新的配置文件。在我使用角色 1 将值存储在主机 1 上的变量中之后,我移动到主机 2,运行角色 2 并使用主机 1 存储的变量中的这些值进行处理。

我尝试使用主机变量:

 hostvars.LBL.db.stdout 
 hostvars['LBL']['db'] 
 hostvars['LBL']['db']['stdout'] 

我得到错误:

in get_variables raise Exception("host not found: %s" % hostname) Exception: host not found: LBL

LBL 存在于主机中,因为我运行第一个角色

我在一台主机上设置了一个变量,我希望该变量对另一台主机可用。所有这些都在一个剧本中。可以吗?

hostvars 不能像这样使用它:

---   
- name: test hostvars host1
  hosts: LBL
  tasks:
    - command: "ls /bin"
        register: ls_out

- name: test hostvars host2
  hosts: LM
  tasks:
    - debug:
        var: " hostvars['LBL']['ls_out']['stdout'] "

错误:

fatal: [10.104.148.138] => host not found: LBL

/etc/ansible/hosts

[root@NS1 ansible]# cat /etc/ansible/hosts
[LBL]
10.104.148.136
[LM]
10.104.148.138

【问题讨论】:

你能发布剧本中设置变量 var1/2 的部分吗? hostvars.LBL.* 应该有效。如果 var1/2 是在库存文件中定义的,那么也发布它。 这是第一个角色 - name: bla bla shell: cd /ACE/conf && grep ^db.url local1.properties | awk -F/ 'print $4' | awk -F? 'print $1' register: db 如果我这样做并在同一角色中回显 do.stdout 效果很好。我不使用库存。在第二个角色中,如果我这样做 shell: echo " hostvars['LBL']['db']['stdout'] " 它会给出错误 “hostvars 无法像这样使用它:”这是不可能的。这两个任务对我来说工作得很好,并按预期打印ls_out。发布了错误的代码或什么...?请详细说明“不工作” 我在上面的问题中添加了那个单一剧本的输出。 Ansible 版本是 1.9.0.1 ... 1.8.4 的输出相同 【参考方案1】:

这个话题很复杂,看你想要什么,有两种不同的答案。

访问在同一主机的另一个角色中定义的变量

例子:

---
- hosts: host1
- roles:
    - role1
    - role2

目标:您想从角色 1 访问角色 2 中的某个变量。

使用set_fact 模块。

角色1:

name: save precious value
set_fact: 
  pantsu: shiroi

角色2:

name: Nozoki...
debug: msg="Color is  pantsu "

访问一个主机(或组)的静态变量到另一个

例子:

[group_foo]
host1
host2
[group_bar]
host3
host4

group_vars/group_foo

important_value=bla-bla-ba

目标:您想在 group2 的剧本中使用它。

这要复杂得多。

在 group_vars/group_bar 内

other_var: 'hostvars[groups["group_foo"][0]].important_value'

你可以使用除'0'之外的其他索引。

【讨论】:

第一个例子是值分配的基本变量,这里不适用,因为我使用了两个不同的主机。在第一台主机上,这些值是动态的,就像一个始终更新的配置文件。在我将值存储在具有第一个角色的第一个主机上的变量中之后,我移动到第二个主机,运行第二个角色并使用主机存储的变量中的这些值进行处理。我在一个主机上设置了一个变量,我希望该变量对另一台主机可用。所有这些都在一个剧本中。基本上两个主机都在同一个组中。 你可以在一本剧本中设置事实,而不是通过hostvars: hostvars['serv1']['saved_fact'] 访问它 --- - name: test hostvars host1 hosts: LBL tasks: - shell: echo hostname register: ls_out - set_fact: ls_down=' ls_out.stdout ' - name: test hostvars host2 hosts: LM tasks: - shell: echo hostvars['LBL']['ls_down'] 错误:fatal: [10.104.148.138] => host not found: LBL @ady8531,您使用 set_fact 错误。它不需要 foo=bar 符号,它需要一个字典列表。请参阅此处的示例:docs.ansible.com/set_fact_module.html。这里是上面代码的工作示例:gist.github.com/amarao/5ebe4d9c2fd04dfaf287 感谢您就如何窥视 shiroipantu 提出明确的建议。 :P【参考方案2】:

问题出在您的库存中。

这条消息:

fatal: [10.104.148.138] => host not found: LBL

是因为LBL 是一个组而不是主机。群组LBL 有一个主机:10.104.148.136

执行以下操作之一:

1。将您的库存 (/etc/ansible/hosts) 更改为:

LBL ansible_ssh_host=10.104.148.136
LM ansible_ssh_host=10.104.148.138

2。或者,如果您真的知道自己在做什么并且 LBL 是一个组,并且您想保持这种状态,那么使用以下命令访问变量:

 hostvars['10.104.148.136']['db']['stdout'] 

LBL 是一个组而不是一个主机。 More info.

【讨论】:

实际上我修改了 hosts 文件并添加了:[ow.hosts] LBL LM 并且它正在工作。非常感谢! 是否可以使用类似的东西:with_items: " hostvars['LBL']['db_*'] " 循环?示例:在 LBL 主机上,我存储了一些名为:db_ace、db_abc 等的变量。如何在另一个主机的循环中调用它们? hostvars.LBL.db_* ? @ady8531 什么都没有。检查是否有一些 Jinja2 过滤器来过滤 a 列表。如果有,那么您可以过滤 hostvars['LBL'].keys(),将其分配给其他变量使用set_factvars: 部分..【参考方案3】:

这是我的解决方案。我的任务是在两台服务器之间同步数据,我想像这样传递服务器名称:ansible-playbook sync.yaml -e "source=host1 destination=host2"

这是主要的剧本:

---

- name: get_sync_facts
  hosts: " source "
  roles:
    - set_sync_facts

- name: sync
  hosts: " destination " 
  roles:
    - get_sync_facts
    - sync

这里是 set_sync_facts 角色:

---

- set_fact: src_media_dir='/some/dir/'
- set_fact: src_user='myuser'
- set_fact: src_host='1.1.1.1'
- set_fact: src_port=12345
- set_fact: src_db_user='dbuser'
- set_fact: src_db_password='something'
- set_fact: src_db_name='some_db'

(我实际上是从任务中获得了其中一些,而从主机变量中获得了其他一些,但你明白了)

这是 get_sync_facts 角色:

---

- set_fact: src_media_dir= hostvars[source]['src_media_dir'] 
- set_fact: src_user= hostvars[source]['src_user'] 
- set_fact: src_host= hostvars[source]['src_host'] 
- set_fact: src_port= hostvars[source]['src_port'] 
- set_fact: src_db_user= hostvars[source]['src_db_user'] 
- set_fact: src_db_password= hostvars[source]['src_db_password'] 
- set_fact: src_db_name= hostvars[source]['src_db_name'] 

你可以不这样做,直接在你的剧本中引用主机变量,但这似乎更容易维护,因为它直接对应于 set_sync_facts 角色。

【讨论】:

我基本上有完全相同的问题要解决,但是,您的解决方案似乎对我不起作用。我得到:fatal: [1.2.3.4]: FAILED! => "failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute u'source'" 其中 source 是我的 var(例如分期)。在我的第一场比赛中,我以set_fact: blah=somvar 结束,在第二场比赛中,我从set_fact: blah=hostvars[source][blah] 开始。我做错了什么? 啊,我犯了一个错误,将staging 直接传递给hostvars。转储了整个hostvars 结构并看到它(显然)是由实际主机名组织的。给了我一些可以使用的东西,但是,我可能不得不更改我的库存分组,因为我不能保证 staging[0] 例如将始终是相关的 Web 主机(可能是 db 节点)。如果您不介意我问,您的库存是如何设置的? @seeafish 我的库存完全是动态的。我调用一个返回所有主机的 python 脚本,然后执行以下操作: ansible-playbook -i dynamic_inventory/get_hosts playbook.yaml -e "group=$DESTINATION $SOURCE" 谢谢。我最终将我的静态清单重组为单独的环境和角色,并在需要时使用 :children 进行选择。然后我做两个额外的变量-e source=[source_group]-e target=[target_group] 然后将剧本中的主机设置为source-web 例如。【参考方案4】:

这是一个相当古老的话题,但也许它会对某人有所帮助。我使用 sed 根据额外参数中指定的“目标”获取主机名。

要使用这个“目标”组,必须只包含 1 个主机名。

我的库存主机:

[ansible_local]
localhost

[machine1]
machine1.domain.tld

upgrade_packages.yml

---
- hosts: ' target '
  sudo: yes
  tasks:
  - name: check for Debian system
    shell: /bin/false
    when: ansible_pkg_mgr != "apt"

  - name: full-upgrade all packages
    apt: update_cache=yes upgrade=full
    register: upgrade_result

- hosts: ansible_local
  tasks:
  - name: find out host from target
    shell: /bin/sed -n -e '/^\[ target \]$/,/^\[.*\]$/  /^\[/d; /^$/d; p; '  inventory_file 
    register: target_inventory

  - name: Display all facts from target machine (change when to true if needed)
    debug: var=hostvars[target_inventory.stdout]
    when: false

  - name: Display upgrade result on ansible_local node
    debug: var=hostvars[target_inventory.stdout].upgrade_result.msg

调用:ansible-playbook ./upgrade-packages.yml -e "target=machine1" -v

【讨论】:

使用fail: 模块而不是/bin/false。【参考方案5】:

我有一个类似的设置,其中有三个主机 h1、h2、h3,我想在每个主机上定义一个事实 "important_fact": "foo" (分别是 bar 和 baz)

要获取包含所有主机上所有不同重要事实的列表,您可以这样做:

- set_fact:
    important_facts_list: " hostvars | json('*.important_fact') "
  run_once: yes
  delegate_to: h1

important_facts_list 将包含[ 'foo', 'bar', 'baz' ],您现在可以使用with_items 对其进行迭代。

【讨论】:

以上是关于将 Ansible 变量从一个角色(在一个主机上运行)传递给在同一剧本中的另一台主机上运行的另一个角色的主要内容,如果未能解决你的问题,请参考以下文章

Ansible 角色中的默认值和变量有什​​么区别?

在ansible中如何从另一个变量初始化变量?

在模板中使用 Ansible 库存组主机名

是否可以使用Ansible创建一个在运行每个角色之前提示的剧本?

将剧本运行中的变量保存到 ansible 主机本地文件

掌握Ansible角色(Roles)自动化部署配置LAMP架构