Source code for statistics
# -*- encoding: utf-8 -*-
"""
staticdhcpdlib.statistics
=========================
Defines statistics-delegation methods and structures.
Legal
-----
This file is part of staticDHCPd.
staticDHCPd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
(C) Neil Tallim, 2014 <flan@uguu.ca>
"""
import collections
import logging
import threading
import traceback
_logger = logging.getLogger('statistics')
_stats_lock = threading.Lock()
_stats_callbacks = []
Statistics = collections.namedtuple("Statistics", (
'source_address', 'mac', 'ip', 'subnet', 'serial', 'method', 'processing_time', 'processed', 'port',
))
"""
Statistics associated with a DHCP event.
.. py:attribute:: source_address
:noindex:
An :class:`libpydhcpserver.dhcp.Address` containing the IP and port of the
client.
.. py:attribute:: mac
:noindex:
A :class:`libpydhcpserver.dhcp_types.mac.MAC` containing the MAC of the
client; None if the event was not due to a DHCP packet.
.. py:attribute:: ip
:noindex:
An :class:`libpydhcpserver.dhcp_types.ipv4.IPv4` containing the address
assigned to the client, if any.
.. py:attribute:: subnet
:noindex:
The database-subnet associated with this event.
.. py:attribute:: serial
:noindex:
The database-serial associated with this event.
.. py:attribute:: method
:noindex:
The DHCP method of the received packet.
.. py:attribute:: processing_time
:noindex:
The number of seconds required to finish processing the event.
.. py:attribute:: processed
:noindex:
Whether the packet was fully processed (``False`` if non-DHCP or
blacklisted).
.. py:attribute:: port
:noindex:
The port on which the request was received.
"""
def emit(statistics):
"""
Invokes every registered stats handler to deliver new information.
:param :class:`Statistics` statistics: The statistics to report.
"""
with _stats_lock:
for callback in _stats_callbacks:
try:
callback(statistics)
except Exception:
_logger.critical("Unable to deliver statistics:\n" + traceback.format_exc())
def registerStatsCallback(callback):
"""
Registers a statistics callback.
:param callable callback: A callable that takes :data:`Statistics` as its
argument; if already present, it will not be
registered a second time. This function must never
block for any significant amount of time.
"""
with _stats_lock:
if callback in _stats_callbacks:
_logger.error("Callback %(callback)r is already registered" % {'callback': callback,})
else:
_stats_callbacks.append(callback)
_logger.debug("Registered stats-callback %(callback)r" % {'callback': callback,})
def unregisterStatsCallback(callback):
"""
Unregisters a statistics callback.
:param callable callback: The callable to be removed.
:return bool: True if a callback was removed.
"""
with _stats_lock:
try:
_stats_callbacks.remove(callback)
except ValueError:
_logger.error("Callback %(callback)r is not registered" % {'callback': callback,})
return False
else:
_logger.debug("Unregistered stats-callback %(callback)r" % {'callback': callback,})
return True