1""" 2""" 3 4# Created on 2014.03.15 5# 6# Author: Giovanni Cannata 7# 8# Copyright 2013 - 2020 Giovanni Cannata 9# 10# This file is part of ldap3. 11# 12# ldap3 is free software: you can redistribute it and/or modify 13# it under the terms of the GNU Lesser General Public License as published 14# by the Free Software Foundation, either version 3 of the License, or 15# (at your option) any later version. 16# 17# ldap3 is distributed in the hope that it will be useful, 18# but WITHOUT ANY WARRANTY; without even the implied warranty of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20# GNU Lesser General Public License for more details. 21# 22# You should have received a copy of the GNU Lesser General Public License 23# along with ldap3 in the COPYING and COPYING.LESSER files. 24# If not, see <http://www.gnu.org/licenses/>. 25 26from datetime import datetime, timedelta 27from os import linesep 28 29from .exceptions import LDAPMetricsError 30from ..utils.log import log, log_enabled, ERROR, BASIC 31 32 33class ConnectionUsage(object): 34 """ 35 Collect statistics on connection usage 36 """ 37 38 def reset(self): 39 self.open_sockets = 0 40 self.closed_sockets = 0 41 self.wrapped_sockets = 0 42 self.bytes_transmitted = 0 43 self.bytes_received = 0 44 self.messages_transmitted = 0 45 self.messages_received = 0 46 self.operations = 0 47 self.abandon_operations = 0 48 self.add_operations = 0 49 self.bind_operations = 0 50 self.compare_operations = 0 51 self.delete_operations = 0 52 self.extended_operations = 0 53 self.modify_operations = 0 54 self.modify_dn_operations = 0 55 self.search_operations = 0 56 self.unbind_operations = 0 57 self.referrals_received = 0 58 self.referrals_followed = 0 59 self.referrals_connections = 0 60 self.restartable_failures = 0 61 self.restartable_successes = 0 62 self.servers_from_pool = 0 63 if log_enabled(BASIC): 64 log(BASIC, 'reset usage metrics') 65 66 def __init__(self): 67 self.initial_connection_start_time = None 68 self.open_socket_start_time = None 69 self.connection_stop_time = None 70 self.last_transmitted_time = None 71 self.last_received_time = None 72 self.open_sockets = 0 73 self.closed_sockets = 0 74 self.wrapped_sockets = 0 75 self.bytes_transmitted = 0 76 self.bytes_received = 0 77 self.messages_transmitted = 0 78 self.messages_received = 0 79 self.operations = 0 80 self.abandon_operations = 0 81 self.add_operations = 0 82 self.bind_operations = 0 83 self.compare_operations = 0 84 self.delete_operations = 0 85 self.extended_operations = 0 86 self.modify_operations = 0 87 self.modify_dn_operations = 0 88 self.search_operations = 0 89 self.unbind_operations = 0 90 self.referrals_received = 0 91 self.referrals_followed = 0 92 self.referrals_connections = 0 93 self.restartable_failures = 0 94 self.restartable_successes = 0 95 self.servers_from_pool = 0 96 97 if log_enabled(BASIC): 98 log(BASIC, 'instantiated Usage object') 99 100 def __repr__(self): 101 r = 'Connection Usage:' + linesep 102 r += ' Time: [elapsed: ' + str(self.elapsed_time) + ']' + linesep 103 r += ' Initial start time: ' + (str(self.initial_connection_start_time.isoformat()) if self.initial_connection_start_time else '') + linesep 104 r += ' Open socket time: ' + (str(self.open_socket_start_time.isoformat()) if self.open_socket_start_time else '') + linesep 105 r += ' Last transmitted time: ' + (str(self.last_transmitted_time.isoformat()) if self.last_transmitted_time else '') + linesep 106 r += ' Last received time: ' + (str(self.last_received_time.isoformat()) if self.last_received_time else '') + linesep 107 r += ' Close socket time: ' + (str(self.connection_stop_time.isoformat()) if self.connection_stop_time else '') + linesep 108 r += ' Server:' + linesep 109 r += ' Servers from pool: ' + str(self.servers_from_pool) + linesep 110 r += ' Sockets open: ' + str(self.open_sockets) + linesep 111 r += ' Sockets closed: ' + str(self.closed_sockets) + linesep 112 r += ' Sockets wrapped: ' + str(self.wrapped_sockets) + linesep 113 r += ' Bytes: ' + str(self.bytes_transmitted + self.bytes_received) + linesep 114 r += ' Transmitted: ' + str(self.bytes_transmitted) + linesep 115 r += ' Received: ' + str(self.bytes_received) + linesep 116 r += ' Messages: ' + str(self.messages_transmitted + self.messages_received) + linesep 117 r += ' Transmitted: ' + str(self.messages_transmitted) + linesep 118 r += ' Received: ' + str(self.messages_received) + linesep 119 r += ' Operations: ' + str(self.operations) + linesep 120 r += ' Abandon: ' + str(self.abandon_operations) + linesep 121 r += ' Bind: ' + str(self.bind_operations) + linesep 122 r += ' Add: ' + str(self.add_operations) + linesep 123 r += ' Compare: ' + str(self.compare_operations) + linesep 124 r += ' Delete: ' + str(self.delete_operations) + linesep 125 r += ' Extended: ' + str(self.extended_operations) + linesep 126 r += ' Modify: ' + str(self.modify_operations) + linesep 127 r += ' ModifyDn: ' + str(self.modify_dn_operations) + linesep 128 r += ' Search: ' + str(self.search_operations) + linesep 129 r += ' Unbind: ' + str(self.unbind_operations) + linesep 130 r += ' Referrals: ' + linesep 131 r += ' Received: ' + str(self.referrals_received) + linesep 132 r += ' Followed: ' + str(self.referrals_followed) + linesep 133 r += ' Connections: ' + str(self.referrals_connections) + linesep 134 r += ' Restartable tries: ' + str(self.restartable_failures + self.restartable_successes) + linesep 135 r += ' Failed restarts: ' + str(self.restartable_failures) + linesep 136 r += ' Successful restarts: ' + str(self.restartable_successes) + linesep 137 return r 138 139 def __str__(self): 140 return self.__repr__() 141 142 def __iadd__(self, other): 143 if not isinstance(other, ConnectionUsage): 144 raise LDAPMetricsError('unable to add to ConnectionUsage') 145 146 self.open_sockets += other.open_sockets 147 self.closed_sockets += other.closed_sockets 148 self.wrapped_sockets += other.wrapped_sockets 149 self.bytes_transmitted += other.bytes_transmitted 150 self.bytes_received += other.bytes_received 151 self.messages_transmitted += other.messages_transmitted 152 self.messages_received += other.messages_received 153 self.operations += other.operations 154 self.abandon_operations += other.abandon_operations 155 self.add_operations += other.add_operations 156 self.bind_operations += other.bind_operations 157 self.compare_operations += other.compare_operations 158 self.delete_operations += other.delete_operations 159 self.extended_operations += other.extended_operations 160 self.modify_operations += other.modify_operations 161 self.modify_dn_operations += other.modify_dn_operations 162 self.search_operations += other.search_operations 163 self.unbind_operations += other.unbind_operations 164 self.referrals_received += other.referrals_received 165 self.referrals_followed += other.referrals_followed 166 self.referrals_connections += other.referrals_connections 167 self.restartable_failures += other.restartable_failures 168 self.restartable_successes += other.restartable_successes 169 self.servers_from_pool += other.servers_from_pool 170 return self 171 172 def update_transmitted_message(self, message, length): 173 self.last_transmitted_time = datetime.now() 174 self.bytes_transmitted += length 175 self.operations += 1 176 self.messages_transmitted += 1 177 if message['type'] == 'abandonRequest': 178 self.abandon_operations += 1 179 elif message['type'] == 'addRequest': 180 self.add_operations += 1 181 elif message['type'] == 'bindRequest': 182 self.bind_operations += 1 183 elif message['type'] == 'compareRequest': 184 self.compare_operations += 1 185 elif message['type'] == 'delRequest': 186 self.delete_operations += 1 187 elif message['type'] == 'extendedReq': 188 self.extended_operations += 1 189 elif message['type'] == 'modifyRequest': 190 self.modify_operations += 1 191 elif message['type'] == 'modDNRequest': 192 self.modify_dn_operations += 1 193 elif message['type'] == 'searchRequest': 194 self.search_operations += 1 195 elif message['type'] == 'unbindRequest': 196 self.unbind_operations += 1 197 else: 198 if log_enabled(ERROR): 199 log(ERROR, 'unable to collect usage for unknown message type <%s>', message['type']) 200 raise LDAPMetricsError('unable to collect usage for unknown message type') 201 202 def update_received_message(self, length): 203 self.last_received_time = datetime.now() 204 self.bytes_received += length 205 self.messages_received += 1 206 207 def start(self, reset=True): 208 if reset: 209 self.reset() 210 self.open_socket_start_time = datetime.now() 211 self.connection_stop_time = None 212 if not self.initial_connection_start_time: 213 self.initial_connection_start_time = self.open_socket_start_time 214 215 if log_enabled(BASIC): 216 log(BASIC, 'start collecting usage metrics') 217 218 def stop(self): 219 if self.open_socket_start_time: 220 self.connection_stop_time = datetime.now() 221 if log_enabled(BASIC): 222 log(BASIC, 'stop collecting usage metrics') 223 224 @property 225 def elapsed_time(self): 226 if self.connection_stop_time: 227 return self.connection_stop_time - self.open_socket_start_time 228 else: 229 return (datetime.now() - self.open_socket_start_time) if self.open_socket_start_time else timedelta(0) 230