Skip to content
Snippets Groups Projects
source_file.py 4.25 KiB
Newer Older
#!/usr/bin/python

# Copyright: (c) 2019, Nicolas Karolak <nicolas.karolak@ubicast.eu>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

ANSIBLE_METADATA = {
    "metadata_version": "1.1",
    "status": ["preview"],
    "supported_by": "community",
}


DOCUMENTATION = """
module: source_file
short_description: Source remote bash/dotenv file
description:
    - This module is used to register host variables from a remote bash/dotenv-like file.
    - It handles boolean value (`MY_VAR=1`) and has a basic handling of list (`MY_VAR=one,two,three`) and dictionnary (`MY_VAR=a=1;b=2;c=3`).
version_added: "2.8"
author: "Nicolas Karolak (@nikaro)"
options:
    path:
        description:
            - Path to the file to source.
        required: true
        type: path
    prefix:
        description:
            - Prefix to add to the registred variable name.
        required: false
        default: ""
        type: str
    lower:
        description:
            - Wether to lower or not the variable name.
        required: false
        default: false
        type: bool
notes:
    - The `check_mode` is supported.
"""

EXAMPLES = """
- name: source envsetup file
  source_file:
    prefix: envsetup_
    path: /root/envsetup/conf.sh
    lower: true
"""

RETURN = """
ansible_facts:
    description: Registred vairales.
    returned: on success
    type: dict
    sample:
        key: value
"""

import os
import re

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.parsing.convert_bool import BOOLEANS, boolean
from ansible.module_utils.six import string_types


def run_module():
    module_args = {
        "path": {"type": "path", "required": True},
        "prefix": {"type": "str", "required": False, "default": ""},
        "lower": {"type": "bool", "required": False, "default": False},
    }

    result = {"changed": False}

    module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

    path = module.params["path"]
    prefix = module.params["prefix"]
    lower = boolean(module.params["lower"])
    variables = {}
    regex_valid_name = re.compile(r"^[a-zA-Z][a-zA-Z0-9_-]*$")
    regex_key_value = re.compile(
        r"^(?!#)(?P<key>[a-zA-Z][a-zA-Z0-9_-]*)=(?:[\'\"])?(?P<value>(?:[^\'\"\n])*)(?:[\'\"])?$",
        re.MULTILINE
    )

    if not os.path.isfile(path):
        module.fail_json(msg="'%s' does not exist or is not a file" % path, **result)

    if prefix and not regex_valid_name.match(prefix):
        module.fail_json(
            msg="'%s' is not a valid prefix it must starts with a letter or underscore"
            " character, and contains only letters, numbers and underscores" % prefix,
            **result
        )

    with open(path) as path_fh:
        # load file content and get all "key=value"
        content = path_fh.read()
        content_match = regex_key_value.findall(content)

        for key, value in content_match:
            # merge prefix + key
            if prefix:
                key = "%s%s" % (prefix, key)

            # lower key
            if lower:
                key = key.lower()

            # check key validity
            if not regex_valid_name.match(key):
                module.fail_json(
                    msg="'%s' is not a valid variable name it must starts with a letter or "
                    "underscore character, and contains only letters, numbers and underscores"
                    % key,
                    **result
                )

            # handle list value
            if "," in value:
                value = re.split("[,\n]", value)

            # handle dict value
            if ";" in value and "=" in value:
                value = {i.split("=")[0]: i.split("=")[1] for i in value.split(";")}

            # handle bool value
            if isinstance(value, string_types) and value.lower() in BOOLEANS:
                value = boolean(value)

            # build variables dict
            variables[key] = value

            result["changed"] = True

            if not module.check_mode:
                result["ansible_facts"] = variables

    module.exit_json(**result)


def main():
    run_module()


if __name__ == "__main__":
    main()