#!/usr/bin/python
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function

__metaclass__ = type
DOCUMENTATION = """
---
module: ara_graphite
version_added: "1.0.0"
author: Red Hat (@RedHatOfficial)
short_description: Send ARA stats to graphite
description: >
    Python ansible module to send ARA stats to graphite
options:
  graphite_host:
    description: >
      The hostname of the Graphite server with optional port:
      graphite.example.com:2004. The default port is 2003
    required: True
    type: str
  graphite_prefix:
    description:
      - TBD
    type: str
  graphite_port:
    description:
      - TBD
    default: 2003
    type: int
  ara_mapping:
    description: >
      Mapping task names to Graphite paths
    required: True
    type: dict
  ara_data:
    description: >
      List of ARA results: ara result list --all -f json
    required: True
    type: str
  only_successful_tasks:
    description: >
      Whether to send only successful tasks, ignoring skipped and failed,
      by default True.
    required: False
    default: True
    type: bool
"""

EXAMPLES = """
- name: Get ARA json data
  shell: "{{ local_working_dir }}/bin/ara task list --all -f json"
  register: ara_data

- ara_graphite:
    graphite_host: 10.2.2.2
    ara_data: "{{ ara_task_output.stdout }}"
    ara_mapping:
        - "Name of task that deploys overcloud": overcloud.deploy.seconds
"""

import ast  # noqa: E402
import datetime  # noqa: E402
import socket  # noqa: E402


def stamp(x):
    """Convert ISO timestamp to Unix timestamp

    :param x: string with timestamp
    :return: string with Unix timestamp
    """
    return datetime.datetime.strptime(x, "%Y-%m-%d %H:%M:%S").strftime("%s")


def task_length(x):
    """Calculate task length in seconds from "%H:%M:%S" format

    :param x: datetime string
    :return: number of seconds spent for task
    """
    t = datetime.datetime.strptime(x, "%H:%M:%S")
    return datetime.timedelta(
        hours=t.hour, minutes=t.minute, seconds=t.second
    ).total_seconds()


def translate(mapping, json_data, only_ok):
    """Create data to send to Graphite server in format:

        GraphitePath Timestamp TaskDuration
    GraphitePath is taken from mapping dictionary according to task name.

    :param mapping: dictionary of mapping task names to graphite paths
    :param json_data: JSON data with tasks and times
    :return: list of graphite data
    """
    items = []
    data = ast.literal_eval(json_data)
    for task in data:
        if not only_ok or (only_ok and task["Status"] in ["changed", "ok"]):
            if task["Name"] in mapping:
                timestamp, duration = stamp(task["Time Start"]), task_length(
                    task["Duration"]
                )
                items.append([mapping[task["Name"]], duration, timestamp])
    return items


def send(data, gr_host, gr_port, prefix):
    """Actual sending of data to Graphite server via network

    :param data: list of items to send to Graphite
    :param gr_host: Graphite host (with optional port)
    :param prefix: prefix to append before Graphite path
    :return: True if sent successfully, otherwise False
    """
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(3.0)
    try:
        s.connect((gr_host, gr_port))
    except Exception as exc:
        return False, str(exc)
    for content in data:
        s.send(prefix + " ".join([str(i) for i in content]) + "\n")
    s.close()
    return True, ""


def send_stats(gr_host, gr_port, mapping, json_data, prefix, only_ok):
    """Send ARA statistics to Graphite server

    :param gr_host: Graphite host (with optional port)
    :param mapping: dictionary of mapping task names to graphite paths
    :param json_data: JSON data with tasks and times
    :param prefix: prefix to append before Graphite path
    :return: JSON ansible result
    """
    data2send = translate(mapping, json_data, only_ok)
    response, reason = send(data2send, gr_host, gr_port, prefix)
    if not response:
        return {
            "changed": False,
            "failed": True,
            "graphite_host": gr_host,
            "msg": "Can't connect to Graphite: %s" % reason,
        }
    return {
        "changed": True,
        "graphite_host": gr_host,
        "sent_data": data2send,
    }


def main():
    from ansible.module_utils.basic import AnsibleModule

    module = AnsibleModule(
        argument_spec=dict(
            graphite_host=dict(required=True, type="str"),
            graphite_port=dict(required=False, type="int", default=2003),
            ara_mapping=dict(required=True, type="dict"),
            ara_data=dict(required=True, type="str"),
            graphite_prefix=dict(required=False, type="str", default=""),
            only_successful_tasks=dict(required=False, type="bool", default=True),
        )
    )
    result = send_stats(
        module.params["graphite_host"],
        module.params["graphite_port"],
        module.params["ara_mapping"],
        module.params["ara_data"],
        module.params["graphite_prefix"],
        module.params["only_successful_tasks"],
    )
    module.exit_json(**result)


if __name__ == "__main__":
    main()
