Search code examples
ansiblejinja2

How can I convert a decimal string to an hexadecimal string?


I have a playbook that queries a server for it's SystemID which can be converted to a model number using a vendor-provided table that maps the id to a model. The server returns a decimal value but the table uses the hexadecimal equivalent.

What I want to do is to convert the decimal string to an hexadecimal string that can be matched with an entry in the vendor-provided table.

Example:

Server returns: SystemID = 1792
Matching entry in vendor table: 0x0700

I've searched in the Ansible documentation and Web searched for either a native Ansible command or jinja2 expression to do the conversion.

I've only found the int(value, base=x) jinja2 function that does the opposite of what I am trying to do.

The native python hex() command can do it. But I'd like to avoid that if possible.

Here is the playbook task that parses the servers stdout to get systemid value:

 set_fact:
    server_model: "{{ ( server_model_results.stdout_lines | select('match','SystemID' ) | list| first ).split('=')[1] | regex_replace('^\ |\ /$', '' )  }}"

Environment: Ansible 2.9.7 Python 3.8.0 macOS 10.15.4


Solution

  • With a recent version of ansible running under python 3, you can use the .format() function:

    $ ansible localhost -m debug -a msg="{{ '{:#x}'.format(1792) }}"
    localhost | SUCCESS => {
        "msg": "0x700"
    }
    

    Meanwhile, you can still use an older python format with the % operator inside a jinja2 template string. This will also work with older ansible versions:

    $ ansible localhost -m debug -a msg="{{ '%#x' % 1792 }}"
    localhost | SUCCESS => {
        "msg": "0x700"
    }
    

    You will probably still have to deal with the leading 0 that is present in your file (i.e. 0x0700).

    If all your values are padded to 4 hexa digits in your file (i.e. after the 0x prefix) you can use the following (note that the padding of a length of 6 takes the prefix into account):

    $ ansible localhost -m debug -a msg="{{ '{:#06x}'.format(1792) }}"
    localhost | SUCCESS => {
        "msg": "0x0700"
    }
    $ ansible localhost -m debug -a msg="{{ '%#06x' % 1792 }}"
    localhost | SUCCESS => {
        "msg": "0x0700"
    }
    

    If not, you will have to implement some kind of dynamic 0 padding to the next odd number of chars yourself.

    You might want to switch the 'x' type specifier to 'X' (see doc links above) if hexa digits above nine are uppercase in your vendor table.

    $ ansible localhost -m debug -a msg="{{ '{:#06x}'.format(2569) }}"
    localhost | SUCCESS => {
        "msg": "0x0a09"
    }
    $ ansible localhost -m debug -a msg="{{ '%#06x' % 2569 }}"
    localhost | SUCCESS => {
        "msg": "0x0a09"
    }
    
    $ ansible localhost -m debug -a msg="{{ '{:#06X}'.format(2569) }}"
    localhost | SUCCESS => {
        "msg": "0X0A09"
    }
    $ ansible localhost -m debug -a msg="{{ '%#06X' % 2569 }}"
    localhost | SUCCESS => {
        "msg": "0x0A09"
    }
    

    Meanwhile, if you want to have the uppercase letters in the hexa digit and keep the 0x prefix lowercase, you'll need to adapt a bit, change the format to drop the prefix and hardcode it yourself (note that padding drops to 4 since the prefix is gone):

    $ ansible localhost -m debug -a msg="{{ '0x{:04X}'.format(2569) }}"
    localhost | SUCCESS => {
        "msg": "0x0A09"
    }
    $ ansible localhost -m debug -a msg="{{ '0x%04X' % 2569 }}"
    localhost | SUCCESS => {
        "msg": "0x0A09"
    }
    

    See this other good reference I used for my latest edit.