Search code examples
ansibleyamljinja2

Calculate number of days between two variable (one a set variable, the other from fact)


I've been looking around like crazy to find a solution to this but so far I've been unsuccessful : Calculate number of days between an inventory variable and an Ansible fact.

I have no issue when I manually set both variable, both are recognized as a date and I can work my may around from there like so : (this is the expected output)

Task working between 2 manually set variables:

---
- hosts: localhost

  vars:
    prev_date: 2020-12-01
    cur_date: 2020-12-31

  tasks:
    - name: prev_date debug
      debug:
        msg:
          - "prev_date: {{ prev_date }}"
          - "prev_date type: {{ prev_date | type_debug }}"

    - name: cur_date debug
      debug:
        msg:
          - "cur_date: {{ cur_date }}"
          - "cur_date type: {{ cur_date | type_debug }}"

    # This is the output I want to get but I want cur_date from a dynamic source (fact or shell through register)
    - name: Calculate number of days between prev_date and cur_date (intended result)
      debug:
        msg: "Number of days = {{ (cur_date - prev_date).days }}"

Working output:

TASK [prev_date debug] *************************************************************************************************************************************************
Thursday 31 December 2020  12:55:28 +0100 (0:00:00.227)       0:00:00.227 *****
ok: [localhost] => {
    "msg": [
        "prev_date: 2020-12-01",
        "prev_date type: date"
    ]
}

TASK [cur_date debug] **************************************************************************************************************************************************
Thursday 31 December 2020  12:55:29 +0100 (0:00:00.249)       0:00:00.476 *****
ok: [localhost] => {
    "msg": [
        "cur_date: 2020-12-31",
        "cur_date type: date"
    ]
}

TASK [Calculate number of days between prev_date and cur_date (intended result)] ***************************************************************************************
Thursday 31 December 2020  12:55:29 +0100 (0:00:00.144)       0:00:00.621 *****
ok: [localhost] => {
    "msg": "Number of days = 30"
}

Now when I want to use ansible fact to get the current date dynamically on each run (ansible_date_time.date) because of the variables type

Sample of tasks to reproduce the error

    - name: Value and type of ansible_date_time.date
      debug:
        msg:
          - "ansible_date_time.date: {{ ansible_date_time.date }}"
          - "ansible_date_time.date type: {{ ansible_date_time.date | type_debug }}"

    # Failing due to conditional not working with attribute not being date on ansible_date_time.date
    - name: Calculate number of days between ansible_date_time.date
      debug:
        msg:
          - "Number of days = {{ (ansible_date_time.date - prev_date).days }}"

I get the following output and error:

TASK [New source ansible fact date] ************************************************************************************************************************************
Thursday 31 December 2020  13:06:41 +0100 (0:00:00.143)       0:00:00.741 *****
ok: [localhost] => {
    "msg": [
        "ansible_date_time.date: 2020-12-31",
        "ansible_date_time.date type: AnsibleUnsafeText"
    ]
}

TASK [Calculate number of days between ansible_date_time.date] *********************************************************************************************************
Thursday 31 December 2020  13:06:41 +0100 (0:00:00.150)       0:00:00.892 *****
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on (Number of days = {{ (ansible_date_time.date - prev_date).days }}): unsupported operand type(s) for -: 'AnsibleUnsafeText' and 'datetime.date'"}

I tried find ways to create a new variable out of ansible_date_time.date but I was unable to convert it to datetime.date if that's even possible.


Solution

  • ansible_date_time.date is a string, so you will have to convert it to a date or a datetime.

    Surprisingly enough, Ansible does not have any means to convert a string to a date right away, but you can convert it to a datetime using the to_datetime filter.

    Then, because prev_date contains a date and not a datetime, you can convert the datetime you just created to a date via the date() function of Python's datetime.

    So the playbook would be:

    - hosts: all
      gather_subset:
        - min
          
      tasks:
        - debug:
            msg: >-
              {{ 
                (
                  (ansible_date_time.date | to_datetime('%Y-%m-%d')).date() 
                  - prev_date
                ).days 
              }}
          vars:
            prev_date: 2020-12-01
    

    Which will give:

    ok: [localhost] => 
      msg: '30'