简体   繁体   English

Ansible - 如何在剧本中有条件地反转变量

[英]Ansible - how to conditionally invert variables in a playbook

I needed to be able to invert variables stored in a JSON file that is passed to the playbook from the command line. 我需要能够反转存储在从命令行传递给playbook的JSON文件中的变量。

These are the tasks that I set up (they are identical except for vars), this is a fragment of a playbook: 这些是我设置的任务(它们除了变量之外都是相同的 ),这是一个剧本的片段:

- name: Prepare a .sql file
  delegate_to: 127.0.0.1
  mysql_db:
    name: "{{ source['database']['db_name'] }}"
    state: dump
    login_host: "{{ source['database']['host'] }}"
    login_user: "{{ source['database']['user'] }}"
    login_password: "{{ source['database']['password'] }}"
    target: test_db.sql
  when: invert is not defined

- name: Prepare a .sql file (inverted)
  delegate_to: 127.0.0.1
  mysql_db:
    name: "{{ target['database']['db_name'] }}"
    state: dump
    login_host: "{{ target['database']['host'] }}"
    login_user: "{{ target['database']['user'] }}"
    login_password: "{{ target['database']['password'] }}"
    target: test_db.sql
  when: invert is defined

So consequently when I execute 所以当我执行时

ansible-playbook -i hosts playbook.yml --extra-vars "@dynamic_vars.json"

the first task is executed. 第一个任务被执行。 If I execute 如果我执行

ansible-playbook -i hosts playbook.yml --extra-vars "@dynamic_vars.json" --extra-vars "invert-yes"

the second task is executed that takes the same hash as parameters, but only swaps source for target (which essentially becomes a source in my playbook). 执行第二个任务,它接受与参数相同的哈希,但只交换目标的源(它基本上成为我的剧本中的源)。

As you can see, this is a very simplistic approach, there is a lot of unnecessary duplication, I just do not like it. 正如你所看到的,这是一种非常简单的方法,有很多不必要的重复,我只是不喜欢它。 However, I cannot think of a better way to be able to revert variables at the command line without building some more complex include logic. 但是,我想不出能够在命令行恢复变量而不构建更复杂的包含逻辑的更好方法。

Perhaps you can advice me on how I can do it better? 也许你可以建议我如何做得更好? Thanks! 谢谢!

I'm a big fan of YAMLs anchors and references when it comes to the topic of avoiding repetition. 在涉及避免重复的主题时,我是YAML锚点和参考的忠实粉丝。 Since the content is dynamic, you could take advantage of with_items , which can be used to pass a parameter like so: 由于内容是动态的,你可以利用with_items ,它可以用来传递一个参数,如下所示:

- &sqldump
  name: Prepare a .sql file
  delegate_to: 127.0.0.1
  mysql_db:
    name: "{{ item['database']['db_name'] }}"
    state: dump
    login_host: "{{ item['database']['host'] }}"
    login_user: "{{ item['database']['user'] }}"
    login_password: "{{ item['database']['password'] }}"
    target: test_db.sql
  when: invert is not defined
  with_items:
    - source


- <<: *sqldump
  name: Prepare a .sql file (inverted)
  when: invert is defined
  with_items:
    - target

The 2nd task is a perfect clone of the first one, you then override the name , condition and the loop with_items to pass the target instead of the source . 第二个任务是第一个任务的完美克隆,然后覆盖namecondition和循环with_items以传递target而不是source


After reading your answer to @ydaetskcoR it sounds like you have quite some cases where you need to use the data from one or the other dict. 在阅读了@ydaetskcoR的答案之后,听起来您在某些情况下需要使用来自其中一个或另一个字典的数据。 Maybe in that case it then would make sense to just define the var globally depending on the invert parameter. 也许在那种情况下,根据invert参数来定义var全局是有意义的。 Your vars file could look like that: 您的vars文件可能如下所示:

---

source:
  database: ...
  db_name: ...

target:
  database: ...
  db_name: ...

data: "{{ target if invert is defined else source }}"

You then simply can use data in all your tasks without dealing with conditions any further. 然后,您可以在所有任务中使用data ,而无需进一步处理条件。

- name: Prepare a .sql file
  delegate_to: 127.0.0.1
  mysql_db:
    name: "{{ data['database']['db_name'] }}"
    state: dump
    login_host: "{{ data['database']['host'] }}"
    login_user: "{{ data['database']['user'] }}"
    login_password: "{{ data['database']['password'] }}"
    target: test_db.sql

Of course, this way you have a fixed task name which does not change with the param you pass. 当然,这样你就有了一个固定的任务名称,它不随你传递的参数而改变。

If you are attempting to do the same thing but just want to specify different variables depending on the host/group then a better approach may be to simply set these as host/group vars and run it as a single task. 如果您尝试执行相同的操作但只想根据主机/组指定不同的变量,那么更好的方法可能是将这些变量设置为主机/组变量并将其作为单个任务运行。

If we set up our inventory file a bit like this: 如果我们设置我们的库存文件有点像这样:

[source_and_target-nodes:children]
source-nodes
target-nodes

[source-nodes]
source database_name='source_db' database_login_user='source_user' database_login_pass='source_pass'

[target-nodes]
target database_name='target_db' database_login_user='target_user' database_login_pass='target_pass'

Then we can target the task at the source_and_target-nodes like so: 然后我们可以在source_and_target-nodes处定位任务,如下所示:

- name: Prepare a .sql file
  hosts: source_and_target-nodes
  mysql_db:
    name: "{{ database_name }}"
    state: dump
    login_host: "{{ inventory_hostname }}"
    login_user: "{{ database_login_user }}"
    login_password: "{{ database_login_pass }}"
    target: test_db.sql

You won't be able to access the host vars of a different host this easily if you need to use delegate_to as you are in your question but if you are simply needing to run the play locally you can instead set ansible_connection to local in your host/group vars or setting connection: local in the play. 如果您需要在问题中使用delegate_to ,那么您将无法轻松访问其他主机的主机变量,但如果您只是需要在本地运行该游戏,则可以在主机中将ansible_connection设置为local / group vars或设置connection: local播放中的connection: local

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM