class RedisClusterCache(object):
    """
    Class used for accessing the redis cluster used for caching.
    """
    def __init__(self):
        self.client = self._create_client()

    def _create_client(self):
        """
        Function to connect to the redis cluster and init the client.
        """
        server_list = settings.REDIS_SERVER_LIST.replace(' ', '').split(',')

        nodes = []
        for server in server_list:
            if ':' not in server:
                continue
            host, port = server.split(':')
            nodes.append({'host': host, 'port': port})

        return StrictRedisCluster(startup_nodes=nodes, decode_responses=True)

    def get(self, key):
        return self.client.get(key)

    def exists(self, key):
        return self.client.exists(key)

    def set(self, key, value, timeout=DEFAULT_TIMEOUT):
        self.client.set(key, value, timeout) GCM_PLATFORM = 'gcm' class ResponseLog(models.Model):
    """
    Model for logging info about the device response.
    """
    platform = models.CharField(choices=PLATFORM_CHOICES, max_length=10)
    roundtrip_time = models.FloatField()
    available = models.BooleanField()
    date = models.DateTimeField(auto_now_add=True) ACTION_KEY = 'action' APP_VERSION_KEY = 'app_version' CODEC_KEY = 'codec' CONNECTION_TYPE_KEY = 'connection_type' DIRECTION_KEY = 'direction' FAILED_REASON_KEY = 'failed_reason' HANGUP_REASON_KEY = 'hangup_reason' MOS_KEY = 'mos' NETWORK_KEY = 'network' NETWORK_OPERATOR_KEY = 'network_operator' OS_KEY = 'os' OS_VERSION_KEY = 'os_version' VIALER_CALL_FAILURE_TOTAL_KEY = 'vialer_call_failure_total' VIALER_CALL_SUCCESS_TOTAL_KEY = 'vialer_call_success_total' VIALER_HANGUP_REASON_TOTAL_KEY = 'vialer_hangup_reason_total' VIALER_MIDDLEWARE_INCOMING_CALL_FAILED_TOTAL_KEY = 'vialer_middleware_incoming_call_failed_total' VIALER_MIDDLEWARE_INCOMING_CALL_SUCCESS_TOTAL_KEY = 'vialer_middleware_incoming_call_success_total' VIALER_MIDDLEWARE_PUSH_NOTIFICATION_FAILED_TOTAL_KEY = 'vialer_middleware_push_notification_failed_total' VIALER_MIDDLEWARE_PUSH_NOTIFICATION_SUCCESS_TOTAL_KEY = 'vialer_middleware_push_notification_success_total'
import os
import sys
import django
import time
from ast import literal_eval
from random import randint, random
from django.conf import settings
from django.db import connection, DatabaseError
from prometheus_client import Counter, Gauge, start_http_server
from raven.contrib.django.models import client as raven_client
from redis import RedisError
from rediscluster.exceptions import RedisClusterException
from app.cache import RedisClusterCache
from app.models import GCM_PLATFORM, ResponseLog
from main.prometheus.consts import (
    ACTION_KEY,
    APP_VERSION_KEY,
    CODEC_KEY,
    CONNECTION_TYPE_KEY,
    DIRECTION_KEY,
    FAILED_REASON_KEY,
    HANGUP_REASON_KEY,
    MOS_KEY,
    NETWORK_KEY,
    NETWORK_OPERATOR_KEY,
    OS_KEY,
    OS_VERSION_KEY,
    VIALER_CALL_FAILURE_TOTAL_KEY,
    VIALER_CALL_SUCCESS_TOTAL_KEY,
    VIALER_HANGUP_REASON_TOTAL_KEY,
    VIALER_MIDDLEWARE_INCOMING_CALL_FAILED_TOTAL_KEY,
    VIALER_MIDDLEWARE_INCOMING_CALL_SUCCESS_TOTAL_KEY,
    VIALER_MIDDLEWARE_PUSH_NOTIFICATION_FAILED_TOTAL_KEY,
    VIALER_MIDDLEWARE_PUSH_NOTIFICATION_SUCCESS_TOTAL_KEY)
    'The amount of push notifications that were successful processed by the app',
    ['direction', 'os'],
)

VIALER_MIDDLEWARE_INCOMING_CALL_SUCCESS_TOTAL = Counter(
    VIALER_MIDDLEWARE_INCOMING_CALL_SUCCESS_TOTAL_KEY,
    'The amount of times an incoming call was presented at the middleware',
    ['action', 'os'],
)

VIALER_MIDDLEWARE_INCOMING_CALL_FAILED_TOTAL = Counter(
    VIALER_MIDDLEWARE_INCOMING_CALL_FAILED_TOTAL_KEY,
    'The amount of times an incoming call that were presented but couldn\'t be handled',
    ['action', 'failed_reason', 'os'],
)


def ping_redis():
    """
    Write a key value to Redis to see if it is up

    Returns:
        bool: True if we can read and write to Redis
    """
    try:
        result = REDIS_CLUSTER_CLIENT.client.execute_command('PING')
        for key, value in result.items():
            if value is False:
                return False
    except:
        return False
    return True


def write_read_orm():
    """
    Write a ResponseLog object to the database to see if it is up.

    Returns:
         bool: True if we can read and write using the ORM.
    """
    random_roundtrip = randint(1, 1000)
    random_available = random() > 0.5

    try:
        connection.ensure_connection()
    except:
        raven_client.captureException()
        return False

    try:
        response_log = ResponseLog.objects.create(
            platform=GCM_PLATFORM,
            roundtrip_time=random_roundtrip,
            available=random_available,
        )
    except DatabaseError:
        connection.close()

        raven_client.captureException()
        return False
    else:
        if response_log.available == random_available and response_log.roundtrip_time == random_roundtrip:
            response_log.delete()
            return True
        return False


def increment_vialer_call_success_metric_counter():
    """
    Function that increments the vialer_call_success_total counter.
    """
    # Get the length of the list in redis.
    list_length = REDIS_CLUSTER_CLIENT.client.llen(VIALER_CALL_SUCCESS_TOTAL_KEY)

    # Get the values from the list in redis.
    data_list = REDIS_CLUSTER_CLIENT.client.lrange(VIALER_CALL_SUCCESS_TOTAL_KEY, 0, list_length)

    for value_str in data_list:
        # Parse the string to a dict.
        value_dict = literal_eval(value_str)
        VIALER_CALL_SUCCESS_TOTAL.labels(
            app_version=value_dict[APP_VERSION_KEY],
            codec=value_dict[CODEC_KEY],
            connection_type=value_dict[CONNECTION_TYPE_KEY],
            direction=value_dict[DIRECTION_KEY],
            mos=value_dict[MOS_KEY],
            network=value_dict[NETWORK_KEY],
            network_operator=value_dict.get(NETWORK_OPERATOR_KEY, ''),
            os=value_dict[OS_KEY],
            os_version=value_dict[OS_VERSION_KEY],
        ).inc()

    # Trim the list, this means that the values that are outside
    # of the selected range are deleted. In this case we are keeping
    # all of the values we did not yet process in the list.
    REDIS_CLUSTER_CLIENT.client.ltrim(VIALER_CALL_SUCCESS_TOTAL_KEY, list_length, -1)


def increment_vialer_call_failure_metric_counter():
    """
    Function that increments the vialer_call_failure_total counter.
    """
    # Get the length of the list in redis.
    list_length = REDIS_CLUSTER_CLIENT.client.llen(VIALER_CALL_FAILURE_TOTAL_KEY)

    # Get the values from the list in redis.
    data_list = REDIS_CLUSTER_CLIENT.client.lrange(VIALER_CALL_FAILURE_TOTAL_KEY, 0, list_length)

    for value_str in data_list:
        # Parse the string to a dict.
        value_dict = literal_eval(value_str)
        VIALER_CALL_FAILURE_TOTAL.labels(
            app_version=value_dict[APP_VERSION_KEY],
            connection_type=value_dict[CONNECTION_TYPE_KEY],
            direction=value_dict[DIRECTION_KEY],
            failed_reason=value_dict[FAILED_REASON_KEY],
            network=value_dict[NETWORK_KEY],
            network_operator=value_dict.get(NETWORK_OPERATOR_KEY, ''),
            os=value_dict[OS_KEY],

            os_version=value_dict[OS_VERSION_KEY],