Parallelize WEBRUNNER node provisioning

All Linode creates fire simultaneously via async/poll:0 loop instead of
serial include_tasks. One shared root pass + SSH key generated before
the loop eliminates per-node credential races. async_status collects
all create confirmations before add_host. Play 2 runs with strategy:free
and -f 50 so all nodes configure, scan, and collect results concurrently.

Per-node serial provision file retired — logic is now inline.
This commit is contained in:
n0mad1k
2026-04-30 23:21:23 -04:00
parent e96bbab2b8
commit 4c60ba114d
3 changed files with 197 additions and 5 deletions
@@ -0,0 +1,90 @@
---
# Linode provision tasks for one WEBRUNNER node
# Called in a loop — loop_var: node_chunk
# Requires: linode_token, ssh_key_name, webrunner_name, deployment_id,
# linode_instance_type, linode_region, scanner_ip_log, results_dir
- name: Generate root password for {{ node_chunk.node_name }}
set_fact:
wr_root_pass: "{{ lookup('password', '/dev/null chars=ascii_letters,digits length=32') }}"
- name: Read public key
slurp:
src: "~/.ssh/{{ ssh_key_name }}.pub"
register: wr_pubkey
- name: Register SSH key in Linode ({{ node_chunk.node_name }})
uri:
url: "https://api.linode.com/v4/profile/sshkeys"
method: POST
headers:
Authorization: "Bearer {{ linode_token }}"
Content-Type: "application/json"
body_format: json
body:
label: "{{ webrunner_name }}"
ssh_key: "{{ wr_pubkey.content | b64decode | trim }}"
status_code: [200, 201]
ignore_errors: true
- name: Create Linode instance {{ node_chunk.node_name }}
uri:
url: "https://api.linode.com/v4/linode/instances"
method: POST
headers:
Authorization: "Bearer {{ linode_token }}"
Content-Type: "application/json"
body_format: json
body:
label: "{{ node_chunk.node_name }}"
type: "{{ linode_instance_type | default('g6-standard-2') }}"
region: "{{ linode_region | default('us-east') }}"
image: "linode/debian12"
root_pass: "{{ wr_root_pass }}"
authorized_keys:
- "{{ wr_pubkey.content | b64decode | trim }}"
booted: true
backups_enabled: false
private_ip: false
tags:
- "webrunner"
- "{{ webrunner_name }}"
- "{{ deployment_id }}"
status_code: [200, 201]
register: wr_linode
- name: Extract node IP
set_fact:
wr_node_ip: "{{ wr_linode.json.ipv4[0] }}"
- name: Log scanner IP
lineinfile:
path: "{{ scanner_ip_log }}"
line: "{{ node_chunk.node_name }}: {{ wr_node_ip }}"
create: true
- name: Wait for SSH on {{ node_chunk.node_name }} ({{ wr_node_ip }})
wait_for:
host: "{{ wr_node_ip }}"
port: 22
delay: 30
timeout: 300
- name: Add {{ node_chunk.node_name }} to inventory
add_host:
name: "{{ wr_node_ip }}"
groups: webrunner_nodes
ansible_host: "{{ wr_node_ip }}"
ansible_user: root
ansible_ssh_private_key_file: "~/.ssh/{{ ssh_key_name }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
node_name: "{{ node_chunk.node_name }}"
node_cidrs: "{{ node_chunk.cidrs }}"
node_ip_count: "{{ node_chunk.ip_count }}"
node_idx: "{{ node_chunk.idx }}"
linode_instance_id: "{{ wr_linode.json.id }}"
provider: linode
- name: Show {{ node_chunk.node_name }} ready
debug:
msg: "Linode node ready: {{ node_chunk.node_name }} @ {{ wr_node_ip }} ({{ node_chunk.ip_count | int | string }} IPs)"