---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

##
## test setting timezone, idempotency and checkmode
##

- name: set timezone to Australia/Brisbane (checkmode)
  timezone:
    name: Australia/Brisbane
  check_mode: true
  register: timezone_set_checkmode

- name: ensure timezone reported as changed in checkmode
  assert:
    that:
      - timezone_set_checkmode.changed
      - timezone_set_checkmode.diff.after.name == 'Australia/Brisbane'
      - timezone_set_checkmode.diff.before.name == 'Etc/UTC'

- name: ensure checkmode didn't change the timezone
  command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane
  register: result
  failed_when: result is not failed
  changed_when: false

- name: ensure that checkmode didn't update the timezone in the config file
  command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }}
  when:
    - ansible_service_mgr != 'systemd'
    - ansible_os_family == 'RedHat'

- name: ensure that checkmode didn't update the timezone in the config file
  command: egrep '^Etc/UTC' {{ timezone_config_file }}
  when:
    - ansible_service_mgr != 'systemd'
    - ansible_os_family == 'Debian'

- name: set timezone to Australia/Brisbane
  timezone:
    name: Australia/Brisbane
  register: timezone_set

- name: ensure timezone changed
  assert:
    that:
      - timezone_set.changed
      - timezone_set.diff.after.name == 'Australia/Brisbane'
      - timezone_set.diff.before.name == 'Etc/UTC'

- name: ensure that the timezone is actually set
  command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane
  changed_when: false

- name: ensure that the timezone is updated in the config file
  command: egrep '^(TIME)?ZONE="Australia/Brisbane"' {{ timezone_config_file }}
  when:
    - ansible_service_mgr != 'systemd'
    - ansible_os_family == 'RedHat'

- name: ensure that the timezone is updated in the config file
  command: egrep '^Australia/Brisbane' {{ timezone_config_file }}
  when:
    - ansible_service_mgr != 'systemd'
    - ansible_os_family == 'Debian'

- name: set timezone to Australia/Brisbane again
  timezone:
    name: Australia/Brisbane
  register: timezone_again

- name: ensure timezone idempotency
  assert:
    that:
      - not timezone_again.changed

- name: set timezone to Australia/Brisbane again in checkmode
  timezone:
    name: Australia/Brisbane
  register: timezone_again_checkmode

- name: set timezone idempotency (checkmode)
  assert:
    that:
      - not timezone_again_checkmode.changed

##
## tests for same timezones with different names
##

- name: check dpkg-reconfigure
  shell: type dpkg-reconfigure
  register: check_dpkg_reconfigure
  ignore_errors: true
  changed_when: false

- name: check timedatectl
  shell: type timedatectl && timedatectl
  register: check_timedatectl
  ignore_errors: true
  changed_when: false

- block:
    - name: set timezone to Etc/UTC
      timezone:
        name: Etc/UTC

    - name: change timezone from Etc/UTC to UTC
      timezone:
        name: UTC
      register: timezone_etcutc_to_utc

    - name: check timezone changed from Etc/UTC to UTC
      assert:
        that:
          - timezone_etcutc_to_utc.changed
          - timezone_etcutc_to_utc.diff.before.name == 'Etc/UTC'
          - timezone_etcutc_to_utc.diff.after.name == 'UTC'

    - name: change timezone from UTC to Etc/UTC
      timezone:
        name: Etc/UTC
      register: timezone_utc_to_etcutc

    - name: check timezone changed from UTC to Etc/UTC
      assert:
        that:
          - timezone_utc_to_etcutc.changed
          - timezone_utc_to_etcutc.diff.before.name == 'UTC'
          - timezone_utc_to_etcutc.diff.after.name == 'Etc/UTC'

  when:
    # FIXME: Due to the bug of the dpkg-reconfigure, those tests failed on non-systemd debian
    - check_dpkg_reconfigure.rc != 0 or check_timedatectl.rc == 0

##
## no systemd tests for timezone
##

- block:
  ##
  ## test with empty config file
  ##

    - name: empty config file
      command: cp /dev/null {{ timezone_config_file }}

    - name: set timezone to Europe/Belgrade (empty config file)
      timezone:
        name: Europe/Belgrade
      register: timezone_empty_conf

    - name: check if timezone set (empty config file)
      assert:
        that:
          - timezone_empty_conf.changed
          - timezone_empty_conf.diff.after.name == 'Europe/Belgrade'
          - timezone_empty_conf.diff.before.name == 'n/a'

    - name: check if the timezone is actually set (empty config file)
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false


  ##
  ## test with deleted config file
  ##

    - name: remove config file
      file:
        path: '{{ timezone_config_file }}'
        state: absent

    - name: set timezone to Europe/Belgrade (no config file)
      timezone:
        name: Europe/Belgrade
      register: timezone_missing_conf

    - name: check if timezone set (no config file)
      assert:
        that:
          - timezone_missing_conf.changed
          - timezone_missing_conf.diff.after.name == 'Europe/Belgrade'
          - timezone_missing_conf.diff.before.name == 'n/a'

    - name: check if the timezone is actually set (no config file)
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false


  ##
  ## test with /etc/localtime as symbolic link to a zoneinfo file
  ##

    - name: create symlink /etc/locatime -> /usr/share/zoneinfo/Etc/UTC
      file:
        src: /usr/share/zoneinfo/Etc/UTC
        dest: /etc/localtime
        state: link
        force: true

    - name: set timezone to Europe/Belgrade (over symlink)
      timezone:
        name: Europe/Belgrade
      register: timezone_symllink

    - name: check if timezone set (over symlink)
      assert:
        that:
          - timezone_symllink.changed
          - timezone_symllink.diff.after.name == 'Europe/Belgrade'
          - timezone_symllink.diff.before.name == 'Etc/UTC'

    - name: check if the timezone is actually set (over symlink)
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false


  ##
  ## test with /etc/localtime as broken symbolic link
  ##

    - name: set timezone to a broken symlink
      file:
        src: /tmp/foo
        dest: /etc/localtime
        state: link
        force: true

    - name: set timezone to Europe/Belgrade (over broken symlink)
      timezone:
        name: Europe/Belgrade
      register: timezone_symllink_broken

    - name: check if timezone set (over broken symlink)
      assert:
        that:
          - timezone_symllink_broken.changed
          - timezone_symllink_broken.diff.after.name == 'Europe/Belgrade'
          - timezone_symllink_broken.diff.before.name == 'n/a'

    - name: check if the timezone is actually set (over broken symlink)
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false


  ##
  ## test with /etc/localtime set manually using copy
  ##

    - name: set timezone manually by coping zone info file to /etc/localtime
      copy:
        src: /usr/share/zoneinfo/Etc/UTC
        dest: /etc/localtime
        remote_src: true

    - name: set timezone to Europe/Belgrade (over copied file)
      timezone:
        name: Europe/Belgrade
      register: timezone_copied

    - name: check if timezone set (over copied file)
      assert:
        that:
          - timezone_copied.changed
          - timezone_copied.diff.after.name == 'Europe/Belgrade'
          - timezone_copied.diff.before.name == 'n/a'

    - name: check if the timezone is actually set (over copied file)
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false
  when:
    - ansible_service_mgr != 'systemd'
    - timezone_config_file is defined


####
#### hwclock tests
####

- name: check if hwclock is supported in the environment
  command: hwclock --test
  register: hwclock_test
  ignore_errors: true

- name: check if timedatectl works in the environment
  command: timedatectl
  register: timedatectl_test
  ignore_errors: true

- name:
  set_fact:
    hwclock_supported: '{{ hwclock_test is successful or (timedatectl_test is successful and "RTC time: n/a" not in timedatectl_test.stdout) }}'
##
## test set hwclock, idempotency and checkmode
##

- block:
    - name: set hwclock to local
      timezone:
        hwclock: local

    - name: set hwclock to UTC (checkmode)
      timezone:
        hwclock: UTC
      check_mode: true
      register: hwclock_set_checkmode

    - name: ensure hwclock reported as changed (checkmode)
      assert:
        that:
          - hwclock_set_checkmode.changed
          - hwclock_set_checkmode.diff.after.hwclock == 'UTC'
          - hwclock_set_checkmode.diff.before.hwclock == 'local'

    - block:
        - name: ensure that checkmode didn't update hwclock in /etc/adjtime
          command: grep ^UTC /etc/adjtime
          register: result
          failed_when: result is not failed

        - name: ensure that checkmode didn't update hwclock the config file
          command: grep ^UTC=no {{ hwclock_config_file }}
      when: ansible_service_mgr != 'systemd'

    - name: set hwclock to UTC
      timezone:
        hwclock: UTC
      register: hwclock_set

    - name: ensure hwclock changed
      assert:
        that:
          - hwclock_set.changed
          - hwclock_set.diff.after.hwclock == 'UTC'
          - hwclock_set.diff.before.hwclock == 'local'

    - block:
        - name: ensure that hwclock is updated in /etc/adjtime
          command: grep ^UTC /etc/adjtime

        - name: ensure that hwclock is updated in the config file
          command: grep ^UTC=yes {{ hwclock_config_file }}
      when: ansible_service_mgr != 'systemd'

    - name: set hwclock to RTC again
      timezone:
        hwclock: UTC
      register: hwclock_again

    - name: set hwclock idempotency
      assert:
        that:
          - not hwclock_again.changed

    - name: set hwclock to RTC again (checkmode)
      timezone:
        hwclock: UTC
      check_mode: true
      register: hwclock_again_checkmode

    - name: set hwclock idempotency (checkmode)
      assert:
        that:
          - not hwclock_again_checkmode.changed


    ##
    ## no systemd tests for hwclock
    ##

    - block:
        ##
        ## test set hwclock with both /etc/adjtime and conf file deleted
        ##

        - name: remove /etc/adjtime and conf file
          file:
            path: '{{ item }}'
            state: absent
          with_items:
            - /etc/adjtime
            - '{{ hwclock_config_file }}'

        - name: set hwclock to UTC with deleted /etc/adjtime and conf file
          timezone:
            hwclock: UTC
          register: hwclock_set_utc_deleted_adjtime_and_conf

        - name: ensure hwclock changed with deleted /etc/adjtime and conf
          assert:
            that:
              - hwclock_set_utc_deleted_adjtime_and_conf.changed
              - hwclock_set_utc_deleted_adjtime_and_conf.diff.after.hwclock == 'UTC'
              - hwclock_set_utc_deleted_adjtime_and_conf.diff.before.hwclock == 'n/a'


        ##
        ## test set hwclock with /etc/adjtime deleted
        ##

        - name: remove /etc/adjtime
          file:
            path: '{{ item }}'
            state: absent
          with_items:
            - /etc/adjtime

        - name: set hwclock to UTC with deleted /etc/adjtime
          timezone:
            hwclock: UTC
          register: hwclock_set_utc_deleted_adjtime_utc

        - name: ensure hwclock changed with deleted /etc/adjtime
          assert:
            that:
              - not hwclock_set_utc_deleted_adjtime_utc.changed
              - hwclock_set_utc_deleted_adjtime_utc.diff.after.hwclock == 'UTC'
              - hwclock_set_utc_deleted_adjtime_utc.diff.before.hwclock == 'UTC'

        - name: set hwclock to LOCAL with deleted /etc/adjtime
          timezone:
            hwclock: local
          register: hwclock_set_local_deleted_adjtime_local

        - name: ensure hwclock changed to LOCAL with deleted /etc/adjtime
          assert:
            that:
              - hwclock_set_local_deleted_adjtime_local.changed
              - hwclock_set_local_deleted_adjtime_local.diff.after.hwclock == 'local'
              - hwclock_set_local_deleted_adjtime_local.diff.before.hwclock == 'UTC'
     

        ##
        ## test set hwclock with conf file deleted
        ##

        - name: remove conf file
          file:
            path: '{{ item }}'
            state: absent
          with_items:
            - '{{ hwclock_config_file }}'

        - name: set hwclock to UTC with deleted conf
          timezone:
            hwclock: UTC
          register: hwclock_set_utc_deleted_conf

        - name: ensure hwclock changed with deleted /etc/adjtime
          assert:
            that:
              - hwclock_set_utc_deleted_conf.changed
              - hwclock_set_utc_deleted_conf.diff.after.hwclock == 'UTC'
              - hwclock_set_utc_deleted_conf.diff.before.hwclock == 'n/a'


        ##
        ## test set hwclock with /etc/adjtime missing UTC/LOCAL strings
        ##

        - name: create /etc/adjtime without UTC/LOCAL
          copy:
            content: '0.0 0 0\n0'
            dest: /etc/adjtime

        - name: set hwclock to UTC with broken /etc/adjtime
          timezone:
            hwclock: UTC
          register: hwclock_set_utc_broken_adjtime

        - name: ensure hwclock doesn't report changed with broken /etc/adjtime
          assert:
            that:
              - not hwclock_set_utc_broken_adjtime.changed
              - hwclock_set_utc_broken_adjtime.diff.after.hwclock == 'UTC'
              - hwclock_set_utc_broken_adjtime.diff.before.hwclock == 'UTC'

        - name: set hwclock to LOCAL with broken /etc/adjtime
          timezone:
            hwclock: local
          register: hwclock_set_local_broken_adjtime

        - name: ensure hwclock changed to LOCAL with broken /etc/adjtime
          assert:
            that:
              - hwclock_set_local_broken_adjtime.changed
              - hwclock_set_local_broken_adjtime.diff.after.hwclock == 'local'
              - hwclock_set_local_broken_adjtime.diff.before.hwclock == 'UTC'
      when:
        - ansible_service_mgr != 'systemd'
        - hwclock_config_file is defined

    ####
    #### timezone + hwclock tests
    ####

    ##
    ## test set timezone and hwclock, idempotency and checkmode
    ##

    - name: set timezone to Etc/UTC and hwclock to local
      timezone:
        name: Etc/UTC
        hwclock: local

    - name: set timezone to Europe/Belgrade and hwclock to UTC (checkmode)
      timezone:
        name: Europe/Belgrade
        hwclock: UTC
      check_mode: true
      register: tzclock_set_checkmode

    - name: ensure timezone and hwclock reported as changed in checkmode
      assert:
        that:
          - tzclock_set_checkmode.changed
          - tzclock_set_checkmode.diff.after.name == 'Europe/Belgrade'
          - tzclock_set_checkmode.diff.before.name == 'Etc/UTC'
          - tzclock_set_checkmode.diff.after.hwclock == 'UTC'
          - tzclock_set_checkmode.diff.before.hwclock == 'local'

    - name: ensure checkmode didn't change the timezone
      command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane
      register: result
      failed_when: result is not failed
      changed_when: false

    - block:
        - name: ensure that checkmode didn't update the timezone in the config file
          command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }}
          when:
            - ansible_os_family == 'RedHat'

        - name: ensure that checkmode didn't update the timezone in the config file
          command: egrep '^Etc/UTC' {{ timezone_config_file }}
          when:
            - ansible_os_family == 'Debian'

        - name: ensure that checkmode didn't update hwclock in /etc/adjtime
          command: grep ^UTC /etc/adjtime
          register: result
          failed_when: result is not failed

        - name: ensure that checkmode didn't update hwclock the config file
          command: grep ^UTC=no {{ hwclock_config_file }}
      when: ansible_service_mgr != 'systemd'

    - name: set timezone to Europe/Belgrade and hwclock to UTC
      timezone:
        name: Europe/Belgrade
        hwclock: UTC
      register: tzclock_set

    - name: ensure timezone and hwclock changed
      assert:
        that:
          - tzclock_set.changed
          - tzclock_set.diff.after.name == 'Europe/Belgrade'
          - tzclock_set.diff.before.name == 'Etc/UTC'
          - tzclock_set.diff.after.hwclock == 'UTC'
          - tzclock_set.diff.before.hwclock == 'local'

    - name: ensure that the timezone is actually set
      command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade
      changed_when: false

    - block:
        - name: ensure that the timezone is updated in the config file
          command: egrep '^(TIME)?ZONE="Europe/Belgrade"' {{ timezone_config_file }}
          when:
            - ansible_os_family == 'RedHat'

        - name: ensure that the timezone is updated in the config file
          command: egrep 'Europe/Belgrade' {{ timezone_config_file }}
          when:
            - ansible_os_family == 'Debian'

        - name: ensure that hwclock is updated in /etc/adjtime
          command: grep ^UTC /etc/adjtime

        - name: ensure that hwclock is updated in the config file
          command: grep ^UTC=yes {{ hwclock_config_file }}
      when: ansible_service_mgr != 'systemd'

    - name: set timezone to Europe/Belgrade and hwclock to UTC again
      timezone:
        name: Europe/Belgrade
        hwclock: UTC
      register: tzclock_set_again

    - name: set timezone and hwclock idempotency
      assert:
        that:
          - not tzclock_set_again.changed

    - name: set timezone to Europe/Belgrade and hwclock to UTC again (checkmode)
      timezone:
        name: Europe/Belgrade
        hwclock: UTC
      register: tzclock_set_again_checkmode

    - name: set timezone and hwclock idempotency in checkmode
      assert:
        that:
          - not tzclock_set_again_checkmode.changed

  when:
    - ansible_system == 'Linux'
    - hwclock_supported
