# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

"""
Filed descriptor load inspector service
"""

__maintainer__ = "Florian Boucault <florian@fluendo.com>"

import os, tempfile

from elisa.core.utils import resources
from elisa.base_components import service_provider

from twisted.internet import reactor

class FileDescriptorMonitor(service_provider.ServiceProvider):
    """
    Service checking periodically the number of currently opened file
    descriptors and giving a warning if a certain limit is reached.
    """

    default_config = {'max_fd': '70',
                      'frequency': '1.0'}
    config_doc = {'max_fd': 'Maximum number of file descriptors opened at any'
                  ' time.',
                  'frequency': 'Frequency of the monitoring in times per'
                  ' second'}

    def initialize(self):
        service_provider.ServiceProvider.initialize(self)
        self._delayed = None

    def start(self):
        self._max_fd = int(self.config.get('max_fd'))
        self._frequency = float(self.config.get('frequency'))
        self.info("Checking %s times per second that no more than %s file \
                   descriptors are opened", self._frequency, self._max_fd)

        self._delayed = reactor.callLater(1.0/self._frequency, self._check)

    def stop(self):
        if self._delayed != None and self._delayed.active():
            reactor.cancelCallLater(self._delayed)

    def _check(self):
        fds = resources.file_descriptors()
        if len(fds) > self._max_fd:
            set_fds = set(fds)
            count_fds = []
            fd_log, path = tempfile.mkstemp(prefix="elisa_",
                                            suffix="_opened_fd")

            # if a file descriptor is opened more than 3 times, display it
            for fd in set_fds:
                n = fds.count(fd)
                count_fds.append((fd, n))
                if n > 3:
                    self.warning("%s opened %s times" % (fd, n))

            os.write(fd_log, "Opened file descriptors sorted by frequency\n")
            os.write(fd_log, "===========================================\n\n")

            # sort from the most opened fd to the least one
            count_fds.sort(key=lambda e: e[1], reverse=True)

            for fd, n in count_fds:
                os.write(fd_log, "%s %s\n" % (n, fd))

            self.warning("Too many file descriptors opened; complete list at "
                         "%s", path)
            os.close(fd_log)

        self._delayed = reactor.callLater(1.0/self._frequency, self._check)
