I made an Ansible play book that loops through a .yaml file and adds descriptions to interfaces. The problem is after every set command Ansible commits, waits for the commit then configures the next interface. If I run this on a VC with 10 members it will take for ever. Any one know how to make the playbook commit only after all changes have been made?
---
- name: Change configuration using junos_config module
hosts: test
connection: local
gather_facts: no
tasks:
- include_vars: /home/ansible/interfaces.yaml
- name: Change description on interfaces based on a list of variable
junos_config:
lines:
- "set interfaces {{ item.name }} description \"{{ item.description }}\""
comment: "Update description of interface {{ item.name }}"
with_items: "{{ interfaces }}"
register: result
- debug: var=result
I changed the play book which now looks like this:
---
- name: Change configuration using junos_config module
hosts: test
connection: local
gather_facts: no
tasks:
- include_vars: /home/ansible/playbook-core/interfaces.yaml
- name: Change description on interfaces based on a list of variable
junos_config:
lines:
- "{{ lines_template }}"
- "set interfaces {{ item.name }} description \"{{ item.description }}\""
comment: "Update description of port"
vars:
lines_template: "[ {% for interface in interfaces %}'set interfaces {{ item.name }} description \"{{ item.description }}\"',{% endfor %} ]"
with_items: "{{ interfaces }}"
register: result
- debug: var=result
And when I run it I get an error:
An exception occurred during task execution. To see the full traceback, use -
vvv. The error was: TypeError: sequence item 0: expected string, list found
failed: [10.63.255.71] (item={u'name': u'ge-0/0/1', u'description': u'Set by
ansible'}) => {"changed": false, "item": {"description": "Set by ansible",
"name": "ge-0/0/1"}, "module_stderr": "Traceback (most recent ca
ll last):\n File \"/tmp/ansible_wVNymo/ansible_module_junos_config.py\",
line 402, in <module>\n main()\n File
\"/tmp/ansible_wVNymo/ansible_module_junos_config.py\", line 371, in main\n
diff = configure_device(module, warnings, candidate)\n File
\"/tmp/ansible_wVNymo/ansible_module_junos_config.py\", line 293, in
configure_device\nreturn load_config(module, candidate, warnings, **kwargs)\n
File \"/tmp/ansible_wVNy
mo/ansible_modlib.zip/ansible/module_utils/junos.py\", line 199, in
load_config\nTypeError: sequence item 0: expected string, list found\n",
"module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}
Move the loop logic to Jinja2 template:
- name: Change description on interfaces based on a list of variable
junos_config:
lines: "{{ lines_template }}"
- "set interfaces {{ item.name }} description \"{{ item.description }}\""
comment: "Update description of interfaces"
vars:
lines_template: "[ {% for interface in interfaces %}'set interfaces {{ interface.name }} description \"{{ interface.description }}\"',{% endfor %} ]"
Last , could be skipped, but it seems Ansible makes up for that.
The above code is not tested with Juniper, but it should produce what you wanted.
Related
Is there is the way to pass variable with "map type" ?
For example is it possible to do something like below:
jobs:
docker_build_and_push:
uses: test/test/.github/workflows/matrixDocker.yml#main
with:
# Docker image name
service_name: nesso
tags: {"nginx":"new", "api":"old"} <<<<<< ????
Unfortunately, this is not possible yet. As I wrote in a previous answer, the only supported input types for actions are string | number | boolean (schema: with ref: definitions/env).
So the workaround would be either you pass multiple arguments to your action, or you pass them as a JSON string then you parse it with jq (if your action uses shell/bash).
And here are these example workarounds:
Specify more options as inputs to your action
Simply add more inputs to your action instead of one input of type map:
name: 'Hello World'
description: 'Greet someone'
inputs:
greeting-word:
description: What to say
required: false
default: Hello
who-to-greet:
description: 'Who to greet'
required: true
something-else:
description: Something else to say
required: false
default: ""
runs:
using: "composite"
steps:
- name: Greet!
shell: bash
run: echo "${{ inputs.greeting-word }} ${{ inputs.who-to-greet }} ${{ inputs.something-else }}"
Then just pass them form your workflow file
- name: Greet
uses: ./.github/actions/my-action
with:
greeting-word: Hallo
who-to-greet: Bob
Pass arguments as a JSON string
Workflow file:
- name: Greet
uses: ./.github/actions/my-action
with:
greeting-args: '{"greeting-word": "Hello", "who-to-greet": "Bob"}'
The action
name: 'Hello World'
description: 'Greet someone'
inputs:
greeting-args:
required: true
description: Greeting arguments
runs:
using: "composite"
steps:
- name: Greet!
shell: bash
run: |
MY_INPUT='${{ inputs.greeting-args }}'
GREETING_WORD=$(echo $MY_INPUT | jq -r '."greeting-word"')
WHO_TO_GREET=$(echo $MY_INPUT | jq -r '."who-to-greet"')
echo "${GREETING_WORD} ${WHO_TO_GREET}"
Or you can pass it as a multi-line string
This approach is used by actions like actions/upload-artifact
- uses: actions/upload-artifact#v3
with:
name: my-artifact
path: |
path/output/bin/
path/output/test-results
!path/**/*.tmp
And google-github-actions/get-secretmanager-secrets
- id: 'secrets'
uses: 'google-github-actions/get-secretmanager-secrets#v1'
with:
secrets: |-
token:my-project/docker-registry-token
anotherOne:my-project/a-secret
anotherOneToo:my-project/another-secret
i.e you just need to read these lines and split your map's key/values
Hi everyone and thanks in advance for your support, I wanna create 1 API call to create 2 repos, 1 assigned to a Superadmin gitea account and the other for standard gitea user named gitea, I assume this would need to be with a list of users in variables and a list of repos in variables also using with_items but I don't seem to get it, down I'm attaching my API call format in Ansible to achieve 1 repo/1user, please someone help:
- name: "gitea - Create repo"
uri:
url: '{{ gitea_api_url }}/user/repos'
method: POST
body_format: json
status_code: 201
headers:
Authorization: token {{ saved_tokens if token_ is undefined else token_ }}
body:
auto_init: true
default_branch: "main"
description: "hi"
gitignores: ""
issue_labels: ""
license: ""
name: "{{ item }}"
private: true
readme: ""
template: true
trust_model: "default"
owner: '{{ gitea.users }}'
with_items: "{{ repo_names }}"
register: _repostatus
I only assume it could work with a list of 2 users and a list of 2 repo names using somehow with_items and conditionals together, in this case I have the following:
gitea:
users:
- name: '{{ gitea_admin_account }}'
admin: true
- name: '{{ gitea_standard_account }}'
admin: false
repo_names:
- test2
- test3
How to use the 'admin' flag to create 'test2' to the admin and 'test3' to the standard user?
I cannot see an owner key in the Gitea API for [POST] /user/repos, only in the 201 response.
If you want to add other users besides the repository owner, you should probably use [PUT] /repos/{owner}/{repo}/collaborators/{collaborator} or similar after creating the repository.
For this request you can loop by using loop keyword:
- name: "gitea - Add collaborators to total repo_list"
uri:
url: '{{ gitea_api_url }}/repos/{{ repo_owner }}/{{ item.0 }}/collaborators/{{ item.1.name }}'
method: POST
body_format: json
status_code: 204
headers:
Authorization: token {{ saved_tokens if token_ is undefined else token_ }}
body:
permission: "{{ 'admin_permission_str' if item.1.admin else 'regular_permission_str' }}"
loop: "{{ repo_names | product(gitea.users) }}"
register: _repostatus
To iterate over two lists you have to create a cartesian product of them to bring both into one data structure. Then you can iterate over this one data structure. The { repo_names | product(gitea.users) }} results in the following data structure:
[
[
"test2",
{
"admin": true,
"name": "gitea_admin_account"
}
],
[
"test2",
{
"admin": false,
"name": "gitea_standard_account"
}
],
[
"test3",
{
"admin": true,
"name": "gitea_admin_account"
}
],
[
"test3",
{
"admin": false,
"name": "gitea_standard_account"
}
]
]
You can use .0 and .1 to address the repository name or the users dict, respectively.
Alternatively you can work with with_nested/with_cartesian instead of using loop directly, you can find more about this in the Ansible Docs. But this is not necessary if you use loop as described above.
In a YAML playbook, I need to run multiple commands through each element in an array. I am using with_items to iterate, but I am getting syntax errors when I try to blend debug into the tasks.
Everything works when I remove the debug module, but than I can't see whats happening. Example provided below.
Produces Syntax Error:
- name: Iterate through array and display results
shell: "run command 1 on {{ item }}"
register: command1
debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
shell: "run command 2 on {{ item }}"
register: command2
debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
Everything seems to be working fine when I remove the debugs, but I need to see what each command produces because it has important info.
Is there some way to print/debug the results while keeping it inside the with_items iterations?
Edit:
I also tried a few more things but they also gave me a syntax error
Also Tried 1:
- name: Iterate through array and display results
- shell: "run command 1 on {{ item }}"
register: command1
- debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
- shell: "run command 2 on {{ item }}"
register: command2
- debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
Also Tried 2:
- name: Iterate through array and display results
- shell: "run command 1 on {{ item }}"
register: command1
- debug:
msg: "Command 1 || {{ command1.stdout_lines}}"
- shell: "run command 2 on {{ item }}"
register: command2
- debug:
msg: "Command 2 || {{ command2.stdout_lines}}"
with_items: "{{ array_name }}"
As the comments of β.εηοιτ.βε already stated
In Ansible, you can only have one module per item in the list (tasks is a list of tasks, each being an Ansible module) ... you have to have a new item in the list (a dash -), in front of all the debug and shell tasks.
This means,
integrate debug into iterations
I try to blend debug into the tasks
you can't do debugging in that way since the debug_module is for
Print statements during execution
only. However, according your question
Is there some way to print/debug the results while keeping it inside the with_items iterations?
it seems for me that you are more interested in Debugging tasks, play, role or block itself. So for this
Ansible offers a task debugger so you can fix errors during execution instead of editing your playbook and running it again to see if your change worked.
For usage you have to Enabling the debugger with the debugger keyword and may have a look into the Examples, Resolving errors in the debugger and Available debug commands.
Further questions here on SO are
How to debug Ansible issues?
Does Ansible have any feature to allow for debugging/pausing?
I was able to solve the issue, Ansible does not allow iterations through multiple tasks. I had to put the tasks in a separate yml playbook and use include_tasks: to call the playbook.
I have a text parameter in Jenkins that looks like so:
text(
defaultValue: '''
- 'evn-111'
- 'evn-423'
- 'evn-414'
''',
description: 'Add list of devices to create lab',
name: 'DEVICES',
)
I want to take this list of devices and pass it as a string into an ansible playbook.
However, when I pass this string into the ansible playbook like so:
extra_args = "-e devices=${ params.DEVICES }"
sh "ansible-playbook $extra_args deploy_lab.yml"
My output in Jenkins is:
ansible-playbook -e devices=
Followed by recommendations on what arguments I need to pass. Can anyone provide suggestions on how I can accomplish passing a multiline parameter to Ansible? Once I get the variable in the Ansible file, I have a template that creates another yml file using the multi-line text string passed from Jenkins.
My Ansible file looks like this:
---
- hosts: all
gather_facts: true
vars:
devices: ""
tasks:
- name: 'Create device file'
template:
src: devices.j2
dest: "host_vars/devices.yml"
mode: '0600'
delegate_to: localhost
My Template file devices.j2 looks like this:
---
devices_from_jenkins_param:
"{{ devices }}"
In the end, I want there to be a file host_vars/devices.yml that looks like this:
---
devices_from_jenkins_param:
- 'evn-111'
- 'evn-423'
- 'evn-414'
I'm trying to send a json variable ( {{parameter_var}} ) to a Jenkins job from Ansible using a curl call. Here is the task I'm using:
- name: Call Jenkins Job
shell: curl -H "crumb:{{ response.json.crumb }}" '{{ jenkinshost }}/job/{{ jenkinsjob }}/buildWithParameters?token={{ jenkinstoken }}' --data-urlencode json='{"parameter": [{"name":"parameter", "value":\""{{ parameter_var }}"\"}]}'
The error I'm getting is:
Syntax Error while loading YAML.\n mapping values are not allowed in this context
Ansible says the issue is:
YAML thought it was looking for the start of a\nhash/dictionary and was confused to see a second "{".
It seems that instead of accepting {{ parameter_var }} as a variable, it's trying to read it as just a value of "{{ parameter_var }}". I tried a few ways of adding or escaping quotes, but can't seem to figure out what Ansible/YAML is looking for. Am I just adding my quotes wrong, or can I just not send a variable using this method?
I printed out my {{ parameter_var }}. There's nothing unusual about it, so I don't think that's the issue:
{
"msg": {
"msg": "All items completed",
"changed": false,
"results": [
{
"content_length": "142",
...
} ...
}
The goal I'm trying to accomplish is to set parameter "parameter" in Jenkins to the value of {{ parameter_var }}
Here's where I found the syntax for sending the json to Jenkins: https://wiki.jenkins.io/display/JENKINS/Remote+access+API
There are a lot of things that need addressing in your question:
you should not be shell-ing out to curl when there is a uri: module in ansible designed for that purpose
related to that, you left off --fail from your curl, so a non-200 response will cause the shell: to exit 0 which is almost certainly not what you intended
you cannot have a : character followed by whitespace in a yaml document because that's how yaml defines keys in a dict
you should not try and build up JSON using jinja2 templates, rather build up the structure in a dict or list and feed it through | to_json to ensure correct quoting
However, if you insist on that syntax specifically, you can fix it with some light editing:
- name: Call Jenkins Job
shell: >-
curl --fail -H "crumb:{{ response.json.crumb }}"
'{{ jenkinshost }}/job/{{ jenkinsjob }}/buildWithParameters?token={{ jenkinstoken }}'
--data-urlencode json={{ json_data | to_json | quote }}
vars:
json_data:
parameter:
- name: "parameter"
value: '{{ parameter_var }}'