--- - name: Backup and/or Upgrade MikroTik hosts: mikrotik_routers gather_facts: no serial: 10 vars: backup_dir: /opt/mikrotik_backups/ checkmk_url: "http://checkmk-server/mysite/check_mk/api/1.0/domain-types/service/collections/all" checkmk_service_description: "MikroTik Backup/Upgrade" tasks: # ---------------------------- # Always: identity + timestamp # ---------------------------- - name: Get router identity community.routeros.command: commands: /system identity print register: identity_raw tags: always - name: Parse router name ansible.builtin.set_fact: router_name: "{{ identity_raw.stdout[0].split(': ')[1] | trim }}" tags: always - name: Get timestamp ansible.builtin.command: date +%Y-%m-%d_%H-%M-%S register: date_out delegate_to: localhost changed_when: false tags: always - name: Set timestamp fact ansible.builtin.set_fact: ts: "{{ date_out.stdout }}" tags: always # ---------------------------- # Backup (tag: backup) # ---------------------------- - name: Ensure local backup directory exists ansible.builtin.file: path: "{{ backup_dir }}" state: directory mode: "0755" delegate_to: localhost tags: backup - name: Export router config community.routeros.command: commands: /export terse show-sensitive register: export_cfg tags: backup - name: Save export locally ansible.builtin.copy: content: "{{ export_cfg.stdout[0] }}" dest: "{{ backup_dir }}/{{ router_name }}-{{ ts }}.rsc" mode: "0600" delegate_to: localhost tags: backup - name: Mark backup success ansible.builtin.set_fact: backup_ok: true backup_file: "{{ backup_dir }}/{{ router_name }}-{{ ts }}.rsc" tags: backup # ---------------------------- # Upgrade (tag: upgrade) # ---------------------------- - name: Trigger update check community.routeros.command: commands: /system package update check-for-updates once tags: upgrade - name: Wait before reading update info ansible.builtin.pause: seconds: 5 tags: upgrade - name: Get update info (plain) community.routeros.command: commands: /system package update print register: update_info tags: upgrade # ---------------------------- # Parse versions (FIXED) # ---------------------------- - name: Parse installed and latest versions ansible.builtin.set_fact: installed_version: >- {{ (update_info.stdout[0] | regex_search('installed-version: ([^\\s]+)', '\\1') | default(['unknown'], true)) | first }} latest_version: >- {{ (update_info.stdout[0] | regex_search('latest-version: ([^\\s]+)', '\\1') | default(['unknown'], true)) | first }} update_status: >- {{ (update_info.stdout[0] | regex_search('status: (.+)', '\\1') | default(['unknown'], true)) | first }} update_channel: >- {{ (update_info.stdout[0] | regex_search('channel: ([^\\s]+)', '\\1') | default(['unknown'], true)) | first }} tags: upgrade - name: Debug parsed update info ansible.builtin.debug: msg: "Router={{ router_name }} channel={{ update_channel }} installed={{ installed_version }} latest={{ latest_version }} status={{ update_status }}" tags: upgrade # ---------------------------- # Detect problems (FIXED) # ---------------------------- - name: Detect update failure ansible.builtin.set_fact: update_failed: "{{ 'error' in (update_status | lower) or 'failed' in (update_status | lower) }}" tags: upgrade - name: Warn if update failed ansible.builtin.debug: msg: "WARNING: Update check failed on {{ router_name }} (status={{ update_status }})" when: update_failed tags: upgrade # ---------------------------- # Decide upgrade # ---------------------------- - name: Set upgrade-needed fact ansible.builtin.set_fact: upgrade_needed: >- {{ not update_failed and installed_version != 'unknown' and latest_version != 'unknown' and installed_version != latest_version }} tags: upgrade - name: Show decision ansible.builtin.debug: msg: "Router={{ router_name }} installed={{ installed_version }} latest={{ latest_version }} upgrade_needed={{ upgrade_needed }}" tags: upgrade - name: Skip upgrade if already latest ansible.builtin.debug: msg: "Router {{ router_name }} is already on latest version {{ installed_version }}" when: not upgrade_needed tags: upgrade # ---------------------------- # Perform upgrade # ---------------------------- - name: Trigger package download and install community.routeros.command: commands: /system package update install register: upgrade_result when: upgrade_needed tags: upgrade - name: Wait for router to come back online after reboot ansible.builtin.wait_for_connection: delay: 180 timeout: 600 sleep: 10 when: - upgrade_needed - upgrade_result is succeeded tags: upgrade - name: Confirm upgraded version community.routeros.command: commands: /system resource print register: post_upgrade_info when: upgrade_needed tags: upgrade - name: Parse post-upgrade version ansible.builtin.set_fact: post_upgrade_version: "{{ post_upgrade_info.stdout[0] | regex_search('version: ([^\\s]+)', '\\1') | default('unknown', true) }}" when: upgrade_needed tags: upgrade - name: Show post-upgrade RouterOS version ansible.builtin.debug: msg: "Router {{ router_name }} upgraded to version {{ post_upgrade_version }}" when: upgrade_needed tags: upgrade # ---------------------------- # Build Checkmk status # ---------------------------- - name: Build Checkmk result when no upgrade was needed ansible.builtin.set_fact: cmk_state: "{{ 2 if update_failed else 0 }}" cmk_output: >- {{ 'CRIT' if update_failed else 'OK' }} - router={{ router_name }} backup=ok installed={{ installed_version }} latest={{ latest_version }} status={{ update_status }} upgrade_needed=no backup_file={{ backup_file | default('n/a') }} backup_ts={{ ts }} when: not upgrade_needed tags: always - name: Build Checkmk result when upgrade succeeded ansible.builtin.set_fact: cmk_state: "{{ 0 if post_upgrade_version == latest_version else 2 }}" cmk_output: >- {{ 'OK' if post_upgrade_version == latest_version else 'CRIT' }} - router={{ router_name }} backup=ok installed_before={{ installed_version }} latest={{ latest_version }} status={{ update_status }} upgrade_needed=yes upgraded_to={{ post_upgrade_version }} backup_file={{ backup_file | default('n/a') }} backup_ts={{ ts }} when: upgrade_needed tags: always # ---------------------------- # Push result to Checkmk # ---------------------------- - name: Send result to Checkmk delegate_to: localhost ansible.builtin.uri: url: "{{ checkmk_url }}" method: POST headers: Authorization: "Bearer {{ lookup('env', 'CHECKMK_TOKEN') }}" Accept: "application/json" Content-Type: "application/json" body_format: json body: host_name: "{{ router_name }}" service_description: "{{ checkmk_service_description }}" state: "{{ cmk_state | int }}" output: "{{ cmk_output }}" return_content: true status_code: - 200 - 201 - 204 register: checkmk_result failed_when: false tags: always - name: Show Checkmk response ansible.builtin.debug: var: checkmk_result tags: always