0901
This commit is contained in:
@@ -3,8 +3,7 @@
|
|||||||
# Phase 1: Cloud -> NetBox sync (via sot-updater-iponly.yml in wrapper mode)
|
# Phase 1: Cloud -> NetBox sync (via sot-updater-iponly.yml in wrapper mode)
|
||||||
# Phase 2: Refresh ansible_host from nb_ip, reset_connection
|
# Phase 2: Refresh ansible_host from nb_ip, reset_connection
|
||||||
# Phase 3: Run update-rebootin224.yml
|
# Phase 3: Run update-rebootin224.yml
|
||||||
# NOTE: update-rebootin224.yml is responsible for scheduling afterupgrade_check via RabbitMQ (delayed).
|
# Phase 4: Run afterupgrade_check.yml
|
||||||
# Do NOT import/run afterupgrade_check.yml here.
|
|
||||||
|
|
||||||
- hosts: all
|
- hosts: all
|
||||||
gather_facts: no
|
gather_facts: no
|
||||||
@@ -42,9 +41,7 @@
|
|||||||
_lines: "{{ (nb_preflight.stdout_lines | default([])) | map('regex_replace','\r','') | map('trim') | list }}"
|
_lines: "{{ (nb_preflight.stdout_lines | default([])) | map('regex_replace','\r','') | map('trim') | list }}"
|
||||||
_ok_line: "{{ (_lines | select('match','^OK\\s+') | list | last | default('')) }}"
|
_ok_line: "{{ (_lines | select('match','^OK\\s+') | list | last | default('')) }}"
|
||||||
_fail_line: "{{ (_lines | select('match','^FAIL\\s+') | list | last | default('')) }}"
|
_fail_line: "{{ (_lines | select('match','^FAIL\\s+') | list | last | default('')) }}"
|
||||||
# NOTE: regex_search with a replacement can yield a list on some Ansible/Jinja versions;
|
_ip_raw: "{{ _ok_line | regex_search('([0-9]{1,3}(?:\\.[0-9]{1,3}){3})','\\1') | default('') }}"
|
||||||
# keep it as a plain string match for ansible_host.
|
|
||||||
_ip_raw: "{{ _ok_line | regex_search('([0-9]{1,3}(?:\\.[0-9]{1,3}){3})') | default('') }}"
|
|
||||||
set_fact:
|
set_fact:
|
||||||
nb_ip: "{{ _ip_raw }}"
|
nb_ip: "{{ _ip_raw }}"
|
||||||
nb_ip_ok: "{{ (_ip_raw | length) > 0 }}"
|
nb_ip_ok: "{{ (_ip_raw | length) > 0 }}"
|
||||||
@@ -66,3 +63,5 @@
|
|||||||
|
|
||||||
|
|
||||||
- import_playbook: update-rebootin224.yml
|
- import_playbook: update-rebootin224.yml
|
||||||
|
vars:
|
||||||
|
is_run_by: "scheduler"
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}"
|
rmq_exchange: "{{ lookup('env','RMQ_EXCHANGE') | default('controls', true) }}"
|
||||||
control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}"
|
control_queue: "{{ lookup('env','CONTROLQUEUE') | default('queue_controls', true) }}"
|
||||||
|
|
||||||
|
# Invocation context (default: manual; scheduler wrapper sets is_run_by=scheduler)
|
||||||
|
is_run_by_effective: "{{ is_run_by | default('manual') }}"
|
||||||
|
|
||||||
# NEW: Post-upgrade check scheduling (via holding queue -> DLX)
|
# NEW: Post-upgrade check scheduling (via holding queue -> DLX)
|
||||||
# The holding queue is bound to exchange 'deviceconfig.holding' with routing key 'deviceconfig'.
|
# The holding queue is bound to exchange 'deviceconfig.holding' with routing key 'deviceconfig'.
|
||||||
# Messages published here carry a per-message TTL (AMQP 'expiration' property, in ms).
|
# Messages published here carry a per-message TTL (AMQP 'expiration' property, in ms).
|
||||||
@@ -139,9 +142,75 @@
|
|||||||
(not (rmq_tag_failed_pre_resp.json.routed | default(false) | bool))
|
(not (rmq_tag_failed_pre_resp.json.routed | default(false) | bool))
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Scheduler | Publish action_state failed (hostname preflight)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
|
||||||
|
method: POST
|
||||||
|
user: "{{ rmq_user }}"
|
||||||
|
password: "{{ rmq_pass }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
content-type: "application/json"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
properties:
|
||||||
|
content_type: "application/json"
|
||||||
|
routing_key: "{{ control_queue }}"
|
||||||
|
payload: "{{ {
|
||||||
|
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||||
|
'task_name': 'custom_field_set',
|
||||||
|
'task_add1': 'action_state',
|
||||||
|
'task_result': 'failed'
|
||||||
|
} | to_json }}"
|
||||||
|
payload_encoding: "string"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
|
- name: Scheduler | Debug action_state failed published (hostname preflight)
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "scheduler-run detected; published action_state=failed (hostname preflight)"
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
- name: Stop play after hostname preflight failure
|
- name: Stop play after hostname preflight failure
|
||||||
ansible.builtin.meta: end_play
|
ansible.builtin.meta: end_play
|
||||||
|
|
||||||
|
- name: Scheduler context | Debug is_run_by
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "is_run_by={{ is_run_by_effective }}"
|
||||||
|
|
||||||
|
- name: Scheduler | Publish action_state inprogress (scheduler-run)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
|
||||||
|
method: POST
|
||||||
|
user: "{{ rmq_user }}"
|
||||||
|
password: "{{ rmq_pass }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
content-type: "application/json"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
properties:
|
||||||
|
content_type: "application/json"
|
||||||
|
routing_key: "{{ control_queue }}"
|
||||||
|
payload: "{{ {
|
||||||
|
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||||
|
'task_name': 'custom_field_set',
|
||||||
|
'task_add1': 'action_state',
|
||||||
|
'task_result': 'inprogress'
|
||||||
|
} | to_json }}"
|
||||||
|
payload_encoding: "string"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
|
- name: Scheduler | Debug action_state inprogress published
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "scheduler-run detected; published action_state=inprogress"
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
# --- Tag device as update-in-progress at start ---
|
# --- Tag device as update-in-progress at start ---
|
||||||
- name: Build control queue payload for update-in-progress tag
|
- name: Build control queue payload for update-in-progress tag
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
@@ -724,6 +793,79 @@
|
|||||||
when: afterupgrade_payload is defined
|
when: afterupgrade_payload is defined
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
|
|
||||||
|
- name: Scheduler | Publish action_state waiting (success)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
|
||||||
|
method: POST
|
||||||
|
user: "{{ rmq_user }}"
|
||||||
|
password: "{{ rmq_pass }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
content-type: "application/json"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
properties:
|
||||||
|
content_type: "application/json"
|
||||||
|
routing_key: "{{ control_queue }}"
|
||||||
|
payload: "{{ {
|
||||||
|
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||||
|
'task_name': 'custom_field_set',
|
||||||
|
'task_add1': 'action_state',
|
||||||
|
'task_result': 'waiting'
|
||||||
|
} | to_json }}"
|
||||||
|
payload_encoding: "string"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
|
- name: Scheduler | Compute action_restart_timestamp (unix seconds) when reboot scheduled
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
action_restart_timestamp: "{{ (lookup('pipe','date -u +%s') | int) + (reboot_seconds | int) }}"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when:
|
||||||
|
- is_run_by_effective == 'scheduler'
|
||||||
|
- reboot_seconds is defined
|
||||||
|
- (reboot_seconds | int) > 0
|
||||||
|
|
||||||
|
- name: Scheduler | Debug computed action_restart_timestamp
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "action_restart_timestamp={{ action_restart_timestamp }} (reboot_seconds={{ reboot_seconds | int }})"
|
||||||
|
when:
|
||||||
|
- is_run_by_effective == 'scheduler'
|
||||||
|
- action_restart_timestamp is defined
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Scheduler | Publish action_restart_timestamp custom field
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
|
||||||
|
method: POST
|
||||||
|
user: "{{ rmq_user }}"
|
||||||
|
password: "{{ rmq_pass }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
content-type: "application/json"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
properties:
|
||||||
|
content_type: "application/json"
|
||||||
|
routing_key: "{{ control_queue }}"
|
||||||
|
payload: "{{ {
|
||||||
|
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||||
|
'task_name': 'custom_field_set',
|
||||||
|
'task_add1': 'action_restart_timestamp',
|
||||||
|
'task_result': (action_restart_timestamp | string)
|
||||||
|
} | to_json }}"
|
||||||
|
payload_encoding: "string"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when:
|
||||||
|
- is_run_by_effective == 'scheduler'
|
||||||
|
- action_restart_timestamp is defined
|
||||||
|
|
||||||
rescue:
|
rescue:
|
||||||
- name: Build failure task name and detail
|
- name: Build failure task name and detail
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
@@ -807,3 +949,34 @@
|
|||||||
(rmq_tag_failed_resp.json is not defined) or
|
(rmq_tag_failed_resp.json is not defined) or
|
||||||
(not (rmq_tag_failed_resp.json.routed | default(false) | bool))
|
(not (rmq_tag_failed_resp.json.routed | default(false) | bool))
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Scheduler | Publish action_state failed (update failure)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://{{ rmq_host }}:{{ rmq_port }}/api/exchanges/{{ rmq_vhost | urlencode }}/{{ rmq_exchange | urlencode }}/publish"
|
||||||
|
method: POST
|
||||||
|
user: "{{ rmq_user }}"
|
||||||
|
password: "{{ rmq_pass }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
status_code: 200
|
||||||
|
headers:
|
||||||
|
content-type: "application/json"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
properties:
|
||||||
|
content_type: "application/json"
|
||||||
|
routing_key: "{{ control_queue }}"
|
||||||
|
payload: "{{ {
|
||||||
|
'inscope_device': (ansible_hostname | default(inventory_hostname)),
|
||||||
|
'task_name': 'custom_field_set',
|
||||||
|
'task_add1': 'action_state',
|
||||||
|
'task_result': 'failed'
|
||||||
|
} | to_json }}"
|
||||||
|
payload_encoding: "string"
|
||||||
|
changed_when: false
|
||||||
|
delegate_to: localhost
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|
||||||
|
- name: Scheduler | Debug action_state failed published (update failure)
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "scheduler-run detected; published action_state=failed (update failure)"
|
||||||
|
when: is_run_by_effective == 'scheduler'
|
||||||
|
|||||||
Reference in New Issue
Block a user