#!/usr/bin/env python
#
# Check Docker container stats via cAdvisor HTTP API
# ===
#
# Checks CPU or Memory Usage status inside given container
# via cAdvisor API
#
# Copyright 2015 Alexander Bulimov <lazywolf0@gmail.com>
#
# Released under the MIT license, see LICENSE for details.

from __future__ import unicode_literals
from __future__ import division
from __future__ import print_function
import sys
import argparse
import json
import requests


def get_host_data(cadvisor_url, host_name):
    """Get cAdvisor url, hostname, and return host data in JSON"""
    host_raw_data = requests.get(cadvisor_url + "/api/v1.2/docker/" +
                                 host_name,
                                 timeout=10)
    host_data = json.loads(host_raw_data.text)
    host_id = host_data.keys()[0]
    return host_data[host_id]



def get_host_procs(companion_url, host_id, sort_by):
    """Get cAdvisor url, hostname, and return host data in JSON"""
    payload = {'sort': sort_by, 'limit': 5, 'interval': 30}
    ps_response = requests.get(companion_url + "/api/v1.0" + host_id +
                               "/processes",
                               params=payload,
                               timeout=5)
    ps = json.loads(ps_response.text)
    return ps[-1]["processes"]


def get_machine_data(cadvisor_url):
    """Get cAdvisor url and return parent host data in JSON"""
    response = requests.get(cadvisor_url + "/api/v1.2/machine")
    payload = json.loads(response.text)
    return payload


def ok(message):
    print("CheckDockerStats OK: %s" % message)
    sys.exit(0)


def warn(message):
    print("CheckDockerStats WARNING: %s" % message)
    sys.exit(1)


def critical(message):
    print("CheckDockerStats CRITICAL: %s" % message)
    sys.exit(2)


def unknown(message):
    print("CheckDockerStats UNKNOWN: %s" % message)
    sys.exit(3)


def show_procs(procs, mem_limit):
    """Pretty print host procs in ps-like fashion"""
    mem_limit_kb = mem_limit / 1024
    result = ""
    fmt_line = "%(user)5d %(pid)5d %(cpu)5.1f %(mem)5.1f %(vsz)8d \
                %(rss)8d %(state)6s %(command)s\n"
    if procs:
        result = "\n%5s %5s %5s %5s %8s %8s %6s %s\n" % (
            "USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "STAT", "COMMAND")
        for proc in procs:
            result += fmt_line % {
                "user": proc["status"]["RealUid"],
                "pid": proc["stat"]["pid"],
                "cpu": proc["relativecpuusage"],
                "mem": proc["status"]["VmRSS"] * 100 / mem_limit_kb,
                "vsz": proc["status"]["VmSize"],
                "rss": proc["status"]["VmRSS"],
                "state": proc["stat"]["state"],
                "command": proc["cmdline"]}
    return result


def calculate_mem_limit(machine_data, host_data):
    """Calculate real memory limit for container"""
    mem_limit = host_data["spec"]["memory"]["limit"]
    mem_limit_host = machine_data["memory_capacity"]

    if mem_limit > mem_limit_host:
        mem_limit = mem_limit_host
    return mem_limit


def process_cpu_checks(machine_data, host_data,
                       warn_level, crit_level, host_procs):
    """Process CPU checks for data"""
    # Usage % = (Used CPU Time (in nanoseconds) for the interval) /
    #   (interval (in nano secs) * num cores)
    mem_limit = calculate_mem_limit(machine_data, host_data)
    cpu_usage_total_per_min = host_data["stats"][-1]["cpu"]["usage"]["total"] -\
        host_data["stats"][0]["cpu"]["usage"]["total"]
    cpu_num_cores = machine_data["num_cores"]
    cpu_usage_percent = cpu_usage_total_per_min / 60 / 10000000 / cpu_num_cores
    perfdata = ' | cpu_usage=%.2f%%;%d;%d;0;100' % \
        (cpu_usage_percent, warn_level, crit_level)
    message = '%5.2f%% CPU used!' % cpu_usage_percent
    try:
        procs_string = show_procs(host_procs, mem_limit)
    except Exception:
        procs_string = ""

    if cpu_usage_percent > crit_level:
        critical(message + procs_string + perfdata)
    if cpu_usage_percent > warn_level:
        warn(message + procs_string + perfdata)
    ok(message + perfdata)


def process_mem_checks(machine_data, host_data, check_used,
                       warn_level, crit_level, host_procs):
    """Process memory checks for data"""
    mem_limit = calculate_mem_limit(machine_data, host_data)

    mem_used_sum = 0
    for stat in host_data["stats"]:
        mem_used_sum += stat["memory"]["usage"]

    mem_used = mem_used_sum // len(host_data["stats"])
    mem_free_kb = (mem_limit - mem_used) / 1024
    mem_used_kb = mem_used / 1024
    mem_limit_kb = mem_limit / 1024