# Buggy code
def grafana_create_datasource(module, data):
    # define data payload for grafana API
    payload = {
        "orgId": data["org_id"],
        "name": data["name"],
        "type": data["ds_type"],
        "access": data["access"],
        "url": data["url"],
        "database": data["database"],
        "withCredentials": data["with_credentials"],
        "isDefault": data["is_default"],
        "user": data["user"],
        "password": data["password"],
    }

    # define basic auth
    if (
        "basic_auth_user" in data
        and data["basic_auth_user"]
        and "basic_auth_password" in data
        and data["basic_auth_password"]
    ):
        payload["basicAuth"] = True
        payload["basicAuthUser"] = data["basic_auth_user"]
        payload["basicAuthPassword"] = data["basic_auth_password"]
    else:
        payload["basicAuth"] = False

    # define tls auth
    json_data = {}
    if data.get("tls_client_cert") and data.get("tls_client_key"):
        json_data["tlsAuth"] = True
        if data.get("tls_ca_cert"):
            payload["secureJsonData"] = {
                "tlsCACert": data["tls_ca_cert"],
                "tlsClientCert": data["tls_client_cert"],
                "tlsClientKey": data["tls_client_key"],
            }
            json_data["tlsAuthWithCACert"] = True
        else:
            payload["secureJsonData"] = {
                "tlsClientCert": data["tls_client_cert"],
                "tlsClientKey": data["tls_client_key"],
            }
    else:
        json_data["tlsAuth"] = False
        json_data["tlsAuthWithCACert"] = False
        if data.get("tls_ca_cert"):
            payload["secureJsonData"] = {"tlsCACert": data["tls_ca_cert"]}

    if data.get("tls_skip_verify"):
        json_data["tlsSkipVerify"] = True

    # datasource type related parameters
    if data["ds_type"] == "elasticsearch":
        json_data["esVersion"] = data["es_version"]
        json_data["timeField"] = data["time_field"]
        if data.get("interval"):
            json_data["interval"] = data["interval"]
        if data["es_version"] >= 56:
            json_data["maxConcurrentShardRequests"] = data[
                "max_concurrent_shard_requests"
            ]

    if data["ds_type"] == "elasticsearch" or data["ds_type"] == "influxdb":
        if data.get("time_interval"):
            json_data["timeInterval"] = data["time_interval"]

    if data["ds_type"] == "opentsdb":
        json_data["tsdbVersion"] = data["tsdb_version"]
        if data["tsdb_resolution"] == "second":
            json_data["tsdbResolution"] = 1
        else:
            json_data["tsdbResolution"] = 2

    if data["ds_type"] == "postgres":
        json_data["sslmode"] = data["sslmode"]

    if data["ds_type"] == "alexanderzobnin-zabbix-datasource":
        if data.get("trends"):
            json_data["trends"] = True

    payload["jsonData"] = json_data

    # define http header
    headers = {"content-type": "application/json; charset=utf8"}
    if "grafana_api_key" in data and data["grafana_api_key"] is not None:
        headers["Authorization"] = "Bearer %s" % data["grafana_api_key"]
    else:
        auth = base64.b64encode(
            to_bytes(
                "%s:%s" % (data["grafana_user"], data["grafana_password"])
            ).replace("\n", "")
        )
        headers["Authorization"] = "Basic %s" % auth
        grafana_switch_organisation(
            module, data["grafana_url"], data["org_id"], headers
        )

    # test if datasource already exists
    datasource_exists, ds = grafana_datasource_exists(
        module, data["grafana_url"], data["name"], headers=headers
    )

    result = {}
    if datasource_exists is True:
        del ds["typeLogoUrl"]
        if ds["basicAuth"] is False:
            del ds["basicAuthUser"]
            del ds["basicAuthPassword"]
        if "jsonData" in ds:
            if "tlsAuth" in ds["jsonData"] and ds["jsonData"]["tlsAuth"] is False:
                del ds["secureJsonFields"]
            if "tlsAuth" not in ds["jsonData"]:
                del ds["secureJsonFields"]
        payload["id"] = ds["id"]
        if ds == payload:
            # unchanged
            result["name"] = data["name"]
            result["id"] = ds["id"]
            result["msg"] = "Datasource %s unchanged." % data["name"]
            result["changed"] = False
        else:
            # update
            r, info = fetch_url(
                module,
                "%s/api/datasources/%d" % (data["grafana_url"], ds["id"]),
                data=json.dumps(payload),
                headers=headers,
                method="PUT",
            )
            if info["status"] == 200:
                res = json.loads(r.read())
                result["name"] = data["name"]
                result["id"] = ds["id"]
                result["before"] = ds
                result["after"] = payload
                result["msg"] = "Datasource %s updated %s" % (
                    data["name"],
                    res["message"],
                )
                result["changed"] = True
            else:
                raise GrafanaAPIException(
                    "Unable to update the datasource id %d : %s" % (ds["id"], info)
                )
    else:
        # create
        r, info = fetch_url(
            module,
            "%s/api/datasources" % data["grafana_url"],
            data=json.dumps(payload),
            headers=headers,
            method="POST",
        )
        if info["status"] == 200:
            res = json.loads(r.read())
            result["msg"] = "Datasource %s created : %s" % (
                data["name"],
                res["message"],
            )
            result["changed"] = True
            result["name"] = data["name"]
            result["id"] = res["id"]
        else:
            raise GrafanaAPIException(
                "Unable to create the new datasource %s : %s - %s."
                % (data["name"], info["status"], info)
            )

    return result


# Traceback (nTypeError)
TASK [Create MySQL datasource] ***********************************************************************************************************************************************************************
Monday 26 November 2018  18:38:27 +0100 (0:00:00.386)       0:00:01.453 *******
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "/Users/remirey/.ansible/tmp/ansible-tmp-1543253907.275441-99514604312595/AnsiballZ_grafana_datasource.py:17: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses\n  import imp\nTraceback (most recent call last):\n  File \"/Users/remirey/.ansible/tmp/ansible-tmp-1543253907.275441-99514604312595/AnsiballZ_grafana_datasource.py\", line 113, in <module>\n    _ansiballz_main()\n  File \"/Users/remirey/.ansible/tmp/ansible-tmp-1543253907.275441-99514604312595/AnsiballZ_grafana_datasource.py\", line 105, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/Users/remirey/.ansible/tmp/ansible-tmp-1543253907.275441-99514604312595/AnsiballZ_grafana_datasource.py\", line 48, in invoke_module\n    imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/Users/remirey/.virtualenvs/engie/lib/python3.7/imp.py\", line 234, in load_module\n    return load_source(name, filename, file)\n  File \"/Users/remirey/.virtualenvs/engie/lib/python3.7/imp.py\", line 169, in load_source\n    module = _exec(spec, sys.modules[name])\n  File \"<frozen importlib._bootstrap>\", line 630, in _exec\n  File \"<frozen importlib._bootstrap_external>\", line 728, in exec_module\n  File \"<frozen importlib._bootstrap>\", line 219, in _call_with_frames_removed\n  File \"/var/folders/nx/br96pdj13gd30xmz81_fpsw00000gn/T/ansible_grafana_datasource_payload_lo8baeic/__main__.py\", line 541, in <module>\n  File \"/var/folders/nx/br96pdj13gd30xmz81_fpsw00000gn/T/ansible_grafana_datasource_payload_lo8baeic/__main__.py\", line 523, in main\n  File \"/var/folders/nx/br96pdj13gd30xmz81_fpsw00000gn/T/ansible_grafana_datasource_payload_lo8baeic/__main__.py\", line 376, in grafana_create_datasource\nTypeError: a bytes-like object is required, not 'str'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

# Fixed code
def grafana_create_datasource(module, data):
    # define data payload for grafana API
    payload = {
        "orgId": data["org_id"],
        "name": data["name"],
        "type": data["ds_type"],
        "access": data["access"],
        "url": data["url"],
        "database": data["database"],
        "withCredentials": data["with_credentials"],
        "isDefault": data["is_default"],
        "user": data["user"],
        "password": data["password"],
    }

    # define basic auth
    if (
        "basic_auth_user" in data
        and data["basic_auth_user"]
        and "basic_auth_password" in data
        and data["basic_auth_password"]
    ):
        payload["basicAuth"] = True
        payload["basicAuthUser"] = data["basic_auth_user"]
        payload["basicAuthPassword"] = data["basic_auth_password"]
    else:
        payload["basicAuth"] = False

    # define tls auth
    json_data = {}
    if data.get("tls_client_cert") and data.get("tls_client_key"):
        json_data["tlsAuth"] = True
        if data.get("tls_ca_cert"):
            payload["secureJsonData"] = {
                "tlsCACert": data["tls_ca_cert"],
                "tlsClientCert": data["tls_client_cert"],
                "tlsClientKey": data["tls_client_key"],
            }
            json_data["tlsAuthWithCACert"] = True
        else:
            payload["secureJsonData"] = {
                "tlsClientCert": data["tls_client_cert"],
                "tlsClientKey": data["tls_client_key"],
            }
    else:
        json_data["tlsAuth"] = False
        json_data["tlsAuthWithCACert"] = False
        if data.get("tls_ca_cert"):
            payload["secureJsonData"] = {"tlsCACert": data["tls_ca_cert"]}

    if data.get("tls_skip_verify"):
        json_data["tlsSkipVerify"] = True

    # datasource type related parameters
    if data["ds_type"] == "elasticsearch":
        json_data["esVersion"] = data["es_version"]
        json_data["timeField"] = data["time_field"]
        if data.get("interval"):
            json_data["interval"] = data["interval"]
        if data["es_version"] >= 56:
            json_data["maxConcurrentShardRequests"] = data[
                "max_concurrent_shard_requests"
            ]

    if data["ds_type"] == "elasticsearch" or data["ds_type"] == "influxdb":
        if data.get("time_interval"):
            json_data["timeInterval"] = data["time_interval"]

    if data["ds_type"] == "opentsdb":
        json_data["tsdbVersion"] = data["tsdb_version"]
        if data["tsdb_resolution"] == "second":
            json_data["tsdbResolution"] = 1
        else:
            json_data["tsdbResolution"] = 2

    if data["ds_type"] == "postgres":
        json_data["sslmode"] = data["sslmode"]

    if data["ds_type"] == "alexanderzobnin-zabbix-datasource":
        if data.get("trends"):
            json_data["trends"] = True

    payload["jsonData"] = json_data

    # define http header
    headers = {"content-type": "application/json; charset=utf8"}
    if "grafana_api_key" in data and data["grafana_api_key"] is not None:
        headers["Authorization"] = "Bearer %s" % data["grafana_api_key"]
    else:
        headers["Authorization"] = basic_auth_header(
            data["grafana_user"], data["grafana_password"]
        )
        grafana_switch_organisation(
            module, data["grafana_url"], data["org_id"], headers
        )

    # test if datasource already exists
    datasource_exists, ds = grafana_datasource_exists(
        module, data["grafana_url"], data["name"], headers=headers
    )

    result = {}
    if datasource_exists is True:
        del ds["typeLogoUrl"]
        if ds["basicAuth"] is False:
            del ds["basicAuthUser"]
            del ds["basicAuthPassword"]
        if "jsonData" in ds:
            if "tlsAuth" in ds["jsonData"] and ds["jsonData"]["tlsAuth"] is False:
                del ds["secureJsonFields"]
            if "tlsAuth" not in ds["jsonData"]:
                del ds["secureJsonFields"]
        payload["id"] = ds["id"]
        if ds == payload:
            # unchanged
            result["name"] = data["name"]
            result["id"] = ds["id"]
            result["msg"] = "Datasource %s unchanged." % data["name"]
            result["changed"] = False
        else:
            # update
            r, info = fetch_url(
                module,
                "%s/api/datasources/%d" % (data["grafana_url"], ds["id"]),
                data=json.dumps(payload),
                headers=headers,
                method="PUT",
            )
            if info["status"] == 200:
                res = json.loads(r.read())
                result["name"] = data["name"]
                result["id"] = ds["id"]
                result["before"] = ds
                result["after"] = payload
                result["msg"] = "Datasource %s updated %s" % (
                    data["name"],
                    res["message"],
                )
                result["changed"] = True
            else:
                raise GrafanaAPIException(
                    "Unable to update the datasource id %d : %s" % (ds["id"], info)
                )
    else:
        # create
        r, info = fetch_url(
            module,
            "%s/api/datasources" % data["grafana_url"],
            data=json.dumps(payload),
            headers=headers,
            method="POST",
        )
        if info["status"] == 200:
            res = json.loads(r.read())
            result["msg"] = "Datasource %s created : %s" % (
                data["name"],
                res["message"],
            )
            result["changed"] = True
            result["name"] = data["name"]
            result["id"] = res["id"]
        else:
            raise GrafanaAPIException(
                "Unable to create the new datasource %s : %s - %s."
                % (data["name"], info["status"], info)
            )

    return result