1) Fix network disconnect issue while establishing the connection via SSH Tunnel and it impossible to expand the Servers node. Fixes #4724.

2) Fix server connection drops out issue in query tool. Fixes #4818
3) Fix VPN network disconnect issue where pgAdmin4 hangs on expanding the Servers node. Fixes #4926.
4) Ensure that the Servers collection node should expand independently of server connections. Fixes #4933.

Set the default connection timeout to 10 seconds instead of 0.
This commit is contained in:
Aditya Toshniwal
2019-11-26 09:04:41 +05:30
committed by Akshay Joshi
parent 700e01708b
commit 4ed2d74d9c
15 changed files with 272 additions and 135 deletions

View File

@@ -14,19 +14,21 @@ object.
"""
import datetime
from flask import session, request
from flask import session
from flask_login import current_user
from flask_babelex import gettext
import psycopg2
from psycopg2.extensions import adapt
from threading import Lock
import config
from pgadmin.model import Server, User
from pgadmin.model import Server
from .keywords import ScanKeyword
from ..abstract import BaseDriver
from .connection import Connection
from .server_manager import ServerManager
connection_restore_lock = Lock()
class Driver(BaseDriver):
"""
@@ -80,21 +82,30 @@ class Driver(BaseDriver):
return None
if session.sid not in self.managers:
self.managers[session.sid] = managers = dict()
if '__pgsql_server_managers' in session:
session_managers = session['__pgsql_server_managers'].copy()
with connection_restore_lock:
# The wait is over but the object might have been loaded
# by some other thread check again
if session.sid not in self.managers:
self.managers[session.sid] = managers = dict()
if '__pgsql_server_managers' in session:
session_managers =\
session['__pgsql_server_managers'].copy()
for server in \
Server.query.filter_by(
user_id=current_user.id):
manager = managers[str(server.id)] =\
ServerManager(server)
if server.id in session_managers:
manager._restore(session_managers[server.id])
manager.update_session()
for server in Server.query.filter_by(user_id=current_user.id):
manager = managers[str(server.id)] = ServerManager(server)
if server.id in session_managers:
manager._restore(session_managers[server.id])
manager.update_session()
else:
managers = self.managers[session.sid]
if str(sid) in managers:
manager = managers[str(sid)]
manager._restore_connections()
manager.update_session()
with connection_restore_lock:
manager._restore_connections()
manager.update_session()
managers['pinged'] = datetime.datetime.now()
if str(sid) not in managers:

View File

@@ -25,13 +25,10 @@ from pgadmin.model import Server, User
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
CryptKeyMissing
from pgadmin.utils.master_password import get_crypt_key
from threading import Lock
if config.SUPPORT_SSH_TUNNEL:
from sshtunnel import SSHTunnelForwarder, BaseSSHTunnelForwarderError
connection_restore_lock = Lock()
class ServerManager(object):
"""
@@ -292,80 +289,79 @@ WHERE db.oid = {0}""".format(did))
connections = data['connections']
with connection_restore_lock:
for conn_id in connections:
conn_info = connections[conn_id]
if conn_info['conn_id'] in self.connections:
conn = self.connections[conn_info['conn_id']]
else:
conn = self.connections[conn_info['conn_id']] = Connection(
self, conn_info['conn_id'], conn_info['database'],
conn_info['auto_reconnect'], conn_info['async_'],
use_binary_placeholder=conn_info[
'use_binary_placeholder'],
array_to_string=conn_info['array_to_string']
for conn_id in connections:
conn_info = connections[conn_id]
if conn_info['conn_id'] in self.connections:
conn = self.connections[conn_info['conn_id']]
else:
conn = self.connections[conn_info['conn_id']] = Connection(
self, conn_info['conn_id'], conn_info['database'],
conn_info['auto_reconnect'], conn_info['async_'],
use_binary_placeholder=conn_info[
'use_binary_placeholder'],
array_to_string=conn_info['array_to_string']
)
# only try to reconnect if connection was connected previously
# and auto_reconnect is true.
if conn_info['wasConnected'] and conn_info['auto_reconnect']:
try:
# Check SSH Tunnel needs to be created
if self.use_ssh_tunnel == 1 and \
not self.tunnel_created:
status, error = self.create_ssh_tunnel(
data['tunnel_password'])
# Check SSH Tunnel is alive or not.
self.check_ssh_tunnel_alive()
conn.connect(
password=data['password'],
server_types=ServerType.types()
)
# only try to reconnect if connection was connected previously
# and auto_reconnect is true.
if conn_info['wasConnected'] and conn_info['auto_reconnect']:
try:
# Check SSH Tunnel needs to be created
if self.use_ssh_tunnel == 1 and \
not self.tunnel_created:
status, error = self.create_ssh_tunnel(
data['tunnel_password'])
# Check SSH Tunnel is alive or not.
self.check_ssh_tunnel_alive()
conn.connect(
password=data['password'],
server_types=ServerType.types()
)
# This will also update wasConnected flag in
# connection so no need to update the flag manually.
except CryptKeyMissing:
# maintain the status as this will help to restore once
# the key is available
conn.wasConnected = conn_info['wasConnected']
conn.auto_reconnect = conn_info['auto_reconnect']
except Exception as e:
current_app.logger.exception(e)
self.connections.pop(conn_info['conn_id'])
raise
# This will also update wasConnected flag in
# connection so no need to update the flag manually.
except CryptKeyMissing:
# maintain the status as this will help to restore once
# the key is available
conn.wasConnected = conn_info['wasConnected']
conn.auto_reconnect = conn_info['auto_reconnect']
except Exception as e:
current_app.logger.exception(e)
self.connections.pop(conn_info['conn_id'])
raise
def _restore_connections(self):
with connection_restore_lock:
for conn_id in self.connections:
conn = self.connections[conn_id]
# only try to reconnect if connection was connected previously
# and auto_reconnect is true.
wasConnected = conn.wasConnected
auto_reconnect = conn.auto_reconnect
if conn.wasConnected and conn.auto_reconnect:
try:
# Check SSH Tunnel needs to be created
if self.use_ssh_tunnel == 1 and \
not self.tunnel_created:
status, error = self.create_ssh_tunnel(
self.tunnel_password
)
for conn_id in self.connections:
conn = self.connections[conn_id]
# only try to reconnect if connection was connected previously
# and auto_reconnect is true.
wasConnected = conn.wasConnected
auto_reconnect = conn.auto_reconnect
if conn.wasConnected and conn.auto_reconnect:
try:
# Check SSH Tunnel needs to be created
if self.use_ssh_tunnel == 1 and \
not self.tunnel_created:
status, error = self.create_ssh_tunnel(
self.tunnel_password
)
# Check SSH Tunnel is alive or not.
self.check_ssh_tunnel_alive()
# Check SSH Tunnel is alive or not.
self.check_ssh_tunnel_alive()
conn.connect()
# This will also update wasConnected flag in
# connection so no need to update the flag manually.
except CryptKeyMissing:
# maintain the status as this will help to restore once
# the key is available
conn.wasConnected = wasConnected
conn.auto_reconnect = auto_reconnect
except Exception as e:
current_app.logger.exception(e)
raise
conn.connect()
# This will also update wasConnected flag in
# connection so no need to update the flag manually.
except CryptKeyMissing:
# maintain the status as this will help to restore once
# the key is available
conn.wasConnected = wasConnected
conn.auto_reconnect = auto_reconnect
except Exception as e:
self.connections.pop(conn_id)
current_app.logger.exception(e)
raise
def release(self, database=None, conn_id=None, did=None):
# Stop the SSH tunnel if release() function calls without