# Copyright (c) 2011 - 2017, Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""``connpool.py``
`Connection Pool class`
"""
import time
from itertools import chain
from . import loggers
from .custom_exceptions import ConnPoolException
# TODO: add ability to block waiting on a connection to be released
# TODO: check if new connection object isn't link to previous one
[docs]class ConnectionPool(object):
"""Generic connection pool`
"""
class_logger = loggers.ClassLogger()
[docs] def __init__(self, connection_class=None, max_connections=None, time_to_live=30, **connection_kwargs):
"""Initialize ConnectionPool class.
Args:
connection_class(TelnetCMD): Telnet connection class
max_connections(int): Maximum available connections
time_to_live(int): Time to live for each connection
connection_kwargs(dict): Connection arguments
"""
self.connection_class = connection_class
self.connection_kwargs = connection_kwargs
self.max_connections = max_connections or 1
self.time_to_live = time_to_live
self.retry_count = 3
self._available_connections = []
self._in_use_connections = set()
[docs] def get_connection(self):
"""Get a connection from the pool.
Raises:
ConnPoolException: connection is dead, error on connection creation
Returns:
TelnetCMD: Telnet connection
"""
# self.class_logger.info("Try to get connection...")
for i in range(1, self.retry_count + 1):
if len(self._available_connections) > 0:
connection, last_time = self._available_connections.pop()
if (last_time + self.time_to_live) < time.time():
try:
self.class_logger.debug("Time to leave is exceeded. Destroy connection and try to make new one.")
connection.disconnect()
except Exception as err:
self.class_logger.warning("Error occurred while disconnecting connection: %s" % (err, ))
finally:
del connection
# If connection is not created/got on previous step
if "connection" not in locals():
self.class_logger.debug("Connection is not available. Making one...")
try:
connection = self.make_connection()
self.class_logger.debug("...completed.")
except Exception as err:
self.class_logger.debug("...failed: %s." % (err, ))
try:
if "connection" in locals():
if not connection.check_connection():
raise ConnPoolException("Connection is dead.")
self._in_use_connections.add(connection)
self.class_logger.debug("Connection check passed.")
return connection
except Exception as err:
self.class_logger.warning("Error occurred: %s" % (err, ))
try:
self.class_logger.debug("Deleting connection since it's not OK...")
# self.del_connection_in_use(connection)
connection.disconnect()
self.class_logger.debug("...Deletion succeeded.")
except Exception as err:
self.class_logger.warning("Broken connection deletion failed: %s." % (err, ))
sleep_time = i ** 2
self.class_logger.debug("Connection check failed. Sleeping for %s secs before retry." % (sleep_time, ))
time.sleep(sleep_time)
message = "ERROR: Cannot create connection after %s attempts." % (self.retry_count, )
self.class_logger.error(message)
raise ConnPoolException(message)
[docs] def make_connection(self):
"""Create a new connection.
Raises:
ConnPoolException: too many connections
Returns:
TelnetCMD: Telnet connection
"""
if self._created_connections() >= self.max_connections:
raise ConnPoolException("Too many connections.")
connection = self.connection_class(**self.connection_kwargs)
connection.connect()
return connection
[docs] def release(self, connection):
"""Releases the connection back to the pool.
Args:
connection(TelnetCMD): Telnet connection
"""
self._in_use_connections.remove(connection)
self._available_connections.append((connection, time.time()))
[docs] def del_connection_in_use(self, connection):
"""Delete connection in use (in case any error).
Args:
connection(TelnetCMD): Telnet connection
"""
try:
self._in_use_connections.remove(connection)
connection.disconnect()
except Exception as err:
self.class_logger.warning("Error occurred while removing connection: %s" % (err, ))
[docs] def disconnect_all(self):
"""Disconnects all connections in the pool.
"""
all_conns = chain([_x[0] for _x in self._available_connections], self._in_use_connections)
for connection in all_conns:
try:
connection.disconnect()
except Exception as err:
self.class_logger.warning("Error occurred while disconnecting connection: %s" % (err, ))
self._available_connections = []
self._in_use_connections = set()
[docs] def _created_connections(self):
"""Return number of created connections
Returns:
int: Number of created connections
"""
return len(self._available_connections) + len(self._in_use_connections)