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

BIN
docs/en_US/images/server_advanced.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -21,10 +21,14 @@ Bug fixes
| `Issue #3538 <https://redmine.postgresql.org/issues/3538>`_ - Fix issue where the Reset button does not get enabled till all the mandatory fields are provided in the dialog.
| `Issue #4659 <https://redmine.postgresql.org/issues/4659>`_ - Updated documentation for default privileges to clarify more on the grantor.
| `Issue #4724 <https://redmine.postgresql.org/issues/4724>`_ - Fix network disconnect issue while establishing the connection via SSH Tunnel and it impossible to expand the Servers node.
| `Issue #4792 <https://redmine.postgresql.org/issues/4792>`_ - Ensure that the superuser should be able to create database, as the superuser overrides all the access restrictions.
| `Issue #4818 <https://redmine.postgresql.org/issues/4818>`_ - Fix server connection drops out issue in query tool.
| `Issue #4836 <https://redmine.postgresql.org/issues/4836>`_ - Updated the json file name from 'servers.json' to 'pgadmin4/servers.json' in the container deployment section of the documentation.
| `Issue #4878 <https://redmine.postgresql.org/issues/4878>`_ - Ensure that the superuser should be able to create role, as the superuser overrides all the access restrictions.
| `Issue #4925 <https://redmine.postgresql.org/issues/4925>`_ - Shown some text on process watcher till the initial logs are loaded.
| `Issue #4926 <https://redmine.postgresql.org/issues/4926>`_ - Fix VPN network disconnect issue where pgAdmin4 hangs on expanding the Servers node.
| `Issue #4930 <https://redmine.postgresql.org/issues/4930>`_ - Fix main window tab navigation accessibility issue.
| `Issue #4933 <https://redmine.postgresql.org/issues/4933>`_ - Ensure that the Servers collection node should expand independently of server connections.
| `Issue #4934 <https://redmine.postgresql.org/issues/4934>`_ - Fix the help button link on the User Management dialog.
| `Issue #4935 <https://redmine.postgresql.org/issues/4935>`_ - Fix accessibility issues.

View File

@ -170,7 +170,8 @@ Use the fields in the *Advanced* tab to configure a connection:
`Section 33.15 of the Postgres documentation <https://www.postgresql.org/docs/current/libpq-pgpass.html>`_.
* Use the *Connection timeout* field to specify the maximum wait for connection,
in seconds. Zero or not specified means wait indefinitely. It is not
recommended to use a timeout of less than 2 seconds.
recommended to use a timeout of less than 2 seconds. By default it is set to
10 seconds.
.. note:: The password file option is only supported when pgAdmin is using libpq
v10.0 or later to connect to the server.

View File

@ -0,0 +1,27 @@
""" Update the default timeout to 10 seconds instead on 0.
0 indicates wait indefinitely which causes trouble when network connection
to server is lost.
Revision ID: aff1436e3c8c
Revises: a77a0932a568
Create Date: 2019-10-28 12:47:36.828709
"""
from pgadmin.model import db
# revision identifiers, used by Alembic.
revision = 'aff1436e3c8c'
down_revision = 'a77a0932a568'
branch_labels = None
depends_on = None
def upgrade():
db.engine.execute(
'UPDATE server SET connect_timeout=10 WHERE connect_timeout=0 OR connect_timeout IS NULL'
)
def downgrade():
pass

View File

@ -28,6 +28,7 @@ from pgadmin.model import db, Server, ServerGroup, User
from pgadmin.utils.driver import get_driver
from pgadmin.utils.master_password import get_crypt_key
from pgadmin.utils.exception import CryptKeyMissing
from psycopg2 import Error as psycopg2_Error, OperationalError
def has_any(data, keys):
@ -58,7 +59,7 @@ def recovery_state(connection, postgres_version):
else:
in_recovery = None
wal_paused = None
return in_recovery, wal_paused
return status, result, in_recovery, wal_paused
def server_icon_and_background(is_connected, manager, server):
@ -121,19 +122,21 @@ class ServerModule(sg.ServerGroupPluginModule):
for server in servers:
connected = False
manager = None
errmsg = None
was_connected = False
in_recovery = None
wal_paused = None
try:
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
was_connected = conn.wasConnected
except CryptKeyMissing:
# show the nodes at least even if not able to connect.
pass
except psycopg2_Error as e:
current_app.logger.exception(e)
errmsg = str(e)
in_recovery = None
wal_paused = None
if connected:
in_recovery, wal_paused = recovery_state(conn, manager.version)
yield self.generate_browser_node(
"%d" % (server.id),
gid,
@ -151,7 +154,9 @@ class ServerModule(sg.ServerGroupPluginModule):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
if server.tunnel_password is not None else False
if server.tunnel_password is not None else False,
was_connected=was_connected,
errmsg=errmsg
)
@property
@ -352,12 +357,16 @@ class ServerNode(PGChildNodeView):
manager = driver.connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
errmsg = None
in_recovery = None
wal_paused = None
if connected:
in_recovery, wal_paused = recovery_state(conn, manager.version)
else:
in_recovery = None
wal_paused = None
status, result, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
if not status:
connected = False
manager.release()
errmsg = "{0} : {1}".format(server.name, result)
res.append(
self.blueprint.generate_browser_node(
@ -377,7 +386,8 @@ class ServerNode(PGChildNodeView):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
if server.tunnel_password is not None else False
if server.tunnel_password is not None else False,
errmsg=errmsg
)
)
@ -409,12 +419,16 @@ class ServerNode(PGChildNodeView):
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(server.id)
conn = manager.connection()
connected = conn.connected()
errmsg = None
in_recovery = None
wal_paused = None
if connected:
in_recovery, wal_paused = recovery_state(conn, manager.version)
else:
in_recovery = None
wal_paused = None
status, result, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
if not status:
connected = False
manager.release()
errmsg = "{0} : {1}".format(server.name, result)
return make_json_response(
result=self.blueprint.generate_browser_node(
@ -434,8 +448,9 @@ class ServerNode(PGChildNodeView):
is_password_saved=True if server.password is not None
else False,
is_tunnel_password_saved=True
if server.tunnel_password is not None else False
)
if server.tunnel_password is not None else False,
errmsg=errmsg
),
)
@login_required
@ -949,19 +964,33 @@ class ServerNode(PGChildNodeView):
def connect_status(self, gid, sid):
"""Check and return the connection status."""
server = Server.query.filter_by(id=sid).first()
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
res = conn.connected()
connected = conn.connected()
in_recovery = None
wal_paused = None
errmsg = None
if connected:
status, result, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
if res:
from pgadmin.utils.exception import ConnectionLost, \
SSHTunnelConnectionLost
try:
conn.execute_scalar('SELECT 1')
except (ConnectionLost, SSHTunnelConnectionLost):
res = False
if not status:
connected = False
manager.release()
errmsg = "{0} : {1}".format(server.name, result)
return make_json_response(data={'connected': res})
return make_json_response(
data={
'icon': server_icon_and_background(connected, manager, server),
'connected': connected,
'in_recovery': in_recovery,
'wal_pause': wal_paused,
'server_type': manager.server_type if connected else "pg",
'user': manager.user_info if connected else None,
'errmsg': errmsg
}
)
def connect(self, gid, sid):
"""
@ -1078,6 +1107,8 @@ class ServerNode(PGChildNodeView):
tunnel_password=tunnel_password,
server_types=ServerType.types()
)
except OperationalError as e:
return internal_server_error(errormsg=str(e))
except Exception as e:
current_app.logger.exception(e)
return self.get_response_for_password(
@ -1088,7 +1119,7 @@ class ServerNode(PGChildNodeView):
errmsg = errmsg.decode('utf-8')
current_app.logger.error(
"Could not connected to server(#{0}) - '{1}'.\nError: {2}"
"Could not connect to server(#{0}) - '{1}'.\nError: {2}"
.format(server.id, server.name, errmsg)
)
return self.get_response_for_password(server, 401, True,
@ -1125,7 +1156,8 @@ class ServerNode(PGChildNodeView):
%s - %s' % (server.id, server.name))
# Update the recovery and wal pause option for the server
# if connected successfully
in_recovery, wal_paused = recovery_state(conn, manager.version)
_, _, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
return make_json_response(
success=1,

View File

@ -292,6 +292,10 @@ define('pgadmin.node.server', [
// Call added method of node.js
pgAdmin.Browser.Node.callbacks.added.apply(this, arguments);
if(data.was_connected) {
fetch_connection_status(this, data, pgBrowser.tree, item);
}
return true;
},
/* Reload configuration */
@ -732,7 +736,7 @@ define('pgadmin.node.server', [
tunnel_password: undefined,
tunnel_authentication: 0,
save_tunnel_password: false,
connect_timeout: 0,
connect_timeout: 10,
},
// Default values!
initialize: function(attrs, args) {
@ -1273,7 +1277,14 @@ define('pgadmin.node.server', [
}
};
/* Wait till the existing request completes */
if(data.is_connecting) {
return;
}
data.is_connecting = true;
tree.setLeaf(item);
tree.removeIcon(item);
tree.addIcon(item, {icon: 'icon-server-connecting'});
var url = obj.generate_url(item, 'connect', data, true);
$.post(url)
.done(function(res) {
@ -1287,6 +1298,40 @@ define('pgadmin.node.server', [
return onFailure(
xhr, status, error, obj, data, tree, item, wasConnected
);
})
.always(function(){
data.is_connecting = false;
});
};
var fetch_connection_status = function(obj, data, tree, item) {
var url = obj.generate_url(item, 'connect', data, true);
tree.setLeaf(item);
tree.removeIcon(item);
tree.addIcon(item, {icon: 'icon-server-connecting'});
$.get(url)
.done(function(res) {
tree.setInode(item);
if (res && res.data) {
if (typeof res.data.icon == 'string') {
tree.removeIcon(item);
data.icon = res.data.icon;
tree.addIcon(item, {icon: data.icon});
}
_.extend(data, res.data);
var serverInfo = pgBrowser.serverInfo = pgBrowser.serverInfo || {};
serverInfo[data._id] = _.extend({}, data);
if(data.errmsg) {
Alertify.error(data.errmsg);
}
}
})
.fail(function(xhr, status, error) {
tree.setInode(item);
tree.addIcon(item, {icon: 'icon-server-not-connected'});
Alertify.pgRespErrorNotify(xhr, error);
});
};
}

View File

@ -17,7 +17,8 @@ from flask.views import View, MethodViewType, with_metaclass
from flask_babelex import gettext
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.ajax import make_json_response, precondition_required
from pgadmin.utils.ajax import make_json_response, precondition_required,\
internal_server_error
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost,\
CryptKeyMissing
@ -377,11 +378,7 @@ class PGChildNodeView(NodeView):
if not conn.connected():
status, msg = conn.connect()
if not status:
return precondition_required(
gettext(
"Connection to the server has been lost."
)
)
return internal_server_error(errormsg=msg)
except (ConnectionLost, SSHTunnelConnectionLost, CryptKeyMissing):
raise
except Exception as e:

View File

@ -543,6 +543,7 @@ define('pgadmin.dashboard', [
);
const WAIT_COUNTER = 3;
let last_poll_wait_counter = 0;
let resp_not_received_counter = 0;
/* Stop if running, only one poller lives */
self.stopChartsPoller();
@ -563,7 +564,7 @@ define('pgadmin.dashboard', [
/* If none of the chart wants data, don't trouble
* If response not received from prev poll, don't trouble !!
*/
if(chart_names_to_get.length == 0 || last_poll_wait_counter > 0) {
if(chart_names_to_get.length == 0 || last_poll_wait_counter > 0 || resp_not_received_counter >= WAIT_COUNTER) {
/* reduce the number of tries, request should be sent if last_poll_wait_counter
* completes WAIT_COUNTER times.*/
last_poll_wait_counter--;
@ -571,12 +572,12 @@ define('pgadmin.dashboard', [
}
var path = self.getStatsUrl(sid, did, chart_names_to_get);
resp_not_received_counter++;
$.ajax({
url: path,
type: 'GET',
})
.done(function(resp) {
last_poll_wait_counter = 0;
for(let chart_name in resp) {
let chart_obj = chart_store[chart_name].chart_obj;
$(chart_obj.getContainer()).removeClass('graph-error');
@ -584,7 +585,6 @@ define('pgadmin.dashboard', [
}
})
.fail(function(xhr) {
last_poll_wait_counter = 0;
let err = '';
let msg = '';
let cls = 'info';
@ -613,6 +613,10 @@ define('pgadmin.dashboard', [
'<div class="alert alert-' + cls + ' pg-panel-message" role="alert">' + msg + '</div>'
);
}
})
.always(function() {
last_poll_wait_counter = 0;
resp_not_received_counter--;
});
last_poll_wait_counter = WAIT_COUNTER;
};

View File

@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
#
##########################################################################
SCHEMA_VERSION = 23
SCHEMA_VERSION = 24
##########################################################################
#

View File

@ -12,6 +12,7 @@ import $ from 'jquery';
import url_for from '../url_for';
import axios from 'axios';
import * as httpErrorHandler from './query_tool_http_error_handler';
import * as queryTxnStatus from 'sources/sqleditor/query_txn_status_constants';
class LoadingScreen {
constructor(sqlEditor) {
@ -83,7 +84,8 @@ class ExecuteQuery {
self.loadingScreen.hide();
self.enableSQLEditorButtons();
// Enable/Disable commit and rollback button.
if (result.data.data.transaction_status == 2 || result.data.data.transaction_status == 3) {
if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
|| result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
self.enableTransactionButtons();
} else {
self.disableTransactionButtons();
@ -123,7 +125,8 @@ class ExecuteQuery {
self.updateSqlEditorLastTransactionStatus(httpMessage.data.data.transaction_status);
// Enable/Disable commit and rollback button.
if (httpMessage.data.data.transaction_status == 2 || httpMessage.data.data.transaction_status == 3) {
if (httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
|| httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
self.enableTransactionButtons();
} else {
self.disableTransactionButtons();
@ -131,7 +134,7 @@ class ExecuteQuery {
if (ExecuteQuery.isQueryFinished(httpMessage)) {
if (this.sqlServerObject.close_on_idle_transaction &&
httpMessage.data.data.transaction_status == 0)
httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_IDLE)
this.sqlServerObject.check_needed_confirmations_before_closing_panel();
self.loadingScreen.setMessage('Loading data from the database server and rendering...');

View File

@ -0,0 +1,11 @@
/* psycopg2 transaction status constants
* http://initd.org/psycopg/docs/extensions.html#transaction-status-constants
*/
module.exports = {
TRANSACTION_STATUS_IDLE: 0,
TRANSACTION_STATUS_ACTIVE: 1,
TRANSACTION_STATUS_INTRANS: 2,
TRANSACTION_STATUS_INERROR: 3,
TRANSACTION_STATUS_UNKNOWN: 5,
};

View File

@ -9,6 +9,7 @@
import {isValidData} from 'sources/utils';
import $ from 'jquery';
import Alertify from 'pgadmin.alertifyjs';
export class TreeNode {
constructor(id, data, domNode, parent) {
@ -261,6 +262,9 @@ export class Tree {
const parentId = this.translateTreeNodeIdFromACITree(api.parent(item));
this.addNewNode(id, data, item, parentId);
if(data.errmsg) {
Alertify.error(data.errmsg);
}
}
}
}.bind(this));

View File

@ -36,6 +36,7 @@ define('tools.querytool', [
'sources/sqleditor/calculate_query_run_time',
'sources/sqleditor/call_render_after_poll',
'sources/sqleditor/query_tool_preferences',
'sources/sqleditor/query_txn_status_constants',
'sources/csrf',
'tools/datagrid/static/js/datagrid_panel_title',
'sources/window',
@ -52,7 +53,7 @@ define('tools.querytool', [
XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
GeometryViewer, historyColl, queryHist, querySources,
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, csrfToken, panelTitleFunc,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
pgWindow) {
/* Return back, this has been called more than once */
if (pgAdmin.SqlEditor)
@ -4182,8 +4183,9 @@ define('tools.querytool', [
self.unsaved_changes_user_confirmation(msg, false);
} // If a transaction is currently ongoing
else if (self.preferences.prompt_commit_transaction
&& self.last_transaction_status > 0) { // 0 -> idle (no transaction)
var is_commit_disabled = self.last_transaction_status == 3; // 3 -> Failed transaction
&& (self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INTRANS
|| self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INERROR)) {
var is_commit_disabled = self.last_transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR;
self.uncommitted_transaction_user_confirmation(is_commit_disabled);
}
else {

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