- name: Check if auditd service unit file exists
stat:
path: /usr/lib/systemd/system/auditd.service
register: auditd_unit_file
- name: Check auditd service status only if unit exists
systemd:
name: auditd
register: auditd_status
failed_when: false
when: auditd_unit_file.stat.exists
- name: Check if mlocate-updatedb.timer exists
stat:
path: /usr/lib/systemd/system/mlocate-updatedb.timer
register: mlocate_timer_file
- name: Check mlocate updatedb timer status
systemd:
name: mlocate-updatedb.timer
register: mlocate_timer_status
failed_when: false
when: mlocate_timer_file.stat.exists
- name: Check if updatedb.timer exists (alternative)
stat:
path: /usr/lib/systemd/system/updatedb.timer
register: updatedb_timer_file
- name: Check updatedb timer status
systemd:
name: updatedb.timer
register: updatedb_timer_status
failed_when: false
when: updatedb_timer_file.stat.exists and not mlocate_timer_file.stat.exists
- name: Check if mlocate package is installed
package_facts:
manager: rpm
- name: Check locate database paths
stat:
path: "{{ item }}"
register: locate_db_paths
loop:
- /var/lib/mlocate/mlocate.db
- /var/lib/locate/locatedb
- /var/lib/slocate/slocate.db
- name: Check if chronyd service exists (RHEL 10 default)
stat:
path: /usr/lib/systemd/system/chronyd.service
register: chronyd_unit_file
- name: Check chronyd service status
systemd:
name: chronyd
register: chronyd_status
failed_when: false
when: chronyd_unit_file.stat.exists
- name: Check if ntpd service exists (legacy)
stat:
path: /usr/lib/systemd/system/ntpd.service
register: ntpd_unit_file
- name: Check ntpd service status
systemd:
name: ntpd
register: ntpd_status
failed_when: false
when: ntpd_unit_file.stat.exists
- name: Check if systemd-timesyncd service exists (alternative)
stat:
path: /usr/lib/systemd/system/systemd-timesyncd.service
register: timesyncd_unit_file
- name: Check systemd-timesyncd service status
systemd:
name: systemd-timesyncd
register: timesyncd_status
failed_when: false
when: timesyncd_unit_file.stat.exists
- name: Get current time and timezone
setup:
filter: ansible_date_time
register: current_time_info
- name: Check NTP synchronization status (timedatectl)
command: timedatectl status
register: timedatectl_output
failed_when: false
- name: Check chrony sources (if chronyd is running)
command: chronyc sources -v
register: chrony_sources
failed_when: false
when: chronyd_unit_file.stat.exists and chronyd_status.status is defined and chronyd_status.status.ActiveState == 'active'
- name: Get NTP server sync status from chrony
command: chronyc tracking
register: chrony_tracking
failed_when: false
when: chronyd_unit_file.stat.exists and chronyd_status.status is defined and chronyd_status.status.ActiveState == 'active'
- name: Analyze service status
set_fact:
node_result:
hostname: "{{ inventory_hostname }}"
os_info: "{{ ansible_distribution }} {{ ansible_distribution_version }}"
current_time: "{{ current_time_info.ansible_facts.ansible_date_time.iso8601 }}"
timezone: "{{ current_time_info.ansible_facts.ansible_date_time.tz }}"
auditd:
service_exists: "{{ auditd_unit_file.stat.exists }}"
status: "{{ auditd_status.status.ActiveState if (auditd_unit_file.stat.exists and auditd_status.status is defined) else 'N/A' }}"
enabled: "{{ auditd_status.status.UnitFileState if (auditd_unit_file.stat.exists and auditd_status.status is defined) else 'N/A' }}"
running: "{{ auditd_status.status.ActiveState == 'active' if (auditd_unit_file.stat.exists and auditd_status.status is defined) else false }}"
mlocate:
package_installed: "{{ 'mlocate' in ansible_facts.packages or 'plocate' in ansible_facts.packages }}"
timer_exists: "{{ mlocate_timer_file.stat.exists or updatedb_timer_file.stat.exists }}"
timer_service: "{{ 'mlocate-updatedb.timer' if mlocate_timer_file.stat.exists else ('updatedb.timer' if updatedb_timer_file.stat.exists else 'none') }}"
timer_status: "{{ mlocate_timer_status.status.ActiveState if mlocate_timer_status.status is defined else (updatedb_timer_status.status.ActiveState if updatedb_timer_status.status is defined else 'N/A') }}"
timer_running: "{{ (mlocate_timer_status.status.ActiveState == 'active') if mlocate_timer_status.status is defined else ((updatedb_timer_status.status.ActiveState == 'active') if updatedb_timer_status.status is defined else false) }}"
db_exists: "{{ locate_db_paths.results | selectattr('stat.exists', 'equalto', true) | list | length > 0 }}"
db_path: "{{ (locate_db_paths.results | selectattr('stat.exists', 'equalto', true) | first).item if locate_db_paths.results | selectattr('stat.exists', 'equalto', true) | list | length > 0 else 'none' }}"
db_size_mb: "{{ ((locate_db_paths.results | selectattr('stat.exists', 'equalto', true) | first).stat.size / 1024 / 1024) | round(2) if locate_db_paths.results | selectattr('stat.exists', 'equalto', true) | list | length > 0 else 0 }}"
ntp:
chronyd_exists: "{{ chronyd_unit_file.stat.exists }}"
chronyd_status: "{{ chronyd_status.status.ActiveState if chronyd_status.status is defined else 'N/A' }}"
chronyd_enabled: "{{ chronyd_status.status.UnitFileState if chronyd_status.status is defined else 'N/A' }}"
chronyd_running: "{{ chronyd_status.status.ActiveState == 'active' if chronyd_status.status is defined else false }}"
ntpd_exists: "{{ ntpd_unit_file.stat.exists }}"
ntpd_status: "{{ ntpd_status.status.ActiveState if ntpd_status.status is defined else 'N/A' }}"
ntpd_running: "{{ ntpd_status.status.ActiveState == 'active' if ntpd_status.status is defined else false }}"
timesyncd_exists: "{{ timesyncd_unit_file.stat.exists }}"
timesyncd_status: "{{ timesyncd_status.status.ActiveState if timesyncd_status.status is defined else 'N/A' }}"
timesyncd_running: "{{ timesyncd_status.status.ActiveState == 'active' if timesyncd_status.status is defined else false }}"
ntp_synchronized: "{{ 'NTP synchronized: yes' in timedatectl_output.stdout if timedatectl_output.stdout is defined else false }}"
active_service: "{{ 'chronyd' if (chronyd_unit_file.stat.exists and chronyd_status.status is defined and chronyd_status.status.ActiveState == 'active') else ('ntpd' if (ntpd_unit_file.stat.exists and ntpd_status.status is defined and ntpd_status.status.ActiveState == 'active') else ('systemd-timesyncd' if (timesyncd_unit_file.stat.exists and timesyncd_status.status is defined and timesyncd_status.status.ActiveState == 'active') else 'none')) }}"
chrony_sources: "{{ chrony_sources.stdout if chrony_sources.stdout is defined else 'N/A' }}"
chrony_tracking: "{{ chrony_tracking.stdout if chrony_tracking.stdout is defined else 'N/A' }}"
- name: Display clear service status
debug:
msg: |
================================================
HOST: {{ inventory_hostname }} ({{ ansible_distribution }} {{ ansible_distribution_version }})
================================================
🕐 CURRENT TIME: {{ node_result.current_time }} ({{ node_result.timezone }})
🔍 AUDITD SERVICE ANALYSIS:
{% if node_result.auditd.service_exists %}
✅ Service Definition: FOUND (/usr/lib/systemd/system/auditd.service)
{% if node_result.auditd.running %}
✅ Current Status: ACTIVE AND RUNNING
{% else %}
❌ Current Status: {{ node_result.auditd.status | upper }} (NOT RUNNING)
{% endif %}
📋 Boot Status: {{ node_result.auditd.enabled | upper }}
{% else %}
❌ Service Definition: NOT FOUND
❌ auditd is NOT INSTALLED on this system
{% endif %}
📍 MLOCATE/LOCATE SERVICE ANALYSIS:
{% if node_result.mlocate.package_installed %}
✅ Package: INSTALLED (mlocate or plocate found)
{% else %}
❌ Package: NOT INSTALLED
{% endif %}
{% if node_result.mlocate.timer_exists %}
✅ Timer Service: FOUND ({{ node_result.mlocate.timer_service }})
{% if node_result.mlocate.timer_running %}
✅ Timer Status: ACTIVE AND RUNNING
{% else %}
❌ Timer Status: {{ node_result.mlocate.timer_status | upper }} (NOT RUNNING)
{% endif %}
{% else %}
❌ Timer Service: NOT FOUND
{% endif %}
{% if node_result.mlocate.db_exists %}
✅ Locate Database: FOUND
📁 Path: {{ node_result.mlocate.db_path }}
📏 Size: {{ node_result.mlocate.db_size_mb }}MB
{% else %}
❌ Locate Database: NOT FOUND
{% endif %}
🕐 NTP/TIME SYNCHRONIZATION ANALYSIS:
{% if node_result.ntp.active_service != 'none' %}
✅ Active Time Service: {{ node_result.ntp.active_service | upper }}
{% if node_result.ntp.ntp_synchronized %}
✅ NTP Synchronization: SYNCHRONIZED
{% else %}
⚠️ NTP Synchronization: NOT SYNCHRONIZED
{% endif %}
{% else %}
❌ No Active Time Service Found
{% endif %}
📊 Time Services Status:
{% if node_result.ntp.chronyd_exists %}
├─ chronyd: {{ '✅ RUNNING' if node_result.ntp.chronyd_running else ('❌ ' + node_result.ntp.chronyd_status) }} ({{ node_result.ntp.chronyd_enabled }})
{% endif %}
{% if node_result.ntp.ntpd_exists %}
├─ ntpd: {{ '✅ RUNNING' if node_result.ntp.ntpd_running else ('❌ ' + node_result.ntp.ntpd_status) }}
{% endif %}
{% if node_result.ntp.timesyncd_exists %}
└─ systemd-timesyncd: {{ '✅ RUNNING' if node_result.ntp.timesyncd_running else ('❌ ' + node_result.ntp.timesyncd_status) }}
{% endif %}
🎯 SUMMARY:
{% if node_result.auditd.service_exists and node_result.auditd.running %}
✅ auditd: FULLY OPERATIONAL
{% elif node_result.auditd.service_exists %}
⚠️ auditd: INSTALLED BUT NOT RUNNING
{% else %}
❌ auditd: NOT INSTALLED
{% endif %}
{% if node_result.mlocate.package_installed and node_result.mlocate.timer_running and node_result.mlocate.db_exists %}
✅ mlocate: FULLY OPERATIONAL
{% elif node_result.mlocate.package_installed %}
⚠️ mlocate: INSTALLED BUT NOT FULLY FUNCTIONAL
{% else %}
❌ mlocate: NOT INSTALLED
{% endif %}
{% if node_result.ntp.active_service != 'none' and node_result.ntp.ntp_synchronized %}
✅ NTP: FULLY OPERATIONAL ({{ node_result.ntp.active_service }})
{% elif node_result.ntp.active_service != 'none' %}
⚠️ NTP: SERVICE RUNNING BUT NOT SYNCHRONIZED ({{ node_result.ntp.active_service }})
{% else %}
❌ NTP: NO TIME SERVICE RUNNING
{% endif %}
- name: Save individual host result to temp file
copy:
content: "{{ node_result | to_nice_json }}"
dest: "/tmp/service_results_{{ inventory_hostname }}.json"
delegate_to: localhost
- name: Find all result files
find:
paths: /tmp
patterns: "service_results_*.json"
register: result_files
delegate_to: localhost
run_once: true
- name: Read all result files
slurp:
src: "{{ item.path }}"
register: file_contents
loop: "{{ result_files.files }}"
delegate_to: localhost
run_once: true
- name: Combine all results
set_fact:
service_check_results: "{{ service_check_results | default([]) + [item.content | b64decode | from_json] }}"
loop: "{{ file_contents.results }}"
delegate_to: localhost
run_once: true
- name: Clean up temporary files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ result_files.files }}"
delegate_to: localhost
run_once: true
- name: Calculate statistics step by step
set_fact:
total_nodes: "{{ service_check_results | length }}"
delegate_to: localhost
run_once: true
- name: Calculate auditd statistics
set_fact:
auditd_installed_list: "{{ service_check_results | selectattr('auditd.service_exists', 'equalto', true) | list }}"
auditd_running_list: "{{ service_check_results | selectattr('auditd.running', 'equalto', true) | list }}"
delegate_to: localhost
run_once: true
- name: Calculate mlocate statistics
set_fact:
mlocate_installed_list: "{{ service_check_results | selectattr('mlocate.package_installed', 'equalto', true) | list }}"
delegate_to: localhost
run_once: true
- name: Calculate mlocate functional nodes
set_fact:
mlocate_functional_list: "{{ mlocate_installed_list | selectattr('mlocate.timer_running', 'equalto', true) | selectattr('mlocate.db_exists', 'equalto', true) | list }}"
delegate_to: localhost
run_once: true
- name: Calculate NTP statistics
set_fact:
ntp_service_running_list: "{{ service_check_results | selectattr('ntp.active_service', 'ne', 'none') | list }}"
ntp_synchronized_list: "{{ service_check_results | selectattr('ntp.ntp_synchronized', 'equalto', true) | list }}"
delegate_to: localhost
run_once: true
- name: Calculate time differences from first node
set_fact:
time_reference: "{{ service_check_results[0].current_time }}"
delegate_to: localhost
run_once: true
- name: Parse times and calculate differences
set_fact:
time_analysis: |
{% set ref_time = time_reference | to_datetime('%Y-%m-%dT%H:%M:%S%z') %}
{% for node in service_check_results %}
{% set node_time = node.current_time | to_datetime('%Y-%m-%dT%H:%M:%S%z') %}
{% set diff_seconds = ((node_time - ref_time).total_seconds()) %}
{{ node.hostname }}:{{ diff_seconds | round(2) }},
{% endfor %}
delegate_to: localhost
run_once: true
- name: Set final statistics
set_fact:
auditd_installed: "{{ auditd_installed_list | length }}"
auditd_running: "{{ auditd_running_list | length }}"
mlocate_installed: "{{ mlocate_installed_list | length }}"
mlocate_functional: "{{ mlocate_functional_list | length }}"
ntp_service_running: "{{ ntp_service_running_list | length }}"
ntp_synchronized: "{{ ntp_synchronized_list | length }}"
delegate_to: localhost
run_once: true
- name: Get problem nodes
set_fact:
auditd_missing_nodes: "{{ service_check_results | selectattr('auditd.service_exists', 'equalto', false) | map(attribute='hostname') | list }}"
mlocate_missing_nodes: "{{ service_check_results | selectattr('mlocate.package_installed', 'equalto', false) | map(attribute='hostname') | list }}"
ntp_missing_nodes: "{{ service_check_results | selectattr('ntp.active_service', 'equalto', 'none') | map(attribute='hostname') | list }}"
ntp_unsync_nodes: "{{ service_check_results | selectattr('ntp.ntp_synchronized', 'equalto', false) | map(attribute='hostname') | list }}"
delegate_to: localhost
run_once: true
- name: Display final summary
debug:
msg: |
================================================
📊 OVERALL SUMMARY REPORT
================================================
Total Nodes Analyzed: {{ total_nodes }}
🔍 AUDITD SERVICE:
├─ Installed: {{ auditd_installed }}/{{ total_nodes }} nodes ({{ ((auditd_installed | int / total_nodes | int) * 100) | round(1) }}%)
├─ Running: {{ auditd_running }}/{{ total_nodes }} nodes ({{ ((auditd_running | int / total_nodes | int) * 100) | round(1) }}%)
└─ Issues: {% if auditd_missing_nodes %}{{ auditd_missing_nodes | join(', ') }}{% else %}None{% endif %}
📍 MLOCATE SERVICE:
├─ Installed: {{ mlocate_installed }}/{{ total_nodes }} nodes ({{ ((mlocate_installed | int / total_nodes | int) * 100) | round(1) }}%)
├─ Fully Functional: {{ mlocate_functional }}/{{ total_nodes }} nodes ({{ ((mlocate_functional | int / total_nodes | int) * 100) | round(1) }}%)
└─ Issues: {% if mlocate_missing_nodes %}{{ mlocate_missing_nodes | join(', ') }}{% else %}None{% endif %}
🕐 NTP/TIME SYNCHRONIZATION:
├─ Service Running: {{ ntp_service_running }}/{{ total_nodes }} nodes ({{ ((ntp_service_running | int / total_nodes | int) * 100) | round(1) }}%)
├─ NTP Synchronized: {{ ntp_synchronized }}/{{ total_nodes }} nodes ({{ ((ntp_synchronized | int / total_nodes | int) * 100) | round(1) }}%)
├─ No Time Service: {% if ntp_missing_nodes %}{{ ntp_missing_nodes | join(', ') }}{% else %}None{% endif %}
└─ Not Synchronized: {% if ntp_unsync_nodes %}{{ ntp_unsync_nodes | join(', ') }}{% else %}None{% endif %}
⏰ TIME DIFFERENCES FROM REFERENCE ({{ service_check_results[0].hostname }}):
{% for node in service_check_results %}
{% if loop.index0 == 0 %}
├─ {{ node.hostname }}: 0.00s (Reference Node)
{% else %}
{% set ref_time = service_check_results[0].current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') %}
{% set node_time = node.current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') %}
{% set diff_seconds = (node_time - ref_time).total_seconds() %}
{% if diff_seconds | abs > 30 %}
├─ {{ node.hostname }}: {{ diff_seconds | round(2) }}s ⚠️ SIGNIFICANT DRIFT
{% elif diff_seconds | abs > 5 %}
├─ {{ node.hostname }}: {{ diff_seconds | round(2) }}s ⚠️ MINOR DRIFT
{% else %}
├─ {{ node.hostname }}: {{ diff_seconds | round(2) }}s ✅ OK
{% endif %}
{% endif %}
{% endfor %}
delegate_to: localhost
run_once: true
- name: Display auditd issues if any
debug:
msg: |
⚠️ AUDITD ISSUES DETECTED:
{% for node in service_check_results %}
{% if not node.auditd.service_exists %}
❌ {{ node.hostname }}: auditd NOT INSTALLED
{% elif not node.auditd.running %}
⚠️ {{ node.hostname }}: auditd installed but {{ node.auditd.status }}
{% endif %}
{% endfor %}
when: auditd_running | int < total_nodes | int
delegate_to: localhost
run_once: true
- name: Display mlocate issues if any
debug:
msg: |
⚠️ MLOCATE ISSUES DETECTED:
{% for node in service_check_results %}
{% if node.mlocate.package_installed and not (node.mlocate.timer_running and node.mlocate.db_exists) %}
⚠️ {{ node.hostname }}: mlocate installed but timer={{ node.mlocate.timer_status }}, db={{ 'exists' if node.mlocate.db_exists else 'missing' }}
{% endif %}
{% endfor %}
- name: Display NTP/Time issues if any
debug:
msg: |
⚠️ NTP/TIME SYNCHRONIZATION ISSUES DETECTED:
{% for node in service_check_results %}
{% if node.ntp.active_service == 'none' %}
❌ {{ node.hostname }}: NO TIME SERVICE RUNNING
{% elif not node.ntp.ntp_synchronized %}
⚠️ {{ node.hostname }}: {{ node.ntp.active_service }} running but NOT SYNCHRONIZED
{% endif %}
{% endfor %}
when: ntp_service_running | int < total_nodes | int or ntp_synchronized | int < ntp_service_running | int
delegate_to: localhost
run_once: true: localhost
run_once: true
- name: Generate clear status report
copy:
content: |
Hostname,OS,Current_Time,Timezone,Time_Diff_Seconds,auditd_Installed,auditd_Running,auditd_Status,mlocate_Installed,mlocate_Timer_Running,mlocate_DB_Exists,NTP_Service_Running,NTP_Synchronized,NTP_Active_Service,chronyd_Status,ntpd_Status,timesyncd_Status,Overall_auditd,Overall_mlocate,Overall_NTP
{% for node in service_check_results -%}
{% if loop.index0 == 0 -%}
{% set time_diff = 0.0 -%}
{% else -%}
{% set ref_time = service_check_results[0].current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') -%}
{% set node_time = node.current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') -%}
{% set time_diff = (node_time - ref_time).total_seconds() -%}
{% endif -%}
{{ node.hostname }},{{ node.os_info }},{{ node.current_time }},{{ node.timezone }},{{ time_diff | round(2) }},{{ node.auditd.service_exists }},{{ node.auditd.running }},{{ node.auditd.status }},{{ node.mlocate.package_installed }},{{ node.mlocate.timer_running }},{{ node.mlocate.db_exists }},{{ 'YES' if node.ntp.active_service != 'none' else 'NO' }},{{ node.ntp.ntp_synchronized }},{{ node.ntp.active_service }},{{ node.ntp.chronyd_status }},{{ node.ntp.ntpd_status }},{{ node.ntp.timesyncd_Status }},{{ 'OK' if node.auditd.running else ('INSTALLED' if node.auditd.service_exists else 'MISSING') }},{{ 'OK' if (node.mlocate.package_installed and node.mlocate.timer_running and node.mlocate.db_exists) else ('PARTIAL' if node.mlocate.package_installed else 'MISSING') }},{{ 'OK' if (node.ntp.active_service != 'none' and node.ntp.ntp_synchronized) else ('PARTIAL' if node.ntp.active_service != 'none' else 'MISSING') }}
{% endfor %}
dest: "./clear_service_status.csv"
delegate_to: localhost
run_once: true
- name: Generate time synchronization detailed report
copy:
content: |
Hostname,Current_Time,Timezone,Time_Diff_From_Reference,Time_Status,NTP_Service,NTP_Synchronized,chronyd_Running,ntpd_Running,timesyncd_Running,Chrony_Sources,Chrony_Tracking
{% for node in service_check_results -%}
{% if loop.index0 == 0 -%}
{% set time_diff = 0.0 -%}
{% set time_status = "REFERENCE" -%}
{% else -%}
{% set ref_time = service_check_results[0].current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') -%}
{% set node_time = node.current_time | to_datetime('%Y-%m-%dT%H:%M:%SZ') -%}
{% set time_diff = (node_time - ref_time).total_seconds() -%}
{% if time_diff | abs > 30 -%}
{% set time_status = "SIGNIFICANT_DRIFT" -%}
{% elif time_diff | abs > 5 -%}
{% set time_status = "MINOR_DRIFT" -%}
{% else -%}
{% set time_status = "OK" -%}
{% endif -%}
{% endif -%}
{{ node.hostname }},{{ node.current_time }},{{ node.timezone }},{{ time_diff | round(2) }},{{ time_status }},{{ node.ntp.active_service }},{{ node.ntp.ntp_synchronized }},{{ node.ntp.chronyd_running }},{{ node.ntp.ntpd_running }},{{ node.ntp.timesyncd_running }},"{{ node.ntp.chrony_sources | replace('\n', ' | ') | replace('"', '""') }}","{{ node.ntp.chrony_tracking | replace('\n', ' | ') | replace('"', '""') }}"
{% endfor %}
dest: "./time_sync_detailed_report.csv"
delegate_to: localhost
run_once: true
- name: Display action recommendations
debug:
msg: |
================================================
🔧 RECOMMENDED ACTIONS
================================================
{% if auditd_installed < total_nodes %}
📥 Install auditd on missing nodes:
sudo yum install audit (RHEL/CentOS)
{% endif %}
{% if auditd_running < auditd_installed %}
🚀 Start auditd service on inactive nodes:
sudo systemctl start auditd
sudo systemctl enable auditd
{% endif %}
{% if mlocate_installed < total_nodes %}
📥 Install mlocate on missing nodes:
sudo yum install mlocate (RHEL/CentOS)
{% endif %}
{% if mlocate_functional < mlocate_installed %}
🚀 Fix mlocate issues:
sudo systemctl start mlocate-updatedb.timer
sudo systemctl enable mlocate-updatedb.timer
sudo updatedb # Manual database update
{% endif %}
{% if ntp_service_running < total_nodes %}
📥 Install and configure NTP service on missing nodes:
# For RHEL 10 (recommended):
sudo yum install chrony
sudo systemctl start chronyd
sudo systemctl enable chronyd
# Alternative options:
sudo yum install ntp # Legacy ntpd
sudo yum install systemd-timesyncd # Minimal option
{% endif %}
{% if ntp_synchronized < ntp_service_running %}
🚀 Fix NTP synchronization issues:
# Check NTP configuration:
sudo timedatectl status
sudo chronyc sources -v # For chronyd
sudo chronyc tracking # For chronyd
# Force time synchronization:
sudo chrony -q 'server pool.ntp.org iburst'
sudo systemctl restart chronyd
{% endif %}
{% if ntp_unsync_nodes %}
⚠️ Time drift detected on nodes: {{ ntp_unsync_nodes | join(', ') }}
Consider checking network connectivity to NTP servers and firewall settings.
{% endif %}
📄 Reports generated:
- ./clear_service_status.csv (Complete service status)
- ./time_sync_detailed_report.csv (Detailed time sync analysis)
delegate_to: localhost
run_once: true