0541
This commit is contained in:
278
files/ansible-playbooks/sot-updater-iponly.yml
Normal file
278
files/ansible-playbooks/sot-updater-iponly.yml
Normal file
@@ -0,0 +1,278 @@
|
||||
# sot-updater-iponly.yml — NetBox IP sync ONLY via nb_onedevice_update.py (no device scan)
|
||||
|
||||
- name: NetBox IP sync only (Cloud → NetBox via nb_onedevice_update.py); do not scan devices
|
||||
hosts: all
|
||||
gather_facts: no
|
||||
|
||||
vars:
|
||||
pathprefix: "PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH; "
|
||||
dev1_user: "root"
|
||||
dev1_pass: "wavewave"
|
||||
ssh_timeout: 30
|
||||
|
||||
dev2_host: "192.168.1.1"
|
||||
dev2_port: 22
|
||||
dev2_ssh_user: "root"
|
||||
dev2_passfiles: [ "basicpass", "basicpass2" ]
|
||||
|
||||
dev2_side_ip: "192.168.1.11/24"
|
||||
dev1_iface: "br-wan"
|
||||
arping_iface: "eth0"
|
||||
dev2_side_ip_addr: "{{ dev2_side_ip.split('/')[0] }}"
|
||||
|
||||
ssh_opts_common: >-
|
||||
-o PreferredAuthentications=password
|
||||
-o PubkeyAuthentication=no
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o NumberOfPasswordPrompts=1
|
||||
-o ConnectTimeout=15
|
||||
|
||||
rmq_host: "{{ lookup('env','RMQ_HOST') | default('10.210.12.2', true) }}"
|
||||
rmq_port: "{{ lookup('env','RMQ_PORT') | default('15672', true) }}"
|
||||
rmq_user: "{{ lookup('env','RMQ_USER') | default('admin', true) }}"
|
||||
rmq_pass: "{{ lookup('env','RMQ_PASS') | default('change_me', true) }}"
|
||||
rmq_vhost: "{{ lookup('env','RMQ_VHOST') | default('app', true) }}"
|
||||
rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}"
|
||||
control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}"
|
||||
|
||||
# --- Retry attempt tracking (default 0 if missing) ---
|
||||
requeue_attempt: "{{ (lookup('env','REQUEUE_ATTEMPT') | default('0', true)) | int }}"
|
||||
|
||||
pre_tasks:
|
||||
- name: "NB preflight | Verify script exists"
|
||||
delegate_to: localhost
|
||||
ansible.builtin.stat:
|
||||
path: "/opt/containers/ansible-worker/app/nb_onedevice_update.py"
|
||||
register: nb_script
|
||||
|
||||
- name: "NB preflight | Abort softly if script missing"
|
||||
when: not nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "NB preflight skipped: /opt/containers/ansible-worker/app/nb_onedevice_update.py not found."
|
||||
- "Tip: adjust chdir/path or script name."
|
||||
|
||||
- name: "NB preflight | Run nb_onedevice_update.py for {{ inventory_hostname }}"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
environment:
|
||||
PYTHONUNBUFFERED: "1"
|
||||
args:
|
||||
chdir: "/opt/containers/ansible-worker/app"
|
||||
executable: /bin/bash
|
||||
shell: |
|
||||
set -o pipefail
|
||||
python3 -u nb_onedevice_update.py "{{ inventory_hostname }}" --chatty 2>&1
|
||||
register: nb_preflight
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: "NB preflight | Show results"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
debug:
|
||||
msg:
|
||||
- "rc={{ nb_preflight.rc }}"
|
||||
- "stdout_lines:"
|
||||
- "{{ (nb_preflight.stdout_lines | default(['<no stdout>'])) }}"
|
||||
- "stderr_lines:"
|
||||
- "{{ (nb_preflight.stderr_lines | default(['<no stderr>'])) }}"
|
||||
- "raw stdout (joined): {{ nb_preflight.stdout | default('') | trim }}"
|
||||
|
||||
- name: "NB preflight | Detect cloud failure"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
vars:
|
||||
_out: "{{ (nb_preflight.stdout | default('') | regex_replace('\r','')) }}"
|
||||
_lines: >-
|
||||
{{ _out.split('\n') | map('trim') | list }}
|
||||
_cloud_fail_line: >-
|
||||
{{ (_lines | select('match', '^FAIL\\s+Cloud\\b') | list | last | default('')) }}
|
||||
_error_line: >-
|
||||
{{ (_lines | select('match', '^FAIL\\s+') | list | last | default('')) }}
|
||||
_rc_is_3: "{{ (nb_preflight.rc | default(1)) | int == 3 }}"
|
||||
_cloud_error_type: >-
|
||||
{%- if _error_line is search('network error')
|
||||
and _error_line is search('500 error responses') -%}
|
||||
cloud_http_500
|
||||
{%- elif (_cloud_fail_line | length) > 0 -%}
|
||||
cloud_logic
|
||||
{%- elif _rc_is_3 -%}
|
||||
cloud_other
|
||||
{%- else -%}
|
||||
none
|
||||
{%- endif -%}
|
||||
_cloud_retryable: >-
|
||||
{{ _cloud_error_type not in ['cloud_http_500'] }}
|
||||
set_fact:
|
||||
cloud_fail_line: "{{ _cloud_fail_line }}"
|
||||
cloud_bad: "{{ _rc_is_3 or (( _cloud_fail_line | length ) > 0) }}"
|
||||
cloud_error_type: "{{ _cloud_error_type }}"
|
||||
cloud_retryable: "{{ _cloud_retryable }}"
|
||||
|
||||
# --- Extract upgrade_cmd line if any ---
|
||||
- name: "NB preflight | Extract upgrade_cmd line"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
nb_upgrade_line: >-
|
||||
{{
|
||||
(
|
||||
nb_preflight.stdout_lines | default([]) | map('regex_replace','\r','') | map('trim')
|
||||
| select('match', '^NB:\\s*upgrade_cmd\\s*=')
|
||||
| list | first
|
||||
) | default('')
|
||||
}}
|
||||
|
||||
- name: "NB preflight | Parse upgrade_cmd value"
|
||||
when: nb_upgrade_line | length > 0
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
printf '%s\n' "{{ nb_upgrade_line }}" | awk -F'=' '{print $2}' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
|
||||
args: { executable: /bin/bash }
|
||||
register: up_cmd_sh
|
||||
changed_when: false
|
||||
|
||||
- name: "NB preflight | Set parsed upgrade_cmd"
|
||||
when: nb_upgrade_line | length > 0
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
nb_upgrade_cmd: "{{ up_cmd_sh.stdout | default('') | trim }}"
|
||||
|
||||
- name: "NB preflight | Debug parsed upgrade_cmd"
|
||||
when: nb_upgrade_line | length > 0
|
||||
delegate_to: localhost
|
||||
debug:
|
||||
msg:
|
||||
- "upgrade_cmd='{{ nb_upgrade_cmd }}' (len={{ nb_upgrade_cmd|length }})"
|
||||
|
||||
- name: "NB preflight | Parse OK line"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
vars:
|
||||
nb_lines: >-
|
||||
{{ (nb_preflight.stdout | default('') | regex_replace('\r','')) | split('\n') | map('trim') | list }}
|
||||
nb_ok_line: >-
|
||||
{{ (nb_lines | select('match', '^OK\\s+') | list | last | default('')) }}
|
||||
nb_tokens: >-
|
||||
{{ (nb_ok_line | regex_replace('^OK\\s+', '')).split() }}
|
||||
nb_kv: >-
|
||||
{{
|
||||
dict(
|
||||
nb_tokens
|
||||
| select('match', '^[a-zA-Z_]+=')
|
||||
| map('split', '=', 1)
|
||||
| map('list')
|
||||
)
|
||||
}}
|
||||
set_fact:
|
||||
nb_ok: "{{ (nb_preflight.rc|default(1)) == 0 and (nb_ok_line|length)>0 }}"
|
||||
nb_dev: "{{ (nb_tokens|first|default('')) if (nb_tokens|length>0) else '' }}"
|
||||
nb_ip: "{{ nb_kv.get('ip','') }}"
|
||||
nb_fw: "{{ nb_kv.get('fw','') }}"
|
||||
nb_node: "{{ nb_kv.get('node','') }}"
|
||||
nb_sector: "{{ nb_kv.get('sector','') }}"
|
||||
nb_small: "{{ nb_kv.get('small','') }}"
|
||||
nb_ok_line: "{{ nb_ok_line }}"
|
||||
|
||||
- name: "NB preflight | Detect IP change"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
vars:
|
||||
out: "{{ (nb_preflight.stdout | default('') | regex_replace('\r','')) }}"
|
||||
reason: >-
|
||||
{%- if 'IP: moving ' in out -%}moving
|
||||
{%- elif 'IP: create new ' in out -%}create new
|
||||
{%- elif 'IP: pruning stale ' in out -%}pruning stale
|
||||
{%- else -%}none{%- endif -%}
|
||||
set_fact:
|
||||
nb_ip_changed: "{{ reason != 'none' }}"
|
||||
nb_change_reason: "{{ reason }}"
|
||||
|
||||
- name: "NB preflight | Verdict"
|
||||
when: nb_script.stat.exists
|
||||
delegate_to: localhost
|
||||
debug:
|
||||
msg:
|
||||
- "Preflight verdict:"
|
||||
- "Cloud fail: {{ cloud_bad|default(false) }}"
|
||||
- "IP changed: {{ nb_ip_changed|default(false) }}"
|
||||
- "Reason: {{ nb_change_reason|default('none') }}"
|
||||
|
||||
- name: "NB preflight | Compute next_attempt"
|
||||
delegate_to: localhost
|
||||
set_fact:
|
||||
next_attempt: "{{ (requeue_attempt | int) + 1 }}"
|
||||
|
||||
# --- Requeue control: only 3 attempts, 300s apart ---
|
||||
- name: "NB preflight | Publish delayed requeue (300s (10s)) if needed"
|
||||
when:
|
||||
- nb_script.stat.exists
|
||||
- (nb_ip_changed | default(false)) or (
|
||||
(cloud_bad | default(false)) and
|
||||
(cloud_error_type | default('none')) != 'cloud_http_500'
|
||||
)
|
||||
- (requeue_attempt | int) < 3
|
||||
delegate_to: localhost
|
||||
uri:
|
||||
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/app/deviceconfig.delayed/publish"
|
||||
method: POST
|
||||
user: "admin"
|
||||
password: "change_me"
|
||||
force_basic_auth: true
|
||||
status_code: 200
|
||||
return_content: yes
|
||||
headers: { content-type: "application/json" }
|
||||
body_format: json
|
||||
body:
|
||||
properties:
|
||||
content_type: "application/json"
|
||||
headers: { x-delay: 900000 }
|
||||
routing_key: "deviceconfig"
|
||||
payload: "{{ {
|
||||
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||
'task_name': 'sot-updater',
|
||||
'attempt': next_attempt
|
||||
} | to_json }}"
|
||||
payload_encoding: "string"
|
||||
register: rmq_requeue
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: "NB preflight | Log requeue publish response"
|
||||
when:
|
||||
- nb_script.stat.exists
|
||||
- ((cloud_bad | default(false)) or (nb_ip_changed | default(false)))
|
||||
- (requeue_attempt | int) < 3
|
||||
delegate_to: localhost
|
||||
debug:
|
||||
msg:
|
||||
- "Requeue published (attempt {{ (requeue_attempt | int) + 1 }})"
|
||||
- "HTTP: {{ rmq_requeue.status|default('n/a') }}"
|
||||
- "JSON: {{ rmq_requeue.json|default('<none>') }}"
|
||||
|
||||
- name: "NB preflight | Stop host after requeue"
|
||||
when:
|
||||
- nb_script.stat.exists
|
||||
- (
|
||||
(nb_ip_changed | default(false)) or
|
||||
(
|
||||
(cloud_bad | default(false)) and
|
||||
(cloud_error_type | default('none')) != 'cloud_http_500'
|
||||
)
|
||||
)
|
||||
meta: end_host
|
||||
|
||||
- name: "NB preflight | Pause 1s if OK"
|
||||
when:
|
||||
- nb_script.stat.exists
|
||||
- not ((cloud_bad | default(false)) or (nb_ip_changed | default(false)))
|
||||
delegate_to: localhost
|
||||
pause:
|
||||
seconds: 1
|
||||
|
||||
tasks:
|
||||
- name: "IP-only | Stop after NetBox sync (no device scan)"
|
||||
meta: end_host
|
||||
Reference in New Issue
Block a user